Commit e5859eb8 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'parisc-4.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux

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
parents 058e88d3 33f9e024
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
** flush/purge and allocate "regular" cacheable pages for everything. ** flush/purge and allocate "regular" cacheable pages for everything.
*/ */
#define DMA_ERROR_CODE (~(dma_addr_t)0)
#ifdef CONFIG_PA11 #ifdef CONFIG_PA11
extern const struct dma_map_ops pcxl_dma_ops; extern const struct dma_map_ops pcxl_dma_ops;
extern const struct dma_map_ops pcx_dma_ops; extern const struct dma_map_ops pcx_dma_ops;
...@@ -54,12 +56,13 @@ parisc_walk_tree(struct device *dev) ...@@ -54,12 +56,13 @@ parisc_walk_tree(struct device *dev)
break; break;
} }
} }
BUG_ON(!dev->platform_data);
return 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 #ifdef CONFIG_IOMMU_CCIO
struct parisc_device; struct parisc_device;
......
...@@ -34,10 +34,10 @@ static inline unsigned char gsc_readb(unsigned long addr) ...@@ -34,10 +34,10 @@ static inline unsigned char gsc_readb(unsigned long addr)
unsigned char ret; unsigned char ret;
__asm__ __volatile__( __asm__ __volatile__(
" rsm 2,%0\n" " rsm %3,%0\n"
" ldbx 0(%2),%1\n" " ldbx 0(%2),%1\n"
" mtsm %0\n" " mtsm %0\n"
: "=&r" (flags), "=r" (ret) : "r" (addr) ); : "=&r" (flags), "=r" (ret) : "r" (addr), "i" (PSW_SM_D) );
return ret; return ret;
} }
...@@ -48,10 +48,10 @@ static inline unsigned short gsc_readw(unsigned long addr) ...@@ -48,10 +48,10 @@ static inline unsigned short gsc_readw(unsigned long addr)
unsigned short ret; unsigned short ret;
__asm__ __volatile__( __asm__ __volatile__(
" rsm 2,%0\n" " rsm %3,%0\n"
" ldhx 0(%2),%1\n" " ldhx 0(%2),%1\n"
" mtsm %0\n" " mtsm %0\n"
: "=&r" (flags), "=r" (ret) : "r" (addr) ); : "=&r" (flags), "=r" (ret) : "r" (addr), "i" (PSW_SM_D) );
return ret; return ret;
} }
...@@ -87,20 +87,20 @@ static inline void gsc_writeb(unsigned char val, unsigned long addr) ...@@ -87,20 +87,20 @@ static inline void gsc_writeb(unsigned char val, unsigned long addr)
{ {
long flags; long flags;
__asm__ __volatile__( __asm__ __volatile__(
" rsm 2,%0\n" " rsm %3,%0\n"
" stbs %1,0(%2)\n" " stbs %1,0(%2)\n"
" mtsm %0\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) static inline void gsc_writew(unsigned short val, unsigned long addr)
{ {
long flags; long flags;
__asm__ __volatile__( __asm__ __volatile__(
" rsm 2,%0\n" " rsm %3,%0\n"
" sths %1,0(%2)\n" " sths %1,0(%2)\n"
" mtsm %0\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) static inline void gsc_writel(unsigned int val, unsigned long addr)
......
...@@ -49,15 +49,26 @@ static inline void load_context(mm_context_t context) ...@@ -49,15 +49,26 @@ static inline void load_context(mm_context_t context)
mtctl(__space_to_prot(context), 8); 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) { if (prev != next) {
mtctl(__pa(next->pgd), 25); mtctl(__pa(next->pgd), 25);
load_context(next->context); 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) #define deactivate_mm(tsk,mm) do { } while (0)
static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#if !defined(__ASSEMBLY__) #if !defined(__ASSEMBLY__)
extern int pdc_type; 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 */ /* Values for pdc_type */
#define PDC_TYPE_ILLEGAL -1 #define PDC_TYPE_ILLEGAL -1
...@@ -143,6 +145,18 @@ struct pdc_btlb_info { /* PDC_BLOCK_TLB, return of PDC_BTLB_INFO */ ...@@ -143,6 +145,18 @@ struct pdc_btlb_info { /* PDC_BLOCK_TLB, return of PDC_BTLB_INFO */
#endif /* !CONFIG_PA20 */ #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 #ifdef CONFIG_64BIT
struct pdc_memory_table_raddr { /* PDC_MEM/PDC_MEM_TABLE (return info) */ struct pdc_memory_table_raddr { /* PDC_MEM/PDC_MEM_TABLE (return info) */
unsigned long entries_returned; unsigned long entries_returned;
...@@ -301,6 +315,10 @@ int pdc_get_initiator(struct hardware_path *, struct pdc_initiator *); ...@@ -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_read(struct pdc_tod *tod);
int pdc_tod_set(unsigned long sec, unsigned long usec); 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 #ifdef CONFIG_64BIT
int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr, int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr,
struct pdc_memory_table *tbl, unsigned long entries); struct pdc_memory_table *tbl, unsigned long entries);
......
...@@ -147,9 +147,9 @@ ...@@ -147,9 +147,9 @@
#define PDC_PAT_MEM_CELL_CLEAR 6L /* Clear PDT For Cell */ #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_READ 7L /* Read PDT entries For Cell */
#define PDC_PAT_MEM_CELL_RESET 8L /* Reset clear bit 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_SETGM 9L /* Set Good Memory value */
#define PDC_PAT_MEM_ADD_PAGE 10L /* ADDs a page to the cell */ #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_ADDRESS 11L /* Get Physical Location From */
/* Memory Address */ /* Memory Address */
#define PDC_PAT_MEM_GET_TXT_SIZE 12L /* Get Formatted Text Size */ #define PDC_PAT_MEM_GET_TXT_SIZE 12L /* Get Formatted Text Size */
#define PDC_PAT_MEM_GET_PD_TXT 13L /* Get PD Formatted Text */ #define PDC_PAT_MEM_GET_PD_TXT 13L /* Get PD Formatted Text */
...@@ -212,6 +212,23 @@ struct pdc_pat_cpu_num { ...@@ -212,6 +212,23 @@ struct pdc_pat_cpu_num {
unsigned long cpu_loc; 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 { struct pdc_pat_pd_addr_map_entry {
unsigned char entry_type; /* 1 = Memory Descriptor Entry Type */ unsigned char entry_type; /* 1 = Memory Descriptor Entry Type */
unsigned char reserve1[5]; unsigned char reserve1[5];
...@@ -293,15 +310,15 @@ extern int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, unsigned lon ...@@ -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_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_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); extern int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val);
extern int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo);
/* Flag to indicate this is a PAT box...don't use this unless you extern int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret,
** really have to...it might go away some day. 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,
extern int pdc_pat; /* arch/parisc/kernel/inventory.c */ unsigned long *pdt_entries_ptr, unsigned long count,
unsigned long offset);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
......
...@@ -511,6 +511,9 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, ...@@ -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)) #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__ */ #endif /* !__ASSEMBLY__ */
......
...@@ -103,6 +103,8 @@ struct cpuinfo_parisc { ...@@ -103,6 +103,8 @@ struct cpuinfo_parisc {
unsigned long bh_count; /* number of times bh was invoked */ unsigned long bh_count; /* number of times bh was invoked */
unsigned long fp_rev; unsigned long fp_rev;
unsigned long fp_model; 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; unsigned int state;
struct parisc_device *dev; struct parisc_device *dev;
unsigned long loops_per_jiffy; unsigned long loops_per_jiffy;
......
...@@ -68,17 +68,6 @@ struct exception_table_entry { ...@@ -68,17 +68,6 @@ struct exception_table_entry {
#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr )\ #define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr )\
ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1) 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 * 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 * get_fs() - with either a value of 0 to access kernel space (KERNEL_DS which
......
...@@ -131,12 +131,12 @@ ...@@ -131,12 +131,12 @@
#define PDC_TLB_SETUP 1 /* set up miss handling */ #define PDC_TLB_SETUP 1 /* set up miss handling */
#define PDC_MEM 20 /* Manage memory */ #define PDC_MEM 20 /* Manage memory */
#define PDC_MEM_MEMINFO 0 #define PDC_MEM_MEMINFO 0 /* Return PDT info */
#define PDC_MEM_ADD_PAGE 1 #define PDC_MEM_ADD_PAGE 1 /* Add page to PDT */
#define PDC_MEM_CLEAR_PDT 2 #define PDC_MEM_CLEAR_PDT 2 /* Clear PDT */
#define PDC_MEM_READ_PDT 3 #define PDC_MEM_READ_PDT 3 /* Read PDT entry */
#define PDC_MEM_RESET_CLEAR 4 #define PDC_MEM_RESET_CLEAR 4 /* Reset PDT clear flag */
#define PDC_MEM_GOODMEM 5 #define PDC_MEM_GOODMEM 5 /* Set good_mem value */
#define PDC_MEM_TABLE 128 /* Non contig mem map (sprockets) */ #define PDC_MEM_TABLE 128 /* Non contig mem map (sprockets) */
#define PDC_MEM_RETURN_ADDRESS_TABLE PDC_MEM_TABLE #define PDC_MEM_RETURN_ADDRESS_TABLE PDC_MEM_TABLE
#define PDC_MEM_GET_MEMORY_SYSTEM_TABLES_SIZE 131 #define PDC_MEM_GET_MEMORY_SYSTEM_TABLES_SIZE 131
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
extra-y := head.o vmlinux.lds 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 \ pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \
ptrace.o hardware.o inventory.o drivers.o \ ptrace.o hardware.o inventory.o drivers.o \
signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \ signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
......
...@@ -297,11 +297,6 @@ int main(void) ...@@ -297,11 +297,6 @@ int main(void)
#else #else
DEFINE(HUGEPAGE_SIZE, PAGE_SIZE); DEFINE(HUGEPAGE_SIZE, PAGE_SIZE);
#endif #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(); BLANK();
DEFINE(ASM_PDC_RESULT_SIZE, NUM_PDC_RESULT * sizeof(unsigned long)); DEFINE(ASM_PDC_RESULT_SIZE, NUM_PDC_RESULT * sizeof(unsigned long));
BLANK(); BLANK();
......
...@@ -957,6 +957,41 @@ int pdc_tod_read(struct pdc_tod *tod) ...@@ -957,6 +957,41 @@ int pdc_tod_read(struct pdc_tod *tod)
} }
EXPORT_SYMBOL(pdc_tod_read); 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. * pdc_tod_set - Set the Time-Of-Day clock.
* @sec: The number of seconds since epoch. * @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) ...@@ -1383,6 +1418,79 @@ int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val)
return retval; 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 */ #endif /* CONFIG_64BIT */
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <asm/assembly.h> #include <asm/assembly.h>
#include <asm/pdc.h> #include <asm/pdc.h>
#include <asm/psw.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -135,7 +136,7 @@ ENTRY_CFI(os_hpmc) ...@@ -135,7 +136,7 @@ ENTRY_CFI(os_hpmc)
* So turn on the Q bit and turn off the M bit. * 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 %r4,ipsw
mtctl %r0,pcsq mtctl %r0,pcsq
mtctl %r0,pcsq mtctl %r0,pcsq
...@@ -257,7 +258,7 @@ os_hpmc_5: ...@@ -257,7 +258,7 @@ os_hpmc_5:
tovirt_r1 %r30 /* make sp virtual */ 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 */ ldi 1,%r8 /* Set trap code to "1" for HPMC */
load32 PA(intr_save),%r1 load32 PA(intr_save),%r1
be 0(%sr7,%r1) be 0(%sr7,%r1)
......
...@@ -40,6 +40,11 @@ ...@@ -40,6 +40,11 @@
int pdc_type __read_mostly = PDC_TYPE_ILLEGAL; 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) void __init setup_pdc(void)
{ {
long status; long status;
...@@ -78,6 +83,10 @@ void __init setup_pdc(void) ...@@ -78,6 +83,10 @@ void __init setup_pdc(void)
if (status == PDC_OK) { if (status == PDC_OK) {
pdc_type = PDC_TYPE_PAT; pdc_type = PDC_TYPE_PAT;
pr_cont("64 bit PAT.\n"); 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; return;
} }
#endif #endif
......
/*
* 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);
}
}
...@@ -94,7 +94,7 @@ static int processor_probe(struct parisc_device *dev) ...@@ -94,7 +94,7 @@ static int processor_probe(struct parisc_device *dev)
unsigned long txn_addr; unsigned long txn_addr;
unsigned long cpuid; unsigned long cpuid;
struct cpuinfo_parisc *p; struct cpuinfo_parisc *p;
struct pdc_pat_cpu_num cpu_info __maybe_unused; struct pdc_pat_cpu_num cpu_info = { };
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
if (num_online_cpus() >= nr_cpu_ids) { if (num_online_cpus() >= nr_cpu_ids) {
...@@ -113,6 +113,7 @@ static int processor_probe(struct parisc_device *dev) ...@@ -113,6 +113,7 @@ static int processor_probe(struct parisc_device *dev)
*/ */
cpuid = boot_cpu_data.cpu_count; cpuid = boot_cpu_data.cpu_count;
txn_addr = dev->hpa.start; /* for legacy PDC */ txn_addr = dev->hpa.start; /* for legacy PDC */
cpu_info.cpu_num = cpu_info.cpu_loc = cpuid;
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
if (is_pdc_pat()) { if (is_pdc_pat()) {
...@@ -180,6 +181,8 @@ static int processor_probe(struct parisc_device *dev) ...@@ -180,6 +181,8 @@ static int processor_probe(struct parisc_device *dev)
p->hpa = dev->hpa.start; /* save CPU hpa */ p->hpa = dev->hpa.start; /* save CPU hpa */
p->cpuid = cpuid; /* save CPU id */ p->cpuid = cpuid; /* save CPU id */
p->txn_addr = txn_addr; /* save CPU IRQ address */ 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 #ifdef CONFIG_SMP
/* /*
** FIXME: review if any other initialization is clobbered ** FIXME: review if any other initialization is clobbered
......
...@@ -361,7 +361,7 @@ ...@@ -361,7 +361,7 @@
ENTRY_SAME(ni_syscall) /* 263: reserved for vserver */ ENTRY_SAME(ni_syscall) /* 263: reserved for vserver */
ENTRY_SAME(add_key) ENTRY_SAME(add_key)
ENTRY_SAME(request_key) /* 265 */ ENTRY_SAME(request_key) /* 265 */
ENTRY_SAME(keyctl) ENTRY_COMP(keyctl)
ENTRY_SAME(ioprio_set) ENTRY_SAME(ioprio_set)
ENTRY_SAME(ioprio_get) ENTRY_SAME(ioprio_get)
ENTRY_SAME(inotify_init) ENTRY_SAME(inotify_init)
......
...@@ -243,14 +243,30 @@ void __init time_init(void) ...@@ -243,14 +243,30 @@ void __init time_init(void)
static int __init init_cr16_clocksource(void) static int __init init_cr16_clocksource(void)
{ {
/* /*
* The cr16 interval timers are not syncronized across CPUs, so mark * The cr16 interval timers are not syncronized across CPUs on
* them unstable and lower rating on SMP systems. * different sockets, so mark them unstable and lower rating on
* multi-socket SMP systems.
*/ */
if (num_online_cpus() > 1) { if (num_online_cpus() > 1) {
clocksource_cr16.flags = CLOCK_SOURCE_UNSTABLE; int cpu;
clocksource_cr16.rating = 0; 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 */ /* register at clocksource framework */
clocksource_register_hz(&clocksource_cr16, clocksource_register_hz(&clocksource_cr16,
100 * PAGE0->mem_10msec); 100 * PAGE0->mem_10msec);
......
...@@ -56,12 +56,6 @@ ...@@ -56,12 +56,6 @@
mtsp %r1,%sr1 mtsp %r1,%sr1
.endm .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) * unsigned long lclear_user(void *to, unsigned long n)
* *
...@@ -82,16 +76,16 @@ $lclu_loop: ...@@ -82,16 +76,16 @@ $lclu_loop:
$lclu_done: $lclu_done:
bv %r0(%r2) bv %r0(%r2)
copy %r25,%r28 copy %r25,%r28
.exit
ENDPROC_CFI(lclear_user)
.section .fixup,"ax" 2: b $lclu_done
2: fixup_branch $lclu_done ldo 1(%r25),%r25
ldo 1(%r25),%r25
.previous
ASM_EXCEPTIONTABLE_ENTRY(1b,2b) ASM_EXCEPTIONTABLE_ENTRY(1b,2b)
.exit
ENDPROC_CFI(lclear_user)
.procend .procend
/* /*
...@@ -122,16 +116,15 @@ $lslen_done: ...@@ -122,16 +116,15 @@ $lslen_done:
$lslen_nzero: $lslen_nzero:
b $lslen_done b $lslen_done
ldo 1(%r26),%r26 /* special case for N == 0 */ ldo 1(%r26),%r26 /* special case for N == 0 */
ENDPROC_CFI(lstrnlen_user)
.section .fixup,"ax" 3: b $lslen_done
3: fixup_branch $lslen_done
copy %r24,%r26 /* reset r26 so 0 is returned on fault */ copy %r24,%r26 /* reset r26 so 0 is returned on fault */
.previous
ASM_EXCEPTIONTABLE_ENTRY(1b,3b) ASM_EXCEPTIONTABLE_ENTRY(1b,3b)
ASM_EXCEPTIONTABLE_ENTRY(2b,3b) ASM_EXCEPTIONTABLE_ENTRY(2b,3b)
ENDPROC_CFI(lstrnlen_user)
.procend .procend
......
...@@ -29,8 +29,6 @@ ...@@ -29,8 +29,6 @@
#define BITSSET 0x1c0 /* for identifying LDCW */ #define BITSSET 0x1c0 /* for identifying LDCW */
DEFINE_PER_CPU(struct exception_data, exception_data);
int show_unhandled_signals = 1; int show_unhandled_signals = 1;
/* /*
...@@ -143,13 +141,6 @@ int fixup_exception(struct pt_regs *regs) ...@@ -143,13 +141,6 @@ int fixup_exception(struct pt_regs *regs)
fix = search_exception_tables(regs->iaoq[0]); fix = search_exception_tables(regs->iaoq[0]);
if (fix) { 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(). * Fix up get_user() and put_user().
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant * ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant
...@@ -163,6 +154,7 @@ int fixup_exception(struct pt_regs *regs) ...@@ -163,6 +154,7 @@ int fixup_exception(struct pt_regs *regs)
/* zero target register for get_user() */ /* zero target register for get_user() */
if (parisc_acctyp(0, regs->iir) == VM_READ) { if (parisc_acctyp(0, regs->iir) == VM_READ) {
int treg = regs->iir & 0x1f; int treg = regs->iir & 0x1f;
BUG_ON(treg == 0);
regs->gr[treg] = 0; regs->gr[treg] = 0;
} }
} }
...@@ -367,7 +359,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long code, ...@@ -367,7 +359,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long code,
case 15: /* Data TLB miss fault/Data page fault */ case 15: /* Data TLB miss fault/Data page fault */
/* send SIGSEGV when outside of vma */ /* send SIGSEGV when outside of vma */
if (!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_signo = SIGSEGV;
si.si_code = SEGV_MAPERR; si.si_code = SEGV_MAPERR;
break; break;
......
...@@ -381,6 +381,9 @@ static void __init setup_bootmem(void) ...@@ -381,6 +381,9 @@ static void __init setup_bootmem(void)
request_resource(res, &data_resource); request_resource(res, &data_resource);
} }
request_resource(&sysram_resources[0], &pdcdata_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) static int __init parisc_text_address(unsigned long vaddr)
......
...@@ -741,6 +741,8 @@ ccio_map_single(struct device *dev, void *addr, size_t size, ...@@ -741,6 +741,8 @@ ccio_map_single(struct device *dev, void *addr, size_t size,
BUG_ON(!dev); BUG_ON(!dev);
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc)
return DMA_ERROR_CODE;
BUG_ON(size <= 0); BUG_ON(size <= 0);
...@@ -814,6 +816,10 @@ ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size, ...@@ -814,6 +816,10 @@ ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
BUG_ON(!dev); BUG_ON(!dev);
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc) {
WARN_ON(!ioc);
return;
}
DBG_RUN("%s() iovp 0x%lx/%x\n", DBG_RUN("%s() iovp 0x%lx/%x\n",
__func__, (long)iova, size); __func__, (long)iova, size);
...@@ -918,6 +924,8 @@ ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents, ...@@ -918,6 +924,8 @@ ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
BUG_ON(!dev); BUG_ON(!dev);
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc)
return 0;
DBG_RUN_SG("%s() START %d entries\n", __func__, nents); 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, ...@@ -990,6 +998,10 @@ ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
BUG_ON(!dev); BUG_ON(!dev);
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc) {
WARN_ON(!ioc);
return;
}
DBG_RUN_SG("%s() START %d entries, %p,%x\n", DBG_RUN_SG("%s() START %d entries, %p,%x\n",
__func__, nents, sg_virt(sglist), sglist->length); __func__, nents, sg_virt(sglist), sglist->length);
......
...@@ -154,7 +154,10 @@ struct dino_device ...@@ -154,7 +154,10 @@ struct dino_device
}; };
/* Looks nice and keeps the compiler happy */ /* 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; })
/* /*
......
...@@ -111,8 +111,10 @@ static u32 lba_t32; ...@@ -111,8 +111,10 @@ static u32 lba_t32;
/* Looks nice and keeps the compiler happy */ /* 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 ** Only allow 8 subsidiary busses per LBA
......
...@@ -691,6 +691,8 @@ static int sba_dma_supported( struct device *dev, u64 mask) ...@@ -691,6 +691,8 @@ static int sba_dma_supported( struct device *dev, u64 mask)
return 0; return 0;
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc)
return 0;
/* /*
* check if mask is >= than the current max IO Virt Address * 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, ...@@ -722,6 +724,8 @@ sba_map_single(struct device *dev, void *addr, size_t size,
int pide; int pide;
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc)
return DMA_ERROR_CODE;
/* save offset bits */ /* save offset bits */
offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK; 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, ...@@ -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); DBG_RUN("%s() iovp 0x%lx/%x\n", __func__, (long) iova, size);
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc) {
WARN_ON(!ioc);
return;
}
offset = iova & ~IOVP_MASK; offset = iova & ~IOVP_MASK;
iova ^= offset; /* clear offset bits */ iova ^= offset; /* clear offset bits */
size += offset; size += offset;
...@@ -952,6 +960,8 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents, ...@@ -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); DBG_RUN_SG("%s() START %d entries\n", __func__, nents);
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc)
return 0;
/* Fast path single entry scatterlists. */ /* Fast path single entry scatterlists. */
if (nents == 1) { if (nents == 1) {
...@@ -1037,6 +1047,10 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, ...@@ -1037,6 +1047,10 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
__func__, nents, sg_virt(sglist), sglist->length); __func__, nents, sg_virt(sglist), sglist->length);
ioc = GET_IOC(dev); ioc = GET_IOC(dev);
if (!ioc) {
WARN_ON(!ioc);
return;
}
#ifdef SBA_COLLECT_STATS #ifdef SBA_COLLECT_STATS
ioc->usg_calls++; ioc->usg_calls++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment