Merge branch 'parisc-4.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 3 Jul 2017 22:27:58 +0000 (15:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 3 Jul 2017 22:27:58 +0000 (15:27 -0700)
Pull parisc updates from Helge Deller:
 "Main changes are:

   - Added support to the parisc dma functions to return DMA_ERROR_CODE
     if DMA isn't possible. This fixes a long standing kernel crash if
     parport_pc is enabled (by Thomas Bogendoerfer, marked for stable
     series).

   - Use the compat_sys_keyctl() in compat mode (by Eric Biggers, marked
     for stable series).

   - Initial support for the Page Deallocation Table (PDT) which is
     maintained by firmware and holds the list of memory addresses which
     had physical errors. By checking that list we can prevent Linux to
     use those broken memory areas.

   - Ensure IRQs are off in switch_mm().

   - Report SIGSEGV instead of SIGBUS when running out of stack.

   - Mark the cr16 clocksource stable on single-socket and single-core
     machines"

* 'parisc-4.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux:
  parisc: DMA API: return error instead of BUG_ON for dma ops on non dma devs
  parisc: Report SIGSEGV instead of SIGBUS when running out of stack
  parisc: use compat_sys_keyctl()
  parisc: Don't hardcode PSW values in hpmc code
  parisc: Don't hardcode PSW values in gsc_*() functions
  parisc: Avoid zeroing gr[0] in fixup_exception()
  parisc/mm: Ensure IRQs are off in switch_mm()
  parisc: Add Page Deallocation Table (PDT) support
  parisc: Enhance detection of synchronous cr16 clocksources
  parisc: Drop per_cpu uaccess related exception_data struct
  parisc: Inline trivial exception code in lusercopy.S

25 files changed:
arch/parisc/include/asm/dma-mapping.h
arch/parisc/include/asm/io.h
arch/parisc/include/asm/mmu_context.h
arch/parisc/include/asm/pdc.h
arch/parisc/include/asm/pdcpat.h
arch/parisc/include/asm/pgtable.h
arch/parisc/include/asm/processor.h
arch/parisc/include/asm/uaccess.h
arch/parisc/include/uapi/asm/pdc.h
arch/parisc/kernel/Makefile
arch/parisc/kernel/asm-offsets.c
arch/parisc/kernel/firmware.c
arch/parisc/kernel/hpmc.S
arch/parisc/kernel/inventory.c
arch/parisc/kernel/pdt.c [new file with mode: 0644]
arch/parisc/kernel/processor.c
arch/parisc/kernel/syscall_table.S
arch/parisc/kernel/time.c
arch/parisc/lib/lusercopy.S
arch/parisc/mm/fault.c
arch/parisc/mm/init.c
drivers/parisc/ccio-dma.c
drivers/parisc/dino.c
drivers/parisc/lba_pci.c
drivers/parisc/sba_iommu.c

index 5404c6a726b227f57024afec81bbd199d567c810..9a2a8956a695f54373b811db55421fe54807ae9d 100644 (file)
@@ -20,6 +20,8 @@
 ** flush/purge and allocate "regular" cacheable pages for everything.
 */
 
+#define DMA_ERROR_CODE (~(dma_addr_t)0)
+
 #ifdef CONFIG_PA11
 extern const struct dma_map_ops pcxl_dma_ops;
 extern const struct dma_map_ops pcx_dma_ops;
@@ -54,12 +56,13 @@ parisc_walk_tree(struct device *dev)
                        break;
                }
        }
-       BUG_ON(!dev->platform_data);
        return dev->platform_data;
 }
-               
-#define GET_IOC(dev) (HBA_DATA(parisc_walk_tree(dev))->iommu)
-       
+
+#define GET_IOC(dev) ({                                        \
+       void *__pdata = parisc_walk_tree(dev);          \
+       __pdata ? HBA_DATA(__pdata)->iommu : NULL;      \
+})
 
 #ifdef CONFIG_IOMMU_CCIO
 struct parisc_device;
index 1a16f1d1075fc93a39bef3fad4c1c24096888d5d..af98254f7257083840a89236fc050e2077c41cac 100644 (file)
@@ -34,10 +34,10 @@ static inline unsigned char gsc_readb(unsigned long addr)
        unsigned char ret;
 
        __asm__ __volatile__(
-       "       rsm     2,%0\n"
+       "       rsm     %3,%0\n"
        "       ldbx    0(%2),%1\n"
        "       mtsm    %0\n"
-       : "=&r" (flags), "=r" (ret) : "r" (addr) );
+       : "=&r" (flags), "=r" (ret) : "r" (addr), "i" (PSW_SM_D) );
 
        return ret;
 }
@@ -48,10 +48,10 @@ static inline unsigned short gsc_readw(unsigned long addr)
        unsigned short ret;
 
        __asm__ __volatile__(
-       "       rsm     2,%0\n"
+       "       rsm     %3,%0\n"
        "       ldhx    0(%2),%1\n"
        "       mtsm    %0\n"
-       : "=&r" (flags), "=r" (ret) : "r" (addr) );
+       : "=&r" (flags), "=r" (ret) : "r" (addr), "i" (PSW_SM_D) );
 
        return ret;
 }
@@ -87,20 +87,20 @@ static inline void gsc_writeb(unsigned char val, unsigned long addr)
 {
        long flags;
        __asm__ __volatile__(
-       "       rsm     2,%0\n"
+       "       rsm     %3,%0\n"
        "       stbs    %1,0(%2)\n"
        "       mtsm    %0\n"
-       : "=&r" (flags) :  "r" (val), "r" (addr) );
+       : "=&r" (flags) :  "r" (val), "r" (addr), "i" (PSW_SM_D) );
 }
 
 static inline void gsc_writew(unsigned short val, unsigned long addr)
 {
        long flags;
        __asm__ __volatile__(
-       "       rsm     2,%0\n"
+       "       rsm     %3,%0\n"
        "       sths    %1,0(%2)\n"
        "       mtsm    %0\n"
-       : "=&r" (flags) :  "r" (val), "r" (addr) );
+       : "=&r" (flags) :  "r" (val), "r" (addr), "i" (PSW_SM_D) );
 }
 
 static inline void gsc_writel(unsigned int val, unsigned long addr)
index 59be257644335f7fff70b54ca65661633b9eaa22..a8122625787846b8ee4233217911b23e7b89e2f6 100644 (file)
@@ -49,15 +49,26 @@ static inline void load_context(mm_context_t context)
        mtctl(__space_to_prot(context), 8);
 }
 
-static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk)
+static inline void switch_mm_irqs_off(struct mm_struct *prev,
+               struct mm_struct *next, struct task_struct *tsk)
 {
-
        if (prev != next) {
                mtctl(__pa(next->pgd), 25);
                load_context(next->context);
        }
 }
 
+static inline void switch_mm(struct mm_struct *prev,
+               struct mm_struct *next, struct task_struct *tsk)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       switch_mm_irqs_off(prev, next, tsk);
+       local_irq_restore(flags);
+}
+#define switch_mm_irqs_off switch_mm_irqs_off
+
 #define deactivate_mm(tsk,mm)  do { } while (0)
 
 static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
index 451906d78136e5684e850444d5191c2d5375eacf..7569627a032bb6e99ff0a546b864f6e0e64b71bc 100644 (file)
@@ -6,6 +6,8 @@
 #if !defined(__ASSEMBLY__)
 
 extern int pdc_type;
+extern unsigned long parisc_cell_num; /* cell number the CPU runs on (PAT) */
+extern unsigned long parisc_cell_loc; /* cell location of CPU (PAT)       */
 
 /* Values for pdc_type */
 #define PDC_TYPE_ILLEGAL       -1
@@ -143,6 +145,18 @@ struct pdc_btlb_info {     /* PDC_BLOCK_TLB, return of PDC_BTLB_INFO */
 
 #endif /* !CONFIG_PA20 */
 
+struct pdc_mem_retinfo { /* PDC_MEM/PDC_MEM_MEMINFO (return info) */
+       unsigned long pdt_size;
+       unsigned long pdt_entries;
+       unsigned long pdt_status;
+       unsigned long first_dbe_loc;
+       unsigned long good_mem;
+};
+
+struct pdc_mem_read_pdt { /* PDC_MEM/PDC_MEM_READ_PDT (return info) */
+       unsigned long pdt_entries;
+};
+
 #ifdef CONFIG_64BIT
 struct pdc_memory_table_raddr { /* PDC_MEM/PDC_MEM_TABLE (return info) */
        unsigned long entries_returned;
@@ -301,6 +315,10 @@ int pdc_get_initiator(struct hardware_path *, struct pdc_initiator *);
 int pdc_tod_read(struct pdc_tod *tod);
 int pdc_tod_set(unsigned long sec, unsigned long usec);
 
+void pdc_pdt_init(void);       /* in pdt.c */
+int pdc_mem_pdt_info(struct pdc_mem_retinfo *rinfo);
+int pdc_mem_pdt_read_entries(struct pdc_mem_read_pdt *rpdt_read,
+               unsigned long *pdt_entries_ptr);
 #ifdef CONFIG_64BIT
 int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr,
                struct pdc_memory_table *tbl, unsigned long entries);
index e1d289092705f00431408f90ca6bd48e47c8cba4..32e105fb8adb362f261d20432f1f8af068086376 100644 (file)
 #define PDC_PAT_MEM_CELL_CLEAR         6L /* Clear PDT For Cell           */
 #define PDC_PAT_MEM_CELL_READ          7L /* Read PDT entries For Cell    */
 #define PDC_PAT_MEM_CELL_RESET         8L /* Reset clear bit For Cell     */
-#define PDC_PAT_MEM_SETGM              9L /* Set Golden Memory value      */
-#define PDC_PAT_MEM_ADD_PAGE           10L /* ADDs a page to the cell      */
-#define PDC_PAT_MEM_ADDRESS            11L /* Get Physical Location From   */
+#define PDC_PAT_MEM_SETGM              9L /* Set Good Memory value        */
+#define PDC_PAT_MEM_ADD_PAGE           10L /* ADDs a page to the cell      */
+#define PDC_PAT_MEM_ADDRESS            11L /* Get Physical Location From   */
                                                 /* Memory Address               */
 #define PDC_PAT_MEM_GET_TXT_SIZE       12L /* Get Formatted Text Size   */
 #define PDC_PAT_MEM_GET_PD_TXT         13L /* Get PD Formatted Text     */
@@ -212,6 +212,23 @@ struct pdc_pat_cpu_num {
        unsigned long cpu_loc;
 };
 
+struct pdc_pat_mem_retinfo { /* PDC_PAT_MEM/PDC_PAT_MEM_PD_INFO (return info) */
+       unsigned int ke;        /* bit 0: memory inside good memory? */
+       unsigned int current_pdt_entries:16;
+       unsigned int max_pdt_entries:16;
+       unsigned long Cs_bitmap;
+       unsigned long Ic_bitmap;
+       unsigned long good_mem;
+       unsigned long first_dbe_loc; /* first location of double bit error */
+       unsigned long clear_time; /* last PDT clear time (since Jan 1970) */
+};
+
+struct pdc_pat_mem_read_pd_retinfo { /* PDC_PAT_MEM/PDC_PAT_MEM_PD_READ */
+       unsigned long actual_count_bytes;
+       unsigned long pdt_entries;
+};
+
+
 struct pdc_pat_pd_addr_map_entry {
        unsigned char entry_type;       /* 1 = Memory Descriptor Entry Type */
        unsigned char reserve1[5];
@@ -293,15 +310,15 @@ extern int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, unsigned lon
 
 extern int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr, unsigned long count, unsigned long offset);
 
-
 extern int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *val); 
 extern int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val); 
 
-
-/* Flag to indicate this is a PAT box...don't use this unless you
-** really have to...it might go away some day.
-*/
-extern int pdc_pat;     /* arch/parisc/kernel/inventory.c */
+extern int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo);
+extern int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret,
+               unsigned long *pdt_entries_ptr, unsigned long max_entries);
+extern int pdc_pat_mem_read_pd_pdt(struct pdc_pat_mem_read_pd_retinfo *pret,
+               unsigned long *pdt_entries_ptr, unsigned long count,
+               unsigned long offset);
 
 #endif /* __ASSEMBLY__ */
 
index 3a4ed9f91d5727a5293780967411328ae8cc6310..71ca86cb0f168904c1df0f949051d3e5fd750966 100644 (file)
@@ -511,6 +511,9 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr,
 
 #define pte_same(A,B)  (pte_val(A) == pte_val(B))
 
+struct seq_file;
+extern void arch_report_meminfo(struct seq_file *m);
+
 #endif /* !__ASSEMBLY__ */
 
 
index 4c6694b4e77e0dd12ef6aed95508ea035ccdb10c..b3b66c3d6f3c4716c9fdfe55492ffb4d20dc72a1 100644 (file)
@@ -103,6 +103,8 @@ struct cpuinfo_parisc {
        unsigned long bh_count;     /* number of times bh was invoked */
        unsigned long fp_rev;
        unsigned long fp_model;
+       unsigned long cpu_num;      /* CPU number from PAT firmware */
+       unsigned long cpu_loc;      /* CPU location from PAT firmware */
        unsigned int state;
        struct parisc_device *dev;
        unsigned long loops_per_jiffy;
index 6b113f39f30c9b34aa1bc6784af4b0c60ccf3074..c3e114f67485d350b7eb84b671061cf7e9950f91 100644 (file)
@@ -68,17 +68,6 @@ struct exception_table_entry {
 #define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr )\
        ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1)
 
-/*
- * The page fault handler stores, in a per-cpu area, the following information
- * if a fixup routine is available.
- */
-struct exception_data {
-       unsigned long fault_ip;
-       unsigned long fault_gp;
-       unsigned long fault_space;
-       unsigned long fault_addr;
-};
-
 /*
  * load_sr2() preloads the space register %%sr2 - based on the value of
  * get_fs() - with either a value of 0 to access kernel space (KERNEL_DS which
index 0609ff117f67e56c7d12bd3c1f312d97ebc9f286..1f30b49772aa875004683611f668b13946e11d95 100644 (file)
 #define PDC_TLB_SETUP          1       /* set up miss handling         */
 
 #define PDC_MEM                20              /* Manage memory                */
-#define PDC_MEM_MEMINFO                0
-#define PDC_MEM_ADD_PAGE       1
-#define PDC_MEM_CLEAR_PDT      2
-#define PDC_MEM_READ_PDT       3
-#define PDC_MEM_RESET_CLEAR    4
-#define PDC_MEM_GOODMEM                5
+#define PDC_MEM_MEMINFO                0       /* Return PDT info              */
+#define PDC_MEM_ADD_PAGE       1       /* Add page to PDT              */
+#define PDC_MEM_CLEAR_PDT      2       /* Clear PDT                    */
+#define PDC_MEM_READ_PDT       3       /* Read PDT entry               */
+#define PDC_MEM_RESET_CLEAR    4       /* Reset PDT clear flag         */
+#define PDC_MEM_GOODMEM                5       /* Set good_mem value           */
 #define PDC_MEM_TABLE          128     /* Non contig mem map (sprockets) */
 #define PDC_MEM_RETURN_ADDRESS_TABLE   PDC_MEM_TABLE
 #define PDC_MEM_GET_MEMORY_SYSTEM_TABLES_SIZE  131
index 69a11183d48d4da5cf6f9d961d31b7cb91589480..c4294df69fb63a9e4354e06a6f84e8e4f79b505d 100644 (file)
@@ -4,7 +4,7 @@
 
 extra-y                        := head.o vmlinux.lds
 
-obj-y          := cache.o pacache.o setup.o traps.o time.o irq.o \
+obj-y          := cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \
                   pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \
                   ptrace.o hardware.o inventory.o drivers.o \
                   signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
index 1c4fe61a592b3748c2555ed53065feef5aed6a53..dfff8a0d6fd1e19d129aba389ac7f7b87804c3c9 100644 (file)
@@ -297,11 +297,6 @@ int main(void)
 #else
        DEFINE(HUGEPAGE_SIZE, PAGE_SIZE);
 #endif
-       BLANK();
-       DEFINE(EXCDATA_IP, offsetof(struct exception_data, fault_ip));
-       DEFINE(EXCDATA_GP, offsetof(struct exception_data, fault_gp));
-       DEFINE(EXCDATA_SPACE, offsetof(struct exception_data, fault_space));
-       DEFINE(EXCDATA_ADDR, offsetof(struct exception_data, fault_addr));
        BLANK();
        DEFINE(ASM_PDC_RESULT_SIZE, NUM_PDC_RESULT * sizeof(unsigned long));
        BLANK();
index 9d797ae4fa22248665a13e0ff29d72532e9e4bdb..98190252c12fdc801ca8f7f328733727577f48f2 100644 (file)
@@ -957,6 +957,41 @@ int pdc_tod_read(struct pdc_tod *tod)
 }
 EXPORT_SYMBOL(pdc_tod_read);
 
+int pdc_mem_pdt_info(struct pdc_mem_retinfo *rinfo)
+{
+       int retval;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pdc_lock, flags);
+       retval = mem_pdc_call(PDC_MEM, PDC_MEM_MEMINFO, __pa(pdc_result), 0);
+       convert_to_wide(pdc_result);
+       memcpy(rinfo, pdc_result, sizeof(*rinfo));
+       spin_unlock_irqrestore(&pdc_lock, flags);
+
+       return retval;
+}
+
+int pdc_mem_pdt_read_entries(struct pdc_mem_read_pdt *pret,
+               unsigned long *pdt_entries_ptr)
+{
+       int retval;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pdc_lock, flags);
+       retval = mem_pdc_call(PDC_MEM, PDC_MEM_READ_PDT, __pa(pdc_result),
+                       __pa(pdc_result2));
+       if (retval == PDC_OK) {
+               convert_to_wide(pdc_result);
+               memcpy(pret, pdc_result, sizeof(*pret));
+               convert_to_wide(pdc_result2);
+               memcpy(pdt_entries_ptr, pdc_result2,
+                       pret->pdt_entries * sizeof(*pdt_entries_ptr));
+       }
+       spin_unlock_irqrestore(&pdc_lock, flags);
+
+       return retval;
+}
+
 /**
  * pdc_tod_set - Set the Time-Of-Day clock.
  * @sec: The number of seconds since epoch.
@@ -1383,6 +1418,79 @@ int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val)
 
        return retval;
 }
+
+/**
+ * pdc_pat_mem_pdc_info - Retrieve information about page deallocation table
+ * @rinfo: memory pdt information
+ *
+ */
+int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo)
+{
+       int retval;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pdc_lock, flags);
+       retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_INFO,
+                       __pa(&pdc_result));
+       if (retval == PDC_OK)
+               memcpy(rinfo, &pdc_result, sizeof(*rinfo));
+       spin_unlock_irqrestore(&pdc_lock, flags);
+
+       return retval;
+}
+
+/**
+ * pdc_pat_mem_read_cell_pdt - Read PDT entries from (old) PAT firmware
+ * @pret: array of PDT entries
+ * @pdt_entries_ptr: ptr to hold number of PDT entries
+ * @max_entries: maximum number of entries to be read
+ *
+ */
+int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret,
+               unsigned long *pdt_entries_ptr, unsigned long max_entries)
+{
+       int retval;
+       unsigned long flags, entries;
+
+       spin_lock_irqsave(&pdc_lock, flags);
+       /* PDC_PAT_MEM_CELL_READ is available on early PAT machines only */
+       retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_CELL_READ,
+                       __pa(&pdc_result), parisc_cell_num, __pa(&pdc_result2));
+
+       if (retval == PDC_OK) {
+               /* build up return value as for PDC_PAT_MEM_PD_READ */
+               entries = min(pdc_result[0], max_entries);
+               pret->pdt_entries = entries;
+               pret->actual_count_bytes = entries * sizeof(unsigned long);
+               memcpy(pdt_entries_ptr, &pdc_result2, pret->actual_count_bytes);
+       }
+
+       spin_unlock_irqrestore(&pdc_lock, flags);
+       WARN_ON(retval == PDC_OK && pdc_result[0] > max_entries);
+
+       return retval;
+}
+/**
+ * pdc_pat_mem_read_pd_pdt - Read PDT entries from (newer) PAT firmware
+ * @pret: array of PDT entries
+ * @pdt_entries_ptr: ptr to hold number of PDT entries
+ *
+ */
+int pdc_pat_mem_read_pd_pdt(struct pdc_pat_mem_read_pd_retinfo *pret,
+               unsigned long *pdt_entries_ptr, unsigned long count,
+               unsigned long offset)
+{
+       int retval;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pdc_lock, flags);
+       retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_READ,
+               __pa(&pret), __pa(pdt_entries_ptr),
+               count, offset);
+       spin_unlock_irqrestore(&pdc_lock, flags);
+
+       return retval;
+}
 #endif /* CONFIG_64BIT */
 
 
index 0fbd0a0e1cda4244a38ce2e0af11711a71088f5d..e3a8e5e4d5de75897adcea4134f87c7246f60646 100644 (file)
@@ -44,6 +44,7 @@
 
 #include <asm/assembly.h>
 #include <asm/pdc.h>
+#include <asm/psw.h>
 
 #include <linux/linkage.h>
 #include <linux/init.h>
@@ -135,7 +136,7 @@ ENTRY_CFI(os_hpmc)
         * So turn on the Q bit and turn off the M bit.
         */
 
-       ldo     8(%r0),%r4                       /* PSW Q on, PSW M off */
+       ldi     PSW_SM_Q,%r4                   /* PSW Q on, PSW M off */
        mtctl   %r4,ipsw
        mtctl   %r0,pcsq
        mtctl   %r0,pcsq
@@ -257,7 +258,7 @@ os_hpmc_5:
 
        tovirt_r1 %r30      /* make sp virtual */
 
-       rsm 8,%r0           /* Clear Q bit */
+       rsm     PSW_SM_Q,%r0           /* Clear Q bit */
        ldi     1,%r8       /* Set trap code to "1" for HPMC */
        load32  PA(intr_save),%r1
        be      0(%sr7,%r1)
index c9789d9c73b40478bb5ff931fd2c3afb30a4840f..b0fe19ac4d78f03cd524d9b9f2d23ec828dc9100 100644 (file)
 
 int pdc_type __read_mostly = PDC_TYPE_ILLEGAL;
 
+/* cell number and location (PAT firmware only) */
+unsigned long parisc_cell_num __read_mostly;
+unsigned long parisc_cell_loc __read_mostly;
+
+
 void __init setup_pdc(void)
 {
        long status;
@@ -78,6 +83,10 @@ void __init setup_pdc(void)
        if (status == PDC_OK) {
                pdc_type = PDC_TYPE_PAT;
                pr_cont("64 bit PAT.\n");
+               parisc_cell_num = cell_info.cell_num;
+               parisc_cell_loc = cell_info.cell_loc;
+               pr_info("PAT: Running on cell %lu and location %lu.\n",
+                       parisc_cell_num, parisc_cell_loc);
                return;
        }
 #endif
diff --git a/arch/parisc/kernel/pdt.c b/arch/parisc/kernel/pdt.c
new file mode 100644 (file)
index 0000000..f3a797e
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *    Page Deallocation Table (PDT) support
+ *
+ *    The Page Deallocation Table (PDT) holds a table with pointers to bad
+ *    memory (broken RAM modules) which is maintained by firmware.
+ *
+ *    Copyright 2017 by Helge Deller <deller@gmx.de>
+ *
+ *    TODO:
+ *    - check regularily for new bad memory
+ *    - add userspace interface with procfs or sysfs
+ *    - increase number of PDT entries dynamically
+ */
+
+#include <linux/memblock.h>
+#include <linux/seq_file.h>
+
+#include <asm/pdc.h>
+#include <asm/pdcpat.h>
+#include <asm/sections.h>
+#include <asm/pgtable.h>
+
+enum pdt_access_type {
+       PDT_NONE,
+       PDT_PDC,
+       PDT_PAT_NEW,
+       PDT_PAT_OLD
+};
+
+static enum pdt_access_type pdt_type;
+
+/* global PDT status information */
+static struct pdc_mem_retinfo pdt_status;
+
+#define MAX_PDT_TABLE_SIZE     PAGE_SIZE
+#define MAX_PDT_ENTRIES                (MAX_PDT_TABLE_SIZE / sizeof(unsigned long))
+static unsigned long pdt_entry[MAX_PDT_ENTRIES] __page_aligned_bss;
+
+
+/* report PDT entries via /proc/meminfo */
+void arch_report_meminfo(struct seq_file *m)
+{
+       if (pdt_type == PDT_NONE)
+               return;
+
+       seq_printf(m, "PDT_max_entries: %7lu\n",
+                       pdt_status.pdt_size);
+       seq_printf(m, "PDT_cur_entries: %7lu\n",
+                       pdt_status.pdt_entries);
+}
+
+/*
+ * pdc_pdt_init()
+ *
+ * Initialize kernel PDT structures, read initial PDT table from firmware,
+ * report all current PDT entries and mark bad memory with memblock_reserve()
+ * to avoid that the kernel will use broken memory areas.
+ *
+ */
+void __init pdc_pdt_init(void)
+{
+       int ret, i;
+       unsigned long entries;
+       struct pdc_mem_read_pdt pdt_read_ret;
+
+       if (is_pdc_pat()) {
+               struct pdc_pat_mem_retinfo pat_rinfo;
+
+               pdt_type = PDT_PAT_NEW;
+               ret = pdc_pat_mem_pdt_info(&pat_rinfo);
+               pdt_status.pdt_size = pat_rinfo.max_pdt_entries;
+               pdt_status.pdt_entries = pat_rinfo.current_pdt_entries;
+               pdt_status.pdt_status = 0;
+               pdt_status.first_dbe_loc = pat_rinfo.first_dbe_loc;
+               pdt_status.good_mem = pat_rinfo.good_mem;
+       } else {
+               pdt_type = PDT_PDC;
+               ret = pdc_mem_pdt_info(&pdt_status);
+       }
+
+       if (ret != PDC_OK) {
+               pdt_type = PDT_NONE;
+               pr_info("PDT: Firmware does not provide any page deallocation"
+                       " information.\n");
+               return;
+       }
+
+       entries = pdt_status.pdt_entries;
+       WARN_ON(entries > MAX_PDT_ENTRIES);
+
+       pr_info("PDT: size %lu, entries %lu, status %lu, dbe_loc 0x%lx,"
+               " good_mem %lu\n",
+                       pdt_status.pdt_size, pdt_status.pdt_entries,
+                       pdt_status.pdt_status, pdt_status.first_dbe_loc,
+                       pdt_status.good_mem);
+
+       if (entries == 0) {
+               pr_info("PDT: Firmware reports all memory OK.\n");
+               return;
+       }
+
+       if (pdt_status.first_dbe_loc &&
+               pdt_status.first_dbe_loc <= __pa((unsigned long)&_end))
+               pr_crit("CRITICAL: Bad memory inside kernel image memory area!\n");
+
+       pr_warn("PDT: Firmware reports %lu entries of faulty memory:\n",
+               entries);
+
+       if (pdt_type == PDT_PDC)
+               ret = pdc_mem_pdt_read_entries(&pdt_read_ret, pdt_entry);
+       else {
+#ifdef CONFIG_64BIT
+               struct pdc_pat_mem_read_pd_retinfo pat_pret;
+
+               ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry,
+                       MAX_PDT_ENTRIES);
+               if (ret != PDC_OK) {
+                       pdt_type = PDT_PAT_OLD;
+                       ret = pdc_pat_mem_read_pd_pdt(&pat_pret, pdt_entry,
+                               MAX_PDT_TABLE_SIZE, 0);
+               }
+#else
+               ret = PDC_BAD_PROC;
+#endif
+       }
+
+       if (ret != PDC_OK) {
+               pdt_type = PDT_NONE;
+               pr_debug("PDT type %d, retval = %d\n", pdt_type, ret);
+               return;
+       }
+
+       for (i = 0; i < pdt_status.pdt_entries; i++) {
+               if (i < 20)
+                       pr_warn("PDT: BAD PAGE #%d at 0x%08lx (error_type = %lu)\n",
+                               i,
+                               pdt_entry[i] & PAGE_MASK,
+                               pdt_entry[i] & 1);
+
+               /* mark memory page bad */
+               memblock_reserve(pdt_entry[i] & PAGE_MASK, PAGE_SIZE);
+       }
+}
index 85de47f4eb594564bc9639ff76a099b5ff9aa8c7..0ab32779dfa737d8b2a8ad341ac6e169f0f66a1e 100644 (file)
@@ -94,7 +94,7 @@ static int processor_probe(struct parisc_device *dev)
        unsigned long txn_addr;
        unsigned long cpuid;
        struct cpuinfo_parisc *p;
-       struct pdc_pat_cpu_num cpu_info __maybe_unused;
+       struct pdc_pat_cpu_num cpu_info = { };
 
 #ifdef CONFIG_SMP
        if (num_online_cpus() >= nr_cpu_ids) {
@@ -113,6 +113,7 @@ static int processor_probe(struct parisc_device *dev)
         */
        cpuid = boot_cpu_data.cpu_count;
        txn_addr = dev->hpa.start;      /* for legacy PDC */
+       cpu_info.cpu_num = cpu_info.cpu_loc = cpuid;
 
 #ifdef CONFIG_64BIT
        if (is_pdc_pat()) {
@@ -180,6 +181,8 @@ static int processor_probe(struct parisc_device *dev)
        p->hpa = dev->hpa.start;        /* save CPU hpa */
        p->cpuid = cpuid;       /* save CPU id */
        p->txn_addr = txn_addr; /* save CPU IRQ address */
+       p->cpu_num = cpu_info.cpu_num;
+       p->cpu_loc = cpu_info.cpu_loc;
 #ifdef CONFIG_SMP
        /*
        ** FIXME: review if any other initialization is clobbered
index 44aeaa9c039fc421421a5b1b7524495e0d225eba..6308749359e4b7d6ee348062d584f7b747f1a115 100644 (file)
        ENTRY_SAME(ni_syscall)  /* 263: reserved for vserver */
        ENTRY_SAME(add_key)
        ENTRY_SAME(request_key)         /* 265 */
-       ENTRY_SAME(keyctl)
+       ENTRY_COMP(keyctl)
        ENTRY_SAME(ioprio_set)
        ENTRY_SAME(ioprio_get)
        ENTRY_SAME(inotify_init)
index 89421df70160833a84614c1336fee443ee9b5351..2d956aa0a38abbc3829757bab4749dd6a0037490 100644 (file)
@@ -243,14 +243,30 @@ void __init time_init(void)
 static int __init init_cr16_clocksource(void)
 {
        /*
-        * The cr16 interval timers are not syncronized across CPUs, so mark
-        * them unstable and lower rating on SMP systems.
+        * The cr16 interval timers are not syncronized across CPUs on
+        * different sockets, so mark them unstable and lower rating on
+        * multi-socket SMP systems.
         */
        if (num_online_cpus() > 1) {
-               clocksource_cr16.flags = CLOCK_SOURCE_UNSTABLE;
-               clocksource_cr16.rating = 0;
+               int cpu;
+               unsigned long cpu0_loc;
+               cpu0_loc = per_cpu(cpu_data, 0).cpu_loc;
+
+               for_each_online_cpu(cpu) {
+                       if (cpu0_loc == per_cpu(cpu_data, cpu).cpu_loc)
+                               continue;
+
+                       clocksource_cr16.name = "cr16_unstable";
+                       clocksource_cr16.flags = CLOCK_SOURCE_UNSTABLE;
+                       clocksource_cr16.rating = 0;
+                       break;
+               }
        }
 
+       /* XXX: We may want to mark sched_clock stable here if cr16 clocks are
+        *      in sync:
+        *      (clocksource_cr16.flags == CLOCK_SOURCE_IS_CONTINUOUS) */
+
        /* register at clocksource framework */
        clocksource_register_hz(&clocksource_cr16,
                100 * PAGE0->mem_10msec);
index 85c28bb80fb7433dfcfac2fb10f6cc121448119f..d4fe19806d57764144e0b52e9feddc83a18fc160 100644 (file)
        mtsp        %r1,%sr1
        .endm
 
-       .macro fixup_branch lbl
-       ldil        L%\lbl, %r1
-       ldo         R%\lbl(%r1), %r1
-       bv          %r0(%r1)
-       .endm
-
        /*
         * unsigned long lclear_user(void *to, unsigned long n)
         *
@@ -82,16 +76,16 @@ $lclu_loop:
 $lclu_done:
        bv          %r0(%r2)
        copy        %r25,%r28
-       .exit
-ENDPROC_CFI(lclear_user)
 
-       .section .fixup,"ax"
-2:      fixup_branch $lclu_done
-       ldo        1(%r25),%r25
-       .previous
+2:     b           $lclu_done
+       ldo         1(%r25),%r25
 
        ASM_EXCEPTIONTABLE_ENTRY(1b,2b)
 
+       .exit
+ENDPROC_CFI(lclear_user)
+
+
        .procend
 
        /*
@@ -122,16 +116,15 @@ $lslen_done:
 $lslen_nzero:
        b           $lslen_done
        ldo         1(%r26),%r26 /* special case for N == 0 */
-ENDPROC_CFI(lstrnlen_user)
 
-       .section .fixup,"ax"
-3:      fixup_branch $lslen_done
+3:      b          $lslen_done
        copy        %r24,%r26    /* reset r26 so 0 is returned on fault */
-       .previous
 
        ASM_EXCEPTIONTABLE_ENTRY(1b,3b)
        ASM_EXCEPTIONTABLE_ENTRY(2b,3b)
 
+ENDPROC_CFI(lstrnlen_user)
+
        .procend
 
 
index 32ec22146141e56f9436bfbdfaccf0256820a552..5b101f6a5607dc1ef9e229b6f2421a78d35355ce 100644 (file)
@@ -29,8 +29,6 @@
 #define BITSSET                0x1c0   /* for identifying LDCW */
 
 
-DEFINE_PER_CPU(struct exception_data, exception_data);
-
 int show_unhandled_signals = 1;
 
 /*
@@ -143,13 +141,6 @@ int fixup_exception(struct pt_regs *regs)
 
        fix = search_exception_tables(regs->iaoq[0]);
        if (fix) {
-               struct exception_data *d;
-               d = this_cpu_ptr(&exception_data);
-               d->fault_ip = regs->iaoq[0];
-               d->fault_gp = regs->gr[27];
-               d->fault_space = regs->isr;
-               d->fault_addr = regs->ior;
-
                /*
                 * Fix up get_user() and put_user().
                 * ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant
@@ -163,6 +154,7 @@ int fixup_exception(struct pt_regs *regs)
                        /* zero target register for get_user() */
                        if (parisc_acctyp(0, regs->iir) == VM_READ) {
                                int treg = regs->iir & 0x1f;
+                               BUG_ON(treg == 0);
                                regs->gr[treg] = 0;
                        }
                }
@@ -367,7 +359,7 @@ bad_area:
                case 15:        /* Data TLB miss fault/Data page fault */
                        /* send SIGSEGV when outside of vma */
                        if (!vma ||
-                           address < vma->vm_start || address > vma->vm_end) {
+                           address < vma->vm_start || address >= vma->vm_end) {
                                si.si_signo = SIGSEGV;
                                si.si_code = SEGV_MAPERR;
                                break;
index 66f3a63451056e9bd26fbfe3ed914b0ab333b674..1ca9a2b4239fba17f8e524453b04ec7757243b3b 100644 (file)
@@ -381,6 +381,9 @@ static void __init setup_bootmem(void)
                request_resource(res, &data_resource);
        }
        request_resource(&sysram_resources[0], &pdcdata_resource);
+
+       /* Initialize Page Deallocation Table (PDT) and check for bad memory. */
+       pdc_pdt_init();
 }
 
 static int __init parisc_text_address(unsigned long vaddr)
index e32ca2ef9e54047cd527cd32f3667103de8d4a40..56c93f096de95c3b8f465bfc8bf7b7d8811cab64 100644 (file)
@@ -741,6 +741,8 @@ ccio_map_single(struct device *dev, void *addr, size_t size,
 
        BUG_ON(!dev);
        ioc = GET_IOC(dev);
+       if (!ioc)
+               return DMA_ERROR_CODE;
 
        BUG_ON(size <= 0);
 
@@ -814,6 +816,10 @@ ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
        
        BUG_ON(!dev);
        ioc = GET_IOC(dev);
+       if (!ioc) {
+               WARN_ON(!ioc);
+               return;
+       }
 
        DBG_RUN("%s() iovp 0x%lx/%x\n",
                __func__, (long)iova, size);
@@ -918,6 +924,8 @@ ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
        
        BUG_ON(!dev);
        ioc = GET_IOC(dev);
+       if (!ioc)
+               return 0;
        
        DBG_RUN_SG("%s() START %d entries\n", __func__, nents);
 
@@ -990,6 +998,10 @@ ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
 
        BUG_ON(!dev);
        ioc = GET_IOC(dev);
+       if (!ioc) {
+               WARN_ON(!ioc);
+               return;
+       }
 
        DBG_RUN_SG("%s() START %d entries, %p,%x\n",
                __func__, nents, sg_virt(sglist), sglist->length);
index 1133b5cc88ca333b240d8d5ce08535273f2e6d93..5c63b920b4713eaf41954c4e8f0d041472c78b6d 100644 (file)
@@ -154,7 +154,10 @@ struct dino_device
 };
 
 /* Looks nice and keeps the compiler happy */
-#define DINO_DEV(d) ((struct dino_device *) d)
+#define DINO_DEV(d) ({                         \
+       void *__pdata = d;                      \
+       BUG_ON(!__pdata);                       \
+       (struct dino_device *)__pdata; })
 
 
 /*
index 2ec2aef4d2115a24ce14a5faa31d5b6e35acae57..bc286cbbbc9b26d12d6fb57473a2d0c7051d97d6 100644 (file)
@@ -111,8 +111,10 @@ static u32 lba_t32;
 
 
 /* Looks nice and keeps the compiler happy */
-#define LBA_DEV(d) ((struct lba_device *) (d))
-
+#define LBA_DEV(d) ({                          \
+       void *__pdata = d;                      \
+       BUG_ON(!__pdata);                       \
+       (struct lba_device *)__pdata; })
 
 /*
 ** Only allow 8 subsidiary busses per LBA
index 33385e57443382ba090cd4a1e41b5bdf4af58936..87ad5fd6a7a2b77d9768e63991c2e14541e486a7 100644 (file)
@@ -691,6 +691,8 @@ static int sba_dma_supported( struct device *dev, u64 mask)
                return 0;
 
        ioc = GET_IOC(dev);
+       if (!ioc)
+               return 0;
 
        /*
         * check if mask is >= than the current max IO Virt Address
@@ -722,6 +724,8 @@ sba_map_single(struct device *dev, void *addr, size_t size,
        int pide;
 
        ioc = GET_IOC(dev);
+       if (!ioc)
+               return DMA_ERROR_CODE;
 
        /* save offset bits */
        offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK;
@@ -813,6 +817,10 @@ sba_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
        DBG_RUN("%s() iovp 0x%lx/%x\n", __func__, (long) iova, size);
 
        ioc = GET_IOC(dev);
+       if (!ioc) {
+               WARN_ON(!ioc);
+               return;
+       }
        offset = iova & ~IOVP_MASK;
        iova ^= offset;        /* clear offset bits */
        size += offset;
@@ -952,6 +960,8 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
        DBG_RUN_SG("%s() START %d entries\n", __func__, nents);
 
        ioc = GET_IOC(dev);
+       if (!ioc)
+               return 0;
 
        /* Fast path single entry scatterlists. */
        if (nents == 1) {
@@ -1037,6 +1047,10 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
                __func__, nents, sg_virt(sglist), sglist->length);
 
        ioc = GET_IOC(dev);
+       if (!ioc) {
+               WARN_ON(!ioc);
+               return;
+       }
 
 #ifdef SBA_COLLECT_STATS
        ioc->usg_calls++;