aboutsummaryrefslogtreecommitdiff
path: root/arch/parisc/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/parisc/kernel')
-rw-r--r--arch/parisc/kernel/.gitignore1
-rw-r--r--arch/parisc/kernel/Makefile29
-rw-r--r--arch/parisc/kernel/asm-offsets.c38
-rw-r--r--arch/parisc/kernel/audit.c81
-rw-r--r--arch/parisc/kernel/binfmt_elf32.c28
-rw-r--r--arch/parisc/kernel/cache.c376
-rw-r--r--arch/parisc/kernel/compat_audit.c40
-rw-r--r--arch/parisc/kernel/drivers.c115
-rw-r--r--arch/parisc/kernel/entry.S1258
-rw-r--r--arch/parisc/kernel/firmware.c477
-rw-r--r--arch/parisc/kernel/ftrace.c185
-rw-r--r--arch/parisc/kernel/hardware.c42
-rw-r--r--arch/parisc/kernel/head.S59
-rw-r--r--arch/parisc/kernel/hpmc.S25
-rw-r--r--arch/parisc/kernel/init_task.c76
-rw-r--r--arch/parisc/kernel/inventory.c66
-rw-r--r--arch/parisc/kernel/ioctl32.c606
-rw-r--r--arch/parisc/kernel/irq.c447
-rw-r--r--arch/parisc/kernel/module.c352
-rw-r--r--arch/parisc/kernel/pacache.S728
-rw-r--r--arch/parisc/kernel/parisc_ksyms.c59
-rw-r--r--arch/parisc/kernel/pci-dma.c148
-rw-r--r--arch/parisc/kernel/pci.c180
-rw-r--r--arch/parisc/kernel/pdc_chassis.c121
-rw-r--r--arch/parisc/kernel/pdc_cons.c169
-rw-r--r--arch/parisc/kernel/perf.c96
-rw-r--r--arch/parisc/kernel/perf_asm.S29
-rw-r--r--arch/parisc/kernel/perf_images.h4
-rw-r--r--arch/parisc/kernel/process.c259
-rw-r--r--arch/parisc/kernel/processor.c157
-rw-r--r--arch/parisc/kernel/ptrace.c504
-rw-r--r--arch/parisc/kernel/real2.S38
-rw-r--r--arch/parisc/kernel/semaphore.c102
-rw-r--r--arch/parisc/kernel/setup.c84
-rw-r--r--arch/parisc/kernel/signal.c402
-rw-r--r--arch/parisc/kernel/signal32.c235
-rw-r--r--arch/parisc/kernel/signal32.h60
-rw-r--r--arch/parisc/kernel/smp.c491
-rw-r--r--arch/parisc/kernel/stacktrace.c63
-rw-r--r--arch/parisc/kernel/sys32.h48
-rw-r--r--arch/parisc/kernel/sys_parisc.c309
-rw-r--r--arch/parisc/kernel/sys_parisc32.c701
-rw-r--r--arch/parisc/kernel/syscall.S278
-rw-r--r--arch/parisc/kernel/syscall_table.S165
-rw-r--r--arch/parisc/kernel/time.c327
-rw-r--r--arch/parisc/kernel/topology.c8
-rw-r--r--arch/parisc/kernel/traps.c399
-rw-r--r--arch/parisc/kernel/unaligned.c159
-rw-r--r--arch/parisc/kernel/unwind.c116
-rw-r--r--arch/parisc/kernel/vmlinux.lds.S275
50 files changed, 5250 insertions, 5765 deletions
diff --git a/arch/parisc/kernel/.gitignore b/arch/parisc/kernel/.gitignore
new file mode 100644
index 00000000000..c5f676c3c22
--- /dev/null
+++ b/arch/parisc/kernel/.gitignore
@@ -0,0 +1 @@
+vmlinux.lds
diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile
index 171f9c239f6..ff87b4603e3 100644
--- a/arch/parisc/kernel/Makefile
+++ b/arch/parisc/kernel/Makefile
@@ -2,23 +2,36 @@
# Makefile for arch/parisc/kernel
#
-extra-y := init_task.o head.o vmlinux.lds
-
-AFLAGS_entry.o := -traditional
-AFLAGS_pacache.o := -traditional
-CFLAGS_ioctl32.o := -Ifs/
+extra-y := head.o vmlinux.lds
obj-y := cache.o pacache.o setup.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 semaphore.o \
+ ptrace.o hardware.o inventory.o drivers.o \
signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \
topology.o
+ifdef CONFIG_FUNCTION_TRACER
+# Do not profile debug and lowlevel utilities
+CFLAGS_REMOVE_ftrace.o = -pg
+CFLAGS_REMOVE_cache.o = -pg
+CFLAGS_REMOVE_irq.o = -pg
+CFLAGS_REMOVE_pacache.o = -pg
+CFLAGS_REMOVE_perf.o = -pg
+CFLAGS_REMOVE_traps.o = -pg
+CFLAGS_REMOVE_unaligned.o = -pg
+CFLAGS_REMOVE_unwind.o = -pg
+endif
+
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PA11) += pci-dma.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_MODULES) += module.o
-obj-$(CONFIG_64BIT) += binfmt_elf32.o sys_parisc32.o ioctl32.o signal32.o
+obj-$(CONFIG_64BIT) += binfmt_elf32.o sys_parisc32.o signal32.o
+obj-$(CONFIG_STACKTRACE)+= stacktrace.o
+obj-$(CONFIG_AUDIT) += audit.o
+obj64-$(CONFIG_AUDIT) += compat_audit.o
# only supported for PCX-W/U in 64-bit mode at the moment
-obj-$(CONFIG_64BIT) += perf.o perf_asm.o
+obj-$(CONFIG_64BIT) += perf.o perf_asm.o $(obj64-y)
+obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
+obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c
index 1ad44f92d6e..dcd55103a4b 100644
--- a/arch/parisc/kernel/asm-offsets.c
+++ b/arch/parisc/kernel/asm-offsets.c
@@ -30,9 +30,9 @@
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/thread_info.h>
-#include <linux/version.h>
#include <linux/ptrace.h>
#include <linux/hardirq.h>
+#include <linux/kbuild.h>
#include <asm/pgtable.h>
#include <asm/ptrace.h>
@@ -40,22 +40,21 @@
#include <asm/pdc.h>
#include <asm/uaccess.h>
-#define DEFINE(sym, val) \
- asm volatile("\n->" #sym " %0 " #val : : "i" (val))
-
-#define BLANK() asm volatile("\n->" : : )
-
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
#define FRAME_SIZE 128
#else
#define FRAME_SIZE 64
#endif
+#define FRAME_ALIGN 64
-#define align(x,y) (((x)+FRAME_SIZE+(y)-1) - (((x)+(y)-1)%(y)))
+/* Add FRAME_SIZE to the size x and align it to y. All definitions
+ * that use align_frame will include space for a frame.
+ */
+#define align_frame(x,y) (((x)+FRAME_SIZE+(y)-1) - (((x)+(y)-1)%(y)))
int main(void)
{
- DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info));
+ DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack));
DEFINE(TASK_STATE, offsetof(struct task_struct, state));
DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
DEFINE(TASK_SIGPENDING, offsetof(struct task_struct, pending));
@@ -151,7 +150,8 @@ int main(void)
DEFINE(TASK_PT_IOR, offsetof(struct task_struct, thread.regs.ior));
BLANK();
DEFINE(TASK_SZ, sizeof(struct task_struct));
- DEFINE(TASK_SZ_ALGN, align(sizeof(struct task_struct), 64));
+ /* TASK_SZ_ALGN includes space for a stack frame. */
+ DEFINE(TASK_SZ_ALGN, align_frame(sizeof(struct task_struct), FRAME_ALIGN));
BLANK();
DEFINE(PT_PSW, offsetof(struct pt_regs, gr[ 0]));
DEFINE(PT_GR1, offsetof(struct pt_regs, gr[ 1]));
@@ -238,7 +238,8 @@ int main(void)
DEFINE(PT_ISR, offsetof(struct pt_regs, isr));
DEFINE(PT_IOR, offsetof(struct pt_regs, ior));
DEFINE(PT_SIZE, sizeof(struct pt_regs));
- DEFINE(PT_SZ_ALGN, align(sizeof(struct pt_regs), 64));
+ /* PT_SZ_ALGN includes space for a stack frame. */
+ DEFINE(PT_SZ_ALGN, align_frame(sizeof(struct pt_regs), FRAME_ALIGN));
BLANK();
DEFINE(TI_TASK, offsetof(struct thread_info, task));
DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
@@ -247,10 +248,8 @@ int main(void)
DEFINE(TI_SEGMENT, offsetof(struct thread_info, addr_limit));
DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count));
DEFINE(THREAD_SZ, sizeof(struct thread_info));
- DEFINE(THREAD_SZ_ALGN, align(sizeof(struct thread_info), 64));
- BLANK();
- DEFINE(IRQSTAT_SIRQ_PEND, offsetof(irq_cpustat_t, __softirq_pending));
- DEFINE(IRQSTAT_SZ, sizeof(irq_cpustat_t));
+ /* THREAD_SZ_ALGN includes space for a stack frame. */
+ DEFINE(THREAD_SZ_ALGN, align_frame(sizeof(struct thread_info), FRAME_ALIGN));
BLANK();
DEFINE(ICACHE_BASE, offsetof(struct pdc_cache_info, ic_base));
DEFINE(ICACHE_STRIDE, offsetof(struct pdc_cache_info, ic_stride));
@@ -275,8 +274,8 @@ int main(void)
DEFINE(DTLB_OFF_COUNT, offsetof(struct pdc_cache_info, dt_off_count));
DEFINE(DTLB_LOOP, offsetof(struct pdc_cache_info, dt_loop));
BLANK();
- DEFINE(PA_BLOCKSTEP_BIT, 31-PT_BLOCKSTEP_BIT);
- DEFINE(PA_SINGLESTEP_BIT, 31-PT_SINGLESTEP_BIT);
+ DEFINE(TIF_BLOCKSTEP_PA_BIT, 31-TIF_BLOCKSTEP);
+ DEFINE(TIF_SINGLESTEP_PA_BIT, 31-TIF_SINGLESTEP);
BLANK();
DEFINE(ASM_PMD_SHIFT, PMD_SHIFT);
DEFINE(ASM_PGDIR_SHIFT, PGDIR_SHIFT);
@@ -289,11 +288,14 @@ int main(void)
DEFINE(ASM_PGD_ENTRY_SIZE, PGD_ENTRY_SIZE);
DEFINE(ASM_PMD_ENTRY_SIZE, PMD_ENTRY_SIZE);
DEFINE(ASM_PTE_ENTRY_SIZE, PTE_ENTRY_SIZE);
+ DEFINE(ASM_PFN_PTE_SHIFT, PFN_PTE_SHIFT);
DEFINE(ASM_PT_INITIAL, PT_INITIAL);
- DEFINE(ASM_PAGE_SIZE, PAGE_SIZE);
BLANK();
DEFINE(EXCDATA_IP, offsetof(struct exception_data, fault_ip));
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();
return 0;
}
diff --git a/arch/parisc/kernel/audit.c b/arch/parisc/kernel/audit.c
new file mode 100644
index 00000000000..eb64a6148c8
--- /dev/null
+++ b/arch/parisc/kernel/audit.c
@@ -0,0 +1,81 @@
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/audit.h>
+#include <asm/unistd.h>
+
+static unsigned dir_class[] = {
+#include <asm-generic/audit_dir_write.h>
+~0U
+};
+
+static unsigned read_class[] = {
+#include <asm-generic/audit_read.h>
+~0U
+};
+
+static unsigned write_class[] = {
+#include <asm-generic/audit_write.h>
+~0U
+};
+
+static unsigned chattr_class[] = {
+#include <asm-generic/audit_change_attr.h>
+~0U
+};
+
+static unsigned signal_class[] = {
+#include <asm-generic/audit_signal.h>
+~0U
+};
+
+int audit_classify_arch(int arch)
+{
+#ifdef CONFIG_COMPAT
+ if (arch == AUDIT_ARCH_PARISC)
+ return 1;
+#endif
+ return 0;
+}
+
+int audit_classify_syscall(int abi, unsigned syscall)
+{
+#ifdef CONFIG_COMPAT
+ extern int parisc32_classify_syscall(unsigned);
+ if (abi == AUDIT_ARCH_PARISC)
+ return parisc32_classify_syscall(syscall);
+#endif
+ switch (syscall) {
+ case __NR_open:
+ return 2;
+ case __NR_openat:
+ return 3;
+ case __NR_execve:
+ return 5;
+ default:
+ return 0;
+ }
+}
+
+static int __init audit_classes_init(void)
+{
+#ifdef CONFIG_COMPAT
+ extern __u32 parisc32_dir_class[];
+ extern __u32 parisc32_write_class[];
+ extern __u32 parisc32_read_class[];
+ extern __u32 parisc32_chattr_class[];
+ extern __u32 parisc32_signal_class[];
+ audit_register_class(AUDIT_CLASS_WRITE_32, parisc32_write_class);
+ audit_register_class(AUDIT_CLASS_READ_32, parisc32_read_class);
+ audit_register_class(AUDIT_CLASS_DIR_WRITE_32, parisc32_dir_class);
+ audit_register_class(AUDIT_CLASS_CHATTR_32, parisc32_chattr_class);
+ audit_register_class(AUDIT_CLASS_SIGNAL_32, parisc32_signal_class);
+#endif
+ audit_register_class(AUDIT_CLASS_WRITE, write_class);
+ audit_register_class(AUDIT_CLASS_READ, read_class);
+ audit_register_class(AUDIT_CLASS_DIR_WRITE, dir_class);
+ audit_register_class(AUDIT_CLASS_CHATTR, chattr_class);
+ audit_register_class(AUDIT_CLASS_SIGNAL, signal_class);
+ return 0;
+}
+
+__initcall(audit_classes_init);
diff --git a/arch/parisc/kernel/binfmt_elf32.c b/arch/parisc/kernel/binfmt_elf32.c
index d1833f164bb..00dc66f9c2b 100644
--- a/arch/parisc/kernel/binfmt_elf32.c
+++ b/arch/parisc/kernel/binfmt_elf32.c
@@ -75,7 +75,6 @@ struct elf_prpsinfo32
char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
};
-#define elf_addr_t unsigned int
#define init_elf_binfmt init_elf32_binfmt
#define ELF_PLATFORM ("PARISC32\0")
@@ -86,8 +85,9 @@ struct elf_prpsinfo32
* could set a processor dependent flag in the thread_struct.
*/
-#define SET_PERSONALITY(ex, ibcs2) \
- current->personality = PER_LINUX32; \
+#undef SET_PERSONALITY
+#define SET_PERSONALITY(ex) \
+ set_thread_flag(TIF_32BIT); \
current->thread.map_base = DEFAULT_MAP_BASE32; \
current->thread.task_size = DEFAULT_TASK_SIZE32 \
@@ -102,25 +102,3 @@ cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
}
#include "../../../fs/binfmt_elf.c"
-
-/* Set up a separate execution domain for ELF32 binaries running
- * on an ELF64 kernel */
-
-static struct exec_domain parisc32_exec_domain = {
- .name = "Linux/ELF32",
- .pers_low = PER_LINUX32,
- .pers_high = PER_LINUX32,
-};
-
-static int __init parisc32_exec_init(void)
-{
- /* steal the identity signal mappings from the default domain */
- parisc32_exec_domain.signal_map = default_exec_domain.signal_map;
- parisc32_exec_domain.signal_invmap = default_exec_domain.signal_invmap;
-
- register_exec_domain(&parisc32_exec_domain);
-
- return 0;
-}
-
-__initcall(parisc32_exec_init);
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index a065349aee3..f6448c7c62b 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -1,10 +1,9 @@
-/* $Id: cache.c,v 1.4 2000/01/25 00:11:38 prumpf Exp $
- *
+/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 1999 Helge Deller (07-13-1999)
+ * Copyright (C) 1999-2006 Helge Deller <deller@gmx.de> (07-13-1999)
* Copyright (C) 1999 SuSE GmbH Nuernberg
* Copyright (C) 2000 Philipp Rumpf (prumpf@tux.org)
*
@@ -18,94 +17,97 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/pagemap.h>
-
+#include <linux/sched.h>
#include <asm/pdc.h>
#include <asm/cache.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
-#include <asm/system.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/processor.h>
#include <asm/sections.h>
+#include <asm/shmparam.h>
-int split_tlb;
-int dcache_stride;
-int icache_stride;
+int split_tlb __read_mostly;
+int dcache_stride __read_mostly;
+int icache_stride __read_mostly;
EXPORT_SYMBOL(dcache_stride);
+void flush_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr);
+EXPORT_SYMBOL(flush_dcache_page_asm);
+void flush_icache_page_asm(unsigned long phys_addr, unsigned long vaddr);
+
-#if defined(CONFIG_SMP)
/* On some machines (e.g. ones with the Merced bus), there can be
* only a single PxTLB broadcast at a time; this must be guaranteed
* by software. We put a spinlock around all TLB flushes to
* ensure this.
*/
DEFINE_SPINLOCK(pa_tlb_lock);
-EXPORT_SYMBOL(pa_tlb_lock);
-#endif
-struct pdc_cache_info cache_info;
+struct pdc_cache_info cache_info __read_mostly;
#ifndef CONFIG_PA20
-static struct pdc_btlb_info btlb_info;
+static struct pdc_btlb_info btlb_info __read_mostly;
#endif
#ifdef CONFIG_SMP
void
flush_data_cache(void)
{
- on_each_cpu((void (*)(void *))flush_data_cache_local, NULL, 1, 1);
+ on_each_cpu(flush_data_cache_local, NULL, 1);
}
void
flush_instruction_cache(void)
{
- on_each_cpu((void (*)(void *))flush_instruction_cache_local, NULL, 1, 1);
+ on_each_cpu(flush_instruction_cache_local, NULL, 1);
}
#endif
void
flush_cache_all_local(void)
{
- flush_instruction_cache_local();
- flush_data_cache_local();
+ flush_instruction_cache_local(NULL);
+ flush_data_cache_local(NULL);
}
EXPORT_SYMBOL(flush_cache_all_local);
-/* flushes EVERYTHING (tlb & cache) */
+/* Virtual address of pfn. */
+#define pfn_va(pfn) __va(PFN_PHYS(pfn))
void
-flush_all_caches(void)
+update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
{
- flush_cache_all();
- flush_tlb_all();
-}
-EXPORT_SYMBOL(flush_all_caches);
+ unsigned long pfn = pte_pfn(*ptep);
+ struct page *page;
-void
-update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
-{
- struct page *page = pte_page(pte);
-
- if (pfn_valid(page_to_pfn(page)) && page_mapping(page) &&
- test_bit(PG_dcache_dirty, &page->flags)) {
+ /* We don't have pte special. As a result, we can be called with
+ an invalid pfn and we don't need to flush the kernel dcache page.
+ This occurs with FireGL card in C8000. */
+ if (!pfn_valid(pfn))
+ return;
- flush_kernel_dcache_page(page_address(page));
+ page = pfn_to_page(pfn);
+ if (page_mapping(page) && test_bit(PG_dcache_dirty, &page->flags)) {
+ flush_kernel_dcache_page_addr(pfn_va(pfn));
clear_bit(PG_dcache_dirty, &page->flags);
- }
+ } else if (parisc_requires_coherency())
+ flush_kernel_dcache_page_addr(pfn_va(pfn));
}
void
show_cache_info(struct seq_file *m)
{
+ char buf[32];
+
seq_printf(m, "I-cache\t\t: %ld KB\n",
cache_info.ic_size/1024 );
- seq_printf(m, "D-cache\t\t: %ld KB (%s%s, %d-way associative)\n",
+ if (cache_info.dc_loop != 1)
+ snprintf(buf, 32, "%lu-way associative", cache_info.dc_loop);
+ seq_printf(m, "D-cache\t\t: %ld KB (%s%s, %s)\n",
cache_info.dc_size/1024,
(cache_info.dc_conf.cc_wt ? "WT":"WB"),
(cache_info.dc_conf.cc_sh ? ", shared I/D":""),
- (cache_info.dc_conf.cc_assoc)
- );
-
+ ((cache_info.dc_loop == 1) ? "direct mapped" : buf));
seq_printf(m, "ITLB entries\t: %ld\n" "DTLB entries\t: %ld%s\n",
cache_info.it_size,
cache_info.dt_size,
@@ -158,11 +160,11 @@ parisc_cache_init(void)
cache_info.dc_conf.cc_block,
cache_info.dc_conf.cc_line,
cache_info.dc_conf.cc_shift);
- printk(" wt %d sh %d cst %d assoc %d\n",
+ printk(" wt %d sh %d cst %d hv %d\n",
cache_info.dc_conf.cc_wt,
cache_info.dc_conf.cc_sh,
cache_info.dc_conf.cc_cst,
- cache_info.dc_conf.cc_assoc);
+ cache_info.dc_conf.cc_hv);
printk("IC base 0x%lx stride 0x%lx count 0x%lx loop 0x%lx\n",
cache_info.ic_base,
@@ -176,20 +178,20 @@ parisc_cache_init(void)
cache_info.ic_conf.cc_block,
cache_info.ic_conf.cc_line,
cache_info.ic_conf.cc_shift);
- printk(" wt %d sh %d cst %d assoc %d\n",
+ printk(" wt %d sh %d cst %d hv %d\n",
cache_info.ic_conf.cc_wt,
cache_info.ic_conf.cc_sh,
cache_info.ic_conf.cc_cst,
- cache_info.ic_conf.cc_assoc);
+ cache_info.ic_conf.cc_hv);
- printk("D-TLB conf: sh %d page %d cst %d aid %d pad1 %d \n",
+ printk("D-TLB conf: sh %d page %d cst %d aid %d pad1 %d\n",
cache_info.dt_conf.tc_sh,
cache_info.dt_conf.tc_page,
cache_info.dt_conf.tc_cst,
cache_info.dt_conf.tc_aid,
cache_info.dt_conf.tc_pad1);
- printk("I-TLB conf: sh %d page %d cst %d aid %d pad1 %d \n",
+ printk("I-TLB conf: sh %d page %d cst %d aid %d pad1 %d\n",
cache_info.it_conf.tc_sh,
cache_info.it_conf.tc_page,
cache_info.it_conf.tc_cst,
@@ -234,7 +236,8 @@ parisc_cache_init(void)
void disable_sr_hashing(void)
{
- int srhash_type;
+ int srhash_type, retval;
+ unsigned long space_bits;
switch (boot_cpu_data.cpu_type) {
case pcx: /* We shouldn't get this far. setup.c should prevent it. */
@@ -260,25 +263,40 @@ void disable_sr_hashing(void)
}
disable_sr_hashing_asm(srhash_type);
+
+ retval = pdc_spaceid_bits(&space_bits);
+ /* If this procedure isn't implemented, don't panic. */
+ if (retval < 0 && retval != PDC_BAD_OPTION)
+ panic("pdc_spaceid_bits call failed.\n");
+ if (space_bits != 0)
+ panic("SpaceID hashing is still on!\n");
+}
+
+static inline void
+__flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
+ unsigned long physaddr)
+{
+ preempt_disable();
+ flush_dcache_page_asm(physaddr, vmaddr);
+ if (vma->vm_flags & VM_EXEC)
+ flush_icache_page_asm(physaddr, vmaddr);
+ preempt_enable();
}
void flush_dcache_page(struct page *page)
{
struct address_space *mapping = page_mapping(page);
struct vm_area_struct *mpnt;
- struct prio_tree_iter iter;
unsigned long offset;
- unsigned long addr;
+ unsigned long addr, old_addr = 0;
pgoff_t pgoff;
- unsigned long pfn = page_to_pfn(page);
-
if (mapping && !mapping_mapped(mapping)) {
set_bit(PG_dcache_dirty, &page->flags);
return;
}
- flush_kernel_dcache_page(page_address(page));
+ flush_kernel_dcache_page(page);
if (!mapping)
return;
@@ -291,24 +309,26 @@ void flush_dcache_page(struct page *page)
* to flush one address here for them all to become coherent */
flush_dcache_mmap_lock(mapping);
- vma_prio_tree_foreach(mpnt, &iter, &mapping->i_mmap, pgoff, pgoff) {
+ vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) {
offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
addr = mpnt->vm_start + offset;
- /* Flush instructions produce non access tlb misses.
- * On PA, we nullify these instructions rather than
- * taking a page fault if the pte doesn't exist.
- * This is just for speed. If the page translation
- * isn't there, there's no point exciting the
- * nadtlb handler into a nullification frenzy.
- *
- * Make sure we really have this page: the private
- * mappings may cover this area but have COW'd this
- * particular page.
- */
- if (translation_exists(mpnt, addr, pfn)) {
- __flush_cache_page(mpnt, addr);
- break;
+ /* The TLB is the engine of coherence on parisc: The
+ * CPU is entitled to speculate any page with a TLB
+ * mapping, so here we kill the mapping then flush the
+ * page along a special flush only alias mapping.
+ * This guarantees that the page is no-longer in the
+ * cache for any process and nor may it be
+ * speculatively read in (until the user or kernel
+ * specifically accesses it, of course) */
+
+ flush_tlb_page(mpnt, addr);
+ if (old_addr == 0 || (old_addr & (SHM_COLOUR - 1))
+ != (addr & (SHM_COLOUR - 1))) {
+ __flush_cache_page(mpnt, addr, page_to_phys(page));
+ if (old_addr)
+ printk(KERN_ERR "INEQUIVALENT ALIASES 0x%lx and 0x%lx in file %s\n", old_addr, addr, mpnt->vm_file ? (char *)mpnt->vm_file->f_path.dentry->d_name.name : "(null)");
+ old_addr = addr;
}
}
flush_dcache_mmap_unlock(mapping);
@@ -317,24 +337,14 @@ EXPORT_SYMBOL(flush_dcache_page);
/* Defined in arch/parisc/kernel/pacache.S */
EXPORT_SYMBOL(flush_kernel_dcache_range_asm);
-EXPORT_SYMBOL(flush_kernel_dcache_page);
+EXPORT_SYMBOL(flush_kernel_dcache_page_asm);
EXPORT_SYMBOL(flush_data_cache_local);
EXPORT_SYMBOL(flush_kernel_icache_range_asm);
-void clear_user_page_asm(void *page, unsigned long vaddr)
-{
- /* This function is implemented in assembly in pacache.S */
- extern void __clear_user_page_asm(void *page, unsigned long vaddr);
-
- purge_tlb_start();
- __clear_user_page_asm(page, vaddr);
- purge_tlb_end();
-}
-
#define FLUSH_THRESHOLD 0x80000 /* 0.5MB */
-int parisc_cache_flush_threshold = FLUSH_THRESHOLD;
+int parisc_cache_flush_threshold __read_mostly = FLUSH_THRESHOLD;
-void parisc_setup_cache_timing(void)
+void __init parisc_setup_cache_timing(void)
{
unsigned long rangetime, alltime;
unsigned long size;
@@ -358,5 +368,217 @@ void parisc_setup_cache_timing(void)
if (!parisc_cache_flush_threshold)
parisc_cache_flush_threshold = FLUSH_THRESHOLD;
- printk("Setting cache flush threshold to %x (%d CPUs online)\n", parisc_cache_flush_threshold, num_online_cpus());
+ if (parisc_cache_flush_threshold > cache_info.dc_size)
+ parisc_cache_flush_threshold = cache_info.dc_size;
+
+ printk(KERN_INFO "Setting cache flush threshold to %x (%d CPUs online)\n", parisc_cache_flush_threshold, num_online_cpus());
+}
+
+extern void purge_kernel_dcache_page_asm(unsigned long);
+extern void clear_user_page_asm(void *, unsigned long);
+extern void copy_user_page_asm(void *, void *, unsigned long);
+
+void flush_kernel_dcache_page_addr(void *addr)
+{
+ unsigned long flags;
+
+ flush_kernel_dcache_page_asm(addr);
+ purge_tlb_start(flags);
+ pdtlb_kernel(addr);
+ purge_tlb_end(flags);
+}
+EXPORT_SYMBOL(flush_kernel_dcache_page_addr);
+
+void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
+ struct page *pg)
+{
+ /* Copy using kernel mapping. No coherency is needed (all in
+ kunmap) for the `to' page. However, the `from' page needs to
+ be flushed through a mapping equivalent to the user mapping
+ before it can be accessed through the kernel mapping. */
+ preempt_disable();
+ flush_dcache_page_asm(__pa(vfrom), vaddr);
+ preempt_enable();
+ copy_page_asm(vto, vfrom);
+}
+EXPORT_SYMBOL(copy_user_page);
+
+void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
+{
+ unsigned long flags;
+
+ /* Note: purge_tlb_entries can be called at startup with
+ no context. */
+
+ purge_tlb_start(flags);
+ mtsp(mm->context, 1);
+ pdtlb(addr);
+ pitlb(addr);
+ purge_tlb_end(flags);
+}
+EXPORT_SYMBOL(purge_tlb_entries);
+
+void __flush_tlb_range(unsigned long sid, unsigned long start,
+ unsigned long end)
+{
+ unsigned long npages;
+
+ npages = ((end - (start & PAGE_MASK)) + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+ if (npages >= 512) /* 2MB of space: arbitrary, should be tuned */
+ flush_tlb_all();
+ else {
+ unsigned long flags;
+
+ purge_tlb_start(flags);
+ mtsp(sid, 1);
+ if (split_tlb) {
+ while (npages--) {
+ pdtlb(start);
+ pitlb(start);
+ start += PAGE_SIZE;
+ }
+ } else {
+ while (npages--) {
+ pdtlb(start);
+ start += PAGE_SIZE;
+ }
+ }
+ purge_tlb_end(flags);
+ }
+}
+
+static void cacheflush_h_tmp_function(void *dummy)
+{
+ flush_cache_all_local();
+}
+
+void flush_cache_all(void)
+{
+ on_each_cpu(cacheflush_h_tmp_function, NULL, 1);
+}
+
+static inline unsigned long mm_total_size(struct mm_struct *mm)
+{
+ struct vm_area_struct *vma;
+ unsigned long usize = 0;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next)
+ usize += vma->vm_end - vma->vm_start;
+ return usize;
+}
+
+static inline pte_t *get_ptep(pgd_t *pgd, unsigned long addr)
+{
+ pte_t *ptep = NULL;
+
+ if (!pgd_none(*pgd)) {
+ pud_t *pud = pud_offset(pgd, addr);
+ if (!pud_none(*pud)) {
+ pmd_t *pmd = pmd_offset(pud, addr);
+ if (!pmd_none(*pmd))
+ ptep = pte_offset_map(pmd, addr);
+ }
+ }
+ return ptep;
+}
+
+void flush_cache_mm(struct mm_struct *mm)
+{
+ struct vm_area_struct *vma;
+ pgd_t *pgd;
+
+ /* Flushing the whole cache on each cpu takes forever on
+ rp3440, etc. So, avoid it if the mm isn't too big. */
+ if (mm_total_size(mm) >= parisc_cache_flush_threshold) {
+ flush_cache_all();
+ return;
+ }
+
+ if (mm->context == mfsp(3)) {
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ flush_user_dcache_range_asm(vma->vm_start, vma->vm_end);
+ if ((vma->vm_flags & VM_EXEC) == 0)
+ continue;
+ flush_user_icache_range_asm(vma->vm_start, vma->vm_end);
+ }
+ return;
+ }
+
+ pgd = mm->pgd;
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ unsigned long addr;
+
+ for (addr = vma->vm_start; addr < vma->vm_end;
+ addr += PAGE_SIZE) {
+ unsigned long pfn;
+ pte_t *ptep = get_ptep(pgd, addr);
+ if (!ptep)
+ continue;
+ pfn = pte_pfn(*ptep);
+ if (!pfn_valid(pfn))
+ continue;
+ __flush_cache_page(vma, addr, PFN_PHYS(pfn));
+ }
+ }
+}
+
+void
+flush_user_dcache_range(unsigned long start, unsigned long end)
+{
+ if ((end - start) < parisc_cache_flush_threshold)
+ flush_user_dcache_range_asm(start,end);
+ else
+ flush_data_cache();
+}
+
+void
+flush_user_icache_range(unsigned long start, unsigned long end)
+{
+ if ((end - start) < parisc_cache_flush_threshold)
+ flush_user_icache_range_asm(start,end);
+ else
+ flush_instruction_cache();
+}
+
+void flush_cache_range(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
+{
+ unsigned long addr;
+ pgd_t *pgd;
+
+ BUG_ON(!vma->vm_mm->context);
+
+ if ((end - start) >= parisc_cache_flush_threshold) {
+ flush_cache_all();
+ return;
+ }
+
+ if (vma->vm_mm->context == mfsp(3)) {
+ flush_user_dcache_range_asm(start, end);
+ if (vma->vm_flags & VM_EXEC)
+ flush_user_icache_range_asm(start, end);
+ return;
+ }
+
+ pgd = vma->vm_mm->pgd;
+ for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) {
+ unsigned long pfn;
+ pte_t *ptep = get_ptep(pgd, addr);
+ if (!ptep)
+ continue;
+ pfn = pte_pfn(*ptep);
+ if (pfn_valid(pfn))
+ __flush_cache_page(vma, addr, PFN_PHYS(pfn));
+ }
+}
+
+void
+flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn)
+{
+ BUG_ON(!vma->vm_mm->context);
+
+ if (pfn_valid(pfn)) {
+ flush_tlb_page(vma, vmaddr);
+ __flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
+ }
}
diff --git a/arch/parisc/kernel/compat_audit.c b/arch/parisc/kernel/compat_audit.c
new file mode 100644
index 00000000000..c74478f6bc7
--- /dev/null
+++ b/arch/parisc/kernel/compat_audit.c
@@ -0,0 +1,40 @@
+#include <asm/unistd.h>
+
+unsigned int parisc32_dir_class[] = {
+#include <asm-generic/audit_dir_write.h>
+~0U
+};
+
+unsigned int parisc32_chattr_class[] = {
+#include <asm-generic/audit_change_attr.h>
+~0U
+};
+
+unsigned int parisc32_write_class[] = {
+#include <asm-generic/audit_write.h>
+~0U
+};
+
+unsigned int parisc32_read_class[] = {
+#include <asm-generic/audit_read.h>
+~0U
+};
+
+unsigned int parisc32_signal_class[] = {
+#include <asm-generic/audit_signal.h>
+~0U
+};
+
+int parisc32_classify_syscall(unsigned syscall)
+{
+ switch (syscall) {
+ case __NR_open:
+ return 2;
+ case __NR_openat:
+ return 3;
+ case __NR_execve:
+ return 5;
+ default:
+ return 1;
+ }
+}
diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c
index 988844a169e..dba508fe168 100644
--- a/arch/parisc/kernel/drivers.c
+++ b/arch/parisc/kernel/drivers.c
@@ -33,17 +33,18 @@
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/export.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/pdc.h>
#include <asm/parisc-device.h>
/* See comments in include/asm-parisc/pci.h */
-struct hppa_dma_ops *hppa_dma_ops;
+struct hppa_dma_ops *hppa_dma_ops __read_mostly;
EXPORT_SYMBOL(hppa_dma_ops);
static struct device root = {
- .bus_id = "parisc",
+ .init_name = "parisc",
};
static inline int check_dev(struct device *dev)
@@ -173,8 +174,6 @@ int register_parisc_driver(struct parisc_driver *driver)
WARN_ON(driver->drv.probe != NULL);
WARN_ON(driver->drv.remove != NULL);
- driver->drv.probe = parisc_driver_probe;
- driver->drv.remove = parisc_driver_remove;
driver->drv.name = driver->name;
return driver_register(&driver->drv);
@@ -283,18 +282,6 @@ find_pa_parent_type(const struct parisc_device *padev, int type)
return NULL;
}
-#ifdef CONFIG_PCI
-static inline int is_pci_dev(struct device *dev)
-{
- return dev->bus == &pci_bus_type;
-}
-#else
-static inline int is_pci_dev(struct device *dev)
-{
- return 0;
-}
-#endif
-
/*
* get_node_path fills in @path with the firmware path to the device.
* Note that if @node is a parisc device, we don't fill in the 'mod' field.
@@ -307,7 +294,7 @@ static void get_node_path(struct device *dev, struct hardware_path *path)
int i = 5;
memset(&path->bc, -1, 6);
- if (is_pci_dev(dev)) {
+ if (dev_is_pci(dev)) {
unsigned int devfn = to_pci_dev(dev)->devfn;
path->mod = PCI_FUNC(devfn);
path->bc[i--] = PCI_SLOT(devfn);
@@ -315,7 +302,7 @@ static void get_node_path(struct device *dev, struct hardware_path *path)
}
while (dev != &root) {
- if (is_pci_dev(dev)) {
+ if (dev_is_pci(dev)) {
unsigned int devfn = to_pci_dev(dev)->devfn;
path->bc[i--] = PCI_SLOT(devfn) | (PCI_FUNC(devfn)<< 5);
} else if (dev->bus == &parisc_bus_type) {
@@ -395,7 +382,8 @@ EXPORT_SYMBOL(print_pci_hwpath);
static void setup_bus_id(struct parisc_device *padev)
{
struct hardware_path path;
- char *output = padev->dev.bus_id;
+ char name[28];
+ char *output = name;
int i;
get_node_path(padev->dev.parent, &path);
@@ -406,15 +394,15 @@ static void setup_bus_id(struct parisc_device *padev)
output += sprintf(output, "%u:", (unsigned char) path.bc[i]);
}
sprintf(output, "%u", (unsigned char) padev->hw_path);
+ dev_set_name(&padev->dev, name);
}
struct parisc_device * create_tree_node(char id, struct device *parent)
{
- struct parisc_device *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ struct parisc_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
- memset(dev, 0, sizeof(*dev));
dev->hw_path = id;
dev->id.hw_type = HPHW_FAULTY;
@@ -427,7 +415,10 @@ struct parisc_device * create_tree_node(char id, struct device *parent)
/* make the generic dma mask a pointer to the parisc one */
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.coherent_dma_mask = dev->dma_mask;
- device_register(&dev->dev);
+ if (device_register(&dev->dev)) {
+ kfree(dev);
+ return NULL;
+ }
return dev;
}
@@ -499,8 +490,12 @@ alloc_pa_dev(unsigned long hpa, struct hardware_path *mod_path)
dev = create_parisc_device(mod_path);
if (dev->id.hw_type != HPHW_FAULTY) {
- printk("Two devices have hardware path %s. Please file a bug with HP.\n"
- "In the meantime, you could try rearranging your cards.\n", parisc_pathname(dev));
+ printk(KERN_ERR "Two devices have hardware path [%s]. "
+ "IODC data for second device: "
+ "%02x%02x%02x%02x%02x%02x\n"
+ "Rearranging GSC cards sometimes helps\n",
+ parisc_pathname(dev), iodc_data[0], iodc_data[1],
+ iodc_data[3], iodc_data[4], iodc_data[5], iodc_data[6]);
return NULL;
}
@@ -511,8 +506,13 @@ alloc_pa_dev(unsigned long hpa, struct hardware_path *mod_path)
(iodc_data[5] << 8) | iodc_data[6];
dev->hpa.name = parisc_pathname(dev);
dev->hpa.start = hpa;
- if (hpa == 0xf4000000 || hpa == 0xf6000000 ||
- hpa == 0xf8000000 || hpa == 0xfa000000) {
+ /* This is awkward. The STI spec says that gfx devices may occupy
+ * 32MB or 64MB. Unfortunately, we don't know how to tell whether
+ * it's the former or the latter. Assumptions either way can hurt us.
+ */
+ if (hpa == 0xf4000000 || hpa == 0xf8000000) {
+ dev->hpa.end = hpa + 0x03ffffff;
+ } else if (hpa == 0xf6000000 || hpa == 0xfa000000) {
dev->hpa.end = hpa + 0x01ffffff;
} else {
dev->hpa.end = hpa + 0xfff;
@@ -538,6 +538,38 @@ static int parisc_generic_match(struct device *dev, struct device_driver *drv)
return match_device(to_parisc_driver(drv), to_parisc_device(dev));
}
+static ssize_t make_modalias(struct device *dev, char *buf)
+{
+ const struct parisc_device *padev = to_parisc_device(dev);
+ const struct parisc_device_id *id = &padev->id;
+
+ return sprintf(buf, "parisc:t%02Xhv%04Xrev%02Xsv%08X\n",
+ (u8)id->hw_type, (u16)id->hversion, (u8)id->hversion_rev,
+ (u32)id->sversion);
+}
+
+static int parisc_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ const struct parisc_device *padev;
+ char modalias[40];
+
+ if (!dev)
+ return -ENODEV;
+
+ padev = to_parisc_device(dev);
+ if (!padev)
+ return -ENODEV;
+
+ if (add_uevent_var(env, "PARISC_NAME=%s", padev->name))
+ return -ENOMEM;
+
+ make_modalias(dev, modalias);
+ if (add_uevent_var(env, "MODALIAS=%s", modalias))
+ return -ENOMEM;
+
+ return 0;
+}
+
#define pa_dev_attr(name, field, format_string) \
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
@@ -553,19 +585,28 @@ pa_dev_attr(rev, id.hversion_rev, "0x%x\n");
pa_dev_attr_id(hversion, "0x%03x\n");
pa_dev_attr_id(sversion, "0x%05x\n");
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return make_modalias(dev, buf);
+}
+
static struct device_attribute parisc_device_attrs[] = {
__ATTR_RO(irq),
__ATTR_RO(hw_type),
__ATTR_RO(rev),
__ATTR_RO(hversion),
__ATTR_RO(sversion),
+ __ATTR_RO(modalias),
__ATTR_NULL,
};
struct bus_type parisc_bus_type = {
.name = "parisc",
.match = parisc_generic_match,
+ .uevent = parisc_uevent,
.dev_attrs = parisc_device_attrs,
+ .probe = parisc_driver_probe,
+ .remove = parisc_driver_remove,
};
/**
@@ -642,7 +683,7 @@ static int check_parent(struct device * dev, void * data)
if (dev->bus == &parisc_bus_type) {
if (match_parisc_device(dev, d->index, d->modpath))
d->dev = dev;
- } else if (is_pci_dev(dev)) {
+ } else if (dev_is_pci(dev)) {
if (match_pci_device(dev, d->index, d->modpath))
d->dev = dev;
} else if (dev->bus == NULL) {
@@ -678,7 +719,9 @@ parse_tree_node(struct device *parent, int index, struct hardware_path *modpath)
.fn = check_parent,
};
- device_for_each_child(parent, &recurse_data, descend_children);
+ if (device_for_each_child(parent, &recurse_data, descend_children))
+ /* nothing */;
+
return d.dev;
}
@@ -698,7 +741,7 @@ struct device *hwpath_to_device(struct hardware_path *modpath)
if (!parent)
return NULL;
}
- if (is_pci_dev(parent)) /* pci devices already parse MOD */
+ if (dev_is_pci(parent)) /* pci devices already parse MOD */
return parent;
else
return parse_tree_node(parent, 6, modpath);
@@ -717,7 +760,7 @@ void device_to_hwpath(struct device *dev, struct hardware_path *path)
padev = to_parisc_device(dev);
get_node_path(dev->parent, path);
path->mod = padev->hw_path;
- } else if (is_pci_dev(dev)) {
+ } else if (dev_is_pci(dev)) {
get_node_path(dev, path);
}
}
@@ -824,13 +867,13 @@ static void print_parisc_device(struct parisc_device *dev)
static int count;
print_pa_hwpath(dev, hw_path);
- printk(KERN_INFO "%d. %s at 0x%lx [%s] { %d, 0x%x, 0x%.3x, 0x%.5x }",
- ++count, dev->name, dev->hpa.start, hw_path, dev->id.hw_type,
+ printk(KERN_INFO "%d. %s at 0x%p [%s] { %d, 0x%x, 0x%.3x, 0x%.5x }",
+ ++count, dev->name, (void*) dev->hpa.start, hw_path, dev->id.hw_type,
dev->id.hversion_rev, dev->id.hversion, dev->id.sversion);
if (dev->num_addrs) {
int k;
- printk(", additional addresses: ");
+ printk(", additional addresses: ");
for (k = 0; k < dev->num_addrs; k++)
printk("0x%lx ", dev->addr[k]);
}
@@ -842,8 +885,10 @@ static void print_parisc_device(struct parisc_device *dev)
*/
void init_parisc_bus(void)
{
- bus_register(&parisc_bus_type);
- device_register(&root);
+ if (bus_register(&parisc_bus_type))
+ panic("Could not register PA-RISC bus type\n");
+ if (device_register(&root))
+ panic("Could not register PA-RISC root device\n");
get_device(&root);
}
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
index c7e66ee5b08..e8f07dd2840 100644
--- a/arch/parisc/kernel/entry.S
+++ b/arch/parisc/kernel/entry.S
@@ -22,7 +22,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/config.h>
#include <asm/asm-offsets.h>
/* we have the following possibilities to act on an interruption:
@@ -31,23 +30,18 @@
#include <asm/psw.h>
+#include <asm/cache.h> /* for L1_CACHE_SHIFT */
#include <asm/assembly.h> /* for LDREG/STREG defines */
#include <asm/pgtable.h>
#include <asm/signal.h>
#include <asm/unistd.h>
#include <asm/thread_info.h>
-#ifdef CONFIG_64BIT
-#define CMPIB cmpib,*
-#define CMPB cmpb,*
-#define COND(x) *x
+#include <linux/linkage.h>
+#ifdef CONFIG_64BIT
.level 2.0w
#else
-#define CMPIB cmpib,
-#define CMPB cmpb,
-#define COND(x) x
-
.level 2.0
#endif
@@ -71,15 +65,11 @@
rsm PSW_SM_I, %r0 /* barrier for "Relied upon Translation */
mtsp %r0, %sr4
mtsp %r0, %sr5
- mfsp %sr7, %r1
- or,= %r0,%r1,%r0 /* Only save sr7 in sr3 if sr7 != 0 */
- mtsp %r1, %sr3
+ mtsp %r0, %sr6
tovirt_r1 %r29
load32 KERNEL_PSW, %r1
rsm PSW_SM_QUIET,%r0 /* second "heavy weight" ctl op */
- mtsp %r0, %sr6
- mtsp %r0, %sr7
mtctl %r0, %cr17 /* Clear IIASQ tail */
mtctl %r0, %cr17 /* Clear IIASQ head */
mtctl %r1, %ipsw
@@ -96,7 +86,6 @@
* The "get_stack" macros are responsible for determining the
* kernel stack value.
*
- * For Faults:
* If sr7 == 0
* Already using a kernel stack, so call the
* get_stack_use_r30 macro to push a pt_regs structure
@@ -108,26 +97,6 @@
* task pointer pointed to by cr30. Set the stack
* pointer to point to the end of the task structure.
*
- * For Interrupts:
- * If sr7 == 0
- * Already using a kernel stack, check to see if r30
- * is already pointing to the per processor interrupt
- * stack. If it is, call the get_stack_use_r30 macro
- * to push a pt_regs structure on the stack, and store
- * registers there. Otherwise, call get_stack_use_cr31
- * to get a pointer to the base of the interrupt stack
- * and push a pt_regs structure on that stack.
- * else
- * Need to set up a kernel stack, so call the
- * get_stack_use_cr30 macro to set up a pointer
- * to the pt_regs structure contained within the
- * task pointer pointed to by cr30. Set the stack
- * pointer to point to the end of the task structure.
- * N.B: We don't use the interrupt stack for the
- * first interrupt from userland, because signals/
- * resched's are processed when returning to userland,
- * and we can sleep in those cases.
- *
* Note that we use shadowed registers for temps until
* we can save %r26 and %r29. %r26 is used to preserve
* %r8 (a shadowed register) which temporarily contained
@@ -146,17 +115,20 @@
/* we save the registers in the task struct */
+ copy %r30, %r17
mfctl %cr30, %r1
+ ldo THREAD_SZ_ALGN(%r1), %r30
+ mtsp %r0,%sr7
+ mtsp %r16,%sr3
tophys %r1,%r9
LDREG TI_TASK(%r9), %r1 /* thread_info -> task_struct */
tophys %r1,%r9
ldo TASK_REGS(%r9),%r9
- STREG %r30, PT_GR30(%r9)
+ STREG %r17,PT_GR30(%r9)
STREG %r29,PT_GR29(%r9)
STREG %r26,PT_GR26(%r9)
+ STREG %r16,PT_SR7(%r9)
copy %r9,%r29
- mfctl %cr30, %r1
- ldo THREAD_SZ_ALGN(%r1), %r30
.endm
.macro get_stack_use_r30
@@ -164,10 +136,12 @@
/* we put a struct pt_regs on the stack and save the registers there */
tophys %r30,%r9
- STREG %r30,PT_GR30(%r9)
+ copy %r30,%r1
ldo PT_SZ_ALGN(%r30),%r30
+ STREG %r1,PT_GR30(%r9)
STREG %r29,PT_GR29(%r9)
STREG %r26,PT_GR26(%r9)
+ STREG %r16,PT_SR7(%r9)
copy %r9,%r29
.endm
@@ -214,8 +188,8 @@
/* Register definitions for tlb miss handler macros */
- va = r8 /* virtual address for which the trap occured */
- spc = r24 /* space for which the trap occured */
+ va = r8 /* virtual address for which the trap occurred */
+ spc = r24 /* space for which the trap occurred */
#ifndef CONFIG_64BIT
@@ -252,22 +226,13 @@
#ifndef CONFIG_64BIT
/*
* naitlb miss interruption handler (parisc 1.1 - 32 bit)
- *
- * Note: naitlb misses will be treated
- * as an ordinary itlb miss for now.
- * However, note that naitlb misses
- * have the faulting address in the
- * IOR/ISR.
*/
.macro naitlb_11 code
mfctl %isr,spc
- b itlb_miss_11
+ b naitlb_miss_11
mfctl %ior,va
- /* FIXME: If user causes a naitlb miss, the priv level may not be in
- * lower bits of va, where the itlb miss handler is expecting them
- */
.align 32
.endm
@@ -275,26 +240,17 @@
/*
* naitlb miss interruption handler (parisc 2.0)
- *
- * Note: naitlb misses will be treated
- * as an ordinary itlb miss for now.
- * However, note that naitlb misses
- * have the faulting address in the
- * IOR/ISR.
*/
.macro naitlb_20 code
mfctl %isr,spc
#ifdef CONFIG_64BIT
- b itlb_miss_20w
+ b naitlb_miss_20w
#else
- b itlb_miss_20
+ b naitlb_miss_20
#endif
mfctl %ior,va
- /* FIXME: If user causes a naitlb miss, the priv level may not be in
- * lower bits of va, where the itlb miss handler is expecting them
- */
.align 32
.endm
@@ -391,32 +347,6 @@
.align 32
.endm
- /* The following are simple 32 vs 64 bit instruction
- * abstractions for the macros */
- .macro EXTR reg1,start,length,reg2
-#ifdef CONFIG_64BIT
- extrd,u \reg1,32+\start,\length,\reg2
-#else
- extrw,u \reg1,\start,\length,\reg2
-#endif
- .endm
-
- .macro DEP reg1,start,length,reg2
-#ifdef CONFIG_64BIT
- depd \reg1,32+\start,\length,\reg2
-#else
- depw \reg1,\start,\length,\reg2
-#endif
- .endm
-
- .macro DEPI val,start,length,reg
-#ifdef CONFIG_64BIT
- depdi \val,32+\start,\length,\reg
-#else
- depwi \val,\start,\length,\reg
-#endif
- .endm
-
/* In LP64, the space contains part of the upper 32 bits of the
* fault. We have to extract this and place it in the va,
* zeroing the corresponding bits in the space register */
@@ -469,23 +399,27 @@
*/
.macro L2_ptep pmd,pte,index,va,fault
#if PT_NLEVELS == 3
- EXTR \va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index
+ extru \va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index
#else
- EXTR \va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
+# if defined(CONFIG_64BIT)
+ extrd,u \va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
+ #else
+ # if PAGE_SIZE > 4096
+ extru \va,31-ASM_PGDIR_SHIFT,32-ASM_PGDIR_SHIFT,\index
+ # else
+ extru \va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
+ # endif
+# endif
#endif
- DEP %r0,31,PAGE_SHIFT,\pmd /* clear offset */
+ dep %r0,31,PAGE_SHIFT,\pmd /* clear offset */
copy %r0,\pte
ldw,s \index(\pmd),\pmd
bb,>=,n \pmd,_PxD_PRESENT_BIT,\fault
- DEP %r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */
+ dep %r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */
copy \pmd,%r9
-#ifdef CONFIG_64BIT
- shld %r9,PxD_VALUE_SHIFT,\pmd
-#else
- shlw %r9,PxD_VALUE_SHIFT,\pmd
-#endif
- EXTR \va,31-PAGE_SHIFT,ASM_BITS_PER_PTE,\index
- DEP %r0,31,PAGE_SHIFT,\pmd /* clear offset */
+ SHLREG %r9,PxD_VALUE_SHIFT,\pmd
+ extru \va,31-PAGE_SHIFT,ASM_BITS_PER_PTE,\index
+ dep %r0,31,PAGE_SHIFT,\pmd /* clear offset */
shladd \index,BITS_PER_PTE_ENTRY,\pmd,\pmd
LDREG %r0(\pmd),\pte /* pmd is now pte */
bb,>=,n \pte,_PAGE_PRESENT_BIT,\fault
@@ -502,24 +436,58 @@
* all ILP32 processes and all the kernel for machines with
* under 4GB of memory) */
.macro L3_ptep pgd,pte,index,va,fault
+#if PT_NLEVELS == 3 /* we might have a 2-Level scheme, e.g. with 16kb page size */
extrd,u \va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
copy %r0,\pte
- extrd,u,*= \va,31,32,%r0
+ extrd,u,*= \va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0
ldw,s \index(\pgd),\pgd
- extrd,u,*= \va,31,32,%r0
+ extrd,u,*= \va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0
bb,>=,n \pgd,_PxD_PRESENT_BIT,\fault
- extrd,u,*= \va,31,32,%r0
+ extrd,u,*= \va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0
shld \pgd,PxD_VALUE_SHIFT,\index
- extrd,u,*= \va,31,32,%r0
+ extrd,u,*= \va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0
copy \index,\pgd
- extrd,u,*<> \va,31,32,%r0
+ extrd,u,*<> \va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0
ldo ASM_PGD_PMD_OFFSET(\pgd),\pgd
+#endif
L2_ptep \pgd,\pte,\index,\va,\fault
.endm
+ /* Acquire pa_dbit_lock lock. */
+ .macro dbit_lock spc,tmp,tmp1
+#ifdef CONFIG_SMP
+ cmpib,COND(=),n 0,\spc,2f
+ load32 PA(pa_dbit_lock),\tmp
+1: LDCW 0(\tmp),\tmp1
+ cmpib,COND(=) 0,\tmp1,1b
+ nop
+2:
+#endif
+ .endm
+
+ /* Release pa_dbit_lock lock without reloading lock address. */
+ .macro dbit_unlock0 spc,tmp
+#ifdef CONFIG_SMP
+ or,COND(=) %r0,\spc,%r0
+ stw \spc,0(\tmp)
+#endif
+ .endm
+
+ /* Release pa_dbit_lock lock. */
+ .macro dbit_unlock1 spc,tmp
+#ifdef CONFIG_SMP
+ load32 PA(pa_dbit_lock),\tmp
+ dbit_unlock0 \spc,\tmp
+#endif
+ .endm
+
/* Set the _PAGE_ACCESSED bit of the PTE. Be clever and
* don't needlessly dirty the cache line if it was already set */
- .macro update_ptep ptep,pte,tmp,tmp1
+ .macro update_ptep spc,ptep,pte,tmp,tmp1
+#ifdef CONFIG_SMP
+ or,COND(=) %r0,\spc,%r0
+ LDREG 0(\ptep),\pte
+#endif
ldi _PAGE_ACCESSED,\tmp1
or \tmp1,\pte,\tmp
and,COND(<>) \tmp1,\pte,%r0
@@ -528,12 +496,28 @@
/* Set the dirty bit (and accessed bit). No need to be
* clever, this is only used from the dirty fault */
- .macro update_dirty ptep,pte,tmp
+ .macro update_dirty spc,ptep,pte,tmp
+#ifdef CONFIG_SMP
+ or,COND(=) %r0,\spc,%r0
+ LDREG 0(\ptep),\pte
+#endif
ldi _PAGE_ACCESSED|_PAGE_DIRTY,\tmp
or \tmp,\pte,\pte
STREG \pte,0(\ptep)
.endm
+ /* bitshift difference between a PFN (based on kernel's PAGE_SIZE)
+ * to a CPU TLB 4k PFN (4k => 12 bits to shift) */
+ #define PAGE_ADD_SHIFT (PAGE_SHIFT-12)
+
+ /* Drop prot bits and convert to page addr for iitlbt and idtlbt */
+ .macro convert_for_tlb_insert20 pte
+ extrd,u \pte,(63-ASM_PFN_PTE_SHIFT)+(63-58)+PAGE_ADD_SHIFT,\
+ 64-PAGE_SHIFT-PAGE_ADD_SHIFT,\pte
+ depdi _PAGE_SIZE_ENCODING_DEFAULT,63,\
+ (63-58)+PAGE_ADD_SHIFT,\pte
+ .endm
+
/* Convert the pte and prot to tlb insertion values. How
* this happens is quite subtle, read below */
.macro make_insert_tlb spc,pte,prot
@@ -544,7 +528,7 @@
* B <-> _PAGE_DMB (memory break)
*
* Then incredible subtlety: The access rights are
- * _PAGE_GATEWAY _PAGE_EXEC _PAGE_READ
+ * _PAGE_GATEWAY, _PAGE_EXEC and _PAGE_WRITE
* See 3-14 of the parisc 2.0 manual
*
* Finally, _PAGE_READ goes in the top bit of PL1 (so we
@@ -554,7 +538,7 @@
/* PAGE_USER indicates the page can be read with user privileges,
* so deposit X1|11 to PL1|PL2 (remember the upper bit of PL1
- * contains _PAGE_READ */
+ * contains _PAGE_READ) */
extrd,u,*= \pte,_PAGE_USER_BIT+32,1,%r0
depdi 7,11,3,\prot
/* If we're a gateway page, drop PL2 back to zero for promotion
@@ -563,10 +547,17 @@
extrd,u,*= \pte,_PAGE_GATEWAY_BIT+32,1,%r0
depd %r0,11,2,\prot /* If Gateway, Set PL2 to 0 */
- /* Get rid of prot bits and convert to page addr for iitlbt */
+ /* Enforce uncacheable pages.
+ * This should ONLY be use for MMIO on PA 2.0 machines.
+ * Memory/DMA is cache coherent on all PA2.0 machines we support
+ * (that means T-class is NOT supported) and the memory controllers
+ * on most of those machines only handles cache transactions.
+ */
+ extrd,u,*= \pte,_PAGE_NO_CACHE_BIT+32,1,%r0
+ depdi 1,12,1,\prot
- depd %r0,63,PAGE_SHIFT,\pte
- extrd,u \pte,56,32,\pte
+ /* Drop prot bits and convert to page addr for iitlbt and idtlbt */
+ convert_for_tlb_insert20 \pte
.endm
/* Identical macro to make_insert_tlb above, except it
@@ -584,9 +575,8 @@
/* Get rid of prot bits and convert to page addr for iitlba */
- depi 0,31,12,\pte
- extru \pte,24,25,\pte
-
+ depi 0,31,ASM_PFN_PTE_SHIFT,\pte
+ SHRREG \pte,(ASM_PFN_PTE_SHIFT-(31-26)),\pte
.endm
/* This is for ILP32 PA2.0 only. The TLB insertion needs
@@ -607,7 +597,7 @@
* entry (identifying the physical page) and %r23 up with
* the from tlb entry (or nothing if only a to entry---for
* clear_user_page_asm) */
- .macro do_alias spc,tmp,tmp1,va,pte,prot,fault
+ .macro do_alias spc,tmp,tmp1,va,pte,prot,fault,patype
cmpib,COND(<>),n 0,\spc,\fault
ldil L%(TMPALIAS_MAP_START),\tmp
#if defined(CONFIG_64BIT) && (TMPALIAS_MAP_START >= 0x80000000)
@@ -616,10 +606,35 @@
depdi 0,31,32,\tmp
#endif
copy \va,\tmp1
- DEPI 0,31,23,\tmp1
+ depi 0,31,23,\tmp1
cmpb,COND(<>),n \tmp,\tmp1,\fault
- ldi (_PAGE_DIRTY|_PAGE_WRITE|_PAGE_READ),\prot
+ mfctl %cr19,\tmp /* iir */
+ /* get the opcode (first six bits) into \tmp */
+ extrw,u \tmp,5,6,\tmp
+ /*
+ * Only setting the T bit prevents data cache movein
+ * Setting access rights to zero prevents instruction cache movein
+ *
+ * Note subtlety here: _PAGE_GATEWAY, _PAGE_EXEC and _PAGE_WRITE go
+ * to type field and _PAGE_READ goes to top bit of PL1
+ */
+ ldi (_PAGE_REFTRAP|_PAGE_READ|_PAGE_WRITE),\prot
+ /*
+ * so if the opcode is one (i.e. this is a memory management
+ * instruction) nullify the next load so \prot is only T.
+ * Otherwise this is a normal data operation
+ */
+ cmpiclr,= 0x01,\tmp,%r0
+ ldi (_PAGE_DIRTY|_PAGE_READ|_PAGE_WRITE),\prot
+.ifc \patype,20
depd,z \prot,8,7,\prot
+.else
+.ifc \patype,11
+ depw,z \prot,8,7,\prot
+.else
+ .error "undefined PA type to do_alias"
+.endif
+.endif
/*
* OK, it is in the temp alias region, check whether "from" or "to".
* Check "subtle" note in pacache.S re: r23/r26.
@@ -643,13 +658,11 @@
* the static part of the kernel address space.
*/
- .export fault_vector_20
-
.text
.align 4096
-fault_vector_20:
+ENTRY(fault_vector_20)
/* First vector is invalid (0) */
.ascii "cows can fly"
.byte 0
@@ -670,11 +683,7 @@ fault_vector_20:
def 13
def 14
dtlb_20 15
-#if 0
naitlb_20 16
-#else
- def 16
-#endif
nadtlb_20 17
def 18
def 19
@@ -690,14 +699,13 @@ fault_vector_20:
def 29
def 30
def 31
+END(fault_vector_20)
#ifndef CONFIG_64BIT
- .export fault_vector_11
-
.align 2048
-fault_vector_11:
+ENTRY(fault_vector_11)
/* First vector is invalid (0) */
.ascii "cows can fly"
.byte 0
@@ -718,11 +726,7 @@ fault_vector_11:
def 13
def 14
dtlb_11 15
-#if 0
naitlb_11 16
-#else
- def 16
-#endif
nadtlb_11 17
def 18
def 19
@@ -738,129 +742,47 @@ fault_vector_11:
def 29
def 30
def 31
+END(fault_vector_11)
#endif
+ /* Fault vector is separately protected and *must* be on its own page */
+ .align PAGE_SIZE
+ENTRY(end_fault_vector)
.import handle_interruption,code
.import do_cpu_irq_mask,code
/*
- * r26 = function to be called
- * r25 = argument to pass in
- * r24 = flags for do_fork()
- *
- * Kernel threads don't ever return, so they don't need
- * a true register context. We just save away the arguments
- * for copy_thread/ret_ to properly set up the child.
- */
-
-#define CLONE_VM 0x100 /* Must agree with <linux/sched.h> */
-#define CLONE_UNTRACED 0x00800000
-
- .export __kernel_thread, code
- .import do_fork
-__kernel_thread:
- STREG %r2, -RP_OFFSET(%r30)
-
- copy %r30, %r1
- ldo PT_SZ_ALGN(%r30),%r30
-#ifdef CONFIG_64BIT
- /* Yo, function pointers in wide mode are little structs... -PB */
- ldd 24(%r26), %r2
- STREG %r2, PT_GR27(%r1) /* Store childs %dp */
- ldd 16(%r26), %r26
-
- STREG %r22, PT_GR22(%r1) /* save r22 (arg5) */
- copy %r0, %r22 /* user_tid */
-#endif
- STREG %r26, PT_GR26(%r1) /* Store function & argument for child */
- STREG %r25, PT_GR25(%r1)
- ldil L%CLONE_UNTRACED, %r26
- ldo CLONE_VM(%r26), %r26 /* Force CLONE_VM since only init_mm */
- or %r26, %r24, %r26 /* will have kernel mappings. */
- ldi 1, %r25 /* stack_start, signals kernel thread */
- stw %r0, -52(%r30) /* user_tid */
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
- BL do_fork, %r2
- copy %r1, %r24 /* pt_regs */
-
- /* Parent Returns here */
-
- LDREG -PT_SZ_ALGN-RP_OFFSET(%r30), %r2
- ldo -PT_SZ_ALGN(%r30), %r30
- bv %r0(%r2)
- nop
-
- /*
* Child Returns here
*
- * copy_thread moved args from temp save area set up above
- * into task save area.
+ * copy_thread moved args into task save area.
*/
- .export ret_from_kernel_thread
-ret_from_kernel_thread:
+ENTRY(ret_from_kernel_thread)
/* Call schedule_tail first though */
BL schedule_tail, %r2
nop
- LDREG TI_TASK-THREAD_SZ_ALGN(%r30), %r1
+ LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
LDREG TASK_PT_GR25(%r1), %r26
#ifdef CONFIG_64BIT
LDREG TASK_PT_GR27(%r1), %r27
- LDREG TASK_PT_GR22(%r1), %r22
#endif
LDREG TASK_PT_GR26(%r1), %r1
ble 0(%sr7, %r1)
copy %r31, %r2
-
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
- loadgp /* Thread could have been in a module */
-#endif
-#ifndef CONFIG_64BIT
- b sys_exit
-#else
- load32 sys_exit, %r1
- bv %r0(%r1)
-#endif
- ldi 0, %r26
-
- .import sys_execve, code
- .export __execve, code
-__execve:
- copy %r2, %r15
- copy %r30, %r16
- ldo PT_SZ_ALGN(%r30), %r30
- STREG %r26, PT_GR26(%r16)
- STREG %r25, PT_GR25(%r16)
- STREG %r24, PT_GR24(%r16)
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
- BL sys_execve, %r2
- copy %r16, %r26
-
- cmpib,=,n 0,%r28,intr_return /* forward */
-
- /* yes, this will trap and die. */
- copy %r15, %r2
- copy %r16, %r30
- bv %r0(%r2)
+ b finish_child_return
nop
+ENDPROC(ret_from_kernel_thread)
- .align 4
/*
* struct task_struct *_switch_to(struct task_struct *prev,
* struct task_struct *next)
*
* switch kernel stacks and return prev */
- .export _switch_to, code
-_switch_to:
+ENTRY(_switch_to)
STREG %r2, -RP_OFFSET(%r30)
callee_save_float
@@ -885,6 +807,7 @@ _switch_to_ret:
LDREG -RP_OFFSET(%r30), %r2
bv %r0(%r2)
copy %r26, %r28
+ENDPROC(_switch_to)
/*
* Common rfi return path for interruptions, kernel execve, and
@@ -900,10 +823,9 @@ _switch_to_ret:
*
*/
- .align 4096
+ .align PAGE_SIZE
- .export syscall_exit_rfi
-syscall_exit_rfi:
+ENTRY(syscall_exit_rfi)
mfctl %cr30,%r16
LDREG TI_TASK(%r16), %r16 /* thread_info -> task_struct */
ldo TASK_REGS(%r16),%r16
@@ -932,9 +854,9 @@ syscall_exit_rfi:
* (we don't store them in the sigcontext), so set them
* to "proper" values now (otherwise we'll wind up restoring
* whatever was last stored in the task structure, which might
- * be inconsistent if an interrupt occured while on the gateway
- * page) Note that we may be "trashing" values the user put in
- * them, but we don't support the the user changing them.
+ * be inconsistent if an interrupt occurred while on the gateway
+ * page). Note that we may be "trashing" values the user put in
+ * them, but we don't support the user changing them.
*/
STREG %r0,PT_SR2(%r16)
@@ -948,40 +870,46 @@ syscall_exit_rfi:
STREG %r19,PT_SR7(%r16)
intr_return:
- /* NOTE: Need to enable interrupts incase we schedule. */
- ssm PSW_SM_I, %r0
-
- /* Check for software interrupts */
-
- .import irq_stat,data
-
- load32 irq_stat,%r19
-#ifdef CONFIG_SMP
- mfctl %cr30,%r1
- ldw TI_CPU(%r1),%r1 /* get cpu # - int */
- /* shift left ____cacheline_aligned (aka L1_CACHE_BYTES) amount
- ** irq_stat[] is defined using ____cacheline_aligned.
- */
-#ifdef CONFIG_64BIT
- shld %r1, 6, %r20
-#else
- shlw %r1, 5, %r20
-#endif
- add %r19,%r20,%r19 /* now have &irq_stat[smp_processor_id()] */
-#endif /* CONFIG_SMP */
-
-intr_check_resched:
-
/* check for reschedule */
mfctl %cr30,%r1
LDREG TI_FLAGS(%r1),%r19 /* sched.h: TIF_NEED_RESCHED */
bb,<,n %r19,31-TIF_NEED_RESCHED,intr_do_resched /* forward */
+ .import do_notify_resume,code
intr_check_sig:
/* As above */
mfctl %cr30,%r1
- LDREG TI_FLAGS(%r1),%r19 /* sched.h: TIF_SIGPENDING */
- bb,<,n %r19, 31-TIF_SIGPENDING, intr_do_signal /* forward */
+ LDREG TI_FLAGS(%r1),%r19
+ ldi (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), %r20
+ and,COND(<>) %r19, %r20, %r0
+ b,n intr_restore /* skip past if we've nothing to do */
+
+ /* This check is critical to having LWS
+ * working. The IASQ is zero on the gateway
+ * page and we cannot deliver any signals until
+ * we get off the gateway page.
+ *
+ * Only do signals if we are returning to user space
+ */
+ LDREG PT_IASQ0(%r16), %r20
+ cmpib,COND(=),n 0,%r20,intr_restore /* backward */
+ LDREG PT_IASQ1(%r16), %r20
+ cmpib,COND(=),n 0,%r20,intr_restore /* backward */
+
+ /* NOTE: We need to enable interrupts if we have to deliver
+ * signals. We used to do this earlier but it caused kernel
+ * stack overflows. */
+ ssm PSW_SM_I, %r0
+
+ copy %r0, %r25 /* long in_syscall = 0 */
+#ifdef CONFIG_64BIT
+ ldo -16(%r30),%r29 /* Reference param save area */
+#endif
+
+ BL do_notify_resume,%r2
+ copy %r16, %r26 /* struct pt_regs *regs */
+
+ b,n intr_check_sig
intr_restore:
copy %r16,%r29
@@ -1006,24 +934,28 @@ intr_restore:
rfi
nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
+
+#ifndef CONFIG_PREEMPT
+# define intr_do_preempt intr_restore
+#endif /* !CONFIG_PREEMPT */
.import schedule,code
intr_do_resched:
- /* Only do reschedule if we are returning to user space */
+ /* Only call schedule on return to userspace. If we're returning
+ * to kernel space, we may schedule if CONFIG_PREEMPT, otherwise
+ * we jump back to intr_restore.
+ */
LDREG PT_IASQ0(%r16), %r20
- CMPIB= 0,%r20,intr_restore /* backward */
+ cmpib,COND(=) 0, %r20, intr_do_preempt
nop
LDREG PT_IASQ1(%r16), %r20
- CMPIB= 0,%r20,intr_restore /* backward */
+ cmpib,COND(=) 0, %r20, intr_do_preempt
nop
+ /* NOTE: We need to enable interrupts if we schedule. We used
+ * to do this earlier but it caused kernel stack overflows. */
+ ssm PSW_SM_I, %r0
+
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
@@ -1037,63 +969,46 @@ intr_do_resched:
#endif
ldo R%intr_check_sig(%r2), %r2
-
- .import do_signal,code
-intr_do_signal:
- /*
- This check is critical to having LWS
- working. The IASQ is zero on the gateway
- page and we cannot deliver any signals until
- we get off the gateway page.
-
- Only do signals if we are returning to user space
- */
- LDREG PT_IASQ0(%r16), %r20
- CMPIB= 0,%r20,intr_restore /* backward */
- nop
- LDREG PT_IASQ1(%r16), %r20
- CMPIB= 0,%r20,intr_restore /* backward */
+ /* preempt the current task on returning to kernel
+ * mode from an interrupt, iff need_resched is set,
+ * and preempt_count is 0. otherwise, we continue on
+ * our merry way back to the current running task.
+ */
+#ifdef CONFIG_PREEMPT
+ .import preempt_schedule_irq,code
+intr_do_preempt:
+ rsm PSW_SM_I, %r0 /* disable interrupts */
+
+ /* current_thread_info()->preempt_count */
+ mfctl %cr30, %r1
+ LDREG TI_PRE_COUNT(%r1), %r19
+ cmpib,COND(<>) 0, %r19, intr_restore /* if preempt_count > 0 */
+ nop /* prev insn branched backwards */
+
+ /* check if we interrupted a critical path */
+ LDREG PT_PSW(%r16), %r20
+ bb,<,n %r20, 31 - PSW_SM_I, intr_restore
nop
- copy %r0, %r24 /* unsigned long in_syscall */
- copy %r16, %r25 /* struct pt_regs *regs */
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
-
- BL do_signal,%r2
- copy %r0, %r26 /* sigset_t *oldset = NULL */
-
- b intr_check_sig
+ BL preempt_schedule_irq, %r2
nop
+ b,n intr_restore /* ssm PSW_SM_I done by intr_restore */
+#endif /* CONFIG_PREEMPT */
+
/*
* External interrupts.
*/
intr_extint:
- CMPIB=,n 0,%r16,1f
+ cmpib,COND(=),n 0,%r16,1f
+
get_stack_use_cr30
- b,n 3f
+ b,n 2f
1:
-#if 0 /* Interrupt Stack support not working yet! */
- mfctl %cr31,%r1
- copy %r30,%r17
- /* FIXME! depi below has hardcoded idea of interrupt stack size (32k)*/
-#ifdef CONFIG_64BIT
- depdi 0,63,15,%r17
-#else
- depi 0,31,15,%r17
-#endif
- CMPB=,n %r1,%r17,2f
- get_stack_use_cr31
- b,n 3f
-#endif
-2:
get_stack_use_r30
-
-3:
+2:
save_specials %r29
virt_map
save_general %r29
@@ -1114,15 +1029,14 @@ intr_extint:
b do_cpu_irq_mask
ldo R%intr_return(%r2), %r2 /* return to intr_return, not here */
+ENDPROC(syscall_exit_rfi)
/* Generic interruptions (illegal insn, unaligned, page fault, etc) */
- .export intr_save, code /* for os_hpmc */
-
-intr_save:
+ENTRY(intr_save) /* for os_hpmc */
mfsp %sr7,%r16
- CMPIB=,n 0,%r16,1f
+ cmpib,COND(=),n 0,%r16,1f
get_stack_use_cr30
b 2f
copy %r8,%r26
@@ -1144,7 +1058,7 @@ intr_save:
* adjust isr/ior below.
*/
- CMPIB=,n 6,%r26,skip_save_ior
+ cmpib,COND(=),n 6,%r26,skip_save_ior
mfctl %cr20, %r16 /* isr */
@@ -1168,10 +1082,9 @@ intr_save:
*/
/* adjust isr/ior. */
-
- extrd,u %r16,63,7,%r1 /* get high bits from isr for ior */
- depd %r1,31,7,%r17 /* deposit them into ior */
- depdi 0,63,7,%r16 /* clear them from isr */
+ extrd,u %r16,63,SPACEID_SHIFT,%r1 /* get high bits from isr for ior */
+ depd %r1,31,SPACEID_SHIFT,%r17 /* deposit them into ior */
+ depdi 0,63,SPACEID_SHIFT,%r16 /* clear them from isr */
#endif
STREG %r16, PT_ISR(%r29)
STREG %r17, PT_IOR(%r29)
@@ -1196,6 +1109,7 @@ skip_save_ior:
b handle_interruption
ldo R%intr_check_sig(%r2), %r2
+ENDPROC(intr_save)
/*
@@ -1218,11 +1132,11 @@ skip_save_ior:
*/
t0 = r1 /* temporary register 0 */
- va = r8 /* virtual address for which the trap occured */
+ va = r8 /* virtual address for which the trap occurred */
t1 = r9 /* temporary register 1 */
pte = r16 /* pte/phys page # */
prot = r17 /* prot bits */
- spc = r24 /* space for which the trap occured */
+ spc = r24 /* space for which the trap occurred */
ptp = r25 /* page directory/page table pointer */
#ifdef CONFIG_64BIT
@@ -1234,17 +1148,19 @@ dtlb_miss_20w:
L3_ptep ptp,pte,t0,va,dtlb_check_alias_20w
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb spc,pte,prot
idtlbt pte,prot
+ dbit_unlock1 spc,t0
rfir
nop
dtlb_check_alias_20w:
- do_alias spc,t0,t1,va,pte,prot,dtlb_fault
+ do_alias spc,t0,t1,va,pte,prot,dtlb_fault,20
idtlbt pte,prot
@@ -1256,29 +1172,22 @@ nadtlb_miss_20w:
get_pgd spc,ptp
space_check spc,t0,nadtlb_fault
- L3_ptep ptp,pte,t0,va,nadtlb_check_flush_20w
+ L3_ptep ptp,pte,t0,va,nadtlb_check_alias_20w
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb spc,pte,prot
idtlbt pte,prot
+ dbit_unlock1 spc,t0
rfir
nop
-nadtlb_check_flush_20w:
- bb,>=,n pte,_PAGE_FLUSH_BIT,nadtlb_emulate
-
- /* Insert a "flush only" translation */
+nadtlb_check_alias_20w:
+ do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,20
- depdi,z 7,7,3,prot
- depdi 1,10,1,prot
-
- /* Get rid of prot bits and convert to page addr for idtlbt */
-
- depdi 0,63,12,pte
- extrd,u pte,56,52,pte
idtlbt pte,prot
rfir
@@ -1293,7 +1202,8 @@ dtlb_miss_11:
L2_ptep ptp,pte,t0,va,dtlb_check_alias_11
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb_11 spc,pte,prot
@@ -1304,30 +1214,13 @@ dtlb_miss_11:
idtlbp prot,(%sr1,va)
mtsp t0, %sr1 /* Restore sr1 */
+ dbit_unlock1 spc,t0
rfir
nop
dtlb_check_alias_11:
-
- /* Check to see if fault is in the temporary alias region */
-
- cmpib,<>,n 0,spc,dtlb_fault /* forward */
- ldil L%(TMPALIAS_MAP_START),t0
- copy va,t1
- depwi 0,31,23,t1
- cmpb,<>,n t0,t1,dtlb_fault /* forward */
- ldi (_PAGE_DIRTY|_PAGE_WRITE|_PAGE_READ),prot
- depw,z prot,8,7,prot
-
- /*
- * OK, it is in the temp alias region, check whether "from" or "to".
- * Check "subtle" note in pacache.S re: r23/r26.
- */
-
- extrw,u,= va,9,1,r0
- or,tr %r23,%r0,pte /* If "from" use "from" page */
- or %r26,%r0,pte /* else "to", use "to" page */
+ do_alias spc,t0,t1,va,pte,prot,dtlb_fault,11
idtlba pte,(va)
idtlbp prot,(va)
@@ -1340,9 +1233,10 @@ nadtlb_miss_11:
space_check spc,t0,nadtlb_fault
- L2_ptep ptp,pte,t0,va,nadtlb_check_flush_11
+ L2_ptep ptp,pte,t0,va,nadtlb_check_alias_11
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb_11 spc,pte,prot
@@ -1354,30 +1248,16 @@ nadtlb_miss_11:
idtlbp prot,(%sr1,va)
mtsp t0, %sr1 /* Restore sr1 */
+ dbit_unlock1 spc,t0
rfir
nop
-nadtlb_check_flush_11:
- bb,>=,n pte,_PAGE_FLUSH_BIT,nadtlb_emulate
-
- /* Insert a "flush only" translation */
-
- zdepi 7,7,3,prot
- depi 1,10,1,prot
+nadtlb_check_alias_11:
+ do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,11
- /* Get rid of prot bits and convert to page addr for idtlba */
-
- depi 0,31,12,pte
- extru pte,24,25,pte
-
- mfsp %sr1,t0 /* Save sr1 so we can use it in tlb inserts */
- mtsp spc,%sr1
-
- idtlba pte,(%sr1,va)
- idtlbp prot,(%sr1,va)
-
- mtsp t0, %sr1 /* Restore sr1 */
+ idtlba pte,(va)
+ idtlbp prot,(va)
rfir
nop
@@ -1389,19 +1269,21 @@ dtlb_miss_20:
L2_ptep ptp,pte,t0,va,dtlb_check_alias_20
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb spc,pte,prot
f_extend pte,t0
idtlbt pte,prot
+ dbit_unlock1 spc,t0
rfir
nop
dtlb_check_alias_20:
- do_alias spc,t0,t1,va,pte,prot,dtlb_fault
+ do_alias spc,t0,t1,va,pte,prot,dtlb_fault,20
idtlbt pte,prot
@@ -1413,35 +1295,29 @@ nadtlb_miss_20:
space_check spc,t0,nadtlb_fault
- L2_ptep ptp,pte,t0,va,nadtlb_check_flush_20
+ L2_ptep ptp,pte,t0,va,nadtlb_check_alias_20
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb spc,pte,prot
f_extend pte,t0
idtlbt pte,prot
+ dbit_unlock1 spc,t0
rfir
nop
-nadtlb_check_flush_20:
- bb,>=,n pte,_PAGE_FLUSH_BIT,nadtlb_emulate
-
- /* Insert a "flush only" translation */
-
- depdi,z 7,7,3,prot
- depdi 1,10,1,prot
-
- /* Get rid of prot bits and convert to page addr for idtlbt */
+nadtlb_check_alias_20:
+ do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,20
- depdi 0,63,12,pte
- extrd,u pte,56,32,pte
idtlbt pte,prot
rfir
nop
+
#endif
nadtlb_emulate:
@@ -1473,11 +1349,11 @@ nadtlb_emulate:
bb,>=,n %r9,26,nadtlb_nullify /* m bit not set, just nullify */
BL get_register,%r25
extrw,u %r9,15,5,%r8 /* Get index register # */
- CMPIB=,n -1,%r1,nadtlb_fault /* have to use slow path */
+ cmpib,COND(=),n -1,%r1,nadtlb_fault /* have to use slow path */
copy %r1,%r24
BL get_register,%r25
extrw,u %r9,10,5,%r8 /* Get base register # */
- CMPIB=,n -1,%r1,nadtlb_fault /* have to use slow path */
+ cmpib,COND(=),n -1,%r1,nadtlb_fault /* have to use slow path */
BL set_register,%r25
add,l %r1,%r24,%r1 /* doesn't affect c/b bits */
@@ -1509,7 +1385,7 @@ nadtlb_probe_check:
cmpb,<>,n %r16,%r17,nadtlb_fault /* Must be probe,[rw]*/
BL get_register,%r25 /* Find the target register */
extrw,u %r9,31,5,%r8 /* Get target register */
- CMPIB=,n -1,%r1,nadtlb_fault /* have to use slow path */
+ cmpib,COND(=),n -1,%r1,nadtlb_fault /* have to use slow path */
BL set_register,%r25
copy %r0,%r1 /* Write zero to target register */
b nadtlb_nullify /* Nullify return insn */
@@ -1530,11 +1406,45 @@ itlb_miss_20w:
L3_ptep ptp,pte,t0,va,itlb_fault
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb spc,pte,prot
iitlbt pte,prot
+ dbit_unlock1 spc,t0
+
+ rfir
+ nop
+
+naitlb_miss_20w:
+
+ /*
+ * I miss is a little different, since we allow users to fault
+ * on the gateway page which is in the kernel address space.
+ */
+
+ space_adjust spc,va,t0
+ get_pgd spc,ptp
+ space_check spc,t0,naitlb_fault
+
+ L3_ptep ptp,pte,t0,va,naitlb_check_alias_20w
+
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
+
+ make_insert_tlb spc,pte,prot
+
+ iitlbt pte,prot
+ dbit_unlock1 spc,t0
+
+ rfir
+ nop
+
+naitlb_check_alias_20w:
+ do_alias spc,t0,t1,va,pte,prot,naitlb_fault,20
+
+ iitlbt pte,prot
rfir
nop
@@ -1548,7 +1458,8 @@ itlb_miss_11:
L2_ptep ptp,pte,t0,va,itlb_fault
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb_11 spc,pte,prot
@@ -1559,10 +1470,45 @@ itlb_miss_11:
iitlbp prot,(%sr1,va)
mtsp t0, %sr1 /* Restore sr1 */
+ dbit_unlock1 spc,t0
rfir
nop
+naitlb_miss_11:
+ get_pgd spc,ptp
+
+ space_check spc,t0,naitlb_fault
+
+ L2_ptep ptp,pte,t0,va,naitlb_check_alias_11
+
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
+
+ make_insert_tlb_11 spc,pte,prot
+
+ mfsp %sr1,t0 /* Save sr1 so we can use it in tlb inserts */
+ mtsp spc,%sr1
+
+ iitlba pte,(%sr1,va)
+ iitlbp prot,(%sr1,va)
+
+ mtsp t0, %sr1 /* Restore sr1 */
+ dbit_unlock1 spc,t0
+
+ rfir
+ nop
+
+naitlb_check_alias_11:
+ do_alias spc,t0,t1,va,pte,prot,itlb_fault,11
+
+ iitlba pte,(%sr0, va)
+ iitlbp prot,(%sr0, va)
+
+ rfir
+ nop
+
+
itlb_miss_20:
get_pgd spc,ptp
@@ -1570,13 +1516,43 @@ itlb_miss_20:
L2_ptep ptp,pte,t0,va,itlb_fault
- update_ptep ptp,pte,t0,t1
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
make_insert_tlb spc,pte,prot
f_extend pte,t0
iitlbt pte,prot
+ dbit_unlock1 spc,t0
+
+ rfir
+ nop
+
+naitlb_miss_20:
+ get_pgd spc,ptp
+
+ space_check spc,t0,naitlb_fault
+
+ L2_ptep ptp,pte,t0,va,naitlb_check_alias_20
+
+ dbit_lock spc,t0,t1
+ update_ptep spc,ptp,pte,t0,t1
+
+ make_insert_tlb spc,pte,prot
+
+ f_extend pte,t0
+
+ iitlbt pte,prot
+ dbit_unlock1 spc,t0
+
+ rfir
+ nop
+
+naitlb_check_alias_20:
+ do_alias spc,t0,t1,va,pte,prot,naitlb_fault,20
+
+ iitlbt pte,prot
rfir
nop
@@ -1592,29 +1568,13 @@ dbit_trap_20w:
L3_ptep ptp,pte,t0,va,dbit_fault
-#ifdef CONFIG_SMP
- CMPIB=,n 0,spc,dbit_nolock_20w
- load32 PA(pa_dbit_lock),t0
-
-dbit_spin_20w:
- ldcw 0(t0),t1
- cmpib,= 0,t1,dbit_spin_20w
- nop
-
-dbit_nolock_20w:
-#endif
- update_dirty ptp,pte,t1
+ dbit_lock spc,t0,t1
+ update_dirty spc,ptp,pte,t1
make_insert_tlb spc,pte,prot
idtlbt pte,prot
-#ifdef CONFIG_SMP
- CMPIB=,n 0,spc,dbit_nounlock_20w
- ldi 1,t1
- stw t1,0(t0)
-
-dbit_nounlock_20w:
-#endif
+ dbit_unlock0 spc,t0
rfir
nop
@@ -1628,18 +1588,8 @@ dbit_trap_11:
L2_ptep ptp,pte,t0,va,dbit_fault
-#ifdef CONFIG_SMP
- CMPIB=,n 0,spc,dbit_nolock_11
- load32 PA(pa_dbit_lock),t0
-
-dbit_spin_11:
- ldcw 0(t0),t1
- cmpib,= 0,t1,dbit_spin_11
- nop
-
-dbit_nolock_11:
-#endif
- update_dirty ptp,pte,t1
+ dbit_lock spc,t0,t1
+ update_dirty spc,ptp,pte,t1
make_insert_tlb_11 spc,pte,prot
@@ -1650,13 +1600,7 @@ dbit_nolock_11:
idtlbp prot,(%sr1,va)
mtsp t1, %sr1 /* Restore sr1 */
-#ifdef CONFIG_SMP
- CMPIB=,n 0,spc,dbit_nounlock_11
- ldi 1,t1
- stw t1,0(t0)
-
-dbit_nounlock_11:
-#endif
+ dbit_unlock0 spc,t0
rfir
nop
@@ -1668,32 +1612,15 @@ dbit_trap_20:
L2_ptep ptp,pte,t0,va,dbit_fault
-#ifdef CONFIG_SMP
- CMPIB=,n 0,spc,dbit_nolock_20
- load32 PA(pa_dbit_lock),t0
-
-dbit_spin_20:
- ldcw 0(t0),t1
- cmpib,= 0,t1,dbit_spin_20
- nop
-
-dbit_nolock_20:
-#endif
- update_dirty ptp,pte,t1
+ dbit_lock spc,t0,t1
+ update_dirty spc,ptp,pte,t1
make_insert_tlb spc,pte,prot
f_extend pte,t1
idtlbt pte,prot
-
-#ifdef CONFIG_SMP
- CMPIB=,n 0,spc,dbit_nounlock_20
- ldi 1,t1
- stw t1,0(t0)
-
-dbit_nounlock_20:
-#endif
+ dbit_unlock0 spc,t0
rfir
nop
@@ -1717,6 +1644,10 @@ nadtlb_fault:
b intr_save
ldi 17,%r8
+naitlb_fault:
+ b intr_save
+ ldi 16,%r8
+
dtlb_fault:
b intr_save
ldi 15,%r8
@@ -1781,153 +1712,38 @@ dtlb_fault:
LDREG PT_GR18(\regs),%r18
.endm
- .export sys_fork_wrapper
- .export child_return
-sys_fork_wrapper:
+ .macro fork_like name
+ENTRY(sys_\name\()_wrapper)
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
ldo TASK_REGS(%r1),%r1
reg_save %r1
- mfctl %cr27, %r3
- STREG %r3, PT_CR27(%r1)
-
- STREG %r2,-RP_OFFSET(%r30)
- ldo FRAME_SIZE(%r30),%r30
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
-
- /* These are call-clobbered registers and therefore
- also syscall-clobbered (we hope). */
- STREG %r2,PT_GR19(%r1) /* save for child */
- STREG %r30,PT_GR21(%r1)
+ mfctl %cr27, %r28
+ ldil L%sys_\name, %r31
+ be R%sys_\name(%sr4,%r31)
+ STREG %r28, PT_CR27(%r1)
+ENDPROC(sys_\name\()_wrapper)
+ .endm
- LDREG PT_GR30(%r1),%r25
- copy %r1,%r24
- BL sys_clone,%r2
- ldi SIGCHLD,%r26
+fork_like clone
+fork_like fork
+fork_like vfork
- LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
-wrapper_exit:
- ldo -FRAME_SIZE(%r30),%r30 /* get the stackframe */
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
+ /* Set the return value for the child */
+ENTRY(child_return)
+ BL schedule_tail, %r2
+ nop
+finish_child_return:
+ LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
ldo TASK_REGS(%r1),%r1 /* get pt regs */
LDREG PT_CR27(%r1), %r3
mtctl %r3, %cr27
reg_restore %r1
-
- /* strace expects syscall # to be preserved in r20 */
- ldi __NR_fork,%r20
- bv %r0(%r2)
- STREG %r20,PT_GR20(%r1)
-
- /* Set the return value for the child */
-child_return:
- BL schedule_tail, %r2
- nop
-
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE-FRAME_SIZE(%r30), %r1
- LDREG TASK_PT_GR19(%r1),%r2
- b wrapper_exit
+ b syscall_exit
copy %r0,%r28
+ENDPROC(child_return)
-
- .export sys_clone_wrapper
-sys_clone_wrapper:
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
- ldo TASK_REGS(%r1),%r1 /* get pt regs */
- reg_save %r1
- mfctl %cr27, %r3
- STREG %r3, PT_CR27(%r1)
-
- STREG %r2,-RP_OFFSET(%r30)
- ldo FRAME_SIZE(%r30),%r30
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
-
- STREG %r2,PT_GR19(%r1) /* save for child */
- STREG %r30,PT_GR21(%r1)
- BL sys_clone,%r2
- copy %r1,%r24
-
- b wrapper_exit
- LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
-
- .export sys_vfork_wrapper
-sys_vfork_wrapper:
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
- ldo TASK_REGS(%r1),%r1 /* get pt regs */
- reg_save %r1
- mfctl %cr27, %r3
- STREG %r3, PT_CR27(%r1)
-
- STREG %r2,-RP_OFFSET(%r30)
- ldo FRAME_SIZE(%r30),%r30
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
-
- STREG %r2,PT_GR19(%r1) /* save for child */
- STREG %r30,PT_GR21(%r1)
-
- BL sys_vfork,%r2
- copy %r1,%r26
-
- b wrapper_exit
- LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
-
-
- .macro execve_wrapper execve
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
- ldo TASK_REGS(%r1),%r1 /* get pt regs */
-
- /*
- * Do we need to save/restore r3-r18 here?
- * I don't think so. why would new thread need old
- * threads registers?
- */
-
- /* %arg0 - %arg3 are already saved for us. */
-
- STREG %r2,-RP_OFFSET(%r30)
- ldo FRAME_SIZE(%r30),%r30
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
- BL \execve,%r2
- copy %r1,%arg0
-
- ldo -FRAME_SIZE(%r30),%r30
- LDREG -RP_OFFSET(%r30),%r2
-
- /* If exec succeeded we need to load the args */
-
- ldo -1024(%r0),%r1
- cmpb,>>= %r28,%r1,error_\execve
- copy %r2,%r19
-
-error_\execve:
- bv %r0(%r19)
- nop
- .endm
-
- .export sys_execve_wrapper
- .import sys_execve
-
-sys_execve_wrapper:
- execve_wrapper sys_execve
-
-#ifdef CONFIG_64BIT
- .export sys32_execve_wrapper
- .import sys32_execve
-
-sys32_execve_wrapper:
- execve_wrapper sys32_execve
-#endif
-
- .export sys_rt_sigreturn_wrapper
-sys_rt_sigreturn_wrapper:
+ENTRY(sys_rt_sigreturn_wrapper)
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r26
ldo TASK_REGS(%r26),%r26 /* get pt regs */
/* Don't save regs, we are going to restore them from sigcontext. */
@@ -1955,74 +1771,9 @@ sys_rt_sigreturn_wrapper:
*/
bv %r0(%r2)
LDREG PT_GR28(%r1),%r28 /* reload original r28 for syscall_exit */
+ENDPROC(sys_rt_sigreturn_wrapper)
- .export sys_sigaltstack_wrapper
-sys_sigaltstack_wrapper:
- /* Get the user stack pointer */
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
- ldo TASK_REGS(%r1),%r24 /* get pt regs */
- LDREG TASK_PT_GR30(%r24),%r24
- STREG %r2, -RP_OFFSET(%r30)
-#ifdef CONFIG_64BIT
- ldo FRAME_SIZE(%r30), %r30
- b,l do_sigaltstack,%r2
- ldo -16(%r30),%r29 /* Reference param save area */
-#else
- bl do_sigaltstack,%r2
- ldo FRAME_SIZE(%r30), %r30
-#endif
-
- ldo -FRAME_SIZE(%r30), %r30
- LDREG -RP_OFFSET(%r30), %r2
- bv %r0(%r2)
- nop
-
-#ifdef CONFIG_64BIT
- .export sys32_sigaltstack_wrapper
-sys32_sigaltstack_wrapper:
- /* Get the user stack pointer */
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r24
- LDREG TASK_PT_GR30(%r24),%r24
- STREG %r2, -RP_OFFSET(%r30)
- ldo FRAME_SIZE(%r30), %r30
- b,l do_sigaltstack32,%r2
- ldo -16(%r30),%r29 /* Reference param save area */
-
- ldo -FRAME_SIZE(%r30), %r30
- LDREG -RP_OFFSET(%r30), %r2
- bv %r0(%r2)
- nop
-#endif
-
- .export sys_rt_sigsuspend_wrapper
-sys_rt_sigsuspend_wrapper:
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
- ldo TASK_REGS(%r1),%r24
- reg_save %r24
-
- STREG %r2, -RP_OFFSET(%r30)
-#ifdef CONFIG_64BIT
- ldo FRAME_SIZE(%r30), %r30
- b,l sys_rt_sigsuspend,%r2
- ldo -16(%r30),%r29 /* Reference param save area */
-#else
- bl sys_rt_sigsuspend,%r2
- ldo FRAME_SIZE(%r30), %r30
-#endif
-
- ldo -FRAME_SIZE(%r30), %r30
- LDREG -RP_OFFSET(%r30), %r2
-
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
- ldo TASK_REGS(%r1),%r1
- reg_restore %r1
-
- bv %r0(%r2)
- nop
-
- .export syscall_exit
-syscall_exit:
-
+ENTRY(syscall_exit)
/* NOTE: HP-UX syscalls also come through here
* after hpux_syscall_exit fixes up return
* values. */
@@ -2039,14 +1790,13 @@ syscall_exit:
STREG %r28,TASK_PT_GR28(%r1)
#ifdef CONFIG_HPUX
-
/* <linux/personality.h> cannot be easily included */
#define PER_HPUX 0x10
- LDREG TASK_PERSONALITY(%r1),%r19
+ ldw TASK_PERSONALITY(%r1),%r19
/* We can't use "CMPIB<> PER_HPUX" since "im5" field is sign extended */
ldo -PER_HPUX(%r19), %r19
- CMPIB<>,n 0,%r19,1f
+ cmpib,COND(<>),n 0,%r19,1f
/* Save other hpux returns if personality is PER_HPUX */
STREG %r22,TASK_PT_GR22(%r1)
@@ -2060,46 +1810,51 @@ syscall_exit:
*/
loadgp
-syscall_check_bh:
+syscall_check_resched:
- /* Check for software interrupts */
+ /* check for reschedule */
- .import irq_stat,data
+ LDREG TI_FLAGS-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r19 /* long */
+ bb,<,n %r19, 31-TIF_NEED_RESCHED, syscall_do_resched /* forward */
- load32 irq_stat,%r19
+ .import do_signal,code
+syscall_check_sig:
+ LDREG TI_FLAGS-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r19
+ ldi (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), %r26
+ and,COND(<>) %r19, %r26, %r0
+ b,n syscall_restore /* skip past if we've nothing to do */
-#ifdef CONFIG_SMP
- /* sched.h: int processor */
- /* %r26 is used as scratch register to index into irq_stat[] */
- ldw TI_CPU-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r26 /* cpu # */
+syscall_do_signal:
+ /* Save callee-save registers (for sigcontext).
+ * FIXME: After this point the process structure should be
+ * consistent with all the relevant state of the process
+ * before the syscall. We need to verify this.
+ */
+ LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
+ ldo TASK_REGS(%r1), %r26 /* struct pt_regs *regs */
+ reg_save %r26
- /* shift left ____cacheline_aligned (aka L1_CACHE_BYTES) bits */
#ifdef CONFIG_64BIT
- shld %r26, 6, %r20
-#else
- shlw %r26, 5, %r20
+ ldo -16(%r30),%r29 /* Reference param save area */
#endif
- add %r19,%r20,%r19 /* now have &irq_stat[smp_processor_id()] */
-#endif /* CONFIG_SMP */
-syscall_check_resched:
-
- /* check for reschedule */
+ BL do_notify_resume,%r2
+ ldi 1, %r25 /* long in_syscall = 1 */
- LDREG TI_FLAGS-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r19 /* long */
- bb,<,n %r19, 31-TIF_NEED_RESCHED, syscall_do_resched /* forward */
+ LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
+ ldo TASK_REGS(%r1), %r20 /* reload pt_regs */
+ reg_restore %r20
-syscall_check_sig:
- LDREG TI_FLAGS-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r19 /* get ti flags */
- bb,<,n %r19, 31-TIF_SIGPENDING, syscall_do_signal /* forward */
+ b,n syscall_check_sig
syscall_restore:
- /* Are we being ptraced? */
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
- LDREG TASK_PTRACE(%r1), %r19
- bb,< %r19,31,syscall_restore_rfi
- nop
+ /* Are we being ptraced? */
+ ldw TASK_FLAGS(%r1),%r19
+ ldi _TIF_SYSCALL_TRACE_MASK,%r2
+ and,COND(=) %r19,%r2,%r0
+ b,n syscall_restore_rfi
ldo TASK_PT_FR31(%r1),%r19 /* reload fpregs */
rest_fp %r19
@@ -2122,9 +1877,10 @@ syscall_restore:
LDREG TASK_PT_GR31(%r1),%r31 /* restore syscall rp */
/* NOTE: We use rsm/ssm pair to make this operation atomic */
+ LDREG TASK_PT_GR30(%r1),%r1 /* Get user sp */
rsm PSW_SM_I, %r0
- LDREG TASK_PT_GR30(%r1),%r30 /* restore user sp */
- mfsp %sr3,%r1 /* Get users space id */
+ copy %r1,%r30 /* Restore user sp */
+ mfsp %sr3,%r1 /* Get user space id */
mtsp %r1,%sr7 /* Restore sr7 */
ssm PSW_SM_I, %r0
@@ -2160,16 +1916,16 @@ syscall_restore_rfi:
ldi 0x0b,%r20 /* Create new PSW */
depi -1,13,1,%r20 /* C, Q, D, and I bits */
- /* The values of PA_SINGLESTEP_BIT and PA_BLOCKSTEP_BIT are
- * set in include/linux/ptrace.h and converted to PA bitmap
+ /* The values of SINGLESTEP_BIT and BLOCKSTEP_BIT are
+ * set in thread_info.h and converted to PA bitmap
* numbers in asm-offsets.c */
- /* if ((%r19.PA_SINGLESTEP_BIT)) { %r20.27=1} */
- extru,= %r19,PA_SINGLESTEP_BIT,1,%r0
+ /* if ((%r19.SINGLESTEP_BIT)) { %r20.27=1} */
+ extru,= %r19,TIF_SINGLESTEP_PA_BIT,1,%r0
depi -1,27,1,%r20 /* R bit */
- /* if ((%r19.PA_BLOCKSTEP_BIT)) { %r20.7=1} */
- extru,= %r19,PA_BLOCKSTEP_BIT,1,%r0
+ /* if ((%r19.BLOCKSTEP_BIT)) { %r20.7=1} */
+ extru,= %r19,TIF_BLOCKSTEP_PA_BIT,1,%r0
depi -1,7,1,%r20 /* T bit */
STREG %r20,TASK_PT_PSW(%r1)
@@ -2209,15 +1965,23 @@ syscall_restore_rfi:
/* sr2 should be set to zero for userspace syscalls */
STREG %r0,TASK_PT_SR2(%r1)
-pt_regs_ok:
LDREG TASK_PT_GR31(%r1),%r2
- depi 3,31,2,%r2 /* ensure return to user mode. */
- STREG %r2,TASK_PT_IAOQ0(%r1)
+ depi 3,31,2,%r2 /* ensure return to user mode. */
+ STREG %r2,TASK_PT_IAOQ0(%r1)
ldo 4(%r2),%r2
STREG %r2,TASK_PT_IAOQ1(%r1)
+ b intr_restore
copy %r25,%r16
+
+pt_regs_ok:
+ LDREG TASK_PT_IAOQ0(%r1),%r2
+ depi 3,31,2,%r2 /* ensure return to user mode. */
+ STREG %r2,TASK_PT_IAOQ0(%r1)
+ LDREG TASK_PT_IAOQ1(%r1),%r2
+ depi 3,31,2,%r2
+ STREG %r2,TASK_PT_IAOQ1(%r1)
b intr_restore
- nop
+ copy %r25,%r16
.import schedule,code
syscall_do_resched:
@@ -2227,33 +1991,80 @@ syscall_do_resched:
#else
nop
#endif
- b syscall_check_bh /* if resched, we start over again */
+ b syscall_check_resched /* if resched, we start over again */
nop
+ENDPROC(syscall_exit)
- .import do_signal,code
-syscall_do_signal:
- /* Save callee-save registers (for sigcontext).
- FIXME: After this point the process structure should be
- consistent with all the relevant state of the process
- before the syscall. We need to verify this. */
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
- ldo TASK_REGS(%r1), %r25 /* struct pt_regs *regs */
- reg_save %r25
- ldi 1, %r24 /* unsigned long in_syscall */
-
-#ifdef CONFIG_64BIT
- ldo -16(%r30),%r29 /* Reference param save area */
-#endif
- BL do_signal,%r2
- copy %r0, %r26 /* sigset_t *oldset = NULL */
-
- LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
- ldo TASK_REGS(%r1), %r20 /* reload pt_regs */
- reg_restore %r20
+#ifdef CONFIG_FUNCTION_TRACER
+ .import ftrace_function_trampoline,code
+ENTRY(_mcount)
+ copy %r3, %arg2
+ b ftrace_function_trampoline
+ nop
+ENDPROC(_mcount)
- b,n syscall_check_sig
+ENTRY(return_to_handler)
+ load32 return_trampoline, %rp
+ copy %ret0, %arg0
+ copy %ret1, %arg1
+ b ftrace_return_to_handler
+ nop
+return_trampoline:
+ copy %ret0, %rp
+ copy %r23, %ret0
+ copy %r24, %ret1
+
+.globl ftrace_stub
+ftrace_stub:
+ bv %r0(%rp)
+ nop
+ENDPROC(return_to_handler)
+#endif /* CONFIG_FUNCTION_TRACER */
+
+#ifdef CONFIG_IRQSTACKS
+/* void call_on_stack(unsigned long param1, void *func,
+ unsigned long new_stack) */
+ENTRY(call_on_stack)
+ copy %sp, %r1
+
+ /* Regarding the HPPA calling conventions for function pointers,
+ we assume the PIC register is not changed across call. For
+ CONFIG_64BIT, the argument pointer is left to point at the
+ argument region allocated for the call to call_on_stack. */
+# ifdef CONFIG_64BIT
+ /* Switch to new stack. We allocate two 128 byte frames. */
+ ldo 256(%arg2), %sp
+ /* Save previous stack pointer and return pointer in frame marker */
+ STREG %rp, -144(%sp)
+ /* Calls always use function descriptor */
+ LDREG 16(%arg1), %arg1
+ bve,l (%arg1), %rp
+ STREG %r1, -136(%sp)
+ LDREG -144(%sp), %rp
+ bve (%rp)
+ LDREG -136(%sp), %sp
+# else
+ /* Switch to new stack. We allocate two 64 byte frames. */
+ ldo 128(%arg2), %sp
+ /* Save previous stack pointer and return pointer in frame marker */
+ STREG %r1, -68(%sp)
+ STREG %rp, -84(%sp)
+ /* Calls use function descriptor if PLABEL bit is set */
+ bb,>=,n %arg1, 30, 1f
+ depwi 0,31,2, %arg1
+ LDREG 0(%arg1), %arg1
+1:
+ be,l 0(%sr4,%arg1), %sr0, %r31
+ copy %r31, %rp
+ LDREG -84(%sp), %rp
+ bv (%rp)
+ LDREG -68(%sp), %sp
+# endif /* CONFIG_64BIT */
+ENDPROC(call_on_stack)
+#endif /* CONFIG_IRQSTACKS */
+get_register:
/*
* get_register is used by the non access tlb miss handlers to
* copy the value of the general register specified in r8 into
@@ -2264,8 +2075,6 @@ syscall_do_signal:
* a -1 in it, but that is OK, it just means that we will have
* to use the slow path instead).
*/
-
-get_register:
blr %r8,%r0
nop
bv %r0(%r25) /* r0 */
@@ -2333,13 +2142,13 @@ get_register:
bv %r0(%r25) /* r31 */
copy %r31,%r1
+
+set_register:
/*
* set_register is used by the non access tlb miss handlers to
* copy the value of r1 into the general register specified in
* r8.
*/
-
-set_register:
blr %r8,%r0
nop
bv %r0(%r25) /* r0 (silly, but it is a place holder) */
@@ -2406,3 +2215,4 @@ set_register:
copy %r1,%r30
bv %r0(%r25) /* r31 */
copy %r1,%r31
+
diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c
index 553f8fe0322..22395901d47 100644
--- a/arch/parisc/kernel/firmware.c
+++ b/arch/parisc/kernel/firmware.c
@@ -11,7 +11,7 @@
* Copyright 1999 The Puffin Group, (Alex deVries, David Kennedy)
* Copyright 2003 Grant Grundler <grundler parisc-linux org>
* Copyright 2003,2004 Ryan Bradetich <rbrad@parisc-linux.org>
- * Copyright 2004 Thibaut VARENE <varenet@parisc-linux.org>
+ * Copyright 2004,2006 Thibaut VARENE <varenet@parisc-linux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,20 +67,19 @@
#include <asm/page.h>
#include <asm/pdc.h>
#include <asm/pdcpat.h>
-#include <asm/system.h>
#include <asm/processor.h> /* for boot_cpu_data */
static DEFINE_SPINLOCK(pdc_lock);
-static unsigned long pdc_result[32] __attribute__ ((aligned (8)));
-static unsigned long pdc_result2[32] __attribute__ ((aligned (8)));
+extern unsigned long pdc_result[NUM_PDC_RESULT];
+extern unsigned long pdc_result2[NUM_PDC_RESULT];
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
#define WIDE_FIRMWARE 0x1
#define NARROW_FIRMWARE 0x2
/* Firmware needs to be initially set to narrow to determine the
* actual firmware width. */
-int parisc_narrow_firmware = 1;
+int parisc_narrow_firmware __read_mostly = 1;
#endif
/* On most currently-supported platforms, IODC I/O calls are 32-bit calls
@@ -94,12 +93,12 @@ int parisc_narrow_firmware = 1;
* when running a 64-bit kernel on such boxes (e.g. C200 or C360).
*/
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
long real64_call(unsigned long function, ...);
#endif
long real32_call(unsigned long function, ...);
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
# define MEM_PDC (unsigned long)(PAGE0->mem_pdc_hi) << 32 | PAGE0->mem_pdc
# define mem_pdc_call(args...) unlikely(parisc_narrow_firmware) ? real32_call(MEM_PDC, args) : real64_call(MEM_PDC, args)
#else
@@ -117,7 +116,7 @@ long real32_call(unsigned long function, ...);
*/
static unsigned long f_extend(unsigned long address)
{
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
if(unlikely(parisc_narrow_firmware)) {
if((address & 0xff000000) == 0xf0000000)
return 0xf0f0f0f000000000UL | (u32)address;
@@ -139,7 +138,7 @@ static unsigned long f_extend(unsigned long address)
*/
static void convert_to_wide(unsigned long *addr)
{
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
int i;
unsigned int *p = (unsigned int *)addr;
@@ -150,25 +149,42 @@ static void convert_to_wide(unsigned long *addr)
#endif
}
+#ifdef CONFIG_64BIT
+void set_firmware_width_unlocked(void)
+{
+ int ret;
+
+ ret = mem_pdc_call(PDC_MODEL, PDC_MODEL_CAPABILITIES,
+ __pa(pdc_result), 0);
+ convert_to_wide(pdc_result);
+ if (pdc_result[0] != NARROW_FIRMWARE)
+ parisc_narrow_firmware = 0;
+}
+
/**
* set_firmware_width - Determine if the firmware is wide or narrow.
*
- * This function must be called before any pdc_* function that uses the convert_to_wide
- * function.
+ * This function must be called before any pdc_* function that uses the
+ * convert_to_wide function.
*/
-void __init set_firmware_width(void)
+void set_firmware_width(void)
{
-#ifdef __LP64__
- int retval;
+ unsigned long flags;
+ spin_lock_irqsave(&pdc_lock, flags);
+ set_firmware_width_unlocked();
+ spin_unlock_irqrestore(&pdc_lock, flags);
+}
+#else
+void set_firmware_width_unlocked(void)
+{
+ return;
+}
- spin_lock_irq(&pdc_lock);
- retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_CAPABILITIES, __pa(pdc_result), 0);
- convert_to_wide(pdc_result);
- if(pdc_result[0] != NARROW_FIRMWARE)
- parisc_narrow_firmware = 0;
- spin_unlock_irq(&pdc_lock);
-#endif
+void set_firmware_width(void)
+{
+ return;
}
+#endif /*CONFIG_64BIT*/
/**
* pdc_emergency_unlock - Unlock the linux pdc lock
@@ -196,10 +212,11 @@ void pdc_emergency_unlock(void)
int pdc_add_valid(unsigned long address)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_ADD_VALID, PDC_ADD_VALID_VERIFY, address);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -216,15 +233,16 @@ EXPORT_SYMBOL(pdc_add_valid);
int __init pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_info, unsigned long len)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
memcpy(&pdc_result, chassis_info, sizeof(*chassis_info));
memcpy(&pdc_result2, led_info, len);
retval = mem_pdc_call(PDC_CHASSIS, PDC_RETURN_CHASSIS_INFO,
__pa(pdc_result), __pa(pdc_result2), len);
memcpy(chassis_info, pdc_result, sizeof(*chassis_info));
memcpy(led_info, pdc_result2, len);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -235,60 +253,87 @@ int __init pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_inf
*
* Must be correctly formatted or expect system crash
*/
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
int pdc_pat_chassis_send_log(unsigned long state, unsigned long data)
{
int retval = 0;
+ unsigned long flags;
if (!is_pdc_pat())
return -1;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_CHASSIS_LOG, PDC_PAT_CHASSIS_WRITE_LOG, __pa(&state), __pa(&data));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
#endif
/**
- * pdc_chassis_disp - Updates display
+ * pdc_chassis_disp - Updates chassis code
* @retval: -1 on error, 0 on success
- *
- * Works on old PDC only (E class, others?)
*/
int pdc_chassis_disp(unsigned long disp)
{
int retval = 0;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_DISP, disp);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
/**
+ * pdc_chassis_warn - Fetches chassis warnings
+ * @retval: -1 on error, 0 on success
+ */
+int pdc_chassis_warn(unsigned long *warn)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
+ retval = mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_WARN, __pa(pdc_result));
+ *warn = pdc_result[0];
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
+ return retval;
+}
+
+int pdc_coproc_cfg_unlocked(struct pdc_coproc_cfg *pdc_coproc_info)
+{
+ int ret;
+
+ ret = mem_pdc_call(PDC_COPROC, PDC_COPROC_CFG, __pa(pdc_result));
+ convert_to_wide(pdc_result);
+ pdc_coproc_info->ccr_functional = pdc_result[0];
+ pdc_coproc_info->ccr_present = pdc_result[1];
+ pdc_coproc_info->revision = pdc_result[17];
+ pdc_coproc_info->model = pdc_result[18];
+
+ return ret;
+}
+
+/**
* pdc_coproc_cfg - To identify coprocessors attached to the processor.
* @pdc_coproc_info: Return buffer address.
*
* This PDC call returns the presence and status of all the coprocessors
* attached to the processor.
*/
-int __init pdc_coproc_cfg(struct pdc_coproc_cfg *pdc_coproc_info)
+int pdc_coproc_cfg(struct pdc_coproc_cfg *pdc_coproc_info)
{
- int retval;
+ int ret;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
- retval = mem_pdc_call(PDC_COPROC, PDC_COPROC_CFG, __pa(pdc_result));
- convert_to_wide(pdc_result);
- pdc_coproc_info->ccr_functional = pdc_result[0];
- pdc_coproc_info->ccr_present = pdc_result[1];
- pdc_coproc_info->revision = pdc_result[17];
- pdc_coproc_info->model = pdc_result[18];
- spin_unlock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
+ ret = pdc_coproc_cfg_unlocked(pdc_coproc_info);
+ spin_unlock_irqrestore(&pdc_lock, flags);
- return retval;
+ return ret;
}
/**
@@ -306,14 +351,15 @@ int pdc_iodc_read(unsigned long *actcnt, unsigned long hpa, unsigned int index,
void *iodc_data, unsigned int iodc_data_size)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_IODC, PDC_IODC_READ, __pa(pdc_result), hpa,
index, __pa(pdc_result2), iodc_data_size);
convert_to_wide(pdc_result);
*actcnt = pdc_result[0];
memcpy(iodc_data, pdc_result2, iodc_data_size);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -332,14 +378,15 @@ int pdc_system_map_find_mods(struct pdc_system_map_mod_info *pdc_mod_info,
struct pdc_module_path *mod_path, long mod_index)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_SYSTEM_MAP, PDC_FIND_MODULE, __pa(pdc_result),
__pa(pdc_result2), mod_index);
convert_to_wide(pdc_result);
memcpy(pdc_mod_info, pdc_result, sizeof(*pdc_mod_info));
memcpy(mod_path, pdc_result2, sizeof(*mod_path));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
pdc_mod_info->mod_addr = f_extend(pdc_mod_info->mod_addr);
return retval;
@@ -358,13 +405,14 @@ int pdc_system_map_find_addrs(struct pdc_system_map_addr_info *pdc_addr_info,
long mod_index, long addr_index)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_SYSTEM_MAP, PDC_FIND_ADDRESS, __pa(pdc_result),
mod_index, addr_index);
convert_to_wide(pdc_result);
memcpy(pdc_addr_info, pdc_result, sizeof(*pdc_addr_info));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
pdc_addr_info->mod_addr = f_extend(pdc_addr_info->mod_addr);
return retval;
@@ -379,12 +427,13 @@ int pdc_system_map_find_addrs(struct pdc_system_map_addr_info *pdc_addr_info,
int pdc_model_info(struct pdc_model *model)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_INFO, __pa(pdc_result), 0);
convert_to_wide(pdc_result);
memcpy(model, pdc_result, sizeof(*model));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -393,13 +442,16 @@ int pdc_model_info(struct pdc_model *model)
* pdc_model_sysmodel - Get the system model name.
* @name: A char array of at least 81 characters.
*
- * Get system model name from PDC ROM (e.g. 9000/715 or 9000/778/B160L)
+ * Get system model name from PDC ROM (e.g. 9000/715 or 9000/778/B160L).
+ * Using OS_ID_HPUX will return the equivalent of the 'modelname' command
+ * on HP/UX.
*/
int pdc_model_sysmodel(char *name)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_SYSMODEL, __pa(pdc_result),
OS_ID_HPUX, __pa(name));
convert_to_wide(pdc_result);
@@ -409,7 +461,7 @@ int pdc_model_sysmodel(char *name)
} else {
name[0] = 0;
}
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -427,12 +479,13 @@ int pdc_model_sysmodel(char *name)
int pdc_model_versions(unsigned long *versions, int id)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_VERSIONS, __pa(pdc_result), id);
convert_to_wide(pdc_result);
*versions = pdc_result[0];
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -447,13 +500,14 @@ int pdc_model_versions(unsigned long *versions, int id)
int pdc_model_cpuid(unsigned long *cpu_id)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
pdc_result[0] = 0; /* preset zero (call may not be implemented!) */
retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_CPU_ID, __pa(pdc_result), 0);
convert_to_wide(pdc_result);
*cpu_id = pdc_result[0];
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -468,13 +522,18 @@ int pdc_model_cpuid(unsigned long *cpu_id)
int pdc_model_capabilities(unsigned long *capabilities)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
pdc_result[0] = 0; /* preset zero (call may not be implemented!) */
retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_CAPABILITIES, __pa(pdc_result), 0);
convert_to_wide(pdc_result);
- *capabilities = pdc_result[0];
- spin_unlock_irq(&pdc_lock);
+ if (retval == PDC_OK) {
+ *capabilities = pdc_result[0];
+ } else {
+ *capabilities = PDC_MODEL_OS32;
+ }
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -488,16 +547,38 @@ int pdc_model_capabilities(unsigned long *capabilities)
int pdc_cache_info(struct pdc_cache_info *cache_info)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_CACHE, PDC_CACHE_INFO, __pa(pdc_result), 0);
convert_to_wide(pdc_result);
memcpy(cache_info, pdc_result, sizeof(*cache_info));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
+/**
+ * pdc_spaceid_bits - Return whether Space ID hashing is turned on.
+ * @space_bits: Should be 0, if not, bad mojo!
+ *
+ * Returns information about Space ID hashing.
+ */
+int pdc_spaceid_bits(unsigned long *space_bits)
+{
+ int retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
+ pdc_result[0] = 0;
+ retval = mem_pdc_call(PDC_CACHE, PDC_CACHE_RET_SPID, __pa(pdc_result), 0);
+ convert_to_wide(pdc_result);
+ *space_bits = pdc_result[0];
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
+ return retval;
+}
+
#ifndef CONFIG_PA20
/**
* pdc_btlb_info - Return block TLB information.
@@ -508,11 +589,12 @@ int pdc_cache_info(struct pdc_cache_info *cache_info)
int pdc_btlb_info(struct pdc_btlb_info *btlb)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INFO, __pa(pdc_result), 0);
memcpy(btlb, pdc_result, sizeof(*btlb));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
if(retval < 0) {
btlb->max_size = 0;
@@ -536,13 +618,14 @@ int pdc_mem_map_hpa(struct pdc_memory_map *address,
struct pdc_module_path *mod_path)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
memcpy(pdc_result2, mod_path, sizeof(*mod_path));
retval = mem_pdc_call(PDC_MEM_MAP, PDC_MEM_MAP_HPA, __pa(pdc_result),
__pa(pdc_result2));
memcpy(address, pdc_result, sizeof(*address));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -558,8 +641,9 @@ int pdc_mem_map_hpa(struct pdc_memory_map *address,
int pdc_lan_station_id(char *lan_addr, unsigned long hpa)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_LAN_STATION_ID, PDC_LAN_STATION_ID_READ,
__pa(pdc_result), hpa);
if (retval < 0) {
@@ -568,7 +652,7 @@ int pdc_lan_station_id(char *lan_addr, unsigned long hpa)
} else {
memcpy(lan_addr, pdc_result, PDC_LAN_STATION_ID_SIZE);
}
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -578,7 +662,7 @@ EXPORT_SYMBOL(pdc_lan_station_id);
* pdc_stable_read - Read data from Stable Storage.
* @staddr: Stable Storage address to access.
* @memaddr: The memory address where Stable Storage data shall be copied.
- * @count: number of bytes to transfert. count is multiple of 4.
+ * @count: number of bytes to transfer. count is multiple of 4.
*
* This PDC call reads from the Stable Storage address supplied in staddr
* and copies count bytes to the memory address memaddr.
@@ -587,13 +671,14 @@ EXPORT_SYMBOL(pdc_lan_station_id);
int pdc_stable_read(unsigned long staddr, void *memaddr, unsigned long count)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_READ, staddr,
__pa(pdc_result), count);
convert_to_wide(pdc_result);
memcpy(memaddr, pdc_result, count);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -603,7 +688,7 @@ EXPORT_SYMBOL(pdc_stable_read);
* pdc_stable_write - Write data to Stable Storage.
* @staddr: Stable Storage address to access.
* @memaddr: The memory address where Stable Storage data shall be read from.
- * @count: number of bytes to transfert. count is multiple of 4.
+ * @count: number of bytes to transfer. count is multiple of 4.
*
* This PDC call reads count bytes from the supplied memaddr address,
* and copies count bytes to the Stable Storage address staddr.
@@ -612,13 +697,14 @@ EXPORT_SYMBOL(pdc_stable_read);
int pdc_stable_write(unsigned long staddr, void *memaddr, unsigned long count)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
memcpy(pdc_result, memaddr, count);
convert_to_wide(pdc_result);
retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_WRITE, staddr,
__pa(pdc_result), count);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -636,11 +722,12 @@ EXPORT_SYMBOL(pdc_stable_write);
int pdc_stable_get_size(unsigned long *size)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_RETURN_SIZE, __pa(pdc_result));
*size = pdc_result[0];
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -655,10 +742,11 @@ EXPORT_SYMBOL(pdc_stable_get_size);
int pdc_stable_verify_contents(void)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_VERIFY_CONTENTS);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -673,10 +761,11 @@ EXPORT_SYMBOL(pdc_stable_verify_contents);
int pdc_stable_initialize(void)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_INITIALIZE);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -699,8 +788,9 @@ EXPORT_SYMBOL(pdc_stable_initialize);
int pdc_get_initiator(struct hardware_path *hwpath, struct pdc_initiator *initiator)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
/* BCJ-XXXX series boxes. E.G. "9000/785/C3000" */
#define IS_SPROCKETS() (strlen(boot_cpu_data.pdc.sys_model_name) == 14 && \
@@ -740,7 +830,8 @@ int pdc_get_initiator(struct hardware_path *hwpath, struct pdc_initiator *initia
}
out:
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
return (retval >= PDC_OK);
}
EXPORT_SYMBOL(pdc_get_initiator);
@@ -758,13 +849,14 @@ EXPORT_SYMBOL(pdc_get_initiator);
int pdc_pci_irt_size(unsigned long *num_entries, unsigned long hpa)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL_SIZE,
__pa(pdc_result), hpa);
convert_to_wide(pdc_result);
*num_entries = pdc_result[0];
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -781,14 +873,15 @@ int pdc_pci_irt_size(unsigned long *num_entries, unsigned long hpa)
int pdc_pci_irt(unsigned long num_entries, unsigned long hpa, void *tbl)
{
int retval;
+ unsigned long flags;
BUG_ON((unsigned long)tbl & 0x7);
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
pdc_result[0] = num_entries;
retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL,
__pa(pdc_result), hpa, __pa(tbl));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -806,12 +899,15 @@ int pdc_pci_irt(unsigned long num_entries, unsigned long hpa, void *tbl)
unsigned int pdc_pci_config_read(void *hpa, unsigned long cfg_addr)
{
int retval;
- spin_lock_irq(&pdc_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
pdc_result[0] = 0;
pdc_result[1] = 0;
retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_READ_CONFIG,
__pa(pdc_result), hpa, cfg_addr&~3UL, 4UL);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
return retval ? ~0 : (unsigned int) pdc_result[0];
}
@@ -827,12 +923,15 @@ unsigned int pdc_pci_config_read(void *hpa, unsigned long cfg_addr)
void pdc_pci_config_write(void *hpa, unsigned long cfg_addr, unsigned int val)
{
int retval;
- spin_lock_irq(&pdc_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
pdc_result[0] = 0;
retval = mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_WRITE_CONFIG,
__pa(pdc_result), hpa,
cfg_addr&~3UL, 4UL, (unsigned long) val);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
return retval;
}
#endif /* UNTESTED CODE */
@@ -846,12 +945,13 @@ void pdc_pci_config_write(void *hpa, unsigned long cfg_addr, unsigned int val)
int pdc_tod_read(struct pdc_tod *tod)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_TOD, PDC_TOD_READ, __pa(pdc_result), 0);
convert_to_wide(pdc_result);
memcpy(tod, pdc_result, sizeof(*tod));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -867,31 +967,33 @@ EXPORT_SYMBOL(pdc_tod_read);
int pdc_tod_set(unsigned long sec, unsigned long usec)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_TOD, PDC_TOD_WRITE, sec, usec);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
EXPORT_SYMBOL(pdc_tod_set);
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr,
struct pdc_memory_table *tbl, unsigned long entries)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_MEM, PDC_MEM_TABLE, __pa(pdc_result), __pa(pdc_result2), entries);
convert_to_wide(pdc_result);
memcpy(r_addr, pdc_result, sizeof(*r_addr));
memcpy(tbl, pdc_result2, entries * sizeof(*tbl));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
-#endif /* __LP64__ */
+#endif /* CONFIG_64BIT */
/* FIXME: Is this pdc used? I could not find type reference to ftc_bitmap
* so I guessed at unsigned long. Someone who knows what this does, can fix
@@ -900,11 +1002,12 @@ int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr,
int pdc_do_firm_test_reset(unsigned long ftc_bitmap)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_BROADCAST_RESET, PDC_DO_FIRM_TEST_RESET,
PDC_FIRM_TEST_MAGIC, ftc_bitmap);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -917,10 +1020,11 @@ int pdc_do_firm_test_reset(unsigned long ftc_bitmap)
int pdc_do_reset(void)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_BROADCAST_RESET, PDC_DO_RESET);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -934,16 +1038,17 @@ int pdc_do_reset(void)
int __init pdc_soft_power_info(unsigned long *power_reg)
{
int retval;
+ unsigned long flags;
*power_reg = (unsigned long) (-1);
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_SOFT_POWER, PDC_SOFT_POWER_INFO, __pa(pdc_result), 0);
if (retval == PDC_OK) {
convert_to_wide(pdc_result);
*power_reg = f_extend(pdc_result[0]);
}
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -962,9 +1067,12 @@ int __init pdc_soft_power_info(unsigned long *power_reg)
int pdc_soft_power_button(int sw_control)
{
int retval;
- spin_lock_irq(&pdc_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_SOFT_POWER, PDC_SOFT_POWER_ENABLE, __pa(pdc_result), sw_control);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
return retval;
}
@@ -975,9 +1083,11 @@ int pdc_soft_power_button(int sw_control)
*/
void pdc_io_reset(void)
{
- spin_lock_irq(&pdc_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
mem_pdc_call(PDC_IO, PDC_IO_RESET, 0);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
}
/*
@@ -991,82 +1101,55 @@ void pdc_io_reset(void)
*/
void pdc_io_reset_devices(void)
{
- spin_lock_irq(&pdc_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
mem_pdc_call(PDC_IO, PDC_IO_RESET_DEVICES, 0);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
}
+/* locked by pdc_console_lock */
+static int __attribute__((aligned(8))) iodc_retbuf[32];
+static char __attribute__((aligned(64))) iodc_dbuf[4096];
/**
- * pdc_iodc_putc - Console character print using IODC.
- * @c: the character to output.
+ * pdc_iodc_print - Console print using IODC.
+ * @str: the string to output.
+ * @count: length of str
*
* Note that only these special chars are architected for console IODC io:
* BEL, BS, CR, and LF. Others are passed through.
* Since the HP console requires CR+LF to perform a 'newline', we translate
* "\n" to "\r\n".
*/
-void pdc_iodc_putc(unsigned char c)
+int pdc_iodc_print(const unsigned char *str, unsigned count)
{
- /* XXX Should we spinlock posx usage */
- static int posx; /* for simple TAB-Simulation... */
- static int __attribute__((aligned(8))) iodc_retbuf[32];
- static char __attribute__((aligned(64))) iodc_dbuf[4096];
- unsigned int n;
- unsigned int flags;
-
- switch (c) {
- case '\n':
- iodc_dbuf[0] = '\r';
- iodc_dbuf[1] = '\n';
- n = 2;
- posx = 0;
- break;
- case '\t':
- pdc_iodc_putc(' ');
- while (posx & 7) /* expand TAB */
- pdc_iodc_putc(' ');
- return; /* return since IODC can't handle this */
- case '\b':
- posx-=2; /* BS */
- default:
- iodc_dbuf[0] = c;
- n = 1;
- posx++;
- break;
- }
+ unsigned int i;
+ unsigned long flags;
+
+ for (i = 0; i < count;) {
+ switch(str[i]) {
+ case '\n':
+ iodc_dbuf[i+0] = '\r';
+ iodc_dbuf[i+1] = '\n';
+ i += 2;
+ goto print;
+ default:
+ iodc_dbuf[i] = str[i];
+ i++;
+ break;
+ }
+ }
+print:
spin_lock_irqsave(&pdc_lock, flags);
real32_call(PAGE0->mem_cons.iodc_io,
(unsigned long)PAGE0->mem_cons.hpa, ENTRY_IO_COUT,
PAGE0->mem_cons.spa, __pa(PAGE0->mem_cons.dp.layers),
- __pa(iodc_retbuf), 0, __pa(iodc_dbuf), n, 0);
+ __pa(iodc_retbuf), 0, __pa(iodc_dbuf), i, 0);
spin_unlock_irqrestore(&pdc_lock, flags);
-}
-
-/**
- * pdc_iodc_outc - Console character print using IODC (without conversions).
- * @c: the character to output.
- *
- * Write the character directly to the IODC console.
- */
-void pdc_iodc_outc(unsigned char c)
-{
- unsigned int n, flags;
-
- /* fill buffer with one caracter and print it */
- static int __attribute__((aligned(8))) iodc_retbuf[32];
- static char __attribute__((aligned(64))) iodc_dbuf[4096];
-
- n = 1;
- iodc_dbuf[0] = c;
- spin_lock_irqsave(&pdc_lock, flags);
- real32_call(PAGE0->mem_cons.iodc_io,
- (unsigned long)PAGE0->mem_cons.hpa, ENTRY_IO_COUT,
- PAGE0->mem_cons.spa, __pa(PAGE0->mem_cons.dp.layers),
- __pa(iodc_retbuf), 0, __pa(iodc_dbuf), n, 0);
- spin_unlock_irqrestore(&pdc_lock, flags);
+ return i;
}
/**
@@ -1077,11 +1160,9 @@ void pdc_iodc_outc(unsigned char c)
*/
int pdc_iodc_getc(void)
{
- unsigned int flags;
- static int __attribute__((aligned(8))) iodc_retbuf[32];
- static char __attribute__((aligned(64))) iodc_dbuf[4096];
int ch;
int status;
+ unsigned long flags;
/* Bail if no console input device. */
if (!PAGE0->mem_kbd.iodc_io)
@@ -1109,16 +1190,17 @@ int pdc_sti_call(unsigned long func, unsigned long flags,
unsigned long glob_cfg)
{
int retval;
+ unsigned long irqflags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, irqflags);
retval = real32_call(func, flags, inptr, outputr, glob_cfg);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, irqflags);
return retval;
}
EXPORT_SYMBOL(pdc_sti_call);
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
/**
* pdc_pat_cell_get_number - Returns the cell number.
* @cell_info: The return buffer.
@@ -1129,11 +1211,12 @@ EXPORT_SYMBOL(pdc_sti_call);
int pdc_pat_cell_get_number(struct pdc_pat_cell_num *cell_info)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_CELL, PDC_PAT_CELL_GET_NUMBER, __pa(pdc_result));
memcpy(cell_info, pdc_result, sizeof(*cell_info));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -1153,16 +1236,17 @@ int pdc_pat_cell_module(unsigned long *actcnt, unsigned long ploc, unsigned long
unsigned long view_type, void *mem_addr)
{
int retval;
+ unsigned long flags;
static struct pdc_pat_cell_mod_maddr_block result __attribute__ ((aligned (8)));
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_CELL, PDC_PAT_CELL_MODULE, __pa(pdc_result),
ploc, mod, view_type, __pa(&result));
if(!retval) {
*actcnt = pdc_result[0];
memcpy(mem_addr, &result, *actcnt);
}
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -1177,12 +1261,13 @@ int pdc_pat_cell_module(unsigned long *actcnt, unsigned long ploc, unsigned long
int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, void *hpa)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_GET_NUMBER,
__pa(&pdc_result), hpa);
memcpy(cpu_info, pdc_result, sizeof(*cpu_info));
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -1198,12 +1283,13 @@ int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, void *hpa)
int pdc_pat_get_irt_size(unsigned long *num_entries, unsigned long cell_num)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_GET_PCI_ROUTING_TABLE_SIZE,
__pa(pdc_result), cell_num);
*num_entries = pdc_result[0];
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -1218,11 +1304,12 @@ int pdc_pat_get_irt_size(unsigned long *num_entries, unsigned long cell_num)
int pdc_pat_get_irt(void *r_addr, unsigned long cell_num)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_GET_PCI_ROUTING_TABLE,
__pa(r_addr), cell_num);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -1239,13 +1326,14 @@ int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr,
unsigned long count, unsigned long offset)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_PD, PDC_PAT_PD_GET_ADDR_MAP, __pa(pdc_result),
__pa(pdc_result2), count, offset);
*actual_len = pdc_result[0];
memcpy(mem_addr, pdc_result2, *actual_len);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -1260,7 +1348,9 @@ int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr,
int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *mem_addr)
{
int retval;
- spin_lock_irq(&pdc_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_READ,
__pa(pdc_result), pci_addr, pci_size);
switch(pci_size) {
@@ -1268,7 +1358,7 @@ int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *mem_addr)
case 2: *(u16 *)mem_addr = (u16) pdc_result[0];
case 4: *(u32 *)mem_addr = (u32) pdc_result[0];
}
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
@@ -1284,15 +1374,16 @@ int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *mem_addr)
int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val)
{
int retval;
+ unsigned long flags;
- spin_lock_irq(&pdc_lock);
+ spin_lock_irqsave(&pdc_lock, flags);
retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_WRITE,
pci_addr, pci_size, val);
- spin_unlock_irq(&pdc_lock);
+ spin_unlock_irqrestore(&pdc_lock, flags);
return retval;
}
-#endif /* __LP64__ */
+#endif /* CONFIG_64BIT */
/***************** 32-bit real-mode calls ***********/
@@ -1350,7 +1441,7 @@ long real32_call(unsigned long fn, ...)
return real32_call_asm(&real_stack.sp, &real_stack.arg0, fn);
}
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
/***************** 64-bit real-mode calls ***********/
struct wide_stack {
@@ -1401,5 +1492,5 @@ long real64_call(unsigned long fn, ...)
return real64_call_asm(&real64_stack.sp, &real64_stack.arg0, fn);
}
-#endif /* __LP64__ */
+#endif /* CONFIG_64BIT */
diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c
new file mode 100644
index 00000000000..5beb97bafbb
--- /dev/null
+++ b/arch/parisc/kernel/ftrace.c
@@ -0,0 +1,185 @@
+/*
+ * Code for tracing calls in Linux kernel.
+ * Copyright (C) 2009 Helge Deller <deller@gmx.de>
+ *
+ * based on code for x86 which is:
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ * future possible enhancements:
+ * - add CONFIG_DYNAMIC_FTRACE
+ * - add CONFIG_STACK_TRACER
+ */
+
+#include <linux/init.h>
+#include <linux/ftrace.h>
+
+#include <asm/sections.h>
+#include <asm/ftrace.h>
+
+
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+/* Add a function return address to the trace stack on thread info.*/
+static int push_return_trace(unsigned long ret, unsigned long long time,
+ unsigned long func, int *depth)
+{
+ int index;
+
+ if (!current->ret_stack)
+ return -EBUSY;
+
+ /* The return trace stack is full */
+ if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) {
+ atomic_inc(&current->trace_overrun);
+ return -EBUSY;
+ }
+
+ index = ++current->curr_ret_stack;
+ barrier();
+ current->ret_stack[index].ret = ret;
+ current->ret_stack[index].func = func;
+ current->ret_stack[index].calltime = time;
+ *depth = index;
+
+ return 0;
+}
+
+/* Retrieve a function return address to the trace stack on thread info.*/
+static void pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret)
+{
+ int index;
+
+ index = current->curr_ret_stack;
+
+ if (unlikely(index < 0)) {
+ ftrace_graph_stop();
+ WARN_ON(1);
+ /* Might as well panic, otherwise we have no where to go */
+ *ret = (unsigned long)
+ dereference_function_descriptor(&panic);
+ return;
+ }
+
+ *ret = current->ret_stack[index].ret;
+ trace->func = current->ret_stack[index].func;
+ trace->calltime = current->ret_stack[index].calltime;
+ trace->overrun = atomic_read(&current->trace_overrun);
+ trace->depth = index;
+ barrier();
+ current->curr_ret_stack--;
+
+}
+
+/*
+ * Send the trace to the ring-buffer.
+ * @return the original return address.
+ */
+unsigned long ftrace_return_to_handler(unsigned long retval0,
+ unsigned long retval1)
+{
+ struct ftrace_graph_ret trace;
+ unsigned long ret;
+
+ pop_return_trace(&trace, &ret);
+ trace.rettime = local_clock();
+ ftrace_graph_return(&trace);
+
+ if (unlikely(!ret)) {
+ ftrace_graph_stop();
+ WARN_ON(1);
+ /* Might as well panic. What else to do? */
+ ret = (unsigned long)
+ dereference_function_descriptor(&panic);
+ }
+
+ /* HACK: we hand over the old functions' return values
+ in %r23 and %r24. Assembly in entry.S will take care
+ and move those to their final registers %ret0 and %ret1 */
+ asm( "copy %0, %%r23 \n\t"
+ "copy %1, %%r24 \n" : : "r" (retval0), "r" (retval1) );
+
+ return ret;
+}
+
+/*
+ * Hook the return address and push it in the stack of return addrs
+ * in current thread info.
+ */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
+{
+ unsigned long old;
+ unsigned long long calltime;
+ struct ftrace_graph_ent trace;
+
+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
+ return;
+
+ old = *parent;
+ *parent = (unsigned long)
+ dereference_function_descriptor(&return_to_handler);
+
+ if (unlikely(!__kernel_text_address(old))) {
+ ftrace_graph_stop();
+ *parent = old;
+ WARN_ON(1);
+ return;
+ }
+
+ calltime = local_clock();
+
+ if (push_return_trace(old, calltime,
+ self_addr, &trace.depth) == -EBUSY) {
+ *parent = old;
+ return;
+ }
+
+ trace.func = self_addr;
+
+ /* Only trace if the calling function expects to */
+ if (!ftrace_graph_entry(&trace)) {
+ current->curr_ret_stack--;
+ *parent = old;
+ }
+}
+
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+
+void ftrace_function_trampoline(unsigned long parent,
+ unsigned long self_addr,
+ unsigned long org_sp_gr3)
+{
+ extern ftrace_func_t ftrace_trace_function;
+
+ if (function_trace_stop)
+ return;
+
+ if (ftrace_trace_function != ftrace_stub) {
+ ftrace_trace_function(parent, self_addr);
+ return;
+ }
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ if (ftrace_graph_entry && ftrace_graph_return) {
+ unsigned long sp;
+ unsigned long *parent_rp;
+
+ asm volatile ("copy %%r30, %0" : "=r"(sp));
+ /* sanity check: is stack pointer which we got from
+ assembler function in entry.S in a reasonable
+ range compared to current stack pointer? */
+ if ((sp - org_sp_gr3) > 0x400)
+ return;
+
+ /* calculate pointer to %rp in stack */
+ parent_rp = (unsigned long *) org_sp_gr3 - 0x10;
+ /* sanity check: parent_rp should hold parent */
+ if (*parent_rp != parent)
+ return;
+
+ prepare_ftrace_return(parent_rp, self_addr);
+ return;
+ }
+#endif
+}
+
diff --git a/arch/parisc/kernel/hardware.c b/arch/parisc/kernel/hardware.c
index 2071b5bba15..af3bc359dc7 100644
--- a/arch/parisc/kernel/hardware.c
+++ b/arch/parisc/kernel/hardware.c
@@ -36,9 +36,12 @@
* HP PARISC Hardware Database
* Access to this database is only possible during bootup
* so don't reference this table after starting the init process
+ *
+ * NOTE: Product names which are listed here and ends with a '?'
+ * are guessed. If you know the correct name, please let us know.
*/
-static struct hp_hardware hp_hardware_list[] __initdata = {
+static struct hp_hardware hp_hardware_list[] = {
{HPHW_NPROC,0x01,0x4,0x0,"Indigo (840, 930)"},
{HPHW_NPROC,0x8,0x4,0x01,"Firefox(825,925)"},
{HPHW_NPROC,0xA,0x4,0x01,"Top Gun (835,834,935,635)"},
@@ -222,6 +225,7 @@ static struct hp_hardware hp_hardware_list[] __initdata = {
{HPHW_NPROC,0x5DD,0x4,0x81,"Duet W2"},
{HPHW_NPROC,0x5DE,0x4,0x81,"Piccolo W+"},
{HPHW_NPROC,0x5DF,0x4,0x81,"Cantata W2"},
+ {HPHW_NPROC,0x5DF,0x0,0x00,"Marcato W+ (rp5470)?"},
{HPHW_NPROC,0x5E0,0x4,0x91,"Cantata DC- W2"},
{HPHW_NPROC,0x5E1,0x4,0x91,"Crescendo DC- W2"},
{HPHW_NPROC,0x5E2,0x4,0x91,"Crescendo 650 W2"},
@@ -231,6 +235,7 @@ static struct hp_hardware hp_hardware_list[] __initdata = {
{HPHW_NPROC,0x5E6,0x4,0x91,"Keystone/Matterhorn W2 650"},
{HPHW_NPROC,0x5E7,0x4,0x91,"Caribe W2 800"},
{HPHW_NPROC,0x5E8,0x4,0x91,"Pikes Peak W2"},
+ {HPHW_NPROC,0x5EB,0x4,0x91,"Perf/Leone 875 W2+"},
{HPHW_NPROC,0x5FF,0x4,0x91,"Hitachi W"},
{HPHW_NPROC,0x600,0x4,0x81,"Gecko (712/60)"},
{HPHW_NPROC,0x601,0x4,0x81,"Gecko 80 (712/80)"},
@@ -273,7 +278,20 @@ static struct hp_hardware hp_hardware_list[] __initdata = {
{HPHW_NPROC,0x887,0x4,0x91,"Storm Peak Slow"},
{HPHW_NPROC,0x888,0x4,0x91,"Storm Peak Fast DC-"},
{HPHW_NPROC,0x889,0x4,0x91,"Storm Peak Fast"},
- {HPHW_NPROC,0x88A,0x4,0x91,"Crestone Peak"},
+ {HPHW_NPROC,0x88A,0x4,0x91,"Crestone Peak Slow"},
+ {HPHW_NPROC,0x88B,0x4,0x91,"Crestone Peak Fast?"},
+ {HPHW_NPROC,0x88C,0x4,0x91,"Orca Mako+"},
+ {HPHW_NPROC,0x88D,0x4,0x91,"Rainier/Medel Mako+ Slow"},
+ {HPHW_NPROC,0x88E,0x4,0x91,"Rainier/Medel Mako+ Fast"},
+ {HPHW_NPROC,0x892,0x4,0x91,"Mt. Hamilton Slow Mako+?"},
+ {HPHW_NPROC,0x894,0x4,0x91,"Mt. Hamilton Fast Mako+"},
+ {HPHW_NPROC,0x895,0x4,0x91,"Storm Peak Slow Mako+"},
+ {HPHW_NPROC,0x896,0x4,0x91,"Storm Peak Fast Mako+"},
+ {HPHW_NPROC,0x897,0x4,0x91,"Storm Peak DC- Slow Mako+"},
+ {HPHW_NPROC,0x898,0x4,0x91,"Storm Peak DC- Fast Mako+"},
+ {HPHW_NPROC,0x899,0x4,0x91,"Mt. Hamilton Slow Mako+"},
+ {HPHW_NPROC,0x89B,0x4,0x91,"Crestone Peak Mako+ Slow"},
+ {HPHW_NPROC,0x89C,0x4,0x91,"Crestone Peak Mako+ Fast"},
{HPHW_A_DIRECT, 0x004, 0x0000D, 0x00, "Arrakis MUX"},
{HPHW_A_DIRECT, 0x005, 0x0000D, 0x00, "Dyun Kiuh MUX"},
{HPHW_A_DIRECT, 0x006, 0x0000D, 0x00, "Baat Kiuh AP/MUX (40299B)"},
@@ -551,6 +569,7 @@ static struct hp_hardware hp_hardware_list[] __initdata = {
{HPHW_BCPORT, 0x804, 0x0000C, 0x10, "REO I/O BC Merced Port"},
{HPHW_BCPORT, 0x782, 0x0000C, 0x00, "REO I/O BC Ropes Port"},
{HPHW_BCPORT, 0x784, 0x0000C, 0x00, "Pluto I/O BC Ropes Port"},
+ {HPHW_BRIDGE, 0x05D, 0x0000A, 0x00, "SummitHawk Dino PCI Bridge"},
{HPHW_BRIDGE, 0x680, 0x0000A, 0x00, "Dino PCI Bridge"},
{HPHW_BRIDGE, 0x682, 0x0000A, 0x00, "Cujo PCI Bridge"},
{HPHW_BRIDGE, 0x782, 0x0000A, 0x00, "Elroy PCI Bridge"},
@@ -583,8 +602,10 @@ static struct hp_hardware hp_hardware_list[] __initdata = {
{HPHW_CONSOLE, 0x01A, 0x0001F, 0x00, "Jason/Anole 64 Null Console"},
{HPHW_CONSOLE, 0x01B, 0x0001F, 0x00, "Jason/Anole 100 Null Console"},
{HPHW_FABRIC, 0x004, 0x000AA, 0x80, "Halfdome DNA Central Agent"},
+ {HPHW_FABRIC, 0x005, 0x000AA, 0x80, "Keystone DNA Central Agent"},
{HPHW_FABRIC, 0x007, 0x000AA, 0x80, "Caribe DNA Central Agent"},
{HPHW_FABRIC, 0x004, 0x000AB, 0x00, "Halfdome TOGO Fabric Crossbar"},
+ {HPHW_FABRIC, 0x005, 0x000AB, 0x00, "Keystone TOGO Fabric Crossbar"},
{HPHW_FABRIC, 0x004, 0x000AC, 0x00, "Halfdome Sakura Fabric Router"},
{HPHW_FIO, 0x025, 0x0002E, 0x80, "Armyknife Optional X.25"},
{HPHW_FIO, 0x004, 0x0004F, 0x0, "8-Port X.25 EISA-ACC (AMSO)"},
@@ -1183,12 +1204,14 @@ static struct hp_hardware hp_hardware_list[] __initdata = {
{HPHW_FIO, 0x005, 0x000A9, 0x00, "AllegroLow Core PCI USB KB"},
{HPHW_FIO, 0x006, 0x000A9, 0x00, "AllegroHigh Core PCI SuperIO RS-232"},
{HPHW_FIO, 0x006, 0x000A9, 0x00, "AllegroHigh Core PCI USB KB"},
- {HPHW_FIO, 0x007, 0x000A9, 0x0, "Miscelaneous PCI Plug-in"},
+ {HPHW_FIO, 0x007, 0x000A9, 0x0, "Miscellaneous PCI Plug-in"},
{HPHW_FIO, 0x00A, 0x000A9, 0x0, "Lego 360 Core PCI SuperIO RS-232"},
{HPHW_FIO, 0x00A, 0x000A9, 0x0, "Lego 360 Core PCI USB KB"},
{HPHW_FIO, 0x004, 0x00320, 0x0, "Metheus Frame Buffer"},
{HPHW_FIO, 0x004, 0x00340, 0x0, "BARCO CX4500 VME Grphx Cnsl"},
{HPHW_FIO, 0x004, 0x00360, 0x0, "Hughes TOG VME FDDI"},
+ {HPHW_FIO, 0x076, 0x000AD, 0x0, "Crestone Peak Core RS-232"},
+ {HPHW_FIO, 0x077, 0x000AD, 0x0, "Crestone Peak Fast? Core RS-232"},
{HPHW_IOA, 0x185, 0x0000B, 0x00, "Java BC Summit Port"},
{HPHW_IOA, 0x1FF, 0x0000B, 0x00, "Hitachi Ghostview Summit Port"},
{HPHW_IOA, 0x580, 0x0000B, 0x10, "U2-IOA BC Runway Port"},
@@ -1215,7 +1238,7 @@ static struct hp_cpu_type_mask {
unsigned short model;
unsigned short mask;
enum cpu_type cpu;
-} hp_cpu_type_mask_list[] __initdata = {
+} hp_cpu_type_mask_list[] = {
{ 0x0000, 0x0ff0, pcx }, /* 0x0000 - 0x000f */
{ 0x0048, 0x0ff0, pcxl }, /* 0x0040 - 0x004f */
@@ -1292,10 +1315,11 @@ static struct hp_cpu_type_mask {
{ 0x05f0, 0x0ff0, pcxw2 }, /* 0x05f0 - 0x05ff */
{ 0x0600, 0x0fe0, pcxl }, /* 0x0600 - 0x061f */
{ 0x0880, 0x0ff0, mako }, /* 0x0880 - 0x088f */
+ { 0x0890, 0x0ff0, mako2 }, /* 0x0890 - 0x089f */
{ 0x0000, 0x0000, pcx } /* terminate table */
};
-char *cpu_name_version[][2] = {
+const char * const cpu_name_version[][2] = {
[pcx] = { "PA7000 (PCX)", "1.0" },
[pcxs] = { "PA7000 (PCX-S)", "1.1a" },
[pcxt] = { "PA7100 (PCX-T)", "1.1b" },
@@ -1307,11 +1331,11 @@ char *cpu_name_version[][2] = {
[pcxw] = { "PA8500 (PCX-W)", "2.0" },
[pcxw_] = { "PA8600 (PCX-W+)", "2.0" },
[pcxw2] = { "PA8700 (PCX-W2)", "2.0" },
- [mako] = { "PA8800 (Mako)", "2.0" }
+ [mako] = { "PA8800 (Mako)", "2.0" },
+ [mako2] = { "PA8900 (Shortfin)", "2.0" }
};
-const char * __init
-parisc_hardware_description(struct parisc_device_id *id)
+const char *parisc_hardware_description(struct parisc_device_id *id)
{
struct hp_hardware *listptr;
@@ -1349,7 +1373,7 @@ parisc_hardware_description(struct parisc_device_id *id)
/* Interpret hversion (ret[0]) from PDC_MODEL(4)/PDC_MODEL_INFO(0) */
-enum cpu_type __init
+enum cpu_type
parisc_get_cpu_type(unsigned long hversion)
{
struct hp_cpu_type_mask *ptr;
diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S
index 0b47afc2069..d4dc588c0dc 100644
--- a/arch/parisc/kernel/head.S
+++ b/arch/parisc/kernel/head.S
@@ -2,7 +2,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 1999 by Helge Deller
+ * Copyright (C) 1999-2007 by Helge Deller <deller@gmx.de>
* Copyright 1999 SuSE GmbH (Philipp Rumpf)
* Copyright 1999 Philipp Rumpf (prumpf@tux.org)
* Copyright 2000 Hewlett Packard (Paul Bame, bame@puffin.external.hp.com)
@@ -12,8 +12,6 @@
* Initial Version 04-23-1999 by Helge Deller <deller@gmx.de>
*/
-#include <linux/config.h> /* for CONFIG_SMP */
-
#include <asm/asm-offsets.h>
#include <asm/psw.h>
#include <asm/pdc.h>
@@ -21,18 +19,21 @@
#include <asm/assembly.h>
#include <asm/pgtable.h>
- .level LEVEL
+#include <linux/linkage.h>
+#include <linux/init.h>
- .data
+ .level LEVEL
- .export boot_args
-boot_args:
+ __INITDATA
+ENTRY(boot_args)
.word 0 /* arg0 */
.word 0 /* arg1 */
.word 0 /* arg2 */
.word 0 /* arg3 */
+END(boot_args)
+
+ __HEAD
- .text
.align 4
.import init_thread_union,data
.import fault_vector_20,code /* IVA parisc 2.0 32 bit */
@@ -40,10 +41,7 @@ boot_args:
.import fault_vector_11,code /* IVA parisc 1.1 32 bit */
.import $global$ /* forward declaration */
#endif /*!CONFIG_64BIT*/
- .export stext
- .export _stext,data /* Kernel want it this way! */
-_stext:
-stext:
+ENTRY(parisc_kernel_start)
.proc
.callinfo
@@ -76,16 +74,16 @@ $bss_loop:
mtctl %r4,%cr24 /* Initialize kernel root pointer */
mtctl %r4,%cr25 /* Initialize user root pointer */
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
/* Set pmd in pgd */
load32 PA(pmd0),%r5
shrd %r5,PxD_VALUE_SHIFT,%r3
- ldo (PxD_FLAG_PRESENT+PxD_FLAG_VALID)(%r3),%r3
+ ldo (PxD_FLAG_PRESENT+PxD_FLAG_VALID)(%r3),%r3
stw %r3,ASM_PGD_ENTRY*ASM_PGD_ENTRY_SIZE(%r4)
ldo ASM_PMD_ENTRY*ASM_PMD_ENTRY_SIZE(%r5),%r4
#else
/* 2-level page table, so pmd == pgd */
- ldo ASM_PGD_ENTRY*ASM_PGD_ENTRY_SIZE(%r4),%r4
+ ldo ASM_PGD_ENTRY*ASM_PGD_ENTRY_SIZE(%r4),%r4
#endif
/* Fill in pmd with enough pte directories */
@@ -97,30 +95,32 @@ $bss_loop:
1:
stw %r3,0(%r4)
- ldo (ASM_PAGE_SIZE >> PxD_VALUE_SHIFT)(%r3),%r3
+ ldo (PAGE_SIZE >> PxD_VALUE_SHIFT)(%r3),%r3
addib,> -1,%r1,1b
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
ldo ASM_PMD_ENTRY_SIZE(%r4),%r4
#else
ldo ASM_PGD_ENTRY_SIZE(%r4),%r4
#endif
- /* Now initialize the PTEs themselves */
- ldo _PAGE_KERNEL(%r0),%r3 /* Hardwired 0 phys addr start */
+ /* Now initialize the PTEs themselves. We use RWX for
+ * everything ... it will get remapped correctly later */
+ ldo 0+_PAGE_KERNEL_RWX(%r0),%r3 /* Hardwired 0 phys addr start */
+ ldi (1<<(KERNEL_INITIAL_ORDER-PAGE_SHIFT)),%r11 /* PFN count */
load32 PA(pg0),%r1
$pgt_fill_loop:
STREGM %r3,ASM_PTE_ENTRY_SIZE(%r1)
- ldo ASM_PAGE_SIZE(%r3),%r3
- bb,>= %r3,31-KERNEL_INITIAL_ORDER,$pgt_fill_loop
+ ldo (1<<PFN_PTE_SHIFT)(%r3),%r3 /* add one PFN */
+ addib,> -1,%r11,$pgt_fill_loop
nop
/* Load the return address...er...crash 'n burn */
copy %r0,%r2
/* And the RFI Target address too */
- load32 start_kernel,%r11
+ load32 start_parisc,%r11
/* And the initial task pointer */
load32 init_thread_union,%r6
@@ -129,12 +129,8 @@ $pgt_fill_loop:
/* And the stack pointer too */
ldo THREAD_SZ_ALGN(%r6),%sp
- /* And the interrupt stack */
- load32 interrupt_stack,%r6
- mtctl %r6,%cr31
-
#ifdef CONFIG_SMP
- /* Set the smp rendevous address into page zero.
+ /* Set the smp rendezvous address into page zero.
** It would be safer to do this in init_smp_config() but
** it's just way easier to deal with here because
** of 64-bit function ptrs and the address is local to this file.
@@ -197,6 +193,8 @@ common_stext:
ldw MEM_PDC_HI(%r0),%r6
depd %r6, 31, 32, %r3 /* move to upper word */
+ mfctl %cr30,%r6 /* PCX-W2 firmware bug */
+
ldo PDC_PSW(%r0),%arg0 /* 21 */
ldo PDC_PSW_SET_DEFAULTS(%r0),%arg1 /* 2 */
ldo PDC_PSW_WIDE_BIT(%r0),%arg2 /* 2 */
@@ -205,6 +203,8 @@ common_stext:
copy %r0,%arg3
stext_pdc_ret:
+ mtctl %r6,%cr30 /* restore task thread info */
+
/* restore rfi target address*/
ldd TI_TASK-THREAD_SZ_ALGN(%sp), %r10
tophys_r1 %r10
@@ -344,8 +344,11 @@ smp_slave_stext:
.procend
#endif /* CONFIG_SMP */
+
+ENDPROC(parisc_kernel_start)
+
#ifndef CONFIG_64BIT
- .data
+ .section .data..read_mostly
.align 4
.export $global$,data
diff --git a/arch/parisc/kernel/hpmc.S b/arch/parisc/kernel/hpmc.S
index c412c0adc4a..e158b6fbf1b 100644
--- a/arch/parisc/kernel/hpmc.S
+++ b/arch/parisc/kernel/hpmc.S
@@ -46,6 +46,8 @@
#include <asm/assembly.h>
#include <asm/pdc.h>
+#include <linux/linkage.h>
+
/*
* stack for os_hpmc, the HPMC handler.
* buffer for IODC procedures (for the HPMC handler).
@@ -69,17 +71,16 @@ hpmc_raddr:
#define HPMC_PIM_DATA_SIZE 896 /* Enough to hold all architected 2.0 state */
- .export hpmc_pim_data, data
.align 8
-hpmc_pim_data:
+ENTRY(hpmc_pim_data)
.block HPMC_PIM_DATA_SIZE
+END(hpmc_pim_data)
.text
- .export os_hpmc, code
.import intr_save, code
-
-os_hpmc:
+ENTRY(os_hpmc)
+.os_hpmc:
/*
* registers modified:
@@ -294,11 +295,11 @@ os_hpmc_6:
b .
nop
-
- /* this label used to compute os_hpmc checksum */
-
- .export os_hpmc_end, code
-
-os_hpmc_end:
-
+ENDPROC(os_hpmc)
+.os_hpmc_end:
nop
+.data
+.align 4
+ .export os_hpmc_size
+os_hpmc_size:
+ .word .os_hpmc_end-.os_hpmc
diff --git a/arch/parisc/kernel/init_task.c b/arch/parisc/kernel/init_task.c
deleted file mode 100644
index 7e898fd6441..00000000000
--- a/arch/parisc/kernel/init_task.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Static declaration of "init" task data structure.
- *
- * Copyright (C) 2000 Paul Bame <bame at parisc-linux.org>
- * Copyright (C) 2000-2001 John Marvin <jsm at parisc-linux.org>
- * Copyright (C) 2001 Helge Deller <deller @ parisc-linux.org>
- * Copyright (C) 2002 Matthew Wilcox <willy with parisc-linux.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/init_task.h>
-#include <linux/mqueue.h>
-
-#include <asm/uaccess.h>
-#include <asm/pgtable.h>
-#include <asm/pgalloc.h>
-
-static struct fs_struct init_fs = INIT_FS;
-static struct files_struct init_files = INIT_FILES;
-static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
-static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
-struct mm_struct init_mm = INIT_MM(init_mm);
-
-EXPORT_SYMBOL(init_mm);
-
-/*
- * Initial task structure.
- *
- * We need to make sure that this is 16384-byte aligned due to the
- * way process stacks are handled. This is done by having a special
- * "init_task" linker map entry..
- */
-unsigned char interrupt_stack[ISTACK_SIZE] __attribute__ ((section("init_istack"), aligned(4096)));
-union thread_union init_thread_union
- __attribute__((aligned(128))) __attribute__((__section__(".data.init_task"))) =
- { INIT_THREAD_INFO(init_task) };
-
-#ifdef __LP64__
-/* NOTE: This layout exactly conforms to the hybrid L2/L3 page table layout
- * with the first pmd adjacent to the pgd and below it. gcc doesn't actually
- * guarantee that global objects will be laid out in memory in the same order
- * as the order of declaration, so put these in different sections and use
- * the linker script to order them. */
-pmd_t pmd0[PTRS_PER_PMD] __attribute__ ((aligned(PAGE_SIZE))) __attribute__ ((__section__ (".data.vm0.pmd"))) = { {0}, };
-
-#endif
-pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__ ((aligned(PAGE_SIZE))) __attribute__ ((__section__ (".data.vm0.pgd"))) = { {0}, };
-pte_t pg0[PT_INITIAL * PTRS_PER_PTE] __attribute__ ((aligned(PAGE_SIZE))) __attribute__ ((__section__ (".data.vm0.pte"))) = { {0}, };
-
-/*
- * Initial task structure.
- *
- * All other task structs will be allocated on slabs in fork.c
- */
-EXPORT_SYMBOL(init_task);
-
-__asm__(".data");
-struct task_struct init_task = INIT_TASK(init_task);
diff --git a/arch/parisc/kernel/inventory.c b/arch/parisc/kernel/inventory.c
index 1a1c6642273..f0b6722fc70 100644
--- a/arch/parisc/kernel/inventory.c
+++ b/arch/parisc/kernel/inventory.c
@@ -38,7 +38,7 @@
*/
#undef DEBUG_PAT
-int pdc_type = PDC_TYPE_ILLEGAL;
+int pdc_type __read_mostly = PDC_TYPE_ILLEGAL;
void __init setup_pdc(void)
{
@@ -47,7 +47,7 @@ void __init setup_pdc(void)
struct pdc_system_map_mod_info module_result;
struct pdc_module_path module_path;
struct pdc_model model;
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
struct pdc_pat_cell_num cell_info;
#endif
@@ -73,7 +73,7 @@ void __init setup_pdc(void)
* clearer message.
*/
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
status = pdc_pat_cell_get_number(&cell_info);
if (status == PDC_OK) {
pdc_type = PDC_TYPE_PAT;
@@ -93,7 +93,7 @@ void __init setup_pdc(void)
case 0x6: /* 705, 710 */
case 0x7: /* 715, 725 */
case 0x8: /* 745, 747, 742 */
- case 0xA: /* 712 and similiar */
+ case 0xA: /* 712 and similar */
case 0xC: /* 715/64, at least */
pdc_type = PDC_TYPE_SNAKE;
@@ -120,8 +120,8 @@ set_pmem_entry(physmem_range_t *pmem_ptr, unsigned long start,
* pdc info is bad in this case).
*/
- if ( ((start & (PAGE_SIZE - 1)) != 0)
- || ((pages4k & ((1UL << PDC_PAGE_ADJ_SHIFT) - 1)) != 0) ) {
+ if (unlikely( ((start & (PAGE_SIZE - 1)) != 0)
+ || ((pages4k & ((1UL << PDC_PAGE_ADJ_SHIFT) - 1)) != 0) )) {
panic("Memory range doesn't align with page size!\n");
}
@@ -152,7 +152,7 @@ static void __init pagezero_memconfig(void)
npmem_ranges = 1;
}
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
/* All of the PDC PAT specific code is 64-bit only */
@@ -170,25 +170,31 @@ static void __init pagezero_memconfig(void)
static int __init
pat_query_module(ulong pcell_loc, ulong mod_index)
{
- pdc_pat_cell_mod_maddr_block_t pa_pdc_cell;
+ pdc_pat_cell_mod_maddr_block_t *pa_pdc_cell;
unsigned long bytecnt;
unsigned long temp; /* 64-bit scratch value */
long status; /* PDC return value status */
struct parisc_device *dev;
+ pa_pdc_cell = kmalloc(sizeof (*pa_pdc_cell), GFP_KERNEL);
+ if (!pa_pdc_cell)
+ panic("couldn't allocate memory for PDC_PAT_CELL!");
+
/* return cell module (PA or Processor view) */
status = pdc_pat_cell_module(&bytecnt, pcell_loc, mod_index,
- PA_VIEW, &pa_pdc_cell);
+ PA_VIEW, pa_pdc_cell);
if (status != PDC_OK) {
/* no more cell modules or error */
+ kfree(pa_pdc_cell);
return status;
}
- temp = pa_pdc_cell.cba;
- dev = alloc_pa_dev(PAT_GET_CBA(temp), &pa_pdc_cell.mod_path);
+ temp = pa_pdc_cell->cba;
+ dev = alloc_pa_dev(PAT_GET_CBA(temp), &(pa_pdc_cell->mod_path));
if (!dev) {
- return PDC_NE_MOD;
+ kfree(pa_pdc_cell);
+ return PDC_OK;
}
/* alloc_pa_dev sets dev->hpa */
@@ -203,8 +209,9 @@ pat_query_module(ulong pcell_loc, ulong mod_index)
/* save generic info returned from the call */
/* REVISIT: who is the consumer of this? not sure yet... */
- dev->mod_info = pa_pdc_cell.mod_info; /* pass to PAT_GET_ENTITY() */
- dev->pmod_loc = pa_pdc_cell.mod_location;
+ dev->mod_info = pa_pdc_cell->mod_info; /* pass to PAT_GET_ENTITY() */
+ dev->pmod_loc = pa_pdc_cell->mod_location;
+ dev->mod0 = pa_pdc_cell->mod[0];
register_parisc_device(dev); /* advertise device */
@@ -216,14 +223,14 @@ pat_query_module(ulong pcell_loc, ulong mod_index)
case PAT_ENTITY_PROC:
printk(KERN_DEBUG "PAT_ENTITY_PROC: id_eid 0x%lx\n",
- pa_pdc_cell.mod[0]);
+ pa_pdc_cell->mod[0]);
break;
case PAT_ENTITY_MEM:
printk(KERN_DEBUG
"PAT_ENTITY_MEM: amount 0x%lx min_gni_base 0x%lx min_gni_len 0x%lx\n",
- pa_pdc_cell.mod[0], pa_pdc_cell.mod[1],
- pa_pdc_cell.mod[2]);
+ pa_pdc_cell->mod[0], pa_pdc_cell->mod[1],
+ pa_pdc_cell->mod[2]);
break;
case PAT_ENTITY_CA:
printk(KERN_DEBUG "PAT_ENTITY_CA: %ld\n", pcell_loc);
@@ -243,23 +250,26 @@ pat_query_module(ulong pcell_loc, ulong mod_index)
print_ranges:
pdc_pat_cell_module(&bytecnt, pcell_loc, mod_index,
IO_VIEW, &io_pdc_cell);
- printk(KERN_DEBUG "ranges %ld\n", pa_pdc_cell.mod[1]);
- for (i = 0; i < pa_pdc_cell.mod[1]; i++) {
+ printk(KERN_DEBUG "ranges %ld\n", pa_pdc_cell->mod[1]);
+ for (i = 0; i < pa_pdc_cell->mod[1]; i++) {
printk(KERN_DEBUG
" PA_VIEW %ld: 0x%016lx 0x%016lx 0x%016lx\n",
- i, pa_pdc_cell.mod[2 + i * 3], /* type */
- pa_pdc_cell.mod[3 + i * 3], /* start */
- pa_pdc_cell.mod[4 + i * 3]); /* finish (ie end) */
+ i, pa_pdc_cell->mod[2 + i * 3], /* type */
+ pa_pdc_cell->mod[3 + i * 3], /* start */
+ pa_pdc_cell->mod[4 + i * 3]); /* finish (ie end) */
printk(KERN_DEBUG
" IO_VIEW %ld: 0x%016lx 0x%016lx 0x%016lx\n",
- i, io_pdc_cell.mod[2 + i * 3], /* type */
- io_pdc_cell.mod[3 + i * 3], /* start */
- io_pdc_cell.mod[4 + i * 3]); /* finish (ie end) */
+ i, io_pdc_cell->mod[2 + i * 3], /* type */
+ io_pdc_cell->mod[3 + i * 3], /* start */
+ io_pdc_cell->mod[4 + i * 3]); /* finish (ie end) */
}
printk(KERN_DEBUG "\n");
break;
}
#endif /* DEBUG_PAT */
+
+ kfree(pa_pdc_cell);
+
return PDC_OK;
}
@@ -408,13 +418,13 @@ static void __init sprockets_memconfig(void)
}
}
-#else /* !__LP64__ */
+#else /* !CONFIG_64BIT */
#define pat_inventory() do { } while (0)
#define pat_memconfig() do { } while (0)
#define sprockets_memconfig() pagezero_memconfig()
-#endif /* !__LP64__ */
+#endif /* !CONFIG_64BIT */
#ifndef CONFIG_PA20
@@ -499,7 +509,7 @@ add_system_map_addresses(struct parisc_device *dev, int num_addrs,
dev->addr = kmalloc(num_addrs * sizeof(unsigned long), GFP_KERNEL);
if(!dev->addr) {
printk(KERN_ERR "%s %s(): memory allocation failure\n",
- __FILE__, __FUNCTION__);
+ __FILE__, __func__);
return;
}
diff --git a/arch/parisc/kernel/ioctl32.c b/arch/parisc/kernel/ioctl32.c
deleted file mode 100644
index 0a331104ad5..00000000000
--- a/arch/parisc/kernel/ioctl32.c
+++ /dev/null
@@ -1,606 +0,0 @@
-/* $Id: ioctl32.c,v 1.5 2002/10/18 00:21:43 varenet Exp $
- * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
- *
- * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
- * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
- *
- * These routines maintain argument size conversion between 32bit and 64bit
- * ioctls.
- */
-
-#include <linux/syscalls.h>
-
-#define INCLUDES
-#include "compat_ioctl.c"
-
-#include <asm/perf.h>
-#include <asm/ioctls.h>
-
-#define CODE
-#include "compat_ioctl.c"
-
-/* Use this to get at 32-bit user passed pointers.
- See sys_sparc32.c for description about these. */
-#define A(__x) ((unsigned long)(__x))
-/* The same for use with copy_from_user() and copy_to_user(). */
-#define B(__x) ((void *)(unsigned long)(__x))
-
-#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
-/* This really belongs in include/linux/drm.h -DaveM */
-#include "../../../drivers/char/drm/drm.h"
-
-typedef struct drm32_version {
- int version_major; /* Major version */
- int version_minor; /* Minor version */
- int version_patchlevel;/* Patch level */
- int name_len; /* Length of name buffer */
- u32 name; /* Name of driver */
- int date_len; /* Length of date buffer */
- u32 date; /* User-space buffer to hold date */
- int desc_len; /* Length of desc buffer */
- u32 desc; /* User-space buffer to hold desc */
-} drm32_version_t;
-#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t)
-
-static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_version_t *uversion = (drm32_version_t *)arg;
- char *name_ptr, *date_ptr, *desc_ptr;
- u32 tmp1, tmp2, tmp3;
- drm_version_t kversion;
- mm_segment_t old_fs;
- int ret;
-
- memset(&kversion, 0, sizeof(kversion));
- if (get_user(kversion.name_len, &uversion->name_len) ||
- get_user(kversion.date_len, &uversion->date_len) ||
- get_user(kversion.desc_len, &uversion->desc_len) ||
- get_user(tmp1, &uversion->name) ||
- get_user(tmp2, &uversion->date) ||
- get_user(tmp3, &uversion->desc))
- return -EFAULT;
-
- name_ptr = (char *) A(tmp1);
- date_ptr = (char *) A(tmp2);
- desc_ptr = (char *) A(tmp3);
-
- ret = -ENOMEM;
- if (kversion.name_len && name_ptr) {
- kversion.name = kmalloc(kversion.name_len, GFP_KERNEL);
- if (!kversion.name)
- goto out;
- }
- if (kversion.date_len && date_ptr) {
- kversion.date = kmalloc(kversion.date_len, GFP_KERNEL);
- if (!kversion.date)
- goto out;
- }
- if (kversion.desc_len && desc_ptr) {
- kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL);
- if (!kversion.desc)
- goto out;
- }
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion);
- set_fs(old_fs);
-
- if (!ret) {
- if ((kversion.name &&
- copy_to_user(name_ptr, kversion.name, kversion.name_len)) ||
- (kversion.date &&
- copy_to_user(date_ptr, kversion.date, kversion.date_len)) ||
- (kversion.desc &&
- copy_to_user(desc_ptr, kversion.desc, kversion.desc_len)))
- ret = -EFAULT;
- if (put_user(kversion.version_major, &uversion->version_major) ||
- put_user(kversion.version_minor, &uversion->version_minor) ||
- put_user(kversion.version_patchlevel, &uversion->version_patchlevel) ||
- put_user(kversion.name_len, &uversion->name_len) ||
- put_user(kversion.date_len, &uversion->date_len) ||
- put_user(kversion.desc_len, &uversion->desc_len))
- ret = -EFAULT;
- }
-
-out:
- kfree(kversion.name);
- kfree(kversion.date);
- kfree(kversion.desc);
- return ret;
-}
-
-typedef struct drm32_unique {
- int unique_len; /* Length of unique */
- u32 unique; /* Unique name for driver instantiation */
-} drm32_unique_t;
-#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t)
-#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t)
-
-static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_unique_t *uarg = (drm32_unique_t *)arg;
- drm_unique_t karg;
- mm_segment_t old_fs;
- char *uptr;
- u32 tmp;
- int ret;
-
- if (get_user(karg.unique_len, &uarg->unique_len))
- return -EFAULT;
- karg.unique = NULL;
-
- if (get_user(tmp, &uarg->unique))
- return -EFAULT;
-
- uptr = (char *) A(tmp);
-
- if (uptr) {
- karg.unique = kmalloc(karg.unique_len, GFP_KERNEL);
- if (!karg.unique)
- return -ENOMEM;
- if (cmd == DRM32_IOCTL_SET_UNIQUE &&
- copy_from_user(karg.unique, uptr, karg.unique_len)) {
- kfree(karg.unique);
- return -EFAULT;
- }
- }
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- if (cmd == DRM32_IOCTL_GET_UNIQUE)
- ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg);
- else
- ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg);
- set_fs(old_fs);
-
- if (!ret) {
- if (cmd == DRM32_IOCTL_GET_UNIQUE &&
- uptr != NULL &&
- copy_to_user(uptr, karg.unique, karg.unique_len))
- ret = -EFAULT;
- if (put_user(karg.unique_len, &uarg->unique_len))
- ret = -EFAULT;
- }
-
- kfree(karg.unique);
- return ret;
-}
-
-typedef struct drm32_map {
- u32 offset; /* Requested physical address (0 for SAREA)*/
- u32 size; /* Requested physical size (bytes) */
- drm_map_type_t type; /* Type of memory to map */
- drm_map_flags_t flags; /* Flags */
- u32 handle; /* User-space: "Handle" to pass to mmap */
- /* Kernel-space: kernel-virtual address */
- int mtrr; /* MTRR slot used */
- /* Private data */
-} drm32_map_t;
-#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t)
-
-static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_map_t *uarg = (drm32_map_t *) arg;
- drm_map_t karg;
- mm_segment_t old_fs;
- u32 tmp;
- int ret;
-
- ret = get_user(karg.offset, &uarg->offset);
- ret |= get_user(karg.size, &uarg->size);
- ret |= get_user(karg.type, &uarg->type);
- ret |= get_user(karg.flags, &uarg->flags);
- ret |= get_user(tmp, &uarg->handle);
- ret |= get_user(karg.mtrr, &uarg->mtrr);
- if (ret)
- return -EFAULT;
-
- karg.handle = (void *) A(tmp);
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg);
- set_fs(old_fs);
-
- if (!ret) {
- ret = put_user(karg.offset, &uarg->offset);
- ret |= put_user(karg.size, &uarg->size);
- ret |= put_user(karg.type, &uarg->type);
- ret |= put_user(karg.flags, &uarg->flags);
- tmp = (u32) (long)karg.handle;
- ret |= put_user(tmp, &uarg->handle);
- ret |= put_user(karg.mtrr, &uarg->mtrr);
- if (ret)
- ret = -EFAULT;
- }
-
- return ret;
-}
-
-typedef struct drm32_buf_info {
- int count; /* Entries in list */
- u32 list; /* (drm_buf_desc_t *) */
-} drm32_buf_info_t;
-#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t)
-
-static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg;
- drm_buf_desc_t *ulist;
- drm_buf_info_t karg;
- mm_segment_t old_fs;
- int orig_count, ret;
- u32 tmp;
-
- if (get_user(karg.count, &uarg->count) ||
- get_user(tmp, &uarg->list))
- return -EFAULT;
-
- ulist = (drm_buf_desc_t *) A(tmp);
-
- orig_count = karg.count;
-
- karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL);
- if (!karg.list)
- return -EFAULT;
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg);
- set_fs(old_fs);
-
- if (!ret) {
- if (karg.count <= orig_count &&
- (copy_to_user(ulist, karg.list,
- karg.count * sizeof(drm_buf_desc_t))))
- ret = -EFAULT;
- if (put_user(karg.count, &uarg->count))
- ret = -EFAULT;
- }
-
- kfree(karg.list);
- return ret;
-}
-
-typedef struct drm32_buf_free {
- int count;
- u32 list; /* (int *) */
-} drm32_buf_free_t;
-#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t)
-
-static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg;
- drm_buf_free_t karg;
- mm_segment_t old_fs;
- int *ulist;
- int ret;
- u32 tmp;
-
- if (get_user(karg.count, &uarg->count) ||
- get_user(tmp, &uarg->list))
- return -EFAULT;
-
- ulist = (int *) A(tmp);
-
- karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL);
- if (!karg.list)
- return -ENOMEM;
-
- ret = -EFAULT;
- if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int))))
- goto out;
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg);
- set_fs(old_fs);
-
-out:
- kfree(karg.list);
- return ret;
-}
-
-typedef struct drm32_buf_pub {
- int idx; /* Index into master buflist */
- int total; /* Buffer size */
- int used; /* Amount of buffer in use (for DMA) */
- u32 address; /* Address of buffer (void *) */
-} drm32_buf_pub_t;
-
-typedef struct drm32_buf_map {
- int count; /* Length of buflist */
- u32 virtual; /* Mmaped area in user-virtual (void *) */
- u32 list; /* Buffer information (drm_buf_pub_t *) */
-} drm32_buf_map_t;
-#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t)
-
-static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg;
- drm32_buf_pub_t *ulist;
- drm_buf_map_t karg;
- mm_segment_t old_fs;
- int orig_count, ret, i;
- u32 tmp1, tmp2;
-
- if (get_user(karg.count, &uarg->count) ||
- get_user(tmp1, &uarg->virtual) ||
- get_user(tmp2, &uarg->list))
- return -EFAULT;
-
- karg.virtual = (void *) A(tmp1);
- ulist = (drm32_buf_pub_t *) A(tmp2);
-
- orig_count = karg.count;
-
- karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL);
- if (!karg.list)
- return -ENOMEM;
-
- ret = -EFAULT;
- for (i = 0; i < karg.count; i++) {
- if (get_user(karg.list[i].idx, &ulist[i].idx) ||
- get_user(karg.list[i].total, &ulist[i].total) ||
- get_user(karg.list[i].used, &ulist[i].used) ||
- get_user(tmp1, &ulist[i].address))
- goto out;
-
- karg.list[i].address = (void *) A(tmp1);
- }
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg);
- set_fs(old_fs);
-
- if (!ret) {
- for (i = 0; i < orig_count; i++) {
- tmp1 = (u32) (long) karg.list[i].address;
- if (put_user(karg.list[i].idx, &ulist[i].idx) ||
- put_user(karg.list[i].total, &ulist[i].total) ||
- put_user(karg.list[i].used, &ulist[i].used) ||
- put_user(tmp1, &ulist[i].address)) {
- ret = -EFAULT;
- goto out;
- }
- }
- if (put_user(karg.count, &uarg->count))
- ret = -EFAULT;
- }
-
-out:
- kfree(karg.list);
- return ret;
-}
-
-typedef struct drm32_dma {
- /* Indices here refer to the offset into
- buflist in drm_buf_get_t. */
- int context; /* Context handle */
- int send_count; /* Number of buffers to send */
- u32 send_indices; /* List of handles to buffers (int *) */
- u32 send_sizes; /* Lengths of data to send (int *) */
- drm_dma_flags_t flags; /* Flags */
- int request_count; /* Number of buffers requested */
- int request_size; /* Desired size for buffers */
- u32 request_indices; /* Buffer information (int *) */
- u32 request_sizes; /* (int *) */
- int granted_count; /* Number of buffers granted */
-} drm32_dma_t;
-#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t)
-
-/* RED PEN The DRM layer blindly dereferences the send/request
- * indice/size arrays even though they are userland
- * pointers. -DaveM
- */
-static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_dma_t *uarg = (drm32_dma_t *) arg;
- int *u_si, *u_ss, *u_ri, *u_rs;
- drm_dma_t karg;
- mm_segment_t old_fs;
- int ret;
- u32 tmp1, tmp2, tmp3, tmp4;
-
- karg.send_indices = karg.send_sizes = NULL;
- karg.request_indices = karg.request_sizes = NULL;
-
- if (get_user(karg.context, &uarg->context) ||
- get_user(karg.send_count, &uarg->send_count) ||
- get_user(tmp1, &uarg->send_indices) ||
- get_user(tmp2, &uarg->send_sizes) ||
- get_user(karg.flags, &uarg->flags) ||
- get_user(karg.request_count, &uarg->request_count) ||
- get_user(karg.request_size, &uarg->request_size) ||
- get_user(tmp3, &uarg->request_indices) ||
- get_user(tmp4, &uarg->request_sizes) ||
- get_user(karg.granted_count, &uarg->granted_count))
- return -EFAULT;
-
- u_si = (int *) A(tmp1);
- u_ss = (int *) A(tmp2);
- u_ri = (int *) A(tmp3);
- u_rs = (int *) A(tmp4);
-
- if (karg.send_count) {
- karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL);
- karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL);
-
- ret = -ENOMEM;
- if (!karg.send_indices || !karg.send_sizes)
- goto out;
-
- ret = -EFAULT;
- if (copy_from_user(karg.send_indices, u_si,
- (karg.send_count * sizeof(int))) ||
- copy_from_user(karg.send_sizes, u_ss,
- (karg.send_count * sizeof(int))))
- goto out;
- }
-
- if (karg.request_count) {
- karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL);
- karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL);
-
- ret = -ENOMEM;
- if (!karg.request_indices || !karg.request_sizes)
- goto out;
-
- ret = -EFAULT;
- if (copy_from_user(karg.request_indices, u_ri,
- (karg.request_count * sizeof(int))) ||
- copy_from_user(karg.request_sizes, u_rs,
- (karg.request_count * sizeof(int))))
- goto out;
- }
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg);
- set_fs(old_fs);
-
- if (!ret) {
- if (put_user(karg.context, &uarg->context) ||
- put_user(karg.send_count, &uarg->send_count) ||
- put_user(karg.flags, &uarg->flags) ||
- put_user(karg.request_count, &uarg->request_count) ||
- put_user(karg.request_size, &uarg->request_size) ||
- put_user(karg.granted_count, &uarg->granted_count))
- ret = -EFAULT;
-
- if (karg.send_count) {
- if (copy_to_user(u_si, karg.send_indices,
- (karg.send_count * sizeof(int))) ||
- copy_to_user(u_ss, karg.send_sizes,
- (karg.send_count * sizeof(int))))
- ret = -EFAULT;
- }
- if (karg.request_count) {
- if (copy_to_user(u_ri, karg.request_indices,
- (karg.request_count * sizeof(int))) ||
- copy_to_user(u_rs, karg.request_sizes,
- (karg.request_count * sizeof(int))))
- ret = -EFAULT;
- }
- }
-
-out:
- kfree(karg.send_indices);
- kfree(karg.send_sizes);
- kfree(karg.request_indices);
- kfree(karg.request_sizes);
- return ret;
-}
-
-typedef struct drm32_ctx_res {
- int count;
- u32 contexts; /* (drm_ctx_t *) */
-} drm32_ctx_res_t;
-#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm32_ctx_res_t)
-
-static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg;
- drm_ctx_t *ulist;
- drm_ctx_res_t karg;
- mm_segment_t old_fs;
- int orig_count, ret;
- u32 tmp;
-
- karg.contexts = NULL;
- if (get_user(karg.count, &uarg->count) ||
- get_user(tmp, &uarg->contexts))
- return -EFAULT;
-
- ulist = (drm_ctx_t *) A(tmp);
-
- orig_count = karg.count;
- if (karg.count && ulist) {
- karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL);
- if (!karg.contexts)
- return -ENOMEM;
- if (copy_from_user(karg.contexts, ulist,
- (karg.count * sizeof(drm_ctx_t)))) {
- kfree(karg.contexts);
- return -EFAULT;
- }
- }
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg);
- set_fs(old_fs);
-
- if (!ret) {
- if (orig_count) {
- if (copy_to_user(ulist, karg.contexts,
- (orig_count * sizeof(drm_ctx_t))))
- ret = -EFAULT;
- }
- if (put_user(karg.count, &uarg->count))
- ret = -EFAULT;
- }
-
- kfree(karg.contexts);
- return ret;
-}
-
-#endif
-
-#define HANDLE_IOCTL(cmd, handler) { cmd, (ioctl_trans_handler_t)handler, NULL },
-#define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL(cmd, sys_ioctl)
-
-#define IOCTL_TABLE_START struct ioctl_trans ioctl_start[] = {
-#define IOCTL_TABLE_END };
-
-IOCTL_TABLE_START
-#include <linux/compat_ioctl.h>
-
-#define DECLARES
-#include "compat_ioctl.c"
-
-/* PA-specific ioctls */
-COMPATIBLE_IOCTL(PA_PERF_ON)
-COMPATIBLE_IOCTL(PA_PERF_OFF)
-COMPATIBLE_IOCTL(PA_PERF_VERSION)
-
-/* And these ioctls need translation */
-HANDLE_IOCTL(SIOCGPPPSTATS, dev_ifsioc)
-HANDLE_IOCTL(SIOCGPPPCSTATS, dev_ifsioc)
-HANDLE_IOCTL(SIOCGPPPVER, dev_ifsioc)
-
-#if defined(CONFIG_GEN_RTC)
-COMPATIBLE_IOCTL(RTC_AIE_ON)
-COMPATIBLE_IOCTL(RTC_AIE_OFF)
-COMPATIBLE_IOCTL(RTC_UIE_ON)
-COMPATIBLE_IOCTL(RTC_UIE_OFF)
-COMPATIBLE_IOCTL(RTC_PIE_ON)
-COMPATIBLE_IOCTL(RTC_PIE_OFF)
-COMPATIBLE_IOCTL(RTC_WIE_ON)
-COMPATIBLE_IOCTL(RTC_WIE_OFF)
-COMPATIBLE_IOCTL(RTC_ALM_SET) /* struct rtc_time only has ints */
-COMPATIBLE_IOCTL(RTC_ALM_READ) /* struct rtc_time only has ints */
-COMPATIBLE_IOCTL(RTC_RD_TIME) /* struct rtc_time only has ints */
-COMPATIBLE_IOCTL(RTC_SET_TIME) /* struct rtc_time only has ints */
-HANDLE_IOCTL(RTC_IRQP_READ, w_long)
-COMPATIBLE_IOCTL(RTC_IRQP_SET)
-HANDLE_IOCTL(RTC_EPOCH_READ, w_long)
-COMPATIBLE_IOCTL(RTC_EPOCH_SET)
-#endif
-
-#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
-HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version);
-HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique);
-HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique);
-HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap);
-HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs);
-HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs);
-HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs);
-HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma);
-HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx);
-#endif /* DRM */
-IOCTL_TABLE_END
-
-int ioctl_table_size = ARRAY_SIZE(ioctl_start);
diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c
index 006385dbee6..cfe056fe7f5 100644
--- a/arch/parisc/kernel/irq.c
+++ b/arch/parisc/kernel/irq.c
@@ -22,19 +22,21 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/bitops.h>
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
-#include <linux/spinlock.h>
#include <linux/types.h>
+#include <asm/io.h>
+
+#include <asm/smp.h>
+#include <asm/ldcw.h>
#undef PARISC_IRQ_CR16_COUNTS
-extern irqreturn_t timer_interrupt(int, void *, struct pt_regs *);
-extern irqreturn_t ipi_interrupt(int, void *, struct pt_regs *);
+extern irqreturn_t timer_interrupt(int, void *);
+extern irqreturn_t ipi_interrupt(int, void *);
#define EIEM_MASK(irq) (1UL<<(CPU_IRQ_MAX - irq))
@@ -43,48 +45,156 @@ extern irqreturn_t ipi_interrupt(int, void *, struct pt_regs *);
*/
static volatile unsigned long cpu_eiem = 0;
-static void cpu_set_eiem(void *info)
-{
- set_eiem((unsigned long) info);
-}
+/*
+** local ACK bitmap ... habitually set to 1, but reset to zero
+** between ->ack() and ->end() of the interrupt to prevent
+** re-interruption of a processing interrupt.
+*/
+static DEFINE_PER_CPU(unsigned long, local_ack_eiem) = ~0UL;
-static inline void cpu_disable_irq(unsigned int irq)
+static void cpu_mask_irq(struct irq_data *d)
{
- unsigned long eirr_bit = EIEM_MASK(irq);
+ unsigned long eirr_bit = EIEM_MASK(d->irq);
cpu_eiem &= ~eirr_bit;
- on_each_cpu(cpu_set_eiem, (void *) cpu_eiem, 1, 1);
+ /* Do nothing on the other CPUs. If they get this interrupt,
+ * The & cpu_eiem in the do_cpu_irq_mask() ensures they won't
+ * handle it, and the set_eiem() at the bottom will ensure it
+ * then gets disabled */
}
-static void cpu_enable_irq(unsigned int irq)
+static void __cpu_unmask_irq(unsigned int irq)
{
unsigned long eirr_bit = EIEM_MASK(irq);
- mtctl(eirr_bit, 23); /* clear EIRR bit before unmasking */
cpu_eiem |= eirr_bit;
- on_each_cpu(cpu_set_eiem, (void *) cpu_eiem, 1, 1);
+
+ /* This is just a simple NOP IPI. But what it does is cause
+ * all the other CPUs to do a set_eiem(cpu_eiem) at the end
+ * of the interrupt handler */
+ smp_send_all_nop();
}
-static unsigned int cpu_startup_irq(unsigned int irq)
+static void cpu_unmask_irq(struct irq_data *d)
{
- cpu_enable_irq(irq);
+ __cpu_unmask_irq(d->irq);
+}
+
+void cpu_ack_irq(struct irq_data *d)
+{
+ unsigned long mask = EIEM_MASK(d->irq);
+ int cpu = smp_processor_id();
+
+ /* Clear in EIEM so we can no longer process */
+ per_cpu(local_ack_eiem, cpu) &= ~mask;
+
+ /* disable the interrupt */
+ set_eiem(cpu_eiem & per_cpu(local_ack_eiem, cpu));
+
+ /* and now ack it */
+ mtctl(mask, 23);
+}
+
+void cpu_eoi_irq(struct irq_data *d)
+{
+ unsigned long mask = EIEM_MASK(d->irq);
+ int cpu = smp_processor_id();
+
+ /* set it in the eiems---it's no longer in process */
+ per_cpu(local_ack_eiem, cpu) |= mask;
+
+ /* enable the interrupt */
+ set_eiem(cpu_eiem & per_cpu(local_ack_eiem, cpu));
+}
+
+#ifdef CONFIG_SMP
+int cpu_check_affinity(struct irq_data *d, const struct cpumask *dest)
+{
+ int cpu_dest;
+
+ /* timer and ipi have to always be received on all CPUs */
+ if (irqd_is_per_cpu(d))
+ return -EINVAL;
+
+ /* whatever mask they set, we just allow one CPU */
+ cpu_dest = cpumask_first_and(dest, cpu_online_mask);
+
+ return cpu_dest;
+}
+
+static int cpu_set_affinity_irq(struct irq_data *d, const struct cpumask *dest,
+ bool force)
+{
+ int cpu_dest;
+
+ cpu_dest = cpu_check_affinity(d, dest);
+ if (cpu_dest < 0)
+ return -1;
+
+ cpumask_copy(d->affinity, dest);
+
return 0;
}
+#endif
-void no_ack_irq(unsigned int irq) { }
-void no_end_irq(unsigned int irq) { }
-
-static struct hw_interrupt_type cpu_interrupt_type = {
- .typename = "CPU",
- .startup = cpu_startup_irq,
- .shutdown = cpu_disable_irq,
- .enable = cpu_enable_irq,
- .disable = cpu_disable_irq,
- .ack = no_ack_irq,
- .end = no_end_irq,
-// .set_affinity = cpu_set_affinity_irq,
+static struct irq_chip cpu_interrupt_type = {
+ .name = "CPU",
+ .irq_mask = cpu_mask_irq,
+ .irq_unmask = cpu_unmask_irq,
+ .irq_ack = cpu_ack_irq,
+ .irq_eoi = cpu_eoi_irq,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = cpu_set_affinity_irq,
+#endif
+ /* XXX: Needs to be written. We managed without it so far, but
+ * we really ought to write it.
+ */
+ .irq_retrigger = NULL,
};
+DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+#define irq_stats(x) (&per_cpu(irq_stat, x))
+
+/*
+ * /proc/interrupts printing for arch specific interrupts
+ */
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+ int j;
+
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ seq_printf(p, "%*s: ", prec, "STK");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->kernel_stack_usage);
+ seq_puts(p, " Kernel stack usage\n");
+# ifdef CONFIG_IRQSTACKS
+ seq_printf(p, "%*s: ", prec, "IST");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_stack_usage);
+ seq_puts(p, " Interrupt stack usage\n");
+# endif
+#endif
+#ifdef CONFIG_SMP
+ seq_printf(p, "%*s: ", prec, "RES");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count);
+ seq_puts(p, " Rescheduling interrupts\n");
+#endif
+ seq_printf(p, "%*s: ", prec, "UAH");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_unaligned_count);
+ seq_puts(p, " Unaligned access handler traps\n");
+ seq_printf(p, "%*s: ", prec, "FPA");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_fpassist_count);
+ seq_puts(p, " Floating point assist traps\n");
+ seq_printf(p, "%*s: ", prec, "TLB");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count);
+ seq_puts(p, " TLB shootdowns\n");
+ return 0;
+}
+
int show_interrupts(struct seq_file *p, void *v)
{
int i = *(loff_t *) v, j;
@@ -102,21 +212,22 @@ int show_interrupts(struct seq_file *p, void *v)
}
if (i < NR_IRQS) {
+ struct irq_desc *desc = irq_to_desc(i);
struct irqaction *action;
- spin_lock_irqsave(&irq_desc[i].lock, flags);
- action = irq_desc[i].action;
+ raw_spin_lock_irqsave(&desc->lock, flags);
+ action = desc->action;
if (!action)
goto skip;
seq_printf(p, "%3d: ", i);
#ifdef CONFIG_SMP
for_each_online_cpu(j)
- seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
+ seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
#else
seq_printf(p, "%10u ", kstat_irqs(i));
#endif
- seq_printf(p, " %14s", irq_desc[i].handler->typename);
+ seq_printf(p, " %14s", irq_desc_get_chip(desc)->name);
#ifndef PARISC_IRQ_CR16_COUNTS
seq_printf(p, " %s", action->name);
@@ -148,9 +259,12 @@ int show_interrupts(struct seq_file *p, void *v)
seq_putc(p, '\n');
skip:
- spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
}
+ if (i == NR_IRQS)
+ arch_show_interrupts(p, 3);
+
return 0;
}
@@ -164,17 +278,18 @@ int show_interrupts(struct seq_file *p, void *v)
** Then use that to get the Transaction address and data.
*/
-int cpu_claim_irq(unsigned int irq, struct hw_interrupt_type *type, void *data)
+int cpu_claim_irq(unsigned int irq, struct irq_chip *type, void *data)
{
- if (irq_desc[irq].action)
+ if (irq_has_action(irq))
return -EBUSY;
- if (irq_desc[irq].handler != &cpu_interrupt_type)
+ if (irq_get_chip(irq) != &cpu_interrupt_type)
return -EBUSY;
+ /* for iosapic interrupts */
if (type) {
- irq_desc[irq].handler = type;
- irq_desc[irq].handler_data = data;
- cpu_interrupt_type.enable(irq);
+ irq_set_chip_and_handler(irq, type, handle_percpu_irq);
+ irq_set_chip_data(irq, data);
+ __cpu_unmask_irq(irq);
}
return 0;
}
@@ -219,6 +334,18 @@ int txn_alloc_irq(unsigned int bits_wide)
return -1;
}
+
+unsigned long txn_affinity_addr(unsigned int irq, int cpu)
+{
+#ifdef CONFIG_SMP
+ struct irq_data *d = irq_get_irq_data(irq);
+ cpumask_copy(d->affinity, cpumask_of(cpu));
+#endif
+
+ return per_cpu(cpu_data, cpu).txn_addr;
+}
+
+
unsigned long txn_alloc_addr(unsigned int virt_irq)
{
static int next_cpu = -1;
@@ -226,14 +353,15 @@ unsigned long txn_alloc_addr(unsigned int virt_irq)
next_cpu++; /* assign to "next" CPU we want this bugger on */
/* validate entry */
- while ((next_cpu < NR_CPUS) && (!cpu_data[next_cpu].txn_addr ||
- !cpu_online(next_cpu)))
+ while ((next_cpu < nr_cpu_ids) &&
+ (!per_cpu(cpu_data, next_cpu).txn_addr ||
+ !cpu_online(next_cpu)))
next_cpu++;
- if (next_cpu >= NR_CPUS)
+ if (next_cpu >= nr_cpu_ids)
next_cpu = 0; /* nothing else, assign monarch */
- return cpu_data[next_cpu].txn_addr;
+ return txn_affinity_addr(virt_irq, next_cpu);
}
@@ -242,61 +370,200 @@ unsigned int txn_alloc_data(unsigned int virt_irq)
return virt_irq - CPU_IRQ_BASE;
}
-/* ONLY called from entry.S:intr_extint() */
-void do_cpu_irq_mask(struct pt_regs *regs)
+static inline int eirr_to_irq(unsigned long eirr)
{
- unsigned long eirr_val;
+ int bit = fls_long(eirr);
+ return (BITS_PER_LONG - bit) + TIMER_IRQ;
+}
- irq_enter();
+#ifdef CONFIG_IRQSTACKS
+/*
+ * IRQ STACK - used for irq handler
+ */
+#define IRQ_STACK_SIZE (4096 << 2) /* 16k irq stack size */
- /*
- * Only allow interrupt processing to be interrupted by the
- * timer tick
- */
- set_eiem(EIEM_MASK(TIMER_IRQ));
+union irq_stack_union {
+ unsigned long stack[IRQ_STACK_SIZE/sizeof(unsigned long)];
+ volatile unsigned int slock[4];
+ volatile unsigned int lock[1];
+};
+
+DEFINE_PER_CPU(union irq_stack_union, irq_stack_union) = {
+ .slock = { 1,1,1,1 },
+ };
+#endif
+
+
+int sysctl_panic_on_stackoverflow = 1;
+
+static inline void stack_overflow_check(struct pt_regs *regs)
+{
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ #define STACK_MARGIN (256*6)
+
+ /* Our stack starts directly behind the thread_info struct. */
+ unsigned long stack_start = (unsigned long) current_thread_info();
+ unsigned long sp = regs->gr[30];
+ unsigned long stack_usage;
+ unsigned int *last_usage;
+ int cpu = smp_processor_id();
+
+ /* if sr7 != 0, we interrupted a userspace process which we do not want
+ * to check for stack overflow. We will only check the kernel stack. */
+ if (regs->sr[7])
+ return;
+
+ /* calculate kernel stack usage */
+ stack_usage = sp - stack_start;
+#ifdef CONFIG_IRQSTACKS
+ if (likely(stack_usage <= THREAD_SIZE))
+ goto check_kernel_stack; /* found kernel stack */
+
+ /* check irq stack usage */
+ stack_start = (unsigned long) &per_cpu(irq_stack_union, cpu).stack;
+ stack_usage = sp - stack_start;
+
+ last_usage = &per_cpu(irq_stat.irq_stack_usage, cpu);
+ if (unlikely(stack_usage > *last_usage))
+ *last_usage = stack_usage;
+
+ if (likely(stack_usage < (IRQ_STACK_SIZE - STACK_MARGIN)))
+ return;
+
+ pr_emerg("stackcheck: %s will most likely overflow irq stack "
+ "(sp:%lx, stk bottom-top:%lx-%lx)\n",
+ current->comm, sp, stack_start, stack_start + IRQ_STACK_SIZE);
+ goto panic_check;
+
+check_kernel_stack:
+#endif
+
+ /* check kernel stack usage */
+ last_usage = &per_cpu(irq_stat.kernel_stack_usage, cpu);
- /* 1) only process IRQs that are enabled/unmasked (cpu_eiem)
- * 2) We loop here on EIRR contents in order to avoid
- * nested interrupts or having to take another interrupt
- * when we could have just handled it right away.
+ if (unlikely(stack_usage > *last_usage))
+ *last_usage = stack_usage;
+
+ if (likely(stack_usage < (THREAD_SIZE - STACK_MARGIN)))
+ return;
+
+ pr_emerg("stackcheck: %s will most likely overflow kernel stack "
+ "(sp:%lx, stk bottom-top:%lx-%lx)\n",
+ current->comm, sp, stack_start, stack_start + THREAD_SIZE);
+
+#ifdef CONFIG_IRQSTACKS
+panic_check:
+#endif
+ if (sysctl_panic_on_stackoverflow)
+ panic("low stack detected by irq handler - check messages\n");
+#endif
+}
+
+#ifdef CONFIG_IRQSTACKS
+/* in entry.S: */
+void call_on_stack(unsigned long p1, void *func, unsigned long new_stack);
+
+static void execute_on_irq_stack(void *func, unsigned long param1)
+{
+ union irq_stack_union *union_ptr;
+ unsigned long irq_stack;
+ volatile unsigned int *irq_stack_in_use;
+
+ union_ptr = &per_cpu(irq_stack_union, smp_processor_id());
+ irq_stack = (unsigned long) &union_ptr->stack;
+ irq_stack = ALIGN(irq_stack + sizeof(irq_stack_union.slock),
+ 64); /* align for stack frame usage */
+
+ /* We may be called recursive. If we are already using the irq stack,
+ * just continue to use it. Use spinlocks to serialize
+ * the irq stack usage.
*/
- for (;;) {
- unsigned long bit = (1UL << (BITS_PER_LONG - 1));
- unsigned int irq;
- eirr_val = mfctl(23) & cpu_eiem;
- if (!eirr_val)
- break;
+ irq_stack_in_use = (volatile unsigned int *)__ldcw_align(union_ptr);
+ if (!__ldcw(irq_stack_in_use)) {
+ void (*direct_call)(unsigned long p1) = func;
+
+ /* We are using the IRQ stack already.
+ * Do direct call on current stack. */
+ direct_call(param1);
+ return;
+ }
+
+ /* This is where we switch to the IRQ stack. */
+ call_on_stack(param1, func, irq_stack);
- if (eirr_val & EIEM_MASK(TIMER_IRQ))
- set_eiem(0);
+ /* free up irq stack usage. */
+ *irq_stack_in_use = 1;
+}
+
+void do_softirq_own_stack(void)
+{
+ execute_on_irq_stack(__do_softirq, 0);
+}
+#endif /* CONFIG_IRQSTACKS */
- mtctl(eirr_val, 23); /* reset bits we are going to process */
+/* ONLY called from entry.S:intr_extint() */
+void do_cpu_irq_mask(struct pt_regs *regs)
+{
+ struct pt_regs *old_regs;
+ unsigned long eirr_val;
+ int irq, cpu = smp_processor_id();
+#ifdef CONFIG_SMP
+ struct irq_desc *desc;
+ cpumask_t dest;
+#endif
- /* Work our way from MSb to LSb...same order we alloc EIRs */
- for (irq = TIMER_IRQ; eirr_val && bit; bit>>=1, irq++) {
- if (!(bit & eirr_val))
- continue;
+ old_regs = set_irq_regs(regs);
+ local_irq_disable();
+ irq_enter();
- /* clear bit in mask - can exit loop sooner */
- eirr_val &= ~bit;
+ eirr_val = mfctl(23) & cpu_eiem & per_cpu(local_ack_eiem, cpu);
+ if (!eirr_val)
+ goto set_out;
+ irq = eirr_to_irq(eirr_val);
- __do_IRQ(irq, regs);
- }
+#ifdef CONFIG_SMP
+ desc = irq_to_desc(irq);
+ cpumask_copy(&dest, desc->irq_data.affinity);
+ if (irqd_is_per_cpu(&desc->irq_data) &&
+ !cpu_isset(smp_processor_id(), dest)) {
+ int cpu = first_cpu(dest);
+
+ printk(KERN_DEBUG "redirecting irq %d from CPU %d to %d\n",
+ irq, smp_processor_id(), cpu);
+ gsc_writel(irq + CPU_IRQ_BASE,
+ per_cpu(cpu_data, cpu).hpa);
+ goto set_out;
}
- set_eiem(cpu_eiem);
+#endif
+ stack_overflow_check(regs);
+
+#ifdef CONFIG_IRQSTACKS
+ execute_on_irq_stack(&generic_handle_irq, irq);
+#else
+ generic_handle_irq(irq);
+#endif /* CONFIG_IRQSTACKS */
+
+ out:
irq_exit();
-}
+ set_irq_regs(old_regs);
+ return;
+ set_out:
+ set_eiem(cpu_eiem & per_cpu(local_ack_eiem, cpu));
+ goto out;
+}
static struct irqaction timer_action = {
.handler = timer_interrupt,
.name = "timer",
+ .flags = IRQF_TIMER | IRQF_PERCPU | IRQF_IRQPOLL,
};
#ifdef CONFIG_SMP
static struct irqaction ipi_action = {
.handler = ipi_interrupt,
.name = "IPI",
+ .flags = IRQF_PERCPU,
};
#endif
@@ -304,14 +571,15 @@ static void claim_cpu_irqs(void)
{
int i;
for (i = CPU_IRQ_BASE; i <= CPU_IRQ_MAX; i++) {
- irq_desc[i].handler = &cpu_interrupt_type;
+ irq_set_chip_and_handler(i, &cpu_interrupt_type,
+ handle_percpu_irq);
}
- irq_desc[TIMER_IRQ].action = &timer_action;
- irq_desc[TIMER_IRQ].status |= IRQ_PER_CPU;
+ irq_set_handler(TIMER_IRQ, handle_percpu_irq);
+ setup_irq(TIMER_IRQ, &timer_action);
#ifdef CONFIG_SMP
- irq_desc[IPI_IRQ].action = &ipi_action;
- irq_desc[IPI_IRQ].status = IRQ_PER_CPU;
+ irq_set_handler(IPI_IRQ, handle_percpu_irq);
+ setup_irq(IPI_IRQ, &ipi_action);
#endif
}
@@ -319,25 +587,14 @@ void __init init_IRQ(void)
{
local_irq_disable(); /* PARANOID - should already be disabled */
mtctl(~0UL, 23); /* EIRR : clear all pending external intr */
- claim_cpu_irqs();
#ifdef CONFIG_SMP
- if (!cpu_eiem)
+ if (!cpu_eiem) {
+ claim_cpu_irqs();
cpu_eiem = EIEM_MASK(IPI_IRQ) | EIEM_MASK(TIMER_IRQ);
+ }
#else
+ claim_cpu_irqs();
cpu_eiem = EIEM_MASK(TIMER_IRQ);
#endif
set_eiem(cpu_eiem); /* EIEM : enable all external intr */
-
-}
-
-void hw_resend_irq(struct hw_interrupt_type *type, unsigned int irq)
-{
- /* XXX: Needs to be written. We managed without it so far, but
- * we really ought to write it.
- */
-}
-
-void ack_bad_irq(unsigned int irq)
-{
- printk("unexpected IRQ %d\n", irq);
}
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c
index f27cfe4771b..50dfafc3f2c 100644
--- a/arch/parisc/kernel/module.c
+++ b/arch/parisc/kernel/module.c
@@ -6,6 +6,7 @@
*
* Linux/PA-RISC Project (http://www.parisc-linux.org/)
* Copyright (C) 2003 Randolph Chung <tausq at debian . org>
+ * Copyright (C) 2008 Helge Deller <deller@gmx.de>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -24,10 +25,23 @@
*
*
* Notes:
+ * - PLT stub handling
+ * On 32bit (and sometimes 64bit) and with big kernel modules like xfs or
+ * ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may
+ * fail to reach their PLT stub if we only create one big stub array for
+ * all sections at the beginning of the core or init section.
+ * Instead we now insert individual PLT stub entries directly in front of
+ * of the code sections where the stubs are actually called.
+ * This reduces the distance between the PCREL location and the stub entry
+ * so that the relocations can be fulfilled.
+ * While calculating the final layout of the kernel module in memory, the
+ * kernel module loader calls arch_mod_section_prepend() to request the
+ * to be reserved amount of memory in front of each individual section.
+ *
* - SEGREL32 handling
* We are not doing SEGREL32 handling correctly. According to the ABI, we
* should do a value offset, like this:
- * if (is_init(me, (void *)val))
+ * if (in_init(me, (void *)val))
* val -= (uint32_t)me->module_init;
* else
* val -= (uint32_t)me->module_core;
@@ -46,7 +60,11 @@
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/pgtable.h>
#include <asm/unwind.h>
#if 0
@@ -55,9 +73,13 @@
#define DEBUGP(fmt...)
#endif
+#define RELOC_REACHABLE(val, bits) \
+ (( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \
+ ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) ? \
+ 0 : 1)
+
#define CHECK_RELOC(val, bits) \
- if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \
- ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \
+ if (!RELOC_REACHABLE(val, bits)) { \
printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \
me->name, strtab + sym->st_name, (unsigned long)val, bits); \
return -ENOEXEC; \
@@ -67,36 +89,37 @@
* the bottom of the table, which has a maximum signed displacement of
* 0x3fff; however, since we're only going forward, this becomes
* 0x1fff, and thus, since each GOT entry is 8 bytes long we can have
- * at most 1023 entries */
-#define MAX_GOTS 1023
+ * at most 1023 entries.
+ * To overcome this 14bit displacement with some kernel modules, we'll
+ * use instead the unusal 16bit displacement method (see reassemble_16a)
+ * which gives us a maximum positive displacement of 0x7fff, and as such
+ * allows us to allocate up to 4095 GOT entries. */
+#define MAX_GOTS 4095
/* three functions to determine where in the module core
* or init pieces the location is */
-static inline int is_init(struct module *me, void *loc)
+static inline int in_init(struct module *me, void *loc)
{
return (loc >= me->module_init &&
loc <= (me->module_init + me->init_size));
}
-static inline int is_core(struct module *me, void *loc)
+static inline int in_core(struct module *me, void *loc)
{
return (loc >= me->module_core &&
loc <= (me->module_core + me->core_size));
}
-static inline int is_local(struct module *me, void *loc)
+static inline int in_local(struct module *me, void *loc)
{
- return is_init(me, loc) || is_core(me, loc);
+ return in_init(me, loc) || in_core(me, loc);
}
-
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
struct got_entry {
Elf32_Addr addr;
};
-#define Elf_Fdesc Elf32_Fdesc
-
struct stub_entry {
Elf32_Word insns[2]; /* each stub entry has two insns */
};
@@ -105,8 +128,6 @@ struct got_entry {
Elf64_Addr addr;
};
-#define Elf_Fdesc Elf64_Fdesc
-
struct stub_entry {
Elf64_Word insns[4]; /* each stub entry has four insns */
};
@@ -131,12 +152,40 @@ struct stub_entry {
/* The reassemble_* functions prepare an immediate value for
insertion into an opcode. pa-risc uses all sorts of weird bitfields
in the instruction to hold the value. */
+static inline int sign_unext(int x, int len)
+{
+ int len_ones;
+
+ len_ones = (1 << len) - 1;
+ return x & len_ones;
+}
+
+static inline int low_sign_unext(int x, int len)
+{
+ int sign, temp;
+
+ sign = (x >> (len-1)) & 1;
+ temp = sign_unext(x, len-1);
+ return (temp << 1) | sign;
+}
+
static inline int reassemble_14(int as14)
{
return (((as14 & 0x1fff) << 1) |
((as14 & 0x2000) >> 13));
}
+static inline int reassemble_16a(int as16)
+{
+ int s, t;
+
+ /* Unusual 16-bit encoding, for wide mode only. */
+ t = (as16 << 1) & 0xffff;
+ s = (as16 & 0x8000);
+ return (t ^ s ^ (s >> 1)) | (s >> 15);
+}
+
+
static inline int reassemble_17(int as17)
{
return (((as17 & 0x10000) >> 16) |
@@ -165,12 +214,16 @@ static inline int reassemble_22(int as22)
void *module_alloc(unsigned long size)
{
- if (size == 0)
- return NULL;
- return vmalloc(size);
+ /* using RWX means less protection for modules, but it's
+ * easier than trying to map the text, data, init_text and
+ * init_data correctly */
+ return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END,
+ GFP_KERNEL | __GFP_HIGHMEM,
+ PAGE_KERNEL_RWX, NUMA_NO_NODE,
+ __builtin_return_address(0));
}
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
static inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n)
{
return 0;
@@ -249,9 +302,20 @@ static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n)
/* Free memory returned from module_alloc */
void module_free(struct module *mod, void *module_region)
{
+ kfree(mod->arch.section);
+ mod->arch.section = NULL;
+
vfree(module_region);
- /* FIXME: If module_region == mod->init_region, trim exception
- table entries. */
+}
+
+/* Additional bytes needed in front of individual sections */
+unsigned int arch_mod_section_prepend(struct module *mod,
+ unsigned int section)
+{
+ /* size needed for all stubs of this section (including
+ * one additional for correct alignment of the stubs) */
+ return (mod->arch.section[section].stub_entries + 1)
+ * sizeof(struct stub_entry);
}
#define CONST
@@ -260,12 +324,18 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
CONST char *secstrings,
struct module *me)
{
- unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0;
+ unsigned long gots = 0, fdescs = 0, len;
unsigned int i;
+ len = hdr->e_shnum * sizeof(me->arch.section[0]);
+ me->arch.section = kzalloc(len, GFP_KERNEL);
+ if (!me->arch.section)
+ return -ENOMEM;
+
for (i = 1; i < hdr->e_shnum; i++) {
- const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset;
+ const Elf_Rela *rels = (void *)sechdrs[i].sh_addr;
unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels);
+ unsigned int count, s;
if (strncmp(secstrings + sechdrs[i].sh_name,
".PARISC.unwind", 14) == 0)
@@ -281,11 +351,23 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
*/
gots += count_gots(rels, nrels);
fdescs += count_fdescs(rels, nrels);
- if(strncmp(secstrings + sechdrs[i].sh_name,
- ".rela.init", 10) == 0)
- init_stubs += count_stubs(rels, nrels);
- else
- stubs += count_stubs(rels, nrels);
+
+ /* XXX: By sorting the relocs and finding duplicate entries
+ * we could reduce the number of necessary stubs and save
+ * some memory. */
+ count = count_stubs(rels, nrels);
+ if (!count)
+ continue;
+
+ /* so we need relocation stubs. reserve necessary memory. */
+ /* sh_info gives the section for which we need to add stubs. */
+ s = sechdrs[i].sh_info;
+
+ /* each code section should only have one relocation section */
+ WARN_ON(me->arch.section[s].stub_entries);
+
+ /* store number of stubs we need for this section */
+ me->arch.section[s].stub_entries += count;
}
/* align things a bit */
@@ -297,23 +379,13 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
me->arch.fdesc_offset = me->core_size;
me->core_size += fdescs * sizeof(Elf_Fdesc);
- me->core_size = ALIGN(me->core_size, 16);
- me->arch.stub_offset = me->core_size;
- me->core_size += stubs * sizeof(struct stub_entry);
-
- me->init_size = ALIGN(me->init_size, 16);
- me->arch.init_stub_offset = me->init_size;
- me->init_size += init_stubs * sizeof(struct stub_entry);
-
me->arch.got_max = gots;
me->arch.fdesc_max = fdescs;
- me->arch.stub_max = stubs;
- me->arch.init_stub_max = init_stubs;
return 0;
}
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
{
unsigned int i;
@@ -336,9 +408,9 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
value);
return i * sizeof(struct got_entry);
}
-#endif /* __LP64__ */
+#endif /* CONFIG_64BIT */
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
static Elf_Addr get_fdesc(struct module *me, unsigned long value)
{
Elf_Fdesc *fdesc = me->module_core + me->arch.fdesc_offset;
@@ -362,27 +434,38 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value)
fdesc->gp = (Elf_Addr)me->module_core + me->arch.got_offset;
return (Elf_Addr)fdesc;
}
-#endif /* __LP64__ */
+#endif /* CONFIG_64BIT */
+
+enum elf_stub_type {
+ ELF_STUB_GOT,
+ ELF_STUB_MILLI,
+ ELF_STUB_DIRECT,
+};
static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
- int millicode, int init_section)
+ enum elf_stub_type stub_type, Elf_Addr loc0, unsigned int targetsec)
{
- unsigned long i;
struct stub_entry *stub;
-
- if(init_section) {
- i = me->arch.init_stub_count++;
- BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max);
- stub = me->module_init + me->arch.init_stub_offset +
- i * sizeof(struct stub_entry);
- } else {
- i = me->arch.stub_count++;
- BUG_ON(me->arch.stub_count > me->arch.stub_max);
- stub = me->module_core + me->arch.stub_offset +
- i * sizeof(struct stub_entry);
+ int __maybe_unused d;
+
+ /* initialize stub_offset to point in front of the section */
+ if (!me->arch.section[targetsec].stub_offset) {
+ loc0 -= (me->arch.section[targetsec].stub_entries + 1) *
+ sizeof(struct stub_entry);
+ /* get correct alignment for the stubs */
+ loc0 = ALIGN(loc0, sizeof(struct stub_entry));
+ me->arch.section[targetsec].stub_offset = loc0;
}
-#ifndef __LP64__
+ /* get address of stub entry */
+ stub = (void *) me->arch.section[targetsec].stub_offset;
+ me->arch.section[targetsec].stub_offset += sizeof(struct stub_entry);
+
+ /* do not write outside available stub area */
+ BUG_ON(0 == me->arch.section[targetsec].stub_entries--);
+
+
+#ifndef CONFIG_64BIT
/* for 32-bit the stub looks like this:
* ldil L'XXX,%r1
* be,n R'XXX(%sr4,%r1)
@@ -396,7 +479,7 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4);
#else
-/* for 64-bit we have two kinds of stubs:
+/* for 64-bit we have three kinds of stubs:
* for normal function calls:
* ldd 0(%dp),%dp
* ldd 10(%dp), %r1
@@ -408,18 +491,30 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
* ldo 0(%r1), %r1
* ldd 10(%r1), %r1
* bve,n (%r1)
+ *
+ * for direct branches (jumps between different section of the
+ * same module):
+ * ldil 0, %r1
+ * ldo 0(%r1), %r1
+ * bve,n (%r1)
*/
- if (!millicode)
- {
- stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */
+ switch (stub_type) {
+ case ELF_STUB_GOT:
+ d = get_got(me, value, addend);
+ if (d <= 15) {
+ /* Format 5 */
+ stub->insns[0] = 0x0f6010db; /* ldd 0(%dp),%dp */
+ stub->insns[0] |= low_sign_unext(d, 5) << 16;
+ } else {
+ /* Format 3 */
+ stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */
+ stub->insns[0] |= reassemble_16a(d);
+ }
stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */
stub->insns[2] = 0xe820d000; /* bve (%r1) */
stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */
-
- stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff);
- }
- else
- {
+ break;
+ case ELF_STUB_MILLI:
stub->insns[0] = 0x20200000; /* ldil 0,%r1 */
stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */
stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */
@@ -427,25 +522,23 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
stub->insns[0] |= reassemble_21(lrsel(value, addend));
stub->insns[1] |= reassemble_14(rrsel(value, addend));
+ break;
+ case ELF_STUB_DIRECT:
+ stub->insns[0] = 0x20200000; /* ldil 0,%r1 */
+ stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */
+ stub->insns[2] = 0xe820d002; /* bve,n (%r1) */
+
+ stub->insns[0] |= reassemble_21(lrsel(value, addend));
+ stub->insns[1] |= reassemble_14(rrsel(value, addend));
+ break;
}
+
#endif
return (Elf_Addr)stub;
}
-int apply_relocate(Elf_Shdr *sechdrs,
- const char *strtab,
- unsigned int symindex,
- unsigned int relsec,
- struct module *me)
-{
- /* parisc should not need this ... */
- printk(KERN_ERR "module %s: RELOCATION unsupported\n",
- me->name);
- return -ENOEXEC;
-}
-
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
int apply_relocate_add(Elf_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
@@ -459,15 +552,19 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
Elf32_Addr val;
Elf32_Sword addend;
Elf32_Addr dot;
+ Elf_Addr loc0;
+ unsigned int targetsec = sechdrs[relsec].sh_info;
//unsigned long dp = (unsigned long)$global$;
register unsigned long dp asm ("r27");
DEBUGP("Applying relocate section %u to %u\n", relsec,
- sechdrs[relsec].sh_info);
+ targetsec);
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
/* This is where to make the change */
- loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ loc = (void *)sechdrs[targetsec].sh_addr
+ rel[i].r_offset;
+ /* This is the start of the target section */
+ loc0 = sechdrs[targetsec].sh_addr;
/* This is the symbol it is referring to */
sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ ELF32_R_SYM(rel[i].r_info);
@@ -539,19 +636,32 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
break;
case R_PARISC_PCREL17F:
/* 17-bit PC relative address */
- val = get_stub(me, val, addend, 0, is_init(me, loc));
+ /* calculate direct call offset */
+ val += addend;
val = (val - dot - 8)/4;
- CHECK_RELOC(val, 17)
+ if (!RELOC_REACHABLE(val, 17)) {
+ /* direct distance too far, create
+ * stub entry instead */
+ val = get_stub(me, sym->st_value, addend,
+ ELF_STUB_DIRECT, loc0, targetsec);
+ val = (val - dot - 8)/4;
+ CHECK_RELOC(val, 17);
+ }
*loc = (*loc & ~0x1f1ffd) | reassemble_17(val);
break;
case R_PARISC_PCREL22F:
/* 22-bit PC relative address; only defined for pa20 */
- val = get_stub(me, val, addend, 0, is_init(me, loc));
- DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n",
- strtab + sym->st_name, (unsigned long)loc, addend,
- val)
+ /* calculate direct call offset */
+ val += addend;
val = (val - dot - 8)/4;
- CHECK_RELOC(val, 22);
+ if (!RELOC_REACHABLE(val, 22)) {
+ /* direct distance too far, create
+ * stub entry instead */
+ val = get_stub(me, sym->st_value, addend,
+ ELF_STUB_DIRECT, loc0, targetsec);
+ val = (val - dot - 8)/4;
+ CHECK_RELOC(val, 22);
+ }
*loc = (*loc & ~0x3ff1ffd) | reassemble_22(val);
break;
@@ -580,13 +690,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
Elf64_Addr val;
Elf64_Sxword addend;
Elf64_Addr dot;
+ Elf_Addr loc0;
+ unsigned int targetsec = sechdrs[relsec].sh_info;
DEBUGP("Applying relocate section %u to %u\n", relsec,
- sechdrs[relsec].sh_info);
+ targetsec);
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
/* This is where to make the change */
- loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ loc = (void *)sechdrs[targetsec].sh_addr
+ rel[i].r_offset;
+ /* This is the start of the target section */
+ loc0 = sechdrs[targetsec].sh_addr;
/* This is the symbol it is referring to */
sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
+ ELF64_R_SYM(rel[i].r_info);
@@ -642,32 +756,40 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
DEBUGP("PCREL22F Symbol %s loc %p val %lx\n",
strtab + sym->st_name,
loc, val);
+ val += addend;
/* can we reach it locally? */
- if(!is_local(me, (void *)val)) {
+ if (in_local(me, (void *)val)) {
+ /* this is the case where the symbol is local
+ * to the module, but in a different section,
+ * so stub the jump in case it's more than 22
+ * bits away */
+ val = (val - dot - 8)/4;
+ if (!RELOC_REACHABLE(val, 22)) {
+ /* direct distance too far, create
+ * stub entry instead */
+ val = get_stub(me, sym->st_value,
+ addend, ELF_STUB_DIRECT,
+ loc0, targetsec);
+ } else {
+ /* Ok, we can reach it directly. */
+ val = sym->st_value;
+ val += addend;
+ }
+ } else {
+ val = sym->st_value;
if (strncmp(strtab + sym->st_name, "$$", 2)
== 0)
- val = get_stub(me, val, addend, 1,
- is_init(me, loc));
+ val = get_stub(me, val, addend, ELF_STUB_MILLI,
+ loc0, targetsec);
else
- val = get_stub(me, val, addend, 0,
- is_init(me, loc));
+ val = get_stub(me, val, addend, ELF_STUB_GOT,
+ loc0, targetsec);
}
DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n",
strtab + sym->st_name, loc, sym->st_value,
addend, val);
- /* FIXME: local symbols work as long as the
- * core and init pieces aren't separated too
- * far. If this is ever broken, you will trip
- * the check below. The way to fix it would
- * be to generate local stubs to go between init
- * and core */
- if((Elf64_Sxword)(val - dot - 8) > 0x800000 -1 ||
- (Elf64_Sxword)(val - dot - 8) < -0x800000) {
- printk(KERN_ERR "Module %s, symbol %s is out of range for PCREL22F relocation\n",
- me->name, strtab + sym->st_name);
- return -ENOEXEC;
- }
val = (val - dot - 8)/4;
+ CHECK_RELOC(val, 22);
*loc = (*loc & ~0x3ff1ffd) | reassemble_22(val);
break;
case R_PARISC_DIR64:
@@ -683,7 +805,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
break;
case R_PARISC_FPTR64:
/* 64-bit function address */
- if(is_local(me, (void *)(val + addend))) {
+ if(in_local(me, (void *)(val + addend))) {
*loc64 = get_fdesc(me, val+addend);
DEBUGP("FDESC for %s at %p points to %lx\n",
strtab + sym->st_name, *loc64,
@@ -754,12 +876,8 @@ int module_finalize(const Elf_Ehdr *hdr,
addr = (u32 *)entry->addr;
printk("INSNS: %x %x %x %x\n",
addr[0], addr[1], addr[2], addr[3]);
- printk("stubs used %ld, stubs max %ld\n"
- "init_stubs used %ld, init stubs max %ld\n"
- "got entries used %ld, gots max %ld\n"
+ printk("got entries used %ld, gots max %ld\n"
"fdescs used %ld, fdescs max %ld\n",
- me->arch.stub_count, me->arch.stub_max,
- me->arch.init_stub_count, me->arch.init_stub_max,
me->arch.got_count, me->arch.got_max,
me->arch.fdesc_count, me->arch.fdesc_max);
#endif
@@ -770,7 +888,7 @@ int module_finalize(const Elf_Ehdr *hdr,
* ourselves */
for (i = 1; i < hdr->e_shnum; i++) {
if(sechdrs[i].sh_type == SHT_SYMTAB
- && (sechdrs[i].sh_type & SHF_ALLOC)) {
+ && (sechdrs[i].sh_flags & SHF_ALLOC)) {
int strindex = sechdrs[i].sh_link;
/* FIXME: AWFUL HACK
* The cast is to drop the const from
@@ -785,10 +903,14 @@ int module_finalize(const Elf_Ehdr *hdr,
me->name, strtab, symhdr);
if(me->arch.got_count > MAX_GOTS) {
- printk(KERN_ERR "%s: Global Offset Table overflow (used %ld, allowed %d\n", me->name, me->arch.got_count, MAX_GOTS);
+ printk(KERN_ERR "%s: Global Offset Table overflow (used %ld, allowed %d)\n",
+ me->name, me->arch.got_count, MAX_GOTS);
return -EINVAL;
}
-
+
+ kfree(me->arch.section);
+ me->arch.section = NULL;
+
/* no symbol table */
if(symhdr == NULL)
return 0;
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index 9534ee17b9b..b743a80eaba 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -27,32 +27,21 @@
*/
#ifdef CONFIG_64BIT
-#define ADDIB addib,*
-#define CMPB cmpb,*
-#define ANDCM andcm,*
-
.level 2.0w
#else
-#define ADDIB addib,
-#define CMPB cmpb,
-#define ANDCM andcm
-
.level 2.0
#endif
-#include <linux/config.h>
-
#include <asm/psw.h>
#include <asm/assembly.h>
#include <asm/pgtable.h>
#include <asm/cache.h>
+#include <linux/linkage.h>
.text
.align 128
- .export flush_tlb_all_local,code
-
-flush_tlb_all_local:
+ENTRY(flush_tlb_all_local)
.proc
.callinfo NO_CALLS
.entry
@@ -65,7 +54,7 @@ flush_tlb_all_local:
*/
/* pcxt_ssm_bug - relied upon translation! PA 2.0 Arch. F-4 and F-5 */
- rsm PSW_SM_I, %r19 /* save I-bit state */
+ rsm PSW_SM_I, %r19 /* save I-bit state */
load32 PA(1f), %r1
nop
nop
@@ -84,8 +73,7 @@ flush_tlb_all_local:
rfi
nop
-1: ldil L%PA(cache_info), %r1
- ldo R%PA(cache_info)(%r1), %r1
+1: load32 PA(cache_info), %r1
/* Flush Instruction Tlb */
@@ -97,7 +85,7 @@ flush_tlb_all_local:
LDREG ITLB_OFF_COUNT(%r1), %arg2
LDREG ITLB_LOOP(%r1), %arg3
- ADDIB= -1, %arg3, fitoneloop /* Preadjust and test */
+ addib,COND(=) -1, %arg3, fitoneloop /* Preadjust and test */
movb,<,n %arg3, %r31, fitdone /* If loop < 0, skip */
copy %arg0, %r28 /* Init base addr */
@@ -107,14 +95,14 @@ fitmanyloop: /* Loop if LOOP >= 2 */
copy %arg2, %r29 /* Init middle loop count */
fitmanymiddle: /* Loop if LOOP >= 2 */
- ADDIB> -1, %r31, fitmanymiddle /* Adjusted inner loop decr */
+ addib,COND(>) -1, %r31, fitmanymiddle /* Adjusted inner loop decr */
pitlbe 0(%sr1, %r28)
pitlbe,m %arg1(%sr1, %r28) /* Last pitlbe and addr adjust */
- ADDIB> -1, %r29, fitmanymiddle /* Middle loop decr */
+ addib,COND(>) -1, %r29, fitmanymiddle /* Middle loop decr */
copy %arg3, %r31 /* Re-init inner loop count */
movb,tr %arg0, %r28, fitmanyloop /* Re-init base addr */
- ADDIB<=,n -1, %r22, fitdone /* Outer loop count decr */
+ addib,COND(<=),n -1, %r22, fitdone /* Outer loop count decr */
fitoneloop: /* Loop if LOOP = 1 */
mtsp %r20, %sr1
@@ -122,10 +110,10 @@ fitoneloop: /* Loop if LOOP = 1 */
copy %arg2, %r29 /* init middle loop count */
fitonemiddle: /* Loop if LOOP = 1 */
- ADDIB> -1, %r29, fitonemiddle /* Middle loop count decr */
+ addib,COND(>) -1, %r29, fitonemiddle /* Middle loop count decr */
pitlbe,m %arg1(%sr1, %r28) /* pitlbe for one loop */
- ADDIB> -1, %r22, fitoneloop /* Outer loop count decr */
+ addib,COND(>) -1, %r22, fitoneloop /* Outer loop count decr */
add %r21, %r20, %r20 /* increment space */
fitdone:
@@ -140,7 +128,7 @@ fitdone:
LDREG DTLB_OFF_COUNT(%r1), %arg2
LDREG DTLB_LOOP(%r1), %arg3
- ADDIB= -1, %arg3, fdtoneloop /* Preadjust and test */
+ addib,COND(=) -1, %arg3, fdtoneloop /* Preadjust and test */
movb,<,n %arg3, %r31, fdtdone /* If loop < 0, skip */
copy %arg0, %r28 /* Init base addr */
@@ -150,14 +138,14 @@ fdtmanyloop: /* Loop if LOOP >= 2 */
copy %arg2, %r29 /* Init middle loop count */
fdtmanymiddle: /* Loop if LOOP >= 2 */
- ADDIB> -1, %r31, fdtmanymiddle /* Adjusted inner loop decr */
+ addib,COND(>) -1, %r31, fdtmanymiddle /* Adjusted inner loop decr */
pdtlbe 0(%sr1, %r28)
pdtlbe,m %arg1(%sr1, %r28) /* Last pdtlbe and addr adjust */
- ADDIB> -1, %r29, fdtmanymiddle /* Middle loop decr */
+ addib,COND(>) -1, %r29, fdtmanymiddle /* Middle loop decr */
copy %arg3, %r31 /* Re-init inner loop count */
movb,tr %arg0, %r28, fdtmanyloop /* Re-init base addr */
- ADDIB<=,n -1, %r22,fdtdone /* Outer loop count decr */
+ addib,COND(<=),n -1, %r22,fdtdone /* Outer loop count decr */
fdtoneloop: /* Loop if LOOP = 1 */
mtsp %r20, %sr1
@@ -165,10 +153,10 @@ fdtoneloop: /* Loop if LOOP = 1 */
copy %arg2, %r29 /* init middle loop count */
fdtonemiddle: /* Loop if LOOP = 1 */
- ADDIB> -1, %r29, fdtonemiddle /* Middle loop count decr */
+ addib,COND(>) -1, %r29, fdtonemiddle /* Middle loop count decr */
pdtlbe,m %arg1(%sr1, %r28) /* pdtlbe for one loop */
- ADDIB> -1, %r22, fdtoneloop /* Outer loop count decr */
+ addib,COND(>) -1, %r22, fdtoneloop /* Outer loop count decr */
add %r21, %r20, %r20 /* increment space */
@@ -202,18 +190,16 @@ fdtdone:
.exit
.procend
+ENDPROC(flush_tlb_all_local)
- .export flush_instruction_cache_local,code
.import cache_info,data
-flush_instruction_cache_local:
+ENTRY(flush_instruction_cache_local)
.proc
.callinfo NO_CALLS
.entry
- mtsp %r0, %sr1
- ldil L%cache_info, %r1
- ldo R%cache_info(%r1), %r1
+ load32 cache_info, %r1
/* Flush Instruction Cache */
@@ -221,19 +207,46 @@ flush_instruction_cache_local:
LDREG ICACHE_STRIDE(%r1), %arg1
LDREG ICACHE_COUNT(%r1), %arg2
LDREG ICACHE_LOOP(%r1), %arg3
- rsm PSW_SM_I, %r22 /* No mmgt ops during loop*/
- ADDIB= -1, %arg3, fioneloop /* Preadjust and test */
+ rsm PSW_SM_I, %r22 /* No mmgt ops during loop*/
+ mtsp %r0, %sr1
+ addib,COND(=) -1, %arg3, fioneloop /* Preadjust and test */
movb,<,n %arg3, %r31, fisync /* If loop < 0, do sync */
fimanyloop: /* Loop if LOOP >= 2 */
- ADDIB> -1, %r31, fimanyloop /* Adjusted inner loop decr */
+ addib,COND(>) -1, %r31, fimanyloop /* Adjusted inner loop decr */
fice %r0(%sr1, %arg0)
fice,m %arg1(%sr1, %arg0) /* Last fice and addr adjust */
movb,tr %arg3, %r31, fimanyloop /* Re-init inner loop count */
- ADDIB<=,n -1, %arg2, fisync /* Outer loop decr */
+ addib,COND(<=),n -1, %arg2, fisync /* Outer loop decr */
fioneloop: /* Loop if LOOP = 1 */
- ADDIB> -1, %arg2, fioneloop /* Outer loop count decr */
+ /* Some implementations may flush with a single fice instruction */
+ cmpib,COND(>>=),n 15, %arg2, fioneloop2
+
+fioneloop1:
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ fice,m %arg1(%sr1, %arg0)
+ addib,COND(>) -16, %arg2, fioneloop1
+ fice,m %arg1(%sr1, %arg0)
+
+ /* Check if done */
+ cmpb,COND(=),n %arg2, %r0, fisync /* Predict branch taken */
+
+fioneloop2:
+ addib,COND(>) -1, %arg2, fioneloop2 /* Outer loop count decr */
fice,m %arg1(%sr1, %arg0) /* Fice for one loop */
fisync:
@@ -244,18 +257,16 @@ fisync:
.exit
.procend
+ENDPROC(flush_instruction_cache_local)
- .export flush_data_cache_local, code
- .import cache_info, data
-flush_data_cache_local:
+ .import cache_info, data
+ENTRY(flush_data_cache_local)
.proc
.callinfo NO_CALLS
.entry
- mtsp %r0, %sr1
- ldil L%cache_info, %r1
- ldo R%cache_info(%r1), %r1
+ load32 cache_info, %r1
/* Flush Data Cache */
@@ -263,19 +274,46 @@ flush_data_cache_local:
LDREG DCACHE_STRIDE(%r1), %arg1
LDREG DCACHE_COUNT(%r1), %arg2
LDREG DCACHE_LOOP(%r1), %arg3
- rsm PSW_SM_I, %r22
- ADDIB= -1, %arg3, fdoneloop /* Preadjust and test */
+ rsm PSW_SM_I, %r22 /* No mmgt ops during loop*/
+ mtsp %r0, %sr1
+ addib,COND(=) -1, %arg3, fdoneloop /* Preadjust and test */
movb,<,n %arg3, %r31, fdsync /* If loop < 0, do sync */
fdmanyloop: /* Loop if LOOP >= 2 */
- ADDIB> -1, %r31, fdmanyloop /* Adjusted inner loop decr */
+ addib,COND(>) -1, %r31, fdmanyloop /* Adjusted inner loop decr */
fdce %r0(%sr1, %arg0)
fdce,m %arg1(%sr1, %arg0) /* Last fdce and addr adjust */
movb,tr %arg3, %r31, fdmanyloop /* Re-init inner loop count */
- ADDIB<=,n -1, %arg2, fdsync /* Outer loop decr */
+ addib,COND(<=),n -1, %arg2, fdsync /* Outer loop decr */
fdoneloop: /* Loop if LOOP = 1 */
- ADDIB> -1, %arg2, fdoneloop /* Outer loop count decr */
+ /* Some implementations may flush with a single fdce instruction */
+ cmpib,COND(>>=),n 15, %arg2, fdoneloop2
+
+fdoneloop1:
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ fdce,m %arg1(%sr1, %arg0)
+ addib,COND(>) -16, %arg2, fdoneloop1
+ fdce,m %arg1(%sr1, %arg0)
+
+ /* Check if done */
+ cmpb,COND(=),n %arg2, %r0, fdsync /* Predict branch taken */
+
+fdoneloop2:
+ addib,COND(>) -1, %arg2, fdoneloop2 /* Outer loop count decr */
fdce,m %arg1(%sr1, %arg0) /* Fdce for one loop */
fdsync:
@@ -287,11 +325,108 @@ fdsync:
.exit
.procend
+ENDPROC(flush_data_cache_local)
- .export copy_user_page_asm,code
.align 16
-copy_user_page_asm:
+/* Macros to serialize TLB purge operations on SMP. */
+
+ .macro tlb_lock la,flags,tmp
+#ifdef CONFIG_SMP
+ ldil L%pa_tlb_lock,%r1
+ ldo R%pa_tlb_lock(%r1),\la
+ rsm PSW_SM_I,\flags
+1: LDCW 0(\la),\tmp
+ cmpib,<>,n 0,\tmp,3f
+2: ldw 0(\la),\tmp
+ cmpb,<> %r0,\tmp,1b
+ nop
+ b,n 2b
+3:
+#endif
+ .endm
+
+ .macro tlb_unlock la,flags,tmp
+#ifdef CONFIG_SMP
+ ldi 1,\tmp
+ stw \tmp,0(\la)
+ mtsm \flags
+#endif
+ .endm
+
+/* Clear page using kernel mapping. */
+
+ENTRY(clear_page_asm)
+ .proc
+ .callinfo NO_CALLS
+ .entry
+
+#ifdef CONFIG_64BIT
+
+ /* Unroll the loop. */
+ ldi (PAGE_SIZE / 128), %r1
+
+1:
+ std %r0, 0(%r26)
+ std %r0, 8(%r26)
+ std %r0, 16(%r26)
+ std %r0, 24(%r26)
+ std %r0, 32(%r26)
+ std %r0, 40(%r26)
+ std %r0, 48(%r26)
+ std %r0, 56(%r26)
+ std %r0, 64(%r26)
+ std %r0, 72(%r26)
+ std %r0, 80(%r26)
+ std %r0, 88(%r26)
+ std %r0, 96(%r26)
+ std %r0, 104(%r26)
+ std %r0, 112(%r26)
+ std %r0, 120(%r26)
+
+ /* Note reverse branch hint for addib is taken. */
+ addib,COND(>),n -1, %r1, 1b
+ ldo 128(%r26), %r26
+
+#else
+
+ /*
+ * Note that until (if) we start saving the full 64-bit register
+ * values on interrupt, we can't use std on a 32 bit kernel.
+ */
+ ldi (PAGE_SIZE / 64), %r1
+
+1:
+ stw %r0, 0(%r26)
+ stw %r0, 4(%r26)
+ stw %r0, 8(%r26)
+ stw %r0, 12(%r26)
+ stw %r0, 16(%r26)
+ stw %r0, 20(%r26)
+ stw %r0, 24(%r26)
+ stw %r0, 28(%r26)
+ stw %r0, 32(%r26)
+ stw %r0, 36(%r26)
+ stw %r0, 40(%r26)
+ stw %r0, 44(%r26)
+ stw %r0, 48(%r26)
+ stw %r0, 52(%r26)
+ stw %r0, 56(%r26)
+ stw %r0, 60(%r26)
+
+ addib,COND(>),n -1, %r1, 1b
+ ldo 64(%r26), %r26
+#endif
+ bv %r0(%r2)
+ nop
+ .exit
+
+ .procend
+ENDPROC(clear_page_asm)
+
+/* Copy page using kernel mapping. */
+
+ENTRY(copy_page_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -299,17 +434,14 @@ copy_user_page_asm:
#ifdef CONFIG_64BIT
/* PA8x00 CPUs can consume 2 loads or 1 store per cycle.
* Unroll the loop by hand and arrange insn appropriately.
- * GCC probably can do this just as well.
+ * Prefetch doesn't improve performance on rp3440.
+ * GCC probably can do this just as well...
*/
- ldd 0(%r25), %r19
- ldi 32, %r1 /* PAGE_SIZE/128 == 32 */
- ldw 64(%r25), %r0 /* prefetch 1 cacheline ahead */
- ldw 128(%r25), %r0 /* prefetch 2 */
+ ldi (PAGE_SIZE / 128), %r1
-1: ldd 8(%r25), %r20
- ldw 192(%r25), %r0 /* prefetch 3 */
- ldw 256(%r25), %r0 /* prefetch 4 */
+1: ldd 0(%r25), %r19
+ ldd 8(%r25), %r20
ldd 16(%r25), %r21
ldd 24(%r25), %r22
@@ -343,20 +475,16 @@ copy_user_page_asm:
ldd 112(%r25), %r21
ldd 120(%r25), %r22
+ ldo 128(%r25), %r25
std %r19, 96(%r26)
std %r20, 104(%r26)
- ldo 128(%r25), %r25
std %r21, 112(%r26)
std %r22, 120(%r26)
- ldo 128(%r26), %r26
- /* conditional branches nullify on forward taken branch, and on
- * non-taken backward branch. Note that .+4 is a backwards branch.
- * The ldd should only get executed if the branch is taken.
- */
- ADDIB>,n -1, %r1, 1b /* bundle 10 */
- ldd 0(%r25), %r19 /* start next loads */
+ /* Note reverse branch hint for addib is taken. */
+ addib,COND(>),n -1, %r1, 1b
+ ldo 128(%r26), %r26
#else
@@ -368,7 +496,7 @@ copy_user_page_asm:
* use ldd/std on a 32 bit kernel.
*/
ldw 0(%r25), %r19
- ldi 64, %r1 /* PAGE_SIZE/64 == 64 */
+ ldi (PAGE_SIZE / 64), %r1
1:
ldw 4(%r25), %r20
@@ -404,7 +532,7 @@ copy_user_page_asm:
stw %r21, 56(%r26)
stw %r22, 60(%r26)
ldo 64(%r26), %r26
- ADDIB>,n -1, %r1, 1b
+ addib,COND(>),n -1, %r1, 1b
ldw 0(%r25), %r19
#endif
bv %r0(%r2)
@@ -412,6 +540,7 @@ copy_user_page_asm:
.exit
.procend
+ENDPROC(copy_page_asm)
/*
* NOTE: Code in clear_user_page has a hard coded dependency on the
@@ -434,7 +563,14 @@ copy_user_page_asm:
* %r23 physical page (shifted for tlb insert) of "from" translation
*/
-#if 0
+ /* Drop prot bits and convert to page addr for iitlbt and idtlbt */
+ #define PAGE_ADD_SHIFT (PAGE_SHIFT-12)
+ .macro convert_phys_for_tlb_insert20 phys
+ extrd,u \phys, 56-PAGE_ADD_SHIFT, 32-PAGE_ADD_SHIFT, \phys
+#if _PAGE_SIZE_ENCODING_DEFAULT
+ depdi _PAGE_SIZE_ENCODING_DEFAULT, 63, (63-58), \phys
+#endif
+ .endm
/*
* We can't do this since copy_user_page is used to bring in
@@ -447,42 +583,112 @@ copy_user_page_asm:
* use it if more information is passed into copy_user_page().
* Have to do some measurements to see if it is worthwhile to
* lobby for such a change.
+ *
*/
- .export copy_user_page_asm,code
-
-copy_user_page_asm:
+ENTRY(copy_user_page_asm)
.proc
.callinfo NO_CALLS
.entry
+ /* Convert virtual `to' and `from' addresses to physical addresses.
+ Move `from' physical address to non shadowed register. */
ldil L%(__PAGE_OFFSET), %r1
sub %r26, %r1, %r26
- sub %r25, %r1, %r23 /* move physical addr into non shadowed reg */
+ sub %r25, %r1, %r23
ldil L%(TMPALIAS_MAP_START), %r28
#ifdef CONFIG_64BIT
- extrd,u %r26,56,32, %r26 /* convert phys addr to tlb insert format */
- extrd,u %r23,56,32, %r23 /* convert phys addr to tlb insert format */
- depd %r24,63,22, %r28 /* Form aliased virtual address 'to' */
- depdi 0, 63,12, %r28 /* Clear any offset bits */
+#if (TMPALIAS_MAP_START >= 0x80000000)
+ depdi 0, 31,32, %r28 /* clear any sign extension */
+#endif
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
+ convert_phys_for_tlb_insert20 %r23 /* convert phys addr to tlb insert format */
+ depd %r24,63,22, %r28 /* Form aliased virtual address 'to' */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
copy %r28, %r29
depdi 1, 41,1, %r29 /* Form aliased virtual address 'from' */
#else
extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
extrw,u %r23, 24,25, %r23 /* convert phys addr to tlb insert format */
depw %r24, 31,22, %r28 /* Form aliased virtual address 'to' */
- depwi 0, 31,12, %r28 /* Clear any offset bits */
+ depwi 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */
copy %r28, %r29
depwi 1, 9,1, %r29 /* Form aliased virtual address 'from' */
#endif
/* Purge any old translations */
+#ifdef CONFIG_PA20
+ pdtlb,l 0(%r28)
+ pdtlb,l 0(%r29)
+#else
+ tlb_lock %r20,%r21,%r22
pdtlb 0(%r28)
pdtlb 0(%r29)
+ tlb_unlock %r20,%r21,%r22
+#endif
+
+#ifdef CONFIG_64BIT
+ /* PA8x00 CPUs can consume 2 loads or 1 store per cycle.
+ * Unroll the loop by hand and arrange insn appropriately.
+ * GCC probably can do this just as well.
+ */
- ldi 64, %r1
+ ldd 0(%r29), %r19
+ ldi (PAGE_SIZE / 128), %r1
+
+1: ldd 8(%r29), %r20
+
+ ldd 16(%r29), %r21
+ ldd 24(%r29), %r22
+ std %r19, 0(%r28)
+ std %r20, 8(%r28)
+
+ ldd 32(%r29), %r19
+ ldd 40(%r29), %r20
+ std %r21, 16(%r28)
+ std %r22, 24(%r28)
+
+ ldd 48(%r29), %r21
+ ldd 56(%r29), %r22
+ std %r19, 32(%r28)
+ std %r20, 40(%r28)
+
+ ldd 64(%r29), %r19
+ ldd 72(%r29), %r20
+ std %r21, 48(%r28)
+ std %r22, 56(%r28)
+
+ ldd 80(%r29), %r21
+ ldd 88(%r29), %r22
+ std %r19, 64(%r28)
+ std %r20, 72(%r28)
+
+ ldd 96(%r29), %r19
+ ldd 104(%r29), %r20
+ std %r21, 80(%r28)
+ std %r22, 88(%r28)
+
+ ldd 112(%r29), %r21
+ ldd 120(%r29), %r22
+ std %r19, 96(%r28)
+ std %r20, 104(%r28)
+
+ ldo 128(%r29), %r29
+ std %r21, 112(%r28)
+ std %r22, 120(%r28)
+ ldo 128(%r28), %r28
+
+ /* conditional branches nullify on forward taken branch, and on
+ * non-taken backward branch. Note that .+4 is a backwards branch.
+ * The ldd should only get executed if the branch is taken.
+ */
+ addib,COND(>),n -1, %r1, 1b /* bundle 10 */
+ ldd 0(%r29), %r19 /* start next loads */
+
+#else
+ ldi (PAGE_SIZE / 64), %r1
/*
* This loop is optimized for PCXL/PCXL2 ldw/ldw and stw/stw
@@ -493,9 +699,7 @@ copy_user_page_asm:
* use ldd/std on a 32 bit kernel.
*/
-
-1:
- ldw 0(%r29), %r19
+1: ldw 0(%r29), %r19
ldw 4(%r29), %r20
ldw 8(%r29), %r21
ldw 12(%r29), %r22
@@ -528,19 +732,19 @@ copy_user_page_asm:
stw %r21, 56(%r28)
stw %r22, 60(%r28)
ldo 64(%r28), %r28
- ADDIB> -1, %r1,1b
+
+ addib,COND(>) -1, %r1,1b
ldo 64(%r29), %r29
+#endif
bv %r0(%r2)
nop
.exit
.procend
-#endif
+ENDPROC(copy_user_page_asm)
- .export __clear_user_page_asm,code
-
-__clear_user_page_asm:
+ENTRY(clear_user_page_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -552,24 +756,30 @@ __clear_user_page_asm:
#if (TMPALIAS_MAP_START >= 0x80000000)
depdi 0, 31,32, %r28 /* clear any sign extension */
#endif
- extrd,u %r26, 56,32, %r26 /* convert phys addr to tlb insert format */
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
depd %r25, 63,22, %r28 /* Form aliased virtual address 'to' */
- depdi 0, 63,12, %r28 /* Clear any offset bits */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
#else
extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
depw %r25, 31,22, %r28 /* Form aliased virtual address 'to' */
- depwi 0, 31,12, %r28 /* Clear any offset bits */
+ depwi 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */
#endif
/* Purge any old translation */
+#ifdef CONFIG_PA20
+ pdtlb,l 0(%r28)
+#else
+ tlb_lock %r20,%r21,%r22
pdtlb 0(%r28)
+ tlb_unlock %r20,%r21,%r22
+#endif
#ifdef CONFIG_64BIT
- ldi 32, %r1 /* PAGE_SIZE/128 == 32 */
+ ldi (PAGE_SIZE / 128), %r1
/* PREFETCH (Write) has not (yet) been proven to help here */
-/* #define PREFETCHW_OP ldd 256(%0), %r0 */
+ /* #define PREFETCHW_OP ldd 256(%0), %r0 */
1: std %r0, 0(%r28)
std %r0, 8(%r28)
@@ -587,15 +797,13 @@ __clear_user_page_asm:
std %r0, 104(%r28)
std %r0, 112(%r28)
std %r0, 120(%r28)
- ADDIB> -1, %r1, 1b
+ addib,COND(>) -1, %r1, 1b
ldo 128(%r28), %r28
#else /* ! CONFIG_64BIT */
+ ldi (PAGE_SIZE / 64), %r1
- ldi 64, %r1 /* PAGE_SIZE/64 == 64 */
-
-1:
- stw %r0, 0(%r28)
+1: stw %r0, 0(%r28)
stw %r0, 4(%r28)
stw %r0, 8(%r28)
stw %r0, 12(%r28)
@@ -611,7 +819,7 @@ __clear_user_page_asm:
stw %r0, 52(%r28)
stw %r0, 56(%r28)
stw %r0, 60(%r28)
- ADDIB> -1, %r1, 1b
+ addib,COND(>) -1, %r1, 1b
ldo 64(%r28), %r28
#endif /* CONFIG_64BIT */
@@ -620,98 +828,163 @@ __clear_user_page_asm:
.exit
.procend
+ENDPROC(clear_user_page_asm)
- .export flush_kernel_dcache_page
-
-flush_kernel_dcache_page:
+ENTRY(flush_dcache_page_asm)
.proc
.callinfo NO_CALLS
.entry
+ ldil L%(TMPALIAS_MAP_START), %r28
+#ifdef CONFIG_64BIT
+#if (TMPALIAS_MAP_START >= 0x80000000)
+ depdi 0, 31,32, %r28 /* clear any sign extension */
+#endif
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
+ depd %r25, 63,22, %r28 /* Form aliased virtual address 'to' */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
+#else
+ extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
+ depw %r25, 31,22, %r28 /* Form aliased virtual address 'to' */
+ depwi 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */
+#endif
+
+ /* Purge any old translation */
+
+#ifdef CONFIG_PA20
+ pdtlb,l 0(%r28)
+#else
+ tlb_lock %r20,%r21,%r22
+ pdtlb 0(%r28)
+ tlb_unlock %r20,%r21,%r22
+#endif
+
ldil L%dcache_stride, %r1
- ldw R%dcache_stride(%r1), %r23
+ ldw R%dcache_stride(%r1), r31
#ifdef CONFIG_64BIT
depdi,z 1, 63-PAGE_SHIFT,1, %r25
#else
depwi,z 1, 31-PAGE_SHIFT,1, %r25
#endif
- add %r26, %r25, %r25
- sub %r25, %r23, %r25
+ add %r28, %r25, %r25
+ sub %r25, r31, %r25
+
+
+1: fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ fdc,m r31(%r28)
+ cmpb,COND(<<) %r28, %r25,1b
+ fdc,m r31(%r28)
+ sync
-1: fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- fdc,m %r23(%r26)
- CMPB<< %r26, %r25,1b
- fdc,m %r23(%r26)
+#ifdef CONFIG_PA20
+ pdtlb,l 0(%r25)
+#else
+ tlb_lock %r20,%r21,%r22
+ pdtlb 0(%r25)
+ tlb_unlock %r20,%r21,%r22
+#endif
- sync
bv %r0(%r2)
nop
.exit
.procend
-
- .export flush_user_dcache_page
+ENDPROC(flush_dcache_page_asm)
-flush_user_dcache_page:
+ENTRY(flush_icache_page_asm)
.proc
.callinfo NO_CALLS
.entry
- ldil L%dcache_stride, %r1
- ldw R%dcache_stride(%r1), %r23
-
+ ldil L%(TMPALIAS_MAP_START), %r28
#ifdef CONFIG_64BIT
- depdi,z 1,63-PAGE_SHIFT,1, %r25
+#if (TMPALIAS_MAP_START >= 0x80000000)
+ depdi 0, 31,32, %r28 /* clear any sign extension */
+#endif
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
+ depd %r25, 63,22, %r28 /* Form aliased virtual address 'to' */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
#else
- depwi,z 1,31-PAGE_SHIFT,1, %r25
+ extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
+ depw %r25, 31,22, %r28 /* Form aliased virtual address 'to' */
+ depwi 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */
#endif
- add %r26, %r25, %r25
- sub %r25, %r23, %r25
+ /* Purge any old translation */
-1: fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- fdc,m %r23(%sr3, %r26)
- CMPB<< %r26, %r25,1b
- fdc,m %r23(%sr3, %r26)
+#ifdef CONFIG_PA20
+ pitlb,l %r0(%sr4,%r28)
+#else
+ tlb_lock %r20,%r21,%r22
+ pitlb (%sr4,%r28)
+ tlb_unlock %r20,%r21,%r22
+#endif
+
+ ldil L%icache_stride, %r1
+ ldw R%icache_stride(%r1), %r31
+
+#ifdef CONFIG_64BIT
+ depdi,z 1, 63-PAGE_SHIFT,1, %r25
+#else
+ depwi,z 1, 31-PAGE_SHIFT,1, %r25
+#endif
+ add %r28, %r25, %r25
+ sub %r25, %r31, %r25
+
+
+ /* fic only has the type 26 form on PA1.1, requiring an
+ * explicit space specification, so use %sr4 */
+1: fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ fic,m %r31(%sr4,%r28)
+ cmpb,COND(<<) %r28, %r25,1b
+ fic,m %r31(%sr4,%r28)
sync
+
+#ifdef CONFIG_PA20
+ pitlb,l %r0(%sr4,%r25)
+#else
+ tlb_lock %r20,%r21,%r22
+ pitlb (%sr4,%r25)
+ tlb_unlock %r20,%r21,%r22
+#endif
+
bv %r0(%r2)
nop
.exit
.procend
+ENDPROC(flush_icache_page_asm)
- .export flush_user_icache_page
-
-flush_user_icache_page:
+ENTRY(flush_kernel_dcache_page_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -728,23 +1001,23 @@ flush_user_icache_page:
sub %r25, %r23, %r25
-1: fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- fic,m %r23(%sr3, %r26)
- CMPB<< %r26, %r25,1b
- fic,m %r23(%sr3, %r26)
+1: fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ fdc,m %r23(%r26)
+ cmpb,COND(<<) %r26, %r25,1b
+ fdc,m %r23(%r26)
sync
bv %r0(%r2)
@@ -752,11 +1025,9 @@ flush_user_icache_page:
.exit
.procend
+ENDPROC(flush_kernel_dcache_page_asm)
-
- .export purge_kernel_dcache_page
-
-purge_kernel_dcache_page:
+ENTRY(purge_kernel_dcache_page_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -787,7 +1058,7 @@ purge_kernel_dcache_page:
pdc,m %r23(%r26)
pdc,m %r23(%r26)
pdc,m %r23(%r26)
- CMPB<< %r26, %r25, 1b
+ cmpb,COND(<<) %r26, %r25, 1b
pdc,m %r23(%r26)
sync
@@ -796,76 +1067,9 @@ purge_kernel_dcache_page:
.exit
.procend
+ENDPROC(purge_kernel_dcache_page_asm)
-#if 0
- /* Currently not used, but it still is a possible alternate
- * solution.
- */
-
- .export flush_alias_page
-
-flush_alias_page:
- .proc
- .callinfo NO_CALLS
- .entry
-
- tophys_r1 %r26
-
- ldil L%(TMPALIAS_MAP_START), %r28
-#ifdef CONFIG_64BIT
- extrd,u %r26, 56,32, %r26 /* convert phys addr to tlb insert format */
- depd %r25, 63,22, %r28 /* Form aliased virtual address 'to' */
- depdi 0, 63,12, %r28 /* Clear any offset bits */
-#else
- extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
- depw %r25, 31,22, %r28 /* Form aliased virtual address 'to' */
- depwi 0, 31,12, %r28 /* Clear any offset bits */
-#endif
-
- /* Purge any old translation */
-
- pdtlb 0(%r28)
-
- ldil L%dcache_stride, %r1
- ldw R%dcache_stride(%r1), %r23
-
-#ifdef CONFIG_64BIT
- depdi,z 1, 63-PAGE_SHIFT,1, %r29
-#else
- depwi,z 1, 31-PAGE_SHIFT,1, %r29
-#endif
- add %r28, %r29, %r29
- sub %r29, %r23, %r29
-
-1: fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- fdc,m %r23(%r28)
- CMPB<< %r28, %r29, 1b
- fdc,m %r23(%r28)
-
- sync
- bv %r0(%r2)
- nop
- .exit
-
- .procend
-#endif
-
- .export flush_user_dcache_range_asm
-
-flush_user_dcache_range_asm:
+ENTRY(flush_user_dcache_range_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -875,7 +1079,7 @@ flush_user_dcache_range_asm:
ldo -1(%r23), %r21
ANDCM %r26, %r21, %r26
-1: CMPB<<,n %r26, %r25, 1b
+1: cmpb,COND(<<),n %r26, %r25, 1b
fdc,m %r23(%sr3, %r26)
sync
@@ -884,10 +1088,9 @@ flush_user_dcache_range_asm:
.exit
.procend
+ENDPROC(flush_user_dcache_range_asm)
- .export flush_kernel_dcache_range_asm
-
-flush_kernel_dcache_range_asm:
+ENTRY(flush_kernel_dcache_range_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -897,7 +1100,7 @@ flush_kernel_dcache_range_asm:
ldo -1(%r23), %r21
ANDCM %r26, %r21, %r26
-1: CMPB<<,n %r26, %r25,1b
+1: cmpb,COND(<<),n %r26, %r25,1b
fdc,m %r23(%r26)
sync
@@ -907,10 +1110,9 @@ flush_kernel_dcache_range_asm:
.exit
.procend
+ENDPROC(flush_kernel_dcache_range_asm)
- .export flush_user_icache_range_asm
-
-flush_user_icache_range_asm:
+ENTRY(flush_user_icache_range_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -920,7 +1122,7 @@ flush_user_icache_range_asm:
ldo -1(%r23), %r21
ANDCM %r26, %r21, %r26
-1: CMPB<<,n %r26, %r25,1b
+1: cmpb,COND(<<),n %r26, %r25,1b
fic,m %r23(%sr3, %r26)
sync
@@ -929,10 +1131,9 @@ flush_user_icache_range_asm:
.exit
.procend
+ENDPROC(flush_user_icache_range_asm)
- .export flush_kernel_icache_page
-
-flush_kernel_icache_page:
+ENTRY(flush_kernel_icache_page)
.proc
.callinfo NO_CALLS
.entry
@@ -964,7 +1165,7 @@ flush_kernel_icache_page:
fic,m %r23(%sr4, %r26)
fic,m %r23(%sr4, %r26)
fic,m %r23(%sr4, %r26)
- CMPB<< %r26, %r25, 1b
+ cmpb,COND(<<) %r26, %r25, 1b
fic,m %r23(%sr4, %r26)
sync
@@ -973,10 +1174,9 @@ flush_kernel_icache_page:
.exit
.procend
+ENDPROC(flush_kernel_icache_page)
- .export flush_kernel_icache_range_asm
-
-flush_kernel_icache_range_asm:
+ENTRY(flush_kernel_icache_range_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -986,7 +1186,7 @@ flush_kernel_icache_range_asm:
ldo -1(%r23), %r21
ANDCM %r26, %r21, %r26
-1: CMPB<<,n %r26, %r25, 1b
+1: cmpb,COND(<<),n %r26, %r25, 1b
fic,m %r23(%sr4, %r26)
sync
@@ -994,14 +1194,13 @@ flush_kernel_icache_range_asm:
nop
.exit
.procend
+ENDPROC(flush_kernel_icache_range_asm)
/* align should cover use of rfi in disable_sr_hashing_asm and
* srdis_done.
*/
.align 256
- .export disable_sr_hashing_asm,code
-
-disable_sr_hashing_asm:
+ENTRY(disable_sr_hashing_asm)
.proc
.callinfo NO_CALLS
.entry
@@ -1090,5 +1289,6 @@ srdis_done:
.exit
.procend
+ENDPROC(disable_sr_hashing_asm)
.end
diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c
index f40a777dd38..568b2c61ea0 100644
--- a/arch/parisc/kernel/parisc_ksyms.c
+++ b/arch/parisc/kernel/parisc_ksyms.c
@@ -7,7 +7,7 @@
* Copyright (C) 2001-2003 Grant Grundler <grundler with parisc-linux.org>
* Copyright (C) 2002-2003 Matthew Wilcox <willy at parisc-linux.org>
* Copyright (C) 2002 Randolph Chung <tausq at parisc-linux.org>
- * Copyright (C) 2002-2003 Helge Deller <deller with parisc-linux.org>
+ * Copyright (C) 2002-2007 Helge Deller <deller with parisc-linux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,47 +24,26 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/string.h>
-EXPORT_SYMBOL(memchr);
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(memscan);
EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(strcat);
-EXPORT_SYMBOL(strchr);
-EXPORT_SYMBOL(strcmp);
-EXPORT_SYMBOL(strcpy);
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(strncat);
-EXPORT_SYMBOL(strncmp);
-EXPORT_SYMBOL(strncpy);
-EXPORT_SYMBOL(strnlen);
-EXPORT_SYMBOL(strrchr);
-EXPORT_SYMBOL(strstr);
-EXPORT_SYMBOL(strpbrk);
-
-#include <linux/pm.h>
-EXPORT_SYMBOL(pm_power_off);
-
-#include <asm/atomic.h>
+
+#include <linux/atomic.h>
EXPORT_SYMBOL(__xchg8);
EXPORT_SYMBOL(__xchg32);
EXPORT_SYMBOL(__cmpxchg_u32);
#ifdef CONFIG_SMP
EXPORT_SYMBOL(__atomic_hash);
#endif
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
EXPORT_SYMBOL(__xchg64);
EXPORT_SYMBOL(__cmpxchg_u64);
#endif
#include <asm/uaccess.h>
-EXPORT_SYMBOL(lstrncpy_from_user);
EXPORT_SYMBOL(lclear_user);
EXPORT_SYMBOL(lstrnlen_user);
@@ -78,30 +57,17 @@ EXPORT_SYMBOL(fixup_get_user_skip_2);
EXPORT_SYMBOL(fixup_put_user_skip_1);
EXPORT_SYMBOL(fixup_put_user_skip_2);
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
/* Needed so insmod can set dp value */
extern int $global$;
EXPORT_SYMBOL($global$);
#endif
#include <asm/io.h>
-EXPORT_SYMBOL(__ioremap);
-EXPORT_SYMBOL(iounmap);
EXPORT_SYMBOL(memcpy_toio);
EXPORT_SYMBOL(memcpy_fromio);
EXPORT_SYMBOL(memset_io);
-#include <asm/unistd.h>
-EXPORT_SYMBOL(sys_open);
-EXPORT_SYMBOL(sys_lseek);
-EXPORT_SYMBOL(sys_read);
-EXPORT_SYMBOL(sys_write);
-
-#include <asm/semaphore.h>
-EXPORT_SYMBOL(__up);
-EXPORT_SYMBOL(__down_interruptible);
-EXPORT_SYMBOL(__down);
-
extern void $$divI(void);
extern void $$divU(void);
extern void $$remI(void);
@@ -154,16 +120,18 @@ extern void __ashrdi3(void);
extern void __ashldi3(void);
extern void __lshrdi3(void);
extern void __muldi3(void);
+extern void __ucmpdi2(void);
EXPORT_SYMBOL(__ashrdi3);
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__lshrdi3);
EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__ucmpdi2);
asmlinkage void * __canonicalize_funcptr_for_compare(void *);
EXPORT_SYMBOL(__canonicalize_funcptr_for_compare);
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
extern void __divdi3(void);
extern void __udivdi3(void);
extern void __umoddi3(void);
@@ -175,7 +143,7 @@ EXPORT_SYMBOL(__umoddi3);
EXPORT_SYMBOL(__moddi3);
#endif
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
extern void $$dyncall(void);
EXPORT_SYMBOL($$dyncall);
#endif
@@ -185,3 +153,12 @@ EXPORT_SYMBOL($$dyncall);
EXPORT_SYMBOL(node_data);
EXPORT_SYMBOL(pfnnid_map);
#endif
+
+#ifdef CONFIG_FUNCTION_TRACER
+extern void _mcount(void);
+EXPORT_SYMBOL(_mcount);
+#endif
+
+/* from pacache.S -- needed for clear/copy_page */
+EXPORT_SYMBOL(clear_page_asm);
+EXPORT_SYMBOL(copy_page_asm);
diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c
index f94a02ef3d9..d87d1c476d8 100644
--- a/arch/parisc/kernel/pci-dma.c
+++ b/arch/parisc/kernel/pci-dma.c
@@ -2,7 +2,7 @@
** PARISC 1.1 Dynamic DMA mapping support.
** This implementation is for PA-RISC platforms that do not support
** I/O TLBs (aka DMA address translation hardware).
-** See Documentation/DMA-mapping.txt for interface definitions.
+** See Documentation/DMA-API-HOWTO.txt for interface definitions.
**
** (c) Copyright 1999,2000 Hewlett-Packard Company
** (c) Copyright 2000 Grant Grundler
@@ -18,12 +18,15 @@
*/
#include <linux/init.h>
+#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
-#include <linux/slab.h>
+#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/scatterlist.h>
+#include <linux/export.h>
#include <asm/cacheflush.h>
#include <asm/dma.h> /* for DMA_CHUNK_SIZE */
@@ -33,10 +36,9 @@
#include <asm/uaccess.h>
#include <asm/tlbflush.h> /* for purge_tlb_*() macros */
-static struct proc_dir_entry * proc_gsc_root = NULL;
-static int pcxl_proc_info(char *buffer, char **start, off_t offset, int length);
-static unsigned long pcxl_used_bytes = 0;
-static unsigned long pcxl_used_pages = 0;
+static struct proc_dir_entry * proc_gsc_root __read_mostly = NULL;
+static unsigned long pcxl_used_bytes __read_mostly = 0;
+static unsigned long pcxl_used_pages __read_mostly = 0;
extern unsigned long pcxl_dma_start; /* Start of pcxl dma mapping area */
static spinlock_t pcxl_res_lock;
@@ -89,12 +91,14 @@ static inline int map_pte_uncached(pte_t * pte,
if (end > PMD_SIZE)
end = PMD_SIZE;
do {
+ unsigned long flags;
+
if (!pte_none(*pte))
printk(KERN_ERR "map_pte_uncached: page already exists\n");
set_pte(pte, __mk_pte(*paddr_ptr, PAGE_KERNEL_UNC));
- purge_tlb_start();
+ purge_tlb_start(flags);
pdtlb_kernel(orig_vaddr);
- purge_tlb_end();
+ purge_tlb_end(flags);
vaddr += PAGE_SIZE;
orig_vaddr += PAGE_SIZE;
(*paddr_ptr) += PAGE_SIZE;
@@ -167,11 +171,13 @@ static inline void unmap_uncached_pte(pmd_t * pmd, unsigned long vaddr,
if (end > PMD_SIZE)
end = PMD_SIZE;
do {
+ unsigned long flags;
pte_t page = *pte;
+
pte_clear(&init_mm, vaddr, pte);
- purge_tlb_start();
+ purge_tlb_start(flags);
pdtlb_kernel(orig_vaddr);
- purge_tlb_end();
+ purge_tlb_end(flags);
vaddr += PAGE_SIZE;
orig_vaddr += PAGE_SIZE;
pte++;
@@ -330,6 +336,54 @@ pcxl_free_range(unsigned long vaddr, size_t size)
dump_resmap();
}
+static int proc_pcxl_dma_show(struct seq_file *m, void *v)
+{
+#if 0
+ u_long i = 0;
+ unsigned long *res_ptr = (u_long *)pcxl_res_map;
+#endif
+ unsigned long total_pages = pcxl_res_size << 3; /* 8 bits per byte */
+
+ seq_printf(m, "\nDMA Mapping Area size : %d bytes (%ld pages)\n",
+ PCXL_DMA_MAP_SIZE, total_pages);
+
+ seq_printf(m, "Resource bitmap : %d bytes\n", pcxl_res_size);
+
+ seq_puts(m, " total: free: used: % used:\n");
+ seq_printf(m, "blocks %8d %8ld %8ld %8ld%%\n", pcxl_res_size,
+ pcxl_res_size - pcxl_used_bytes, pcxl_used_bytes,
+ (pcxl_used_bytes * 100) / pcxl_res_size);
+
+ seq_printf(m, "pages %8ld %8ld %8ld %8ld%%\n", total_pages,
+ total_pages - pcxl_used_pages, pcxl_used_pages,
+ (pcxl_used_pages * 100 / total_pages));
+
+#if 0
+ seq_puts(m, "\nResource bitmap:");
+
+ for(; i < (pcxl_res_size / sizeof(u_long)); ++i, ++res_ptr) {
+ if ((i & 7) == 0)
+ seq_puts(m,"\n ");
+ seq_printf(m, "%s %08lx", buf, *res_ptr);
+ }
+#endif
+ seq_putc(m, '\n');
+ return 0;
+}
+
+static int proc_pcxl_dma_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, proc_pcxl_dma_show, NULL);
+}
+
+static const struct file_operations proc_pcxl_dma_ops = {
+ .owner = THIS_MODULE,
+ .open = proc_pcxl_dma_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int __init
pcxl_dma_init(void)
{
@@ -342,14 +396,14 @@ pcxl_dma_init(void)
pcxl_res_map = (char *)__get_free_pages(GFP_KERNEL,
get_order(pcxl_res_size));
memset(pcxl_res_map, 0, pcxl_res_size);
- proc_gsc_root = proc_mkdir("gsc", 0);
+ proc_gsc_root = proc_mkdir("gsc", NULL);
if (!proc_gsc_root)
printk(KERN_WARNING
"pcxl_dma_init: Unable to create gsc /proc dir entry\n");
else {
struct proc_dir_entry* ent;
- ent = create_proc_info_entry("pcxl_dma", 0,
- proc_gsc_root, pcxl_proc_info);
+ ent = proc_create("pcxl_dma", 0, proc_gsc_root,
+ &proc_pcxl_dma_ops);
if (!ent)
printk(KERN_WARNING
"pci-dma.c: Unable to create pcxl_dma /proc entry.\n");
@@ -398,10 +452,7 @@ static void pa11_dma_free_consistent (struct device *dev, size_t size, void *vad
static dma_addr_t pa11_dma_map_single(struct device *dev, void *addr, size_t size, enum dma_data_direction direction)
{
- if (direction == DMA_NONE) {
- printk(KERN_ERR "pa11_dma_map_single(PCI_DMA_NONE) called by %p\n", __builtin_return_address(0));
- BUG();
- }
+ BUG_ON(direction == DMA_NONE);
flush_kernel_dcache_range((unsigned long) addr, size);
return virt_to_phys(addr);
@@ -409,10 +460,7 @@ static dma_addr_t pa11_dma_map_single(struct device *dev, void *addr, size_t siz
static void pa11_dma_unmap_single(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction)
{
- if (direction == DMA_NONE) {
- printk(KERN_ERR "pa11_dma_unmap_single(PCI_DMA_NONE) called by %p\n", __builtin_return_address(0));
- BUG();
- }
+ BUG_ON(direction == DMA_NONE);
if (direction == DMA_TO_DEVICE)
return;
@@ -431,8 +479,7 @@ static int pa11_dma_map_sg(struct device *dev, struct scatterlist *sglist, int n
{
int i;
- if (direction == DMA_NONE)
- BUG();
+ BUG_ON(direction == DMA_NONE);
for (i = 0; i < nents; i++, sglist++ ) {
unsigned long vaddr = sg_virt_addr(sglist);
@@ -447,8 +494,7 @@ static void pa11_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, in
{
int i;
- if (direction == DMA_NONE)
- BUG();
+ BUG_ON(direction == DMA_NONE);
if (direction == DMA_TO_DEVICE)
return;
@@ -462,16 +508,14 @@ static void pa11_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, in
static void pa11_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction)
{
- if (direction == DMA_NONE)
- BUG();
+ BUG_ON(direction == DMA_NONE);
flush_kernel_dcache_range((unsigned long) phys_to_virt(dma_handle) + offset, size);
}
static void pa11_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction)
{
- if (direction == DMA_NONE)
- BUG();
+ BUG_ON(direction == DMA_NONE);
flush_kernel_dcache_range((unsigned long) phys_to_virt(dma_handle) + offset, size);
}
@@ -520,11 +564,10 @@ static void *fail_alloc_consistent(struct device *dev, size_t size,
static void *pa11_dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
{
- void *addr = NULL;
+ void *addr;
- /* rely on kmalloc to be cacheline aligned */
- addr = kmalloc(size, flag);
- if(addr)
+ addr = (void *)__get_free_pages(flag, get_order(size));
+ if (addr)
*dma_handle = (dma_addr_t)virt_to_phys(addr);
return addr;
@@ -533,7 +576,7 @@ static void *pa11_dma_alloc_noncoherent(struct device *dev, size_t size,
static void pa11_dma_free_noncoherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t iova)
{
- kfree(vaddr);
+ free_pages((unsigned long)vaddr, get_order(size));
return;
}
@@ -551,40 +594,3 @@ struct hppa_dma_ops pcx_dma_ops = {
.dma_sync_sg_for_cpu = pa11_dma_sync_sg_for_cpu,
.dma_sync_sg_for_device = pa11_dma_sync_sg_for_device,
};
-
-
-static int pcxl_proc_info(char *buf, char **start, off_t offset, int len)
-{
-#if 0
- u_long i = 0;
- unsigned long *res_ptr = (u_long *)pcxl_res_map;
-#endif
- unsigned long total_pages = pcxl_res_size << 3; /* 8 bits per byte */
-
- sprintf(buf, "\nDMA Mapping Area size : %d bytes (%ld pages)\n",
- PCXL_DMA_MAP_SIZE, total_pages);
-
- sprintf(buf, "%sResource bitmap : %d bytes\n", buf, pcxl_res_size);
-
- strcat(buf, " total: free: used: % used:\n");
- sprintf(buf, "%sblocks %8d %8ld %8ld %8ld%%\n", buf, pcxl_res_size,
- pcxl_res_size - pcxl_used_bytes, pcxl_used_bytes,
- (pcxl_used_bytes * 100) / pcxl_res_size);
-
- sprintf(buf, "%spages %8ld %8ld %8ld %8ld%%\n", buf, total_pages,
- total_pages - pcxl_used_pages, pcxl_used_pages,
- (pcxl_used_pages * 100 / total_pages));
-
-#if 0
- strcat(buf, "\nResource bitmap:");
-
- for(; i < (pcxl_res_size / sizeof(u_long)); ++i, ++res_ptr) {
- if ((i & 7) == 0)
- strcat(buf,"\n ");
- sprintf(buf, "%s %08lx", buf, *res_ptr);
- }
-#endif
- strcat(buf, "\n");
- return strlen(buf);
-}
-
diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c
index 88cba49c530..64f2764a8ce 100644
--- a/arch/parisc/kernel/pci.c
+++ b/arch/parisc/kernel/pci.c
@@ -1,5 +1,4 @@
-/* $Id: pci.c,v 1.6 2000/01/29 00:12:05 grundler Exp $
- *
+/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
@@ -9,18 +8,14 @@
* Copyright (C) 1999-2001 Hewlett-Packard Company
* Copyright (C) 1999-2001 Grant Grundler
*/
-#include <linux/config.h>
#include <linux/eisa.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/slab.h>
#include <linux/types.h>
#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/cache.h> /* for L1_CACHE_BYTES */
#include <asm/superio.h>
#define DEBUG_RESOURCES 0
@@ -47,18 +42,17 @@
* this makes the boot time much longer than necessary.
* 20ms seems to work for all the HP PCI implementations to date.
*
- * XXX: turn into a #defined constant in <asm/pci.h> ?
+ * #define pci_post_reset_delay 50
*/
-int pci_post_reset_delay = 50;
-struct pci_port_ops *pci_port;
-struct pci_bios_ops *pci_bios;
+struct pci_port_ops *pci_port __read_mostly;
+struct pci_bios_ops *pci_bios __read_mostly;
-int pci_hba_count = 0;
+static int pci_hba_count __read_mostly;
/* parisc_pci_hba used by pci_port->in/out() ops to lookup bus data. */
#define PCI_HBA_MAX 32
-struct pci_hba_data *parisc_pci_hba[PCI_HBA_MAX];
+static struct pci_hba_data *parisc_pci_hba[PCI_HBA_MAX] __read_mostly;
/********************************************************************
@@ -126,6 +120,10 @@ static int __init pcibios_init(void)
} else {
printk(KERN_WARNING "pci_bios != NULL but init() is!\n");
}
+
+ /* Set the CLS for PCI as early as possible. */
+ pci_cache_line_size = pci_dfl_cache_line_size;
+
return 0;
}
@@ -141,11 +139,6 @@ void pcibios_fixup_bus(struct pci_bus *bus)
}
-char *pcibios_setup(char *str)
-{
- return str;
-}
-
/*
* Called by pci_set_master() - a driver interface.
*
@@ -174,7 +167,7 @@ void pcibios_set_master(struct pci_dev *dev)
** upper byte is PCI_LATENCY_TIMER.
*/
pci_write_config_word(dev, PCI_CACHE_LINE_SIZE,
- (0x80 << 8) | (L1_CACHE_BYTES / sizeof(u32)));
+ (0x80 << 8) | pci_cache_line_size);
}
@@ -196,88 +189,6 @@ void __init pcibios_init_bus(struct pci_bus *bus)
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bridge_ctl);
}
-
-/* KLUGE: Link the child and parent resources - generic PCI didn't */
-static void
-pcibios_link_hba_resources( struct resource *hba_res, struct resource *r)
-{
- if (!r->parent) {
- printk(KERN_EMERG "PCI: resource not parented! [%lx-%lx]\n",
- r->start, r->end);
- r->parent = hba_res;
-
- /* reverse link is harder *sigh* */
- if (r->parent->child) {
- if (r->parent->sibling) {
- struct resource *next = r->parent->sibling;
- while (next->sibling)
- next = next->sibling;
- next->sibling = r;
- } else {
- r->parent->sibling = r;
- }
- } else
- r->parent->child = r;
- }
-}
-
-/* called by drivers/pci/setup-bus.c:pci_setup_bridge(). */
-void __devinit pcibios_resource_to_bus(struct pci_dev *dev,
- struct pci_bus_region *region, struct resource *res)
-{
- struct pci_bus *bus = dev->bus;
- struct pci_hba_data *hba = HBA_DATA(bus->bridge->platform_data);
-
- if (res->flags & IORESOURCE_IO) {
- /*
- ** I/O space may see busnumbers here. Something
- ** in the form of 0xbbxxxx where bb is the bus num
- ** and xxxx is the I/O port space address.
- ** Remaining address translation are done in the
- ** PCI Host adapter specific code - ie dino_out8.
- */
- region->start = PCI_PORT_ADDR(res->start);
- region->end = PCI_PORT_ADDR(res->end);
- } else if (res->flags & IORESOURCE_MEM) {
- /* Convert MMIO addr to PCI addr (undo global virtualization) */
- region->start = PCI_BUS_ADDR(hba, res->start);
- region->end = PCI_BUS_ADDR(hba, res->end);
- }
-
- DBG_RES("pcibios_resource_to_bus(%02x %s [%lx,%lx])\n",
- bus->number, res->flags & IORESOURCE_IO ? "IO" : "MEM",
- region->start, region->end);
-
- /* KLUGE ALERT
- ** if this resource isn't linked to a "parent", then it seems
- ** to be a child of the HBA - lets link it in.
- */
- pcibios_link_hba_resources(&hba->io_space, bus->resource[0]);
- pcibios_link_hba_resources(&hba->lmmio_space, bus->resource[1]);
-}
-
-void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
- struct pci_bus_region *region)
-{
- struct pci_bus *bus = dev->bus;
- struct pci_hba_data *hba = HBA_DATA(bus->bridge->platform_data);
-
- if (res->flags & IORESOURCE_MEM) {
- res->start = PCI_HOST_ADDR(hba, region->start);
- res->end = PCI_HOST_ADDR(hba, region->end);
- }
-
- if (res->flags & IORESOURCE_IO) {
- res->start = region->start;
- res->end = region->end;
- }
-}
-
-#ifdef CONFIG_HOTPLUG
-EXPORT_SYMBOL(pcibios_resource_to_bus);
-EXPORT_SYMBOL(pcibios_bus_to_resource);
-#endif
-
/*
* pcibios align resources() is called every time generic PCI code
* wants to generate a new address. The process of looking for
@@ -287,10 +198,10 @@ EXPORT_SYMBOL(pcibios_bus_to_resource);
* Since we are just checking candidates, don't use any fields other
* than res->start.
*/
-void pcibios_align_resource(void *data, struct resource *res,
- unsigned long size, unsigned long alignment)
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
+ resource_size_t size, resource_size_t alignment)
{
- unsigned long mask, align;
+ resource_size_t mask, align, start = res->start;
DBG_RES("pcibios_align_resource(%s, (%p) [%lx,%lx]/%x, 0x%lx, 0x%lx)\n",
pci_name(((struct pci_dev *) data)),
@@ -302,13 +213,40 @@ void pcibios_align_resource(void *data, struct resource *res,
/* Align to largest of MIN or input size */
mask = max(alignment, align) - 1;
- res->start += mask;
- res->start &= ~mask;
+ start += mask;
+ start &= ~mask;
- /* The caller updates the end field, we don't. */
+ return start;
}
+int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state, int write_combine)
+{
+ unsigned long prot;
+
+ /*
+ * I/O space can be accessed via normal processor loads and stores on
+ * this platform but for now we elect not to do this and portable
+ * drivers should not do this anyway.
+ */
+ if (mmap_state == pci_mmap_io)
+ return -EINVAL;
+
+ if (write_combine)
+ return -EINVAL;
+
+ /*
+ * Ignore write-combine; for now only return uncached mappings.
+ */
+ prot = pgprot_val(vma->vm_page_prot);
+ prot |= _PAGE_NO_CACHE;
+ vma->vm_page_prot = __pgprot(prot);
+
+ return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
/*
* A driver is enabling the device. We make sure that all the appropriate
* bits are set to allow the device to operate as the driver is expecting.
@@ -319,23 +257,15 @@ void pcibios_align_resource(void *data, struct resource *res,
*/
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
- u16 cmd;
- int idx;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ int err;
+ u16 cmd, old_cmd;
- for (idx = 0; idx < DEVICE_COUNT_RESOURCE; idx++) {
- struct resource *r = &dev->resource[idx];
+ err = pci_enable_resources(dev, mask);
+ if (err < 0)
+ return err;
- /* only setup requested resources */
- if (!(mask & (1<<idx)))
- continue;
-
- if (r->flags & IORESOURCE_IO)
- cmd |= PCI_COMMAND_IO;
- if (r->flags & IORESOURCE_MEM)
- cmd |= PCI_COMMAND_MEMORY;
- }
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
cmd |= (PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
@@ -344,8 +274,12 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
if (dev->bus->bridge_ctl & PCI_BRIDGE_CTL_FAST_BACK)
cmd |= PCI_COMMAND_FAST_BACK;
#endif
- DBGC("PCIBIOS: Enabling device %s cmd 0x%04x\n", pci_name(dev), cmd);
- pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+ if (cmd != old_cmd) {
+ dev_info(&dev->dev, "enabling SERR and PARITY (%04x -> %04x)\n",
+ old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
return 0;
}
diff --git a/arch/parisc/kernel/pdc_chassis.c b/arch/parisc/kernel/pdc_chassis.c
index 52004ae28d2..3e04242de5a 100644
--- a/arch/parisc/kernel/pdc_chassis.c
+++ b/arch/parisc/kernel/pdc_chassis.c
@@ -1,13 +1,12 @@
/*
- * interfaces to log Chassis Codes via PDC (firmware)
+ * interfaces to Chassis Codes via PDC (firmware)
*
* Copyright (C) 2002 Laurent Canet <canetl@esiee.fr>
- * Copyright (C) 2002-2004 Thibaut VARENE <varenet@parisc-linux.org>
+ * Copyright (C) 2002-2006 Thibaut VARENE <varenet@parisc-linux.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -17,6 +16,10 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * TODO: poll chassis warns, trigger (configurable) machine shutdown when
+ * needed.
+ * Find out how to get Chassis warnings out of PAT boxes?
*/
#undef PDC_CHASSIS_DEBUG
@@ -27,19 +30,23 @@
#endif
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/notifier.h>
+#include <linux/cache.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <asm/pdc_chassis.h>
#include <asm/processor.h>
#include <asm/pdc.h>
#include <asm/pdcpat.h>
+#define PDC_CHASSIS_VER "0.05"
#ifdef CONFIG_PDC_CHASSIS
-static int pdc_chassis_old = 0;
-static unsigned int pdc_chassis_enabled = 1;
+static unsigned int pdc_chassis_enabled __read_mostly = 1;
/**
@@ -64,7 +71,7 @@ __setup("pdcchassis=", pdc_chassis_setup);
* Currently, only E class and A180 are known to work with this.
* Inspired by Christoph Plattner
*/
-
+#if 0
static void __init pdc_chassis_checkold(void)
{
switch(CPU_HVERSION) {
@@ -73,7 +80,6 @@ static void __init pdc_chassis_checkold(void)
case 0x482: /* E45 */
case 0x483: /* E55 */
case 0x516: /* A180 */
- pdc_chassis_old = 1;
break;
default:
@@ -81,7 +87,7 @@ static void __init pdc_chassis_checkold(void)
}
DPRINTK(KERN_DEBUG "%s: pdc_chassis_checkold(); pdc_chassis_old = %d\n", __FILE__, pdc_chassis_old);
}
-
+#endif
/**
* pdc_chassis_panic_event() - Called by the panic handler.
@@ -131,29 +137,20 @@ static struct notifier_block pdc_chassis_reboot_block = {
void __init parisc_pdc_chassis_init(void)
{
#ifdef CONFIG_PDC_CHASSIS
- int handle = 0;
- if (pdc_chassis_enabled) {
+ if (likely(pdc_chassis_enabled)) {
DPRINTK(KERN_DEBUG "%s: parisc_pdc_chassis_init()\n", __FILE__);
/* Let see if we have something to handle... */
- /* Check for PDC_PAT or old LED Panel */
- pdc_chassis_checkold();
- if (is_pdc_pat()) {
- printk(KERN_INFO "Enabling PDC_PAT chassis codes support.\n");
- handle = 1;
- }
- else if (pdc_chassis_old) {
- printk(KERN_INFO "Enabling old style chassis LED panel support.\n");
- handle = 1;
- }
-
- if (handle) {
- /* initialize panic notifier chain */
- notifier_chain_register(&panic_notifier_list, &pdc_chassis_panic_block);
-
- /* initialize reboot notifier chain */
- register_reboot_notifier(&pdc_chassis_reboot_block);
- }
+ printk(KERN_INFO "Enabling %s chassis codes support v%s\n",
+ is_pdc_pat() ? "PDC_PAT" : "regular",
+ PDC_CHASSIS_VER);
+
+ /* initialize panic notifier chain */
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &pdc_chassis_panic_block);
+
+ /* initialize reboot notifier chain */
+ register_reboot_notifier(&pdc_chassis_reboot_block);
}
#endif /* CONFIG_PDC_CHASSIS */
}
@@ -178,7 +175,7 @@ int pdc_chassis_send_status(int message)
/* Maybe we should do that in an other way ? */
int retval = 0;
#ifdef CONFIG_PDC_CHASSIS
- if (pdc_chassis_enabled) {
+ if (likely(pdc_chassis_enabled)) {
DPRINTK(KERN_DEBUG "%s: pdc_chassis_send_status(%d)\n", __FILE__, message);
@@ -214,9 +211,12 @@ int pdc_chassis_send_status(int message)
}
} else retval = -1;
#else
- if (pdc_chassis_old) {
+ if (1) {
switch (message) {
case PDC_CHASSIS_DIRECT_BSTART:
+ retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_INIT));
+ break;
+
case PDC_CHASSIS_DIRECT_BCOMPLETE:
retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_RUN));
break;
@@ -243,3 +243,60 @@ int pdc_chassis_send_status(int message)
#endif /* CONFIG_PDC_CHASSIS */
return retval;
}
+
+#ifdef CONFIG_PDC_CHASSIS_WARN
+#ifdef CONFIG_PROC_FS
+static int pdc_chassis_warn_show(struct seq_file *m, void *v)
+{
+ unsigned long warn;
+ u32 warnreg;
+
+ if (pdc_chassis_warn(&warn) != PDC_OK)
+ return -EIO;
+
+ warnreg = (warn & 0xFFFFFFFF);
+
+ if ((warnreg >> 24) & 0xFF)
+ seq_printf(m, "Chassis component failure! (eg fan or PSU): 0x%.2x\n",
+ (warnreg >> 24) & 0xFF);
+
+ seq_printf(m, "Battery: %s\n", (warnreg & 0x04) ? "Low!" : "OK");
+ seq_printf(m, "Temp low: %s\n", (warnreg & 0x02) ? "Exceeded!" : "OK");
+ seq_printf(m, "Temp mid: %s\n", (warnreg & 0x01) ? "Exceeded!" : "OK");
+ return 0;
+}
+
+static int pdc_chassis_warn_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pdc_chassis_warn_show, NULL);
+}
+
+static const struct file_operations pdc_chassis_warn_fops = {
+ .open = pdc_chassis_warn_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init pdc_chassis_create_procfs(void)
+{
+ unsigned long test;
+ int ret;
+
+ ret = pdc_chassis_warn(&test);
+ if ((ret == PDC_BAD_PROC) || (ret == PDC_BAD_OPTION)) {
+ /* seems that some boxes (eg L1000) do not implement this */
+ printk(KERN_INFO "Chassis warnings not supported.\n");
+ return 0;
+ }
+
+ printk(KERN_INFO "Enabling PDC chassis warnings support v%s\n",
+ PDC_CHASSIS_VER);
+ proc_create("chassis", 0400, NULL, &pdc_chassis_warn_fops);
+ return 0;
+}
+
+__initcall(pdc_chassis_create_procfs);
+
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_PDC_CHASSIS_WARN */
diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c
index 215d78c87bc..d5cae55195e 100644
--- a/arch/parisc/kernel/pdc_cons.c
+++ b/arch/parisc/kernel/pdc_cons.c
@@ -12,6 +12,7 @@
* Copyright (C) 2001 Helge Deller <deller at parisc-linux.org>
* Copyright (C) 2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org>
* Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org>
+ * Copyright (C) 2010 Guy Martin <gmsoft at tuxicoman.be>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -31,12 +32,11 @@
/*
* The PDC console is a simple console, which can be used for debugging
- * boot related problems on HP PA-RISC machines.
+ * boot related problems on HP PA-RISC machines. It is also useful when no
+ * other console works.
*
* This code uses the ROM (=PDC) based functions to read and write characters
* from and to PDC's boot path.
- * Since all character read from that path must be polled, this code never
- * can or will be a fully functional linux console.
*/
/* Define EARLY_BOOTUP_DEBUG to debug kernel related boot problems.
@@ -44,57 +44,176 @@
#define EARLY_BOOTUP_DEBUG
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/tty.h>
+#include <asm/page.h> /* for PAGE0 */
#include <asm/pdc.h> /* for iodc_call() proto and friends */
+static DEFINE_SPINLOCK(pdc_console_lock);
+static struct console pdc_cons;
static void pdc_console_write(struct console *co, const char *s, unsigned count)
{
- while(count--)
- pdc_iodc_putc(*s++);
+ int i = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_console_lock, flags);
+ do {
+ i += pdc_iodc_print(s + i, count - i);
+ } while (i < count);
+ spin_unlock_irqrestore(&pdc_console_lock, flags);
}
-void pdc_outc(unsigned char c)
+int pdc_console_poll_key(struct console *co)
{
- pdc_iodc_outc(c);
+ int c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdc_console_lock, flags);
+ c = pdc_iodc_getc();
+ spin_unlock_irqrestore(&pdc_console_lock, flags);
+
+ return c;
}
-void pdc_printf(const char *fmt, ...)
+static int pdc_console_setup(struct console *co, char *options)
{
- va_list args;
- char buf[1024];
- int i, len;
+ return 0;
+}
+
+#if defined(CONFIG_PDC_CONSOLE)
+#include <linux/vt_kern.h>
+#include <linux/tty_flip.h>
- va_start(args, fmt);
- len = vscnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
+#define PDC_CONS_POLL_DELAY (30 * HZ / 1000)
+
+static void pdc_console_poll(unsigned long unused);
+static DEFINE_TIMER(pdc_console_timer, pdc_console_poll, 0, 0);
+static struct tty_port tty_port;
+
+static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ tty_port_tty_set(&tty_port, tty);
+ mod_timer(&pdc_console_timer, jiffies + PDC_CONS_POLL_DELAY);
- for (i = 0; i < len; i++)
- pdc_iodc_outc(buf[i]);
+ return 0;
}
-int pdc_console_poll_key(struct console *co)
+static void pdc_console_tty_close(struct tty_struct *tty, struct file *filp)
{
- return pdc_iodc_getc();
+ if (tty->count == 1) {
+ del_timer_sync(&pdc_console_timer);
+ tty_port_tty_set(&tty_port, NULL);
+ }
}
-static int pdc_console_setup(struct console *co, char *options)
+static int pdc_console_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ pdc_console_write(NULL, buf, count);
+ return count;
+}
+
+static int pdc_console_tty_write_room(struct tty_struct *tty)
+{
+ return 32768; /* no limit, no buffer used */
+}
+
+static int pdc_console_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ return 0; /* no buffer */
+}
+
+static const struct tty_operations pdc_console_tty_ops = {
+ .open = pdc_console_tty_open,
+ .close = pdc_console_tty_close,
+ .write = pdc_console_tty_write,
+ .write_room = pdc_console_tty_write_room,
+ .chars_in_buffer = pdc_console_tty_chars_in_buffer,
+};
+
+static void pdc_console_poll(unsigned long unused)
+{
+ int data, count = 0;
+
+ while (1) {
+ data = pdc_console_poll_key(NULL);
+ if (data == -1)
+ break;
+ tty_insert_flip_char(&tty_port, data & 0xFF, TTY_NORMAL);
+ count ++;
+ }
+
+ if (count)
+ tty_flip_buffer_push(&tty_port);
+
+ if (pdc_cons.flags & CON_ENABLED)
+ mod_timer(&pdc_console_timer, jiffies + PDC_CONS_POLL_DELAY);
+}
+
+static struct tty_driver *pdc_console_tty_driver;
+
+static int __init pdc_console_tty_driver_init(void)
{
+ int err;
+
+ /* Check if the console driver is still registered.
+ * It is unregistered if the pdc console was not selected as the
+ * primary console. */
+
+ struct console *tmp;
+
+ console_lock();
+ for_each_console(tmp)
+ if (tmp == &pdc_cons)
+ break;
+ console_unlock();
+
+ if (!tmp) {
+ printk(KERN_INFO "PDC console driver not registered anymore, not creating %s\n", pdc_cons.name);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "The PDC console driver is still registered, removing CON_BOOT flag\n");
+ pdc_cons.flags &= ~CON_BOOT;
+
+ pdc_console_tty_driver = alloc_tty_driver(1);
+
+ if (!pdc_console_tty_driver)
+ return -ENOMEM;
+
+ tty_port_init(&tty_port);
+
+ pdc_console_tty_driver->driver_name = "pdc_cons";
+ pdc_console_tty_driver->name = "ttyB";
+ pdc_console_tty_driver->major = MUX_MAJOR;
+ pdc_console_tty_driver->minor_start = 0;
+ pdc_console_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM;
+ pdc_console_tty_driver->init_termios = tty_std_termios;
+ pdc_console_tty_driver->flags = TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_RESET_TERMIOS;
+ tty_set_operations(pdc_console_tty_driver, &pdc_console_tty_ops);
+ tty_port_link_device(&tty_port, pdc_console_tty_driver, 0);
+
+ err = tty_register_driver(pdc_console_tty_driver);
+ if (err) {
+ printk(KERN_ERR "Unable to register the PDC console TTY driver\n");
+ tty_port_destroy(&tty_port);
+ return err;
+ }
+
return 0;
}
-#if defined(CONFIG_PDC_CONSOLE)
+module_init(pdc_console_tty_driver_init);
+
static struct tty_driver * pdc_console_device (struct console *c, int *index)
{
- extern struct tty_driver console_driver;
- *index = c->index ? c->index-1 : fg_console;
- return &console_driver;
+ *index = c->index;
+ return pdc_console_tty_driver;
}
#else
#define pdc_console_device NULL
@@ -105,7 +224,7 @@ static struct console pdc_cons = {
.write = pdc_console_write,
.device = pdc_console_device,
.setup = pdc_console_setup,
- .flags = CON_BOOT | CON_PRINTBUFFER | CON_ENABLED,
+ .flags = CON_BOOT | CON_PRINTBUFFER,
.index = -1,
};
diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c
index 44670d6e06f..ba0c053e25a 100644
--- a/arch/parisc/kernel/perf.c
+++ b/arch/parisc/kernel/perf.c
@@ -24,7 +24,7 @@
*
* This driver programs the PCX-U/PCX-W performance counters
* on the PA-RISC 2.0 chips. The driver keeps all images now
- * internally to the kernel to hopefully eliminate the possiblity
+ * internally to the kernel to hopefully eliminate the possibility
* of a bad image halting the CPU. Also, there are different
* images for the PCX-W and later chips vs the PCX-U chips.
*
@@ -42,6 +42,7 @@
* on every box.
*/
+#include <linux/capability.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
@@ -66,21 +67,21 @@ struct rdr_tbl_ent {
uint8_t write_control;
};
-static int perf_processor_interface = UNKNOWN_INTF;
-static int perf_enabled = 0;
+static int perf_processor_interface __read_mostly = UNKNOWN_INTF;
+static int perf_enabled __read_mostly;
static spinlock_t perf_lock;
-struct parisc_device *cpu_device = NULL;
+struct parisc_device *cpu_device __read_mostly;
/* RDRs to write for PCX-W */
-static int perf_rdrs_W[] =
+static const int perf_rdrs_W[] =
{ 0, 1, 4, 5, 6, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, -1 };
/* RDRs to write for PCX-U */
-static int perf_rdrs_U[] =
+static const int perf_rdrs_U[] =
{ 0, 1, 4, 5, 6, 7, 16, 17, 18, 20, 21, 22, 23, 24, 25, -1 };
/* RDR register descriptions for PCX-W */
-static struct rdr_tbl_ent perf_rdr_tbl_W[] = {
+static const struct rdr_tbl_ent perf_rdr_tbl_W[] = {
{ 19, 1, 8 }, /* RDR 0 */
{ 16, 1, 16 }, /* RDR 1 */
{ 72, 2, 0 }, /* RDR 2 */
@@ -116,7 +117,7 @@ static struct rdr_tbl_ent perf_rdr_tbl_W[] = {
};
/* RDR register descriptions for PCX-U */
-static struct rdr_tbl_ent perf_rdr_tbl_U[] = {
+static const struct rdr_tbl_ent perf_rdr_tbl_U[] = {
{ 19, 1, 8 }, /* RDR 0 */
{ 32, 1, 16 }, /* RDR 1 */
{ 20, 1, 0 }, /* RDR 2 */
@@ -155,7 +156,7 @@ static struct rdr_tbl_ent perf_rdr_tbl_U[] = {
* A non-zero write_control in the above tables is a byte offset into
* this array.
*/
-static uint64_t perf_bitmasks[] = {
+static const uint64_t perf_bitmasks[] = {
0x0000000000000000ul, /* first dbl word must be zero */
0xfdffe00000000000ul, /* RDR0 bitmask */
0x003f000000000000ul, /* RDR1 bitmask */
@@ -170,9 +171,9 @@ static uint64_t perf_bitmasks[] = {
/*
* Write control bitmasks for Pa-8700 processor given
- * somethings have changed slightly.
+ * some things have changed slightly.
*/
-static uint64_t perf_bitmasks_piranha[] = {
+static const uint64_t perf_bitmasks_piranha[] = {
0x0000000000000000ul, /* first dbl word must be zero */
0xfdffe00000000000ul, /* RDR0 bitmask */
0x003f000000000000ul, /* RDR1 bitmask */
@@ -185,7 +186,7 @@ static uint64_t perf_bitmasks_piranha[] = {
0xfffc000000000000ul
};
-static uint64_t *bitmask_array; /* array of bitmasks to use */
+static const uint64_t *bitmask_array; /* array of bitmasks to use */
/******************************************************************************
* Function Prototypes
@@ -196,11 +197,10 @@ static int perf_open(struct inode *inode, struct file *file);
static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos);
static ssize_t perf_write(struct file *file, const char __user *buf, size_t count,
loff_t *ppos);
-static int perf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg);
+static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static void perf_start_counters(void);
static int perf_stop_counters(uint32_t *raddr);
-static struct rdr_tbl_ent * perf_rdr_get_entry(uint32_t rdr_num);
+static const struct rdr_tbl_ent * perf_rdr_get_entry(uint32_t rdr_num);
static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer);
static int perf_rdr_clear(uint32_t rdr_num);
static int perf_write_image(uint64_t *memaddr);
@@ -438,48 +438,53 @@ static void perf_patch_images(void)
* must be running on the processor that you wish to change.
*/
-static int perf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
+static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long error_start;
- uint32_t raddr[4];
+ uint32_t raddr[4];
+ int error = 0;
switch (cmd) {
case PA_PERF_ON:
/* Start the counters */
perf_start_counters();
- return 0;
+ break;
case PA_PERF_OFF:
error_start = perf_stop_counters(raddr);
if (error_start != 0) {
printk(KERN_ERR "perf_off: perf_stop_counters = %ld\n", error_start);
- return -EFAULT;
+ error = -EFAULT;
+ break;
}
/* copy out the Counters */
if (copy_to_user((void __user *)arg, raddr,
sizeof (raddr)) != 0) {
- return -EFAULT;
+ error = -EFAULT;
+ break;
}
- return 0;
+ break;
case PA_PERF_VERSION:
/* Return the version # */
- return put_user(PERF_VERSION, (int *)arg);
+ error = put_user(PERF_VERSION, (int *)arg);
+ break;
default:
- break;
+ error = -ENOTTY;
}
- return -ENOTTY;
+
+ return error;
}
-static struct file_operations perf_fops = {
+static const struct file_operations perf_fops = {
.llseek = no_llseek,
.read = perf_read,
.write = perf_write,
- .ioctl = perf_ioctl,
+ .unlocked_ioctl = perf_ioctl,
+ .compat_ioctl = perf_ioctl,
.open = perf_open,
.release = perf_release
};
@@ -506,10 +511,12 @@ static int __init perf_init(void)
} else if (boot_cpu_data.cpu_type == pcxw ||
boot_cpu_data.cpu_type == pcxw_ ||
boot_cpu_data.cpu_type == pcxw2 ||
- boot_cpu_data.cpu_type == mako) {
+ boot_cpu_data.cpu_type == mako ||
+ boot_cpu_data.cpu_type == mako2) {
perf_processor_interface = CUDA_INTF;
if (boot_cpu_data.cpu_type == pcxw2 ||
- boot_cpu_data.cpu_type == mako)
+ boot_cpu_data.cpu_type == mako ||
+ boot_cpu_data.cpu_type == mako2)
bitmask_array = perf_bitmasks_piranha;
} else {
perf_processor_interface = UNKNOWN_INTF;
@@ -530,9 +537,9 @@ static int __init perf_init(void)
spin_lock_init(&perf_lock);
/* TODO: this only lets us access the first cpu.. what to do for SMP? */
- cpu_device = cpu_data[0].dev;
+ cpu_device = per_cpu(cpu_data, 0).dev;
printk("Performance monitoring counters enabled for %s\n",
- cpu_data[0].dev->name);
+ per_cpu(cpu_data, 0).dev->name);
return 0;
}
@@ -569,27 +576,27 @@ static int perf_stop_counters(uint32_t *raddr)
if (!perf_rdr_read_ubuf(16, userbuf))
return -13;
- /* Counter0 is bits 1398 thru 1429 */
+ /* Counter0 is bits 1398 to 1429 */
tmp64 = (userbuf[21] << 22) & 0x00000000ffc00000;
tmp64 |= (userbuf[22] >> 42) & 0x00000000003fffff;
/* OR sticky0 (bit 1430) to counter0 bit 32 */
tmp64 |= (userbuf[22] >> 10) & 0x0000000080000000;
raddr[0] = (uint32_t)tmp64;
- /* Counter1 is bits 1431 thru 1462 */
+ /* Counter1 is bits 1431 to 1462 */
tmp64 = (userbuf[22] >> 9) & 0x00000000ffffffff;
/* OR sticky1 (bit 1463) to counter1 bit 32 */
tmp64 |= (userbuf[22] << 23) & 0x0000000080000000;
raddr[1] = (uint32_t)tmp64;
- /* Counter2 is bits 1464 thru 1495 */
+ /* Counter2 is bits 1464 to 1495 */
tmp64 = (userbuf[22] << 24) & 0x00000000ff000000;
tmp64 |= (userbuf[23] >> 40) & 0x0000000000ffffff;
/* OR sticky2 (bit 1496) to counter2 bit 32 */
tmp64 |= (userbuf[23] >> 8) & 0x0000000080000000;
raddr[2] = (uint32_t)tmp64;
- /* Counter3 is bits 1497 thru 1528 */
+ /* Counter3 is bits 1497 to 1528 */
tmp64 = (userbuf[23] >> 7) & 0x00000000ffffffff;
/* OR sticky3 (bit 1529) to counter3 bit 32 */
tmp64 |= (userbuf[23] << 25) & 0x0000000080000000;
@@ -611,7 +618,7 @@ static int perf_stop_counters(uint32_t *raddr)
userbuf[23] = 0;
/*
- * Write back the zero'ed bytes + the image given
+ * Write back the zeroed bytes + the image given
* the read was destructive.
*/
perf_rdr_write(16, userbuf);
@@ -647,7 +654,7 @@ static int perf_stop_counters(uint32_t *raddr)
* Retrieve a pointer to the description of what this
* RDR contains.
*/
-static struct rdr_tbl_ent * perf_rdr_get_entry(uint32_t rdr_num)
+static const struct rdr_tbl_ent * perf_rdr_get_entry(uint32_t rdr_num)
{
if (perf_processor_interface == ONYX_INTF) {
return &perf_rdr_tbl_U[rdr_num];
@@ -665,7 +672,7 @@ static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer)
{
uint64_t data, data_mask = 0;
uint32_t width, xbits, i;
- struct rdr_tbl_ent *tentry;
+ const struct rdr_tbl_ent *tentry;
tentry = perf_rdr_get_entry(rdr_num);
if ((width = tentry->width) == 0)
@@ -713,7 +720,7 @@ static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer)
*/
static int perf_rdr_clear(uint32_t rdr_num)
{
- struct rdr_tbl_ent *tentry;
+ const struct rdr_tbl_ent *tentry;
int32_t i;
tentry = perf_rdr_get_entry(rdr_num);
@@ -745,10 +752,11 @@ static int perf_write_image(uint64_t *memaddr)
uint64_t buffer[MAX_RDR_WORDS];
uint64_t *bptr;
uint32_t dwords;
- uint32_t *intrigue_rdr;
- uint64_t *intrigue_bitmask, tmp64;
+ const uint32_t *intrigue_rdr;
+ const uint64_t *intrigue_bitmask;
+ uint64_t tmp64;
void __iomem *runway;
- struct rdr_tbl_ent *tentry;
+ const struct rdr_tbl_ent *tentry;
int i;
/* Clear out counters */
@@ -799,7 +807,7 @@ static int perf_write_image(uint64_t *memaddr)
return -1;
}
- runway = ioremap(cpu_device->hpa.start, 4096);
+ runway = ioremap_nocache(cpu_device->hpa.start, 4096);
/* Merge intrigue bits into Runway STATUS 0 */
tmp64 = __raw_readq(runway + RUNWAY_STATUS) & 0xffecfffffffffffful;
@@ -822,7 +830,7 @@ static int perf_write_image(uint64_t *memaddr)
*/
static void perf_rdr_write(uint32_t rdr_num, uint64_t *buffer)
{
- struct rdr_tbl_ent *tentry;
+ const struct rdr_tbl_ent *tentry;
int32_t i;
printk("perf_rdr_write\n");
diff --git a/arch/parisc/kernel/perf_asm.S b/arch/parisc/kernel/perf_asm.S
index adb3c644491..fa6ea99bb32 100644
--- a/arch/parisc/kernel/perf_asm.S
+++ b/arch/parisc/kernel/perf_asm.S
@@ -19,9 +19,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/config.h>
#include <asm/assembly.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+
#ifdef CONFIG_64BIT
.level 2.0w
#endif /* CONFIG_64BIT */
@@ -42,10 +44,8 @@
; starting/stopping the coprocessor with the pmenb/pmdis.
;
.text
- .align 32
- .export perf_intrigue_enable_perf_counters,code
-perf_intrigue_enable_perf_counters:
+ENTRY(perf_intrigue_enable_perf_counters)
.proc
.callinfo frame=0,NO_CALLS
.entry
@@ -70,9 +70,9 @@ perf_intrigue_enable_perf_counters:
nop
.exit
.procend
+ENDPROC(perf_intrigue_enable_perf_counters)
- .export perf_intrigue_disable_perf_counters,code
-perf_intrigue_disable_perf_counters:
+ENTRY(perf_intrigue_disable_perf_counters)
.proc
.callinfo frame=0,NO_CALLS
.entry
@@ -87,6 +87,7 @@ perf_intrigue_disable_perf_counters:
mtctl %r26,ccr ; turn off performance coprocessor
.exit
.procend
+ENDPROC(perf_intrigue_disable_perf_counters)
;***********************************************************************
;*
@@ -118,8 +119,7 @@ perf_intrigue_disable_perf_counters:
;*
;***********************************************************************
- .export perf_rdr_shift_in_W,code
-perf_rdr_shift_in_W:
+ENTRY(perf_rdr_shift_in_W)
.proc
.callinfo frame=0,NO_CALLS
.entry
@@ -551,6 +551,7 @@ perf_rdr_shift_in_W_leave:
.exit
MTDIAG_2 (24) ; restore DR2
.procend
+ENDPROC(perf_rdr_shift_in_W)
;***********************************************************************
@@ -576,8 +577,7 @@ perf_rdr_shift_in_W_leave:
;*
;***********************************************************************
- .export perf_rdr_shift_out_W,code
-perf_rdr_shift_out_W:
+ENTRY(perf_rdr_shift_out_W)
.proc
.callinfo frame=0,NO_CALLS
.entry
@@ -984,6 +984,7 @@ perf_rdr_shift_out_W_leave:
.exit
MTDIAG_2 (23) ; restore DR2
.procend
+ENDPROC(perf_rdr_shift_out_W)
;***********************************************************************
@@ -1013,8 +1014,7 @@ perf_rdr_shift_out_W_leave:
;*
;***********************************************************************
- .export perf_rdr_shift_in_U,code
-perf_rdr_shift_in_U:
+ENTRY(perf_rdr_shift_in_U)
.proc
.callinfo frame=0,NO_CALLS
.entry
@@ -1344,6 +1344,7 @@ perf_rdr_shift_in_U_leave:
.exit
MTDIAG_2 (24) ; restore DR2
.procend
+ENDPROC(perf_rdr_shift_in_U)
;***********************************************************************
;*
@@ -1370,8 +1371,7 @@ perf_rdr_shift_in_U_leave:
;*
;***********************************************************************
- .export perf_rdr_shift_out_U,code
-perf_rdr_shift_out_U:
+ENTRY(perf_rdr_shift_out_U)
.proc
.callinfo frame=0,NO_CALLS
.entry
@@ -1688,4 +1688,5 @@ perf_rdr_shift_out_U_leave:
.exit
MTDIAG_2 (23) ; restore DR2
.procend
+ENDPROC(perf_rdr_shift_out_U)
diff --git a/arch/parisc/kernel/perf_images.h b/arch/parisc/kernel/perf_images.h
index d9562fe3f75..7fef9644df4 100644
--- a/arch/parisc/kernel/perf_images.h
+++ b/arch/parisc/kernel/perf_images.h
@@ -25,7 +25,7 @@
#define PCXU_IMAGE_SIZE 584
-static uint32_t onyx_images[][PCXU_IMAGE_SIZE/sizeof(uint32_t)] = {
+static uint32_t onyx_images[][PCXU_IMAGE_SIZE/sizeof(uint32_t)] __read_mostly = {
/*
* CPI:
*
@@ -2093,7 +2093,7 @@ static uint32_t onyx_images[][PCXU_IMAGE_SIZE/sizeof(uint32_t)] = {
};
#define PCXW_IMAGE_SIZE 576
-static uint32_t cuda_images[][PCXW_IMAGE_SIZE/sizeof(uint32_t)] = {
+static uint32_t cuda_images[][PCXW_IMAGE_SIZE/sizeof(uint32_t)] __read_mostly = {
/*
* CPI: FROM CPI.IDF (Image 0)
*
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index 7fdca87ef64..0bbbf0d3f60 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -13,7 +13,7 @@
* Copyright (C) 2000 Grant Grundler <grundler with parisc-linux.org>
* Copyright (C) 2001 Alan Modra <amodra at parisc-linux.org>
* Copyright (C) 2001-2002 Ryan Bradetich <rbrad at parisc-linux.org>
- * Copyright (C) 2001-2002 Helge Deller <deller at parisc-linux.org>
+ * Copyright (C) 2001-2014 Helge Deller <deller@gmx.de>
* Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org>
*
*
@@ -38,72 +38,29 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/fs.h>
#include <linux/module.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/kallsyms.h>
+#include <linux/uaccess.h>
+#include <linux/rcupdate.h>
+#include <linux/random.h>
#include <asm/io.h>
#include <asm/asm-offsets.h>
+#include <asm/assembly.h>
#include <asm/pdc.h>
#include <asm/pdc_chassis.h>
#include <asm/pgalloc.h>
-#include <asm/uaccess.h>
#include <asm/unwind.h>
+#include <asm/sections.h>
-static int hlt_counter;
-
-/*
- * Power off function, if any
- */
-void (*pm_power_off)(void);
-
-void disable_hlt(void)
-{
- hlt_counter++;
-}
-
-EXPORT_SYMBOL(disable_hlt);
-
-void enable_hlt(void)
-{
- hlt_counter--;
-}
-
-EXPORT_SYMBOL(enable_hlt);
-
-void default_idle(void)
-{
- barrier();
-}
-
-/*
- * The idle thread. There's no useful work to be
- * done, so just try to conserve power and have a
- * low exit latency (ie sit in a loop waiting for
- * somebody to say that they'd like to reschedule)
- */
-void cpu_idle(void)
-{
- /* endless idle loop with no priority at all */
- while (1) {
- while (!need_resched())
- barrier();
- schedule();
- check_pgt_cache();
- }
-}
-
-
-#ifdef __LP64__
-#define COMMAND_GLOBAL 0xfffffffffffe0030UL
-#else
-#define COMMAND_GLOBAL 0xfffe0030
-#endif
-
+#define COMMAND_GLOBAL F_EXTEND(0xfffe0030)
#define CMD_RESET 5 /* reset any module */
/*
@@ -158,6 +115,7 @@ void machine_halt(void)
*/
}
+void (*chassis_power_off)(void);
/*
* This routine is called from sys_reboot to actually turn off the
@@ -166,8 +124,8 @@ void machine_halt(void)
void machine_power_off(void)
{
/* If there is a registered power off handler, call it. */
- if(pm_power_off)
- pm_power_off();
+ if (chassis_power_off)
+ chassis_power_off();
/* Put the soft power button back under hardware control.
* If the user had already pressed the power button, the
@@ -180,26 +138,11 @@ void machine_power_off(void)
* software. The user has to press the button himself. */
printk(KERN_EMERG "System shut down completed.\n"
- KERN_EMERG "Please power this system off now.");
+ "Please power this system off now.");
}
-
-/*
- * Create a kernel thread
- */
-
-extern pid_t __kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
-pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
-{
-
- /*
- * FIXME: Once we are sure we don't need any debug here,
- * kernel_thread can become a #define.
- */
-
- return __kernel_thread(fn, arg, flags);
-}
-EXPORT_SYMBOL(kernel_thread);
+void (*pm_power_off)(void) = machine_power_off;
+EXPORT_SYMBOL(pm_power_off);
/*
* Free current thread data structures etc..
@@ -213,7 +156,6 @@ void flush_thread(void)
/* Only needs to handle fpu stuff or perf monitors.
** REVISIT: several arches implement a "lazy fpu state".
*/
- set_fs(USER_DS);
}
void release_thread(struct task_struct *dead_task)
@@ -239,59 +181,12 @@ int dump_task_fpu (struct task_struct *tsk, elf_fpregset_t *r)
return 1;
}
-/* Note that "fork()" is implemented in terms of clone, with
- parameters (SIGCHLD, regs->gr[30], regs). */
int
-sys_clone(unsigned long clone_flags, unsigned long usp,
- struct pt_regs *regs)
+copy_thread(unsigned long clone_flags, unsigned long usp,
+ unsigned long arg, struct task_struct *p)
{
- /* Arugments from userspace are:
- r26 = Clone flags.
- r25 = Child stack.
- r24 = parent_tidptr.
- r23 = Is the TLS storage descriptor
- r22 = child_tidptr
-
- However, these last 3 args are only examined
- if the proper flags are set. */
- int __user *child_tidptr;
- int __user *parent_tidptr;
-
- /* usp must be word aligned. This also prevents users from
- * passing in the value 1 (which is the signal for a special
- * return for a kernel thread) */
- usp = ALIGN(usp, 4);
-
- /* A zero value for usp means use the current stack */
- if (usp == 0)
- usp = regs->gr[30];
-
- if (clone_flags & CLONE_PARENT_SETTID)
- parent_tidptr = (int __user *)regs->gr[24];
- else
- parent_tidptr = NULL;
-
- if (clone_flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID))
- child_tidptr = (int __user *)regs->gr[22];
- else
- child_tidptr = NULL;
-
- return do_fork(clone_flags, usp, regs, 0, parent_tidptr, child_tidptr);
-}
-
-int
-sys_vfork(struct pt_regs *regs)
-{
- return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gr[30], regs, 0, NULL, NULL);
-}
-
-int
-copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
- unsigned long unused, /* in ia64 this is "user_stack_size" */
- struct task_struct * p, struct pt_regs * pregs)
-{
- struct pt_regs * cregs = &(p->thread.regs);
- struct thread_info *ti = p->thread_info;
+ struct pt_regs *cregs = &(p->thread.regs);
+ void *stack = task_stack_page(p);
/* We have to use void * instead of a function pointer, because
* function pointers aren't a pointer to the function on 64-bit.
@@ -301,49 +196,40 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
#ifdef CONFIG_HPUX
extern void * const hpux_child_return;
#endif
+ if (unlikely(p->flags & PF_KTHREAD)) {
+ memset(cregs, 0, sizeof(struct pt_regs));
+ if (!usp) /* idle thread */
+ return 0;
- *cregs = *pregs;
-
- /* Set the return value for the child. Note that this is not
- actually restored by the syscall exit path, but we put it
- here for consistency in case of signals. */
- cregs->gr[28] = 0; /* child */
-
- /*
- * We need to differentiate between a user fork and a
- * kernel fork. We can't use user_mode, because the
- * the syscall path doesn't save iaoq. Right now
- * We rely on the fact that kernel_thread passes
- * in zero for usp.
- */
- if (usp == 1) {
/* kernel thread */
- cregs->ksp = (((unsigned long)(ti)) + THREAD_SZ_ALGN);
/* Must exit via ret_from_kernel_thread in order
* to call schedule_tail()
*/
+ cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE;
cregs->kpc = (unsigned long) &ret_from_kernel_thread;
/*
* Copy function and argument to be called from
* ret_from_kernel_thread.
*/
-#ifdef __LP64__
- cregs->gr[27] = pregs->gr[27];
+#ifdef CONFIG_64BIT
+ cregs->gr[27] = ((unsigned long *)usp)[3];
+ cregs->gr[26] = ((unsigned long *)usp)[2];
+#else
+ cregs->gr[26] = usp;
#endif
- cregs->gr[26] = pregs->gr[26];
- cregs->gr[25] = pregs->gr[25];
+ cregs->gr[25] = arg;
} else {
/* user thread */
- /*
- * Note that the fork wrappers are responsible
- * for setting gr[21].
- */
-
- /* Use same stack depth as parent */
- cregs->ksp = ((unsigned long)(ti))
- + (pregs->gr[21] & (THREAD_SIZE - 1));
- cregs->gr[30] = usp;
- if (p->personality == PER_HPUX) {
+ /* usp must be word aligned. This also prevents users from
+ * passing in the value 1 (which is the signal for a special
+ * return for a kernel thread) */
+ if (usp) {
+ usp = ALIGN(usp, 4);
+ if (likely(usp))
+ cregs->gr[30] = usp;
+ }
+ cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE;
+ if (personality(p->personality) == PER_HPUX) {
#ifdef CONFIG_HPUX
cregs->kpc = (unsigned long) &hpux_child_return;
#else
@@ -354,8 +240,7 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
}
/* Setup thread TLS area from the 4th parameter in clone */
if (clone_flags & CLONE_SETTLS)
- cregs->cr27 = pregs->gr[23];
-
+ cregs->cr27 = cregs->gr[23];
}
return 0;
@@ -366,38 +251,16 @@ unsigned long thread_saved_pc(struct task_struct *t)
return t->thread.regs.kpc;
}
-/*
- * sys_execve() executes a new program.
- */
-
-asmlinkage int sys_execve(struct pt_regs *regs)
-{
- int error;
- char *filename;
-
- filename = getname((const char __user *) regs->gr[26]);
- error = PTR_ERR(filename);
- if (IS_ERR(filename))
- goto out;
- error = do_execve(filename, (char __user **) regs->gr[25],
- (char __user **) regs->gr[24], regs);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
- }
- putname(filename);
-out:
-
- return error;
-}
-
-unsigned long
+unsigned long
get_wchan(struct task_struct *p)
{
struct unwind_frame_info info;
unsigned long ip;
int count = 0;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
/*
* These bracket the sleeping functions..
*/
@@ -412,3 +275,33 @@ get_wchan(struct task_struct *p)
} while (count++ < 16);
return 0;
}
+
+#ifdef CONFIG_64BIT
+void *dereference_function_descriptor(void *ptr)
+{
+ Elf64_Fdesc *desc = ptr;
+ void *p;
+
+ if (!probe_kernel_address(&desc->addr, p))
+ ptr = p;
+ return ptr;
+}
+#endif
+
+static inline unsigned long brk_rnd(void)
+{
+ /* 8MB for 32bit, 1GB for 64bit */
+ if (is_32bit_task())
+ return (get_random_int() & 0x7ffUL) << PAGE_SHIFT;
+ else
+ return (get_random_int() & 0x3ffffUL) << PAGE_SHIFT;
+}
+
+unsigned long arch_randomize_brk(struct mm_struct *mm)
+{
+ unsigned long ret = PAGE_ALIGN(mm->brk + brk_rnd());
+
+ if (ret < mm->brk)
+ return mm->brk;
+ return ret;
+}
diff --git a/arch/parisc/kernel/processor.c b/arch/parisc/kernel/processor.c
index 4f5bbcf1f5a..b68d977ce30 100644
--- a/arch/parisc/kernel/processor.c
+++ b/arch/parisc/kernel/processor.c
@@ -1,9 +1,8 @@
-/* $Id: processor.c,v 1.1 2002/07/20 16:27:06 rhirst Exp $
- *
+/*
* Initial setup-routines for HP 9000 based hardware.
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
- * Modifications for PA-RISC (C) 1999 Helge Deller <deller@gmx.de>
+ * Modifications for PA-RISC (C) 1999-2008 Helge Deller <deller@gmx.de>
* Modifications copyright 1999 SuSE GmbH (Philipp Rumpf)
* Modifications copyright 2000 Martin K. Petersen <mkp@mkp.net>
* Modifications copyright 2000 Philipp Rumpf <prumpf@tux.org>
@@ -26,7 +25,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
-#include <linux/config.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/mm.h>
@@ -34,7 +32,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/cpu.h>
-
+#include <asm/param.h>
#include <asm/cache.h>
#include <asm/hardware.h> /* for register_parisc_driver() stuff */
#include <asm/processor.h>
@@ -44,10 +42,12 @@
#include <asm/irq.h> /* for struct irq_region */
#include <asm/parisc-device.h>
-struct system_cpuinfo_parisc boot_cpu_data;
+struct system_cpuinfo_parisc boot_cpu_data __read_mostly;
EXPORT_SYMBOL(boot_cpu_data);
-struct cpuinfo_parisc cpu_data[NR_CPUS];
+DEFINE_PER_CPU(struct cpuinfo_parisc, cpu_data);
+
+extern int update_cr16_clocksource(void); /* from time.c */
/*
** PARISC CPU driver - claim "device" and initialize CPU data structures.
@@ -62,12 +62,29 @@ struct cpuinfo_parisc cpu_data[NR_CPUS];
** will call register_parisc_driver(&cpu_driver) before calling do_inventory().
**
** The goal of consolidating CPU initialization into one place is
-** to make sure all CPU's get initialized the same way.
+** to make sure all CPUs get initialized the same way.
** The code path not shared is how PDC hands control of the CPU to the OS.
** The initialization of OS data structures is the same (done below).
*/
/**
+ * init_cpu_profiler - enable/setup per cpu profiling hooks.
+ * @cpunum: The processor instance.
+ *
+ * FIXME: doesn't do much yet...
+ */
+static void
+init_percpu_prof(unsigned long cpunum)
+{
+ struct cpuinfo_parisc *p;
+
+ p = &per_cpu(cpu_data, cpunum);
+ p->prof_counter = 1;
+ p->prof_multiplier = 1;
+}
+
+
+/**
* processor_probe - Determine if processor driver should claim this device.
* @dev: The device which has been found.
*
@@ -75,13 +92,18 @@ struct cpuinfo_parisc cpu_data[NR_CPUS];
* (return 1). If so, initialize the chip and tell other partners in crime
* they have work to do.
*/
-static int __init processor_probe(struct parisc_device *dev)
+static int processor_probe(struct parisc_device *dev)
{
unsigned long txn_addr;
unsigned long cpuid;
struct cpuinfo_parisc *p;
-#ifndef CONFIG_SMP
+#ifdef CONFIG_SMP
+ if (num_online_cpus() >= nr_cpu_ids) {
+ printk(KERN_INFO "num_online_cpus() >= nr_cpu_ids\n");
+ return 1;
+ }
+#else
if (boot_cpu_data.cpu_count > 0) {
printk(KERN_INFO "CONFIG_SMP=n ignoring additional CPUs\n");
return 1;
@@ -94,26 +116,32 @@ static int __init processor_probe(struct parisc_device *dev)
cpuid = boot_cpu_data.cpu_count;
txn_addr = dev->hpa.start; /* for legacy PDC */
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
if (is_pdc_pat()) {
ulong status;
unsigned long bytecnt;
- pdc_pat_cell_mod_maddr_block_t pa_pdc_cell;
+ pdc_pat_cell_mod_maddr_block_t *pa_pdc_cell;
#undef USE_PAT_CPUID
#ifdef USE_PAT_CPUID
struct pdc_pat_cpu_num cpu_info;
#endif
+ pa_pdc_cell = kmalloc(sizeof (*pa_pdc_cell), GFP_KERNEL);
+ if (!pa_pdc_cell)
+ panic("couldn't allocate memory for PDC_PAT_CELL!");
+
status = pdc_pat_cell_module(&bytecnt, dev->pcell_loc,
- dev->mod_index, PA_VIEW, &pa_pdc_cell);
+ dev->mod_index, PA_VIEW, pa_pdc_cell);
BUG_ON(PDC_OK != status);
/* verify it's the same as what do_pat_inventory() found */
- BUG_ON(dev->mod_info != pa_pdc_cell.mod_info);
- BUG_ON(dev->pmod_loc != pa_pdc_cell.mod_location);
+ BUG_ON(dev->mod_info != pa_pdc_cell->mod_info);
+ BUG_ON(dev->pmod_loc != pa_pdc_cell->mod_location);
+
+ txn_addr = pa_pdc_cell->mod[0]; /* id_eid for IO sapic */
- txn_addr = pa_pdc_cell.mod[0]; /* id_eid for IO sapic */
+ kfree(pa_pdc_cell);
#ifdef USE_PAT_CPUID
/* We need contiguous numbers for cpuid. Firmware's notion
@@ -141,11 +169,12 @@ static int __init processor_probe(struct parisc_device *dev)
}
#endif
- p = &cpu_data[cpuid];
+ p = &per_cpu(cpu_data, cpuid);
boot_cpu_data.cpu_count++;
- /* initialize counters */
- memset(p, 0, sizeof(struct cpuinfo_parisc));
+ /* initialize counters - CPU 0 gets it_value set in time_init() */
+ if (cpuid)
+ memset(p, 0, sizeof(struct cpuinfo_parisc));
p->loops_per_jiffy = loops_per_jiffy;
p->dev = dev; /* Save IODC data in case we need it */
@@ -153,20 +182,15 @@ static int __init processor_probe(struct parisc_device *dev)
p->cpuid = cpuid; /* save CPU id */
p->txn_addr = txn_addr; /* save CPU IRQ address */
#ifdef CONFIG_SMP
- spin_lock_init(&p->lock);
-
/*
** FIXME: review if any other initialization is clobbered
- ** for boot_cpu by the above memset().
+ ** for boot_cpu by the above memset().
*/
-
- /* stolen from init_percpu_prof() */
- cpu_data[cpuid].prof_counter = 1;
- cpu_data[cpuid].prof_multiplier = 1;
+ init_percpu_prof(cpuid);
#endif
/*
- ** CONFIG_SMP: init_smp_config() will attempt to get CPU's into
+ ** CONFIG_SMP: init_smp_config() will attempt to get CPUs into
** OS control. RENDEZVOUS is the default state - see mem_set above.
** p->state = STATE_RENDEZVOUS;
*/
@@ -195,11 +219,17 @@ static int __init processor_probe(struct parisc_device *dev)
*/
#ifdef CONFIG_SMP
if (cpuid) {
- cpu_set(cpuid, cpu_present_map);
+ set_cpu_present(cpuid, true);
cpu_up(cpuid);
}
#endif
+ /* If we've registered more than one cpu,
+ * we'll use the jiffies clocksource since cr16
+ * is not synchronized between CPUs.
+ */
+ update_cr16_clocksource();
+
return 0;
}
@@ -250,19 +280,6 @@ void __init collect_boot_cpu_data(void)
}
-/**
- * init_cpu_profiler - enable/setup per cpu profiling hooks.
- * @cpunum: The processor instance.
- *
- * FIXME: doesn't do much yet...
- */
-static inline void __init
-init_percpu_prof(int cpunum)
-{
- cpu_data[cpunum].prof_counter = 1;
- cpu_data[cpunum].prof_multiplier = 1;
-}
-
/**
* init_per_cpu - Handle individual processor initializations.
@@ -282,7 +299,7 @@ init_percpu_prof(int cpunum)
*
* o Enable CPU profiling hooks.
*/
-int __init init_per_cpu(int cpunum)
+int init_per_cpu(int cpunum)
{
int ret;
struct pdc_coproc_cfg coproc_cfg;
@@ -296,8 +313,8 @@ int __init init_per_cpu(int cpunum)
/* FWIW, FP rev/model is a more accurate way to determine
** CPU type. CPU rev/model has some ambiguous cases.
*/
- cpu_data[cpunum].fp_rev = coproc_cfg.revision;
- cpu_data[cpunum].fp_model = coproc_cfg.model;
+ per_cpu(cpu_data, cpunum).fp_rev = coproc_cfg.revision;
+ per_cpu(cpu_data, cpunum).fp_model = coproc_cfg.model;
printk(KERN_INFO "FP[%d] enabled: Rev %ld Model %ld\n",
cpunum, coproc_cfg.revision, coproc_cfg.model);
@@ -311,11 +328,11 @@ int __init init_per_cpu(int cpunum)
} else {
printk(KERN_WARNING "WARNING: No FP CoProcessor?!"
" (coproc_cfg.ccr_functional == 0x%lx, expected 0xc0)\n"
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
"Halting Machine - FP required\n"
#endif
, coproc_cfg.ccr_functional);
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
mdelay(100); /* previous chars get pushed to console */
panic("FP CoProc not reported");
#endif
@@ -328,24 +345,22 @@ int __init init_per_cpu(int cpunum)
}
/*
- * Display cpu info for all cpu's.
+ * Display CPU info for all CPUs.
*/
int
show_cpuinfo (struct seq_file *m, void *v)
{
- int n;
+ unsigned long cpu;
- for(n=0; n<boot_cpu_data.cpu_count; n++) {
+ for_each_online_cpu(cpu) {
+ const struct cpuinfo_parisc *cpuinfo = &per_cpu(cpu_data, cpu);
#ifdef CONFIG_SMP
- if (0 == cpu_data[n].hpa)
+ if (0 == cpuinfo->hpa)
continue;
-#ifdef ENTRY_SYS_CPUS
-#error iCOD support wants to show CPU state here
-#endif
#endif
- seq_printf(m, "processor\t: %d\n"
+ seq_printf(m, "processor\t: %lu\n"
"cpu family\t: PA-RISC %s\n",
- n, boot_cpu_data.family_name);
+ cpu, boot_cpu_data.family_name);
seq_printf(m, "cpu\t\t: %s\n", boot_cpu_data.cpu_name );
@@ -354,11 +369,31 @@ show_cpuinfo (struct seq_file *m, void *v)
boot_cpu_data.cpu_hz / 1000000,
boot_cpu_data.cpu_hz % 1000000 );
+ seq_printf(m, "capabilities\t:");
+ if (boot_cpu_data.pdc.capabilities & PDC_MODEL_OS32)
+ seq_puts(m, " os32");
+ if (boot_cpu_data.pdc.capabilities & PDC_MODEL_OS64)
+ seq_puts(m, " os64");
+ if (boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC)
+ seq_puts(m, " iopdir_fdc");
+ switch (boot_cpu_data.pdc.capabilities & PDC_MODEL_NVA_MASK) {
+ case PDC_MODEL_NVA_SUPPORTED:
+ seq_puts(m, " nva_supported");
+ break;
+ case PDC_MODEL_NVA_SLOW:
+ seq_puts(m, " nva_slow");
+ break;
+ case PDC_MODEL_NVA_UNSUPPORTED:
+ seq_puts(m, " needs_equivalent_aliasing");
+ break;
+ }
+ seq_printf(m, " (0x%02lx)\n", boot_cpu_data.pdc.capabilities);
+
seq_printf(m, "model\t\t: %s\n"
"model name\t: %s\n",
boot_cpu_data.pdc.sys_model_name,
- cpu_data[n].dev ?
- cpu_data[n].dev->name : "Unknown" );
+ cpuinfo->dev ?
+ cpuinfo->dev->name : "Unknown");
seq_printf(m, "hversion\t: 0x%08x\n"
"sversion\t: 0x%08x\n",
@@ -369,8 +404,8 @@ show_cpuinfo (struct seq_file *m, void *v)
show_cache_info(m);
seq_printf(m, "bogomips\t: %lu.%02lu\n",
- cpu_data[n].loops_per_jiffy / (500000 / HZ),
- (cpu_data[n].loops_per_jiffy / (5000 / HZ)) % 100);
+ cpuinfo->loops_per_jiffy / (500000 / HZ),
+ (cpuinfo->loops_per_jiffy / (5000 / HZ)) % 100);
seq_printf(m, "software id\t: %ld\n\n",
boot_cpu_data.pdc.model.sw_id);
@@ -378,7 +413,7 @@ show_cpuinfo (struct seq_file *m, void *v)
return 0;
}
-static struct parisc_device_id processor_tbl[] = {
+static const struct parisc_device_id processor_tbl[] = {
{ HPHW_NPROC, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, SVERSION_ANY_ID },
{ 0, }
};
@@ -390,7 +425,7 @@ static struct parisc_driver cpu_driver = {
};
/**
- * processor_init - Processor initalization procedure.
+ * processor_init - Processor initialization procedure.
*
* Register this driver.
*/
diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
index b6fe202a620..e842ee233db 100644
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -4,39 +4,176 @@
* Copyright (C) 2000 Hewlett-Packard Co, Linuxcare Inc.
* Copyright (C) 2000 Matthew Wilcox <matthew@wil.cx>
* Copyright (C) 2000 David Huggins-Daines <dhd@debian.org>
+ * Copyright (C) 2008 Helge Deller <deller@gmx.de>
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/user.h>
#include <linux/personality.h>
#include <linux/security.h>
#include <linux/compat.h>
#include <linux/signal.h>
+#include <linux/audit.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include <asm/processor.h>
#include <asm/asm-offsets.h>
/* PSW bits we allow the debugger to modify */
-#define USER_PSW_BITS (PSW_N | PSW_V | PSW_CB)
+#define USER_PSW_BITS (PSW_N | PSW_B | PSW_V | PSW_CB)
-#undef DEBUG_PTRACE
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *task)
+{
+ clear_tsk_thread_flag(task, TIF_SINGLESTEP);
+ clear_tsk_thread_flag(task, TIF_BLOCKSTEP);
+
+ /* make sure the trap bits are not set */
+ pa_psw(task)->r = 0;
+ pa_psw(task)->t = 0;
+ pa_psw(task)->h = 0;
+ pa_psw(task)->l = 0;
+}
+
+/*
+ * The following functions are called by ptrace_resume() when
+ * enabling or disabling single/block tracing.
+ */
+void user_disable_single_step(struct task_struct *task)
+{
+ ptrace_disable(task);
+}
+
+void user_enable_single_step(struct task_struct *task)
+{
+ clear_tsk_thread_flag(task, TIF_BLOCKSTEP);
+ set_tsk_thread_flag(task, TIF_SINGLESTEP);
+
+ if (pa_psw(task)->n) {
+ struct siginfo si;
+
+ /* Nullified, just crank over the queue. */
+ task_regs(task)->iaoq[0] = task_regs(task)->iaoq[1];
+ task_regs(task)->iasq[0] = task_regs(task)->iasq[1];
+ task_regs(task)->iaoq[1] = task_regs(task)->iaoq[0] + 4;
+ pa_psw(task)->n = 0;
+ pa_psw(task)->x = 0;
+ pa_psw(task)->y = 0;
+ pa_psw(task)->z = 0;
+ pa_psw(task)->b = 0;
+ ptrace_disable(task);
+ /* Don't wake up the task, but let the
+ parent know something happened. */
+ si.si_code = TRAP_TRACE;
+ si.si_addr = (void __user *) (task_regs(task)->iaoq[0] & ~3);
+ si.si_signo = SIGTRAP;
+ si.si_errno = 0;
+ force_sig_info(SIGTRAP, &si, task);
+ /* notify_parent(task, SIGCHLD); */
+ return;
+ }
+
+ /* Enable recovery counter traps. The recovery counter
+ * itself will be set to zero on a task switch. If the
+ * task is suspended on a syscall then the syscall return
+ * path will overwrite the recovery counter with a suitable
+ * value such that it traps once back in user space. We
+ * disable interrupts in the tasks PSW here also, to avoid
+ * interrupts while the recovery counter is decrementing.
+ */
+ pa_psw(task)->r = 1;
+ pa_psw(task)->t = 0;
+ pa_psw(task)->h = 0;
+ pa_psw(task)->l = 0;
+}
+
+void user_enable_block_step(struct task_struct *task)
+{
+ clear_tsk_thread_flag(task, TIF_SINGLESTEP);
+ set_tsk_thread_flag(task, TIF_BLOCKSTEP);
+
+ /* Enable taken branch trap. */
+ pa_psw(task)->r = 0;
+ pa_psw(task)->t = 1;
+ pa_psw(task)->h = 0;
+ pa_psw(task)->l = 0;
+}
+
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
+{
+ unsigned long tmp;
+ long ret = -EIO;
+
+ switch (request) {
+
+ /* Read the word at location addr in the USER area. For ptraced
+ processes, the kernel saves all regs on a syscall. */
+ case PTRACE_PEEKUSR:
+ if ((addr & (sizeof(unsigned long)-1)) ||
+ addr >= sizeof(struct pt_regs))
+ break;
+ tmp = *(unsigned long *) ((char *) task_regs(child) + addr);
+ ret = put_user(tmp, (unsigned long __user *) data);
+ break;
+
+ /* Write the word at location addr in the USER area. This will need
+ to change when the kernel no longer saves all regs on a syscall.
+ FIXME. There is a problem at the moment in that r3-r18 are only
+ saved if the process is ptraced on syscall entry, and even then
+ those values are overwritten by actual register values on syscall
+ exit. */
+ case PTRACE_POKEUSR:
+ /* Some register values written here may be ignored in
+ * entry.S:syscall_restore_rfi; e.g. iaoq is written with
+ * r31/r31+4, and not with the values in pt_regs.
+ */
+ if (addr == PT_PSW) {
+ /* Allow writing to Nullify, Divide-step-correction,
+ * and carry/borrow bits.
+ * BEWARE, if you set N, and then single step, it won't
+ * stop on the nullified instruction.
+ */
+ data &= USER_PSW_BITS;
+ task_regs(child)->gr[0] &= ~USER_PSW_BITS;
+ task_regs(child)->gr[0] |= data;
+ ret = 0;
+ break;
+ }
+
+ if ((addr & (sizeof(unsigned long)-1)) ||
+ addr >= sizeof(struct pt_regs))
+ break;
+ if ((addr >= PT_GR1 && addr <= PT_GR31) ||
+ addr == PT_IAOQ0 || addr == PT_IAOQ1 ||
+ (addr >= PT_FR0 && addr <= PT_FR31 + 4) ||
+ addr == PT_SAR) {
+ *(unsigned long *) ((char *) task_regs(child) + addr) = data;
+ ret = 0;
+ }
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ return ret;
+}
-#ifdef DEBUG_PTRACE
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
-#ifdef __LP64__
+#ifdef CONFIG_COMPAT
/* This function is needed to translate 32 bit pt_regs offsets in to
* 64 bit pt_regs offsets. For example, a 32 bit gdb under a 64 bit kernel
@@ -49,10 +186,10 @@
* being 64 bit in both cases.
*/
-static long translate_usr_offset(long offset)
+static compat_ulong_t translate_usr_offset(compat_ulong_t offset)
{
if (offset < 0)
- return -1;
+ return sizeof(struct pt_regs);
else if (offset <= 32*4) /* gr[0..31] */
return offset * 2 + 4;
else if (offset <= 32*4+32*8) /* gr[0..31] + fr[0..31] */
@@ -60,117 +197,27 @@ static long translate_usr_offset(long offset)
else if (offset < sizeof(struct pt_regs)/2 + 32*4)
return offset * 2 + 4 - 32*8;
else
- return -1;
-}
-#endif
-
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
- */
-void ptrace_disable(struct task_struct *child)
-{
- /* make sure the trap bits are not set */
- pa_psw(child)->r = 0;
- pa_psw(child)->t = 0;
- pa_psw(child)->h = 0;
- pa_psw(child)->l = 0;
+ return sizeof(struct pt_regs);
}
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
+ compat_ulong_t addr, compat_ulong_t data)
{
- long ret;
-#ifdef DEBUG_PTRACE
- long oaddr=addr, odata=data;
-#endif
+ compat_uint_t tmp;
+ long ret = -EIO;
switch (request) {
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- int copied;
-
-#ifdef __LP64__
- if (is_compat_task(child)) {
- unsigned int tmp;
-
- addr &= 0xffffffffL;
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- goto out_tsk;
- ret = put_user(tmp,(unsigned int *) data);
- DBG("sys_ptrace(PEEK%s, %d, %lx, %lx) returning %ld, data %x\n",
- request == PTRACE_PEEKTEXT ? "TEXT" : "DATA",
- pid, oaddr, odata, ret, tmp);
- }
- else
-#endif
- {
- unsigned long tmp;
-
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- goto out_tsk;
- ret = put_user(tmp,(unsigned long *) data);
- }
- goto out_tsk;
- }
- /* when I and D space are separate, this will have to be fixed. */
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA:
- ret = 0;
-#ifdef __LP64__
- if (is_compat_task(child)) {
- unsigned int tmp = (unsigned int)data;
- DBG("sys_ptrace(POKE%s, %d, %lx, %lx)\n",
- request == PTRACE_POKETEXT ? "TEXT" : "DATA",
- pid, oaddr, odata);
- addr &= 0xffffffffL;
- if (access_process_vm(child, addr, &tmp, sizeof(tmp), 1) == sizeof(tmp))
- goto out_tsk;
- }
- else
-#endif
- {
- if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
- goto out_tsk;
- }
- ret = -EIO;
- goto out_tsk;
+ case PTRACE_PEEKUSR:
+ if (addr & (sizeof(compat_uint_t)-1))
+ break;
+ addr = translate_usr_offset(addr);
+ if (addr >= sizeof(struct pt_regs))
+ break;
- /* Read the word at location addr in the USER area. For ptraced
- processes, the kernel saves all regs on a syscall. */
- case PTRACE_PEEKUSR: {
- ret = -EIO;
-#ifdef __LP64__
- if (is_compat_task(child)) {
- unsigned int tmp;
-
- if (addr & (sizeof(int)-1))
- goto out_tsk;
- if ((addr = translate_usr_offset(addr)) < 0)
- goto out_tsk;
-
- tmp = *(unsigned int *) ((char *) task_regs(child) + addr);
- ret = put_user(tmp, (unsigned int *) data);
- DBG("sys_ptrace(PEEKUSR, %d, %lx, %lx) returning %ld, addr %lx, data %x\n",
- pid, oaddr, odata, ret, addr, tmp);
- }
- else
-#endif
- {
- unsigned long tmp;
-
- if ((addr & (sizeof(long)-1)) || (unsigned long) addr >= sizeof(struct pt_regs))
- goto out_tsk;
- tmp = *(unsigned long *) ((char *) task_regs(child) + addr);
- ret = put_user(tmp, (unsigned long *) data);
- }
- goto out_tsk;
- }
+ tmp = *(compat_uint_t *) ((char *) task_regs(child) + addr);
+ ret = put_user(tmp, (compat_uint_t *) (unsigned long) data);
+ break;
/* Write the word at location addr in the USER area. This will need
to change when the kernel no longer saves all regs on a syscall.
@@ -179,204 +226,79 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
those values are overwritten by actual register values on syscall
exit. */
case PTRACE_POKEUSR:
- ret = -EIO;
/* Some register values written here may be ignored in
* entry.S:syscall_restore_rfi; e.g. iaoq is written with
* r31/r31+4, and not with the values in pt_regs.
*/
- /* PT_PSW=0, so this is valid for 32 bit processes under 64
- * bit kernels.
- */
if (addr == PT_PSW) {
- /* PT_PSW=0, so this is valid for 32 bit processes
- * under 64 bit kernels.
- *
- * Allow writing to Nullify, Divide-step-correction,
- * and carry/borrow bits.
- * BEWARE, if you set N, and then single step, it won't
- * stop on the nullified instruction.
+ /* Since PT_PSW==0, it is valid for 32 bit processes
+ * under 64 bit kernels as well.
*/
- DBG("sys_ptrace(POKEUSR, %d, %lx, %lx)\n",
- pid, oaddr, odata);
- data &= USER_PSW_BITS;
- task_regs(child)->gr[0] &= ~USER_PSW_BITS;
- task_regs(child)->gr[0] |= data;
- ret = 0;
- goto out_tsk;
- }
-#ifdef __LP64__
- if (is_compat_task(child)) {
- if (addr & (sizeof(int)-1))
- goto out_tsk;
- if ((addr = translate_usr_offset(addr)) < 0)
- goto out_tsk;
- DBG("sys_ptrace(POKEUSR, %d, %lx, %lx) addr %lx\n",
- pid, oaddr, odata, addr);
+ ret = arch_ptrace(child, request, addr, data);
+ } else {
+ if (addr & (sizeof(compat_uint_t)-1))
+ break;
+ addr = translate_usr_offset(addr);
+ if (addr >= sizeof(struct pt_regs))
+ break;
if (addr >= PT_FR0 && addr <= PT_FR31 + 4) {
/* Special case, fp regs are 64 bits anyway */
- *(unsigned int *) ((char *) task_regs(child) + addr) = data;
+ *(__u64 *) ((char *) task_regs(child) + addr) = data;
ret = 0;
}
else if ((addr >= PT_GR1+4 && addr <= PT_GR31+4) ||
addr == PT_IAOQ0+4 || addr == PT_IAOQ1+4 ||
addr == PT_SAR+4) {
/* Zero the top 32 bits */
- *(unsigned int *) ((char *) task_regs(child) + addr - 4) = 0;
- *(unsigned int *) ((char *) task_regs(child) + addr) = data;
- ret = 0;
- }
- goto out_tsk;
- }
- else
-#endif
- {
- if ((addr & (sizeof(long)-1)) || (unsigned long) addr >= sizeof(struct pt_regs))
- goto out_tsk;
- if ((addr >= PT_GR1 && addr <= PT_GR31) ||
- addr == PT_IAOQ0 || addr == PT_IAOQ1 ||
- (addr >= PT_FR0 && addr <= PT_FR31 + 4) ||
- addr == PT_SAR) {
- *(unsigned long *) ((char *) task_regs(child) + addr) = data;
+ *(__u32 *) ((char *) task_regs(child) + addr - 4) = 0;
+ *(__u32 *) ((char *) task_regs(child) + addr) = data;
ret = 0;
}
- goto out_tsk;
- }
-
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT:
- ret = -EIO;
- DBG("sys_ptrace(%s)\n",
- request == PTRACE_SYSCALL ? "SYSCALL" : "CONT");
- if (!valid_signal(data))
- goto out_tsk;
- child->ptrace &= ~(PT_SINGLESTEP|PT_BLOCKSTEP);
- if (request == PTRACE_SYSCALL) {
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- } else {
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- }
- child->exit_code = data;
- goto out_wake_notrap;
-
- case PTRACE_KILL:
- /*
- * make the child exit. Best I can do is send it a
- * sigkill. perhaps it should be put in the status
- * that it wants to exit.
- */
- DBG("sys_ptrace(KILL)\n");
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
- goto out_tsk;
- child->exit_code = SIGKILL;
- goto out_wake_notrap;
-
- case PTRACE_SINGLEBLOCK:
- DBG("sys_ptrace(SINGLEBLOCK)\n");
- ret = -EIO;
- if (!valid_signal(data))
- goto out_tsk;
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->ptrace &= ~PT_SINGLESTEP;
- child->ptrace |= PT_BLOCKSTEP;
- child->exit_code = data;
-
- /* Enable taken branch trap. */
- pa_psw(child)->r = 0;
- pa_psw(child)->t = 1;
- pa_psw(child)->h = 0;
- pa_psw(child)->l = 0;
- goto out_wake;
-
- case PTRACE_SINGLESTEP:
- DBG("sys_ptrace(SINGLESTEP)\n");
- ret = -EIO;
- if (!valid_signal(data))
- goto out_tsk;
-
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->ptrace &= ~PT_BLOCKSTEP;
- child->ptrace |= PT_SINGLESTEP;
- child->exit_code = data;
-
- if (pa_psw(child)->n) {
- struct siginfo si;
-
- /* Nullified, just crank over the queue. */
- task_regs(child)->iaoq[0] = task_regs(child)->iaoq[1];
- task_regs(child)->iasq[0] = task_regs(child)->iasq[1];
- task_regs(child)->iaoq[1] = task_regs(child)->iaoq[0] + 4;
- pa_psw(child)->n = 0;
- pa_psw(child)->x = 0;
- pa_psw(child)->y = 0;
- pa_psw(child)->z = 0;
- pa_psw(child)->b = 0;
- ptrace_disable(child);
- /* Don't wake up the child, but let the
- parent know something happened. */
- si.si_code = TRAP_TRACE;
- si.si_addr = (void __user *) (task_regs(child)->iaoq[0] & ~3);
- si.si_signo = SIGTRAP;
- si.si_errno = 0;
- force_sig_info(SIGTRAP, &si, child);
- //notify_parent(child, SIGCHLD);
- //ret = 0;
- goto out_wake;
}
-
- /* Enable recovery counter traps. The recovery counter
- * itself will be set to zero on a task switch. If the
- * task is suspended on a syscall then the syscall return
- * path will overwrite the recovery counter with a suitable
- * value such that it traps once back in user space. We
- * disable interrupts in the childs PSW here also, to avoid
- * interrupts while the recovery counter is decrementing.
- */
- pa_psw(child)->r = 1;
- pa_psw(child)->t = 0;
- pa_psw(child)->h = 0;
- pa_psw(child)->l = 0;
- /* give it a chance to run. */
- goto out_wake;
-
- case PTRACE_DETACH:
- ret = ptrace_detach(child, data);
- goto out_tsk;
-
- case PTRACE_GETEVENTMSG:
- ret = put_user(child->ptrace_message, (unsigned int __user *) data);
- goto out;
+ break;
default:
- ret = ptrace_request(child, request, addr, data);
- goto out;
+ ret = compat_ptrace_request(child, request, addr, data);
+ break;
}
-out_wake_notrap:
- ptrace_disable(child);
-out_wake:
- wake_up_process(child);
- ret = 0;
-out_tsk:
- DBG("arch_ptrace(%ld, %d, %lx, %lx) returning %ld\n",
- request, pid, oaddr, odata, ret);
return ret;
}
+#endif
-void syscall_trace(void)
+long do_syscall_trace_enter(struct pt_regs *regs)
{
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- return;
- if (!(current->ptrace & PT_PTRACED))
- return;
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
+ long ret = 0;
+
+ if (test_thread_flag(TIF_SYSCALL_TRACE) &&
+ tracehook_report_syscall_entry(regs))
+ ret = -1L;
+
+#ifdef CONFIG_64BIT
+ if (!is_compat_task())
+ audit_syscall_entry(AUDIT_ARCH_PARISC64,
+ regs->gr[20],
+ regs->gr[26], regs->gr[25],
+ regs->gr[24], regs->gr[23]);
+ else
+#endif
+ audit_syscall_entry(AUDIT_ARCH_PARISC,
+ regs->gr[20] & 0xffffffff,
+ regs->gr[26] & 0xffffffff,
+ regs->gr[25] & 0xffffffff,
+ regs->gr[24] & 0xffffffff,
+ regs->gr[23] & 0xffffffff);
+
+ return ret ? : regs->gr[20];
+}
+
+void do_syscall_trace_exit(struct pt_regs *regs)
+{
+ int stepping = test_thread_flag(TIF_SINGLESTEP) ||
+ test_thread_flag(TIF_BLOCKSTEP);
+
+ audit_syscall_exit(regs);
+
+ if (stepping || test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall_exit(regs, stepping);
}
diff --git a/arch/parisc/kernel/real2.S b/arch/parisc/kernel/real2.S
index 8c2859cca77..5f3d3a1f903 100644
--- a/arch/parisc/kernel/real2.S
+++ b/arch/parisc/kernel/real2.S
@@ -7,12 +7,25 @@
* Copyright (C) 2000 Hewlett Packard (Paul Bame bame@puffin.external.hp.com)
*
*/
-#include <linux/config.h>
+#include <asm/pdc.h>
#include <asm/psw.h>
#include <asm/assembly.h>
+#include <asm/asm-offsets.h>
+
+#include <linux/linkage.h>
+
.section .bss
+
+ .export pdc_result
+ .export pdc_result2
+ .align 8
+pdc_result:
+ .block ASM_PDC_RESULT_SIZE
+pdc_result2:
+ .block ASM_PDC_RESULT_SIZE
+
.export real_stack
.export real32_stack
.export real64_stack
@@ -40,8 +53,6 @@ save_cr_end:
.text
- .export real32_call_asm
-
/* unsigned long real32_call_asm(unsigned int *sp,
* unsigned int *arg0p,
* unsigned int iodc_fn)
@@ -50,7 +61,7 @@ save_cr_end:
* iodc_fn is the IODC function to call
*/
-real32_call_asm:
+ENTRY(real32_call_asm)
STREG %rp, -RP_OFFSET(%sp) /* save RP */
#ifdef CONFIG_64BIT
callee_save
@@ -108,6 +119,7 @@ ric_ret:
LDREG -RP_OFFSET(%sp), %rp /* restore RP */
bv 0(%rp)
nop
+ENDPROC(real32_call_asm)
# define PUSH_CR(r, where) mfctl r, %r1 ! STREG,ma %r1, REG_SZ(where)
@@ -219,7 +231,6 @@ rfi_r2v_1:
/************************ 64-bit real-mode calls ***********************/
/* This is only usable in wide kernels right now and will probably stay so */
.text
- .export real64_call_asm
/* unsigned long real64_call_asm(unsigned long *sp,
* unsigned long *arg0p,
* unsigned long fn)
@@ -227,7 +238,7 @@ rfi_r2v_1:
* arg0p points to where saved arg values may be found
* iodc_fn is the IODC function to call
*/
-real64_call_asm:
+ENTRY(real64_call_asm)
std %rp, -0x10(%sp) /* save RP */
std %sp, -8(%arg0) /* save SP on real-mode stack */
copy %arg0, %sp /* adopt the real-mode SP */
@@ -273,28 +284,21 @@ r64_ret:
ldd -0x10(%sp), %rp /* restore RP */
bv 0(%rp)
nop
+ENDPROC(real64_call_asm)
#endif
- .export pc_in_user_space
- .text
- /* Doesn't belong here but I couldn't find a nicer spot. */
- /* Should never get called, only used by profile stuff in time.c */
-pc_in_user_space:
- bv,n 0(%rp)
- nop
-
-
- .export __canonicalize_funcptr_for_compare
.text
/* http://lists.parisc-linux.org/hypermail/parisc-linux/10916.html
** GCC 3.3 and later has a new function in libgcc.a for
** comparing function pointers.
*/
-__canonicalize_funcptr_for_compare:
+ENTRY(__canonicalize_funcptr_for_compare)
#ifdef CONFIG_64BIT
bve (%r2)
#else
bv %r0(%r2)
#endif
copy %r26,%r28
+ENDPROC(__canonicalize_funcptr_for_compare)
+
diff --git a/arch/parisc/kernel/semaphore.c b/arch/parisc/kernel/semaphore.c
deleted file mode 100644
index ee806bcc372..00000000000
--- a/arch/parisc/kernel/semaphore.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Semaphore implementation Copyright (c) 2001 Matthew Wilcox, Hewlett-Packard
- */
-
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-
-/*
- * Semaphores are complex as we wish to avoid using two variables.
- * `count' has multiple roles, depending on its value. If it is positive
- * or zero, there are no waiters. The functions here will never be
- * called; see <asm/semaphore.h>
- *
- * When count is -1 it indicates there is at least one task waiting
- * for the semaphore.
- *
- * When count is less than that, there are '- count - 1' wakeups
- * pending. ie if it has value -3, there are 2 wakeups pending.
- *
- * Note that these functions are only called when there is contention
- * on the lock, and as such all this is the "non-critical" part of the
- * whole semaphore business. The critical part is the inline stuff in
- * <asm/semaphore.h> where we want to avoid any extra jumps and calls.
- */
-void __up(struct semaphore *sem)
-{
- sem->count--;
- wake_up(&sem->wait);
-}
-
-#define wakers(count) (-1 - count)
-
-#define DOWN_HEAD \
- int ret = 0; \
- DECLARE_WAITQUEUE(wait, current); \
- \
- /* Note that someone is waiting */ \
- if (sem->count == 0) \
- sem->count = -1; \
- \
- /* protected by the sentry still -- use unlocked version */ \
- wait.flags = WQ_FLAG_EXCLUSIVE; \
- __add_wait_queue_tail(&sem->wait, &wait); \
- lost_race: \
- spin_unlock_irq(&sem->sentry); \
-
-#define DOWN_TAIL \
- spin_lock_irq(&sem->sentry); \
- if (wakers(sem->count) == 0 && ret == 0) \
- goto lost_race; /* Someone stole our wakeup */ \
- __remove_wait_queue(&sem->wait, &wait); \
- current->state = TASK_RUNNING; \
- if (!waitqueue_active(&sem->wait) && (sem->count < 0)) \
- sem->count = wakers(sem->count);
-
-#define UPDATE_COUNT \
- sem->count += (sem->count < 0) ? 1 : - 1;
-
-
-void __sched __down(struct semaphore * sem)
-{
- DOWN_HEAD
-
- for(;;) {
- set_task_state(current, TASK_UNINTERRUPTIBLE);
- /* we can _read_ this without the sentry */
- if (sem->count != -1)
- break;
- schedule();
- }
-
- DOWN_TAIL
- UPDATE_COUNT
-}
-
-int __sched __down_interruptible(struct semaphore * sem)
-{
- DOWN_HEAD
-
- for(;;) {
- set_task_state(current, TASK_INTERRUPTIBLE);
- /* we can _read_ this without the sentry */
- if (sem->count != -1)
- break;
-
- if (signal_pending(current)) {
- ret = -EINTR;
- break;
- }
- schedule();
- }
-
- DOWN_TAIL
-
- if (!ret) {
- UPDATE_COUNT
- }
-
- return ret;
-}
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c
index 73e9c34b094..72a3c658ad7 100644
--- a/arch/parisc/kernel/setup.c
+++ b/arch/parisc/kernel/setup.c
@@ -1,5 +1,4 @@
-/* $Id: setup.c,v 1.8 2000/02/02 04:42:38 prumpf Exp $
- *
+/*
* Initial setup-routines for HP 9000 based hardware.
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
@@ -27,7 +26,6 @@
*
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/initrd.h>
#include <linux/init.h>
@@ -37,6 +35,7 @@
#include <linux/pci.h>
#undef PCI_DEBUG
#include <linux/proc_fs.h>
+#include <linux/export.h>
#include <asm/processor.h>
#include <asm/pdc.h>
@@ -45,24 +44,20 @@
#include <asm/pdc_chassis.h>
#include <asm/io.h>
#include <asm/setup.h>
+#include <asm/unwind.h>
-char command_line[COMMAND_LINE_SIZE];
+static char __initdata command_line[COMMAND_LINE_SIZE];
/* Intended for ccio/sba/cpu statistics under /proc/bus/{runway|gsc} */
-struct proc_dir_entry * proc_runway_root = NULL;
-struct proc_dir_entry * proc_gsc_root = NULL;
-struct proc_dir_entry * proc_mckinley_root = NULL;
+struct proc_dir_entry * proc_runway_root __read_mostly = NULL;
+struct proc_dir_entry * proc_gsc_root __read_mostly = NULL;
+struct proc_dir_entry * proc_mckinley_root __read_mostly = NULL;
#if !defined(CONFIG_PA20) && (defined(CONFIG_IOMMU_CCIO) || defined(CONFIG_IOMMU_SBA))
-int parisc_bus_is_phys = 1; /* Assume no IOMMU is present */
+int parisc_bus_is_phys __read_mostly = 1; /* Assume no IOMMU is present */
EXPORT_SYMBOL(parisc_bus_is_phys);
#endif
-/* This sets the vmerge boundary and size, it's here because it has to
- * be available on all platforms (zero means no-virtual merging) */
-unsigned long parisc_vmerge_boundary = 0;
-unsigned long parisc_vmerge_max_size = 0;
-
void __init setup_cmdline(char **cmdline_p)
{
extern unsigned int boot_args[];
@@ -72,9 +67,10 @@ void __init setup_cmdline(char **cmdline_p)
/* boot_args[0] is free-mem start, boot_args[1] is ptr to command line */
if (boot_args[0] < 64) {
/* called from hpux boot loader */
- saved_command_line[0] = '\0';
+ boot_command_line[0] = '\0';
} else {
- strcpy(saved_command_line, (char *)__va(boot_args[1]));
+ strlcpy(boot_command_line, (char *)__va(boot_args[1]),
+ COMMAND_LINE_SIZE);
#ifdef CONFIG_BLK_DEV_INITRD
if (boot_args[2] != 0) /* did palo pass us a ramdisk? */
@@ -85,7 +81,7 @@ void __init setup_cmdline(char **cmdline_p)
#endif
}
- strcpy(command_line, saved_command_line);
+ strcpy(command_line, boot_command_line);
*cmdline_p = command_line;
}
@@ -121,21 +117,24 @@ extern void collect_boot_cpu_data(void);
void __init setup_arch(char **cmdline_p)
{
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
extern int parisc_narrow_firmware;
#endif
+ unwind_init();
init_per_cpu(smp_processor_id()); /* Set Modes & Enable FP */
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
printk(KERN_INFO "The 64-bit Kernel has started...\n");
#else
printk(KERN_INFO "The 32-bit Kernel has started...\n");
#endif
+ printk(KERN_INFO "Default page size is %dKB.\n", (int)(PAGE_SIZE / 1024));
+
pdc_console_init();
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
if(parisc_narrow_firmware) {
printk(KERN_INFO "Kernel is using PDC in 32-bit mode.\n");
}
@@ -157,13 +156,13 @@ void __init setup_arch(char **cmdline_p)
#endif
#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
- conswitchp = &dummy_con; /* we use take_over_console() later ! */
+ conswitchp = &dummy_con; /* we use do_take_over_console() later ! */
#endif
}
/*
- * Display cpu info for all cpu's.
+ * Display CPU info for all CPUs.
* for parisc this is in processor.c
*/
extern int show_cpuinfo (struct seq_file *m, void *v);
@@ -191,7 +190,7 @@ c_stop (struct seq_file *m, void *v)
{
}
-struct seq_operations cpuinfo_op = {
+const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
@@ -226,6 +225,7 @@ static void __init parisc_proc_mkdir(void)
}
break;
case mako:
+ case mako2:
if (NULL == proc_mckinley_root)
{
proc_mckinley_root = proc_mkdir("bus/mckinley", NULL);
@@ -303,6 +303,8 @@ extern void eisa_init(void);
static int __init parisc_init(void)
{
+ u32 osid = (OS_ID_LINUX << 16);
+
parisc_proc_mkdir();
parisc_init_resources();
do_device_inventory(); /* probe for hardware */
@@ -311,10 +313,17 @@ static int __init parisc_init(void)
/* set up a new led state on systems shipped LED State panel */
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BSTART);
+
+ /* tell PDC we're Linux. Nevermind failure. */
+ pdc_stable_write(0x40, &osid, sizeof(osid));
processor_init();
- printk(KERN_INFO "CPU(s): %d x %s at %d.%06d MHz\n",
- boot_cpu_data.cpu_count,
+#ifdef CONFIG_SMP
+ pr_info("CPU(s): %d out of %d %s at %d.%06d MHz online\n",
+ num_online_cpus(), num_present_cpus(),
+#else
+ pr_info("CPU(s): 1 x %s at %d.%06d MHz\n",
+#endif
boot_cpu_data.cpu_name,
boot_cpu_data.cpu_hz / 1000000,
boot_cpu_data.cpu_hz % 1000000 );
@@ -363,6 +372,31 @@ static int __init parisc_init(void)
return 0;
}
-
arch_initcall(parisc_init);
+void start_parisc(void)
+{
+ extern void start_kernel(void);
+
+ int ret, cpunum;
+ struct pdc_coproc_cfg coproc_cfg;
+
+ cpunum = smp_processor_id();
+
+ set_firmware_width_unlocked();
+
+ ret = pdc_coproc_cfg_unlocked(&coproc_cfg);
+ if (ret >= 0 && coproc_cfg.ccr_functional) {
+ mtctl(coproc_cfg.ccr_functional, 10);
+
+ per_cpu(cpu_data, cpunum).fp_rev = coproc_cfg.revision;
+ per_cpu(cpu_data, cpunum).fp_model = coproc_cfg.model;
+
+ asm volatile ("fstd %fr0,8(%sp)");
+ } else {
+ panic("must have an fpu to boot linux");
+ }
+
+ start_kernel();
+ // not reached
+}
diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c
index 82c24e62ab6..1cba8f29bb4 100644
--- a/arch/parisc/kernel/signal.c
+++ b/arch/parisc/kernel/signal.c
@@ -16,17 +16,16 @@
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
-#include <linux/smp_lock.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/compat.h>
#include <linux/elf.h>
-#include <linux/personality.h>
#include <asm/ucontext.h>
#include <asm/rt_sigframe.h>
#include <asm/uaccess.h>
@@ -35,7 +34,6 @@
#include <asm/asm-offsets.h>
#ifdef CONFIG_COMPAT
-#include <linux/compat.h>
#include "signal32.h"
#endif
@@ -50,9 +48,6 @@
#define DBG(LEVEL, ...)
#endif
-
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
/* gcc will complain if a pointer is cast to an integer of different
* size. If you really need to do this (and we do for an ELF32 user
* application in an ELF64 kernel) then you have to do a cast to an
@@ -60,58 +55,6 @@
* this. */
#define A(__x) ((unsigned long)(__x))
-int do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall);
-
-/*
- * Atomically swap in the new signal mask, and wait for a signal.
- */
-#ifdef __LP64__
-#include "sys32.h"
-#endif
-
-asmlinkage int
-sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs)
-{
- sigset_t saveset, newset;
-#ifdef __LP64__
- compat_sigset_t newset32;
-
- if(personality(current->personality) == PER_LINUX32){
- /* XXX: Don't preclude handling different sized sigset_t's. */
- if (sigsetsize != sizeof(compat_sigset_t))
- return -EINVAL;
- if (copy_from_user(&newset32, (compat_sigset_t __user *)unewset, sizeof(newset32)))
- return -EFAULT;
- sigset_32to64(&newset,&newset32);
-
- } else
-#endif
- {
- /* XXX: Don't preclude handling different sized sigset_t's. */
- if (sigsetsize != sizeof(sigset_t))
- return -EINVAL;
-
- if (copy_from_user(&newset, unewset, sizeof(newset)))
- return -EFAULT;
- }
-
- sigdelsetmask(&newset, ~_BLOCKABLE);
-
- spin_lock_irq(&current->sighand->siglock);
- saveset = current->blocked;
- current->blocked = newset;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- regs->gr[28] = -EINTR;
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(&saveset, regs, 1))
- return -EINTR;
- }
-}
-
/*
* Do a signal return - restore sigcontext.
*/
@@ -135,7 +78,7 @@ restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
err |= __copy_from_user(regs->iaoq, sc->sc_iaoq, sizeof(regs->iaoq));
err |= __copy_from_user(regs->iasq, sc->sc_iasq, sizeof(regs->iasq));
err |= __get_user(regs->sar, &sc->sc_sar);
- DBG(2,"restore_sigcontext: iaoq is 0x%#lx / 0x%#lx\n",
+ DBG(2,"restore_sigcontext: iaoq is %#lx / %#lx\n",
regs->iaoq[0],regs->iaoq[1]);
DBG(2,"restore_sigcontext: r28 is %ld\n", regs->gr[28]);
return err;
@@ -145,28 +88,30 @@ void
sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
{
struct rt_sigframe __user *frame;
- struct siginfo si;
sigset_t set;
unsigned long usp = (regs->gr[30] & ~(0x01UL));
unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE;
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
compat_sigset_t compat_set;
struct compat_rt_sigframe __user * compat_frame;
- if(personality(current->personality) == PER_LINUX32)
+ if (is_compat_task())
sigframe_size = PARISC_RT_SIGFRAME_SIZE32;
#endif
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
/* Unwind the user stack to get the rt_sigframe structure. */
frame = (struct rt_sigframe __user *)
(usp - sigframe_size);
DBG(2,"sys_rt_sigreturn: frame is %p\n", frame);
-#ifdef __LP64__
+ regs->orig_r28 = 1; /* no restarts for sigreturn */
+
+#ifdef CONFIG_64BIT
compat_frame = (struct compat_rt_sigframe __user *)frame;
- if(personality(current->personality) == PER_LINUX32){
+ if (is_compat_task()) {
DBG(2,"sys_rt_sigreturn: ELF32 process.\n");
if (__copy_from_user(&compat_set, &compat_frame->uc.uc_sigmask, sizeof(compat_set)))
goto give_sigsegv;
@@ -178,15 +123,11 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
goto give_sigsegv;
}
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(&current->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ set_current_blocked(&set);
/* Good thing we saved the old gr[30], eh? */
-#ifdef __LP64__
- if(personality(current->personality) == PER_LINUX32){
+#ifdef CONFIG_64BIT
+ if (is_compat_task()) {
DBG(1,"sys_rt_sigreturn: compat_frame->uc.uc_mcontext 0x%p\n",
&compat_frame->uc.uc_mcontext);
// FIXME: Load upper half from register file
@@ -195,7 +136,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
goto give_sigsegv;
DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n",
usp, &compat_frame->uc.uc_stack);
- if (do_sigaltstack32(&compat_frame->uc.uc_stack, NULL, usp) == -EFAULT)
+ if (compat_restore_altstack(&compat_frame->uc.uc_stack))
goto give_sigsegv;
} else
#endif
@@ -206,7 +147,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
goto give_sigsegv;
DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n",
usp, &frame->uc.uc_stack);
- if (do_sigaltstack(&frame->uc.uc_stack, NULL, usp) == -EFAULT)
+ if (restore_altstack(&frame->uc.uc_stack))
goto give_sigsegv;
}
@@ -225,13 +166,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
give_sigsegv:
DBG(1,"sys_rt_sigreturn: Sending SIGSEGV\n");
- si.si_signo = SIGSEGV;
- si.si_errno = 0;
- si.si_code = SI_KERNEL;
- si.si_pid = current->pid;
- si.si_uid = current->uid;
- si.si_addr = &frame->uc;
- force_sig_info(SIGSEGV, &si, current);
+ force_sig(SIGSEGV, current);
return;
}
@@ -248,8 +183,10 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
DBG(1,"get_sigframe: ka = %#lx, sp = %#lx, frame_size = %#lx\n",
(unsigned long)ka, sp, frame_size);
- if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp))
- sp = current->sas_ss_sp; /* Stacks grow up! */
+ /* Align alternate stack and reserve 64 bytes for the signal
+ handler's frame marker. */
+ if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp))
+ sp = (current->sas_ss_sp + 0x7f) & ~0x3f; /* Stacks grow up! */
DBG(1,"get_sigframe: Returning sp = %#lx\n", (unsigned long)sp);
return (void __user *) sp; /* Stacks grow up. Fun. */
@@ -296,10 +233,8 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
struct rt_sigframe __user *frame;
unsigned long rp, usp;
unsigned long haddr, sigframe_size;
- struct siginfo si;
int err = 0;
-#ifdef __LP64__
- compat_int_t compat_val;
+#ifdef CONFIG_64BIT
struct compat_rt_sigframe __user * compat_frame;
compat_sigset_t compat_set;
#endif
@@ -312,22 +247,14 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
DBG(1,"setup_rt_frame: frame %p info %p\n", frame, info);
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
compat_frame = (struct compat_rt_sigframe __user *)frame;
- if(personality(current->personality) == PER_LINUX32) {
+ if (is_compat_task()) {
DBG(1,"setup_rt_frame: frame->info = 0x%p\n", &compat_frame->info);
- err |= compat_copy_siginfo_to_user(&compat_frame->info, info);
- DBG(1,"SETUP_RT_FRAME: 1\n");
- compat_val = (compat_int_t)current->sas_ss_sp;
- err |= __put_user(compat_val, &compat_frame->uc.uc_stack.ss_sp);
- DBG(1,"SETUP_RT_FRAME: 2\n");
- compat_val = (compat_int_t)current->sas_ss_size;
- err |= __put_user(compat_val, &compat_frame->uc.uc_stack.ss_size);
- DBG(1,"SETUP_RT_FRAME: 3\n");
- compat_val = sas_ss_flags(regs->gr[30]);
- err |= __put_user(compat_val, &compat_frame->uc.uc_stack.ss_flags);
+ err |= copy_siginfo_to_user32(&compat_frame->info, info);
+ err |= __compat_save_altstack( &compat_frame->uc.uc_stack, regs->gr[30]);
DBG(1,"setup_rt_frame: frame->uc = 0x%p\n", &compat_frame->uc);
DBG(1,"setup_rt_frame: frame->uc.uc_mcontext = 0x%p\n", &compat_frame->uc.uc_mcontext);
err |= setup_sigcontext32(&compat_frame->uc.uc_mcontext,
@@ -339,14 +266,11 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
{
DBG(1,"setup_rt_frame: frame->info = 0x%p\n", &frame->info);
err |= copy_siginfo_to_user(&frame->info, info);
- err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
- err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= __put_user(sas_ss_flags(regs->gr[30]),
- &frame->uc.uc_stack.ss_flags);
+ err |= __save_altstack(&frame->uc.uc_stack, regs->gr[30]);
DBG(1,"setup_rt_frame: frame->uc = 0x%p\n", &frame->uc);
DBG(1,"setup_rt_frame: frame->uc.uc_mcontext = 0x%p\n", &frame->uc.uc_mcontext);
err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, in_syscall);
- /* FIXME: Should probably be converted aswell for the compat case */
+ /* FIXME: Should probably be converted as well for the compat case */
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
}
@@ -369,7 +293,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
#if DEBUG_SIG
/* Assert that we're flushing in the correct space... */
{
- int sid;
+ unsigned long sid;
asm ("mfsp %%sr3,%0" : "=r" (sid));
DBG(1,"setup_rt_frame: Flushing 64 bytes at space %#x offset %p\n",
sid, frame->tramp);
@@ -381,7 +305,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
flush_user_icache_range((unsigned long) &frame->tramp[0],
(unsigned long) &frame->tramp[TRAMP_SIZE]);
- /* TRAMP Words 0-4, Lenght 5 = SIGRESTARTBLOCK_TRAMP
+ /* TRAMP Words 0-4, Length 5 = SIGRESTARTBLOCK_TRAMP
* TRAMP Words 5-9, Length 4 = SIGRETURN_TRAMP
* So the SIGRETURN_TRAMP is at the end of SIGRESTARTBLOCK_TRAMP
*/
@@ -392,8 +316,8 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
haddr = A(ka->sa.sa_handler);
/* The sa_handler may be a pointer to a function descriptor */
-#ifdef __LP64__
- if(personality(current->personality) == PER_LINUX32) {
+#ifdef CONFIG_64BIT
+ if (is_compat_task()) {
#endif
if (haddr & PA_PLABEL_FDESC) {
Elf32_Fdesc fdesc;
@@ -407,7 +331,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
haddr = fdesc.addr;
regs->gr[19] = fdesc.gp;
}
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
} else {
Elf64_Fdesc fdesc;
Elf64_Fdesc __user *ufdesc = (Elf64_Fdesc __user *)A(haddr & ~3);
@@ -427,20 +351,20 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
/* The syscall return path will create IAOQ values from r31.
*/
sigframe_size = PARISC_RT_SIGFRAME_SIZE;
-#ifdef __LP64__
- if(personality(current->personality) == PER_LINUX32)
+#ifdef CONFIG_64BIT
+ if (is_compat_task())
sigframe_size = PARISC_RT_SIGFRAME_SIZE32;
#endif
if (in_syscall) {
regs->gr[31] = haddr;
-#ifdef __LP64__
- if(personality(current->personality) == PER_LINUX)
+#ifdef CONFIG_64BIT
+ if (!test_thread_flag(TIF_32BIT))
sigframe_size |= 1;
#endif
} else {
unsigned long psw = USER_PSW;
-#ifdef __LP64__
- if(personality(current->personality) == PER_LINUX)
+#ifdef CONFIG_64BIT
+ if (!test_thread_flag(TIF_32BIT))
psw |= PSW_W;
#endif
@@ -464,8 +388,8 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
regs->gr[2] = rp; /* userland return pointer */
regs->gr[26] = sig; /* signal number */
-#ifdef __LP64__
- if(personality(current->personality) == PER_LINUX32){
+#ifdef CONFIG_64BIT
+ if (is_compat_task()) {
regs->gr[25] = A(&compat_frame->info); /* siginfo pointer */
regs->gr[24] = A(&compat_frame->uc); /* ucontext pointer */
} else
@@ -498,24 +422,112 @@ give_sigsegv:
* OK, we're invoking a handler.
*/
-static long
+static void
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
- sigset_t *oldset, struct pt_regs *regs, int in_syscall)
+ struct pt_regs *regs, int in_syscall)
{
+ sigset_t *oldset = sigmask_to_save();
DBG(1,"handle_signal: sig=%ld, ka=%p, info=%p, oldset=%p, regs=%p\n",
sig, ka, info, oldset, regs);
/* Set up the stack frame */
if (!setup_rt_frame(sig, ka, info, oldset, regs, in_syscall))
- return 0;
-
- spin_lock_irq(&current->sighand->siglock);
- sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
- if (!(ka->sa.sa_flags & SA_NODEFER))
- sigaddset(&current->blocked,sig);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
- return 1;
+ return;
+
+ signal_delivered(sig, info, ka, regs,
+ test_thread_flag(TIF_SINGLESTEP) ||
+ test_thread_flag(TIF_BLOCKSTEP));
+
+ DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
+ regs->gr[28]);
+}
+
+static inline void
+syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
+{
+ if (regs->orig_r28)
+ return;
+ regs->orig_r28 = 1; /* no more restarts */
+ /* Check the return code */
+ switch (regs->gr[28]) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
+ regs->gr[28] = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ DBG(1,"ERESTARTSYS: putting -EINTR\n");
+ regs->gr[28] = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ /* A syscall is just a branch, so all
+ * we have to do is fiddle the return pointer.
+ */
+ regs->gr[31] -= 8; /* delayed branching */
+ break;
+ }
+}
+
+static inline void
+insert_restart_trampoline(struct pt_regs *regs)
+{
+ if (regs->orig_r28)
+ return;
+ regs->orig_r28 = 1; /* no more restarts */
+ switch(regs->gr[28]) {
+ case -ERESTART_RESTARTBLOCK: {
+ /* Restart the system call - no handlers present */
+ unsigned int *usp = (unsigned int *)regs->gr[30];
+
+ /* Setup a trampoline to restart the syscall
+ * with __NR_restart_syscall
+ *
+ * 0: <return address (orig r31)>
+ * 4: <2nd half for 64-bit>
+ * 8: ldw 0(%sp), %r31
+ * 12: be 0x100(%sr2, %r0)
+ * 16: ldi __NR_restart_syscall, %r20
+ */
+#ifdef CONFIG_64BIT
+ put_user(regs->gr[31] >> 32, &usp[0]);
+ put_user(regs->gr[31] & 0xffffffff, &usp[1]);
+ put_user(0x0fc010df, &usp[2]);
+#else
+ put_user(regs->gr[31], &usp[0]);
+ put_user(0x0fc0109f, &usp[2]);
+#endif
+ put_user(0xe0008200, &usp[3]);
+ put_user(0x34140000, &usp[4]);
+
+ /* Stack is 64-byte aligned, and we only need
+ * to flush 1 cache line.
+ * Flushing one cacheline is cheap.
+ * "sync" on bigger (> 4 way) boxes is not.
+ */
+ flush_user_dcache_range(regs->gr[30], regs->gr[30] + 4);
+ flush_user_icache_range(regs->gr[30], regs->gr[30] + 4);
+
+ regs->gr[31] = regs->gr[30] + 8;
+ return;
+ }
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR: {
+ /* Hooray for delayed branching. We don't
+ * have to restore %r20 (the system call
+ * number) because it gets loaded in the delay
+ * slot of the branch external instruction.
+ */
+ regs->gr[31] -= 8;
+ return;
+ }
+ default:
+ break;
+ }
}
/*
@@ -529,131 +541,45 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
* registers). As noted below, the syscall number gets restored for
* us due to the magic of delayed branching.
*/
-
-asmlinkage int
-do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall)
+asmlinkage void
+do_signal(struct pt_regs *regs, long in_syscall)
{
siginfo_t info;
struct k_sigaction ka;
int signr;
- DBG(1,"\ndo_signal: oldset=0x%p, regs=0x%p, sr7 %#lx, in_syscall=%d\n",
- oldset, regs, regs->sr[7], in_syscall);
-
- /* Everyone else checks to see if they are in kernel mode at
- this point and exits if that's the case. I'm not sure why
- we would be called in that case, but for some reason we
- are. */
-
- if (!oldset)
- oldset = &current->blocked;
-
- DBG(1,"do_signal: oldset %08lx / %08lx\n",
- oldset->sig[0], oldset->sig[1]);
-
+ DBG(1,"\ndo_signal: regs=0x%p, sr7 %#lx, in_syscall=%d\n",
+ regs, regs->sr[7], in_syscall);
- /* May need to force signal if handle_signal failed to deliver */
- while (1) {
-
- signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- DBG(3,"do_signal: signr = %d, regs->gr[28] = %ld\n", signr, regs->gr[28]);
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ DBG(3,"do_signal: signr = %d, regs->gr[28] = %ld\n", signr, regs->gr[28]);
- if (signr <= 0)
- break;
-
+ if (signr > 0) {
/* Restart a system call if necessary. */
- if (in_syscall) {
- /* Check the return code */
- switch (regs->gr[28]) {
- case -ERESTART_RESTARTBLOCK:
- current_thread_info()->restart_block.fn = do_no_restart_syscall;
- case -ERESTARTNOHAND:
- DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
- regs->gr[28] = -EINTR;
- break;
-
- case -ERESTARTSYS:
- if (!(ka.sa.sa_flags & SA_RESTART)) {
- DBG(1,"ERESTARTSYS: putting -EINTR\n");
- regs->gr[28] = -EINTR;
- break;
- }
- /* fallthrough */
- case -ERESTARTNOINTR:
- /* A syscall is just a branch, so all
- we have to do is fiddle the return pointer. */
- regs->gr[31] -= 8; /* delayed branching */
- /* Preserve original r28. */
- regs->gr[28] = regs->orig_r28;
- break;
- }
- }
- /* Whee! Actually deliver the signal. If the
- delivery failed, we need to continue to iterate in
- this loop so we can deliver the SIGSEGV... */
- if (handle_signal(signr, &info, &ka, oldset, regs, in_syscall)) {
- DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
- regs->gr[28]);
- return 1;
- }
+ if (in_syscall)
+ syscall_restart(regs, &ka);
+
+ handle_signal(signr, &info, &ka, regs, in_syscall);
+ return;
}
- /* end of while(1) looping forever if we can't force a signal */
/* Did we come from a system call? */
- if (in_syscall) {
- /* Restart the system call - no handlers present */
- if (regs->gr[28] == -ERESTART_RESTARTBLOCK) {
- unsigned int *usp = (unsigned int *)regs->gr[30];
-
- /* Setup a trampoline to restart the syscall
- * with __NR_restart_syscall
- *
- * 0: <return address (orig r31)>
- * 4: <2nd half for 64-bit>
- * 8: ldw 0(%sp), %r31
- * 12: be 0x100(%sr2, %r0)
- * 16: ldi __NR_restart_syscall, %r20
- */
-#ifndef __LP64__
- put_user(regs->gr[31], &usp[0]);
- put_user(0x0fc0109f, &usp[2]);
-#else
- put_user(regs->gr[31] >> 32, &usp[0]);
- put_user(regs->gr[31] & 0xffffffff, &usp[1]);
- put_user(0x0fc010df, &usp[2]);
-#endif
- put_user(0xe0008200, &usp[3]);
- put_user(0x34140000, &usp[4]);
-
- /* Stack is 64-byte aligned, and we only need
- * to flush 1 cache line.
- * Flushing one cacheline is cheap.
- * "sync" on bigger (> 4 way) boxes is not.
- */
- asm("fdc %%r0(%%sr3, %0)\n"
- "sync\n"
- "fic %%r0(%%sr3, %0)\n"
- "sync\n"
- : : "r"(regs->gr[30]));
-
- regs->gr[31] = regs->gr[30] + 8;
- /* Preserve original r28. */
- regs->gr[28] = regs->orig_r28;
- } else if (regs->gr[28] == -ERESTARTNOHAND ||
- regs->gr[28] == -ERESTARTSYS ||
- regs->gr[28] == -ERESTARTNOINTR) {
- /* Hooray for delayed branching. We don't
- have to restore %r20 (the system call
- number) because it gets loaded in the delay
- slot of the branch external instruction. */
- regs->gr[31] -= 8;
- /* Preserve original r28. */
- regs->gr[28] = regs->orig_r28;
- }
- }
+ if (in_syscall)
+ insert_restart_trampoline(regs);
DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n",
regs->gr[28]);
- return 0;
+ restore_saved_sigmask();
+}
+
+void do_notify_resume(struct pt_regs *regs, long in_syscall)
+{
+ if (test_thread_flag(TIF_SIGPENDING))
+ do_signal(regs, in_syscall);
+
+ if (test_thread_flag(TIF_NOTIFY_RESUME)) {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ }
}
diff --git a/arch/parisc/kernel/signal32.c b/arch/parisc/kernel/signal32.c
index 0792e20efef..984abbee71c 100644
--- a/arch/parisc/kernel/signal32.c
+++ b/arch/parisc/kernel/signal32.c
@@ -1,6 +1,8 @@
/* Signal support for 32-bit kernel builds
*
* Copyright (C) 2001 Matthew Wilcox <willy at parisc-linux.org>
+ * Copyright (C) 2006 Kyle McMartin <kyle at parisc-linux.org>
+ *
* Code was mostly borrowed from kernel/signal.c.
* See kernel/signal.c for additional Copyrights.
*
@@ -21,21 +23,17 @@
*/
#include <linux/compat.h>
-#include <linux/slab.h>
#include <linux/module.h>
#include <linux/unistd.h>
-#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/errno.h>
-#include <asm/compat_signal.h>
#include <asm/uaccess.h>
#include "signal32.h"
-#include "sys32.h"
#define DEBUG_COMPAT_SIG 0
#define DEBUG_COMPAT_SIG_LEVEL 2
@@ -48,8 +46,6 @@
#define DBG(LEVEL, ...)
#endif
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
inline void
sigset_32to64(sigset_t *s64, compat_sigset_t *s32)
{
@@ -63,127 +59,6 @@ sigset_64to32(compat_sigset_t *s32, sigset_t *s64)
s32->sig[1] = (s64->sig[0] >> 32) & 0xffffffffUL;
}
-static int
-put_sigset32(compat_sigset_t __user *up, sigset_t *set, size_t sz)
-{
- compat_sigset_t s;
-
- if (sz != sizeof *set) panic("put_sigset32()");
- sigset_64to32(&s, set);
-
- return copy_to_user(up, &s, sizeof s);
-}
-
-static int
-get_sigset32(compat_sigset_t __user *up, sigset_t *set, size_t sz)
-{
- compat_sigset_t s;
- int r;
-
- if (sz != sizeof *set) panic("put_sigset32()");
-
- if ((r = copy_from_user(&s, up, sz)) == 0) {
- sigset_32to64(set, &s);
- }
-
- return r;
-}
-
-int sys32_rt_sigprocmask(int how, compat_sigset_t __user *set, compat_sigset_t __user *oset,
- unsigned int sigsetsize)
-{
- sigset_t old_set, new_set;
- int ret;
-
- if (set && get_sigset32(set, &new_set, sigsetsize))
- return -EFAULT;
-
- KERNEL_SYSCALL(ret, sys_rt_sigprocmask, how, set ? (sigset_t __user *)&new_set : NULL,
- oset ? (sigset_t __user *)&old_set : NULL, sigsetsize);
-
- if (!ret && oset && put_sigset32(oset, &old_set, sigsetsize))
- return -EFAULT;
-
- return ret;
-}
-
-
-int sys32_rt_sigpending(compat_sigset_t __user *uset, unsigned int sigsetsize)
-{
- int ret;
- sigset_t set;
-
- KERNEL_SYSCALL(ret, sys_rt_sigpending, (sigset_t __user *)&set, sigsetsize);
-
- if (!ret && put_sigset32(uset, &set, sigsetsize))
- return -EFAULT;
-
- return ret;
-}
-
-long
-sys32_rt_sigaction(int sig, const struct sigaction32 __user *act, struct sigaction32 __user *oact,
- size_t sigsetsize)
-{
- struct k_sigaction32 new_sa32, old_sa32;
- struct k_sigaction new_sa, old_sa;
- int ret = -EINVAL;
-
- if (act) {
- if (copy_from_user(&new_sa32.sa, act, sizeof new_sa32.sa))
- return -EFAULT;
- new_sa.sa.sa_handler = (__sighandler_t)(unsigned long)new_sa32.sa.sa_handler;
- new_sa.sa.sa_flags = new_sa32.sa.sa_flags;
- sigset_32to64(&new_sa.sa.sa_mask, &new_sa32.sa.sa_mask);
- }
-
- ret = do_sigaction(sig, act ? &new_sa : NULL, oact ? &old_sa : NULL);
-
- if (!ret && oact) {
- sigset_64to32(&old_sa32.sa.sa_mask, &old_sa.sa.sa_mask);
- old_sa32.sa.sa_flags = old_sa.sa.sa_flags;
- old_sa32.sa.sa_handler = (__sighandler_t32)(unsigned long)old_sa.sa.sa_handler;
- if (copy_to_user(oact, &old_sa32.sa, sizeof old_sa32.sa))
- return -EFAULT;
- }
- return ret;
-}
-
-int
-do_sigaltstack32 (const compat_stack_t __user *uss32, compat_stack_t __user *uoss32, unsigned long sp)
-{
- compat_stack_t ss32, oss32;
- stack_t ss, oss;
- stack_t *ssp = NULL, *ossp = NULL;
- int ret;
-
- if (uss32) {
- if (copy_from_user(&ss32, uss32, sizeof ss32))
- return -EFAULT;
-
- ss.ss_sp = (void __user *)(unsigned long)ss32.ss_sp;
- ss.ss_flags = ss32.ss_flags;
- ss.ss_size = ss32.ss_size;
-
- ssp = &ss;
- }
-
- if (uoss32)
- ossp = &oss;
-
- KERNEL_SYSCALL(ret, do_sigaltstack, (const stack_t __user *)ssp, (stack_t __user *)ossp, sp);
-
- if (!ret && uoss32) {
- oss32.ss_sp = (unsigned int)(unsigned long)oss.ss_sp;
- oss32.ss_flags = oss.ss_flags;
- oss32.ss_size = oss.ss_size;
- if (copy_to_user(uoss32, &oss32, sizeof *uoss32))
- return -EFAULT;
- }
-
- return ret;
-}
-
long
restore_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __user * rf,
struct pt_regs *regs)
@@ -289,7 +164,7 @@ setup_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __
&sc->sc_iaoq[0], compat_reg);
/* Store upper half */
- compat_reg = (compat_uint_t)(regs->gr[32] >> 32);
+ compat_reg = (compat_uint_t)(regs->gr[31] >> 32);
err |= __put_user(compat_reg, &rf->rf_iaoq[0]);
DBG(2,"setup_sigcontext32: upper half iaoq[0] = %#x\n", compat_reg);
@@ -299,7 +174,7 @@ setup_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __
DBG(2,"setup_sigcontext32: sc->sc_iaoq[1] = %p <= %#x\n",
&sc->sc_iaoq[1], compat_reg);
/* Store upper half */
- compat_reg = (compat_uint_t)((regs->gr[32]+4) >> 32);
+ compat_reg = (compat_uint_t)((regs->gr[31]+4) >> 32);
err |= __put_user(compat_reg, &rf->rf_iaoq[1]);
DBG(2,"setup_sigcontext32: upper half iaoq[1] = %#x\n", compat_reg);
@@ -398,3 +273,105 @@ setup_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __
return err;
}
+
+int
+copy_siginfo_from_user32 (siginfo_t *to, compat_siginfo_t __user *from)
+{
+ compat_uptr_t addr;
+ int err;
+
+ if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
+ return -EFAULT;
+
+ err = __get_user(to->si_signo, &from->si_signo);
+ err |= __get_user(to->si_errno, &from->si_errno);
+ err |= __get_user(to->si_code, &from->si_code);
+
+ if (to->si_code < 0)
+ err |= __copy_from_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+ else {
+ switch (to->si_code >> 16) {
+ case __SI_CHLD >> 16:
+ err |= __get_user(to->si_utime, &from->si_utime);
+ err |= __get_user(to->si_stime, &from->si_stime);
+ err |= __get_user(to->si_status, &from->si_status);
+ default:
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
+ break;
+ case __SI_FAULT >> 16:
+ err |= __get_user(addr, &from->si_addr);
+ to->si_addr = compat_ptr(addr);
+ break;
+ case __SI_POLL >> 16:
+ err |= __get_user(to->si_band, &from->si_band);
+ err |= __get_user(to->si_fd, &from->si_fd);
+ break;
+ case __SI_RT >> 16: /* This is not generated by the kernel as of now. */
+ case __SI_MESGQ >> 16:
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
+ err |= __get_user(to->si_int, &from->si_int);
+ break;
+ }
+ }
+ return err;
+}
+
+int
+copy_siginfo_to_user32 (compat_siginfo_t __user *to, const siginfo_t *from)
+{
+ compat_uptr_t addr;
+ compat_int_t val;
+ int err;
+
+ if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
+ return -EFAULT;
+
+ /* If you change siginfo_t structure, please be sure
+ this code is fixed accordingly.
+ It should never copy any pad contained in the structure
+ to avoid security leaks, but must copy the generic
+ 3 ints plus the relevant union member.
+ This routine must convert siginfo from 64bit to 32bit as well
+ at the same time. */
+ err = __put_user(from->si_signo, &to->si_signo);
+ err |= __put_user(from->si_errno, &to->si_errno);
+ err |= __put_user((short)from->si_code, &to->si_code);
+ if (from->si_code < 0)
+ err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+ else {
+ switch (from->si_code >> 16) {
+ case __SI_CHLD >> 16:
+ err |= __put_user(from->si_utime, &to->si_utime);
+ err |= __put_user(from->si_stime, &to->si_stime);
+ err |= __put_user(from->si_status, &to->si_status);
+ default:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ break;
+ case __SI_FAULT >> 16:
+ addr = ptr_to_compat(from->si_addr);
+ err |= __put_user(addr, &to->si_addr);
+ break;
+ case __SI_POLL >> 16:
+ err |= __put_user(from->si_band, &to->si_band);
+ err |= __put_user(from->si_fd, &to->si_fd);
+ break;
+ case __SI_TIMER >> 16:
+ err |= __put_user(from->si_tid, &to->si_tid);
+ err |= __put_user(from->si_overrun, &to->si_overrun);
+ val = (compat_int_t)from->si_int;
+ err |= __put_user(val, &to->si_int);
+ break;
+ case __SI_RT >> 16: /* Not generated by the kernel as of now. */
+ case __SI_MESGQ >> 16:
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pid, &to->si_pid);
+ val = (compat_int_t)from->si_int;
+ err |= __put_user(val, &to->si_int);
+ break;
+ }
+ }
+ return err;
+}
diff --git a/arch/parisc/kernel/signal32.h b/arch/parisc/kernel/signal32.h
index 4d1569e717c..af51d4ccee4 100644
--- a/arch/parisc/kernel/signal32.h
+++ b/arch/parisc/kernel/signal32.h
@@ -20,19 +20,67 @@
#define _PARISC64_KERNEL_SIGNAL32_H
#include <linux/compat.h>
-#include <asm/compat_signal.h>
-#include <asm/compat_rt_sigframe.h>
+
+/* 32-bit ucontext as seen from an 64-bit kernel */
+struct compat_ucontext {
+ compat_uint_t uc_flags;
+ compat_uptr_t uc_link;
+ compat_stack_t uc_stack; /* struct compat_sigaltstack (12 bytes)*/
+ /* FIXME: Pad out to get uc_mcontext to start at an 8-byte aligned boundary */
+ compat_uint_t pad[1];
+ struct compat_sigcontext uc_mcontext;
+ compat_sigset_t uc_sigmask; /* mask last for extensibility */
+};
/* ELF32 signal handling */
-struct k_sigaction32 {
- struct compat_sigaction sa;
+int copy_siginfo_to_user32 (compat_siginfo_t __user *to, const siginfo_t *from);
+int copy_siginfo_from_user32 (siginfo_t *to, compat_siginfo_t __user *from);
+
+/* In a deft move of uber-hackery, we decide to carry the top half of all
+ * 64-bit registers in a non-portable, non-ABI, hidden structure.
+ * Userspace can read the hidden structure if it *wants* but is never
+ * guaranteed to be in the same place. In fact the uc_sigmask from the
+ * ucontext_t structure may push the hidden register file downards
+ */
+struct compat_regfile {
+ /* Upper half of all the 64-bit registers that were truncated
+ on a copy to a 32-bit userspace */
+ compat_int_t rf_gr[32];
+ compat_int_t rf_iasq[2];
+ compat_int_t rf_iaoq[2];
+ compat_int_t rf_sar;
};
+#define COMPAT_SIGRETURN_TRAMP 4
+#define COMPAT_SIGRESTARTBLOCK_TRAMP 5
+#define COMPAT_TRAMP_SIZE (COMPAT_SIGRETURN_TRAMP + \
+ COMPAT_SIGRESTARTBLOCK_TRAMP)
+
+struct compat_rt_sigframe {
+ /* XXX: Must match trampoline size in arch/parisc/kernel/signal.c
+ Secondary to that it must protect the ERESTART_RESTARTBLOCK
+ trampoline we left on the stack (we were bad and didn't
+ change sp so we could run really fast.) */
+ compat_uint_t tramp[COMPAT_TRAMP_SIZE];
+ compat_siginfo_t info;
+ struct compat_ucontext uc;
+ /* Hidden location of truncated registers, *must* be last. */
+ struct compat_regfile regs;
+};
+
+/*
+ * The 32-bit ABI wants at least 48 bytes for a function call frame:
+ * 16 bytes for arg0-arg3, and 32 bytes for magic (the only part of
+ * which Linux/parisc uses is sp-20 for the saved return pointer...)
+ * Then, the stack pointer must be rounded to a cache line (64 bytes).
+ */
+#define SIGFRAME32 64
+#define FUNCTIONCALLFRAME32 48
+#define PARISC_RT_SIGFRAME_SIZE32 (((sizeof(struct compat_rt_sigframe) + FUNCTIONCALLFRAME32) + SIGFRAME32) & -SIGFRAME32)
+
void sigset_32to64(sigset_t *s64, compat_sigset_t *s32);
void sigset_64to32(compat_sigset_t *s32, sigset_t *s64);
-int do_sigaltstack32 (const compat_stack_t __user *uss32,
- compat_stack_t __user *uoss32, unsigned long sp);
long restore_sigcontext32(struct compat_sigcontext __user *sc,
struct compat_regfile __user *rf,
struct pt_regs *regs);
diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c
index 5db3be4e270..ceda229ea6c 100644
--- a/arch/parisc/kernel/smp.c
+++ b/arch/parisc/kernel/smp.c
@@ -8,7 +8,7 @@
** Lots of stuff stolen from arch/alpha/kernel/smp.c
** ...and then parisc stole from arch/ia64/kernel/smp.c. Thanks David! :^)
**
-** Thanks to John Curry and Ullas Ponnadi. I learned alot from their work.
+** Thanks to John Curry and Ullas Ponnadi. I learned a lot from their work.
** -grant (1/12/2001)
**
** This program is free software; you can redistribute it and/or modify
@@ -16,13 +16,8 @@
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
*/
-#undef ENTRY_SYS_CPUS /* syscall support for iCOD-like functionality */
-
-#include <linux/config.h>
-
#include <linux/types.h>
#include <linux/spinlock.h>
-#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -32,14 +27,16 @@
#include <linux/smp.h>
#include <linux/kernel_stat.h>
#include <linux/mm.h>
+#include <linux/err.h>
#include <linux/delay.h>
#include <linux/bitops.h>
+#include <linux/ftrace.h>
+#include <linux/cpu.h>
-#include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/current.h>
#include <asm/delay.h>
-#include <asm/pgalloc.h> /* for flush_tlb_all() proto/macro */
+#include <asm/tlbflush.h>
#include <asm/io.h>
#include <asm/irq.h> /* for CPU_IRQ_REGION and friends */
@@ -52,40 +49,24 @@
#include <asm/unistd.h>
#include <asm/cacheflush.h>
-#define kDEBUG 0
-
-DEFINE_SPINLOCK(smp_lock);
+#undef DEBUG_SMP
+#ifdef DEBUG_SMP
+static int smp_debug_lvl = 0;
+#define smp_debug(lvl, printargs...) \
+ if (lvl >= smp_debug_lvl) \
+ printk(printargs);
+#else
+#define smp_debug(lvl, ...) do { } while(0)
+#endif /* DEBUG_SMP */
volatile struct task_struct *smp_init_current_idle_task;
-static volatile int cpu_now_booting = 0; /* track which CPU is booting */
+/* track which CPU is booting */
+static volatile int cpu_now_booting;
static int parisc_max_cpus = 1;
-/* online cpus are ones that we've managed to bring up completely
- * possible cpus are all valid cpu
- * present cpus are all detected cpu
- *
- * On startup we bring up the "possible" cpus. Since we discover
- * CPUs later, we add them as hotplug, so the possible cpu mask is
- * empty in the beginning.
- */
-
-cpumask_t cpu_online_map = CPU_MASK_NONE; /* Bitmap of online CPUs */
-cpumask_t cpu_possible_map = CPU_MASK_ALL; /* Bitmap of Present CPUs */
-
-EXPORT_SYMBOL(cpu_online_map);
-EXPORT_SYMBOL(cpu_possible_map);
-
-
-struct smp_call_struct {
- void (*func) (void *info);
- void *info;
- long wait;
- atomic_t unstarted_count;
- atomic_t unfinished_count;
-};
-static volatile struct smp_call_struct *smp_call_function_data;
+static DEFINE_PER_CPU(spinlock_t, ipi_lock);
enum ipi_message_type {
IPI_NOP=0,
@@ -108,13 +89,6 @@ enum ipi_message_type {
static void
ipi_init(int cpuid)
{
-
- /* If CPU is present ... */
-#ifdef ENTRY_SYS_CPUS
- /* *and* running (not stopped) ... */
-#error iCOD support wants state checked here.
-#endif
-
#error verify IRQ_OFFSET(IPI_IRQ) is ipi_interrupt() in new IRQ region
if(cpu_online(cpuid) )
@@ -134,44 +108,29 @@ ipi_init(int cpuid)
static void
halt_processor(void)
{
-#ifdef ENTRY_SYS_CPUS
-#error halt_processor() needs rework
-/*
-** o migrate I/O interrupts off this CPU.
-** o leave IPI enabled - __cli() will disable IPI.
-** o leave CPU in online map - just change the state
-*/
- cpu_data[this_cpu].state = STATE_STOPPED;
- mark_bh(IPI_BH);
-#else
/* REVISIT : redirect I/O Interrupts to another CPU? */
/* REVISIT : does PM *know* this CPU isn't available? */
- cpu_clear(smp_processor_id(), cpu_online_map);
+ set_cpu_online(smp_processor_id(), false);
local_irq_disable();
for (;;)
;
-#endif
}
-irqreturn_t
-ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t __irq_entry
+ipi_interrupt(int irq, void *dev_id)
{
int this_cpu = smp_processor_id();
- struct cpuinfo_parisc *p = &cpu_data[this_cpu];
+ struct cpuinfo_parisc *p = &per_cpu(cpu_data, this_cpu);
unsigned long ops;
unsigned long flags;
- /* Count this now; we may make a call that never returns. */
- p->ipi_count++;
-
- mb(); /* Order interrupt and bit testing. */
-
for (;;) {
- spin_lock_irqsave(&(p->lock),flags);
+ spinlock_t *lock = &per_cpu(ipi_lock, this_cpu);
+ spin_lock_irqsave(lock, flags);
ops = p->pending_ipi;
p->pending_ipi = 0;
- spin_unlock_irqrestore(&(p->lock),flags);
+ spin_unlock_irqrestore(lock, flags);
mb(); /* Order bit clearing and data access. */
@@ -181,86 +140,45 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
while (ops) {
unsigned long which = ffz(~ops);
+ ops &= ~(1 << which);
+
switch (which) {
+ case IPI_NOP:
+ smp_debug(100, KERN_DEBUG "CPU%d IPI_NOP\n", this_cpu);
+ break;
+
case IPI_RESCHEDULE:
-#if (kDEBUG>=100)
- printk(KERN_DEBUG "CPU%d IPI_RESCHEDULE\n",this_cpu);
-#endif /* kDEBUG */
- ops &= ~(1 << IPI_RESCHEDULE);
- /*
- * Reschedule callback. Everything to be
- * done is done by the interrupt return path.
- */
+ smp_debug(100, KERN_DEBUG "CPU%d IPI_RESCHEDULE\n", this_cpu);
+ inc_irq_stat(irq_resched_count);
+ scheduler_ipi();
break;
case IPI_CALL_FUNC:
-#if (kDEBUG>=100)
- printk(KERN_DEBUG "CPU%d IPI_CALL_FUNC\n",this_cpu);
-#endif /* kDEBUG */
- ops &= ~(1 << IPI_CALL_FUNC);
- {
- volatile struct smp_call_struct *data;
- void (*func)(void *info);
- void *info;
- int wait;
-
- data = smp_call_function_data;
- func = data->func;
- info = data->info;
- wait = data->wait;
-
- mb();
- atomic_dec ((atomic_t *)&data->unstarted_count);
-
- /* At this point, *data can't
- * be relied upon.
- */
-
- (*func)(info);
-
- /* Notify the sending CPU that the
- * task is done.
- */
- mb();
- if (wait)
- atomic_dec ((atomic_t *)&data->unfinished_count);
- }
+ smp_debug(100, KERN_DEBUG "CPU%d IPI_CALL_FUNC\n", this_cpu);
+ generic_smp_call_function_interrupt();
break;
case IPI_CPU_START:
-#if (kDEBUG>=100)
- printk(KERN_DEBUG "CPU%d IPI_CPU_START\n",this_cpu);
-#endif /* kDEBUG */
- ops &= ~(1 << IPI_CPU_START);
-#ifdef ENTRY_SYS_CPUS
- p->state = STATE_RUNNING;
-#endif
+ smp_debug(100, KERN_DEBUG "CPU%d IPI_CPU_START\n", this_cpu);
break;
case IPI_CPU_STOP:
-#if (kDEBUG>=100)
- printk(KERN_DEBUG "CPU%d IPI_CPU_STOP\n",this_cpu);
-#endif /* kDEBUG */
- ops &= ~(1 << IPI_CPU_STOP);
-#ifdef ENTRY_SYS_CPUS
-#else
+ smp_debug(100, KERN_DEBUG "CPU%d IPI_CPU_STOP\n", this_cpu);
halt_processor();
-#endif
break;
case IPI_CPU_TEST:
-#if (kDEBUG>=100)
- printk(KERN_DEBUG "CPU%d is alive!\n",this_cpu);
-#endif /* kDEBUG */
- ops &= ~(1 << IPI_CPU_TEST);
+ smp_debug(100, KERN_DEBUG "CPU%d is alive!\n", this_cpu);
break;
default:
printk(KERN_CRIT "Unknown IPI num on CPU%d: %lu\n",
this_cpu, which);
- ops &= ~(1 << which);
return IRQ_NONE;
} /* Switch */
+ /* let in any pending interrupts */
+ local_irq_enable();
+ local_irq_disable();
} /* while (ops) */
}
return IRQ_HANDLED;
@@ -270,23 +188,29 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static inline void
ipi_send(int cpu, enum ipi_message_type op)
{
- struct cpuinfo_parisc *p = &cpu_data[cpu];
+ struct cpuinfo_parisc *p = &per_cpu(cpu_data, cpu);
+ spinlock_t *lock = &per_cpu(ipi_lock, cpu);
unsigned long flags;
- spin_lock_irqsave(&(p->lock),flags);
+ spin_lock_irqsave(lock, flags);
p->pending_ipi |= 1 << op;
- gsc_writel(IPI_IRQ - CPU_IRQ_BASE, cpu_data[cpu].hpa);
- spin_unlock_irqrestore(&(p->lock),flags);
+ gsc_writel(IPI_IRQ - CPU_IRQ_BASE, p->hpa);
+ spin_unlock_irqrestore(lock, flags);
}
+static void
+send_IPI_mask(const struct cpumask *mask, enum ipi_message_type op)
+{
+ int cpu;
+
+ for_each_cpu(cpu, mask)
+ ipi_send(cpu, op);
+}
static inline void
send_IPI_single(int dest_cpu, enum ipi_message_type op)
{
- if (dest_cpu == NO_PROC_ID) {
- BUG();
- return;
- }
+ BUG_ON(dest_cpu == NO_PROC_ID);
ipi_send(dest_cpu, op);
}
@@ -296,8 +220,8 @@ send_IPI_allbutself(enum ipi_message_type op)
{
int i;
- for (i = 0; i < NR_CPUS; i++) {
- if (cpu_online(i) && i != smp_processor_id())
+ for_each_online_cpu(i) {
+ if (i != smp_processor_id())
send_IPI_single(i, op);
}
}
@@ -312,107 +236,20 @@ smp_send_start(void) { send_IPI_allbutself(IPI_CPU_START); }
void
smp_send_reschedule(int cpu) { send_IPI_single(cpu, IPI_RESCHEDULE); }
-
-/**
- * Run a function on all other CPUs.
- * <func> The function to run. This must be fast and non-blocking.
- * <info> An arbitrary pointer to pass to the function.
- * <retry> If true, keep retrying until ready.
- * <wait> If true, wait until function has completed on other CPUs.
- * [RETURNS] 0 on success, else a negative status code.
- *
- * Does not return until remote CPUs are nearly ready to execute <func>
- * or have executed.
- */
-
-int
-smp_call_function (void (*func) (void *info), void *info, int retry, int wait)
+void
+smp_send_all_nop(void)
{
- struct smp_call_struct data;
- unsigned long timeout;
- static DEFINE_SPINLOCK(lock);
- int retries = 0;
-
- if (num_online_cpus() < 2)
- return 0;
-
- /* Can deadlock when called with interrupts disabled */
- WARN_ON(irqs_disabled());
-
- data.func = func;
- data.info = info;
- data.wait = wait;
- atomic_set(&data.unstarted_count, num_online_cpus() - 1);
- atomic_set(&data.unfinished_count, num_online_cpus() - 1);
-
- if (retry) {
- spin_lock (&lock);
- while (smp_call_function_data != 0)
- barrier();
- }
- else {
- spin_lock (&lock);
- if (smp_call_function_data) {
- spin_unlock (&lock);
- return -EBUSY;
- }
- }
-
- smp_call_function_data = &data;
- spin_unlock (&lock);
-
- /* Send a message to all other CPUs and wait for them to respond */
- send_IPI_allbutself(IPI_CALL_FUNC);
-
- retry:
- /* Wait for response */
- timeout = jiffies + HZ;
- while ( (atomic_read (&data.unstarted_count) > 0) &&
- time_before (jiffies, timeout) )
- barrier ();
-
- if (atomic_read (&data.unstarted_count) > 0) {
- printk(KERN_CRIT "SMP CALL FUNCTION TIMED OUT! (cpu=%d), try %d\n",
- smp_processor_id(), ++retries);
- goto retry;
- }
- /* We either got one or timed out. Release the lock */
-
- mb();
- smp_call_function_data = NULL;
-
- while (wait && atomic_read (&data.unfinished_count) > 0)
- barrier ();
-
- return 0;
+ send_IPI_allbutself(IPI_NOP);
}
-EXPORT_SYMBOL(smp_call_function);
-
-/*
- * Flush all other CPU's tlb and then mine. Do this with on_each_cpu()
- * as we want to ensure all TLB's flushed before proceeding.
- */
-
-extern void flush_tlb_all_local(void);
-
-void
-smp_flush_tlb_all(void)
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
- on_each_cpu((void (*)(void *))flush_tlb_all_local, NULL, 1, 1);
+ send_IPI_mask(mask, IPI_CALL_FUNC);
}
-
-void
-smp_do_timer(struct pt_regs *regs)
+void arch_send_call_function_single_ipi(int cpu)
{
- int cpu = smp_processor_id();
- struct cpuinfo_parisc *data = &cpu_data[cpu];
-
- if (!--data->prof_counter) {
- data->prof_counter = data->prof_multiplier;
- update_process_times(user_mode(regs));
- }
+ send_IPI_single(cpu, IPI_CALL_FUNC);
}
/*
@@ -421,8 +258,9 @@ smp_do_timer(struct pt_regs *regs)
static void __init
smp_cpu_init(int cpunum)
{
- extern int init_per_cpu(int); /* arch/parisc/kernel/setup.c */
+ extern int init_per_cpu(int); /* arch/parisc/kernel/processor.c */
extern void init_IRQ(void); /* arch/parisc/kernel/irq.c */
+ extern void start_cpu_itimer(void); /* arch/parisc/kernel/time.c */
/* Set modes and Enable floating point coprocessor */
(void) init_per_cpu(cpunum);
@@ -432,22 +270,25 @@ smp_cpu_init(int cpunum)
mb();
/* Well, support 2.4 linux scheme as well. */
- if (cpu_test_and_set(cpunum, cpu_online_map))
- {
+ if (cpu_online(cpunum)) {
extern void machine_halt(void); /* arch/parisc.../process.c */
printk(KERN_CRIT "CPU#%d already initialized!\n", cpunum);
machine_halt();
- }
+ }
+
+ notify_cpu_starting(cpunum);
+
+ set_cpu_online(cpunum, true);
/* Initialise the idle task for this CPU */
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
- if(current->mm)
- BUG();
+ BUG_ON(current->mm);
enter_lazy_tlb(&init_mm, current);
- init_IRQ(); /* make sure no IRQ's are enabled or pending */
+ init_IRQ(); /* make sure no IRQs are enabled or pending */
+ start_cpu_itimer();
}
@@ -458,27 +299,16 @@ smp_cpu_init(int cpunum)
void __init smp_callin(void)
{
int slave_id = cpu_now_booting;
-#if 0
- void *istack;
-#endif
smp_cpu_init(slave_id);
-
-#if 0 /* NOT WORKING YET - see entry.S */
- istack = (void *)__get_free_pages(GFP_KERNEL,ISTACK_ORDER);
- if (istack == NULL) {
- printk(KERN_CRIT "Failed to allocate interrupt stack for cpu %d\n",slave_id);
- BUG();
- }
- mtctl(istack,31);
-#endif
+ preempt_disable();
flush_cache_all_local(); /* start with known state */
- flush_tlb_all_local();
+ flush_tlb_all_local(NULL);
local_irq_enable(); /* Interrupts have been off until now */
- cpu_idle(); /* Wait for timer to schedule some work */
+ cpu_startup_entry(CPUHP_ONLINE);
/* NOTREACHED */
panic("smp_callin() AAAAaaaaahhhh....\n");
@@ -487,26 +317,12 @@ void __init smp_callin(void)
/*
* Bring one cpu online.
*/
-int __init smp_boot_one_cpu(int cpuid)
+int smp_boot_one_cpu(int cpuid, struct task_struct *idle)
{
- struct task_struct *idle;
+ const struct cpuinfo_parisc *p = &per_cpu(cpu_data, cpuid);
long timeout;
- /*
- * Create an idle task for this CPU. Note the address wed* give
- * to kernel_thread is irrelevant -- it's going to start
- * where OS_BOOT_RENDEVZ vector in SAL says to start. But
- * this gets all the other task-y sort of data structures set
- * up like we wish. We need to pull the just created idle task
- * off the run queue and stuff it into the init_tasks[] array.
- * Sheesh . . .
- */
-
- idle = fork_idle(cpuid);
- if (IS_ERR(idle))
- panic("SMP: fork failed for CPU:%d", cpuid);
-
- idle->thread_info->cpu = cpuid;
+ task_thread_info(idle)->cpu = cpuid;
/* Let _start know what logical CPU we're booting
** (offset into init_tasks[],cpu_data[])
@@ -520,7 +336,7 @@ int __init smp_boot_one_cpu(int cpuid)
smp_init_current_idle_task = idle ;
mb();
- printk("Releasing cpu %d now, hpa=%lx\n", cpuid, cpu_data[cpuid].hpa);
+ printk(KERN_INFO "Releasing cpu %d now, hpa=%lx\n", cpuid, p->hpa);
/*
** This gets PDC to release the CPU from a very tight loop.
@@ -531,7 +347,7 @@ int __init smp_boot_one_cpu(int cpuid)
** EIR{0}). MEM_RENDEZ is valid only when it is nonzero and the
** contents of memory are valid."
*/
- gsc_writel(TIMER_IRQ - CPU_IRQ_BASE, cpu_data[cpuid].hpa);
+ gsc_writel(TIMER_IRQ - CPU_IRQ_BASE, p->hpa);
mb();
/*
@@ -549,50 +365,41 @@ int __init smp_boot_one_cpu(int cpuid)
udelay(100);
barrier();
}
-
- put_task_struct(idle);
- idle = NULL;
-
printk(KERN_CRIT "SMP: CPU:%d is stuck.\n", cpuid);
return -1;
alive:
/* Remember the Slave data */
-#if (kDEBUG>=100)
- printk(KERN_DEBUG "SMP: CPU:%d came alive after %ld _us\n",
+ smp_debug(100, KERN_DEBUG "SMP: CPU:%d came alive after %ld _us\n",
cpuid, timeout * 100);
-#endif /* kDEBUG */
-#ifdef ENTRY_SYS_CPUS
- cpu_data[cpuid].state = STATE_RUNNING;
-#endif
return 0;
}
-void __devinit smp_prepare_boot_cpu(void)
+void __init smp_prepare_boot_cpu(void)
{
- int bootstrap_processor=cpu_data[0].cpuid; /* CPU ID of BSP */
-
-#ifdef ENTRY_SYS_CPUS
- cpu_data[0].state = STATE_RUNNING;
-#endif
+ int bootstrap_processor = per_cpu(cpu_data, 0).cpuid;
/* Setup BSP mappings */
- printk("SMP: bootstrap CPU ID is %d\n",bootstrap_processor);
+ printk(KERN_INFO "SMP: bootstrap CPU ID is %d\n", bootstrap_processor);
- cpu_set(bootstrap_processor, cpu_online_map);
- cpu_set(bootstrap_processor, cpu_present_map);
+ set_cpu_online(bootstrap_processor, true);
+ set_cpu_present(bootstrap_processor, true);
}
/*
** inventory.c:do_inventory() hasn't yet been run and thus we
-** don't 'discover' the additional CPU's until later.
+** don't 'discover' the additional CPUs until later.
*/
void __init smp_prepare_cpus(unsigned int max_cpus)
{
- cpus_clear(cpu_present_map);
- cpu_set(0, cpu_present_map);
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ spin_lock_init(&per_cpu(ipi_lock, cpu));
+
+ init_cpu_present(cpumask_of(0));
parisc_max_cpus = max_cpus;
if (!max_cpus)
@@ -606,114 +413,14 @@ void smp_cpus_done(unsigned int cpu_max)
}
-int __devinit __cpu_up(unsigned int cpu)
+int __cpu_up(unsigned int cpu, struct task_struct *tidle)
{
if (cpu != 0 && cpu < parisc_max_cpus)
- smp_boot_one_cpu(cpu);
+ smp_boot_one_cpu(cpu, tidle);
return cpu_online(cpu) ? 0 : -ENOSYS;
}
-
-
-#ifdef ENTRY_SYS_CPUS
-/* Code goes along with:
-** entry.s: ENTRY_NAME(sys_cpus) / * 215, for cpu stat * /
-*/
-int sys_cpus(int argc, char **argv)
-{
- int i,j=0;
- extern int current_pid(int cpu);
-
- if( argc > 2 ) {
- printk("sys_cpus:Only one argument supported\n");
- return (-1);
- }
- if ( argc == 1 ){
-
-#ifdef DUMP_MORE_STATE
- for(i=0; i<NR_CPUS; i++) {
- int cpus_per_line = 4;
- if(cpu_online(i)) {
- if (j++ % cpus_per_line)
- printk(" %3d",i);
- else
- printk("\n %3d",i);
- }
- }
- printk("\n");
-#else
- printk("\n 0\n");
-#endif
- } else if((argc==2) && !(strcmp(argv[1],"-l"))) {
- printk("\nCPUSTATE TASK CPUNUM CPUID HARDCPU(HPA)\n");
-#ifdef DUMP_MORE_STATE
- for(i=0;i<NR_CPUS;i++) {
- if (!cpu_online(i))
- continue;
- if (cpu_data[i].cpuid != NO_PROC_ID) {
- switch(cpu_data[i].state) {
- case STATE_RENDEZVOUS:
- printk("RENDEZVS ");
- break;
- case STATE_RUNNING:
- printk((current_pid(i)!=0) ? "RUNNING " : "IDLING ");
- break;
- case STATE_STOPPED:
- printk("STOPPED ");
- break;
- case STATE_HALTED:
- printk("HALTED ");
- break;
- default:
- printk("%08x?", cpu_data[i].state);
- break;
- }
- if(cpu_online(i)) {
- printk(" %4d",current_pid(i));
- }
- printk(" %6d",cpu_number_map(i));
- printk(" %5d",i);
- printk(" 0x%lx\n",cpu_data[i].hpa);
- }
- }
-#else
- printk("\n%s %4d 0 0 --------",
- (current->pid)?"RUNNING ": "IDLING ",current->pid);
-#endif
- } else if ((argc==2) && !(strcmp(argv[1],"-s"))) {
-#ifdef DUMP_MORE_STATE
- printk("\nCPUSTATE CPUID\n");
- for (i=0;i<NR_CPUS;i++) {
- if (!cpu_online(i))
- continue;
- if (cpu_data[i].cpuid != NO_PROC_ID) {
- switch(cpu_data[i].state) {
- case STATE_RENDEZVOUS:
- printk("RENDEZVS");break;
- case STATE_RUNNING:
- printk((current_pid(i)!=0) ? "RUNNING " : "IDLING");
- break;
- case STATE_STOPPED:
- printk("STOPPED ");break;
- case STATE_HALTED:
- printk("HALTED ");break;
- default:
- }
- printk(" %5d\n",i);
- }
- }
-#else
- printk("\n%s CPU0",(current->pid==0)?"RUNNING ":"IDLING ");
-#endif
- } else {
- printk("sys_cpus:Unknown request\n");
- return (-1);
- }
- return 0;
-}
-#endif /* ENTRY_SYS_CPUS */
-
#ifdef CONFIG_PROC_FS
int __init
setup_profiling_timer(unsigned int multiplier)
diff --git a/arch/parisc/kernel/stacktrace.c b/arch/parisc/kernel/stacktrace.c
new file mode 100644
index 00000000000..2fe914c5f53
--- /dev/null
+++ b/arch/parisc/kernel/stacktrace.c
@@ -0,0 +1,63 @@
+/*
+ * Stack trace management functions
+ *
+ * Copyright (C) 2009 Helge Deller <deller@gmx.de>
+ * based on arch/x86/kernel/stacktrace.c by Ingo Molnar <mingo@redhat.com>
+ * and parisc unwind functions by Randolph Chung <tausq@debian.org>
+ *
+ * TODO: Userspace stacktrace (CONFIG_USER_STACKTRACE_SUPPORT)
+ */
+#include <linux/module.h>
+#include <linux/stacktrace.h>
+
+#include <asm/unwind.h>
+
+static void dump_trace(struct task_struct *task, struct stack_trace *trace)
+{
+ struct unwind_frame_info info;
+
+ /* initialize unwind info */
+ if (task == current) {
+ unsigned long sp;
+ struct pt_regs r;
+HERE:
+ asm volatile ("copy %%r30, %0" : "=r"(sp));
+ memset(&r, 0, sizeof(struct pt_regs));
+ r.iaoq[0] = (unsigned long)&&HERE;
+ r.gr[2] = (unsigned long)__builtin_return_address(0);
+ r.gr[30] = sp;
+ unwind_frame_init(&info, task, &r);
+ } else {
+ unwind_frame_init_from_blocked_task(&info, task);
+ }
+
+ /* unwind stack and save entries in stack_trace struct */
+ trace->nr_entries = 0;
+ while (trace->nr_entries < trace->max_entries) {
+ if (unwind_once(&info) < 0 || info.ip == 0)
+ break;
+
+ if (__kernel_text_address(info.ip))
+ trace->entries[trace->nr_entries++] = info.ip;
+ }
+}
+
+
+/*
+ * Save stack-backtrace addresses into a stack_trace buffer.
+ */
+void save_stack_trace(struct stack_trace *trace)
+{
+ dump_trace(current, trace);
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ dump_trace(tsk, trace);
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
diff --git a/arch/parisc/kernel/sys32.h b/arch/parisc/kernel/sys32.h
deleted file mode 100644
index 06c2090cfab..00000000000
--- a/arch/parisc/kernel/sys32.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2002 Richard Hirst <rhirst at parisc-linux.org>
- * Copyright (C) 2003 James Bottomley <jejb at parisc-linux.org>
- * Copyright (C) 2003 Randolph Chung <tausq with parisc-linux.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef _PARISC64_KERNEL_SYS32_H
-#define _PARISC64_KERNEL_SYS32_H
-
-#include <linux/compat.h>
-
-/* Call a kernel syscall which will use kernel space instead of user
- * space for its copy_to/from_user.
- */
-#define KERNEL_SYSCALL(ret, syscall, args...) \
-{ \
- mm_segment_t old_fs = get_fs(); \
- set_fs(KERNEL_DS); \
- ret = syscall(args); \
- set_fs (old_fs); \
-}
-
-#ifdef CONFIG_COMPAT
-
-typedef __u32 __sighandler_t32;
-
-struct sigaction32 {
- __sighandler_t32 sa_handler;
- unsigned int sa_flags;
- compat_sigset_t sa_mask; /* mask last for extensibility */
-};
-
-#endif
-
-#endif
diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c
index d15a1d53e10..e1ffea2f9a0 100644
--- a/arch/parisc/kernel/sys_parisc.c
+++ b/arch/parisc/kernel/sys_parisc.c
@@ -5,6 +5,7 @@
* Copyright (C) 1999-2003 Matthew Wilcox <willy at parisc-linux.org>
* Copyright (C) 2000-2003 Paul Bame <bame at parisc-linux.org>
* Copyright (C) 2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org>
+ * Copyright (C) 1999-2014 Helge Deller <deller@gmx.de>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -23,131 +24,248 @@
*/
#include <asm/uaccess.h>
+#include <asm/elf.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/linkage.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/shm.h>
-#include <linux/smp_lock.h>
#include <linux/syscalls.h>
-
-int sys_pipe(int __user *fildes)
+#include <linux/utsname.h>
+#include <linux/personality.h>
+#include <linux/random.h>
+
+/* we construct an artificial offset for the mapping based on the physical
+ * address of the kernel mapping variable */
+#define GET_LAST_MMAP(filp) \
+ (filp ? ((unsigned long) filp->f_mapping) >> 8 : 0UL)
+#define SET_LAST_MMAP(filp, val) \
+ { /* nothing */ }
+
+static int get_offset(unsigned int last_mmap)
{
- int fd[2];
- int error;
-
- error = do_pipe(fd);
- if (!error) {
- if (copy_to_user(fildes, fd, 2*sizeof(int)))
- error = -EFAULT;
- }
- return error;
+ return (last_mmap & (SHM_COLOUR-1)) >> PAGE_SHIFT;
}
-static unsigned long get_unshared_area(unsigned long addr, unsigned long len)
+static unsigned long shared_align_offset(unsigned int last_mmap,
+ unsigned long pgoff)
{
- struct vm_area_struct *vma;
+ return (get_offset(last_mmap) + pgoff) << PAGE_SHIFT;
+}
- addr = PAGE_ALIGN(addr);
+static inline unsigned long COLOR_ALIGN(unsigned long addr,
+ unsigned int last_mmap, unsigned long pgoff)
+{
+ unsigned long base = (addr+SHM_COLOUR-1) & ~(SHM_COLOUR-1);
+ unsigned long off = (SHM_COLOUR-1) &
+ (shared_align_offset(last_mmap, pgoff) << PAGE_SHIFT);
- for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
- /* At this point: (!vma || addr < vma->vm_end). */
- if (TASK_SIZE - len < addr)
- return -ENOMEM;
- if (!vma || addr + len <= vma->vm_start)
- return addr;
- addr = vma->vm_end;
- }
+ return base + off;
}
-#define DCACHE_ALIGN(addr) (((addr) + (SHMLBA - 1)) &~ (SHMLBA - 1))
-
/*
- * We need to know the offset to use. Old scheme was to look for
- * existing mapping and use the same offset. New scheme is to use the
- * address of the kernel data structure as the seed for the offset.
- * We'll see how that works...
- *
- * The mapping is cacheline aligned, so there's no information in the bottom
- * few bits of the address. We're looking for 10 bits (4MB / 4k), so let's
- * drop the bottom 8 bits and use bits 8-17.
+ * Top of mmap area (just below the process stack).
*/
-static int get_offset(struct address_space *mapping)
+
+static unsigned long mmap_upper_limit(void)
{
- int offset = (unsigned long) mapping << (PAGE_SHIFT - 8);
- return offset & 0x3FF000;
+ unsigned long stack_base;
+
+ /* Limit stack size - see setup_arg_pages() in fs/exec.c */
+ stack_base = rlimit_max(RLIMIT_STACK);
+ if (stack_base > STACK_SIZE_MAX)
+ stack_base = STACK_SIZE_MAX;
+
+ return PAGE_ALIGN(STACK_TOP - stack_base);
}
-static unsigned long get_shared_area(struct address_space *mapping,
- unsigned long addr, unsigned long len, unsigned long pgoff)
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
{
+ struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
- int offset = mapping ? get_offset(mapping) : 0;
-
- addr = DCACHE_ALIGN(addr - offset) + offset;
-
- for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
- /* At this point: (!vma || addr < vma->vm_end). */
- if (TASK_SIZE - len < addr)
- return -ENOMEM;
- if (!vma || addr + len <= vma->vm_start)
- return addr;
- addr = DCACHE_ALIGN(vma->vm_end - offset) + offset;
- if (addr < vma->vm_end) /* handle wraparound */
- return -ENOMEM;
+ unsigned long task_size = TASK_SIZE;
+ int do_color_align, last_mmap;
+ struct vm_unmapped_area_info info;
+
+ if (len > task_size)
+ return -ENOMEM;
+
+ do_color_align = 0;
+ if (filp || (flags & MAP_SHARED))
+ do_color_align = 1;
+ last_mmap = GET_LAST_MMAP(filp);
+
+ if (flags & MAP_FIXED) {
+ if ((flags & MAP_SHARED) && last_mmap &&
+ (addr - shared_align_offset(last_mmap, pgoff))
+ & (SHM_COLOUR - 1))
+ return -EINVAL;
+ goto found_addr;
+ }
+
+ if (addr) {
+ if (do_color_align && last_mmap)
+ addr = COLOR_ALIGN(addr, last_mmap, pgoff);
+ else
+ addr = PAGE_ALIGN(addr);
+
+ vma = find_vma(mm, addr);
+ if (task_size - len >= addr &&
+ (!vma || addr + len <= vma->vm_start))
+ goto found_addr;
}
+
+ info.flags = 0;
+ info.length = len;
+ info.low_limit = mm->mmap_legacy_base;
+ info.high_limit = mmap_upper_limit();
+ info.align_mask = last_mmap ? (PAGE_MASK & (SHM_COLOUR - 1)) : 0;
+ info.align_offset = shared_align_offset(last_mmap, pgoff);
+ addr = vm_unmapped_area(&info);
+
+found_addr:
+ if (do_color_align && !last_mmap && !(addr & ~PAGE_MASK))
+ SET_LAST_MMAP(filp, addr - (pgoff << PAGE_SHIFT));
+
+ return addr;
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags)
+unsigned long
+arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
+ const unsigned long len, const unsigned long pgoff,
+ const unsigned long flags)
{
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+ unsigned long addr = addr0;
+ int do_color_align, last_mmap;
+ struct vm_unmapped_area_info info;
+
+#ifdef CONFIG_64BIT
+ /* This should only ever run for 32-bit processes. */
+ BUG_ON(!test_thread_flag(TIF_32BIT));
+#endif
+
+ /* requested length too big for entire address space */
if (len > TASK_SIZE)
return -ENOMEM;
- if (!addr)
- addr = TASK_UNMAPPED_BASE;
- if (filp) {
- addr = get_shared_area(filp->f_mapping, addr, len, pgoff);
- } else if(flags & MAP_SHARED) {
- addr = get_shared_area(NULL, addr, len, pgoff);
- } else {
- addr = get_unshared_area(addr, len);
+ do_color_align = 0;
+ if (filp || (flags & MAP_SHARED))
+ do_color_align = 1;
+ last_mmap = GET_LAST_MMAP(filp);
+
+ if (flags & MAP_FIXED) {
+ if ((flags & MAP_SHARED) && last_mmap &&
+ (addr - shared_align_offset(last_mmap, pgoff))
+ & (SHM_COLOUR - 1))
+ return -EINVAL;
+ goto found_addr;
}
+
+ /* requesting a specific address */
+ if (addr) {
+ if (do_color_align && last_mmap)
+ addr = COLOR_ALIGN(addr, last_mmap, pgoff);
+ else
+ addr = PAGE_ALIGN(addr);
+ vma = find_vma(mm, addr);
+ if (TASK_SIZE - len >= addr &&
+ (!vma || addr + len <= vma->vm_start))
+ goto found_addr;
+ }
+
+ info.flags = VM_UNMAPPED_AREA_TOPDOWN;
+ info.length = len;
+ info.low_limit = PAGE_SIZE;
+ info.high_limit = mm->mmap_base;
+ info.align_mask = last_mmap ? (PAGE_MASK & (SHM_COLOUR - 1)) : 0;
+ info.align_offset = shared_align_offset(last_mmap, pgoff);
+ addr = vm_unmapped_area(&info);
+ if (!(addr & ~PAGE_MASK))
+ goto found_addr;
+ VM_BUG_ON(addr != -ENOMEM);
+
+ /*
+ * A failed mmap() very likely causes application failure,
+ * so fall back to the bottom-up function here. This scenario
+ * can happen with large stack limits and large mmap()
+ * allocations.
+ */
+ return arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
+
+found_addr:
+ if (do_color_align && !last_mmap && !(addr & ~PAGE_MASK))
+ SET_LAST_MMAP(filp, addr - (pgoff << PAGE_SHIFT));
+
return addr;
}
-static unsigned long do_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags, unsigned long fd,
- unsigned long pgoff)
+static int mmap_is_legacy(void)
+{
+ if (current->personality & ADDR_COMPAT_LAYOUT)
+ return 1;
+
+ /* parisc stack always grows up - so a unlimited stack should
+ * not be an indicator to use the legacy memory layout.
+ * if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
+ * return 1;
+ */
+
+ return sysctl_legacy_va_layout;
+}
+
+static unsigned long mmap_rnd(void)
{
- struct file * file = NULL;
- unsigned long error = -EBADF;
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
+ unsigned long rnd = 0;
+
+ /*
+ * 8 bits of randomness in 32bit mmaps, 20 address space bits
+ * 28 bits of randomness in 64bit mmaps, 40 address space bits
+ */
+ if (current->flags & PF_RANDOMIZE) {
+ if (is_32bit_task())
+ rnd = get_random_int() % (1<<8);
+ else
+ rnd = get_random_int() % (1<<28);
}
+ return rnd << PAGE_SHIFT;
+}
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+static unsigned long mmap_legacy_base(void)
+{
+ return TASK_UNMAPPED_BASE + mmap_rnd();
+}
- down_write(&current->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(&current->mm->mmap_sem);
+/*
+ * This function, called very early during the creation of a new
+ * process VM image, sets up which VM layout function to use:
+ */
+void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+ mm->mmap_legacy_base = mmap_legacy_base();
+ mm->mmap_base = mmap_upper_limit();
- if (file != NULL)
- fput(file);
-out:
- return error;
+ if (mmap_is_legacy()) {
+ mm->mmap_base = mm->mmap_legacy_base;
+ mm->get_unmapped_area = arch_get_unmapped_area;
+ } else {
+ mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+ }
}
+
asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd,
unsigned long pgoff)
{
/* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE
we have. */
- return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12));
+ return sys_mmap_pgoff(addr, len, prot, flags, fd,
+ pgoff >> (PAGE_SHIFT - 12));
}
asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
@@ -155,7 +273,8 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
unsigned long offset)
{
if (!(offset & ~PAGE_MASK)) {
- return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+ return sys_mmap_pgoff(addr, len, prot, flags, fd,
+ offset >> PAGE_SHIFT);
} else {
return -EINVAL;
}
@@ -231,6 +350,21 @@ asmlinkage long parisc_fadvise64_64(int fd,
(loff_t)high_len << 32 | low_len, advice);
}
+asmlinkage long parisc_sync_file_range(int fd,
+ u32 hi_off, u32 lo_off, u32 hi_nbytes, u32 lo_nbytes,
+ unsigned int flags)
+{
+ return sys_sync_file_range(fd, (loff_t)hi_off << 32 | lo_off,
+ (loff_t)hi_nbytes << 32 | lo_nbytes, flags);
+}
+
+asmlinkage long parisc_fallocate(int fd, int mode, u32 offhi, u32 offlo,
+ u32 lenhi, u32 lenlo)
+{
+ return sys_fallocate(fd, mode, ((u64)offhi << 32) | offlo,
+ ((u64)lenhi << 32) | lenlo);
+}
+
asmlinkage unsigned long sys_alloc_hugepages(int key, unsigned long addr, unsigned long len, int prot, int flag)
{
return -ENOMEM;
@@ -240,3 +374,18 @@ asmlinkage int sys_free_hugepages(unsigned long addr)
{
return -EINVAL;
}
+
+long parisc_personality(unsigned long personality)
+{
+ long err;
+
+ if (personality(current->personality) == PER_LINUX32
+ && personality(personality) == PER_LINUX)
+ personality = (personality & ~PER_MASK) | PER_LINUX32;
+
+ err = sys_personality(personality);
+ if (personality(err) == PER_LINUX32)
+ err = (err & ~PER_MASK) | PER_LINUX;
+
+ return err;
+}
diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c
index 61356901841..93c1963d76f 100644
--- a/arch/parisc/kernel/sys_parisc32.c
+++ b/arch/parisc/kernel/sys_parisc32.c
@@ -4,92 +4,16 @@
* Copyright (C) 2000-2001 Hewlett Packard Company
* Copyright (C) 2000 John Marvin
* Copyright (C) 2001 Matthew Wilcox
+ * Copyright (C) 2014 Helge Deller <deller@gmx.de>
*
* These routines maintain argument size conversion between 32bit and 64bit
* environment. Based heavily on sys_ia32.c and sys_sparc32.c.
*/
-#include <linux/config.h>
#include <linux/compat.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/file.h>
-#include <linux/signal.h>
-#include <linux/resource.h>
-#include <linux/times.h>
-#include <linux/utsname.h>
-#include <linux/time.h>
-#include <linux/timex.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/sem.h>
-#include <linux/msg.h>
-#include <linux/shm.h>
-#include <linux/slab.h>
-#include <linux/uio.h>
-#include <linux/nfs_fs.h>
-#include <linux/ncp_fs.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/nfsd/nfsd.h>
-#include <linux/nfsd/cache.h>
-#include <linux/nfsd/xdr.h>
-#include <linux/nfsd/syscall.h>
-#include <linux/poll.h>
-#include <linux/personality.h>
-#include <linux/stat.h>
-#include <linux/highmem.h>
-#include <linux/highuid.h>
-#include <linux/mman.h>
-#include <linux/binfmts.h>
-#include <linux/namei.h>
-#include <linux/vfs.h>
-#include <linux/ptrace.h>
-#include <linux/swap.h>
#include <linux/syscalls.h>
-#include <asm/types.h>
-#include <asm/uaccess.h>
-#include <asm/semaphore.h>
-#include <asm/mmu_context.h>
-
-#include "sys32.h"
-
-#undef DEBUG
-
-#ifdef DEBUG
-#define DBG(x) printk x
-#else
-#define DBG(x)
-#endif
-
-/*
- * sys32_execve() executes a new program.
- */
-
-asmlinkage int sys32_execve(struct pt_regs *regs)
-{
- int error;
- char *filename;
-
- DBG(("sys32_execve(%p) r26 = 0x%lx\n", regs, regs->gr[26]));
- filename = getname((const char __user *) regs->gr[26]);
- error = PTR_ERR(filename);
- if (IS_ERR(filename))
- goto out;
- error = compat_do_execve(filename, compat_ptr(regs->gr[25]),
- compat_ptr(regs->gr[24]), regs);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
- }
- putname(filename);
-out:
-
- return error;
-}
asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23,
int r22, int r21, int r20)
@@ -99,622 +23,11 @@ asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23,
return -ENOSYS;
}
-#ifdef CONFIG_SYSCTL
-
-struct __sysctl_args32 {
- u32 name;
- int nlen;
- u32 oldval;
- u32 oldlenp;
- u32 newval;
- u32 newlen;
- u32 __unused[4];
-};
-
-asmlinkage long sys32_sysctl(struct __sysctl_args32 __user *args)
-{
- struct __sysctl_args32 tmp;
- int error;
- unsigned int oldlen32;
- size_t oldlen, *oldlenp = NULL;
- unsigned long addr = (((long __force)&args->__unused[0]) + 7) & ~7;
- extern int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp,
- void *newval, size_t newlen);
-
- DBG(("sysctl32(%p)\n", args));
-
- if (copy_from_user(&tmp, args, sizeof(tmp)))
- return -EFAULT;
-
- if (tmp.oldval && tmp.oldlenp) {
- /* Duh, this is ugly and might not work if sysctl_args
- is in read-only memory, but do_sysctl does indirectly
- a lot of uaccess in both directions and we'd have to
- basically copy the whole sysctl.c here, and
- glibc's __sysctl uses rw memory for the structure
- anyway. */
- /* a possibly better hack than this, which will avoid the
- * problem if the struct is read only, is to push the
- * 'oldlen' value out to the user's stack instead. -PB
- */
- if (get_user(oldlen32, (u32 *)(u64)tmp.oldlenp))
- return -EFAULT;
- oldlen = oldlen32;
- if (put_user(oldlen, (size_t *)addr))
- return -EFAULT;
- oldlenp = (size_t *)addr;
- }
-
- lock_kernel();
- error = do_sysctl((int *)(u64)tmp.name, tmp.nlen, (void *)(u64)tmp.oldval,
- oldlenp, (void *)(u64)tmp.newval, tmp.newlen);
- unlock_kernel();
- if (oldlenp) {
- if (!error) {
- if (get_user(oldlen, (size_t *)addr)) {
- error = -EFAULT;
- } else {
- oldlen32 = oldlen;
- if (put_user(oldlen32, (u32 *)(u64)tmp.oldlenp))
- error = -EFAULT;
- }
- }
- if (copy_to_user(&args->__unused[0], tmp.__unused, sizeof(tmp.__unused)))
- error = -EFAULT;
- }
- return error;
-}
-
-#endif /* CONFIG_SYSCTL */
-
-asmlinkage long sys32_sched_rr_get_interval(pid_t pid,
- struct compat_timespec __user *interval)
-{
- struct timespec t;
- int ret;
-
- KERNEL_SYSCALL(ret, sys_sched_rr_get_interval, pid, (struct timespec __user *)&t);
- if (put_compat_timespec(&t, interval))
- return -EFAULT;
- return ret;
-}
-
-static int
-put_compat_timeval(struct compat_timeval __user *u, struct timeval *t)
-{
- struct compat_timeval t32;
- t32.tv_sec = t->tv_sec;
- t32.tv_usec = t->tv_usec;
- return copy_to_user(u, &t32, sizeof t32);
-}
-
-static inline long get_ts32(struct timespec *o, struct compat_timeval __user *i)
-{
- long usec;
-
- if (__get_user(o->tv_sec, &i->tv_sec))
- return -EFAULT;
- if (__get_user(usec, &i->tv_usec))
- return -EFAULT;
- o->tv_nsec = usec * 1000;
- return 0;
-}
-
-asmlinkage int
-sys32_gettimeofday(struct compat_timeval __user *tv, struct timezone __user *tz)
-{
- extern void do_gettimeofday(struct timeval *tv);
-
- if (tv) {
- struct timeval ktv;
- do_gettimeofday(&ktv);
- if (put_compat_timeval(tv, &ktv))
- return -EFAULT;
- }
- if (tz) {
- extern struct timezone sys_tz;
- if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
- return -EFAULT;
- }
- return 0;
-}
-
-asmlinkage
-int sys32_settimeofday(struct compat_timeval __user *tv, struct timezone __user *tz)
-{
- struct timespec kts;
- struct timezone ktz;
-
- if (tv) {
- if (get_ts32(&kts, tv))
- return -EFAULT;
- }
- if (tz) {
- if (copy_from_user(&ktz, tz, sizeof(ktz)))
- return -EFAULT;
- }
-
- return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL);
-}
-
-int cp_compat_stat(struct kstat *stat, struct compat_stat __user *statbuf)
-{
- int err;
-
- if (stat->size > MAX_NON_LFS || !new_valid_dev(stat->dev) ||
- !new_valid_dev(stat->rdev))
- return -EOVERFLOW;
-
- err = put_user(new_encode_dev(stat->dev), &statbuf->st_dev);
- err |= put_user(stat->ino, &statbuf->st_ino);
- err |= put_user(stat->mode, &statbuf->st_mode);
- err |= put_user(stat->nlink, &statbuf->st_nlink);
- err |= put_user(0, &statbuf->st_reserved1);
- err |= put_user(0, &statbuf->st_reserved2);
- err |= put_user(new_encode_dev(stat->rdev), &statbuf->st_rdev);
- err |= put_user(stat->size, &statbuf->st_size);
- err |= put_user(stat->atime.tv_sec, &statbuf->st_atime);
- err |= put_user(stat->atime.tv_nsec, &statbuf->st_atime_nsec);
- err |= put_user(stat->mtime.tv_sec, &statbuf->st_mtime);
- err |= put_user(stat->mtime.tv_nsec, &statbuf->st_mtime_nsec);
- err |= put_user(stat->ctime.tv_sec, &statbuf->st_ctime);
- err |= put_user(stat->ctime.tv_nsec, &statbuf->st_ctime_nsec);
- err |= put_user(stat->blksize, &statbuf->st_blksize);
- err |= put_user(stat->blocks, &statbuf->st_blocks);
- err |= put_user(0, &statbuf->__unused1);
- err |= put_user(0, &statbuf->__unused2);
- err |= put_user(0, &statbuf->__unused3);
- err |= put_user(0, &statbuf->__unused4);
- err |= put_user(0, &statbuf->__unused5);
- err |= put_user(0, &statbuf->st_fstype); /* not avail */
- err |= put_user(0, &statbuf->st_realdev); /* not avail */
- err |= put_user(0, &statbuf->st_basemode); /* not avail */
- err |= put_user(0, &statbuf->st_spareshort);
- err |= put_user(stat->uid, &statbuf->st_uid);
- err |= put_user(stat->gid, &statbuf->st_gid);
- err |= put_user(0, &statbuf->st_spare4[0]);
- err |= put_user(0, &statbuf->st_spare4[1]);
- err |= put_user(0, &statbuf->st_spare4[2]);
-
- return err;
-}
-
-struct linux32_dirent {
- u32 d_ino;
- compat_off_t d_off;
- u16 d_reclen;
- char d_name[1];
-};
-
-struct old_linux32_dirent {
- u32 d_ino;
- u32 d_offset;
- u16 d_namlen;
- char d_name[1];
-};
-
-struct getdents32_callback {
- struct linux32_dirent __user * current_dir;
- struct linux32_dirent __user * previous;
- int count;
- int error;
-};
-
-struct readdir32_callback {
- struct old_linux32_dirent __user * dirent;
- int count;
-};
-
-#define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1)))
-#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
-static int
-filldir32 (void *__buf, const char *name, int namlen, loff_t offset, ino_t ino,
- unsigned int d_type)
-{
- struct linux32_dirent __user * dirent;
- struct getdents32_callback * buf = (struct getdents32_callback *) __buf;
- int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1, 4);
-
- buf->error = -EINVAL; /* only used if we fail.. */
- if (reclen > buf->count)
- return -EINVAL;
- dirent = buf->previous;
- if (dirent)
- put_user(offset, &dirent->d_off);
- dirent = buf->current_dir;
- buf->previous = dirent;
- put_user(ino, &dirent->d_ino);
- put_user(reclen, &dirent->d_reclen);
- copy_to_user(dirent->d_name, name, namlen);
- put_user(0, dirent->d_name + namlen);
- dirent = ((void __user *)dirent) + reclen;
- buf->current_dir = dirent;
- buf->count -= reclen;
- return 0;
-}
-
-asmlinkage long
-sys32_getdents (unsigned int fd, void __user * dirent, unsigned int count)
-{
- struct file * file;
- struct linux32_dirent __user * lastdirent;
- struct getdents32_callback buf;
- int error;
-
- error = -EBADF;
- file = fget(fd);
- if (!file)
- goto out;
-
- buf.current_dir = (struct linux32_dirent __user *) dirent;
- buf.previous = NULL;
- buf.count = count;
- buf.error = 0;
-
- error = vfs_readdir(file, filldir32, &buf);
- if (error < 0)
- goto out_putf;
- error = buf.error;
- lastdirent = buf.previous;
- if (lastdirent) {
- put_user(file->f_pos, &lastdirent->d_off);
- error = count - buf.count;
- }
-
-out_putf:
- fput(file);
-out:
- return error;
-}
-
-static int
-fillonedir32 (void * __buf, const char * name, int namlen, loff_t offset, ino_t ino,
- unsigned int d_type)
-{
- struct readdir32_callback * buf = (struct readdir32_callback *) __buf;
- struct old_linux32_dirent __user * dirent;
-
- if (buf->count)
- return -EINVAL;
- buf->count++;
- dirent = buf->dirent;
- put_user(ino, &dirent->d_ino);
- put_user(offset, &dirent->d_offset);
- put_user(namlen, &dirent->d_namlen);
- copy_to_user(dirent->d_name, name, namlen);
- put_user(0, dirent->d_name + namlen);
- return 0;
-}
-
-asmlinkage long
-sys32_readdir (unsigned int fd, void __user * dirent, unsigned int count)
-{
- int error;
- struct file * file;
- struct readdir32_callback buf;
-
- error = -EBADF;
- file = fget(fd);
- if (!file)
- goto out;
-
- buf.count = 0;
- buf.dirent = dirent;
-
- error = vfs_readdir(file, fillonedir32, &buf);
- if (error >= 0)
- error = buf.count;
- fput(file);
-out:
- return error;
-}
-
-/*** copied from mips64 ***/
-/*
- * Ooo, nasty. We need here to frob 32-bit unsigned longs to
- * 64-bit unsigned longs.
- */
-
-static inline int
-get_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset)
-{
- n = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32));
- if (ufdset) {
- unsigned long odd;
-
- if (!access_ok(VERIFY_WRITE, ufdset, n*sizeof(u32)))
- return -EFAULT;
-
- odd = n & 1UL;
- n &= ~1UL;
- while (n) {
- unsigned long h, l;
- __get_user(l, ufdset);
- __get_user(h, ufdset+1);
- ufdset += 2;
- *fdset++ = h << 32 | l;
- n -= 2;
- }
- if (odd)
- __get_user(*fdset, ufdset);
- } else {
- /* Tricky, must clear full unsigned long in the
- * kernel fdset at the end, this makes sure that
- * actually happens.
- */
- memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32));
- }
- return 0;
-}
-
-static inline void
-set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset)
-{
- unsigned long odd;
- n = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32));
-
- if (!ufdset)
- return;
-
- odd = n & 1UL;
- n &= ~1UL;
- while (n) {
- unsigned long h, l;
- l = *fdset++;
- h = l >> 32;
- __put_user(l, ufdset);
- __put_user(h, ufdset+1);
- ufdset += 2;
- n -= 2;
- }
- if (odd)
- __put_user(*fdset, ufdset);
-}
-
-struct msgbuf32 {
- int mtype;
- char mtext[1];
-};
-
-asmlinkage long sys32_msgsnd(int msqid,
- struct msgbuf32 __user *umsgp32,
- size_t msgsz, int msgflg)
-{
- struct msgbuf *mb;
- struct msgbuf32 mb32;
- int err;
-
- if ((mb = kmalloc(msgsz + sizeof *mb + 4, GFP_KERNEL)) == NULL)
- return -ENOMEM;
-
- err = get_user(mb32.mtype, &umsgp32->mtype);
- mb->mtype = mb32.mtype;
- err |= copy_from_user(mb->mtext, &umsgp32->mtext, msgsz);
-
- if (err)
- err = -EFAULT;
- else
- KERNEL_SYSCALL(err, sys_msgsnd, msqid, (struct msgbuf __user *)mb, msgsz, msgflg);
-
- kfree(mb);
- return err;
-}
-
-asmlinkage long sys32_msgrcv(int msqid,
- struct msgbuf32 __user *umsgp32,
- size_t msgsz, long msgtyp, int msgflg)
-{
- struct msgbuf *mb;
- struct msgbuf32 mb32;
- int err, len;
-
- if ((mb = kmalloc(msgsz + sizeof *mb + 4, GFP_KERNEL)) == NULL)
- return -ENOMEM;
-
- KERNEL_SYSCALL(err, sys_msgrcv, msqid, (struct msgbuf __user *)mb, msgsz, msgtyp, msgflg);
-
- if (err >= 0) {
- len = err;
- mb32.mtype = mb->mtype;
- err = put_user(mb32.mtype, &umsgp32->mtype);
- err |= copy_to_user(&umsgp32->mtext, mb->mtext, len);
- if (err)
- err = -EFAULT;
- else
- err = len;
- }
-
- kfree(mb);
- return err;
-}
-
-asmlinkage int sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, s32 count)
-{
- mm_segment_t old_fs = get_fs();
- int ret;
- off_t of;
-
- if (offset && get_user(of, offset))
- return -EFAULT;
-
- set_fs(KERNEL_DS);
- ret = sys_sendfile(out_fd, in_fd, offset ? (off_t __user *)&of : NULL, count);
- set_fs(old_fs);
-
- if (offset && put_user(of, offset))
- return -EFAULT;
-
- return ret;
-}
-
-asmlinkage int sys32_sendfile64(int out_fd, int in_fd, compat_loff_t __user *offset, s32 count)
-{
- mm_segment_t old_fs = get_fs();
- int ret;
- loff_t lof;
-
- if (offset && get_user(lof, offset))
- return -EFAULT;
-
- set_fs(KERNEL_DS);
- ret = sys_sendfile64(out_fd, in_fd, offset ? (loff_t __user *)&lof : NULL, count);
- set_fs(old_fs);
-
- if (offset && put_user(lof, offset))
- return -EFAULT;
-
- return ret;
-}
-
-
-struct timex32 {
- unsigned int modes; /* mode selector */
- int offset; /* time offset (usec) */
- int freq; /* frequency offset (scaled ppm) */
- int maxerror; /* maximum error (usec) */
- int esterror; /* estimated error (usec) */
- int status; /* clock command/status */
- int constant; /* pll time constant */
- int precision; /* clock precision (usec) (read only) */
- int tolerance; /* clock frequency tolerance (ppm)
- * (read only)
- */
- struct compat_timeval time; /* (read only) */
- int tick; /* (modified) usecs between clock ticks */
-
- int ppsfreq; /* pps frequency (scaled ppm) (ro) */
- int jitter; /* pps jitter (us) (ro) */
- int shift; /* interval duration (s) (shift) (ro) */
- int stabil; /* pps stability (scaled ppm) (ro) */
- int jitcnt; /* jitter limit exceeded (ro) */
- int calcnt; /* calibration intervals (ro) */
- int errcnt; /* calibration errors (ro) */
- int stbcnt; /* stability limit exceeded (ro) */
-
- int :32; int :32; int :32; int :32;
- int :32; int :32; int :32; int :32;
- int :32; int :32; int :32; int :32;
-};
-
-asmlinkage long sys32_adjtimex(struct timex32 __user *txc_p32)
-{
- struct timex txc;
- struct timex32 t32;
- int ret;
- extern int do_adjtimex(struct timex *txc);
-
- if(copy_from_user(&t32, txc_p32, sizeof(struct timex32)))
- return -EFAULT;
-#undef CP
-#define CP(x) txc.x = t32.x
- CP(modes); CP(offset); CP(freq); CP(maxerror); CP(esterror);
- CP(status); CP(constant); CP(precision); CP(tolerance);
- CP(time.tv_sec); CP(time.tv_usec); CP(tick); CP(ppsfreq); CP(jitter);
- CP(shift); CP(stabil); CP(jitcnt); CP(calcnt); CP(errcnt);
- CP(stbcnt);
- ret = do_adjtimex(&txc);
-#undef CP
-#define CP(x) t32.x = txc.x
- CP(modes); CP(offset); CP(freq); CP(maxerror); CP(esterror);
- CP(status); CP(constant); CP(precision); CP(tolerance);
- CP(time.tv_sec); CP(time.tv_usec); CP(tick); CP(ppsfreq); CP(jitter);
- CP(shift); CP(stabil); CP(jitcnt); CP(calcnt); CP(errcnt);
- CP(stbcnt);
- return copy_to_user(txc_p32, &t32, sizeof(struct timex32)) ? -EFAULT : ret;
-}
-
-
-struct sysinfo32 {
- s32 uptime;
- u32 loads[3];
- u32 totalram;
- u32 freeram;
- u32 sharedram;
- u32 bufferram;
- u32 totalswap;
- u32 freeswap;
- unsigned short procs;
- u32 totalhigh;
- u32 freehigh;
- u32 mem_unit;
- char _f[12];
-};
-
-/* We used to call sys_sysinfo and translate the result. But sys_sysinfo
- * undoes the good work done elsewhere, and rather than undoing the
- * damage, I decided to just duplicate the code from sys_sysinfo here.
- */
-
-asmlinkage int sys32_sysinfo(struct sysinfo32 __user *info)
-{
- struct sysinfo val;
- int err;
- unsigned long seq;
-
- /* We don't need a memset here because we copy the
- * struct to userspace once element at a time.
- */
-
- do {
- seq = read_seqbegin(&xtime_lock);
- val.uptime = jiffies / HZ;
-
- val.loads[0] = avenrun[0] << (SI_LOAD_SHIFT - FSHIFT);
- val.loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT);
- val.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT);
-
- val.procs = nr_threads;
- } while (read_seqretry(&xtime_lock, seq));
-
-
- si_meminfo(&val);
- si_swapinfo(&val);
-
- err = put_user (val.uptime, &info->uptime);
- err |= __put_user (val.loads[0], &info->loads[0]);
- err |= __put_user (val.loads[1], &info->loads[1]);
- err |= __put_user (val.loads[2], &info->loads[2]);
- err |= __put_user (val.totalram, &info->totalram);
- err |= __put_user (val.freeram, &info->freeram);
- err |= __put_user (val.sharedram, &info->sharedram);
- err |= __put_user (val.bufferram, &info->bufferram);
- err |= __put_user (val.totalswap, &info->totalswap);
- err |= __put_user (val.freeswap, &info->freeswap);
- err |= __put_user (val.procs, &info->procs);
- err |= __put_user (val.totalhigh, &info->totalhigh);
- err |= __put_user (val.freehigh, &info->freehigh);
- err |= __put_user (val.mem_unit, &info->mem_unit);
- return err ? -EFAULT : 0;
-}
-
-
-/* lseek() needs a wrapper because 'offset' can be negative, but the top
- * half of the argument has been zeroed by syscall.S.
- */
-
-asmlinkage int sys32_lseek(unsigned int fd, int offset, unsigned int origin)
-{
- return sys_lseek(fd, offset, origin);
-}
-
-asmlinkage long sys32_semctl(int semid, int semnum, int cmd, union semun arg)
-{
- union semun u;
-
- if (cmd == SETVAL) {
- /* Ugh. arg is a union of int,ptr,ptr,ptr, so is 8 bytes.
- * The int should be in the first 4, but our argument
- * frobbing has left it in the last 4.
- */
- u.val = *((int *)&arg + 1);
- return sys_semctl (semid, semnum, cmd, u);
- }
- return sys_semctl (semid, semnum, cmd, arg);
-}
-
-long sys32_lookup_dcookie(u32 cookie_high, u32 cookie_low, char __user *buf,
- size_t len)
+asmlinkage long sys32_fanotify_mark(compat_int_t fanotify_fd, compat_uint_t flags,
+ compat_uint_t mask0, compat_uint_t mask1, compat_int_t dfd,
+ const char __user * pathname)
{
- return sys_lookup_dcookie((u64)cookie_high << 32 | cookie_low,
- buf, len);
+ return sys_fanotify_mark(fanotify_fd, flags,
+ ((__u64)mask1 << 32) | mask0,
+ dfd, pathname);
}
diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S
index b29b76b42bb..83878601103 100644
--- a/arch/parisc/kernel/syscall.S
+++ b/arch/parisc/kernel/syscall.S
@@ -1,21 +1,46 @@
/*
* Linux/PA-RISC Project (http://www.parisc-linux.org/)
*
- * System call entry code Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai>
+ * System call entry code / Linux gateway page
+ * Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai>
* Licensed under the GNU GPL.
* thanks to Philipp Rumpf, Mike Shaver and various others
* sorry about the wall, puffin..
*/
-#include <linux/config.h> /* for CONFIG_SMP */
+
+/*
+How does the Linux gateway page on PA-RISC work?
+------------------------------------------------
+The Linux gateway page on PA-RISC is "special".
+It actually has PAGE_GATEWAY bits set (this is linux terminology; in parisc
+terminology it's Execute, promote to PL0) in the page map. So anything
+executing on this page executes with kernel level privilege (there's more to it
+than that: to have this happen, you also have to use a branch with a ,gate
+completer to activate the privilege promotion). The upshot is that everything
+that runs on the gateway page runs at kernel privilege but with the current
+user process address space (although you have access to kernel space via %sr2).
+For the 0x100 syscall entry, we redo the space registers to point to the kernel
+address space (preserving the user address space in %sr3), move to wide mode if
+required, save the user registers and branch into the kernel syscall entry
+point. For all the other functions, we execute at kernel privilege but don't
+flip address spaces. The basic upshot of this is that these code snippets are
+executed atomically (because the kernel can't be pre-empted) and they may
+perform architecturally forbidden (to PL3) operations (like setting control
+registers).
+*/
+
#include <asm/asm-offsets.h>
#include <asm/unistd.h>
#include <asm/errno.h>
+#include <asm/page.h>
#include <asm/psw.h>
#include <asm/thread_info.h>
-
#include <asm/assembly.h>
#include <asm/processor.h>
+#include <asm/cache.h>
+
+#include <linux/linkage.h>
/* We fill the empty parts of the gateway page with
* something that will kill the kernel or a
@@ -23,29 +48,12 @@
*/
#define KILL_INSN break 0,0
-#ifdef CONFIG_64BIT
- .level 2.0w
-#else
- .level 1.1
-#endif
-
-#ifndef CONFIG_64BIT
- .macro fixup_branch,lbl
- b \lbl
- .endm
-#else
- .macro fixup_branch,lbl
- ldil L%\lbl, %r1
- ldo R%\lbl(%r1), %r1
- bv,n %r0(%r1)
- .endm
-#endif
+ .level LEVEL
.text
.import syscall_exit,code
.import syscall_exit_rfi,code
- .export linux_gateway_page
/* Linux gateway page is aliased to virtual page 0 in the kernel
* address space. Since it is a gateway page it cannot be
@@ -55,26 +63,25 @@
* pointers.
*/
- .align 4096
-linux_gateway_page:
+ .align PAGE_SIZE
+ENTRY(linux_gateway_page)
/* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
.rept 44
KILL_INSN
.endr
- /* ADDRESS 0xb0 to 0xb4, lws uses 1 insns for entry */
+ /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */
/* Light-weight-syscall entry must always be located at 0xb0 */
/* WARNING: Keep this number updated with table size changes */
#define __NR_lws_entries (2)
lws_entry:
- /* Unconditional branch to lws_start, located on the
- same gateway page */
- b,n lws_start
+ gate lws_start, %r0 /* increase privilege */
+ depi 3, 31, 2, %r31 /* Ensure we return into user mode. */
- /* Fill from 0xb4 to 0xe0 */
- .rept 11
+ /* Fill from 0xb8 to 0xe0 */
+ .rept 10
KILL_INSN
.endr
@@ -164,7 +171,7 @@ linux_gateway_entry:
#endif
STREG %r2, TASK_PT_GR30(%r1) /* ... and save it */
- STREG %r20, TASK_PT_GR20(%r1)
+ STREG %r20, TASK_PT_GR20(%r1) /* Syscall number */
STREG %r21, TASK_PT_GR21(%r1)
STREG %r22, TASK_PT_GR22(%r1)
STREG %r23, TASK_PT_GR23(%r1) /* 4th argument */
@@ -173,7 +180,7 @@ linux_gateway_entry:
STREG %r26, TASK_PT_GR26(%r1) /* 1st argument */
STREG %r27, TASK_PT_GR27(%r1) /* user dp */
STREG %r28, TASK_PT_GR28(%r1) /* return value 0 */
- STREG %r28, TASK_PT_ORIG_R28(%r1) /* return value 0 (saved for signals) */
+ STREG %r0, TASK_PT_ORIG_R28(%r1) /* don't prohibit restarts */
STREG %r29, TASK_PT_GR29(%r1) /* return value 1 */
STREG %r31, TASK_PT_GR31(%r1) /* preserve syscall return ptr */
@@ -197,9 +204,10 @@ linux_gateway_entry:
/* Are we being ptraced? */
mfctl %cr30, %r1
- LDREG TI_TASK(%r1),%r1
- LDREG TASK_PTRACE(%r1), %r1
- bb,<,n %r1,31,.Ltracesys
+ LDREG TI_FLAGS(%r1),%r1
+ ldi _TIF_SYSCALL_TRACE_MASK, %r19
+ and,COND(=) %r1, %r19, %r0
+ b,n .Ltracesys
/* Note! We cannot use the syscall table that is mapped
nearby since the gateway page is mapped execute-only. */
@@ -215,7 +223,7 @@ linux_gateway_entry:
ldil L%sys_call_table, %r1
ldo R%sys_call_table(%r1), %r19
#endif
- comiclr,>>= __NR_Linux_syscalls, %r20, %r0
+ comiclr,>> __NR_Linux_syscalls, %r20, %r0
b,n .Lsyscall_nosys
LDREGX %r20(%r19), %r19
@@ -304,26 +312,34 @@ tracesys:
STREG %r18,PT_GR18(%r2)
/* Finished saving things for the debugger */
- ldil L%syscall_trace,%r1
+ copy %r2,%r26
+ ldil L%do_syscall_trace_enter,%r1
ldil L%tracesys_next,%r2
- be R%syscall_trace(%sr7,%r1)
+ be R%do_syscall_trace_enter(%sr7,%r1)
ldo R%tracesys_next(%r2),%r2
-tracesys_next:
+tracesys_next:
+ /* do_syscall_trace_enter either returned the syscallno, or -1L,
+ * so we skip restoring the PT_GR20 below, since we pulled it from
+ * task->thread.regs.gr[20] above.
+ */
+ copy %ret0,%r20
ldil L%sys_call_table,%r1
ldo R%sys_call_table(%r1), %r19
ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */
LDREG TI_TASK(%r1), %r1
- LDREG TASK_PT_GR20(%r1), %r20
LDREG TASK_PT_GR26(%r1), %r26 /* Restore the users args */
LDREG TASK_PT_GR25(%r1), %r25
LDREG TASK_PT_GR24(%r1), %r24
LDREG TASK_PT_GR23(%r1), %r23
-#ifdef CONFIG_64BIT
LDREG TASK_PT_GR22(%r1), %r22
LDREG TASK_PT_GR21(%r1), %r21
+#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
+#else
+ stw %r22, -52(%r30) /* 5th argument */
+ stw %r21, -56(%r30) /* 6th argument */
#endif
comiclr,>>= __NR_Linux_syscalls, %r20, %r0
@@ -352,7 +368,8 @@ tracesys_exit:
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
- bl syscall_trace, %r2
+ ldo TASK_REGS(%r1),%r26
+ bl do_syscall_trace_exit,%r2
STREG %r28,TASK_PT_GR28(%r1) /* save return value now */
ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */
LDREG TI_TASK(%r1), %r1
@@ -369,29 +386,63 @@ tracesys_exit:
tracesys_sigexit:
ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */
- LDREG 0(%r1), %r1
+ LDREG TI_TASK(%r1), %r1
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
- bl syscall_trace, %r2
- nop
+ bl do_syscall_trace_exit,%r2
+ ldo TASK_REGS(%r1),%r26
ldil L%syscall_exit_rfi,%r1
be,n R%syscall_exit_rfi(%sr7,%r1)
/*********************************************************
- Light-weight-syscall code
+ 32/64-bit Light-Weight-Syscall ABI
+
+ * - Indicates a hint for userspace inline asm
+ implementations.
+
+ Syscall number (caller-saves)
+ - %r20
+ * In asm clobber.
+
+ Argument registers (caller-saves)
+ - %r26, %r25, %r24, %r23, %r22
+ * In asm input.
+
+ Return registers (caller-saves)
+ - %r28 (return), %r21 (errno)
+ * In asm output.
- r20 - lws number
- r26,r25,r24,r23,r22 - Input registers
- r28 - Function return register
- r21 - Error code.
+ Caller-saves registers
+ - %r1, %r27, %r29
+ - %r2 (return pointer)
+ - %r31 (ble link register)
+ * In asm clobber.
- Scracth: Any of the above that aren't being
- currently used, including r1.
+ Callee-saves registers
+ - %r3-%r18
+ - %r30 (stack pointer)
+ * Not in asm clobber.
- Return pointer: r31 (Not usable)
+ If userspace is 32-bit:
+ Callee-saves registers
+ - %r19 (32-bit PIC register)
+
+ Differences from 32-bit calling convention:
+ - Syscall number in %r20
+ - Additional argument register %r22 (arg4)
+ - Callee-saves %r19.
+
+ If userspace is 64-bit:
+ Callee-saves registers
+ - %r27 (64-bit PIC register)
+
+ Differences from 64-bit calling convention:
+ - Syscall number in %r20
+ - Additional argument register %r22 (arg4)
+ - Callee-saves %r27.
Error codes returned by entry path:
@@ -399,9 +450,6 @@ tracesys_sigexit:
*********************************************************/
lws_start:
- /* Gate and ensure we return to userspace */
- gate .+8, %r0
- depi 3, 31, 2, %r31 /* Ensure we return to userspace */
#ifdef CONFIG_64BIT
/* FIXME: If we are a 64-bit kernel just
@@ -418,7 +466,7 @@ lws_start:
#endif
/* Is the lws entry number valid? */
- comiclr,>>= __NR_lws_entries, %r20, %r0
+ comiclr,>> __NR_lws_entries, %r20, %r0
b,n lws_exit_nosys
/* WARNING: Trashing sr2 and sr3 */
@@ -449,7 +497,7 @@ lws_exit:
/* now reset the lowest bit of sp if it was set */
xor %r30,%r1,%r30
#endif
- be,n 0(%sr3, %r31)
+ be,n 0(%sr7, %r31)
@@ -489,7 +537,8 @@ lws_compare_and_swap64:
b,n lws_compare_and_swap
#else
/* If we are not a 64-bit kernel, then we don't
- * implement having 64-bit input registers
+ * have 64-bit input registers, and calling
+ * the 64-bit LWS CAS returns ENOSYS.
*/
b,n lws_exit_nosys
#endif
@@ -504,7 +553,6 @@ lws_compare_and_swap32:
#endif
lws_compare_and_swap:
-#ifdef CONFIG_SMP
/* Load start of lock table */
ldil L%lws_lock_start, %r20
ldo R%lws_lock_start(%r20), %r28
@@ -518,7 +566,7 @@ lws_compare_and_swap:
shlw %r20, 4, %r20
add %r20, %r28, %r20
-# ifdef ENABLE_LWS_DEBUG
+# if ENABLE_LWS_DEBUG
/*
DEBUG, check for deadlock!
If the thread register values are the same
@@ -527,6 +575,7 @@ lws_compare_and_swap:
We *must* giveup this call and fail.
*/
ldw 4(%sr2,%r20), %r28 /* Load thread register */
+ /* WARNING: If cr27 cycles to the same value we have problems */
mfctl %cr27, %r21 /* Get current thread register */
cmpb,<>,n %r21, %r28, cas_lock /* Called recursive? */
b lws_exit /* Return error! */
@@ -540,14 +589,15 @@ cas_nocontend:
# endif
/* ENABLE_LWS_DEBUG */
- ldcw 0(%sr2,%r20), %r28 /* Try to acquire the lock */
+ rsm PSW_SM_I, %r0 /* Disable interrupts */
+ /* COW breaks can cause contention on UP systems */
+ LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */
cmpb,<>,n %r0, %r28, cas_action /* Did we get it? */
cas_wouldblock:
ldo 2(%r0), %r28 /* 2nd case */
+ ssm PSW_SM_I, %r0
b lws_exit /* Contended... */
ldo -EAGAIN(%r0), %r21 /* Spin in userspace */
-#endif
-/* CONFIG_SMP */
/*
prev = *addr;
@@ -566,36 +616,35 @@ cas_wouldblock:
perspective
*/
cas_action:
-#if defined CONFIG_SMP && defined ENABLE_LWS_DEBUG
+#if defined CONFIG_SMP && ENABLE_LWS_DEBUG
/* DEBUG */
mfctl %cr27, %r1
stw %r1, 4(%sr2,%r20)
#endif
/* The load and store could fail */
-1: ldw 0(%sr3,%r26), %r28
+1: ldw,ma 0(%sr3,%r26), %r28
sub,<> %r28, %r25, %r0
-2: stw %r24, 0(%sr3,%r26)
-#ifdef CONFIG_SMP
+2: stw,ma %r24, 0(%sr3,%r26)
/* Free lock */
- stw %r20, 0(%sr2,%r20)
-# ifdef ENABLE_LWS_DEBUG
+ stw,ma %r20, 0(%sr2,%r20)
+#if ENABLE_LWS_DEBUG
/* Clear thread register indicator */
stw %r0, 4(%sr2,%r20)
-# endif
#endif
+ /* Enable interrupts */
+ ssm PSW_SM_I, %r0
/* Return to userspace, set no error */
b lws_exit
copy %r0, %r21
3:
- /* Error occured on load or store */
-#ifdef CONFIG_SMP
+ /* Error occurred on load or store */
/* Free lock */
stw %r20, 0(%sr2,%r20)
-# ifdef ENABLE_LWS_DEBUG
+#if ENABLE_LWS_DEBUG
stw %r0, 4(%sr2,%r20)
-# endif
#endif
+ ssm PSW_SM_I, %r0
b lws_exit
ldo -EFAULT(%r0),%r21 /* set errno */
nop
@@ -606,86 +655,56 @@ cas_action:
/* Two exception table entries, one for the load,
the other for the store. Either return -EFAULT.
Each of the entries must be relocated. */
- .section __ex_table,"aw"
-#ifdef CONFIG_64BIT
- /* Pad the address calculation */
- .word 0,(2b - linux_gateway_page)
- .word 0,(3b - linux_gateway_page)
-#else
- .word (2b - linux_gateway_page)
- .word (3b - linux_gateway_page)
-#endif
- .previous
-
- .section __ex_table,"aw"
-#ifdef CONFIG_64BIT
- /* Pad the address calculation */
- .word 0,(1b - linux_gateway_page)
- .word 0,(3b - linux_gateway_page)
-#else
- .word (1b - linux_gateway_page)
- .word (3b - linux_gateway_page)
-#endif
- .previous
+ ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 3b-linux_gateway_page)
+ ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 3b-linux_gateway_page)
-end_compare_and_swap:
/* Make sure nothing else is placed on this page */
- .align 4096
- .export end_linux_gateway_page
-end_linux_gateway_page:
+ .align PAGE_SIZE
+END(linux_gateway_page)
+ENTRY(end_linux_gateway_page)
/* Relocate symbols assuming linux_gateway_page is mapped
to virtual address 0x0 */
-#ifdef CONFIG_64BIT
- /* FIXME: The code will always be on the gateay page
- and thus it will be on the first 4k, the
- assembler seems to think that the final
- subtraction result is only a word in
- length, so we pad the value.
- */
-#define LWS_ENTRY(_name_) .word 0,(lws_##_name_ - linux_gateway_page)
-#else
-#define LWS_ENTRY(_name_) .word (lws_##_name_ - linux_gateway_page)
-#endif
- .align 4096
+#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page)
+
+ .section .rodata,"a"
+
+ .align 8
/* Light-weight-syscall table */
/* Start of lws table. */
- .export lws_table
-.Llws_table:
-lws_table:
+ENTRY(lws_table)
LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic compare and swap */
LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic compare and swap */
+END(lws_table)
/* End of lws table */
- .align 4096
- .export sys_call_table
-.Lsys_call_table:
-sys_call_table:
+ .align 8
+ENTRY(sys_call_table)
#include "syscall_table.S"
+END(sys_call_table)
#ifdef CONFIG_64BIT
- .align 4096
- .export sys_call_table64
-.Lsys_call_table64:
-sys_call_table64:
+ .align 8
+ENTRY(sys_call_table64)
#define SYSCALL_TABLE_64BIT
#include "syscall_table.S"
+END(sys_call_table64)
#endif
-#ifdef CONFIG_SMP
/*
All light-weight-syscall atomic operations
will use this set of locks
+
+ NOTE: The lws_lock_start symbol must be
+ at least 16-byte aligned for safe use
+ with ldcw.
*/
.section .data
- .align 4096
- .export lws_lock_start
-.Llws_lock_start:
-lws_lock_start:
+ .align L1_CACHE_BYTES
+ENTRY(lws_lock_start)
/* lws locks */
- .align 16
.rept 16
/* Keep locks aligned at 16-bytes */
.word 1
@@ -693,9 +712,8 @@ lws_lock_start:
.word 0
.word 0
.endr
+END(lws_lock_start)
.previous
-#endif
-/* CONFIG_SMP for lws_lock_start */
.end
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
index 32cbc048932..84c5d3a58fa 100644
--- a/arch/parisc/kernel/syscall_table.S
+++ b/arch/parisc/kernel/syscall_table.S
@@ -10,10 +10,10 @@
* Copyright (C) 2000 Grant Grundler <grundler at parisc-linux.org>
* Copyright (C) 2001 Richard Hirst <rhirst with parisc-linux.org>
* Copyright (C) 2001-2002 Ryan Bradetich <rbrad at parisc-linux.org>
- * Copyright (C) 2001 Helge Deller <deller at parisc-linux.org>
+ * Copyright (C) 2001-2007 Helge Deller <deller at parisc-linux.org>
* Copyright (C) 2000-2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org>
* Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org>
- *
+ * Copyright (C) 2005-2006 Kyle McMartin <kyle at parisc-linux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,16 +30,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#undef ENTRY_SAME
-#undef ENTRY_DIFF
-#undef ENTRY_UHOH
-#undef ENTRY_COMP
-#undef ENTRY_OURS
#if defined(CONFIG_64BIT) && !defined(SYSCALL_TABLE_64BIT)
/* Use ENTRY_SAME for 32-bit syscalls which are the same on wide and
* narrow palinux. Use ENTRY_DIFF for those where a 32-bit specific
* implementation is required on wide palinux. Use ENTRY_COMP where
- * the compatability layer has a useful 32-bit implementation.
+ * the compatibility layer has a useful 32-bit implementation.
*/
#define ENTRY_SAME(_name_) .dword sys_##_name_
#define ENTRY_DIFF(_name_) .dword sys32_##_name_
@@ -65,13 +60,13 @@
ENTRY_SAME(fork_wrapper)
ENTRY_SAME(read)
ENTRY_SAME(write)
- ENTRY_SAME(open) /* 5 */
+ ENTRY_COMP(open) /* 5 */
ENTRY_SAME(close)
ENTRY_SAME(waitpid)
ENTRY_SAME(creat)
ENTRY_SAME(link)
ENTRY_SAME(unlink) /* 10 */
- ENTRY_DIFF(execve_wrapper)
+ ENTRY_COMP(execve)
ENTRY_SAME(chdir)
/* See comments in kernel/time.c!!! Maybe we don't need this? */
ENTRY_COMP(time)
@@ -81,7 +76,7 @@
ENTRY_SAME(socket)
/* struct stat is MAYBE identical wide and narrow ?? */
ENTRY_COMP(newstat)
- ENTRY_DIFF(lseek)
+ ENTRY_COMP(lseek)
ENTRY_SAME(getpid) /* 20 */
/* the 'void * data' parameter may need re-packing in wide */
ENTRY_COMP(mount)
@@ -92,7 +87,7 @@
ENTRY_SAME(setuid)
ENTRY_SAME(getuid)
ENTRY_COMP(stime) /* 25 */
- ENTRY_SAME(ptrace)
+ ENTRY_COMP(ptrace)
ENTRY_SAME(alarm)
/* see stat comment */
ENTRY_COMP(newfstat)
@@ -135,7 +130,7 @@
ENTRY_SAME(newuname)
ENTRY_SAME(umask) /* 60 */
ENTRY_SAME(chroot)
- ENTRY_SAME(ustat)
+ ENTRY_COMP(ustat)
ENTRY_SAME(dup2)
ENTRY_SAME(getppid)
ENTRY_SAME(getpgrp) /* 65 */
@@ -154,8 +149,8 @@
ENTRY_COMP(getrlimit)
ENTRY_COMP(getrusage)
/* struct timeval and timezone are maybe?? consistent wide and narrow */
- ENTRY_DIFF(gettimeofday)
- ENTRY_DIFF(settimeofday)
+ ENTRY_COMP(gettimeofday)
+ ENTRY_COMP(settimeofday)
ENTRY_SAME(getgroups) /* 80 */
ENTRY_SAME(setgroups)
/* struct socketaddr... */
@@ -170,8 +165,8 @@
ENTRY_SAME(mmap2)
ENTRY_SAME(mmap) /* 90 */
ENTRY_SAME(munmap)
- ENTRY_SAME(truncate)
- ENTRY_SAME(ftruncate)
+ ENTRY_COMP(truncate)
+ ENTRY_COMP(ftruncate)
ENTRY_SAME(fchmod)
ENTRY_SAME(fchown) /* 95 */
ENTRY_SAME(getpriority)
@@ -197,17 +192,17 @@
/* struct rusage contains longs... */
ENTRY_COMP(wait4)
ENTRY_SAME(swapoff) /* 115 */
- ENTRY_DIFF(sysinfo)
+ ENTRY_COMP(sysinfo)
ENTRY_SAME(shutdown)
ENTRY_SAME(fsync)
ENTRY_SAME(madvise)
ENTRY_SAME(clone_wrapper) /* 120 */
ENTRY_SAME(setdomainname)
- ENTRY_DIFF(sendfile)
+ ENTRY_COMP(sendfile)
/* struct sockaddr... */
ENTRY_SAME(recvfrom)
/* struct timex contains longs */
- ENTRY_DIFF(adjtimex)
+ ENTRY_COMP(adjtimex)
ENTRY_SAME(mprotect) /* 125 */
/* old_sigset_t forced to 32 bits. Beware glibc sigset_t */
ENTRY_COMP(sigprocmask)
@@ -221,15 +216,13 @@
ENTRY_SAME(fchdir)
ENTRY_SAME(bdflush)
ENTRY_SAME(sysfs) /* 135 */
- ENTRY_SAME(personality)
+ ENTRY_OURS(personality)
ENTRY_SAME(ni_syscall) /* for afs_syscall */
ENTRY_SAME(setfsuid)
ENTRY_SAME(setfsgid)
/* I think this might work */
ENTRY_SAME(llseek) /* 140 */
- /* struct linux_dirent has longs, like 'unsigned long d_ino' which
- * almost definitely should be 'ino_t d_ino' but it's too late now */
- ENTRY_DIFF(getdents)
+ ENTRY_COMP(getdents)
/* it is POSSIBLE that select will be OK because even though fd_set
* contains longs, the macros and sizes are clever. */
ENTRY_COMP(select)
@@ -241,7 +234,7 @@
ENTRY_SAME(getsid)
ENTRY_SAME(fdatasync)
/* struct __sysctl_args is a mess */
- ENTRY_DIFF(sysctl)
+ ENTRY_COMP(sysctl)
ENTRY_SAME(mlock) /* 150 */
ENTRY_SAME(munlock)
ENTRY_SAME(mlockall)
@@ -254,27 +247,24 @@
ENTRY_SAME(sched_yield)
ENTRY_SAME(sched_get_priority_max)
ENTRY_SAME(sched_get_priority_min) /* 160 */
- /* These 2 would've worked if someone had defined struct timespec
- * carefully, like timeval for example (which is about the same).
- * Unfortunately it contains a long :-( */
- ENTRY_DIFF(sched_rr_get_interval)
+ ENTRY_COMP(sched_rr_get_interval)
ENTRY_COMP(nanosleep)
ENTRY_SAME(mremap)
ENTRY_SAME(setresuid)
ENTRY_SAME(getresuid) /* 165 */
- ENTRY_DIFF(sigaltstack_wrapper)
+ ENTRY_COMP(sigaltstack)
ENTRY_SAME(ni_syscall) /* query_module */
ENTRY_SAME(poll)
/* structs contain pointers and an in_addr... */
- ENTRY_COMP(nfsservctl)
+ ENTRY_SAME(ni_syscall) /* was nfsservctl */
ENTRY_SAME(setresgid) /* 170 */
ENTRY_SAME(getresgid)
ENTRY_SAME(prctl)
/* signals need a careful review */
ENTRY_SAME(rt_sigreturn_wrapper)
- ENTRY_DIFF(rt_sigaction)
- ENTRY_DIFF(rt_sigprocmask) /* 175 */
- ENTRY_DIFF(rt_sigpending)
+ ENTRY_COMP(rt_sigaction)
+ ENTRY_COMP(rt_sigprocmask) /* 175 */
+ ENTRY_COMP(rt_sigpending)
ENTRY_COMP(rt_sigtimedwait)
/* even though the struct siginfo_t is different, it appears like
* all the paths use values which should be same wide and narrow.
@@ -282,19 +272,19 @@
* to worry about faulting trying to copy in a larger 64-bit
* struct from a 32-bit user-space app.
*/
- ENTRY_SAME(rt_sigqueueinfo)
- ENTRY_SAME(rt_sigsuspend_wrapper) /* not really SAME -- see the code */
+ ENTRY_COMP(rt_sigqueueinfo)
+ ENTRY_COMP(rt_sigsuspend)
ENTRY_SAME(chown) /* 180 */
/* setsockopt() used by iptables: SO_SET_REPLACE/SO_SET_ADD_COUNTERS */
ENTRY_COMP(setsockopt)
- ENTRY_SAME(getsockopt)
+ ENTRY_COMP(getsockopt)
ENTRY_COMP(sendmsg)
ENTRY_COMP(recvmsg)
ENTRY_SAME(semop) /* 185 */
ENTRY_SAME(semget)
- ENTRY_DIFF(semctl)
- ENTRY_DIFF(msgsnd)
- ENTRY_DIFF(msgrcv)
+ ENTRY_COMP(semctl)
+ ENTRY_COMP(msgsnd)
+ ENTRY_COMP(msgrcv)
ENTRY_SAME(msgget) /* 190 */
ENTRY_SAME(msgctl)
ENTRY_SAME(shmat)
@@ -314,32 +304,32 @@
ENTRY_SAME(gettid)
ENTRY_OURS(readahead)
ENTRY_SAME(tkill)
- ENTRY_SAME(sendfile64)
+ ENTRY_COMP(sendfile64)
ENTRY_COMP(futex) /* 210 */
ENTRY_COMP(sched_setaffinity)
ENTRY_COMP(sched_getaffinity)
ENTRY_SAME(ni_syscall) /* set_thread_area */
ENTRY_SAME(ni_syscall) /* get_thread_area */
- ENTRY_SAME(io_setup) /* 215 */
+ ENTRY_COMP(io_setup) /* 215 */
ENTRY_SAME(io_destroy)
- ENTRY_SAME(io_getevents)
- ENTRY_SAME(io_submit)
+ ENTRY_COMP(io_getevents)
+ ENTRY_COMP(io_submit)
ENTRY_SAME(io_cancel)
ENTRY_SAME(alloc_hugepages) /* 220 */
ENTRY_SAME(free_hugepages)
ENTRY_SAME(exit_group)
- ENTRY_DIFF(lookup_dcookie)
+ ENTRY_COMP(lookup_dcookie)
ENTRY_SAME(epoll_create)
ENTRY_SAME(epoll_ctl) /* 225 */
ENTRY_SAME(epoll_wait)
ENTRY_SAME(remap_file_pages)
ENTRY_SAME(semtimedop)
- ENTRY_SAME(mq_open)
+ ENTRY_COMP(mq_open)
ENTRY_SAME(mq_unlink) /* 230 */
- ENTRY_SAME(mq_timedsend)
- ENTRY_SAME(mq_timedreceive)
- ENTRY_SAME(mq_notify)
- ENTRY_SAME(mq_getsetattr)
+ ENTRY_COMP(mq_timedsend)
+ ENTRY_COMP(mq_timedreceive)
+ ENTRY_COMP(mq_notify)
+ ENTRY_COMP(mq_getsetattr)
ENTRY_COMP(waitid) /* 235 */
ENTRY_OURS(fadvise64_64)
ENTRY_SAME(set_tid_address)
@@ -374,5 +364,80 @@
ENTRY_SAME(keyctl)
ENTRY_SAME(ioprio_set)
ENTRY_SAME(ioprio_get)
+ ENTRY_SAME(inotify_init)
+ ENTRY_SAME(inotify_add_watch) /* 270 */
+ ENTRY_SAME(inotify_rm_watch)
+ ENTRY_SAME(migrate_pages)
+ ENTRY_COMP(pselect6)
+ ENTRY_COMP(ppoll)
+ ENTRY_COMP(openat) /* 275 */
+ ENTRY_SAME(mkdirat)
+ ENTRY_SAME(mknodat)
+ ENTRY_SAME(fchownat)
+ ENTRY_COMP(futimesat)
+ ENTRY_SAME(fstatat64) /* 280 */
+ ENTRY_SAME(unlinkat)
+ ENTRY_SAME(renameat)
+ ENTRY_SAME(linkat)
+ ENTRY_SAME(symlinkat)
+ ENTRY_SAME(readlinkat) /* 285 */
+ ENTRY_SAME(fchmodat)
+ ENTRY_SAME(faccessat)
+ ENTRY_SAME(unshare)
+ ENTRY_COMP(set_robust_list)
+ ENTRY_COMP(get_robust_list) /* 290 */
+ ENTRY_SAME(splice)
+ ENTRY_OURS(sync_file_range)
+ ENTRY_SAME(tee)
+ ENTRY_COMP(vmsplice)
+ ENTRY_COMP(move_pages) /* 295 */
+ ENTRY_SAME(getcpu)
+ ENTRY_COMP(epoll_pwait)
+ ENTRY_COMP(statfs64)
+ ENTRY_COMP(fstatfs64)
+ ENTRY_COMP(kexec_load) /* 300 */
+ ENTRY_COMP(utimensat)
+ ENTRY_COMP(signalfd)
+ ENTRY_SAME(ni_syscall) /* was timerfd */
+ ENTRY_SAME(eventfd)
+ ENTRY_OURS(fallocate) /* 305 */
+ ENTRY_SAME(timerfd_create)
+ ENTRY_COMP(timerfd_settime)
+ ENTRY_COMP(timerfd_gettime)
+ ENTRY_COMP(signalfd4)
+ ENTRY_SAME(eventfd2) /* 310 */
+ ENTRY_SAME(epoll_create1)
+ ENTRY_SAME(dup3)
+ ENTRY_SAME(pipe2)
+ ENTRY_SAME(inotify_init1)
+ ENTRY_COMP(preadv) /* 315 */
+ ENTRY_COMP(pwritev)
+ ENTRY_COMP(rt_tgsigqueueinfo)
+ ENTRY_SAME(perf_event_open)
+ ENTRY_COMP(recvmmsg)
+ ENTRY_SAME(accept4) /* 320 */
+ ENTRY_SAME(prlimit64)
+ ENTRY_SAME(fanotify_init)
+ ENTRY_DIFF(fanotify_mark)
+ ENTRY_COMP(clock_adjtime)
+ ENTRY_SAME(name_to_handle_at) /* 325 */
+ ENTRY_COMP(open_by_handle_at)
+ ENTRY_SAME(syncfs)
+ ENTRY_SAME(setns)
+ ENTRY_COMP(sendmmsg)
+ ENTRY_COMP(process_vm_readv) /* 330 */
+ ENTRY_COMP(process_vm_writev)
+ ENTRY_SAME(kcmp)
+ ENTRY_SAME(finit_module)
+ ENTRY_SAME(sched_setattr)
+ ENTRY_SAME(sched_getattr) /* 335 */
+ ENTRY_COMP(utimes)
+ ENTRY_SAME(renameat2)
+
/* Nothing yet */
+#undef ENTRY_SAME
+#undef ENTRY_DIFF
+#undef ENTRY_UHOH
+#undef ENTRY_COMP
+#undef ENTRY_OURS
diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c
index cded2568078..70e105d6242 100644
--- a/arch/parisc/kernel/time.c
+++ b/arch/parisc/kernel/time.c
@@ -10,7 +10,6 @@
* 1998-12-20 Updated NTP code according to technical memorandum Jan '96
* "A Kernel Model for Precision Timekeeping" by Dave Mills
*/
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/sched.h>
@@ -23,71 +22,149 @@
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/profile.h>
+#include <linux/clocksource.h>
+#include <linux/platform_device.h>
+#include <linux/ftrace.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/page.h>
#include <asm/param.h>
#include <asm/pdc.h>
#include <asm/led.h>
#include <linux/timex.h>
-/* xtime and wall_jiffies keep wall-clock time */
-extern unsigned long wall_jiffies;
+static unsigned long clocktick __read_mostly; /* timer cycles per tick */
-static long clocktick; /* timer cycles per tick */
-static long halftick;
+/*
+ * We keep time on PA-RISC Linux by using the Interval Timer which is
+ * a pair of registers; one is read-only and one is write-only; both
+ * accessed through CR16. The read-only register is 32 or 64 bits wide,
+ * and increments by 1 every CPU clock tick. The architecture only
+ * guarantees us a rate between 0.5 and 2, but all implementations use a
+ * rate of 1. The write-only register is 32-bits wide. When the lowest
+ * 32 bits of the read-only register compare equal to the write-only
+ * register, it raises a maskable external interrupt. Each processor has
+ * an Interval Timer of its own and they are not synchronised.
+ *
+ * We want to generate an interrupt every 1/HZ seconds. So we program
+ * CR16 to interrupt every @clocktick cycles. The it_value in cpu_data
+ * is programmed with the intended time of the next tick. We can be
+ * held off for an arbitrarily long period of time by interrupts being
+ * disabled, so we may miss one or more ticks.
+ */
+irqreturn_t __irq_entry timer_interrupt(int irq, void *dev_id)
+{
+ unsigned long now, now2;
+ unsigned long next_tick;
+ unsigned long cycles_elapsed, ticks_elapsed = 1;
+ unsigned long cycles_remainder;
+ unsigned int cpu = smp_processor_id();
+ struct cpuinfo_parisc *cpuinfo = &per_cpu(cpu_data, cpu);
-#ifdef CONFIG_SMP
-extern void smp_do_timer(struct pt_regs *regs);
-#endif
+ /* gcc can optimize for "read-only" case with a local clocktick */
+ unsigned long cpt = clocktick;
-irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- long now;
- long next_tick;
- int nticks;
- int cpu = smp_processor_id();
+ profile_tick(CPU_PROFILING);
- profile_tick(CPU_PROFILING, regs);
+ /* Initialize next_tick to the expected tick time. */
+ next_tick = cpuinfo->it_value;
+ /* Get current cycle counter (Control Register 16). */
now = mfctl(16);
- /* initialize next_tick to time at last clocktick */
- next_tick = cpu_data[cpu].it_value;
- /* since time passes between the interrupt and the mfctl()
- * above, it is never true that last_tick + clocktick == now. If we
- * never miss a clocktick, we could set next_tick = last_tick + clocktick
- * but maybe we'll miss ticks, hence the loop.
- *
- * Variables are *signed*.
- */
+ cycles_elapsed = now - next_tick;
- nticks = 0;
- while((next_tick - now) < halftick) {
- next_tick += clocktick;
- nticks++;
+ if ((cycles_elapsed >> 6) < cpt) {
+ /* use "cheap" math (add/subtract) instead
+ * of the more expensive div/mul method
+ */
+ cycles_remainder = cycles_elapsed;
+ while (cycles_remainder > cpt) {
+ cycles_remainder -= cpt;
+ ticks_elapsed++;
+ }
+ } else {
+ /* TODO: Reduce this to one fdiv op */
+ cycles_remainder = cycles_elapsed % cpt;
+ ticks_elapsed += cycles_elapsed / cpt;
}
+
+ /* convert from "division remainder" to "remainder of clock tick" */
+ cycles_remainder = cpt - cycles_remainder;
+
+ /* Determine when (in CR16 cycles) next IT interrupt will fire.
+ * We want IT to fire modulo clocktick even if we miss/skip some.
+ * But those interrupts don't in fact get delivered that regularly.
+ */
+ next_tick = now + cycles_remainder;
+
+ cpuinfo->it_value = next_tick;
+
+ /* Program the IT when to deliver the next interrupt.
+ * Only bottom 32-bits of next_tick are writable in CR16!
+ */
mtctl(next_tick, 16);
- cpu_data[cpu].it_value = next_tick;
- while (nticks--) {
-#ifdef CONFIG_SMP
- smp_do_timer(regs);
-#else
- update_process_times(user_mode(regs));
+ /* Skip one clocktick on purpose if we missed next_tick.
+ * The new CR16 must be "later" than current CR16 otherwise
+ * itimer would not fire until CR16 wrapped - e.g 4 seconds
+ * later on a 1Ghz processor. We'll account for the missed
+ * tick on the next timer interrupt.
+ *
+ * "next_tick - now" will always give the difference regardless
+ * if one or the other wrapped. If "now" is "bigger" we'll end up
+ * with a very large unsigned number.
+ */
+ now2 = mfctl(16);
+ if (next_tick - now2 > cpt)
+ mtctl(next_tick+cpt, 16);
+
+#if 1
+/*
+ * GGG: DEBUG code for how many cycles programming CR16 used.
+ */
+ if (unlikely(now2 - now > 0x3000)) /* 12K cycles */
+ printk (KERN_CRIT "timer_interrupt(CPU %d): SLOW! 0x%lx cycles!"
+ " cyc %lX rem %lX "
+ " next/now %lX/%lX\n",
+ cpu, now2 - now, cycles_elapsed, cycles_remainder,
+ next_tick, now );
#endif
- if (cpu == 0) {
- write_seqlock(&xtime_lock);
- do_timer(regs);
- write_sequnlock(&xtime_lock);
- }
+
+ /* Can we differentiate between "early CR16" (aka Scenario 1) and
+ * "long delay" (aka Scenario 3)? I don't think so.
+ *
+ * Timer_interrupt will be delivered at least a few hundred cycles
+ * after the IT fires. But it's arbitrary how much time passes
+ * before we call it "late". I've picked one second.
+ *
+ * It's important NO printk's are between reading CR16 and
+ * setting up the next value. May introduce huge variance.
+ */
+ if (unlikely(ticks_elapsed > HZ)) {
+ /* Scenario 3: very long delay? bad in any case */
+ printk (KERN_CRIT "timer_interrupt(CPU %d): delayed!"
+ " cycles %lX rem %lX "
+ " next/now %lX/%lX\n",
+ cpu,
+ cycles_elapsed, cycles_remainder,
+ next_tick, now );
}
-
- /* check soft power switch status */
- if (cpu == 0 && !atomic_read(&power_tasklet.count))
- tasklet_schedule(&power_tasklet);
+
+ /* Done mucking with unreliable delivery of interrupts.
+ * Go do system house keeping.
+ */
+
+ if (!--cpuinfo->prof_counter) {
+ cpuinfo->prof_counter = cpuinfo->prof_multiplier;
+ update_process_times(user_mode(get_irq_regs()));
+ }
+
+ if (cpu == 0)
+ xtime_update(ticks_elapsed);
return IRQ_HANDLED;
}
@@ -110,137 +187,87 @@ unsigned long profile_pc(struct pt_regs *regs)
EXPORT_SYMBOL(profile_pc);
-/*** converted from ia64 ***/
-/*
- * Return the number of micro-seconds that elapsed since the last
- * update to wall time (aka xtime aka wall_jiffies). The xtime_lock
- * must be at least read-locked when calling this routine.
- */
-static inline unsigned long
-gettimeoffset (void)
+/* clock source code */
+
+static cycle_t read_cr16(struct clocksource *cs)
{
-#ifndef CONFIG_SMP
- /*
- * FIXME: This won't work on smp because jiffies are updated by cpu 0.
- * Once parisc-linux learns the cr16 difference between processors,
- * this could be made to work.
- */
- long last_tick;
- long elapsed_cycles;
+ return get_cycles();
+}
- /* it_value is the intended time of the next tick */
- last_tick = cpu_data[smp_processor_id()].it_value;
+static struct clocksource clocksource_cr16 = {
+ .name = "cr16",
+ .rating = 300,
+ .read = read_cr16,
+ .mask = CLOCKSOURCE_MASK(BITS_PER_LONG),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
- /* Subtract one tick and account for possible difference between
- * when we expected the tick and when it actually arrived.
- * (aka wall vs real)
- */
- last_tick -= clocktick * (jiffies - wall_jiffies + 1);
- elapsed_cycles = mfctl(16) - last_tick;
+#ifdef CONFIG_SMP
+int update_cr16_clocksource(void)
+{
+ /* since the cr16 cycle counters are not synchronized across CPUs,
+ we'll check if we should switch to a safe clocksource: */
+ if (clocksource_cr16.rating != 0 && num_online_cpus() > 1) {
+ clocksource_change_rating(&clocksource_cr16, 0);
+ return 1;
+ }
- /* the precision of this math could be improved */
- return elapsed_cycles / (PAGE0->mem_10msec / 10000);
-#else
return 0;
-#endif
}
+#else
+int update_cr16_clocksource(void)
+{
+ return 0; /* no change */
+}
+#endif /*CONFIG_SMP*/
-void
-do_gettimeofday (struct timeval *tv)
+void __init start_cpu_itimer(void)
{
- unsigned long flags, seq, usec, sec;
-
- do {
- seq = read_seqbegin_irqsave(&xtime_lock, flags);
- usec = gettimeoffset();
- sec = xtime.tv_sec;
- usec += (xtime.tv_nsec / 1000);
- } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
-
- while (usec >= 1000000) {
- usec -= 1000000;
- ++sec;
- }
+ unsigned int cpu = smp_processor_id();
+ unsigned long next_tick = mfctl(16) + clocktick;
- tv->tv_sec = sec;
- tv->tv_usec = usec;
+ mtctl(next_tick, 16); /* kick off Interval Timer (CR16) */
+
+ per_cpu(cpu_data, cpu).it_value = next_tick;
}
-EXPORT_SYMBOL(do_gettimeofday);
+static struct platform_device rtc_generic_dev = {
+ .name = "rtc-generic",
+ .id = -1,
+};
-int
-do_settimeofday (struct timespec *tv)
+static int __init rtc_init(void)
{
- time_t wtm_sec, sec = tv->tv_sec;
- long wtm_nsec, nsec = tv->tv_nsec;
-
- if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
- return -EINVAL;
-
- write_seqlock_irq(&xtime_lock);
- {
- /*
- * This is revolting. We need to set "xtime"
- * correctly. However, the value in this location is
- * the value at the most recent update of wall time.
- * Discover what correction gettimeofday would have
- * done, and then undo it!
- */
- nsec -= gettimeoffset() * 1000;
-
- wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
- wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
-
- set_normalized_timespec(&xtime, sec, nsec);
- set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+ if (platform_device_register(&rtc_generic_dev) < 0)
+ printk(KERN_ERR "unable to register rtc device...\n");
- ntp_clear();
- }
- write_sequnlock_irq(&xtime_lock);
- clock_was_set();
+ /* not necessarily an error */
return 0;
}
-EXPORT_SYMBOL(do_settimeofday);
+module_init(rtc_init);
-/*
- * XXX: We can do better than this.
- * Returns nanoseconds
- */
-
-unsigned long long sched_clock(void)
+void read_persistent_clock(struct timespec *ts)
{
- return (unsigned long long)jiffies * (1000000000 / HZ);
+ static struct pdc_tod tod_data;
+ if (pdc_tod_read(&tod_data) == 0) {
+ ts->tv_sec = tod_data.tod_sec;
+ ts->tv_nsec = tod_data.tod_usec * 1000;
+ } else {
+ printk(KERN_ERR "Error reading tod clock\n");
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ }
}
-
void __init time_init(void)
{
- unsigned long next_tick;
- static struct pdc_tod tod_data;
+ unsigned long current_cr16_khz;
clocktick = (100 * PAGE0->mem_10msec) / HZ;
- halftick = clocktick / 2;
-
- /* Setup clock interrupt timing */
- next_tick = mfctl(16);
- next_tick += clocktick;
- cpu_data[smp_processor_id()].it_value = next_tick;
-
- /* kick off Itimer (CR16) */
- mtctl(next_tick, 16);
+ start_cpu_itimer(); /* get CPU 0 started */
- if(pdc_tod_read(&tod_data) == 0) {
- write_seqlock_irq(&xtime_lock);
- xtime.tv_sec = tod_data.tod_sec;
- xtime.tv_nsec = tod_data.tod_usec * 1000;
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
- write_sequnlock_irq(&xtime_lock);
- } else {
- printk(KERN_ERR "Error reading tod clock\n");
- xtime.tv_sec = 0;
- xtime.tv_nsec = 0;
- }
+ /* register at clocksource framework */
+ current_cr16_khz = PAGE0->mem_10msec/10; /* kHz */
+ clocksource_register_khz(&clocksource_cr16, current_cr16_khz);
}
-
diff --git a/arch/parisc/kernel/topology.c b/arch/parisc/kernel/topology.c
index ac2a4068141..f5159381fdd 100644
--- a/arch/parisc/kernel/topology.c
+++ b/arch/parisc/kernel/topology.c
@@ -1,5 +1,5 @@
/*
- * arch/parisc/kernel/topology.c - Populate driverfs with topology information
+ * arch/parisc/kernel/topology.c - Populate sysfs with topology information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,16 +20,16 @@
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/cpu.h>
+#include <linux/cache.h>
-static struct cpu cpu_devices[NR_CPUS];
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
static int __init topology_init(void)
{
- struct node *parent = NULL;
int num;
for_each_present_cpu(num) {
- register_cpu(&cpu_devices[num], num, parent);
+ register_cpu(&per_cpu(cpu_devices, num), num);
}
return 0;
}
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 15914f0235a..47ee620d15d 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -10,46 +10,47 @@
* state in 'asm.s'.
*/
-#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/timer.h>
+#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/smp.h>
-#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/console.h>
-#include <linux/kallsyms.h>
+#include <linux/bug.h>
+#include <linux/ratelimit.h>
#include <asm/assembly.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/traps.h>
#include <asm/unaligned.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/smp.h>
#include <asm/pdc.h>
#include <asm/pdc_chassis.h>
#include <asm/unwind.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
#include "../math-emu/math-emu.h" /* for handle_fpe() */
-#define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */
- /* dumped to the console via printk) */
-
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
DEFINE_SPINLOCK(pa_dbit_lock);
#endif
-int printbinary(char *buf, unsigned long x, int nbits)
+static void parisc_show_stack(struct task_struct *task, unsigned long *sp,
+ struct pt_regs *regs);
+
+static int printbinary(char *buf, unsigned long x, int nbits)
{
unsigned long mask = 1UL << (nbits - 1);
while (mask != 0) {
@@ -61,62 +62,47 @@ int printbinary(char *buf, unsigned long x, int nbits)
return nbits;
}
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
#define RFMT "%016lx"
#else
#define RFMT "%08lx"
#endif
+#define FFMT "%016llx" /* fpregs are 64-bit always */
-void show_regs(struct pt_regs *regs)
+#define PRINTREGS(lvl,r,f,fmt,x) \
+ printk("%s%s%02d-%02d " fmt " " fmt " " fmt " " fmt "\n", \
+ lvl, f, (x), (x+3), (r)[(x)+0], (r)[(x)+1], \
+ (r)[(x)+2], (r)[(x)+3])
+
+static void print_gr(char *level, struct pt_regs *regs)
{
int i;
- char buf[128], *p;
- char *level;
- unsigned long cr30;
- unsigned long cr31;
- /* carlos says that gcc understands better memory in a struct,
- * and it makes our life easier with fpregs -- T-Bone */
- struct { u32 sw[2]; } s;
-
- level = user_mode(regs) ? KERN_DEBUG : KERN_CRIT;
-
- printk("%s\n", level); /* don't want to have that pretty register dump messed up */
+ char buf[64];
+ printk("%s\n", level);
printk("%s YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI\n", level);
printbinary(buf, regs->gr[0], 32);
printk("%sPSW: %s %s\n", level, buf, print_tainted());
- for (i = 0; i < 32; i += 4) {
- int j;
- p = buf;
- p += sprintf(p, "%sr%02d-%02d ", level, i, i + 3);
- for (j = 0; j < 4; j++) {
- p += sprintf(p, " " RFMT, (i+j) == 0 ? 0 : regs->gr[i + j]);
- }
- printk("%s\n", buf);
- }
+ for (i = 0; i < 32; i += 4)
+ PRINTREGS(level, regs->gr, "r", RFMT, i);
+}
- for (i = 0; i < 8; i += 4) {
- int j;
- p = buf;
- p += sprintf(p, "%ssr%d-%d ", level, i, i + 3);
- for (j = 0; j < 4; j++) {
- p += sprintf(p, " " RFMT, regs->sr[i + j]);
- }
- printk("%s\n", buf);
- }
+static void print_fr(char *level, struct pt_regs *regs)
+{
+ int i;
+ char buf[64];
+ struct { u32 sw[2]; } s;
/* FR are 64bit everywhere. Need to use asm to get the content
* of fpsr/fper1, and we assume that we won't have a FP Identify
* in our way, otherwise we're screwed.
* The fldd is used to restore the T-bit if there was one, as the
* store clears it anyway.
- * BTW, PA2.0 book says "thou shall not use fstw on FPSR/FPERs". */
- __asm__ (
- "fstd %%fr0,0(%1) \n\t"
- "fldd 0(%1),%%fr0 \n\t"
- : "=m" (s) : "r" (&s) : "%r0"
- );
+ * PA2.0 book says "thou shall not use fstw on FPSR/FPERs" - T-Bone */
+ asm volatile ("fstd %%fr0,0(%1) \n\t"
+ "fldd 0(%1),%%fr0 \n\t"
+ : "=m" (s) : "r" (&s) : "r0");
printk("%s\n", level);
printk("%s VZOUICununcqcqcqcqcqcrmunTDVZOUI\n", level);
@@ -125,14 +111,28 @@ void show_regs(struct pt_regs *regs)
printk("%sFPER1: %08x\n", level, s.sw[1]);
/* here we'll print fr0 again, tho it'll be meaningless */
- for (i = 0; i < 32; i += 4) {
- int j;
- p = buf;
- p += sprintf(p, "%sfr%02d-%02d ", level, i, i + 3);
- for (j = 0; j < 4; j++)
- p += sprintf(p, " %016llx", (i+j) == 0 ? 0 : regs->fr[i+j]);
- printk("%s\n", buf);
- }
+ for (i = 0; i < 32; i += 4)
+ PRINTREGS(level, regs->fr, "fr", FFMT, i);
+}
+
+void show_regs(struct pt_regs *regs)
+{
+ int i, user;
+ char *level;
+ unsigned long cr30, cr31;
+
+ user = user_mode(regs);
+ level = user ? KERN_DEBUG : KERN_CRIT;
+
+ show_regs_print_info(level);
+
+ print_gr(level, regs);
+
+ for (i = 0; i < 8; i += 4)
+ PRINTREGS(level, regs->sr, "sr", RFMT, i);
+
+ if (user)
+ print_fr(level, regs);
cr30 = mfctl(30);
cr31 = mfctl(31);
@@ -144,98 +144,120 @@ void show_regs(struct pt_regs *regs)
printk("%s CPU: %8d CR30: " RFMT " CR31: " RFMT "\n",
level, current_thread_info()->cpu, cr30, cr31);
printk("%s ORIG_R28: " RFMT "\n", level, regs->orig_r28);
- printk(level);
- print_symbol(" IAOQ[0]: %s\n", regs->iaoq[0]);
- printk(level);
- print_symbol(" IAOQ[1]: %s\n", regs->iaoq[1]);
- printk(level);
- print_symbol(" RP(r2): %s\n", regs->gr[2]);
+
+ if (user) {
+ printk("%s IAOQ[0]: " RFMT "\n", level, regs->iaoq[0]);
+ printk("%s IAOQ[1]: " RFMT "\n", level, regs->iaoq[1]);
+ printk("%s RP(r2): " RFMT "\n", level, regs->gr[2]);
+ } else {
+ printk("%s IAOQ[0]: %pS\n", level, (void *) regs->iaoq[0]);
+ printk("%s IAOQ[1]: %pS\n", level, (void *) regs->iaoq[1]);
+ printk("%s RP(r2): %pS\n", level, (void *) regs->gr[2]);
+
+ parisc_show_stack(current, NULL, regs);
+ }
}
+static DEFINE_RATELIMIT_STATE(_hppa_rs,
+ DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
-void dump_stack(void)
-{
- show_stack(NULL, NULL);
+#define parisc_printk_ratelimited(critical, regs, fmt, ...) { \
+ if ((critical || show_unhandled_signals) && __ratelimit(&_hppa_rs)) { \
+ printk(fmt, ##__VA_ARGS__); \
+ show_regs(regs); \
+ } \
}
-EXPORT_SYMBOL(dump_stack);
static void do_show_stack(struct unwind_frame_info *info)
{
int i = 1;
- printk("Backtrace:\n");
+ printk(KERN_CRIT "Backtrace:\n");
while (i <= 16) {
if (unwind_once(info) < 0 || info->ip == 0)
break;
if (__kernel_text_address(info->ip)) {
- printk(" [<" RFMT ">] ", info->ip);
-#ifdef CONFIG_KALLSYMS
- print_symbol("%s\n", info->ip);
-#else
- if ((i & 0x03) == 0)
- printk("\n");
-#endif
+ printk(KERN_CRIT " [<" RFMT ">] %pS\n",
+ info->ip, (void *) info->ip);
i++;
}
}
- printk("\n");
+ printk(KERN_CRIT "\n");
}
-void show_stack(struct task_struct *task, unsigned long *s)
+static void parisc_show_stack(struct task_struct *task, unsigned long *sp,
+ struct pt_regs *regs)
{
struct unwind_frame_info info;
+ struct task_struct *t;
+
+ t = task ? task : current;
+ if (regs) {
+ unwind_frame_init(&info, t, regs);
+ goto show_stack;
+ }
- if (!task) {
+ if (t == current) {
unsigned long sp;
- struct pt_regs *r;
HERE:
asm volatile ("copy %%r30, %0" : "=r"(sp));
- r = (struct pt_regs *)kmalloc(sizeof(struct pt_regs), GFP_KERNEL);
- if (!r)
- return;
- memset(r, 0, sizeof(struct pt_regs));
- r->iaoq[0] = (unsigned long)&&HERE;
- r->gr[2] = (unsigned long)__builtin_return_address(0);
- r->gr[30] = sp;
- unwind_frame_init(&info, current, r);
- kfree(r);
+ {
+ struct pt_regs r;
+
+ memset(&r, 0, sizeof(struct pt_regs));
+ r.iaoq[0] = (unsigned long)&&HERE;
+ r.gr[2] = (unsigned long)__builtin_return_address(0);
+ r.gr[30] = sp;
+
+ unwind_frame_init(&info, current, &r);
+ }
} else {
- unwind_frame_init_from_blocked_task(&info, task);
+ unwind_frame_init_from_blocked_task(&info, t);
}
+show_stack:
do_show_stack(&info);
}
+void show_stack(struct task_struct *t, unsigned long *sp)
+{
+ return parisc_show_stack(t, sp, NULL);
+}
+
+int is_valid_bugaddr(unsigned long iaoq)
+{
+ return 1;
+}
+
void die_if_kernel(char *str, struct pt_regs *regs, long err)
{
if (user_mode(regs)) {
if (err == 0)
return; /* STFU */
- printk(KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n",
- current->comm, current->pid, str, err, regs->iaoq[0]);
-#ifdef PRINT_USER_FAULTS
- /* XXX for debugging only */
- show_regs(regs);
-#endif
+ parisc_printk_ratelimited(1, regs,
+ KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n",
+ current->comm, task_pid_nr(current), str, err, regs->iaoq[0]);
+
return;
}
oops_in_progress = 1;
+ oops_enter();
+
/* Amuse the user in a SPARC fashion */
- printk(
-" _______________________________ \n"
-" < Your System ate a SPARC! Gah! >\n"
-" ------------------------------- \n"
-" \\ ^__^\n"
-" \\ (xx)\\_______\n"
-" (__)\\ )\\/\\\n"
-" U ||----w |\n"
-" || ||\n");
+ if (err) printk(KERN_CRIT
+ " _______________________________ \n"
+ " < Your System ate a SPARC! Gah! >\n"
+ " ------------------------------- \n"
+ " \\ ^__^\n"
+ " (__)\\ )\\/\\\n"
+ " U ||----w |\n"
+ " || ||\n");
/* unlock the pdc lock if necessary */
pdc_emergency_unlock();
@@ -247,83 +269,73 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
if (!console_drivers)
pdc_console_restart();
- printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n",
- current->comm, current->pid, str, err);
- show_regs(regs);
+ if (err)
+ printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n",
+ current->comm, task_pid_nr(current), str, err);
/* Wot's wrong wif bein' racy? */
if (current->thread.flags & PARISC_KERNEL_DEATH) {
- printk(KERN_CRIT "%s() recursion detected.\n", __FUNCTION__);
+ printk(KERN_CRIT "%s() recursion detected.\n", __func__);
local_irq_enable();
while (1);
}
-
current->thread.flags |= PARISC_KERNEL_DEATH;
- do_exit(SIGSEGV);
-}
-int syscall_ipi(int (*syscall) (struct pt_regs *), struct pt_regs *regs)
-{
- return syscall(regs);
+ show_regs(regs);
+ dump_stack();
+ add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
+
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+
+ if (panic_on_oops) {
+ printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n");
+ ssleep(5);
+ panic("Fatal exception");
+ }
+
+ oops_exit();
+ do_exit(SIGSEGV);
}
/* gdb uses break 4,8 */
#define GDB_BREAK_INSN 0x10004
-void handle_gdb_break(struct pt_regs *regs, int wot)
+static void handle_gdb_break(struct pt_regs *regs, int wot)
{
struct siginfo si;
- si.si_code = wot;
- si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
si.si_signo = SIGTRAP;
si.si_errno = 0;
+ si.si_code = wot;
+ si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
force_sig_info(SIGTRAP, &si, current);
}
-void handle_break(unsigned iir, struct pt_regs *regs)
+static void handle_break(struct pt_regs *regs)
{
- struct siginfo si;
-
- switch(iir) {
- case 0x00:
-#ifdef PRINT_USER_FAULTS
- printk(KERN_DEBUG "break 0,0: pid=%d command='%s'\n",
- current->pid, current->comm);
-#endif
- die_if_kernel("Breakpoint", regs, 0);
-#ifdef PRINT_USER_FAULTS
- show_regs(regs);
-#endif
- si.si_code = TRAP_BRKPT;
- si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
- si.si_signo = SIGTRAP;
- force_sig_info(SIGTRAP, &si, current);
- break;
-
- case GDB_BREAK_INSN:
- die_if_kernel("Breakpoint", regs, 0);
- handle_gdb_break(regs, TRAP_BRKPT);
- break;
-
- default:
-#ifdef PRINT_USER_FAULTS
- printk(KERN_DEBUG "break %#08x: pid=%d command='%s'\n",
- iir, current->pid, current->comm);
- show_regs(regs);
-#endif
- si.si_signo = SIGTRAP;
- si.si_code = TRAP_BRKPT;
- si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
- force_sig_info(SIGTRAP, &si, current);
- return;
+ unsigned iir = regs->iir;
+
+ if (unlikely(iir == PARISC_BUG_BREAK_INSN && !user_mode(regs))) {
+ /* check if a BUG() or WARN() trapped here. */
+ enum bug_trap_type tt;
+ tt = report_bug(regs->iaoq[0] & ~3, regs);
+ if (tt == BUG_TRAP_TYPE_WARN) {
+ regs->iaoq[0] += 4;
+ regs->iaoq[1] += 4;
+ return; /* return to next instruction when WARN_ON(). */
+ }
+ die_if_kernel("Unknown kernel breakpoint", regs,
+ (tt == BUG_TRAP_TYPE_NONE) ? 9 : 0);
}
-}
+ if (unlikely(iir != GDB_BREAK_INSN))
+ parisc_printk_ratelimited(0, regs,
+ KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
+ iir & 31, (iir>>13) & ((1<<13)-1),
+ task_pid_nr(current), current->comm);
-int handle_toc(void)
-{
- printk(KERN_CRIT "TOC call.\n");
- return 0;
+ /* send standard GDB signal */
+ handle_gdb_break(regs, TRAP_BRKPT);
}
static void default_trap(int code, struct pt_regs *regs)
@@ -332,7 +344,7 @@ static void default_trap(int code, struct pt_regs *regs)
show_regs(regs);
}
-void (*cpu_lpmc) (int code, struct pt_regs *regs) = default_trap;
+void (*cpu_lpmc) (int code, struct pt_regs *regs) __read_mostly = default_trap;
void transfer_pim_to_trap_frame(struct pt_regs *regs)
@@ -476,7 +488,7 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o
panic(msg);
}
-void handle_interruption(int code, struct pt_regs *regs)
+void notrace handle_interruption(int code, struct pt_regs *regs)
{
unsigned long fault_address = 0;
unsigned long fault_space = 0;
@@ -509,10 +521,10 @@ void handle_interruption(int code, struct pt_regs *regs)
*/
if (((unsigned long)regs->iaoq[0] & 3) &&
((unsigned long)regs->iasq[0] != (unsigned long)regs->sr[7])) {
- /* Kill the user process later */
- regs->iaoq[0] = 0 | 3;
+ /* Kill the user process later */
+ regs->iaoq[0] = 0 | 3;
regs->iaoq[1] = regs->iaoq[0] + 4;
- regs->iasq[0] = regs->iasq[0] = regs->sr[7];
+ regs->iasq[0] = regs->iasq[1] = regs->sr[7];
regs->gr[0] &= ~PSW_B;
return;
}
@@ -528,8 +540,8 @@ void handle_interruption(int code, struct pt_regs *regs)
/* set up a new led state on systems shipped with a LED State panel */
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_HPMC);
-
- parisc_terminate("High Priority Machine Check (HPMC)",
+
+ parisc_terminate("High Priority Machine Check (HPMC)",
regs, code, 0);
/* NOT REACHED */
@@ -550,7 +562,8 @@ void handle_interruption(int code, struct pt_regs *regs)
/* Low-priority machine check */
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_LPMC);
- flush_all_caches();
+ flush_cache_all();
+ flush_tlb_all();
cpu_lpmc(5, regs);
return;
@@ -568,15 +581,15 @@ void handle_interruption(int code, struct pt_regs *regs)
case 9:
/* Break instruction trap */
- handle_break(regs->iir,regs);
+ handle_break(regs);
return;
-
+
case 10:
/* Privileged operation trap */
die_if_kernel("Privileged operation", regs, code);
si.si_code = ILL_PRVOPC;
goto give_sigill;
-
+
case 11:
/* Privileged register trap */
if ((regs->iir & 0xffdfffe0) == 0x034008a0) {
@@ -615,12 +628,12 @@ void handle_interruption(int code, struct pt_regs *regs)
case 13:
/* Conditional Trap
- The condition succees in an instruction which traps
+ The condition succeeds in an instruction which traps
on condition */
if(user_mode(regs)){
si.si_signo = SIGFPE;
/* Set to zero, and let the userspace app figure it out from
- the insn pointed to by si_addr */
+ the insn pointed to by si_addr */
si.si_code = 0;
si.si_addr = (void __user *) regs->iaoq[0];
force_sig_info(SIGFPE, &si, current);
@@ -632,9 +645,10 @@ void handle_interruption(int code, struct pt_regs *regs)
case 14:
/* Assist Exception Trap, i.e. floating point exception. */
die_if_kernel("Floating point exception", regs, 0); /* quiet */
+ __inc_irq_stat(irq_fpassist_count);
handle_fpe(regs);
return;
-
+
case 15:
/* Data TLB miss fault/Data page fault */
/* Fall through */
@@ -646,15 +660,15 @@ void handle_interruption(int code, struct pt_regs *regs)
case 17:
/* Non-access data TLB miss fault/Non-access data page fault */
/* FIXME:
- Still need to add slow path emulation code here!
- If the insn used a non-shadow register, then the tlb
+ Still need to add slow path emulation code here!
+ If the insn used a non-shadow register, then the tlb
handlers could not have their side-effect (e.g. probe
writing to a target register) emulated since rfir would
erase the changes to said register. Instead we have to
setup everything, call this function we are in, and emulate
by hand. Technically we need to emulate:
fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw
- */
+ */
fault_address = regs->ior;
fault_space = regs->isr;
break;
@@ -726,6 +740,10 @@ void handle_interruption(int code, struct pt_regs *regs)
/* Fall Through */
case 27:
/* Data memory protection ID trap */
+ if (code == 27 && !user_mode(regs) &&
+ fixup_exception(regs))
+ return;
+
die_if_kernel("Protection id trap", regs, code);
si.si_code = SEGV_MAPERR;
si.si_signo = SIGSEGV;
@@ -744,11 +762,9 @@ void handle_interruption(int code, struct pt_regs *regs)
default:
if (user_mode(regs)) {
-#ifdef PRINT_USER_FAULTS
- printk(KERN_DEBUG "\nhandle_interruption() pid=%d command='%s'\n",
- current->pid, current->comm);
- show_regs(regs);
-#endif
+ parisc_printk_ratelimited(0, regs, KERN_DEBUG
+ "handle_interruption() pid=%d command='%s'\n",
+ task_pid_nr(current), current->comm);
/* SIGBUS, for lack of a better one. */
si.si_signo = SIGBUS;
si.si_code = BUS_OBJERR;
@@ -765,15 +781,10 @@ void handle_interruption(int code, struct pt_regs *regs)
if (user_mode(regs)) {
if ((fault_space >> SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) {
-#ifdef PRINT_USER_FAULTS
- if (fault_space == 0)
- printk(KERN_DEBUG "User Fault on Kernel Space ");
- else
- printk(KERN_DEBUG "User Fault (long pointer) (fault %d) ",
- code);
- printk("pid=%d command='%s'\n", current->pid, current->comm);
- show_regs(regs);
-#endif
+ parisc_printk_ratelimited(0, regs, KERN_DEBUG
+ "User fault %d on space 0x%08lx, pid=%d command='%s'\n",
+ code, fault_space,
+ task_pid_nr(current), current->comm);
si.si_signo = SIGSEGV;
si.si_errno = 0;
si.si_code = SEGV_MAPERR;
@@ -785,14 +796,14 @@ void handle_interruption(int code, struct pt_regs *regs)
else {
/*
- * The kernel should never fault on its own address space.
+ * The kernel should never fault on its own address space,
+ * unless pagefault_disable() was called before.
*/
- if (fault_space == 0)
+ if (fault_space == 0 && !in_atomic())
{
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC);
parisc_terminate("Kernel Fault", regs, code, fault_address);
-
}
}
@@ -802,13 +813,14 @@ void handle_interruption(int code, struct pt_regs *regs)
int __init check_ivt(void *iva)
{
+ extern u32 os_hpmc_size;
+ extern const u32 os_hpmc[];
+
int i;
u32 check = 0;
u32 *ivap;
u32 *hpmcp;
u32 length;
- extern void os_hpmc(void);
- extern void os_hpmc_end(void);
if (strcmp((char *)iva, "cows can fly"))
return -1;
@@ -819,8 +831,7 @@ int __init check_ivt(void *iva)
*ivap++ = 0;
/* Compute Checksum for HPMC handler */
-
- length = (u32)((unsigned long)os_hpmc_end - (unsigned long)os_hpmc);
+ length = os_hpmc_size;
ivap[7] = length;
hpmcp = (u32 *)os_hpmc;
@@ -836,7 +847,7 @@ int __init check_ivt(void *iva)
return 0;
}
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
extern const void fault_vector_11;
#endif
extern const void fault_vector_20;
@@ -848,7 +859,7 @@ void __init trap_init(void)
if (boot_cpu_data.cpu_type >= pcxu)
iva = (void *) &fault_vector_20;
else
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
panic("Can't boot 64-bit OS on PA1.1 processor!");
#else
iva = (void *) &fault_vector_11;
diff --git a/arch/parisc/kernel/unaligned.c b/arch/parisc/kernel/unaligned.c
index eaae8a021f9..d7c0acb35ec 100644
--- a/arch/parisc/kernel/unaligned.c
+++ b/arch/parisc/kernel/unaligned.c
@@ -20,20 +20,24 @@
*
*/
-#include <linux/config.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/ratelimit.h>
#include <asm/uaccess.h>
+#include <asm/hardirq.h>
/* #define DEBUG_UNALIGNED 1 */
#ifdef DEBUG_UNALIGNED
-#define DPRINTF(fmt, args...) do { printk(KERN_DEBUG "%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__ ); printk(KERN_DEBUG fmt, ##args ); } while (0)
+#define DPRINTF(fmt, args...) do { printk(KERN_DEBUG "%s:%d:%s ", __FILE__, __LINE__, __func__ ); printk(KERN_DEBUG fmt, ##args ); } while (0)
#else
#define DPRINTF(fmt, args...)
#endif
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
#define RFMT "%016lx"
#else
#define RFMT "%08lx"
@@ -43,6 +47,8 @@
"\tldil L%%" #lbl ", %%r1\n" \
"\tldo R%%" #lbl "(%%r1), %%r1\n" \
"\tbv,n %%r0(%%r1)\n"
+/* If you use FIXUP_BRANCH, then you must list this clobber */
+#define FIXUP_BRANCH_CLOBBER "r1"
/* 1111 1100 0000 0000 0001 0011 1100 0000 */
#define OPCODE1(a,b,c) ((a)<<26|(b)<<12|(c)<<6)
@@ -122,7 +128,7 @@
#define ERR_NOTHANDLED -1
#define ERR_PAGEFAULT -2
-int unaligned_enabled = 1;
+int unaligned_enabled __read_mostly = 1;
void die_if_kernel (char *str, struct pt_regs *regs, long err);
@@ -146,18 +152,11 @@ static int emulate_ldh(struct pt_regs *regs, int toreg)
"4: ldi -2, %1\n"
FIXUP_BRANCH(3b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,4b\n"
-" .dword 2b,4b\n"
-#else
-" .word 1b,4b\n"
-" .word 2b,4b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b, 4b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b, 4b)
: "=r" (val), "=r" (ret)
: "0" (val), "r" (saddr), "r" (regs->isr)
- : "r20" );
+ : "r20", FIXUP_BRANCH_CLOBBER );
DPRINTF("val = 0x" RFMT "\n", val);
@@ -191,18 +190,11 @@ static int emulate_ldw(struct pt_regs *regs, int toreg, int flop)
"4: ldi -2, %1\n"
FIXUP_BRANCH(3b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,4b\n"
-" .dword 2b,4b\n"
-#else
-" .word 1b,4b\n"
-" .word 2b,4b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b, 4b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b, 4b)
: "=r" (val), "=r" (ret)
: "0" (val), "r" (saddr), "r" (regs->isr)
- : "r19", "r20" );
+ : "r19", "r20", FIXUP_BRANCH_CLOBBER );
DPRINTF("val = 0x" RFMT "\n", val);
@@ -223,7 +215,7 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
regs->isr, regs->ior, toreg);
#ifdef CONFIG_PA20
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
if (!flop)
return -1;
#endif
@@ -242,18 +234,11 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
"4: ldi -2, %1\n"
FIXUP_BRANCH(3b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,4b\n"
-" .dword 2b,4b\n"
-#else
-" .word 1b,4b\n"
-" .word 2b,4b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
: "=r" (val), "=r" (ret)
: "0" (val), "r" (saddr), "r" (regs->isr)
- : "r19", "r20" );
+ : "r19", "r20", FIXUP_BRANCH_CLOBBER );
#else
{
unsigned long valh=0,vall=0;
@@ -274,20 +259,12 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
"5: ldi -2, %2\n"
FIXUP_BRANCH(4b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,5b\n"
-" .dword 2b,5b\n"
-" .dword 3b,5b\n"
-#else
-" .word 1b,5b\n"
-" .word 2b,5b\n"
-" .word 3b,5b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b,5b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b,5b)
+ ASM_EXCEPTIONTABLE_ENTRY(3b,5b)
: "=r" (valh), "=r" (vall), "=r" (ret)
: "0" (valh), "1" (vall), "r" (saddr), "r" (regs->isr)
- : "r19", "r20" );
+ : "r19", "r20", FIXUP_BRANCH_CLOBBER );
val=((__u64)valh<<32)|(__u64)vall;
}
#endif
@@ -324,18 +301,11 @@ static int emulate_sth(struct pt_regs *regs, int frreg)
"4: ldi -2, %0\n"
FIXUP_BRANCH(3b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,4b\n"
-" .dword 2b,4b\n"
-#else
-" .word 1b,4b\n"
-" .word 2b,4b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
: "=r" (ret)
: "r" (val), "r" (regs->ior), "r" (regs->isr)
- : "r19" );
+ : "r19", FIXUP_BRANCH_CLOBBER );
return ret;
}
@@ -378,18 +348,11 @@ static int emulate_stw(struct pt_regs *regs, int frreg, int flop)
"4: ldi -2, %0\n"
FIXUP_BRANCH(3b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,4b\n"
-" .dword 2b,4b\n"
-#else
-" .word 1b,4b\n"
-" .word 2b,4b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
: "=r" (ret)
: "r" (val), "r" (regs->ior), "r" (regs->isr)
- : "r19", "r20", "r21", "r22", "r1" );
+ : "r19", "r20", "r21", "r22", "r1", FIXUP_BRANCH_CLOBBER );
return 0;
}
@@ -409,7 +372,7 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
val, regs->isr, regs->ior);
#ifdef CONFIG_PA20
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
if (!flop)
return -1;
#endif
@@ -435,22 +398,13 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
"6: ldi -2, %0\n"
FIXUP_BRANCH(5b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,6b\n"
-" .dword 2b,6b\n"
-" .dword 3b,6b\n"
-" .dword 4b,6b\n"
-#else
-" .word 1b,6b\n"
-" .word 2b,6b\n"
-" .word 3b,6b\n"
-" .word 4b,6b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b,6b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b,6b)
+ ASM_EXCEPTIONTABLE_ENTRY(3b,6b)
+ ASM_EXCEPTIONTABLE_ENTRY(4b,6b)
: "=r" (ret)
: "r" (val), "r" (regs->ior), "r" (regs->isr)
- : "r19", "r20", "r21", "r22", "r1" );
+ : "r19", "r20", "r21", "r22", "r1", FIXUP_BRANCH_CLOBBER );
#else
{
unsigned long valh=(val>>32),vall=(val&0xffffffffl);
@@ -478,24 +432,14 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
"7: ldi -2, %0\n"
FIXUP_BRANCH(6b)
" .previous\n"
-" .section __ex_table,\"aw\"\n"
-#ifdef __LP64__
-" .dword 1b,7b\n"
-" .dword 2b,7b\n"
-" .dword 3b,7b\n"
-" .dword 4b,7b\n"
-" .dword 5b,7b\n"
-#else
-" .word 1b,7b\n"
-" .word 2b,7b\n"
-" .word 3b,7b\n"
-" .word 4b,7b\n"
-" .word 5b,7b\n"
-#endif
-" .previous\n"
+ ASM_EXCEPTIONTABLE_ENTRY(1b,7b)
+ ASM_EXCEPTIONTABLE_ENTRY(2b,7b)
+ ASM_EXCEPTIONTABLE_ENTRY(3b,7b)
+ ASM_EXCEPTIONTABLE_ENTRY(4b,7b)
+ ASM_EXCEPTIONTABLE_ENTRY(5b,7b)
: "=r" (ret)
: "r" (valh), "r" (vall), "r" (regs->ior), "r" (regs->isr)
- : "r19", "r20", "r21", "r1" );
+ : "r19", "r20", "r21", "r1", FIXUP_BRANCH_CLOBBER );
}
#endif
@@ -504,30 +448,26 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
void handle_unaligned(struct pt_regs *regs)
{
- static unsigned long unaligned_count = 0;
- static unsigned long last_time = 0;
+ static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
unsigned long newbase = R1(regs->iir)?regs->gr[R1(regs->iir)]:0;
int modify = 0;
int ret = ERR_NOTHANDLED;
struct siginfo si;
register int flop=0; /* true if this is a flop */
+ __inc_irq_stat(irq_unaligned_count);
+
/* log a message with pacing */
if (user_mode(regs)) {
if (current->thread.flags & PARISC_UAC_SIGBUS) {
goto force_sigbus;
}
- if (unaligned_count > 5 && jiffies - last_time > 5*HZ) {
- unaligned_count = 0;
- last_time = jiffies;
- }
-
- if (!(current->thread.flags & PARISC_UAC_NOPRINT)
- && ++unaligned_count < 5) {
+ if (!(current->thread.flags & PARISC_UAC_NOPRINT) &&
+ __ratelimit(&ratelimit)) {
char buf[256];
sprintf(buf, "%s(%d): unaligned access to 0x" RFMT " at ip=0x" RFMT "\n",
- current->comm, current->pid, regs->ior, regs->iaoq[0]);
+ current->comm, task_pid_nr(current), regs->ior, regs->iaoq[0]);
printk(KERN_WARNING "%s", buf);
#ifdef DEBUG_UNALIGNED
show_regs(regs);
@@ -682,15 +622,12 @@ void handle_unaligned(struct pt_regs *regs)
flop=1;
ret = emulate_std(regs, R2(regs->iir),1);
break;
-
-#ifdef CONFIG_PA20
case OPCODE_LDD_L:
ret = emulate_ldd(regs, R2(regs->iir),0);
break;
case OPCODE_STD_L:
ret = emulate_std(regs, R2(regs->iir),0);
break;
-#endif
}
#endif
switch (regs->iir & OPCODE3_MASK)
diff --git a/arch/parisc/kernel/unwind.c b/arch/parisc/kernel/unwind.c
index db141108412..ddd988b267a 100644
--- a/arch/parisc/kernel/unwind.c
+++ b/arch/parisc/kernel/unwind.c
@@ -8,14 +8,17 @@
* understand what is happening here
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/kallsyms.h>
+#include <linux/sort.h>
#include <asm/uaccess.h>
#include <asm/assembly.h>
+#include <asm/asm-offsets.h>
+#include <asm/ptrace.h>
#include <asm/unwind.h>
@@ -26,6 +29,8 @@
#define dbg(x...)
#endif
+#define KERNEL_START (KERNEL_BINARY_TEXT_START)
+
extern struct unwind_table_entry __start___unwind[];
extern struct unwind_table_entry __stop___unwind[];
@@ -35,7 +40,7 @@ static spinlock_t unwind_lock;
* we can call unwind_init as early in the bootup process as
* possible (before the slab allocator is initialized)
*/
-static struct unwind_table kernel_unwind_table;
+static struct unwind_table kernel_unwind_table __read_mostly;
static LIST_HEAD(unwind_tables);
static inline const struct unwind_table_entry *
@@ -75,8 +80,11 @@ find_unwind_entry(unsigned long addr)
if (addr >= table->start &&
addr <= table->end)
e = find_unwind_entry_in_table(table, addr);
- if (e)
+ if (e) {
+ /* Move-to-front to exploit common traces */
+ list_move(&table->list, &unwind_tables);
break;
+ }
}
return e;
@@ -111,24 +119,18 @@ unwind_table_init(struct unwind_table *table, const char *name,
}
}
+static int cmp_unwind_table_entry(const void *a, const void *b)
+{
+ return ((const struct unwind_table_entry *)a)->region_start
+ - ((const struct unwind_table_entry *)b)->region_start;
+}
+
static void
unwind_table_sort(struct unwind_table_entry *start,
struct unwind_table_entry *finish)
{
- struct unwind_table_entry el, *p, *q;
-
- for (p = start + 1; p < finish; ++p) {
- if (p[0].region_start < p[-1].region_start) {
- el = *p;
- q = p;
- do {
- q[0] = q[-1];
- --q;
- } while (q > start &&
- el.region_start < q[-1].region_start);
- *q = el;
- }
- }
+ sort(start, finish - start, sizeof(struct unwind_table_entry),
+ cmp_unwind_table_entry, NULL);
}
struct unwind_table *
@@ -166,7 +168,7 @@ void unwind_table_remove(struct unwind_table *table)
}
/* Called from setup_arch to import the kernel unwind info */
-static int unwind_init(void)
+int __init unwind_init(void)
{
long start, stop;
register unsigned long gp __asm__ ("r27");
@@ -197,6 +199,29 @@ static int unwind_init(void)
return 0;
}
+#ifdef CONFIG_64BIT
+#define get_func_addr(fptr) fptr[2]
+#else
+#define get_func_addr(fptr) fptr[0]
+#endif
+
+static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size)
+{
+ extern void handle_interruption(int, struct pt_regs *);
+ static unsigned long *hi = (unsigned long *)&handle_interruption;
+
+ if (pc == get_func_addr(hi)) {
+ struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN);
+ dbg("Unwinding through handle_interruption()\n");
+ info->prev_sp = regs->gr[30];
+ info->prev_ip = regs->iaoq[0];
+
+ return 1;
+ }
+
+ return 0;
+}
+
static void unwind_frame_regs(struct unwind_frame_info *info)
{
const struct unwind_table_entry *e;
@@ -208,19 +233,17 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
e = find_unwind_entry(info->ip);
if (e == NULL) {
unsigned long sp;
- extern char _stext[], _etext[];
dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
#ifdef CONFIG_KALLSYMS
/* Handle some frequent special cases.... */
{
- char symname[KSYM_NAME_LEN+1];
+ char symname[KSYM_NAME_LEN];
char *modname;
- unsigned long symsize, offset;
- kallsyms_lookup(info->ip, &symsize, &offset,
- &modname, symname);
+ kallsyms_lookup(info->ip, NULL, NULL, &modname,
+ symname);
dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
@@ -257,8 +280,7 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
break;
info->prev_ip = tmp;
sp = info->prev_sp;
- } while (info->prev_ip < (unsigned long)_stext ||
- info->prev_ip > (unsigned long)_etext);
+ } while (!kernel_text_address(info->prev_ip));
info->rp = 0;
@@ -311,13 +333,15 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
}
}
- info->prev_sp = info->sp - frame_size;
- if (e->Millicode)
- info->rp = info->r31;
- else if (rpoffset)
- info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
- info->prev_ip = info->rp;
- info->rp = 0;
+ if (!unwind_special(info, e->region_start, frame_size)) {
+ info->prev_sp = info->sp - frame_size;
+ if (e->Millicode)
+ info->rp = info->r31;
+ else if (rpoffset)
+ info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
+ info->prev_ip = info->rp;
+ info->rp = 0;
+ }
dbg("analyzing func @ %lx, setting prev_sp=%lx "
"prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp,
@@ -344,7 +368,7 @@ void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct
struct pt_regs *r = &t->thread.regs;
struct pt_regs *r2;
- r2 = (struct pt_regs *)kmalloc(sizeof(struct pt_regs), GFP_KERNEL);
+ r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC);
if (!r2)
return;
*r2 = *r;
@@ -390,4 +414,28 @@ int unwind_to_user(struct unwind_frame_info *info)
return ret;
}
-module_init(unwind_init);
+unsigned long return_address(unsigned int level)
+{
+ struct unwind_frame_info info;
+ struct pt_regs r;
+ unsigned long sp;
+
+ /* initialize unwind info */
+ asm volatile ("copy %%r30, %0" : "=r"(sp));
+ memset(&r, 0, sizeof(struct pt_regs));
+ r.iaoq[0] = (unsigned long) current_text_addr();
+ r.gr[2] = (unsigned long) __builtin_return_address(0);
+ r.gr[30] = sp;
+ unwind_frame_init(&info, current, &r);
+
+ /* unwind stack */
+ ++level;
+ do {
+ if (unwind_once(&info) < 0 || info.ip == 0)
+ return 0;
+ if (!kernel_text_address(info.ip))
+ return 0;
+ } while (info.ip && level--);
+
+ return info.ip;
+}
diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S
index e5fac3e08c7..0dacc5ca555 100644
--- a/arch/parisc/kernel/vmlinux.lds.S
+++ b/arch/parisc/kernel/vmlinux.lds.S
@@ -6,27 +6,24 @@
* Copyright (C) 2000 Michael Ang <mang with subcarrier.org>
* Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org>
* Copyright (C) 2003 James Bottomley <jejb with parisc-linux.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Copyright (C) 2006-2013 Helge Deller <deller@gmx.de>
+ */
+
+/*
+ * Put page table entries (swapper_pg_dir) as the first thing in .bss. This
+ * will ensure that it has .bss alignment (PAGE_SIZE).
*/
-#include <linux/config.h>
+#define BSS_FIRST_SECTIONS *(.data..vm0.pmd) \
+ *(.data..vm0.pgd) \
+ *(.data..vm0.pte)
+
#include <asm-generic/vmlinux.lds.h>
+
/* needed for the processor specific cache alignment size */
#include <asm/cache.h>
#include <asm/page.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
/* ld script to make hppa Linux kernel */
#ifndef CONFIG_64BIT
@@ -37,7 +34,7 @@ OUTPUT_FORMAT("elf64-hppa-linux")
OUTPUT_ARCH(hppa:hppa2.0w)
#endif
-ENTRY(_stext)
+ENTRY(parisc_kernel_start)
#ifndef CONFIG_64BIT
jiffies = jiffies_64 + 4;
#else
@@ -45,163 +42,119 @@ jiffies = jiffies_64;
#endif
SECTIONS
{
+ . = KERNEL_BINARY_TEXT_START;
+
+ __init_begin = .;
+ HEAD_TEXT_SECTION
+ INIT_TEXT_SECTION(8)
+
+ . = ALIGN(PAGE_SIZE);
+ INIT_DATA_SECTION(PAGE_SIZE)
+ /* we have to discard exit text and such at runtime, not link time */
+ .exit.text :
+ {
+ EXIT_TEXT
+ }
+ .exit.data :
+ {
+ EXIT_DATA
+ }
+ PERCPU_SECTION(8)
+ . = ALIGN(PAGE_SIZE);
+ __init_end = .;
+ /* freed after init ends here */
+
+ _text = .; /* Text and read-only data */
+ _stext = .;
+ .text ALIGN(PAGE_SIZE) : {
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
+ KPROBES_TEXT
+ IRQENTRY_TEXT
+ *(.text.do_softirq)
+ *(.text.sys_exit)
+ *(.text.do_sigaltstack)
+ *(.text.do_fork)
+ *(.text.*)
+ *(.fixup)
+ *(.lock.text) /* out-of-line lock text */
+ *(.gnu.warning)
+ }
+ . = ALIGN(PAGE_SIZE);
+ _etext = .;
+ /* End of text section */
+
+ /* Start of data section */
+ _sdata = .;
- . = KERNEL_BINARY_TEXT_START;
-
- _text = .; /* Text and read-only data */
- .text ALIGN(16) : {
- *(.text)
- SCHED_TEXT
- LOCK_TEXT
- *(.text.do_softirq)
- *(.text.sys_exit)
- *(.text.do_sigaltstack)
- *(.text.do_fork)
- *(.text.*)
- *(.fixup)
- *(.lock.text) /* out-of-line lock text */
- *(.gnu.warning)
- } = 0
-
- _etext = .; /* End of text section */
-
- RODATA
-
- /* writeable */
- . = ALIGN(4096); /* Make sure this is paged aligned so
- that we can properly leave these
- as writable */
- data_start = .;
-
- . = ALIGN(16); /* Exception table */
- __start___ex_table = .;
- __ex_table : { *(__ex_table) }
- __stop___ex_table = .;
-
- __start___unwind = .; /* unwind info */
- .PARISC.unwind : { *(.PARISC.unwind) }
- __stop___unwind = .;
-
- .data : { /* Data */
- *(.data)
- *(.data.vm0.pmd)
- *(.data.vm0.pgd)
- *(.data.vm0.pte)
- CONSTRUCTORS
+ RO_DATA_SECTION(8)
+
+#ifdef CONFIG_64BIT
+ . = ALIGN(16);
+ /* Linkage tables */
+ .opd : {
+ *(.opd)
+ } PROVIDE (__gp = .);
+ .plt : {
+ *(.plt)
+ }
+ .dlt : {
+ *(.dlt)
}
+#endif
- . = ALIGN(4096);
- /* nosave data is really only used for software suspend...it's here
- * just in case we ever implement it */
- __nosave_begin = .;
- .data_nosave : { *(.data.nosave) }
- . = ALIGN(4096);
- __nosave_end = .;
+ /* unwind info */
+ .PARISC.unwind : {
+ __start___unwind = .;
+ *(.PARISC.unwind)
+ __stop___unwind = .;
+ }
- . = ALIGN(L1_CACHE_BYTES);
- .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+ /* writeable */
+ /* Make sure this is page aligned so
+ * that we can properly leave these
+ * as writable
+ */
+ . = ALIGN(PAGE_SIZE);
+ data_start = .;
- /* PA-RISC locks requires 16-byte alignment */
- . = ALIGN(16);
- .data.lock_aligned : { *(.data.lock_aligned) }
+ EXCEPTION_TABLE(8)
+ NOTES
- _edata = .; /* End of data section */
+ /* Data */
+ RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, PAGE_SIZE)
- . = ALIGN(16384); /* init_task */
- .data.init_task : { *(.data.init_task) }
+ /* PA-RISC locks requires 16-byte alignment */
+ . = ALIGN(16);
+ .data..lock_aligned : {
+ *(.data..lock_aligned)
+ }
- /* The interrupt stack is currently partially coded, but not yet
- * implemented */
- . = ALIGN(16384);
- init_istack : { *(init_istack) }
+ /* End of data section */
+ _edata = .;
-#ifdef CONFIG_64BIT
- . = ALIGN(16); /* Linkage tables */
- .opd : { *(.opd) } PROVIDE (__gp = .);
- .plt : { *(.plt) }
- .dlt : { *(.dlt) }
-#endif
+ /* BSS */
+ BSS_SECTION(PAGE_SIZE, PAGE_SIZE, 8)
- . = ALIGN(16384);
- __init_begin = .;
- .init.text : {
- _sinittext = .;
- *(.init.text)
- _einittext = .;
- }
- .init.data : { *(.init.data) }
- . = ALIGN(16);
- __setup_start = .;
- .init.setup : { *(.init.setup) }
- __setup_end = .;
- __initcall_start = .;
- .initcall.init : {
- *(.initcall1.init)
- *(.initcall2.init)
- *(.initcall3.init)
- *(.initcall4.init)
- *(.initcall5.init)
- *(.initcall6.init)
- *(.initcall7.init)
- }
- __initcall_end = .;
- __con_initcall_start = .;
- .con_initcall.init : { *(.con_initcall.init) }
- __con_initcall_end = .;
- SECURITY_INIT
- /* alternate instruction replacement. This is a mechanism x86 uses
- * to detect the CPU type and replace generic instruction sequences
- * with CPU specific ones. We don't currently do this in PA, but
- * it seems like a good idea... */
- . = ALIGN(4);
- __alt_instructions = .;
- .altinstructions : { *(.altinstructions) }
- __alt_instructions_end = .;
- .altinstr_replacement : { *(.altinstr_replacement) }
- /* .exit.text is discard at runtime, not link time, to deal with references
- from .altinstructions and .eh_frame */
- .exit.text : { *(.exit.text) }
- .exit.data : { *(.exit.data) }
- . = ALIGN(4096);
- __initramfs_start = .;
- .init.ramfs : { *(.init.ramfs) }
- __initramfs_end = .;
- . = ALIGN(32);
- __per_cpu_start = .;
- .data.percpu : { *(.data.percpu) }
- __per_cpu_end = .;
- . = ALIGN(4096);
- __init_end = .;
- /* freed after init ends here */
-
- __bss_start = .; /* BSS */
- .bss : { *(.bss) *(COMMON) }
- __bss_stop = .;
+ _end = . ;
- _end = . ;
+ STABS_DEBUG
+ .note 0 : { *(.note) }
- /* Sections to be discarded */
- /DISCARD/ : {
- *(.exitcall.exit)
+ /* Sections to be discarded */
+ DISCARDS
+ /DISCARD/ : {
#ifdef CONFIG_64BIT
- /* temporary hack until binutils is fixed to not emit these
- for static binaries */
- *(.interp)
- *(.dynsym)
- *(.dynstr)
- *(.dynamic)
- *(.hash)
+ /* temporary hack until binutils is fixed to not emit these
+ * for static binaries
+ */
+ *(.interp)
+ *(.dynsym)
+ *(.dynstr)
+ *(.dynamic)
+ *(.hash)
+ *(.gnu.hash)
#endif
}
-
- /* Stabs debugging sections. */
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
- .comment 0 : { *(.comment) }
- .note 0 : { *(.note) }
-
}