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
This commit is contained in:
commit
e5859eb845
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -147,9 +147,9 @@
|
||||
#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__ */
|
||||
|
||||
|
@ -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__ */
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -131,12 +131,12 @@
|
||||
#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
|
||||
|
@ -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 \
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -40,6 +40,11 @@
|
||||
|
||||
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
|
||||
|
143
arch/parisc/kernel/pdt.c
Normal file
143
arch/parisc/kernel/pdt.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -361,7 +361,7 @@
|
||||
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)
|
||||
|
@ -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);
|
||||
|
@ -56,12 +56,6 @@
|
||||
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,15 +76,15 @@ $lclu_loop:
|
||||
$lclu_done:
|
||||
bv %r0(%r2)
|
||||
copy %r25,%r28
|
||||
|
||||
2: b $lclu_done
|
||||
ldo 1(%r25),%r25
|
||||
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,2b)
|
||||
|
||||
.exit
|
||||
ENDPROC_CFI(lclear_user)
|
||||
|
||||
.section .fixup,"ax"
|
||||
2: fixup_branch $lclu_done
|
||||
ldo 1(%r25),%r25
|
||||
.previous
|
||||
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,2b)
|
||||
|
||||
.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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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; })
|
||||
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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++;
|
||||
|
Loading…
Reference in New Issue
Block a user