diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /arch/parisc/kernel | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/parisc/kernel')
73 files changed, 30546 insertions, 0 deletions
diff --git a/arch/parisc/kernel/.gitignore b/arch/parisc/kernel/.gitignore new file mode 100644 index 000000000..bbb90f92d --- /dev/null +++ b/arch/parisc/kernel/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +vmlinux.lds diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile new file mode 100644 index 000000000..3d138c9cf --- /dev/null +++ b/arch/parisc/kernel/Makefile @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for arch/parisc/kernel +# + +extra-y := vmlinux.lds + +obj-y := head.o cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \ + pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \ + ptrace.o hardware.o inventory.o drivers.o alternative.o \ + signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \ + process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \ + patch.o toc.o toc_asm.o + +ifdef CONFIG_FUNCTION_TRACER +# Do not profile debug and lowlevel utilities +CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_cache.o = $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_perf.o = $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_unwind.o = $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_patch.o = $(CC_FLAGS_FTRACE) +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) += 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 $(obj64-y) +obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += topology.o +obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o +obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o +obj-$(CONFIG_JUMP_LABEL) += jump_label.o +obj-$(CONFIG_KGDB) += kgdb.o +obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KEXEC_CORE) += kexec.o relocate_kernel.o +obj-$(CONFIG_KEXEC_FILE) += kexec_file.o + +# vdso +obj-y += vdso.o +obj-$(CONFIG_64BIT) += vdso64/ +obj-y += vdso32/ diff --git a/arch/parisc/kernel/alternative.c b/arch/parisc/kernel/alternative.c new file mode 100644 index 000000000..25c4d6c33 --- /dev/null +++ b/arch/parisc/kernel/alternative.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Alternative live-patching for parisc. + * Copyright (C) 2018 Helge Deller <deller@gmx.de> + * + */ + +#include <asm/processor.h> +#include <asm/sections.h> +#include <asm/alternative.h> +#include <asm/cacheflush.h> + +#include <linux/module.h> + +static int no_alternatives; +static int __init setup_no_alternatives(char *str) +{ + no_alternatives = 1; + return 1; +} +__setup("no-alternatives", setup_no_alternatives); + +void __init_or_module apply_alternatives(struct alt_instr *start, + struct alt_instr *end, const char *module_name) +{ + struct alt_instr *entry; + int index = 0, applied = 0; + int num_cpus = num_present_cpus(); + u16 cond_check; + + cond_check = ALT_COND_ALWAYS | + ((num_cpus == 1) ? ALT_COND_NO_SMP : 0) | + ((cache_info.dc_size == 0) ? ALT_COND_NO_DCACHE : 0) | + ((cache_info.ic_size == 0) ? ALT_COND_NO_ICACHE : 0) | + (running_on_qemu ? ALT_COND_RUN_ON_QEMU : 0) | + ((split_tlb == 0) ? ALT_COND_NO_SPLIT_TLB : 0) | + /* + * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit + * set (bit #61, big endian), we have to flush and sync every + * time IO-PDIR is changed in Ike/Astro. + */ + (((boot_cpu_data.cpu_type > pcxw_) && + ((boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC) == 0)) + ? ALT_COND_NO_IOC_FDC : 0); + + for (entry = start; entry < end; entry++, index++) { + + u32 *from, replacement; + u16 cond; + s16 len; + + from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset); + len = entry->len; + cond = entry->cond; + replacement = entry->replacement; + + WARN_ON(!cond); + + if ((cond & ALT_COND_ALWAYS) == 0 && no_alternatives) + continue; + + pr_debug("Check %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n", + index, cond, len, from, replacement); + + /* Bounce out if none of the conditions are true. */ + if ((cond & cond_check) == 0) + continue; + + /* Want to replace pdtlb by a pdtlb,l instruction? */ + if (replacement == INSN_PxTLB) { + replacement = *from; + if (boot_cpu_data.cpu_type >= pcxu) /* >= pa2.0 ? */ + replacement |= (1 << 10); /* set el bit */ + } + + /* + * Replace instruction with NOPs? + * For long distance insert a branch instruction instead. + */ + if (replacement == INSN_NOP && len > 1) + replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */ + + pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n", + index, cond, len, replacement, from, from); + + if (len < 0) { + /* Replace multiple instruction by new code */ + u32 *source; + len = -len; + source = (u32 *)((ulong)&entry->replacement + entry->replacement); + memcpy(from, source, 4 * len); + } else { + /* Replace by one instruction */ + *from = replacement; + } + applied++; + } + + pr_info("%s%salternatives: applied %d out of %d patches\n", + module_name ? : "", module_name ? " " : "", + applied, index); +} + + +void __init apply_alternatives_all(void) +{ + set_kernel_text_rw(1); + + apply_alternatives((struct alt_instr *) &__alt_instructions, + (struct alt_instr *) &__alt_instructions_end, NULL); + + if (cache_info.dc_size == 0 && cache_info.ic_size == 0) { + pr_info("alternatives: optimizing cache-flushes.\n"); + static_branch_disable(&parisc_has_cache); + } + if (cache_info.dc_size == 0) + static_branch_disable(&parisc_has_dcache); + if (cache_info.ic_size == 0) + static_branch_disable(&parisc_has_icache); + + set_kernel_text_rw(0); +} diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c new file mode 100644 index 000000000..94652e13c --- /dev/null +++ b/arch/parisc/kernel/asm-offsets.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + * + * Copyright (C) 2000-2001 John Marvin <jsm at parisc-linux.org> + * Copyright (C) 2000 David Huggins-Daines <dhd with pobox.org> + * Copyright (C) 2000 Sam Creasey <sammy@sammy.net> + * Copyright (C) 2000 Grant Grundler <grundler with parisc-linux.org> + * Copyright (C) 2001 Paul Bame <bame at parisc-linux.org> + * Copyright (C) 2001 Richard Hirst <rhirst at parisc-linux.org> + * Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org> + * Copyright (C) 2003 James Bottomley <jejb at parisc-linux.org> + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/thread_info.h> +#include <linux/ptrace.h> +#include <linux/hardirq.h> +#include <linux/kbuild.h> +#include <linux/pgtable.h> + +#include <asm/assembly.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/pdc.h> +#include <uapi/asm/sigcontext.h> +#include <asm/ucontext.h> +#include <asm/rt_sigframe.h> +#include <linux/uaccess.h> +#include "signal32.h" + +/* 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_TI_FLAGS, offsetof(struct task_struct, thread_info.flags)); +#ifdef CONFIG_SMP + DEFINE(TASK_TI_CPU, offsetof(struct task_struct, thread_info.cpu)); +#endif + DEFINE(TASK_STACK, offsetof(struct task_struct, stack)); + DEFINE(TASK_PAGEFAULT_DISABLED, offsetof(struct task_struct, pagefault_disabled)); + BLANK(); + DEFINE(TASK_REGS, offsetof(struct task_struct, thread.regs)); + DEFINE(TASK_PT_PSW, offsetof(struct task_struct, thread.regs.gr[ 0])); + DEFINE(TASK_PT_GR1, offsetof(struct task_struct, thread.regs.gr[ 1])); + DEFINE(TASK_PT_GR2, offsetof(struct task_struct, thread.regs.gr[ 2])); + DEFINE(TASK_PT_GR3, offsetof(struct task_struct, thread.regs.gr[ 3])); + DEFINE(TASK_PT_GR4, offsetof(struct task_struct, thread.regs.gr[ 4])); + DEFINE(TASK_PT_GR5, offsetof(struct task_struct, thread.regs.gr[ 5])); + DEFINE(TASK_PT_GR6, offsetof(struct task_struct, thread.regs.gr[ 6])); + DEFINE(TASK_PT_GR7, offsetof(struct task_struct, thread.regs.gr[ 7])); + DEFINE(TASK_PT_GR8, offsetof(struct task_struct, thread.regs.gr[ 8])); + DEFINE(TASK_PT_GR9, offsetof(struct task_struct, thread.regs.gr[ 9])); + DEFINE(TASK_PT_GR10, offsetof(struct task_struct, thread.regs.gr[10])); + DEFINE(TASK_PT_GR11, offsetof(struct task_struct, thread.regs.gr[11])); + DEFINE(TASK_PT_GR12, offsetof(struct task_struct, thread.regs.gr[12])); + DEFINE(TASK_PT_GR13, offsetof(struct task_struct, thread.regs.gr[13])); + DEFINE(TASK_PT_GR14, offsetof(struct task_struct, thread.regs.gr[14])); + DEFINE(TASK_PT_GR15, offsetof(struct task_struct, thread.regs.gr[15])); + DEFINE(TASK_PT_GR16, offsetof(struct task_struct, thread.regs.gr[16])); + DEFINE(TASK_PT_GR17, offsetof(struct task_struct, thread.regs.gr[17])); + DEFINE(TASK_PT_GR18, offsetof(struct task_struct, thread.regs.gr[18])); + DEFINE(TASK_PT_GR19, offsetof(struct task_struct, thread.regs.gr[19])); + DEFINE(TASK_PT_GR20, offsetof(struct task_struct, thread.regs.gr[20])); + DEFINE(TASK_PT_GR21, offsetof(struct task_struct, thread.regs.gr[21])); + DEFINE(TASK_PT_GR22, offsetof(struct task_struct, thread.regs.gr[22])); + DEFINE(TASK_PT_GR23, offsetof(struct task_struct, thread.regs.gr[23])); + DEFINE(TASK_PT_GR24, offsetof(struct task_struct, thread.regs.gr[24])); + DEFINE(TASK_PT_GR25, offsetof(struct task_struct, thread.regs.gr[25])); + DEFINE(TASK_PT_GR26, offsetof(struct task_struct, thread.regs.gr[26])); + DEFINE(TASK_PT_GR27, offsetof(struct task_struct, thread.regs.gr[27])); + DEFINE(TASK_PT_GR28, offsetof(struct task_struct, thread.regs.gr[28])); + DEFINE(TASK_PT_GR29, offsetof(struct task_struct, thread.regs.gr[29])); + DEFINE(TASK_PT_GR30, offsetof(struct task_struct, thread.regs.gr[30])); + DEFINE(TASK_PT_GR31, offsetof(struct task_struct, thread.regs.gr[31])); + DEFINE(TASK_PT_FR0, offsetof(struct task_struct, thread.regs.fr[ 0])); + DEFINE(TASK_PT_FR1, offsetof(struct task_struct, thread.regs.fr[ 1])); + DEFINE(TASK_PT_FR2, offsetof(struct task_struct, thread.regs.fr[ 2])); + DEFINE(TASK_PT_FR3, offsetof(struct task_struct, thread.regs.fr[ 3])); + DEFINE(TASK_PT_FR4, offsetof(struct task_struct, thread.regs.fr[ 4])); + DEFINE(TASK_PT_FR5, offsetof(struct task_struct, thread.regs.fr[ 5])); + DEFINE(TASK_PT_FR6, offsetof(struct task_struct, thread.regs.fr[ 6])); + DEFINE(TASK_PT_FR7, offsetof(struct task_struct, thread.regs.fr[ 7])); + DEFINE(TASK_PT_FR8, offsetof(struct task_struct, thread.regs.fr[ 8])); + DEFINE(TASK_PT_FR9, offsetof(struct task_struct, thread.regs.fr[ 9])); + DEFINE(TASK_PT_FR10, offsetof(struct task_struct, thread.regs.fr[10])); + DEFINE(TASK_PT_FR11, offsetof(struct task_struct, thread.regs.fr[11])); + DEFINE(TASK_PT_FR12, offsetof(struct task_struct, thread.regs.fr[12])); + DEFINE(TASK_PT_FR13, offsetof(struct task_struct, thread.regs.fr[13])); + DEFINE(TASK_PT_FR14, offsetof(struct task_struct, thread.regs.fr[14])); + DEFINE(TASK_PT_FR15, offsetof(struct task_struct, thread.regs.fr[15])); + DEFINE(TASK_PT_FR16, offsetof(struct task_struct, thread.regs.fr[16])); + DEFINE(TASK_PT_FR17, offsetof(struct task_struct, thread.regs.fr[17])); + DEFINE(TASK_PT_FR18, offsetof(struct task_struct, thread.regs.fr[18])); + DEFINE(TASK_PT_FR19, offsetof(struct task_struct, thread.regs.fr[19])); + DEFINE(TASK_PT_FR20, offsetof(struct task_struct, thread.regs.fr[20])); + DEFINE(TASK_PT_FR21, offsetof(struct task_struct, thread.regs.fr[21])); + DEFINE(TASK_PT_FR22, offsetof(struct task_struct, thread.regs.fr[22])); + DEFINE(TASK_PT_FR23, offsetof(struct task_struct, thread.regs.fr[23])); + DEFINE(TASK_PT_FR24, offsetof(struct task_struct, thread.regs.fr[24])); + DEFINE(TASK_PT_FR25, offsetof(struct task_struct, thread.regs.fr[25])); + DEFINE(TASK_PT_FR26, offsetof(struct task_struct, thread.regs.fr[26])); + DEFINE(TASK_PT_FR27, offsetof(struct task_struct, thread.regs.fr[27])); + DEFINE(TASK_PT_FR28, offsetof(struct task_struct, thread.regs.fr[28])); + DEFINE(TASK_PT_FR29, offsetof(struct task_struct, thread.regs.fr[29])); + DEFINE(TASK_PT_FR30, offsetof(struct task_struct, thread.regs.fr[30])); + DEFINE(TASK_PT_FR31, offsetof(struct task_struct, thread.regs.fr[31])); + DEFINE(TASK_PT_SR0, offsetof(struct task_struct, thread.regs.sr[ 0])); + DEFINE(TASK_PT_SR1, offsetof(struct task_struct, thread.regs.sr[ 1])); + DEFINE(TASK_PT_SR2, offsetof(struct task_struct, thread.regs.sr[ 2])); + DEFINE(TASK_PT_SR3, offsetof(struct task_struct, thread.regs.sr[ 3])); + DEFINE(TASK_PT_SR4, offsetof(struct task_struct, thread.regs.sr[ 4])); + DEFINE(TASK_PT_SR5, offsetof(struct task_struct, thread.regs.sr[ 5])); + DEFINE(TASK_PT_SR6, offsetof(struct task_struct, thread.regs.sr[ 6])); + DEFINE(TASK_PT_SR7, offsetof(struct task_struct, thread.regs.sr[ 7])); + DEFINE(TASK_PT_IASQ0, offsetof(struct task_struct, thread.regs.iasq[0])); + DEFINE(TASK_PT_IASQ1, offsetof(struct task_struct, thread.regs.iasq[1])); + DEFINE(TASK_PT_IAOQ0, offsetof(struct task_struct, thread.regs.iaoq[0])); + DEFINE(TASK_PT_IAOQ1, offsetof(struct task_struct, thread.regs.iaoq[1])); + DEFINE(TASK_PT_CR27, offsetof(struct task_struct, thread.regs.cr27)); + DEFINE(TASK_PT_ORIG_R28, offsetof(struct task_struct, thread.regs.orig_r28)); + DEFINE(TASK_PT_KSP, offsetof(struct task_struct, thread.regs.ksp)); + DEFINE(TASK_PT_KPC, offsetof(struct task_struct, thread.regs.kpc)); + DEFINE(TASK_PT_SAR, offsetof(struct task_struct, thread.regs.sar)); + DEFINE(TASK_PT_IIR, offsetof(struct task_struct, thread.regs.iir)); + DEFINE(TASK_PT_ISR, offsetof(struct task_struct, thread.regs.isr)); + DEFINE(TASK_PT_IOR, offsetof(struct task_struct, thread.regs.ior)); + BLANK(); + DEFINE(PT_PSW, offsetof(struct pt_regs, gr[ 0])); + DEFINE(PT_GR1, offsetof(struct pt_regs, gr[ 1])); + DEFINE(PT_GR2, offsetof(struct pt_regs, gr[ 2])); + DEFINE(PT_GR3, offsetof(struct pt_regs, gr[ 3])); + DEFINE(PT_GR4, offsetof(struct pt_regs, gr[ 4])); + DEFINE(PT_GR5, offsetof(struct pt_regs, gr[ 5])); + DEFINE(PT_GR6, offsetof(struct pt_regs, gr[ 6])); + DEFINE(PT_GR7, offsetof(struct pt_regs, gr[ 7])); + DEFINE(PT_GR8, offsetof(struct pt_regs, gr[ 8])); + DEFINE(PT_GR9, offsetof(struct pt_regs, gr[ 9])); + DEFINE(PT_GR10, offsetof(struct pt_regs, gr[10])); + DEFINE(PT_GR11, offsetof(struct pt_regs, gr[11])); + DEFINE(PT_GR12, offsetof(struct pt_regs, gr[12])); + DEFINE(PT_GR13, offsetof(struct pt_regs, gr[13])); + DEFINE(PT_GR14, offsetof(struct pt_regs, gr[14])); + DEFINE(PT_GR15, offsetof(struct pt_regs, gr[15])); + DEFINE(PT_GR16, offsetof(struct pt_regs, gr[16])); + DEFINE(PT_GR17, offsetof(struct pt_regs, gr[17])); + DEFINE(PT_GR18, offsetof(struct pt_regs, gr[18])); + DEFINE(PT_GR19, offsetof(struct pt_regs, gr[19])); + DEFINE(PT_GR20, offsetof(struct pt_regs, gr[20])); + DEFINE(PT_GR21, offsetof(struct pt_regs, gr[21])); + DEFINE(PT_GR22, offsetof(struct pt_regs, gr[22])); + DEFINE(PT_GR23, offsetof(struct pt_regs, gr[23])); + DEFINE(PT_GR24, offsetof(struct pt_regs, gr[24])); + DEFINE(PT_GR25, offsetof(struct pt_regs, gr[25])); + DEFINE(PT_GR26, offsetof(struct pt_regs, gr[26])); + DEFINE(PT_GR27, offsetof(struct pt_regs, gr[27])); + DEFINE(PT_GR28, offsetof(struct pt_regs, gr[28])); + DEFINE(PT_GR29, offsetof(struct pt_regs, gr[29])); + DEFINE(PT_GR30, offsetof(struct pt_regs, gr[30])); + DEFINE(PT_GR31, offsetof(struct pt_regs, gr[31])); + DEFINE(PT_FR0, offsetof(struct pt_regs, fr[ 0])); + DEFINE(PT_FR1, offsetof(struct pt_regs, fr[ 1])); + DEFINE(PT_FR2, offsetof(struct pt_regs, fr[ 2])); + DEFINE(PT_FR3, offsetof(struct pt_regs, fr[ 3])); + DEFINE(PT_FR4, offsetof(struct pt_regs, fr[ 4])); + DEFINE(PT_FR5, offsetof(struct pt_regs, fr[ 5])); + DEFINE(PT_FR6, offsetof(struct pt_regs, fr[ 6])); + DEFINE(PT_FR7, offsetof(struct pt_regs, fr[ 7])); + DEFINE(PT_FR8, offsetof(struct pt_regs, fr[ 8])); + DEFINE(PT_FR9, offsetof(struct pt_regs, fr[ 9])); + DEFINE(PT_FR10, offsetof(struct pt_regs, fr[10])); + DEFINE(PT_FR11, offsetof(struct pt_regs, fr[11])); + DEFINE(PT_FR12, offsetof(struct pt_regs, fr[12])); + DEFINE(PT_FR13, offsetof(struct pt_regs, fr[13])); + DEFINE(PT_FR14, offsetof(struct pt_regs, fr[14])); + DEFINE(PT_FR15, offsetof(struct pt_regs, fr[15])); + DEFINE(PT_FR16, offsetof(struct pt_regs, fr[16])); + DEFINE(PT_FR17, offsetof(struct pt_regs, fr[17])); + DEFINE(PT_FR18, offsetof(struct pt_regs, fr[18])); + DEFINE(PT_FR19, offsetof(struct pt_regs, fr[19])); + DEFINE(PT_FR20, offsetof(struct pt_regs, fr[20])); + DEFINE(PT_FR21, offsetof(struct pt_regs, fr[21])); + DEFINE(PT_FR22, offsetof(struct pt_regs, fr[22])); + DEFINE(PT_FR23, offsetof(struct pt_regs, fr[23])); + DEFINE(PT_FR24, offsetof(struct pt_regs, fr[24])); + DEFINE(PT_FR25, offsetof(struct pt_regs, fr[25])); + DEFINE(PT_FR26, offsetof(struct pt_regs, fr[26])); + DEFINE(PT_FR27, offsetof(struct pt_regs, fr[27])); + DEFINE(PT_FR28, offsetof(struct pt_regs, fr[28])); + DEFINE(PT_FR29, offsetof(struct pt_regs, fr[29])); + DEFINE(PT_FR30, offsetof(struct pt_regs, fr[30])); + DEFINE(PT_FR31, offsetof(struct pt_regs, fr[31])); + DEFINE(PT_SR0, offsetof(struct pt_regs, sr[ 0])); + DEFINE(PT_SR1, offsetof(struct pt_regs, sr[ 1])); + DEFINE(PT_SR2, offsetof(struct pt_regs, sr[ 2])); + DEFINE(PT_SR3, offsetof(struct pt_regs, sr[ 3])); + DEFINE(PT_SR4, offsetof(struct pt_regs, sr[ 4])); + DEFINE(PT_SR5, offsetof(struct pt_regs, sr[ 5])); + DEFINE(PT_SR6, offsetof(struct pt_regs, sr[ 6])); + DEFINE(PT_SR7, offsetof(struct pt_regs, sr[ 7])); + DEFINE(PT_IASQ0, offsetof(struct pt_regs, iasq[0])); + DEFINE(PT_IASQ1, offsetof(struct pt_regs, iasq[1])); + DEFINE(PT_IAOQ0, offsetof(struct pt_regs, iaoq[0])); + DEFINE(PT_IAOQ1, offsetof(struct pt_regs, iaoq[1])); + DEFINE(PT_CR27, offsetof(struct pt_regs, cr27)); + DEFINE(PT_ORIG_R28, offsetof(struct pt_regs, orig_r28)); + DEFINE(PT_KSP, offsetof(struct pt_regs, ksp)); + DEFINE(PT_KPC, offsetof(struct pt_regs, kpc)); + DEFINE(PT_SAR, offsetof(struct pt_regs, sar)); + DEFINE(PT_IIR, offsetof(struct pt_regs, iir)); + DEFINE(PT_ISR, offsetof(struct pt_regs, isr)); + DEFINE(PT_IOR, offsetof(struct pt_regs, ior)); + /* PT_SZ_ALGN includes space for a stack frame. */ + DEFINE(PT_SZ_ALGN, align_frame(sizeof(struct pt_regs), FRAME_ALIGN)); + BLANK(); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_PRE_COUNT, offsetof(struct task_struct, thread_info.preempt_count)); + BLANK(); + DEFINE(ASM_SIGFRAME_SIZE, PARISC_RT_SIGFRAME_SIZE); + DEFINE(SIGFRAME_CONTEXT_REGS, offsetof(struct rt_sigframe, uc.uc_mcontext) - PARISC_RT_SIGFRAME_SIZE); +#ifdef CONFIG_64BIT + DEFINE(ASM_SIGFRAME_SIZE32, PARISC_RT_SIGFRAME_SIZE32); + DEFINE(SIGFRAME_CONTEXT_REGS32, offsetof(struct compat_rt_sigframe, uc.uc_mcontext) - PARISC_RT_SIGFRAME_SIZE32); +#else + DEFINE(ASM_SIGFRAME_SIZE32, PARISC_RT_SIGFRAME_SIZE); + DEFINE(SIGFRAME_CONTEXT_REGS32, offsetof(struct rt_sigframe, uc.uc_mcontext) - PARISC_RT_SIGFRAME_SIZE); +#endif + BLANK(); + DEFINE(ICACHE_BASE, offsetof(struct pdc_cache_info, ic_base)); + DEFINE(ICACHE_STRIDE, offsetof(struct pdc_cache_info, ic_stride)); + DEFINE(ICACHE_COUNT, offsetof(struct pdc_cache_info, ic_count)); + DEFINE(ICACHE_LOOP, offsetof(struct pdc_cache_info, ic_loop)); + DEFINE(DCACHE_BASE, offsetof(struct pdc_cache_info, dc_base)); + DEFINE(DCACHE_STRIDE, offsetof(struct pdc_cache_info, dc_stride)); + DEFINE(DCACHE_COUNT, offsetof(struct pdc_cache_info, dc_count)); + DEFINE(DCACHE_LOOP, offsetof(struct pdc_cache_info, dc_loop)); + DEFINE(ITLB_SID_BASE, offsetof(struct pdc_cache_info, it_sp_base)); + DEFINE(ITLB_SID_STRIDE, offsetof(struct pdc_cache_info, it_sp_stride)); + DEFINE(ITLB_SID_COUNT, offsetof(struct pdc_cache_info, it_sp_count)); + DEFINE(ITLB_OFF_BASE, offsetof(struct pdc_cache_info, it_off_base)); + DEFINE(ITLB_OFF_STRIDE, offsetof(struct pdc_cache_info, it_off_stride)); + DEFINE(ITLB_OFF_COUNT, offsetof(struct pdc_cache_info, it_off_count)); + DEFINE(ITLB_LOOP, offsetof(struct pdc_cache_info, it_loop)); + DEFINE(DTLB_SID_BASE, offsetof(struct pdc_cache_info, dt_sp_base)); + DEFINE(DTLB_SID_STRIDE, offsetof(struct pdc_cache_info, dt_sp_stride)); + DEFINE(DTLB_SID_COUNT, offsetof(struct pdc_cache_info, dt_sp_count)); + DEFINE(DTLB_OFF_BASE, offsetof(struct pdc_cache_info, dt_off_base)); + DEFINE(DTLB_OFF_STRIDE, offsetof(struct pdc_cache_info, dt_off_stride)); + DEFINE(DTLB_OFF_COUNT, offsetof(struct pdc_cache_info, dt_off_count)); + DEFINE(DTLB_LOOP, offsetof(struct pdc_cache_info, dt_loop)); + BLANK(); + 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); + DEFINE(ASM_BITS_PER_PGD, BITS_PER_PGD); + DEFINE(ASM_BITS_PER_PMD, BITS_PER_PMD); + DEFINE(ASM_BITS_PER_PTE, BITS_PER_PTE); + DEFINE(ASM_PMD_ENTRY, ((PAGE_OFFSET & PMD_MASK) >> PMD_SHIFT)); + DEFINE(ASM_PGD_ENTRY, PAGE_OFFSET >> PGDIR_SHIFT); + 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); + BLANK(); + /* HUGEPAGE_SIZE is only used in vmlinux.lds.S to align kernel text + * and kernel data on physical huge pages */ +#ifdef CONFIG_HUGETLB_PAGE + DEFINE(HUGEPAGE_SIZE, 1UL << REAL_HPAGE_SHIFT); +#else + DEFINE(HUGEPAGE_SIZE, PAGE_SIZE); +#endif + 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 000000000..f420b5552 --- /dev/null +++ b/arch/parisc/kernel/audit.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +#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 AUDITSC_OPEN; + case __NR_openat: + return AUDITSC_OPENAT; + case __NR_execve: + return AUDITSC_EXECVE; + case __NR_openat2: + return AUDITSC_OPENAT2; + default: + return AUDITSC_NATIVE; + } +} + +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/cache.c b/arch/parisc/kernel/cache.c new file mode 100644 index 000000000..ca4a302d4 --- /dev/null +++ b/arch/parisc/kernel/cache.c @@ -0,0 +1,776 @@ +/* + * 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-2006 Helge Deller <deller@gmx.de> (07-13-1999) + * Copyright (C) 1999 SuSE GmbH Nuernberg + * Copyright (C) 2000 Philipp Rumpf (prumpf@tux.org) + * + * Cache and TLB management + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/pagemap.h> +#include <linux/sched.h> +#include <linux/sched/mm.h> +#include <asm/pdc.h> +#include <asm/cache.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> +#include <asm/page.h> +#include <asm/processor.h> +#include <asm/sections.h> +#include <asm/shmparam.h> +#include <asm/mmu_context.h> + +int split_tlb __ro_after_init; +int dcache_stride __ro_after_init; +int icache_stride __ro_after_init; +EXPORT_SYMBOL(dcache_stride); + +void flush_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr); +EXPORT_SYMBOL(flush_dcache_page_asm); +void purge_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr); +void flush_icache_page_asm(unsigned long phys_addr, unsigned long vaddr); + +/* Internal implementation in arch/parisc/kernel/pacache.S */ +void flush_data_cache_local(void *); /* flushes local data-cache only */ +void flush_instruction_cache_local(void); /* flushes local code-cache only */ + +/* On some machines (i.e., ones with the Merced bus), there can be + * only a single PxTLB broadcast at a time; this must be guaranteed + * by software. We need a spinlock around all TLB flushes to ensure + * this. + */ +DEFINE_SPINLOCK(pa_tlb_flush_lock); + +#if defined(CONFIG_64BIT) && defined(CONFIG_SMP) +int pa_serialize_tlb_flushes __ro_after_init; +#endif + +struct pdc_cache_info cache_info __ro_after_init; +#ifndef CONFIG_PA20 +static struct pdc_btlb_info btlb_info __ro_after_init; +#endif + +DEFINE_STATIC_KEY_TRUE(parisc_has_cache); +DEFINE_STATIC_KEY_TRUE(parisc_has_dcache); +DEFINE_STATIC_KEY_TRUE(parisc_has_icache); + +static void cache_flush_local_cpu(void *dummy) +{ + if (static_branch_likely(&parisc_has_icache)) + flush_instruction_cache_local(); + if (static_branch_likely(&parisc_has_dcache)) + flush_data_cache_local(NULL); +} + +void flush_cache_all_local(void) +{ + cache_flush_local_cpu(NULL); +} + +void flush_cache_all(void) +{ + if (static_branch_likely(&parisc_has_cache)) + on_each_cpu(cache_flush_local_cpu, NULL, 1); +} + +static inline void flush_data_cache(void) +{ + if (static_branch_likely(&parisc_has_dcache)) + on_each_cpu(flush_data_cache_local, NULL, 1); +} + + +/* Kernel virtual address of pfn. */ +#define pfn_va(pfn) __va(PFN_PHYS(pfn)) + +void +__update_cache(pte_t pte) +{ + unsigned long pfn = pte_pfn(pte); + struct page *page; + + /* 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; + + page = pfn_to_page(pfn); + if (page_mapping_file(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 ); + 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, alias=%d)\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_loop == 1) ? "direct mapped" : buf), + cache_info.dc_conf.cc_alias + ); + seq_printf(m, "ITLB entries\t: %ld\n" "DTLB entries\t: %ld%s\n", + cache_info.it_size, + cache_info.dt_size, + cache_info.dt_conf.tc_sh ? " - shared with ITLB":"" + ); + +#ifndef CONFIG_PA20 + /* BTLB - Block TLB */ + if (btlb_info.max_size==0) { + seq_printf(m, "BTLB\t\t: not supported\n" ); + } else { + seq_printf(m, + "BTLB fixed\t: max. %d pages, pagesize=%d (%dMB)\n" + "BTLB fix-entr.\t: %d instruction, %d data (%d combined)\n" + "BTLB var-entr.\t: %d instruction, %d data (%d combined)\n", + btlb_info.max_size, (int)4096, + btlb_info.max_size>>8, + btlb_info.fixed_range_info.num_i, + btlb_info.fixed_range_info.num_d, + btlb_info.fixed_range_info.num_comb, + btlb_info.variable_range_info.num_i, + btlb_info.variable_range_info.num_d, + btlb_info.variable_range_info.num_comb + ); + } +#endif +} + +void __init +parisc_cache_init(void) +{ + if (pdc_cache_info(&cache_info) < 0) + panic("parisc_cache_init: pdc_cache_info failed"); + +#if 0 + printk("ic_size %lx dc_size %lx it_size %lx\n", + cache_info.ic_size, + cache_info.dc_size, + cache_info.it_size); + + printk("DC base 0x%lx stride 0x%lx count 0x%lx loop 0x%lx\n", + cache_info.dc_base, + cache_info.dc_stride, + cache_info.dc_count, + cache_info.dc_loop); + + printk("dc_conf = 0x%lx alias %d blk %d line %d shift %d\n", + *(unsigned long *) (&cache_info.dc_conf), + cache_info.dc_conf.cc_alias, + 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 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_hv); + + printk("IC base 0x%lx stride 0x%lx count 0x%lx loop 0x%lx\n", + cache_info.ic_base, + cache_info.ic_stride, + cache_info.ic_count, + cache_info.ic_loop); + + printk("IT base 0x%lx stride 0x%lx count 0x%lx loop 0x%lx off_base 0x%lx off_stride 0x%lx off_count 0x%lx\n", + cache_info.it_sp_base, + cache_info.it_sp_stride, + cache_info.it_sp_count, + cache_info.it_loop, + cache_info.it_off_base, + cache_info.it_off_stride, + cache_info.it_off_count); + + printk("DT base 0x%lx stride 0x%lx count 0x%lx loop 0x%lx off_base 0x%lx off_stride 0x%lx off_count 0x%lx\n", + cache_info.dt_sp_base, + cache_info.dt_sp_stride, + cache_info.dt_sp_count, + cache_info.dt_loop, + cache_info.dt_off_base, + cache_info.dt_off_stride, + cache_info.dt_off_count); + + printk("ic_conf = 0x%lx alias %d blk %d line %d shift %d\n", + *(unsigned long *) (&cache_info.ic_conf), + cache_info.ic_conf.cc_alias, + 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 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_hv); + + printk("D-TLB conf: sh %d page %d cst %d aid %d sr %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_sr); + + printk("I-TLB conf: sh %d page %d cst %d aid %d sr %d\n", + cache_info.it_conf.tc_sh, + cache_info.it_conf.tc_page, + cache_info.it_conf.tc_cst, + cache_info.it_conf.tc_aid, + cache_info.it_conf.tc_sr); +#endif + + split_tlb = 0; + if (cache_info.dt_conf.tc_sh == 0 || cache_info.dt_conf.tc_sh == 2) { + if (cache_info.dt_conf.tc_sh == 2) + printk(KERN_WARNING "Unexpected TLB configuration. " + "Will flush I/D separately (could be optimized).\n"); + + split_tlb = 1; + } + + /* "New and Improved" version from Jim Hull + * (1 << (cc_block-1)) * (cc_line << (4 + cnf.cc_shift)) + * The following CAFL_STRIDE is an optimized version, see + * http://lists.parisc-linux.org/pipermail/parisc-linux/2004-June/023625.html + * http://lists.parisc-linux.org/pipermail/parisc-linux/2004-June/023671.html + */ +#define CAFL_STRIDE(cnf) (cnf.cc_line << (3 + cnf.cc_block + cnf.cc_shift)) + dcache_stride = CAFL_STRIDE(cache_info.dc_conf); + icache_stride = CAFL_STRIDE(cache_info.ic_conf); +#undef CAFL_STRIDE + +#ifndef CONFIG_PA20 + if (pdc_btlb_info(&btlb_info) < 0) { + memset(&btlb_info, 0, sizeof btlb_info); + } +#endif + + if ((boot_cpu_data.pdc.capabilities & PDC_MODEL_NVA_MASK) == + PDC_MODEL_NVA_UNSUPPORTED) { + printk(KERN_WARNING "parisc_cache_init: Only equivalent aliasing supported!\n"); +#if 0 + panic("SMP kernel required to avoid non-equivalent aliasing"); +#endif + } +} + +void disable_sr_hashing(void) +{ + 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. */ + BUG(); + return; + + case pcxs: + case pcxt: + case pcxt_: + srhash_type = SRHASH_PCXST; + break; + + case pcxl: + srhash_type = SRHASH_PCXL; + break; + + case pcxl2: /* pcxl2 doesn't support space register hashing */ + return; + + default: /* Currently all PA2.0 machines use the same ins. sequence */ + srhash_type = SRHASH_PA20; + break; + } + + 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) +{ + if (!static_branch_likely(&parisc_has_cache)) + return; + preempt_disable(); + flush_dcache_page_asm(physaddr, vmaddr); + if (vma->vm_flags & VM_EXEC) + flush_icache_page_asm(physaddr, vmaddr); + preempt_enable(); +} + +static void flush_user_cache_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + unsigned long flags, space, pgd, prot; +#ifdef CONFIG_TLB_PTLOCK + unsigned long pgd_lock; +#endif + + vmaddr &= PAGE_MASK; + + preempt_disable(); + + /* Set context for flush */ + local_irq_save(flags); + prot = mfctl(8); + space = mfsp(SR_USER); + pgd = mfctl(25); +#ifdef CONFIG_TLB_PTLOCK + pgd_lock = mfctl(28); +#endif + switch_mm_irqs_off(NULL, vma->vm_mm, NULL); + local_irq_restore(flags); + + flush_user_dcache_range_asm(vmaddr, vmaddr + PAGE_SIZE); + if (vma->vm_flags & VM_EXEC) + flush_user_icache_range_asm(vmaddr, vmaddr + PAGE_SIZE); + flush_tlb_page(vma, vmaddr); + + /* Restore previous context */ + local_irq_save(flags); +#ifdef CONFIG_TLB_PTLOCK + mtctl(pgd_lock, 28); +#endif + mtctl(pgd, 25); + mtsp(space, SR_USER); + mtctl(prot, 8); + local_irq_restore(flags); + + preempt_enable(); +} + +static inline pte_t *get_ptep(struct mm_struct *mm, unsigned long addr) +{ + pte_t *ptep = NULL; + pgd_t *pgd = mm->pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + + if (!pgd_none(*pgd)) { + p4d = p4d_offset(pgd, addr); + if (!p4d_none(*p4d)) { + pud = pud_offset(p4d, addr); + if (!pud_none(*pud)) { + pmd = pmd_offset(pud, addr); + if (!pmd_none(*pmd)) + ptep = pte_offset_map(pmd, addr); + } + } + } + return ptep; +} + +static inline bool pte_needs_flush(pte_t pte) +{ + return (pte_val(pte) & (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_NO_CACHE)) + == (_PAGE_PRESENT | _PAGE_ACCESSED); +} + +void flush_dcache_page(struct page *page) +{ + struct address_space *mapping = page_mapping_file(page); + struct vm_area_struct *mpnt; + unsigned long offset; + unsigned long addr, old_addr = 0; + unsigned long count = 0; + unsigned long flags; + pgoff_t pgoff; + + if (mapping && !mapping_mapped(mapping)) { + set_bit(PG_dcache_dirty, &page->flags); + return; + } + + flush_kernel_dcache_page_addr(page_address(page)); + + if (!mapping) + return; + + pgoff = page->index; + + /* + * We have carefully arranged in arch_get_unmapped_area() that + * *any* mappings of a file are always congruently mapped (whether + * declared as MAP_PRIVATE or MAP_SHARED), so we only need + * to flush one address here for them all to become coherent + * on machines that support equivalent aliasing + */ + flush_dcache_mmap_lock_irqsave(mapping, flags); + vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) { + offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; + addr = mpnt->vm_start + offset; + if (parisc_requires_coherency()) { + pte_t *ptep; + + ptep = get_ptep(mpnt->vm_mm, addr); + if (ptep && pte_needs_flush(*ptep)) + flush_user_cache_page(mpnt, addr); + } else { + /* + * 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)); + /* + * Software is allowed to have any number + * of private mappings to a page. + */ + if (!(mpnt->vm_flags & VM_SHARED)) + continue; + if (old_addr) + pr_err("INEQUIVALENT ALIASES 0x%lx and 0x%lx in file %pD\n", + old_addr, addr, mpnt->vm_file); + old_addr = addr; + } + } + WARN_ON(++count == 4096); + } + flush_dcache_mmap_unlock_irqrestore(mapping, flags); +} +EXPORT_SYMBOL(flush_dcache_page); + +/* Defined in arch/parisc/kernel/pacache.S */ +EXPORT_SYMBOL(flush_kernel_dcache_range_asm); +EXPORT_SYMBOL(flush_kernel_icache_range_asm); + +#define FLUSH_THRESHOLD 0x80000 /* 0.5MB */ +static unsigned long parisc_cache_flush_threshold __ro_after_init = FLUSH_THRESHOLD; + +#define FLUSH_TLB_THRESHOLD (16*1024) /* 16 KiB minimum TLB threshold */ +static unsigned long parisc_tlb_flush_threshold __ro_after_init = ~0UL; + +void __init parisc_setup_cache_timing(void) +{ + unsigned long rangetime, alltime; + unsigned long size; + unsigned long threshold, threshold2; + + alltime = mfctl(16); + flush_data_cache(); + alltime = mfctl(16) - alltime; + + size = (unsigned long)(_end - _text); + rangetime = mfctl(16); + flush_kernel_dcache_range((unsigned long)_text, size); + rangetime = mfctl(16) - rangetime; + + printk(KERN_DEBUG "Whole cache flush %lu cycles, flushing %lu bytes %lu cycles\n", + alltime, size, rangetime); + + threshold = L1_CACHE_ALIGN((unsigned long)((uint64_t)size * alltime / rangetime)); + pr_info("Calculated flush threshold is %lu KiB\n", + threshold/1024); + + /* + * The threshold computed above isn't very reliable. The following + * heuristic works reasonably well on c8000/rp3440. + */ + threshold2 = cache_info.dc_size * num_online_cpus(); + parisc_cache_flush_threshold = threshold2; + printk(KERN_INFO "Cache flush threshold set to %lu KiB\n", + parisc_cache_flush_threshold/1024); + + /* calculate TLB flush threshold */ + + /* On SMP machines, skip the TLB measure of kernel text which + * has been mapped as huge pages. */ + if (num_online_cpus() > 1 && !parisc_requires_coherency()) { + threshold = max(cache_info.it_size, cache_info.dt_size); + threshold *= PAGE_SIZE; + threshold /= num_online_cpus(); + goto set_tlb_threshold; + } + + size = (unsigned long)_end - (unsigned long)_text; + rangetime = mfctl(16); + flush_tlb_kernel_range((unsigned long)_text, (unsigned long)_end); + rangetime = mfctl(16) - rangetime; + + alltime = mfctl(16); + flush_tlb_all(); + alltime = mfctl(16) - alltime; + + printk(KERN_INFO "Whole TLB flush %lu cycles, Range flush %lu bytes %lu cycles\n", + alltime, size, rangetime); + + threshold = PAGE_ALIGN((num_online_cpus() * size * alltime) / rangetime); + printk(KERN_INFO "Calculated TLB flush threshold %lu KiB\n", + threshold/1024); + +set_tlb_threshold: + if (threshold > FLUSH_TLB_THRESHOLD) + parisc_tlb_flush_threshold = threshold; + else + parisc_tlb_flush_threshold = FLUSH_TLB_THRESHOLD; + + printk(KERN_INFO "TLB flush threshold set to %lu KiB\n", + parisc_tlb_flush_threshold/1024); +} + +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(const void *addr) +{ + unsigned long flags; + + flush_kernel_dcache_page_asm(addr); + purge_tlb_start(flags); + pdtlb(SR_KERNEL, addr); + purge_tlb_end(flags); +} +EXPORT_SYMBOL(flush_kernel_dcache_page_addr); + +static void flush_cache_page_if_present(struct vm_area_struct *vma, + unsigned long vmaddr, unsigned long pfn) +{ + pte_t *ptep = get_ptep(vma->vm_mm, vmaddr); + + /* + * The pte check is racy and sometimes the flush will trigger + * a non-access TLB miss. Hopefully, the page has already been + * flushed. + */ + if (ptep && pte_needs_flush(*ptep)) + flush_cache_page(vma, vmaddr, pfn); +} + +void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + void *kto, *kfrom; + + kfrom = kmap_local_page(from); + kto = kmap_local_page(to); + flush_cache_page_if_present(vma, vaddr, page_to_pfn(from)); + copy_page_asm(kto, kfrom); + kunmap_local(kto); + kunmap_local(kfrom); +} + +void copy_to_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, void *dst, void *src, int len) +{ + flush_cache_page_if_present(vma, user_vaddr, page_to_pfn(page)); + memcpy(dst, src, len); + flush_kernel_dcache_range_asm((unsigned long)dst, (unsigned long)dst + len); +} + +void copy_from_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, void *dst, void *src, int len) +{ + flush_cache_page_if_present(vma, user_vaddr, page_to_pfn(page)); + memcpy(dst, src, len); +} + +/* __flush_tlb_range() + * + * returns 1 if all TLBs were flushed. + */ +int __flush_tlb_range(unsigned long sid, unsigned long start, + unsigned long end) +{ + unsigned long flags; + + if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) && + end - start >= parisc_tlb_flush_threshold) { + flush_tlb_all(); + return 1; + } + + /* Purge TLB entries for small ranges using the pdtlb and + pitlb instructions. These instructions execute locally + but cause a purge request to be broadcast to other TLBs. */ + while (start < end) { + purge_tlb_start(flags); + mtsp(sid, SR_TEMP1); + pdtlb(SR_TEMP1, start); + pitlb(SR_TEMP1, start); + purge_tlb_end(flags); + start += PAGE_SIZE; + } + return 0; +} + +static void flush_cache_pages(struct vm_area_struct *vma, unsigned long start, unsigned long end) +{ + unsigned long addr, pfn; + pte_t *ptep; + + for (addr = start; addr < end; addr += PAGE_SIZE) { + /* + * The vma can contain pages that aren't present. Although + * the pte search is expensive, we need the pte to find the + * page pfn and to check whether the page should be flushed. + */ + ptep = get_ptep(vma->vm_mm, addr); + if (ptep && pte_needs_flush(*ptep)) { + if (parisc_requires_coherency()) { + flush_user_cache_page(vma, addr); + } else { + pfn = pte_pfn(*ptep); + if (WARN_ON(!pfn_valid(pfn))) + return; + __flush_cache_page(vma, addr, PFN_PHYS(pfn)); + } + } + } +} + +static inline unsigned long mm_total_size(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + unsigned long usize = 0; + VMA_ITERATOR(vmi, mm, 0); + + for_each_vma(vmi, vma) { + if (usize >= parisc_cache_flush_threshold) + break; + usize += vma->vm_end - vma->vm_start; + } + return usize; +} + +void flush_cache_mm(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + VMA_ITERATOR(vmi, mm, 0); + + /* + * Flushing the whole cache on each cpu takes forever on + * rp3440, etc. So, avoid it if the mm isn't too big. + * + * Note that we must flush the entire cache on machines + * with aliasing caches to prevent random segmentation + * faults. + */ + if (!parisc_requires_coherency() + || mm_total_size(mm) >= parisc_cache_flush_threshold) { + if (WARN_ON(IS_ENABLED(CONFIG_SMP) && arch_irqs_disabled())) + return; + flush_tlb_all(); + flush_cache_all(); + return; + } + + /* Flush mm */ + for_each_vma(vmi, vma) + flush_cache_pages(vma, vma->vm_start, vma->vm_end); +} + +void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) +{ + if (!parisc_requires_coherency() + || end - start >= parisc_cache_flush_threshold) { + if (WARN_ON(IS_ENABLED(CONFIG_SMP) && arch_irqs_disabled())) + return; + flush_tlb_range(vma, start, end); + flush_cache_all(); + return; + } + + flush_cache_pages(vma, start, end); +} + +void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn) +{ + if (WARN_ON(!pfn_valid(pfn))) + return; + if (parisc_requires_coherency()) + flush_user_cache_page(vma, vmaddr); + else + __flush_cache_page(vma, vmaddr, PFN_PHYS(pfn)); +} + +void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr) +{ + if (!PageAnon(page)) + return; + + if (parisc_requires_coherency()) { + if (vma->vm_flags & VM_SHARED) + flush_data_cache(); + else + flush_user_cache_page(vma, vmaddr); + return; + } + + flush_tlb_page(vma, vmaddr); + preempt_disable(); + flush_dcache_page_asm(page_to_phys(page), vmaddr); + preempt_enable(); +} + +void flush_kernel_vmap_range(void *vaddr, int size) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size; + + if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) && + (unsigned long)size >= parisc_cache_flush_threshold) { + flush_tlb_kernel_range(start, end); + flush_data_cache(); + return; + } + + flush_kernel_dcache_range_asm(start, end); + flush_tlb_kernel_range(start, end); +} +EXPORT_SYMBOL(flush_kernel_vmap_range); + +void invalidate_kernel_vmap_range(void *vaddr, int size) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size; + + /* Ensure DMA is complete */ + asm_syncdma(); + + if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) && + (unsigned long)size >= parisc_cache_flush_threshold) { + flush_tlb_kernel_range(start, end); + flush_data_cache(); + return; + } + + purge_kernel_dcache_range_asm(start, end); + flush_tlb_kernel_range(start, end); +} +EXPORT_SYMBOL(invalidate_kernel_vmap_range); diff --git a/arch/parisc/kernel/compat_audit.c b/arch/parisc/kernel/compat_audit.c new file mode 100644 index 000000000..539b16891 --- /dev/null +++ b/arch/parisc/kernel/compat_audit.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/audit_arch.h> +#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 AUDITSC_OPEN; + case __NR_openat: + return AUDITSC_OPENAT; + case __NR_execve: + return AUDITSC_EXECVE; + case __NR_openat2: + return AUDITSC_OPENAT2; + default: + return AUDITSC_COMPAT; + } +} diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c new file mode 100644 index 000000000..8f12b9f31 --- /dev/null +++ b/arch/parisc/kernel/drivers.c @@ -0,0 +1,1101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * drivers.c + * + * Copyright (c) 1999 The Puffin Group + * Copyright (c) 2001 Matthew Wilcox for Hewlett Packard + * Copyright (c) 2001 Helge Deller <deller@gmx.de> + * Copyright (c) 2001,2002 Ryan Bradetich + * Copyright (c) 2004-2005 Thibaut VARENE <varenet@parisc-linux.org> + * + * The file handles registering devices and drivers, then matching them. + * It's the closest we get to a dating agency. + * + * If you're thinking about modifying this file, here are some gotchas to + * bear in mind: + * - 715/Mirage device paths have a dummy device between Lasi and its children + * - The EISA adapter may show up as a sibling or child of Wax + * - Dino has an optionally functional serial port. If firmware enables it, + * it shows up as a child of Dino. If firmware disables it, the buswalk + * finds it and it shows up as a child of Cujo + * - Dino has both parisc and pci devices as children + * - parisc devices are discovered in a random order, including children + * before parents in some cases. + */ + +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/export.h> +#include <linux/dma-map-ops.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/pdc.h> +#include <asm/parisc-device.h> +#include <asm/ropes.h> + +/* See comments in include/asm-parisc/pci.h */ +const struct dma_map_ops *hppa_dma_ops __ro_after_init; +EXPORT_SYMBOL(hppa_dma_ops); + +static struct device root = { + .init_name = "parisc", +}; + +static inline int check_dev(struct device *dev) +{ + if (dev->bus == &parisc_bus_type) { + struct parisc_device *pdev; + pdev = to_parisc_device(dev); + return pdev->id.hw_type != HPHW_FAULTY; + } + return 1; +} + +static struct device * +parse_tree_node(struct device *parent, int index, struct hardware_path *modpath); + +struct recurse_struct { + void * obj; + int (*fn)(struct device *, void *); +}; + +static int descend_children(struct device * dev, void * data) +{ + struct recurse_struct * recurse_data = (struct recurse_struct *)data; + + if (recurse_data->fn(dev, recurse_data->obj)) + return 1; + else + return device_for_each_child(dev, recurse_data, descend_children); +} + +/** + * for_each_padev - Iterate over all devices in the tree + * @fn: Function to call for each device. + * @data: Data to pass to the called function. + * + * This performs a depth-first traversal of the tree, calling the + * function passed for each node. It calls the function for parents + * before children. + */ + +static int for_each_padev(int (*fn)(struct device *, void *), void * data) +{ + struct recurse_struct recurse_data = { + .obj = data, + .fn = fn, + }; + return device_for_each_child(&root, &recurse_data, descend_children); +} + +/** + * match_device - Report whether this driver can handle this device + * @driver: the PA-RISC driver to try + * @dev: the PA-RISC device to try + */ +static int match_device(struct parisc_driver *driver, struct parisc_device *dev) +{ + const struct parisc_device_id *ids; + + for (ids = driver->id_table; ids->sversion; ids++) { + if ((ids->sversion != SVERSION_ANY_ID) && + (ids->sversion != dev->id.sversion)) + continue; + + if ((ids->hw_type != HWTYPE_ANY_ID) && + (ids->hw_type != dev->id.hw_type)) + continue; + + if ((ids->hversion != HVERSION_ANY_ID) && + (ids->hversion != dev->id.hversion)) + continue; + + return 1; + } + return 0; +} + +static int parisc_driver_probe(struct device *dev) +{ + int rc; + struct parisc_device *pa_dev = to_parisc_device(dev); + struct parisc_driver *pa_drv = to_parisc_driver(dev->driver); + + rc = pa_drv->probe(pa_dev); + + if (!rc) + pa_dev->driver = pa_drv; + + return rc; +} + +static void __exit parisc_driver_remove(struct device *dev) +{ + struct parisc_device *pa_dev = to_parisc_device(dev); + struct parisc_driver *pa_drv = to_parisc_driver(dev->driver); + + if (pa_drv->remove) + pa_drv->remove(pa_dev); +} + + +/** + * register_parisc_driver - Register this driver if it can handle a device + * @driver: the PA-RISC driver to try + */ +int register_parisc_driver(struct parisc_driver *driver) +{ + /* FIXME: we need this because apparently the sti + * driver can be registered twice */ + if (driver->drv.name) { + pr_warn("BUG: skipping previously registered driver %s\n", + driver->name); + return 1; + } + + if (!driver->probe) { + pr_warn("BUG: driver %s has no probe routine\n", driver->name); + return 1; + } + + driver->drv.bus = &parisc_bus_type; + + /* We install our own probe and remove routines */ + WARN_ON(driver->drv.probe != NULL); + WARN_ON(driver->drv.remove != NULL); + + driver->drv.name = driver->name; + + return driver_register(&driver->drv); +} +EXPORT_SYMBOL(register_parisc_driver); + + +struct match_count { + struct parisc_driver * driver; + int count; +}; + +static int match_and_count(struct device * dev, void * data) +{ + struct match_count * m = data; + struct parisc_device * pdev = to_parisc_device(dev); + + if (check_dev(dev)) { + if (match_device(m->driver, pdev)) + m->count++; + } + return 0; +} + +/** + * count_parisc_driver - count # of devices this driver would match + * @driver: the PA-RISC driver to try + * + * Use by IOMMU support to "guess" the right size IOPdir. + * Formula is something like memsize/(num_iommu * entry_size). + */ +int __init count_parisc_driver(struct parisc_driver *driver) +{ + struct match_count m = { + .driver = driver, + .count = 0, + }; + + for_each_padev(match_and_count, &m); + + return m.count; +} + + + +/** + * unregister_parisc_driver - Unregister this driver from the list of drivers + * @driver: the PA-RISC driver to unregister + */ +int unregister_parisc_driver(struct parisc_driver *driver) +{ + driver_unregister(&driver->drv); + return 0; +} +EXPORT_SYMBOL(unregister_parisc_driver); + +struct find_data { + unsigned long hpa; + struct parisc_device * dev; +}; + +static int find_device(struct device * dev, void * data) +{ + struct parisc_device * pdev = to_parisc_device(dev); + struct find_data * d = (struct find_data*)data; + + if (check_dev(dev)) { + if (pdev->hpa.start == d->hpa) { + d->dev = pdev; + return 1; + } + } + return 0; +} + +static struct parisc_device *find_device_by_addr(unsigned long hpa) +{ + struct find_data d = { + .hpa = hpa, + }; + int ret; + + ret = for_each_padev(find_device, &d); + return ret ? d.dev : NULL; +} + +static int __init is_IKE_device(struct device *dev, void *data) +{ + struct parisc_device *pdev = to_parisc_device(dev); + + if (!check_dev(dev)) + return 0; + if (pdev->id.hw_type != HPHW_BCPORT) + return 0; + if (IS_IKE(pdev) || + (pdev->id.hversion == REO_MERCED_PORT) || + (pdev->id.hversion == REOG_MERCED_PORT)) { + return 1; + } + return 0; +} + +int __init machine_has_merced_bus(void) +{ + int ret; + + ret = for_each_padev(is_IKE_device, NULL); + return ret ? 1 : 0; +} + +/** + * find_pa_parent_type - Find a parent of a specific type + * @dev: The device to start searching from + * @type: The device type to search for. + * + * Walks up the device tree looking for a device of the specified type. + * If it finds it, it returns it. If not, it returns NULL. + */ +const struct parisc_device * +find_pa_parent_type(const struct parisc_device *padev, int type) +{ + const struct device *dev = &padev->dev; + while (dev != &root) { + struct parisc_device *candidate = to_parisc_device(dev); + if (candidate->id.hw_type == type) + return candidate; + dev = dev->parent; + } + + return NULL; +} + +/* + * 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. + * This is because both callers pass the parent and fill in the mod + * themselves. If @node is a PCI device, we do fill it in, even though this + * is inconsistent. + */ +static void get_node_path(struct device *dev, struct hardware_path *path) +{ + int i = 5; + memset(&path->bc, -1, 6); + + if (dev_is_pci(dev)) { + unsigned int devfn = to_pci_dev(dev)->devfn; + path->mod = PCI_FUNC(devfn); + path->bc[i--] = PCI_SLOT(devfn); + dev = dev->parent; + } + + while (dev != &root) { + 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) { + path->bc[i--] = to_parisc_device(dev)->hw_path; + } + dev = dev->parent; + } +} + +static char *print_hwpath(struct hardware_path *path, char *output) +{ + int i; + for (i = 0; i < 6; i++) { + if (path->bc[i] == -1) + continue; + output += sprintf(output, "%u/", (unsigned char) path->bc[i]); + } + output += sprintf(output, "%u", (unsigned char) path->mod); + return output; +} + +/** + * print_pa_hwpath - Returns hardware path for PA devices + * dev: The device to return the path for + * output: Pointer to a previously-allocated array to place the path in. + * + * This function fills in the output array with a human-readable path + * to a PA device. This string is compatible with that used by PDC, and + * may be printed on the outside of the box. + */ +char *print_pa_hwpath(struct parisc_device *dev, char *output) +{ + struct hardware_path path; + + get_node_path(dev->dev.parent, &path); + path.mod = dev->hw_path; + return print_hwpath(&path, output); +} +EXPORT_SYMBOL(print_pa_hwpath); + +#if defined(CONFIG_PCI) || defined(CONFIG_ISA) +/** + * get_pci_node_path - Determines the hardware path for a PCI device + * @pdev: The device to return the path for + * @path: Pointer to a previously-allocated array to place the path in. + * + * This function fills in the hardware_path structure with the route to + * the specified PCI device. This structure is suitable for passing to + * PDC calls. + */ +void get_pci_node_path(struct pci_dev *pdev, struct hardware_path *path) +{ + get_node_path(&pdev->dev, path); +} +EXPORT_SYMBOL(get_pci_node_path); + +/** + * print_pci_hwpath - Returns hardware path for PCI devices + * dev: The device to return the path for + * output: Pointer to a previously-allocated array to place the path in. + * + * This function fills in the output array with a human-readable path + * to a PCI device. This string is compatible with that used by PDC, and + * may be printed on the outside of the box. + */ +char *print_pci_hwpath(struct pci_dev *dev, char *output) +{ + struct hardware_path path; + + get_pci_node_path(dev, &path); + return print_hwpath(&path, output); +} +EXPORT_SYMBOL(print_pci_hwpath); + +#endif /* defined(CONFIG_PCI) || defined(CONFIG_ISA) */ + +static void setup_bus_id(struct parisc_device *padev) +{ + struct hardware_path path; + char name[28]; + char *output = name; + int i; + + get_node_path(padev->dev.parent, &path); + + for (i = 0; i < 6; i++) { + if (path.bc[i] == -1) + continue; + 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 * __init create_tree_node(char id, struct device *parent) +{ + struct parisc_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->hw_path = id; + dev->id.hw_type = HPHW_FAULTY; + + dev->dev.parent = parent; + setup_bus_id(dev); + + dev->dev.bus = &parisc_bus_type; + dev->dma_mask = 0xffffffffUL; /* PARISC devices are 32-bit */ + + /* 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; + if (device_register(&dev->dev)) { + kfree(dev); + return NULL; + } + + return dev; +} + +struct match_id_data { + char id; + struct parisc_device * dev; +}; + +static int match_by_id(struct device * dev, void * data) +{ + struct parisc_device * pdev = to_parisc_device(dev); + struct match_id_data * d = data; + + if (pdev->hw_path == d->id) { + d->dev = pdev; + return 1; + } + return 0; +} + +/** + * alloc_tree_node - returns a device entry in the iotree + * @parent: the parent node in the tree + * @id: the element of the module path for this entry + * + * Checks all the children of @parent for a matching @id. If none + * found, it allocates a new device and returns it. + */ +static struct parisc_device * __init alloc_tree_node( + struct device *parent, char id) +{ + struct match_id_data d = { + .id = id, + }; + if (device_for_each_child(parent, &d, match_by_id)) + return d.dev; + else + return create_tree_node(id, parent); +} + +static struct parisc_device *create_parisc_device(struct hardware_path *modpath) +{ + int i; + struct device *parent = &root; + for (i = 0; i < 6; i++) { + if (modpath->bc[i] == -1) + continue; + parent = &alloc_tree_node(parent, modpath->bc[i])->dev; + } + return alloc_tree_node(parent, modpath->mod); +} + +struct parisc_device * __init +alloc_pa_dev(unsigned long hpa, struct hardware_path *mod_path) +{ + int status; + unsigned long bytecnt; + u8 iodc_data[32]; + struct parisc_device *dev; + const char *name; + + /* Check to make sure this device has not already been added - Ryan */ + if (find_device_by_addr(hpa) != NULL) + return NULL; + + status = pdc_iodc_read(&bytecnt, hpa, 0, &iodc_data, 32); + if (status != PDC_OK) + return NULL; + + dev = create_parisc_device(mod_path); + if (dev->id.hw_type != HPHW_FAULTY) { + pr_err("Two devices have hardware path [%s]. IODC data for second device: %7phN\n" + "Rearranging GSC cards sometimes helps\n", + parisc_pathname(dev), iodc_data); + return NULL; + } + + dev->id.hw_type = iodc_data[3] & 0x1f; + dev->id.hversion = (iodc_data[0] << 4) | ((iodc_data[1] & 0xf0) >> 4); + dev->id.hversion_rev = iodc_data[1] & 0x0f; + dev->id.sversion = ((iodc_data[4] & 0x0f) << 16) | + (iodc_data[5] << 8) | iodc_data[6]; + dev->hpa.start = hpa; + /* 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; + } + dev->hpa.flags = IORESOURCE_MEM; + dev->hpa.name = dev->name; + name = parisc_hardware_description(&dev->id) ? : "unknown"; + snprintf(dev->name, sizeof(dev->name), "%s [%s]", + name, parisc_pathname(dev)); + + /* Silently fail things like mouse ports which are subsumed within + * the keyboard controller + */ + if ((hpa & 0xfff) == 0 && insert_resource(&iomem_resource, &dev->hpa)) + pr_warn("Unable to claim HPA %lx for device %s\n", hpa, name); + + return dev; +} + +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) \ +{ \ + struct parisc_device *padev = to_parisc_device(dev); \ + return sprintf(buf, format_string, padev->field); \ +} \ +static DEVICE_ATTR_RO(name); + +#define pa_dev_attr_id(field, format) pa_dev_attr(field, id.field, format) + +pa_dev_attr(irq, irq, "%u\n"); +pa_dev_attr_id(hw_type, "0x%02x\n"); +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 DEVICE_ATTR_RO(modalias); + +static struct attribute *parisc_device_attrs[] = { + &dev_attr_irq.attr, + &dev_attr_hw_type.attr, + &dev_attr_rev.attr, + &dev_attr_hversion.attr, + &dev_attr_sversion.attr, + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(parisc_device); + +struct bus_type parisc_bus_type = { + .name = "parisc", + .match = parisc_generic_match, + .uevent = parisc_uevent, + .dev_groups = parisc_device_groups, + .probe = parisc_driver_probe, + .remove = __exit_p(parisc_driver_remove), +}; + +/** + * register_parisc_device - Locate a driver to manage this device. + * @dev: The parisc device. + * + * Search the driver list for a driver that is willing to manage + * this device. + */ +int __init register_parisc_device(struct parisc_device *dev) +{ + if (!dev) + return 0; + + if (dev->driver) + return 1; + + return 0; +} + +/** + * match_pci_device - Matches a pci device against a given hardware path + * entry. + * @dev: the generic device (known to be contained by a pci_dev). + * @index: the current BC index + * @modpath: the hardware path. + * @return: true if the device matches the hardware path. + */ +static int match_pci_device(struct device *dev, int index, + struct hardware_path *modpath) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int id; + + if (index == 5) { + /* we are at the end of the path, and on the actual device */ + unsigned int devfn = pdev->devfn; + return ((modpath->bc[5] == PCI_SLOT(devfn)) && + (modpath->mod == PCI_FUNC(devfn))); + } + + /* index might be out of bounds for bc[] */ + if (index >= 6) + return 0; + + id = PCI_SLOT(pdev->devfn) | (PCI_FUNC(pdev->devfn) << 5); + return (modpath->bc[index] == id); +} + +/** + * match_parisc_device - Matches a parisc device against a given hardware + * path entry. + * @dev: the generic device (known to be contained by a parisc_device). + * @index: the current BC index + * @modpath: the hardware path. + * @return: true if the device matches the hardware path. + */ +static int match_parisc_device(struct device *dev, int index, + struct hardware_path *modpath) +{ + struct parisc_device *curr = to_parisc_device(dev); + char id = (index == 6) ? modpath->mod : modpath->bc[index]; + + return (curr->hw_path == id); +} + +struct parse_tree_data { + int index; + struct hardware_path * modpath; + struct device * dev; +}; + +static int check_parent(struct device * dev, void * data) +{ + struct parse_tree_data * d = data; + + if (check_dev(dev)) { + if (dev->bus == &parisc_bus_type) { + if (match_parisc_device(dev, d->index, d->modpath)) + d->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) { + /* we are on a bus bridge */ + struct device *new = parse_tree_node(dev, d->index, d->modpath); + if (new) + d->dev = new; + } + } + return d->dev != NULL; +} + +/** + * parse_tree_node - returns a device entry in the iotree + * @parent: the parent node in the tree + * @index: the current BC index + * @modpath: the hardware_path struct to match a device against + * @return: The corresponding device if found, NULL otherwise. + * + * Checks all the children of @parent for a matching @id. If none + * found, it returns NULL. + */ +static struct device * +parse_tree_node(struct device *parent, int index, struct hardware_path *modpath) +{ + struct parse_tree_data d = { + .index = index, + .modpath = modpath, + }; + + struct recurse_struct recurse_data = { + .obj = &d, + .fn = check_parent, + }; + + if (device_for_each_child(parent, &recurse_data, descend_children)) + /* nothing */; + + return d.dev; +} + +/** + * hwpath_to_device - Finds the generic device corresponding to a given hardware path. + * @modpath: the hardware path. + * @return: The target device, NULL if not found. + */ +struct device *hwpath_to_device(struct hardware_path *modpath) +{ + int i; + struct device *parent = &root; + for (i = 0; i < 6; i++) { + if (modpath->bc[i] == -1) + continue; + parent = parse_tree_node(parent, i, modpath); + if (!parent) + return NULL; + } + if (dev_is_pci(parent)) /* pci devices already parse MOD */ + return parent; + else + return parse_tree_node(parent, 6, modpath); +} +EXPORT_SYMBOL(hwpath_to_device); + +/** + * device_to_hwpath - Populates the hwpath corresponding to the given device. + * @param dev the target device + * @param path pointer to a previously allocated hwpath struct to be filled in + */ +void device_to_hwpath(struct device *dev, struct hardware_path *path) +{ + struct parisc_device *padev; + if (dev->bus == &parisc_bus_type) { + padev = to_parisc_device(dev); + get_node_path(dev->parent, path); + path->mod = padev->hw_path; + } else if (dev_is_pci(dev)) { + get_node_path(dev, path); + } +} +EXPORT_SYMBOL(device_to_hwpath); + +#define BC_PORT_MASK 0x8 +#define BC_LOWER_PORT 0x8 + +#define BUS_CONVERTER(dev) \ + ((dev->id.hw_type == HPHW_IOA) || (dev->id.hw_type == HPHW_BCPORT)) + +#define IS_LOWER_PORT(dev) \ + ((gsc_readl(dev->hpa.start + offsetof(struct bc_module, io_status)) \ + & BC_PORT_MASK) == BC_LOWER_PORT) + +#define MAX_NATIVE_DEVICES 64 +#define NATIVE_DEVICE_OFFSET 0x1000 + +#define FLEX_MASK F_EXTEND(0xfffc0000) +#define IO_IO_LOW offsetof(struct bc_module, io_io_low) +#define IO_IO_HIGH offsetof(struct bc_module, io_io_high) +#define READ_IO_IO_LOW(dev) (unsigned long)(signed int)gsc_readl(dev->hpa.start + IO_IO_LOW) +#define READ_IO_IO_HIGH(dev) (unsigned long)(signed int)gsc_readl(dev->hpa.start + IO_IO_HIGH) + +static void walk_native_bus(unsigned long io_io_low, unsigned long io_io_high, + struct device *parent); + +static void __init walk_lower_bus(struct parisc_device *dev) +{ + unsigned long io_io_low, io_io_high; + + if (!BUS_CONVERTER(dev) || IS_LOWER_PORT(dev)) + return; + + if (dev->id.hw_type == HPHW_IOA) { + io_io_low = (unsigned long)(signed int)(READ_IO_IO_LOW(dev) << 16); + io_io_high = io_io_low + MAX_NATIVE_DEVICES * NATIVE_DEVICE_OFFSET; + } else { + io_io_low = (READ_IO_IO_LOW(dev) + ~FLEX_MASK) & FLEX_MASK; + io_io_high = (READ_IO_IO_HIGH(dev)+ ~FLEX_MASK) & FLEX_MASK; + } + + walk_native_bus(io_io_low, io_io_high, &dev->dev); +} + +/** + * walk_native_bus -- Probe a bus for devices + * @io_io_low: Base address of this bus. + * @io_io_high: Last address of this bus. + * @parent: The parent bus device. + * + * A native bus (eg Runway or GSC) may have up to 64 devices on it, + * spaced at intervals of 0x1000 bytes. PDC may not inform us of these + * devices, so we have to probe for them. Unfortunately, we may find + * devices which are not physically connected (such as extra serial & + * keyboard ports). This problem is not yet solved. + */ +static void __init walk_native_bus(unsigned long io_io_low, + unsigned long io_io_high, struct device *parent) +{ + int i, devices_found = 0; + unsigned long hpa = io_io_low; + struct hardware_path path; + + get_node_path(parent, &path); + do { + for(i = 0; i < MAX_NATIVE_DEVICES; i++, hpa += NATIVE_DEVICE_OFFSET) { + struct parisc_device *dev; + + /* Was the device already added by Firmware? */ + dev = find_device_by_addr(hpa); + if (!dev) { + path.mod = i; + dev = alloc_pa_dev(hpa, &path); + if (!dev) + continue; + + register_parisc_device(dev); + devices_found++; + } + walk_lower_bus(dev); + } + } while(!devices_found && hpa < io_io_high); +} + +#define CENTRAL_BUS_ADDR F_EXTEND(0xfff80000) + +/** + * walk_central_bus - Find devices attached to the central bus + * + * PDC doesn't tell us about all devices in the system. This routine + * finds devices connected to the central bus. + */ +void __init walk_central_bus(void) +{ + walk_native_bus(CENTRAL_BUS_ADDR, + CENTRAL_BUS_ADDR + (MAX_NATIVE_DEVICES * NATIVE_DEVICE_OFFSET), + &root); +} + +static __init void print_parisc_device(struct parisc_device *dev) +{ + static int count __initdata; + + pr_info("%d. %s at %pap { type:%d, hv:%#x, sv:%#x, rev:%#x }", + ++count, dev->name, &(dev->hpa.start), dev->id.hw_type, + dev->id.hversion, dev->id.sversion, dev->id.hversion_rev); + + if (dev->num_addrs) { + int k; + pr_cont(", additional addresses: "); + for (k = 0; k < dev->num_addrs; k++) + pr_cont("0x%lx ", dev->addr[k]); + } + pr_cont("\n"); +} + +/** + * init_parisc_bus - Some preparation to be done before inventory + */ +void __init init_parisc_bus(void) +{ + 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); +} + +static __init void qemu_header(void) +{ + int num; + unsigned long *p; + + pr_info("--- cut here ---\n"); + pr_info("/* AUTO-GENERATED HEADER FILE FOR SEABIOS FIRMWARE */\n"); + pr_cont("/* generated with Linux kernel */\n"); + pr_cont("/* search for PARISC_QEMU_MACHINE_HEADER in Linux */\n\n"); + + pr_info("#define PARISC_MODEL \"%s\"\n\n", + boot_cpu_data.pdc.sys_model_name); + + #define p ((unsigned long *)&boot_cpu_data.pdc.model) + pr_info("#define PARISC_PDC_MODEL 0x%lx, 0x%lx, 0x%lx, " + "0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); + #undef p + + pr_info("#define PARISC_PDC_VERSION 0x%04lx\n\n", + boot_cpu_data.pdc.versions); + + pr_info("#define PARISC_PDC_CPUID 0x%04lx\n\n", + boot_cpu_data.pdc.cpuid); + + pr_info("#define PARISC_PDC_CAPABILITIES 0x%04lx\n\n", + boot_cpu_data.pdc.capabilities); + + pr_info("#define PARISC_PDC_ENTRY_ORG 0x%04lx\n\n", +#ifdef CONFIG_64BIT + (unsigned long)(PAGE0->mem_pdc_hi) << 32 | +#endif + (unsigned long)PAGE0->mem_pdc); + + pr_info("#define PARISC_PDC_CACHE_INFO"); + p = (unsigned long *) &cache_info; + for (num = 0; num < sizeof(cache_info); num += sizeof(unsigned long)) { + if (((num % 5) == 0)) { + pr_cont(" \\\n"); + pr_info("\t"); + } + pr_cont("%s0x%04lx", + num?", ":"", *p++); + } + pr_cont("\n\n"); +} + +static __init int qemu_print_hpa(struct device *lin_dev, void *data) +{ + struct parisc_device *dev = to_parisc_device(lin_dev); + unsigned long hpa = dev->hpa.start; + + pr_cont("\t{\t.hpa = 0x%08lx,\\\n", hpa); + pr_cont("\t\t.iodc = &iodc_data_hpa_%08lx,\\\n", hpa); + pr_cont("\t\t.mod_info = &mod_info_hpa_%08lx,\\\n", hpa); + pr_cont("\t\t.mod_path = &mod_path_hpa_%08lx,\\\n", hpa); + pr_cont("\t\t.num_addr = HPA_%08lx_num_addr,\\\n", hpa); + pr_cont("\t\t.add_addr = { HPA_%08lx_add_addr } },\\\n", hpa); + return 0; +} + + +static __init void qemu_footer(void) +{ + pr_info("\n\n#define PARISC_DEVICE_LIST \\\n"); + for_each_padev(qemu_print_hpa, NULL); + pr_cont("\t{ 0, }\n"); + pr_info("--- cut here ---\n"); +} + +/* print iodc data of the various hpa modules for qemu inclusion */ +static __init int qemu_print_iodc_data(struct device *lin_dev, void *data) +{ + struct parisc_device *dev = to_parisc_device(lin_dev); + unsigned long count; + unsigned long hpa = dev->hpa.start; + int status; + struct pdc_iodc iodc_data; + + int mod_index; + struct pdc_system_map_mod_info pdc_mod_info; + struct pdc_module_path mod_path; + + status = pdc_iodc_read(&count, hpa, 0, + &iodc_data, sizeof(iodc_data)); + if (status != PDC_OK) { + pr_info("No IODC data for hpa 0x%08lx\n", hpa); + return 0; + } + + pr_info("\n"); + + pr_info("#define HPA_%08lx_DESCRIPTION \"%s\"\n", + hpa, parisc_hardware_description(&dev->id)); + + mod_index = 0; + do { + status = pdc_system_map_find_mods(&pdc_mod_info, + &mod_path, mod_index++); + } while (status == PDC_OK && pdc_mod_info.mod_addr != hpa); + + pr_info("static struct pdc_system_map_mod_info" + " mod_info_hpa_%08lx = {\n", hpa); + #define DO(member) \ + pr_cont("\t." #member " = 0x%x,\n", \ + (unsigned int)pdc_mod_info.member) + DO(mod_addr); + DO(mod_pgs); + DO(add_addrs); + pr_cont("};\n"); + #undef DO + pr_info("static struct pdc_module_path " + "mod_path_hpa_%08lx = {\n", hpa); + pr_cont("\t.path = { "); + pr_cont(".flags = 0x%x, ", mod_path.path.flags); + pr_cont(".bc = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x }, ", + (unsigned char)mod_path.path.bc[0], + (unsigned char)mod_path.path.bc[1], + (unsigned char)mod_path.path.bc[2], + (unsigned char)mod_path.path.bc[3], + (unsigned char)mod_path.path.bc[4], + (unsigned char)mod_path.path.bc[5]); + pr_cont(".mod = 0x%x ", mod_path.path.mod); + pr_cont(" },\n"); + pr_cont("\t.layers = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x }\n", + mod_path.layers[0], mod_path.layers[1], mod_path.layers[2], + mod_path.layers[3], mod_path.layers[4], mod_path.layers[5]); + pr_cont("};\n"); + + pr_info("static struct pdc_iodc iodc_data_hpa_%08lx = {\n", hpa); + #define DO(member) \ + pr_cont("\t." #member " = 0x%04lx,\n", \ + (unsigned long)iodc_data.member) + DO(hversion_model); + DO(hversion); + DO(spa); + DO(type); + DO(sversion_rev); + DO(sversion_model); + DO(sversion_opt); + DO(rev); + DO(dep); + DO(features); + DO(checksum); + DO(length); + #undef DO + pr_cont("\t/* pad: 0x%04x, 0x%04x */\n", + iodc_data.pad[0], iodc_data.pad[1]); + pr_cont("};\n"); + + pr_info("#define HPA_%08lx_num_addr %d\n", hpa, dev->num_addrs); + pr_info("#define HPA_%08lx_add_addr ", hpa); + count = 0; + if (dev->num_addrs == 0) + pr_cont("0"); + while (count < dev->num_addrs) { + pr_cont("0x%08lx, ", dev->addr[count]); + count++; + } + pr_cont("\n\n"); + + return 0; +} + + + +static __init int print_one_device(struct device * dev, void * data) +{ + struct parisc_device * pdev = to_parisc_device(dev); + + if (check_dev(dev)) + print_parisc_device(pdev); + return 0; +} + +/** + * print_parisc_devices - Print out a list of devices found in this system + */ +void __init print_parisc_devices(void) +{ + for_each_padev(print_one_device, NULL); + #define PARISC_QEMU_MACHINE_HEADER 0 + if (PARISC_QEMU_MACHINE_HEADER) { + qemu_header(); + for_each_padev(qemu_print_iodc_data, NULL); + qemu_footer(); + } +} diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S new file mode 100644 index 000000000..335887673 --- /dev/null +++ b/arch/parisc/kernel/entry.S @@ -0,0 +1,2319 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * kernel entry points (interruptions, system call wrappers) + * Copyright (C) 1999,2000 Philipp Rumpf + * Copyright (C) 1999 SuSE GmbH Nuernberg + * Copyright (C) 2000 Hewlett-Packard (John Marvin) + * Copyright (C) 1999 Hewlett-Packard (Frank Rowand) + */ + +#include <asm/asm-offsets.h> + +/* we have the following possibilities to act on an interruption: + * - handle in assembly and use shadowed registers only + * - save registers to kernel stack and handle in assembly or C */ + + +#include <asm/psw.h> +#include <asm/cache.h> /* for L1_CACHE_SHIFT */ +#include <asm/assembly.h> /* for LDREG/STREG defines */ +#include <asm/signal.h> +#include <asm/unistd.h> +#include <asm/ldcw.h> +#include <asm/traps.h> +#include <asm/thread_info.h> +#include <asm/alternative.h> + +#include <linux/linkage.h> +#include <linux/pgtable.h> + +#ifdef CONFIG_64BIT + .level 2.0w +#else + .level 2.0 +#endif + + /* Get aligned page_table_lock address for this mm from cr28/tr4 */ + .macro get_ptl reg + mfctl %cr28,\reg + .endm + + /* space_to_prot macro creates a prot id from a space id */ + +#if (SPACEID_SHIFT) == 0 + .macro space_to_prot spc prot + depd,z \spc,62,31,\prot + .endm +#else + .macro space_to_prot spc prot + extrd,u \spc,(64 - (SPACEID_SHIFT)),32,\prot + .endm +#endif + /* + * The "get_stack" macros are responsible for determining the + * kernel stack value. + * + * If sr7 == 0 + * Already using a kernel stack, so call the + * get_stack_use_r30 macro to push a pt_regs structure + * on the stack, and store registers there. + * 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. Load the stack + * pointer from the task structure. + * + * 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 + * either the fault type ("code") or the eirr. We need + * to use a non-shadowed register to carry the value over + * the rfir in virt_map. We use %r26 since this value winds + * up being passed as the argument to either do_cpu_irq_mask + * or handle_interruption. %r29 is used to hold a pointer + * the register save area, and once again, it needs to + * be a non-shadowed register so that it survives the rfir. + */ + + .macro get_stack_use_cr30 + + /* we save the registers in the task struct */ + + copy %r30, %r17 + mfctl %cr30, %r1 + tophys %r1,%r9 /* task_struct */ + LDREG TASK_STACK(%r9),%r30 + ldo PT_SZ_ALGN(%r30),%r30 + mtsp %r0,%sr7 /* clear sr7 after kernel stack was set! */ + mtsp %r16,%sr3 + ldo TASK_REGS(%r9),%r9 + STREG %r17,PT_GR30(%r9) + STREG %r29,PT_GR29(%r9) + STREG %r26,PT_GR26(%r9) + STREG %r16,PT_SR7(%r9) + copy %r9,%r29 + .endm + + .macro get_stack_use_r30 + + /* we put a struct pt_regs on the stack and save the registers there */ + + tophys %r30,%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 + + .macro rest_stack + LDREG PT_GR1(%r29), %r1 + LDREG PT_GR30(%r29),%r30 + LDREG PT_GR29(%r29),%r29 + .endm + + /* default interruption handler + * (calls traps.c:handle_interruption) */ + .macro def code + b intr_save + ldi \code, %r8 + .align 32 + .endm + + /* Interrupt interruption handler + * (calls irq.c:do_cpu_irq_mask) */ + .macro extint code + b intr_extint + mfsp %sr7,%r16 + .align 32 + .endm + + .import os_hpmc, code + + /* HPMC handler */ + .macro hpmc code + nop /* must be a NOP, will be patched later */ + load32 PA(os_hpmc), %r3 + bv,n 0(%r3) + nop + .word 0 /* checksum (will be patched) */ + .word 0 /* address of handler */ + .word 0 /* length of handler */ + .endm + + /* + * Performance Note: Instructions will be moved up into + * this part of the code later on, once we are sure + * that the tlb miss handlers are close to final form. + */ + + /* Register definitions for tlb miss handler macros */ + + va = r8 /* virtual address for which the trap occurred */ + spc = r24 /* space for which the trap occurred */ + +#ifndef CONFIG_64BIT + + /* + * itlb miss interruption handler (parisc 1.1 - 32 bit) + */ + + .macro itlb_11 code + + mfctl %pcsq, spc + b itlb_miss_11 + mfctl %pcoq, va + + .align 32 + .endm +#endif + + /* + * itlb miss interruption handler (parisc 2.0) + */ + + .macro itlb_20 code + mfctl %pcsq, spc +#ifdef CONFIG_64BIT + b itlb_miss_20w +#else + b itlb_miss_20 +#endif + mfctl %pcoq, va + + .align 32 + .endm + +#ifndef CONFIG_64BIT + /* + * naitlb miss interruption handler (parisc 1.1 - 32 bit) + */ + + .macro naitlb_11 code + + mfctl %isr,spc + b naitlb_miss_11 + mfctl %ior,va + + .align 32 + .endm +#endif + + /* + * naitlb miss interruption handler (parisc 2.0) + */ + + .macro naitlb_20 code + + mfctl %isr,spc +#ifdef CONFIG_64BIT + b naitlb_miss_20w +#else + b naitlb_miss_20 +#endif + mfctl %ior,va + + .align 32 + .endm + +#ifndef CONFIG_64BIT + /* + * dtlb miss interruption handler (parisc 1.1 - 32 bit) + */ + + .macro dtlb_11 code + + mfctl %isr, spc + b dtlb_miss_11 + mfctl %ior, va + + .align 32 + .endm +#endif + + /* + * dtlb miss interruption handler (parisc 2.0) + */ + + .macro dtlb_20 code + + mfctl %isr, spc +#ifdef CONFIG_64BIT + b dtlb_miss_20w +#else + b dtlb_miss_20 +#endif + mfctl %ior, va + + .align 32 + .endm + +#ifndef CONFIG_64BIT + /* nadtlb miss interruption handler (parisc 1.1 - 32 bit) */ + + .macro nadtlb_11 code + + mfctl %isr,spc + b nadtlb_miss_11 + mfctl %ior,va + + .align 32 + .endm +#endif + + /* nadtlb miss interruption handler (parisc 2.0) */ + + .macro nadtlb_20 code + + mfctl %isr,spc +#ifdef CONFIG_64BIT + b nadtlb_miss_20w +#else + b nadtlb_miss_20 +#endif + mfctl %ior,va + + .align 32 + .endm + +#ifndef CONFIG_64BIT + /* + * dirty bit trap interruption handler (parisc 1.1 - 32 bit) + */ + + .macro dbit_11 code + + mfctl %isr,spc + b dbit_trap_11 + mfctl %ior,va + + .align 32 + .endm +#endif + + /* + * dirty bit trap interruption handler (parisc 2.0) + */ + + .macro dbit_20 code + + mfctl %isr,spc +#ifdef CONFIG_64BIT + b dbit_trap_20w +#else + b dbit_trap_20 +#endif + mfctl %ior,va + + .align 32 + .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 */ + .macro space_adjust spc,va,tmp +#ifdef CONFIG_64BIT + extrd,u \spc,63,SPACEID_SHIFT,\tmp + depd %r0,63,SPACEID_SHIFT,\spc + depd \tmp,31,SPACEID_SHIFT,\va +#endif + .endm + + .import swapper_pg_dir,code + + /* Get the pgd. For faults on space zero (kernel space), this + * is simply swapper_pg_dir. For user space faults, the + * pgd is stored in %cr25 */ + .macro get_pgd spc,reg + ldil L%PA(swapper_pg_dir),\reg + ldo R%PA(swapper_pg_dir)(\reg),\reg + or,COND(=) %r0,\spc,%r0 + mfctl %cr25,\reg + .endm + + /* + space_check(spc,tmp,fault) + + spc - The space we saw the fault with. + tmp - The place to store the current space. + fault - Function to call on failure. + + Only allow faults on different spaces from the + currently active one if we're the kernel + + */ + .macro space_check spc,tmp,fault + mfsp %sr7,\tmp + /* check against %r0 which is same value as LINUX_GATEWAY_SPACE */ + or,COND(<>) %r0,\spc,%r0 /* user may execute gateway page + * as kernel, so defeat the space + * check if it is */ + copy \spc,\tmp + or,COND(=) %r0,\tmp,%r0 /* nullify if executing as kernel */ + cmpb,COND(<>),n \tmp,\spc,\fault + .endm + + /* Look up a PTE in a 2-Level scheme (faulting at each + * level if the entry isn't present + * + * NOTE: we use ldw even for LP64, since the short pointers + * can address up to 1TB + */ + .macro L2_ptep pmd,pte,index,va,fault +#if CONFIG_PGTABLE_LEVELS == 3 + extru_safe \va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index +#else + extru_safe \va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index +#endif + dep %r0,31,PAGE_SHIFT,\pmd /* clear offset */ +#if CONFIG_PGTABLE_LEVELS < 3 + copy %r0,\pte +#endif + ldw,s \index(\pmd),\pmd + bb,>=,n \pmd,_PxD_PRESENT_BIT,\fault + dep %r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */ + SHLREG \pmd,PxD_VALUE_SHIFT,\pmd + extru_safe \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 /* pmd is now pte */ + .endm + + /* Look up PTE in a 3-Level scheme. */ + .macro L3_ptep pgd,pte,index,va,fault +#if CONFIG_PGTABLE_LEVELS == 3 + copy %r0,\pte + extrd,u \va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index + ldw,s \index(\pgd),\pgd + bb,>=,n \pgd,_PxD_PRESENT_BIT,\fault + shld \pgd,PxD_VALUE_SHIFT,\pgd +#endif + L2_ptep \pgd,\pte,\index,\va,\fault + .endm + + /* Acquire page_table_lock and check page is present. */ + .macro ptl_lock spc,ptp,pte,tmp,tmp1,fault +#ifdef CONFIG_TLB_PTLOCK +98: cmpib,COND(=),n 0,\spc,2f + get_ptl \tmp +1: LDCW 0(\tmp),\tmp1 + cmpib,COND(=) 0,\tmp1,1b + nop + LDREG 0(\ptp),\pte + bb,<,n \pte,_PAGE_PRESENT_BIT,3f + b \fault + stw \spc,0(\tmp) +99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP) +#endif +2: LDREG 0(\ptp),\pte + bb,>=,n \pte,_PAGE_PRESENT_BIT,\fault +3: + .endm + + /* Release page_table_lock without reloading lock address. + Note that the values in the register spc are limited to + NR_SPACE_IDS (262144). Thus, the stw instruction always + stores a nonzero value even when register spc is 64 bits. + We use an ordered store to ensure all prior accesses are + performed prior to releasing the lock. */ + .macro ptl_unlock0 spc,tmp +#ifdef CONFIG_TLB_PTLOCK +98: or,COND(=) %r0,\spc,%r0 + stw,ma \spc,0(\tmp) +99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP) +#endif + .endm + + /* Release page_table_lock. */ + .macro ptl_unlock1 spc,tmp +#ifdef CONFIG_TLB_PTLOCK +98: get_ptl \tmp + ptl_unlock0 \spc,\tmp +99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP) +#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_accessed ptp,pte,tmp,tmp1 + ldi _PAGE_ACCESSED,\tmp1 + or \tmp1,\pte,\tmp + and,COND(<>) \tmp1,\pte,%r0 + STREG \tmp,0(\ptp) + .endm + + /* Set the dirty bit (and accessed bit). No need to be + * clever, this is only used from the dirty fault */ + .macro update_dirty ptp,pte,tmp + ldi _PAGE_ACCESSED|_PAGE_DIRTY,\tmp + or \tmp,\pte,\pte + STREG \pte,0(\ptp) + .endm + + /* We have (depending on the page size): + * - 38 to 52-bit Physical Page Number + * - 12 to 26-bit page offset + */ + /* 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) + #define PAGE_ADD_HUGE_SHIFT (REAL_HPAGE_SHIFT-12) + #define PFN_START_BIT (63-ASM_PFN_PTE_SHIFT+(63-58)-PAGE_ADD_SHIFT) + + /* Drop prot bits and convert to page addr for iitlbt and idtlbt */ + .macro convert_for_tlb_insert20 pte,tmp +#ifdef CONFIG_HUGETLB_PAGE + copy \pte,\tmp + extrd,u \tmp,PFN_START_BIT,PFN_START_BIT+1,\pte + + depdi _PAGE_SIZE_ENCODING_DEFAULT,63,\ + (63-58)+PAGE_ADD_SHIFT,\pte + extrd,u,*= \tmp,_PAGE_HPAGE_BIT+32,1,%r0 + depdi _HUGE_PAGE_SIZE_ENCODING_DEFAULT,63,\ + (63-58)+PAGE_ADD_HUGE_SHIFT,\pte +#else /* Huge pages disabled */ + extrd,u \pte,PFN_START_BIT,PFN_START_BIT+1,\pte + depdi _PAGE_SIZE_ENCODING_DEFAULT,63,\ + (63-58)+PAGE_ADD_SHIFT,\pte +#endif + .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,tmp + space_to_prot \spc \prot /* create prot id from space */ + /* The following is the real subtlety. This is depositing + * T <-> _PAGE_REFTRAP + * D <-> _PAGE_DIRTY + * B <-> _PAGE_DMB (memory break) + * + * Then incredible subtlety: The access rights are + * _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 + * trigger an access rights trap in user space if the user + * tries to read an unreadable page */ +#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT + /* need to drop DMB bit, as it's used as SPECIAL flag */ + depi 0,_PAGE_SPECIAL_BIT,1,\pte +#endif + depd \pte,8,7,\prot + + /* 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) */ + 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 + * to kernel privilege (so we can execute the page as kernel). + * Any privilege promotion page always denys read and write */ + extrd,u,*= \pte,_PAGE_GATEWAY_BIT+32,1,%r0 + depd %r0,11,2,\prot /* If Gateway, Set PL2 to 0 */ + + /* 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 + + /* Drop prot bits and convert to page addr for iitlbt and idtlbt */ + convert_for_tlb_insert20 \pte \tmp + .endm + + /* Identical macro to make_insert_tlb above, except it + * makes the tlb entry for the differently formatted pa11 + * insertion instructions */ + .macro make_insert_tlb_11 spc,pte,prot +#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT + /* need to drop DMB bit, as it's used as SPECIAL flag */ + depi 0,_PAGE_SPECIAL_BIT,1,\pte +#endif + zdep \spc,30,15,\prot + dep \pte,8,7,\prot + extru,= \pte,_PAGE_NO_CACHE_BIT,1,%r0 + depi 1,12,1,\prot + extru,= \pte,_PAGE_USER_BIT,1,%r0 + depi 7,11,3,\prot /* Set for user space (1 rsvd for read) */ + extru,= \pte,_PAGE_GATEWAY_BIT,1,%r0 + depi 0,11,2,\prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for iitlba */ + + 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 + * to extend into I/O space if the address is 0xfXXXXXXX + * so we extend the f's into the top word of the pte in + * this case */ + .macro f_extend pte,tmp + extrd,s \pte,42,4,\tmp + addi,<> 1,\tmp,%r0 + extrd,s \pte,63,25,\pte + .endm + + /* The alias region is comprised of a pair of 4 MB regions + * aligned to 8 MB. It is used to clear/copy/flush user pages + * using kernel virtual addresses congruent with the user + * virtual address. + * + * To use the alias page, you set %r26 up with the to TLB + * 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,patype + cmpib,COND(<>),n 0,\spc,\fault + ldil L%(TMPALIAS_MAP_START),\tmp + copy \va,\tmp1 + depi_safe 0,31,TMPALIAS_SIZE_BITS+1,\tmp1 + cmpb,COND(<>),n \tmp,\tmp1,\fault + 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. + */ + extrw,u,= \va,31-TMPALIAS_SIZE_BITS,1,%r0 + or,COND(tr) %r23,%r0,\pte + or %r26,%r0,\pte + + /* convert phys addr in \pte (from r23 or r26) to tlb insert format */ + SHRREG \pte,PAGE_SHIFT+PAGE_ADD_SHIFT-5, \pte + depi_safe _PAGE_SIZE_ENCODING_DEFAULT, 31,5, \pte + .endm + + + /* + * Fault_vectors are architecturally required to be aligned on a 2K + * boundary + */ + + .section .text.hot + .align 2048 + +ENTRY(fault_vector_20) + /* First vector is invalid (0) */ + .ascii "cows can fly" + .byte 0 + .align 32 + + hpmc 1 + def 2 + def 3 + extint 4 + def 5 + itlb_20 PARISC_ITLB_TRAP + def 7 + def 8 + def 9 + def 10 + def 11 + def 12 + def 13 + def 14 + dtlb_20 15 + naitlb_20 16 + nadtlb_20 17 + def 18 + def 19 + dbit_20 20 + def 21 + def 22 + def 23 + def 24 + def 25 + def 26 + def 27 + def 28 + def 29 + def 30 + def 31 +END(fault_vector_20) + +#ifndef CONFIG_64BIT + + .align 2048 + +ENTRY(fault_vector_11) + /* First vector is invalid (0) */ + .ascii "cows can fly" + .byte 0 + .align 32 + + hpmc 1 + def 2 + def 3 + extint 4 + def 5 + itlb_11 PARISC_ITLB_TRAP + def 7 + def 8 + def 9 + def 10 + def 11 + def 12 + def 13 + def 14 + dtlb_11 15 + naitlb_11 16 + nadtlb_11 17 + def 18 + def 19 + dbit_11 20 + def 21 + def 22 + def 23 + def 24 + def 25 + def 26 + def 27 + def 28 + 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 + + .import handle_interruption,code + .import do_cpu_irq_mask,code + + /* + * Child Returns here + * + * copy_thread moved args into task save area. + */ + +ENTRY(ret_from_kernel_thread) + /* Call schedule_tail first though */ + BL schedule_tail, %r2 + nop + + mfctl %cr30,%r1 /* task_struct */ + LDREG TASK_PT_GR25(%r1), %r26 +#ifdef CONFIG_64BIT + LDREG TASK_PT_GR27(%r1), %r27 +#endif + LDREG TASK_PT_GR26(%r1), %r1 + ble 0(%sr7, %r1) + copy %r31, %r2 + b finish_child_return + nop +END(ret_from_kernel_thread) + + + /* + * struct task_struct *_switch_to(struct task_struct *prev, + * struct task_struct *next) + * + * switch kernel stacks and return prev */ +ENTRY_CFI(_switch_to) + STREG %r2, -RP_OFFSET(%r30) + + callee_save_float + callee_save + + load32 _switch_to_ret, %r2 + + STREG %r2, TASK_PT_KPC(%r26) + LDREG TASK_PT_KPC(%r25), %r2 + + STREG %r30, TASK_PT_KSP(%r26) + LDREG TASK_PT_KSP(%r25), %r30 + bv %r0(%r2) + mtctl %r25,%cr30 + +ENTRY(_switch_to_ret) + mtctl %r0, %cr0 /* Needed for single stepping */ + callee_rest + callee_rest_float + + LDREG -RP_OFFSET(%r30), %r2 + bv %r0(%r2) + copy %r26, %r28 +ENDPROC_CFI(_switch_to) + + /* + * Common rfi return path for interruptions, kernel execve, and + * sys_rt_sigreturn (sometimes). The sys_rt_sigreturn syscall will + * return via this path if the signal was received when the process + * was running; if the process was blocked on a syscall then the + * normal syscall_exit path is used. All syscalls for traced + * proceses exit via intr_restore. + * + * XXX If any syscalls that change a processes space id ever exit + * this way, then we will need to copy %sr3 in to PT_SR[3..7], and + * adjust IASQ[0..1]. + * + */ + + .align PAGE_SIZE + +ENTRY_CFI(syscall_exit_rfi) + mfctl %cr30,%r16 /* task_struct */ + ldo TASK_REGS(%r16),%r16 + /* Force iaoq to userspace, as the user has had access to our current + * context via sigcontext. Also Filter the PSW for the same reason. + */ + LDREG PT_IAOQ0(%r16),%r19 + depi PRIV_USER,31,2,%r19 + STREG %r19,PT_IAOQ0(%r16) + LDREG PT_IAOQ1(%r16),%r19 + depi PRIV_USER,31,2,%r19 + STREG %r19,PT_IAOQ1(%r16) + LDREG PT_PSW(%r16),%r19 + load32 USER_PSW_MASK,%r1 +#ifdef CONFIG_64BIT + load32 USER_PSW_HI_MASK,%r20 + depd %r20,31,32,%r1 +#endif + and %r19,%r1,%r19 /* Mask out bits that user shouldn't play with */ + load32 USER_PSW,%r1 + or %r19,%r1,%r19 /* Make sure default USER_PSW bits are set */ + STREG %r19,PT_PSW(%r16) + + /* + * If we aren't being traced, we never saved space registers + * (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 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) + mfsp %sr3,%r19 + STREG %r19,PT_SR0(%r16) + STREG %r19,PT_SR1(%r16) + STREG %r19,PT_SR3(%r16) + STREG %r19,PT_SR4(%r16) + STREG %r19,PT_SR5(%r16) + STREG %r19,PT_SR6(%r16) + STREG %r19,PT_SR7(%r16) + +ENTRY(intr_return) + /* check for reschedule */ + mfctl %cr30,%r1 + LDREG TASK_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 TASK_TI_FLAGS(%r1),%r19 + ldi (_TIF_USER_WORK_MASK & ~_TIF_NEED_RESCHED), %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 LINUX_GATEWAY_SPACE, %r20, intr_restore /* forward */ + LDREG PT_IASQ1(%r16), %r20 + cmpib,COND(=),n LINUX_GATEWAY_SPACE, %r20, intr_restore /* forward */ + + copy %r0, %r25 /* long in_syscall = 0 */ +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ +#endif + + /* 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 + + BL do_notify_resume,%r2 + copy %r16, %r26 /* struct pt_regs *regs */ + + b,n intr_check_sig + +intr_restore: + copy %r16,%r29 + ldo PT_FR31(%r29),%r1 + rest_fp %r1 + rest_general %r29 + + /* inverse of virt_map */ + pcxt_ssm_bug + rsm PSW_SM_QUIET,%r0 /* prepare for rfi */ + tophys_r1 %r29 + + /* Restore space id's and special cr's from PT_REGS + * structure pointed to by r29 + */ + rest_specials %r29 + + /* IMPORTANT: rest_stack restores r29 last (we are using it)! + * It also restores r1 and r30. + */ + rest_stack + + rfi + nop + +#ifndef CONFIG_PREEMPTION +# define intr_do_preempt intr_restore +#endif /* !CONFIG_PREEMPTION */ + + .import schedule,code +intr_do_resched: + /* Only call schedule on return to userspace. If we're returning + * to kernel space, we may schedule if CONFIG_PREEMPTION, otherwise + * we jump back to intr_restore. + */ + LDREG PT_IASQ0(%r16), %r20 + cmpib,COND(=) 0, %r20, intr_do_preempt + nop + LDREG PT_IASQ1(%r16), %r20 + 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 + + ldil L%intr_check_sig, %r2 +#ifndef CONFIG_64BIT + b schedule +#else + load32 schedule, %r20 + bv %r0(%r20) +#endif + ldo R%intr_check_sig(%r2), %r2 + + /* 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_PREEMPTION + .import preempt_schedule_irq,code +intr_do_preempt: + rsm PSW_SM_I, %r0 /* disable interrupts */ + + /* current_thread_info()->preempt_count */ + mfctl %cr30, %r1 + ldw TI_PRE_COUNT(%r1), %r19 + cmpib,<> 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 + + /* ssm PSW_SM_I done later in intr_restore */ +#ifdef CONFIG_MLONGCALLS + ldil L%intr_restore, %r2 + load32 preempt_schedule_irq, %r1 + bv %r0(%r1) + ldo R%intr_restore(%r2), %r2 +#else + ldil L%intr_restore, %r1 + BL preempt_schedule_irq, %r2 + ldo R%intr_restore(%r1), %r2 +#endif +#endif /* CONFIG_PREEMPTION */ + + /* + * External interrupts. + */ + +intr_extint: + cmpib,COND(=),n 0,%r16,1f + + get_stack_use_cr30 + b,n 2f + +1: + get_stack_use_r30 +2: + save_specials %r29 + virt_map + save_general %r29 + + ldo PT_FR0(%r29), %r24 + save_fp %r24 + + loadgp + + copy %r29, %r26 /* arg0 is pt_regs */ + copy %r29, %r16 /* save pt_regs */ + + ldil L%intr_return, %r2 + +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ +#endif + + b do_cpu_irq_mask + ldo R%intr_return(%r2), %r2 /* return to intr_return, not here */ +ENDPROC_CFI(syscall_exit_rfi) + + + /* Generic interruptions (illegal insn, unaligned, page fault, etc) */ + +ENTRY_CFI(intr_save) /* for os_hpmc */ + mfsp %sr7,%r16 + cmpib,COND(=),n 0,%r16,1f + get_stack_use_cr30 + b 2f + copy %r8,%r26 + +1: + get_stack_use_r30 + copy %r8,%r26 + +2: + save_specials %r29 + + /* If this trap is a itlb miss, skip saving/adjusting isr/ior */ + cmpib,COND(=),n PARISC_ITLB_TRAP,%r26,skip_save_ior + + + mfctl %isr, %r16 + nop /* serialize mfctl on PA 2.0 to avoid 4 cycle penalty */ + mfctl %ior, %r17 + + +#ifdef CONFIG_64BIT + /* + * If the interrupted code was running with W bit off (32 bit), + * clear the b bits (bits 0 & 1) in the ior. + * save_specials left ipsw value in r8 for us to test. + */ + extrd,u,*<> %r8,PSW_W_BIT,1,%r0 + depdi 0,1,2,%r17 + + /* adjust isr/ior: get high bits from isr and deposit in ior */ + space_adjust %r16,%r17,%r1 +#endif + STREG %r16, PT_ISR(%r29) + STREG %r17, PT_IOR(%r29) + +#if 0 && defined(CONFIG_64BIT) + /* Revisit when we have 64-bit code above 4Gb */ + b,n intr_save2 + +skip_save_ior: + /* We have a itlb miss, and when executing code above 4 Gb on ILP64, we + * need to adjust iasq/iaoq here in the same way we adjusted isr/ior + * above. + */ + extrd,u,* %r8,PSW_W_BIT,1,%r1 + cmpib,COND(=),n 1,%r1,intr_save2 + LDREG PT_IASQ0(%r29), %r16 + LDREG PT_IAOQ0(%r29), %r17 + /* adjust iasq/iaoq */ + space_adjust %r16,%r17,%r1 + STREG %r16, PT_IASQ0(%r29) + STREG %r17, PT_IAOQ0(%r29) +#else +skip_save_ior: +#endif + +intr_save2: + virt_map + save_general %r29 + + ldo PT_FR0(%r29), %r25 + save_fp %r25 + + loadgp + + copy %r29, %r25 /* arg1 is pt_regs */ +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ +#endif + + ldil L%intr_check_sig, %r2 + copy %r25, %r16 /* save pt_regs */ + + b handle_interruption + ldo R%intr_check_sig(%r2), %r2 +ENDPROC_CFI(intr_save) + + + /* + * Note for all tlb miss handlers: + * + * cr24 contains a pointer to the kernel address space + * page directory. + * + * cr25 contains a pointer to the current user address + * space page directory. + * + * sr3 will contain the space id of the user address space + * of the current running thread while that thread is + * running in the kernel. + */ + + /* + * register number allocations. Note that these are all + * in the shadowed registers + */ + + t0 = r1 /* temporary register 0 */ + 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 occurred */ + ptp = r25 /* page directory/page table pointer */ + +#ifdef CONFIG_64BIT + +dtlb_miss_20w: + space_adjust spc,va,t0 + get_pgd spc,ptp + space_check spc,t0,dtlb_fault + + L3_ptep ptp,pte,t0,va,dtlb_check_alias_20w + + ptl_lock spc,ptp,pte,t0,t1,dtlb_check_alias_20w + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + idtlbt pte,prot + + ptl_unlock1 spc,t0 + rfir + nop + +dtlb_check_alias_20w: + do_alias spc,t0,t1,va,pte,prot,dtlb_fault,20 + + idtlbt pte,prot + + rfir + nop + +nadtlb_miss_20w: + space_adjust spc,va,t0 + get_pgd spc,ptp + space_check spc,t0,nadtlb_fault + + L3_ptep ptp,pte,t0,va,nadtlb_check_alias_20w + + ptl_lock spc,ptp,pte,t0,t1,nadtlb_check_alias_20w + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + idtlbt pte,prot + + ptl_unlock1 spc,t0 + rfir + nop + +nadtlb_check_alias_20w: + do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,20 + + idtlbt pte,prot + + rfir + nop + +#else + +dtlb_miss_11: + get_pgd spc,ptp + + space_check spc,t0,dtlb_fault + + L2_ptep ptp,pte,t0,va,dtlb_check_alias_11 + + ptl_lock spc,ptp,pte,t0,t1,dtlb_check_alias_11 + update_accessed ptp,pte,t0,t1 + + make_insert_tlb_11 spc,pte,prot + + mfsp %sr1,t1 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + idtlba pte,(%sr1,va) + idtlbp prot,(%sr1,va) + + mtsp t1, %sr1 /* Restore sr1 */ + + ptl_unlock1 spc,t0 + rfir + nop + +dtlb_check_alias_11: + do_alias spc,t0,t1,va,pte,prot,dtlb_fault,11 + + idtlba pte,(va) + idtlbp prot,(va) + + rfir + nop + +nadtlb_miss_11: + get_pgd spc,ptp + + space_check spc,t0,nadtlb_fault + + L2_ptep ptp,pte,t0,va,nadtlb_check_alias_11 + + ptl_lock spc,ptp,pte,t0,t1,nadtlb_check_alias_11 + update_accessed ptp,pte,t0,t1 + + make_insert_tlb_11 spc,pte,prot + + mfsp %sr1,t1 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + idtlba pte,(%sr1,va) + idtlbp prot,(%sr1,va) + + mtsp t1, %sr1 /* Restore sr1 */ + + ptl_unlock1 spc,t0 + rfir + nop + +nadtlb_check_alias_11: + do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,11 + + idtlba pte,(va) + idtlbp prot,(va) + + rfir + nop + +dtlb_miss_20: + space_adjust spc,va,t0 + get_pgd spc,ptp + space_check spc,t0,dtlb_fault + + L2_ptep ptp,pte,t0,va,dtlb_check_alias_20 + + ptl_lock spc,ptp,pte,t0,t1,dtlb_check_alias_20 + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + f_extend pte,t1 + + idtlbt pte,prot + + ptl_unlock1 spc,t0 + rfir + nop + +dtlb_check_alias_20: + do_alias spc,t0,t1,va,pte,prot,dtlb_fault,20 + + idtlbt pte,prot + + rfir + nop + +nadtlb_miss_20: + get_pgd spc,ptp + + space_check spc,t0,nadtlb_fault + + L2_ptep ptp,pte,t0,va,nadtlb_check_alias_20 + + ptl_lock spc,ptp,pte,t0,t1,nadtlb_check_alias_20 + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + f_extend pte,t1 + + idtlbt pte,prot + + ptl_unlock1 spc,t0 + rfir + nop + +nadtlb_check_alias_20: + do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,20 + + idtlbt pte,prot + + rfir + nop + +#endif + +nadtlb_emulate: + + /* + * Non-access misses can be caused by fdc,fic,pdc,lpa,probe and + * probei instructions. The kernel no longer faults doing flushes. + * Use of lpa and probe instructions is rare. Given the issue + * with shadow registers, we defer everything to the "slow" path. + */ + b,n nadtlb_fault + +#ifdef CONFIG_64BIT +itlb_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,itlb_fault + + L3_ptep ptp,pte,t0,va,itlb_fault + + ptl_lock spc,ptp,pte,t0,t1,itlb_fault + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + iitlbt pte,prot + + ptl_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 + + ptl_lock spc,ptp,pte,t0,t1,naitlb_check_alias_20w + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + iitlbt pte,prot + + ptl_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 + +#else + +itlb_miss_11: + get_pgd spc,ptp + + space_check spc,t0,itlb_fault + + L2_ptep ptp,pte,t0,va,itlb_fault + + ptl_lock spc,ptp,pte,t0,t1,itlb_fault + update_accessed ptp,pte,t0,t1 + + make_insert_tlb_11 spc,pte,prot + + mfsp %sr1,t1 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + iitlba pte,(%sr1,va) + iitlbp prot,(%sr1,va) + + mtsp t1, %sr1 /* Restore sr1 */ + + ptl_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 + + ptl_lock spc,ptp,pte,t0,t1,naitlb_check_alias_11 + update_accessed ptp,pte,t0,t1 + + make_insert_tlb_11 spc,pte,prot + + mfsp %sr1,t1 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + iitlba pte,(%sr1,va) + iitlbp prot,(%sr1,va) + + mtsp t1, %sr1 /* Restore sr1 */ + + ptl_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 + + space_check spc,t0,itlb_fault + + L2_ptep ptp,pte,t0,va,itlb_fault + + ptl_lock spc,ptp,pte,t0,t1,itlb_fault + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + f_extend pte,t1 + + iitlbt pte,prot + + ptl_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 + + ptl_lock spc,ptp,pte,t0,t1,naitlb_check_alias_20 + update_accessed ptp,pte,t0,t1 + + make_insert_tlb spc,pte,prot,t1 + + f_extend pte,t1 + + iitlbt pte,prot + + ptl_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 + +#endif + +#ifdef CONFIG_64BIT + +dbit_trap_20w: + space_adjust spc,va,t0 + get_pgd spc,ptp + space_check spc,t0,dbit_fault + + L3_ptep ptp,pte,t0,va,dbit_fault + + ptl_lock spc,ptp,pte,t0,t1,dbit_fault + update_dirty ptp,pte,t1 + + make_insert_tlb spc,pte,prot,t1 + + idtlbt pte,prot + + ptl_unlock0 spc,t0 + rfir + nop +#else + +dbit_trap_11: + + get_pgd spc,ptp + + space_check spc,t0,dbit_fault + + L2_ptep ptp,pte,t0,va,dbit_fault + + ptl_lock spc,ptp,pte,t0,t1,dbit_fault + update_dirty ptp,pte,t1 + + make_insert_tlb_11 spc,pte,prot + + mfsp %sr1,t1 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + idtlba pte,(%sr1,va) + idtlbp prot,(%sr1,va) + + mtsp t1, %sr1 /* Restore sr1 */ + + ptl_unlock0 spc,t0 + rfir + nop + +dbit_trap_20: + get_pgd spc,ptp + + space_check spc,t0,dbit_fault + + L2_ptep ptp,pte,t0,va,dbit_fault + + ptl_lock spc,ptp,pte,t0,t1,dbit_fault + update_dirty ptp,pte,t1 + + make_insert_tlb spc,pte,prot,t1 + + f_extend pte,t1 + + idtlbt pte,prot + + ptl_unlock0 spc,t0 + rfir + nop +#endif + + .import handle_interruption,code + +kernel_bad_space: + b intr_save + ldi 31,%r8 /* Use an unused code */ + +dbit_fault: + b intr_save + ldi 20,%r8 + +itlb_fault: + b intr_save + ldi PARISC_ITLB_TRAP,%r8 + +nadtlb_fault: + b intr_save + ldi 17,%r8 + +naitlb_fault: + b intr_save + ldi 16,%r8 + +dtlb_fault: + b intr_save + ldi 15,%r8 + + /* Register saving semantics for system calls: + + %r1 clobbered by system call macro in userspace + %r2 saved in PT_REGS by gateway page + %r3 - %r18 preserved by C code (saved by signal code) + %r19 - %r20 saved in PT_REGS by gateway page + %r21 - %r22 non-standard syscall args + stored in kernel stack by gateway page + %r23 - %r26 arg3-arg0, saved in PT_REGS by gateway page + %r27 - %r30 saved in PT_REGS by gateway page + %r31 syscall return pointer + */ + + /* Floating point registers (FIXME: what do we do with these?) + + %fr0 - %fr3 status/exception, not preserved + %fr4 - %fr7 arguments + %fr8 - %fr11 not preserved by C code + %fr12 - %fr21 preserved by C code + %fr22 - %fr31 not preserved by C code + */ + + .macro reg_save regs + STREG %r3, PT_GR3(\regs) + STREG %r4, PT_GR4(\regs) + STREG %r5, PT_GR5(\regs) + STREG %r6, PT_GR6(\regs) + STREG %r7, PT_GR7(\regs) + STREG %r8, PT_GR8(\regs) + STREG %r9, PT_GR9(\regs) + STREG %r10,PT_GR10(\regs) + STREG %r11,PT_GR11(\regs) + STREG %r12,PT_GR12(\regs) + STREG %r13,PT_GR13(\regs) + STREG %r14,PT_GR14(\regs) + STREG %r15,PT_GR15(\regs) + STREG %r16,PT_GR16(\regs) + STREG %r17,PT_GR17(\regs) + STREG %r18,PT_GR18(\regs) + .endm + + .macro reg_restore regs + LDREG PT_GR3(\regs), %r3 + LDREG PT_GR4(\regs), %r4 + LDREG PT_GR5(\regs), %r5 + LDREG PT_GR6(\regs), %r6 + LDREG PT_GR7(\regs), %r7 + LDREG PT_GR8(\regs), %r8 + LDREG PT_GR9(\regs), %r9 + LDREG PT_GR10(\regs),%r10 + LDREG PT_GR11(\regs),%r11 + LDREG PT_GR12(\regs),%r12 + LDREG PT_GR13(\regs),%r13 + LDREG PT_GR14(\regs),%r14 + LDREG PT_GR15(\regs),%r15 + LDREG PT_GR16(\regs),%r16 + LDREG PT_GR17(\regs),%r17 + LDREG PT_GR18(\regs),%r18 + .endm + + .macro fork_like name +ENTRY_CFI(sys_\name\()_wrapper) + mfctl %cr30,%r1 + ldo TASK_REGS(%r1),%r1 + reg_save %r1 + mfctl %cr27, %r28 + ldil L%sys_\name, %r31 + be R%sys_\name(%sr4,%r31) + STREG %r28, PT_CR27(%r1) +ENDPROC_CFI(sys_\name\()_wrapper) + .endm + +fork_like clone +fork_like clone3 +fork_like fork +fork_like vfork + + /* Set the return value for the child */ +ENTRY(child_return) + BL schedule_tail, %r2 + nop +finish_child_return: + mfctl %cr30,%r1 + ldo TASK_REGS(%r1),%r1 /* get pt regs */ + + LDREG PT_CR27(%r1), %r3 + mtctl %r3, %cr27 + reg_restore %r1 + b syscall_exit + copy %r0,%r28 +END(child_return) + +ENTRY_CFI(sys_rt_sigreturn_wrapper) + mfctl %cr30,%r26 + ldo TASK_REGS(%r26),%r26 /* get pt regs */ + /* Don't save regs, we are going to restore them from sigcontext. */ + STREG %r2, -RP_OFFSET(%r30) +#ifdef CONFIG_64BIT + ldo FRAME_SIZE(%r30), %r30 + BL sys_rt_sigreturn,%r2 + ldo -16(%r30),%r29 /* Reference param save area */ +#else + BL sys_rt_sigreturn,%r2 + ldo FRAME_SIZE(%r30), %r30 +#endif + + ldo -FRAME_SIZE(%r30), %r30 + LDREG -RP_OFFSET(%r30), %r2 + + /* FIXME: I think we need to restore a few more things here. */ + mfctl %cr30,%r1 + ldo TASK_REGS(%r1),%r1 /* get pt regs */ + reg_restore %r1 + + /* If the signal was received while the process was blocked on a + * syscall, then r2 will take us to syscall_exit; otherwise r2 will + * take us to syscall_exit_rfi and on to intr_return. + */ + bv %r0(%r2) + LDREG PT_GR28(%r1),%r28 /* reload original r28 for syscall_exit */ +ENDPROC_CFI(sys_rt_sigreturn_wrapper) + +ENTRY(syscall_exit) + /* NOTE: Not all syscalls exit this way. rt_sigreturn will exit + * via syscall_exit_rfi if the signal was received while the process + * was running. + */ + + /* save return value now */ + mfctl %cr30, %r1 + STREG %r28,TASK_PT_GR28(%r1) + + /* Seems to me that dp could be wrong here, if the syscall involved + * calling a module, and nothing got round to restoring dp on return. + */ + loadgp + +syscall_check_resched: + + /* check for reschedule */ + mfctl %cr30,%r19 + LDREG TASK_TI_FLAGS(%r19),%r19 /* long */ + bb,<,n %r19, 31-TIF_NEED_RESCHED, syscall_do_resched /* forward */ + + .import do_signal,code +syscall_check_sig: + mfctl %cr30,%r19 + LDREG TASK_TI_FLAGS(%r19),%r19 + ldi (_TIF_USER_WORK_MASK & ~_TIF_NEED_RESCHED), %r26 + and,COND(<>) %r19, %r26, %r0 + b,n syscall_restore /* skip past if we've nothing to do */ + +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. + */ + mfctl %cr30,%r1 + ldo TASK_REGS(%r1), %r26 /* struct pt_regs *regs */ + reg_save %r26 + +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ +#endif + + BL do_notify_resume,%r2 + ldi 1, %r25 /* long in_syscall = 1 */ + + mfctl %cr30,%r1 + ldo TASK_REGS(%r1), %r20 /* reload pt_regs */ + reg_restore %r20 + + b,n syscall_check_sig + +syscall_restore: + mfctl %cr30,%r1 + + /* Are we being ptraced? */ + LDREG TASK_TI_FLAGS(%r1),%r19 + ldi _TIF_SINGLESTEP|_TIF_BLOCKSTEP,%r2 + and,COND(=) %r19,%r2,%r0 + b,n syscall_restore_rfi + + ldo TASK_PT_FR31(%r1),%r19 /* reload fpregs */ + rest_fp %r19 + + LDREG TASK_PT_SAR(%r1),%r19 /* restore SAR */ + mtsar %r19 + + LDREG TASK_PT_GR2(%r1),%r2 /* restore user rp */ + LDREG TASK_PT_GR19(%r1),%r19 + LDREG TASK_PT_GR20(%r1),%r20 + LDREG TASK_PT_GR21(%r1),%r21 + LDREG TASK_PT_GR22(%r1),%r22 + LDREG TASK_PT_GR23(%r1),%r23 + LDREG TASK_PT_GR24(%r1),%r24 + LDREG TASK_PT_GR25(%r1),%r25 + LDREG TASK_PT_GR26(%r1),%r26 + LDREG TASK_PT_GR27(%r1),%r27 /* restore user dp */ + LDREG TASK_PT_GR28(%r1),%r28 /* syscall return value */ + LDREG TASK_PT_GR29(%r1),%r29 + 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 + copy %r1,%r30 /* Restore user sp */ + mfsp %sr3,%r1 /* Get user space id */ + mtsp %r1,%sr7 /* Restore sr7 */ + ssm PSW_SM_I, %r0 + + /* Set sr2 to zero for userspace syscalls to work. */ + mtsp %r0,%sr2 + mtsp %r1,%sr4 /* Restore sr4 */ + mtsp %r1,%sr5 /* Restore sr5 */ + mtsp %r1,%sr6 /* Restore sr6 */ + + depi PRIV_USER,31,2,%r31 /* ensure return to user mode. */ + +#ifdef CONFIG_64BIT + /* decide whether to reset the wide mode bit + * + * For a syscall, the W bit is stored in the lowest bit + * of sp. Extract it and reset W if it is zero */ + extrd,u,*<> %r30,63,1,%r1 + rsm PSW_SM_W, %r0 + /* now reset the lowest bit of sp if it was set */ + xor %r30,%r1,%r30 +#endif + be,n 0(%sr3,%r31) /* return to user space */ + + /* We have to return via an RFI, so that PSW T and R bits can be set + * appropriately. + * This sets up pt_regs so we can return via intr_restore, which is not + * the most efficient way of doing things, but it works. + */ +syscall_restore_rfi: + ldo -1(%r0),%r2 /* Set recovery cntr to -1 */ + mtctl %r2,%cr0 /* for immediate trap */ + LDREG TASK_PT_PSW(%r1),%r2 /* Get old PSW */ + ldi 0x0b,%r20 /* Create new PSW */ + depi -1,13,1,%r20 /* C, Q, D, and I bits */ + + /* 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.SINGLESTEP_BIT)) { %r20.27=1} */ + extru,= %r19,TIF_SINGLESTEP_PA_BIT,1,%r0 + depi -1,27,1,%r20 /* R bit */ + + /* 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) + + /* Always store space registers, since sr3 can be changed (e.g. fork) */ + + mfsp %sr3,%r25 + STREG %r25,TASK_PT_SR3(%r1) + STREG %r25,TASK_PT_SR4(%r1) + STREG %r25,TASK_PT_SR5(%r1) + STREG %r25,TASK_PT_SR6(%r1) + STREG %r25,TASK_PT_SR7(%r1) + STREG %r25,TASK_PT_IASQ0(%r1) + STREG %r25,TASK_PT_IASQ1(%r1) + + /* XXX W bit??? */ + /* Now if old D bit is clear, it means we didn't save all registers + * on syscall entry, so do that now. This only happens on TRACEME + * calls, or if someone attached to us while we were on a syscall. + * We could make this more efficient by not saving r3-r18, but + * then we wouldn't be able to use the common intr_restore path. + * It is only for traced processes anyway, so performance is not + * an issue. + */ + bb,< %r2,30,pt_regs_ok /* Branch if D set */ + ldo TASK_REGS(%r1),%r25 + reg_save %r25 /* Save r3 to r18 */ + + /* Save the current sr */ + mfsp %sr0,%r2 + STREG %r2,TASK_PT_SR0(%r1) + + /* Save the scratch sr */ + mfsp %sr1,%r2 + STREG %r2,TASK_PT_SR1(%r1) + + /* sr2 should be set to zero for userspace syscalls */ + STREG %r0,TASK_PT_SR2(%r1) + + LDREG TASK_PT_GR31(%r1),%r2 + depi PRIV_USER,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 PRIV_USER,31,2,%r2 /* ensure return to user mode. */ + STREG %r2,TASK_PT_IAOQ0(%r1) + LDREG TASK_PT_IAOQ1(%r1),%r2 + depi PRIV_USER,31,2,%r2 + STREG %r2,TASK_PT_IAOQ1(%r1) + b intr_restore + copy %r25,%r16 + +syscall_do_resched: + load32 syscall_check_resched,%r2 /* if resched, we start over again */ + load32 schedule,%r19 + bv %r0(%r19) /* jumps to schedule() */ +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ +#else + nop +#endif +END(syscall_exit) + + +#ifdef CONFIG_FUNCTION_TRACER + + .import ftrace_function_trampoline,code + .align L1_CACHE_BYTES +ENTRY_CFI(mcount, caller) +_mcount: + .export _mcount,data + /* + * The 64bit mcount() function pointer needs 4 dwords, of which the + * first two are free. We optimize it here and put 2 instructions for + * calling mcount(), and 2 instructions for ftrace_stub(). That way we + * have all on one L1 cacheline. + */ + ldi 0, %arg3 + b ftrace_function_trampoline + copy %r3, %arg2 /* caller original %sp */ +ftrace_stub: + .globl ftrace_stub + .type ftrace_stub, @function +#ifdef CONFIG_64BIT + bve (%rp) +#else + bv %r0(%rp) +#endif + nop +#ifdef CONFIG_64BIT + .dword mcount + .dword 0 /* code in head.S puts value of global gp here */ +#endif +ENDPROC_CFI(mcount) + +#ifdef CONFIG_DYNAMIC_FTRACE + +#ifdef CONFIG_64BIT +#define FTRACE_FRAME_SIZE (2*FRAME_SIZE) +#else +#define FTRACE_FRAME_SIZE FRAME_SIZE +#endif +ENTRY_CFI(ftrace_caller, caller,frame=FTRACE_FRAME_SIZE,CALLS,SAVE_RP,SAVE_SP) +ftrace_caller: + .global ftrace_caller + + STREG %r3, -FTRACE_FRAME_SIZE+1*REG_SZ(%sp) + ldo -FTRACE_FRAME_SIZE(%sp), %r3 + STREG %rp, -RP_OFFSET(%r3) + + /* Offset 0 is already allocated for %r1 */ + STREG %r23, 2*REG_SZ(%r3) + STREG %r24, 3*REG_SZ(%r3) + STREG %r25, 4*REG_SZ(%r3) + STREG %r26, 5*REG_SZ(%r3) + STREG %r28, 6*REG_SZ(%r3) + STREG %r29, 7*REG_SZ(%r3) +#ifdef CONFIG_64BIT + STREG %r19, 8*REG_SZ(%r3) + STREG %r20, 9*REG_SZ(%r3) + STREG %r21, 10*REG_SZ(%r3) + STREG %r22, 11*REG_SZ(%r3) + STREG %r27, 12*REG_SZ(%r3) + STREG %r31, 13*REG_SZ(%r3) + loadgp + ldo -16(%sp),%r29 +#endif + LDREG 0(%r3), %r25 + copy %rp, %r26 + ldo -8(%r25), %r25 + ldi 0, %r23 /* no pt_regs */ + b,l ftrace_function_trampoline, %rp + copy %r3, %r24 + + LDREG -RP_OFFSET(%r3), %rp + LDREG 2*REG_SZ(%r3), %r23 + LDREG 3*REG_SZ(%r3), %r24 + LDREG 4*REG_SZ(%r3), %r25 + LDREG 5*REG_SZ(%r3), %r26 + LDREG 6*REG_SZ(%r3), %r28 + LDREG 7*REG_SZ(%r3), %r29 +#ifdef CONFIG_64BIT + LDREG 8*REG_SZ(%r3), %r19 + LDREG 9*REG_SZ(%r3), %r20 + LDREG 10*REG_SZ(%r3), %r21 + LDREG 11*REG_SZ(%r3), %r22 + LDREG 12*REG_SZ(%r3), %r27 + LDREG 13*REG_SZ(%r3), %r31 +#endif + LDREG 1*REG_SZ(%r3), %r3 + + LDREGM -FTRACE_FRAME_SIZE(%sp), %r1 + /* Adjust return point to jump back to beginning of traced function */ + ldo -4(%r1), %r1 + bv,n (%r1) + +ENDPROC_CFI(ftrace_caller) + +#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS +ENTRY_CFI(ftrace_regs_caller,caller,frame=FTRACE_FRAME_SIZE+PT_SZ_ALGN, + CALLS,SAVE_RP,SAVE_SP) +ftrace_regs_caller: + .global ftrace_regs_caller + + ldo -FTRACE_FRAME_SIZE(%sp), %r1 + STREG %rp, -RP_OFFSET(%r1) + + copy %sp, %r1 + ldo PT_SZ_ALGN(%sp), %sp + + STREG %rp, PT_GR2(%r1) + STREG %r3, PT_GR3(%r1) + STREG %r4, PT_GR4(%r1) + STREG %r5, PT_GR5(%r1) + STREG %r6, PT_GR6(%r1) + STREG %r7, PT_GR7(%r1) + STREG %r8, PT_GR8(%r1) + STREG %r9, PT_GR9(%r1) + STREG %r10, PT_GR10(%r1) + STREG %r11, PT_GR11(%r1) + STREG %r12, PT_GR12(%r1) + STREG %r13, PT_GR13(%r1) + STREG %r14, PT_GR14(%r1) + STREG %r15, PT_GR15(%r1) + STREG %r16, PT_GR16(%r1) + STREG %r17, PT_GR17(%r1) + STREG %r18, PT_GR18(%r1) + STREG %r19, PT_GR19(%r1) + STREG %r20, PT_GR20(%r1) + STREG %r21, PT_GR21(%r1) + STREG %r22, PT_GR22(%r1) + STREG %r23, PT_GR23(%r1) + STREG %r24, PT_GR24(%r1) + STREG %r25, PT_GR25(%r1) + STREG %r26, PT_GR26(%r1) + STREG %r27, PT_GR27(%r1) + STREG %r28, PT_GR28(%r1) + STREG %r29, PT_GR29(%r1) + STREG %r30, PT_GR30(%r1) + STREG %r31, PT_GR31(%r1) + mfctl %cr11, %r26 + STREG %r26, PT_SAR(%r1) + + copy %rp, %r26 + LDREG -FTRACE_FRAME_SIZE-PT_SZ_ALGN(%sp), %r25 + ldo -8(%r25), %r25 + ldo -FTRACE_FRAME_SIZE(%r1), %arg2 + b,l ftrace_function_trampoline, %rp + copy %r1, %arg3 /* struct pt_regs */ + + ldo -PT_SZ_ALGN(%sp), %r1 + + LDREG PT_SAR(%r1), %rp + mtctl %rp, %cr11 + + LDREG PT_GR2(%r1), %rp + LDREG PT_GR3(%r1), %r3 + LDREG PT_GR4(%r1), %r4 + LDREG PT_GR5(%r1), %r5 + LDREG PT_GR6(%r1), %r6 + LDREG PT_GR7(%r1), %r7 + LDREG PT_GR8(%r1), %r8 + LDREG PT_GR9(%r1), %r9 + LDREG PT_GR10(%r1),%r10 + LDREG PT_GR11(%r1),%r11 + LDREG PT_GR12(%r1),%r12 + LDREG PT_GR13(%r1),%r13 + LDREG PT_GR14(%r1),%r14 + LDREG PT_GR15(%r1),%r15 + LDREG PT_GR16(%r1),%r16 + LDREG PT_GR17(%r1),%r17 + LDREG PT_GR18(%r1),%r18 + LDREG PT_GR19(%r1),%r19 + LDREG PT_GR20(%r1),%r20 + LDREG PT_GR21(%r1),%r21 + LDREG PT_GR22(%r1),%r22 + LDREG PT_GR23(%r1),%r23 + LDREG PT_GR24(%r1),%r24 + LDREG PT_GR25(%r1),%r25 + LDREG PT_GR26(%r1),%r26 + LDREG PT_GR27(%r1),%r27 + LDREG PT_GR28(%r1),%r28 + LDREG PT_GR29(%r1),%r29 + LDREG PT_GR30(%r1),%r30 + LDREG PT_GR31(%r1),%r31 + + ldo -PT_SZ_ALGN(%sp), %sp + LDREGM -FTRACE_FRAME_SIZE(%sp), %r1 + /* Adjust return point to jump back to beginning of traced function */ + ldo -4(%r1), %r1 + bv,n (%r1) + +ENDPROC_CFI(ftrace_regs_caller) + +#endif +#endif + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .align 8 +ENTRY_CFI(return_to_handler, caller,frame=FRAME_SIZE) + .export parisc_return_to_handler,data +parisc_return_to_handler: + copy %r3,%r1 + STREG %r0,-RP_OFFSET(%sp) /* store 0 as %rp */ + copy %sp,%r3 + STREGM %r1,FRAME_SIZE(%sp) + STREG %ret0,8(%r3) + STREG %ret1,16(%r3) + +#ifdef CONFIG_64BIT + loadgp +#endif + + /* call ftrace_return_to_handler(0) */ + .import ftrace_return_to_handler,code + load32 ftrace_return_to_handler,%ret0 + load32 .Lftrace_ret,%r2 +#ifdef CONFIG_64BIT + ldo -16(%sp),%ret1 /* Reference param save area */ + bve (%ret0) +#else + bv %r0(%ret0) +#endif + ldi 0,%r26 +.Lftrace_ret: + copy %ret0,%rp + + /* restore original return values */ + LDREG 8(%r3),%ret0 + LDREG 16(%r3),%ret1 + + /* return from function */ +#ifdef CONFIG_64BIT + bve (%rp) +#else + bv %r0(%rp) +#endif + LDREGM -FRAME_SIZE(%sp),%r3 +ENDPROC_CFI(return_to_handler) + +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ + +#endif /* CONFIG_FUNCTION_TRACER */ + +#ifdef CONFIG_IRQSTACKS +/* void call_on_stack(unsigned long param1, void *func, + unsigned long new_stack) */ +ENTRY_CFI(call_on_stack, FRAME=2*FRAME_SIZE,CALLS,SAVE_RP,SAVE_SP) +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. */ + + /* Switch to new stack. We allocate two frames. */ + ldo 2*FRAME_SIZE(%arg2), %sp +# ifdef CONFIG_64BIT + /* Save previous stack pointer and return pointer in frame marker */ + STREG %rp, -FRAME_SIZE-RP_OFFSET(%sp) + /* Calls always use function descriptor */ + LDREG 16(%arg1), %arg1 + bve,l (%arg1), %rp + STREG %r1, -FRAME_SIZE-REG_SZ(%sp) + LDREG -FRAME_SIZE-RP_OFFSET(%sp), %rp + bve (%rp) + LDREG -FRAME_SIZE-REG_SZ(%sp), %sp +# else + /* Save previous stack pointer and return pointer in frame marker */ + STREG %r1, -FRAME_SIZE-REG_SZ(%sp) + STREG %rp, -FRAME_SIZE-RP_OFFSET(%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 -FRAME_SIZE-RP_OFFSET(%sp), %rp + bv (%rp) + LDREG -FRAME_SIZE-REG_SZ(%sp), %sp +# endif /* CONFIG_64BIT */ +ENDPROC_CFI(call_on_stack) +#endif /* CONFIG_IRQSTACKS */ + +ENTRY_CFI(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 + * r1. This routine can't be used for shadowed registers, since + * the rfir will restore the original value. So, for the shadowed + * registers we put a -1 into r1 to indicate that the register + * should not be used (the register being copied could also have + * a -1 in it, but that is OK, it just means that we will have + * to use the slow path instead). + */ + blr %r8,%r0 + nop + bv %r0(%r25) /* r0 */ + copy %r0,%r1 + bv %r0(%r25) /* r1 - shadowed */ + ldi -1,%r1 + bv %r0(%r25) /* r2 */ + copy %r2,%r1 + bv %r0(%r25) /* r3 */ + copy %r3,%r1 + bv %r0(%r25) /* r4 */ + copy %r4,%r1 + bv %r0(%r25) /* r5 */ + copy %r5,%r1 + bv %r0(%r25) /* r6 */ + copy %r6,%r1 + bv %r0(%r25) /* r7 */ + copy %r7,%r1 + bv %r0(%r25) /* r8 - shadowed */ + ldi -1,%r1 + bv %r0(%r25) /* r9 - shadowed */ + ldi -1,%r1 + bv %r0(%r25) /* r10 */ + copy %r10,%r1 + bv %r0(%r25) /* r11 */ + copy %r11,%r1 + bv %r0(%r25) /* r12 */ + copy %r12,%r1 + bv %r0(%r25) /* r13 */ + copy %r13,%r1 + bv %r0(%r25) /* r14 */ + copy %r14,%r1 + bv %r0(%r25) /* r15 */ + copy %r15,%r1 + bv %r0(%r25) /* r16 - shadowed */ + ldi -1,%r1 + bv %r0(%r25) /* r17 - shadowed */ + ldi -1,%r1 + bv %r0(%r25) /* r18 */ + copy %r18,%r1 + bv %r0(%r25) /* r19 */ + copy %r19,%r1 + bv %r0(%r25) /* r20 */ + copy %r20,%r1 + bv %r0(%r25) /* r21 */ + copy %r21,%r1 + bv %r0(%r25) /* r22 */ + copy %r22,%r1 + bv %r0(%r25) /* r23 */ + copy %r23,%r1 + bv %r0(%r25) /* r24 - shadowed */ + ldi -1,%r1 + bv %r0(%r25) /* r25 - shadowed */ + ldi -1,%r1 + bv %r0(%r25) /* r26 */ + copy %r26,%r1 + bv %r0(%r25) /* r27 */ + copy %r27,%r1 + bv %r0(%r25) /* r28 */ + copy %r28,%r1 + bv %r0(%r25) /* r29 */ + copy %r29,%r1 + bv %r0(%r25) /* r30 */ + copy %r30,%r1 + bv %r0(%r25) /* r31 */ + copy %r31,%r1 +ENDPROC_CFI(get_register) + + +ENTRY_CFI(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. + */ + blr %r8,%r0 + nop + bv %r0(%r25) /* r0 (silly, but it is a place holder) */ + copy %r1,%r0 + bv %r0(%r25) /* r1 */ + copy %r1,%r1 + bv %r0(%r25) /* r2 */ + copy %r1,%r2 + bv %r0(%r25) /* r3 */ + copy %r1,%r3 + bv %r0(%r25) /* r4 */ + copy %r1,%r4 + bv %r0(%r25) /* r5 */ + copy %r1,%r5 + bv %r0(%r25) /* r6 */ + copy %r1,%r6 + bv %r0(%r25) /* r7 */ + copy %r1,%r7 + bv %r0(%r25) /* r8 */ + copy %r1,%r8 + bv %r0(%r25) /* r9 */ + copy %r1,%r9 + bv %r0(%r25) /* r10 */ + copy %r1,%r10 + bv %r0(%r25) /* r11 */ + copy %r1,%r11 + bv %r0(%r25) /* r12 */ + copy %r1,%r12 + bv %r0(%r25) /* r13 */ + copy %r1,%r13 + bv %r0(%r25) /* r14 */ + copy %r1,%r14 + bv %r0(%r25) /* r15 */ + copy %r1,%r15 + bv %r0(%r25) /* r16 */ + copy %r1,%r16 + bv %r0(%r25) /* r17 */ + copy %r1,%r17 + bv %r0(%r25) /* r18 */ + copy %r1,%r18 + bv %r0(%r25) /* r19 */ + copy %r1,%r19 + bv %r0(%r25) /* r20 */ + copy %r1,%r20 + bv %r0(%r25) /* r21 */ + copy %r1,%r21 + bv %r0(%r25) /* r22 */ + copy %r1,%r22 + bv %r0(%r25) /* r23 */ + copy %r1,%r23 + bv %r0(%r25) /* r24 */ + copy %r1,%r24 + bv %r0(%r25) /* r25 */ + copy %r1,%r25 + bv %r0(%r25) /* r26 */ + copy %r1,%r26 + bv %r0(%r25) /* r27 */ + copy %r1,%r27 + bv %r0(%r25) /* r28 */ + copy %r1,%r28 + bv %r0(%r25) /* r29 */ + copy %r1,%r29 + bv %r0(%r25) /* r30 */ + copy %r1,%r30 + bv %r0(%r25) /* r31 */ + copy %r1,%r31 +ENDPROC_CFI(set_register) + diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c new file mode 100644 index 000000000..92276bcf1 --- /dev/null +++ b/arch/parisc/kernel/firmware.c @@ -0,0 +1,1886 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * arch/parisc/kernel/firmware.c - safe PDC access routines + * + * PDC == Processor Dependent Code + * + * See PDC documentation at + * https://parisc.wiki.kernel.org/index.php/Technical_Documentation + * for documentation describing the entry points and calling + * conventions defined below. + * + * Copyright 1999 SuSE GmbH Nuernberg (Philipp Rumpf, prumpf@tux.org) + * 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,2006 Thibaut VARENE <varenet@parisc-linux.org> + */ + +/* I think it would be in everyone's best interest to follow this + * guidelines when writing PDC wrappers: + * + * - the name of the pdc wrapper should match one of the macros + * used for the first two arguments + * - don't use caps for random parts of the name + * - use the static PDC result buffers and "copyout" to structs + * supplied by the caller to encapsulate alignment restrictions + * - hold pdc_lock while in PDC or using static result buffers + * - use __pa() to convert virtual (kernel) pointers to physical + * ones. + * - the name of the struct used for pdc return values should equal + * one of the macros used for the first two arguments to the + * corresponding PDC call + * - keep the order of arguments + * - don't be smart (setting trailing NUL bytes for strings, return + * something useful even if the call failed) unless you are sure + * it's not going to affect functionality or performance + * + * Example: + * int pdc_cache_info(struct pdc_cache_info *cache_info ) + * { + * int retval; + * + * spin_lock_irq(&pdc_lock); + * retval = mem_pdc_call(PDC_CACHE,PDC_CACHE_INFO,__pa(cache_info),0); + * convert_to_wide(pdc_result); + * memcpy(cache_info, pdc_result, sizeof(*cache_info)); + * spin_unlock_irq(&pdc_lock); + * + * return retval; + * } + * prumpf 991016 + */ + +#include <linux/stdarg.h> + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/spinlock.h> + +#include <asm/page.h> +#include <asm/pdc.h> +#include <asm/pdcpat.h> +#include <asm/processor.h> /* for boot_cpu_data */ + +#if defined(BOOTLOADER) +# undef spin_lock_irqsave +# define spin_lock_irqsave(a, b) { b = 1; } +# undef spin_unlock_irqrestore +# define spin_unlock_irqrestore(a, b) +#else +static DEFINE_SPINLOCK(pdc_lock); +#endif + +extern unsigned long pdc_result[NUM_PDC_RESULT]; +extern unsigned long pdc_result2[NUM_PDC_RESULT]; + +#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 __ro_after_init = 2; +#endif + +/* On most currently-supported platforms, IODC I/O calls are 32-bit calls + * and MEM_PDC calls are always the same width as the OS. + * Some PAT boxes may have 64-bit IODC I/O. + * + * Ryan Bradetich added the now obsolete CONFIG_PDC_NARROW to allow + * 64-bit kernels to run on systems with 32-bit MEM_PDC calls. + * This allowed wide kernels to run on Cxxx boxes. + * We now detect 32-bit-only PDC and dynamically switch to 32-bit mode + * when running a 64-bit kernel on such boxes (e.g. C200 or C360). + */ + +#ifdef CONFIG_64BIT +long real64_call(unsigned long function, ...); +#endif +long real32_call(unsigned long function, ...); + +#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 +# define MEM_PDC (unsigned long)PAGE0->mem_pdc +# define mem_pdc_call(args...) real32_call(MEM_PDC, args) +#endif + + +/** + * f_extend - Convert PDC addresses to kernel addresses. + * @address: Address returned from PDC. + * + * This function is used to convert PDC addresses into kernel addresses + * when the PDC address size and kernel address size are different. + */ +static unsigned long f_extend(unsigned long address) +{ +#ifdef CONFIG_64BIT + if(unlikely(parisc_narrow_firmware)) { + if((address & 0xff000000) == 0xf0000000) + return (0xfffffff0UL << 32) | (u32)address; + + if((address & 0xf0000000) == 0xf0000000) + return (0xffffffffUL << 32) | (u32)address; + } +#endif + return address; +} + +/** + * convert_to_wide - Convert the return buffer addresses into kernel addresses. + * @address: The return buffer from PDC. + * + * This function is used to convert the return buffer addresses retrieved from PDC + * into kernel addresses when the PDC address size and kernel address size are + * different. + */ +static void convert_to_wide(unsigned long *addr) +{ +#ifdef CONFIG_64BIT + int i; + unsigned int *p = (unsigned int *)addr; + + if (unlikely(parisc_narrow_firmware)) { + for (i = (NUM_PDC_RESULT-1); i >= 0; --i) + addr[i] = p[i]; + } +#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. + */ +void set_firmware_width(void) +{ + unsigned long flags; + + /* already initialized? */ + if (parisc_narrow_firmware != 2) + return; + + spin_lock_irqsave(&pdc_lock, flags); + set_firmware_width_unlocked(); + spin_unlock_irqrestore(&pdc_lock, flags); +} +#else +void set_firmware_width_unlocked(void) +{ + return; +} + +void set_firmware_width(void) +{ + return; +} +#endif /*CONFIG_64BIT*/ + + +#if !defined(BOOTLOADER) +/** + * pdc_emergency_unlock - Unlock the linux pdc lock + * + * This call unlocks the linux pdc lock in case we need some PDC functions + * (like pdc_add_valid) during kernel stack dump. + */ +void pdc_emergency_unlock(void) +{ + /* Spinlock DEBUG code freaks out if we unconditionally unlock */ + if (spin_is_locked(&pdc_lock)) + spin_unlock(&pdc_lock); +} + + +/** + * pdc_add_valid - Verify address can be accessed without causing a HPMC. + * @address: Address to be verified. + * + * This PDC call attempts to read from the specified address and verifies + * if the address is valid. + * + * The return value is PDC_OK (0) in case accessing this address is valid. + */ +int pdc_add_valid(unsigned long address) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_ADD_VALID, PDC_ADD_VALID_VERIFY, address); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_add_valid); + +/** + * pdc_instr - Get instruction that invokes PDCE_CHECK in HPMC handler. + * @instr: Pointer to variable which will get instruction opcode. + * + * The return value is PDC_OK (0) in case call succeeded. + */ +int __init pdc_instr(unsigned int *instr) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_INSTR, 0UL, __pa(pdc_result)); + convert_to_wide(pdc_result); + *instr = pdc_result[0]; + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_chassis_info - Return chassis information. + * @result: The return buffer. + * @chassis_info: The memory buffer address. + * @len: The size of the memory buffer address. + * + * An HVERSION dependent call for returning the chassis information. + */ +int __init pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_info, unsigned long len) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_chassis_send_log - Sends a PDC PAT CHASSIS log message. + * @retval: -1 on error, 0 on success. Other value are PDC errors + * + * Must be correctly formatted or expect system crash + */ +#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_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_CHASSIS_LOG, PDC_PAT_CHASSIS_WRITE_LOG, __pa(&state), __pa(&data)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif + +/** + * pdc_chassis_disp - Updates chassis code + * @retval: -1 on error, 0 on success + */ +int pdc_chassis_disp(unsigned long disp) +{ + int retval = 0; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_DISP, disp); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_cpu_rendenzvous - Stop currently executing CPU + * @retval: -1 on error, 0 on success + */ +int __pdc_cpu_rendezvous(void) +{ + if (is_pdc_pat()) + return mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_RENDEZVOUS); + else + return mem_pdc_call(PDC_PROC, 1, 0); +} + +/** + * pdc_cpu_rendezvous_lock - Lock PDC while transitioning to rendezvous state + */ +void pdc_cpu_rendezvous_lock(void) +{ + spin_lock(&pdc_lock); +} + +/** + * pdc_cpu_rendezvous_unlock - Unlock PDC after reaching rendezvous state + */ +void pdc_cpu_rendezvous_unlock(void) +{ + spin_unlock(&pdc_lock); +} + +/** + * pdc_pat_get_PDC_entrypoint - Get PDC entry point for current CPU + * @retval: -1 on error, 0 on success + */ +int pdc_pat_get_PDC_entrypoint(unsigned long *pdc_entry) +{ + int retval = 0; + unsigned long flags; + + if (!IS_ENABLED(CONFIG_SMP) || !is_pdc_pat()) { + *pdc_entry = MEM_PDC; + return 0; + } + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_GET_PDC_ENTRYPOINT, + __pa(pdc_result)); + *pdc_entry = pdc_result[0]; + 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 pdc_coproc_cfg(struct pdc_coproc_cfg *pdc_coproc_info) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + ret = pdc_coproc_cfg_unlocked(pdc_coproc_info); + spin_unlock_irqrestore(&pdc_lock, flags); + + return ret; +} + +/** + * pdc_iodc_read - Read data from the modules IODC. + * @actcnt: The actual number of bytes. + * @hpa: The HPA of the module for the iodc read. + * @index: The iodc entry point. + * @iodc_data: A buffer memory for the iodc options. + * @iodc_data_size: Size of the memory buffer. + * + * This PDC call reads from the IODC of the module specified by the hpa + * argument. + */ +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_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_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_iodc_read); + +/** + * pdc_system_map_find_mods - Locate unarchitected modules. + * @pdc_mod_info: Return buffer address. + * @mod_path: pointer to dev path structure. + * @mod_index: fixed address module index. + * + * To locate and identify modules which reside at fixed I/O addresses, which + * do not self-identify via architected bus walks. + */ +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_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_irqrestore(&pdc_lock, flags); + + pdc_mod_info->mod_addr = f_extend(pdc_mod_info->mod_addr); + return retval; +} + +/** + * pdc_system_map_find_addrs - Retrieve additional address ranges. + * @pdc_addr_info: Return buffer address. + * @mod_index: Fixed address module index. + * @addr_index: Address range index. + * + * Retrieve additional information about subsequent address ranges for modules + * with multiple address ranges. + */ +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_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_irqrestore(&pdc_lock, flags); + + pdc_addr_info->mod_addr = f_extend(pdc_addr_info->mod_addr); + return retval; +} + +/** + * pdc_model_info - Return model information about the processor. + * @model: The return buffer. + * + * Returns the version numbers, identifiers, and capabilities from the processor module. + */ +int pdc_model_info(struct pdc_model *model) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * 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). + * 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_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); + + if (retval == PDC_OK) { + name[pdc_result[0]] = '\0'; /* add trailing '\0' */ + } else { + name[0] = 0; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_versions - Identify the version number of each processor. + * @cpu_id: The return buffer. + * @id: The id of the processor to check. + * + * Returns the version number for each processor component. + * + * This comment was here before, but I do not know what it means :( -RB + * id: 0 = cpu revision, 1 = boot-rom-version + */ +int pdc_model_versions(unsigned long *versions, int id) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_cpuid - Returns the CPU_ID. + * @cpu_id: The return buffer. + * + * Returns the CPU_ID value which uniquely identifies the cpu portion of + * the processor module. + */ +int pdc_model_cpuid(unsigned long *cpu_id) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_capabilities - Returns the platform capabilities. + * @capabilities: The return buffer. + * + * Returns information about platform support for 32- and/or 64-bit + * OSes, IO-PDIR coherency, and virtual aliasing. + */ +int pdc_model_capabilities(unsigned long *capabilities) +{ + int retval; + unsigned long flags; + + 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); + if (retval == PDC_OK) { + *capabilities = pdc_result[0]; + } else { + *capabilities = PDC_MODEL_OS32; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_model_platform_info - Returns machine product and serial number. + * @orig_prod_num: Return buffer for original product number. + * @current_prod_num: Return buffer for current product number. + * @serial_no: Return buffer for serial number. + * + * Returns strings containing the original and current product numbers and the + * serial number of the system. + */ +int pdc_model_platform_info(char *orig_prod_num, char *current_prod_num, + char *serial_no) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MODEL, PDC_MODEL_GET_PLATFORM_INFO, + __pa(orig_prod_num), __pa(current_prod_num), __pa(serial_no)); + convert_to_wide(pdc_result); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_cache_info - Return cache and TLB information. + * @cache_info: The return buffer. + * + * Returns information about the processor's cache and TLB. + */ +int pdc_cache_info(struct pdc_cache_info *cache_info) +{ + int retval; + unsigned long flags; + + 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_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. + * @btlb: The return buffer. + * + * Returns information about the hardware Block TLB. + */ +int pdc_btlb_info(struct pdc_btlb_info *btlb) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + if(retval < 0) { + btlb->max_size = 0; + } + return retval; +} + +/** + * pdc_mem_map_hpa - Find fixed module information. + * @address: The return buffer + * @mod_path: pointer to dev path structure. + * + * This call was developed for S700 workstations to allow the kernel to find + * the I/O devices (Core I/O). In the future (Kittyhawk and beyond) this + * call will be replaced (on workstations) by the architected PDC_SYSTEM_MAP + * call. + * + * This call is supported by all existing S700 workstations (up to Gecko). + */ +int pdc_mem_map_hpa(struct pdc_memory_map *address, + struct pdc_module_path *mod_path) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif /* !CONFIG_PA20 */ + +/** + * pdc_lan_station_id - Get the LAN address. + * @lan_addr: The return buffer. + * @hpa: The network device HPA. + * + * Get the LAN station address when it is not directly available from the LAN hardware. + */ +int pdc_lan_station_id(char *lan_addr, unsigned long hpa) +{ + int retval; + unsigned long flags; + + 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) { + /* FIXME: else read MAC from NVRAM */ + memset(lan_addr, 0, PDC_LAN_STATION_ID_SIZE); + } else { + memcpy(lan_addr, pdc_result, PDC_LAN_STATION_ID_SIZE); + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +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 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. + * The call will fail if staddr+count > PDC_STABLE size. + */ +int pdc_stable_read(unsigned long staddr, void *memaddr, unsigned long count) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} +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 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. + * The call will fail if staddr+count > PDC_STABLE size. + */ +int pdc_stable_write(unsigned long staddr, void *memaddr, unsigned long count) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_write); + +/** + * pdc_stable_get_size - Get Stable Storage size in bytes. + * @size: pointer where the size will be stored. + * + * This PDC call returns the number of bytes in the processor's Stable + * Storage, which is the number of contiguous bytes implemented in Stable + * Storage starting from staddr=0. size in an unsigned 64-bit integer + * which is a multiple of four. + */ +int pdc_stable_get_size(unsigned long *size) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_get_size); + +/** + * pdc_stable_verify_contents - Checks that Stable Storage contents are valid. + * + * This PDC call is meant to be used to check the integrity of the current + * contents of Stable Storage. + */ +int pdc_stable_verify_contents(void) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_VERIFY_CONTENTS); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_verify_contents); + +/** + * pdc_stable_initialize - Sets Stable Storage contents to zero and initialize + * the validity indicator. + * + * This PDC call will erase all contents of Stable Storage. Use with care! + */ +int pdc_stable_initialize(void) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_STABLE, PDC_STABLE_INITIALIZE); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_stable_initialize); + +/** + * pdc_get_initiator - Get the SCSI Interface Card params (SCSI ID, SDTR, SE or LVD) + * @hwpath: fully bc.mod style path to the device. + * @initiator: the array to return the result into + * + * Get the SCSI operational parameters from PDC. + * Needed since HPUX never used BIOS or symbios card NVRAM. + * Most ncr/sym cards won't have an entry and just use whatever + * capabilities of the card are (eg Ultra, LVD). But there are + * several cases where it's useful: + * o set SCSI id for Multi-initiator clusters, + * o cable too long (ie SE scsi 10Mhz won't support 6m length), + * o bus width exported is less than what the interface chip supports. + */ +int pdc_get_initiator(struct hardware_path *hwpath, struct pdc_initiator *initiator) +{ + int retval; + unsigned long flags; + + 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 && \ + strncmp(boot_cpu_data.pdc.sys_model_name, "9000/785", 8) == 0) + + retval = mem_pdc_call(PDC_INITIATOR, PDC_GET_INITIATOR, + __pa(pdc_result), __pa(hwpath)); + if (retval < PDC_OK) + goto out; + + if (pdc_result[0] < 16) { + initiator->host_id = pdc_result[0]; + } else { + initiator->host_id = -1; + } + + /* + * Sprockets and Piranha return 20 or 40 (MT/s). Prelude returns + * 1, 2, 5 or 10 for 5, 10, 20 or 40 MT/s, respectively + */ + switch (pdc_result[1]) { + case 1: initiator->factor = 50; break; + case 2: initiator->factor = 25; break; + case 5: initiator->factor = 12; break; + case 25: initiator->factor = 10; break; + case 20: initiator->factor = 12; break; + case 40: initiator->factor = 10; break; + default: initiator->factor = -1; break; + } + + if (IS_SPROCKETS()) { + initiator->width = pdc_result[4]; + initiator->mode = pdc_result[5]; + } else { + initiator->width = -1; + initiator->mode = -1; + } + + out: + spin_unlock_irqrestore(&pdc_lock, flags); + + return (retval >= PDC_OK); +} +EXPORT_SYMBOL(pdc_get_initiator); + + +/** + * pdc_pci_irt_size - Get the number of entries in the interrupt routing table. + * @num_entries: The return value. + * @hpa: The HPA for the device. + * + * This PDC function returns the number of entries in the specified cell's + * interrupt table. + * Similar to PDC_PAT stuff - but added for Forte/Allegro boxes + */ +int pdc_pci_irt_size(unsigned long *num_entries, unsigned long hpa) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pci_irt - Get the PCI interrupt routing table. + * @num_entries: The number of entries in the table. + * @hpa: The Hard Physical Address of the device. + * @tbl: + * + * Get the PCI interrupt routing table for the device at the given HPA. + * Similar to PDC_PAT stuff - but added for Forte/Allegro boxes + */ +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_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_irqrestore(&pdc_lock, flags); + + return retval; +} + + +#if 0 /* UNTEST CODE - left here in case someone needs it */ + +/** + * pdc_pci_config_read - read PCI config space. + * @hpa token from PDC to indicate which PCI device + * @pci_addr configuration space address to read from + * + * Read PCI Configuration space *before* linux PCI subsystem is running. + */ +unsigned int pdc_pci_config_read(void *hpa, unsigned long cfg_addr) +{ + int retval; + 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_irqrestore(&pdc_lock, flags); + + return retval ? ~0 : (unsigned int) pdc_result[0]; +} + + +/** + * pdc_pci_config_write - read PCI config space. + * @hpa token from PDC to indicate which PCI device + * @pci_addr configuration space address to write + * @val value we want in the 32-bit register + * + * Write PCI Configuration space *before* linux PCI subsystem is running. + */ +void pdc_pci_config_write(void *hpa, unsigned long cfg_addr, unsigned int val) +{ + int retval; + 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_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif /* UNTESTED CODE */ + +/** + * pdc_tod_read - Read the Time-Of-Day clock. + * @tod: The return buffer: + * + * Read the Time-Of-Day clock + */ +int pdc_tod_read(struct pdc_tod *tod) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_tod_read); + +int pdc_mem_pdt_info(struct pdc_mem_retinfo *rinfo) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MEM, PDC_MEM_MEMINFO, __pa(pdc_result), 0); + convert_to_wide(pdc_result); + memcpy(rinfo, pdc_result, sizeof(*rinfo)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +int pdc_mem_pdt_read_entries(struct pdc_mem_read_pdt *pret, + unsigned long *pdt_entries_ptr) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_MEM, PDC_MEM_READ_PDT, __pa(pdc_result), + __pa(pdt_entries_ptr)); + if (retval == PDC_OK) { + convert_to_wide(pdc_result); + memcpy(pret, pdc_result, sizeof(*pret)); + } + spin_unlock_irqrestore(&pdc_lock, flags); + +#ifdef CONFIG_64BIT + /* + * 64-bit kernels should not call this PDT function in narrow mode. + * The pdt_entries_ptr array above will now contain 32-bit values + */ + if (WARN_ON_ONCE((retval == PDC_OK) && parisc_narrow_firmware)) + return PDC_ERROR; +#endif + + return retval; +} + +/** + * pdc_pim_toc11 - Fetch TOC PIM 1.1 data from firmware. + * @ret: pointer to return buffer + */ +int pdc_pim_toc11(struct pdc_toc_pim_11 *ret) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PIM, PDC_PIM_TOC, __pa(pdc_result), + __pa(ret), sizeof(*ret)); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; +} + +/** + * pdc_pim_toc20 - Fetch TOC PIM 2.0 data from firmware. + * @ret: pointer to return buffer + */ +int pdc_pim_toc20(struct pdc_toc_pim_20 *ret) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PIM, PDC_PIM_TOC, __pa(pdc_result), + __pa(ret), sizeof(*ret)); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; +} + +/** + * pdc_tod_set - Set the Time-Of-Day clock. + * @sec: The number of seconds since epoch. + * @usec: The number of micro seconds. + * + * Set the Time-Of-Day clock. + */ +int pdc_tod_set(unsigned long sec, unsigned long usec) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_TOD, PDC_TOD_WRITE, sec, usec); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +EXPORT_SYMBOL(pdc_tod_set); + +#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_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_irqrestore(&pdc_lock, flags); + + return retval; +} +#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 + * it later. :) + */ +int pdc_do_firm_test_reset(unsigned long ftc_bitmap) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_do_reset - Reset the system. + * + * Reset the system. + */ +int pdc_do_reset(void) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BROADCAST_RESET, PDC_DO_RESET); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_soft_power_info - Enable soft power switch. + * @power_reg: address of soft power register + * + * Return the absolute address of the soft power switch register + */ +int __init pdc_soft_power_info(unsigned long *power_reg) +{ + int retval; + unsigned long flags; + + *power_reg = (unsigned long) (-1); + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_soft_power_button{_panic} - Control the soft power button behaviour + * @sw_control: 0 for hardware control, 1 for software control + * + * + * This PDC function places the soft power button under software or + * hardware control. + * Under software control the OS may control to when to allow to shut + * down the system. Under hardware control pressing the power button + * powers off the system immediately. + * + * The _panic version relies on spin_trylock to prevent deadlock + * on panic path. + */ +int pdc_soft_power_button(int sw_control) +{ + int retval; + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +int pdc_soft_power_button_panic(int sw_control) +{ + int retval; + unsigned long flags; + + if (!spin_trylock_irqsave(&pdc_lock, flags)) { + pr_emerg("Couldn't enable soft power button\n"); + return -EBUSY; /* ignored by the panic notifier */ + } + + retval = mem_pdc_call(PDC_SOFT_POWER, PDC_SOFT_POWER_ENABLE, __pa(pdc_result), sw_control); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/* + * pdc_io_reset - Hack to avoid overlapping range registers of Bridges devices. + * Primarily a problem on T600 (which parisc-linux doesn't support) but + * who knows what other platform firmware might do with this OS "hook". + */ +void pdc_io_reset(void) +{ + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + mem_pdc_call(PDC_IO, PDC_IO_RESET, 0); + spin_unlock_irqrestore(&pdc_lock, flags); +} + +/* + * pdc_io_reset_devices - Hack to Stop USB controller + * + * If PDC used the usb controller, the usb controller + * is still running and will crash the machines during iommu + * setup, because of still running DMA. This PDC call + * stops the USB controller. + * Normally called after calling pdc_io_reset(). + */ +void pdc_io_reset_devices(void) +{ + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + mem_pdc_call(PDC_IO, PDC_IO_RESET_DEVICES, 0); + spin_unlock_irqrestore(&pdc_lock, flags); +} + +#endif /* defined(BOOTLOADER) */ + +/* locked by pdc_lock */ +static char iodc_dbuf[4096] __page_aligned_bss; + +/** + * 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". + */ +int pdc_iodc_print(const unsigned char *str, unsigned count) +{ + unsigned int i, found = 0; + unsigned long flags; + + count = min_t(unsigned int, count, sizeof(iodc_dbuf)); + + spin_lock_irqsave(&pdc_lock, flags); + for (i = 0; i < count;) { + switch(str[i]) { + case '\n': + iodc_dbuf[i+0] = '\r'; + iodc_dbuf[i+1] = '\n'; + i += 2; + found = 1; + goto print; + default: + iodc_dbuf[i] = str[i]; + i++; + break; + } + } + +print: + 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(pdc_result), 0, __pa(iodc_dbuf), i, 0); + spin_unlock_irqrestore(&pdc_lock, flags); + + return i - found; +} + +#if !defined(BOOTLOADER) +/** + * pdc_iodc_getc - Read a character (non-blocking) from the PDC console. + * + * Read a character (non-blocking) from the PDC console, returns -1 if + * key is not present. + */ +int pdc_iodc_getc(void) +{ + int ch; + int status; + unsigned long flags; + + /* Bail if no console input device. */ + if (!PAGE0->mem_kbd.iodc_io) + return 0; + + /* wait for a keyboard (rs232)-input */ + spin_lock_irqsave(&pdc_lock, flags); + real32_call(PAGE0->mem_kbd.iodc_io, + (unsigned long)PAGE0->mem_kbd.hpa, ENTRY_IO_CIN, + PAGE0->mem_kbd.spa, __pa(PAGE0->mem_kbd.dp.layers), + __pa(pdc_result), 0, __pa(iodc_dbuf), 1, 0); + + ch = *iodc_dbuf; + /* like convert_to_wide() but for first return value only: */ + status = *(int *)&pdc_result; + spin_unlock_irqrestore(&pdc_lock, flags); + + if (status == 0) + return -1; + + return ch; +} + +int pdc_sti_call(unsigned long func, unsigned long flags, + unsigned long inptr, unsigned long outputr, + unsigned long glob_cfg) +{ + int retval; + unsigned long irqflags; + + spin_lock_irqsave(&pdc_lock, irqflags); + retval = real32_call(func, flags, inptr, outputr, glob_cfg); + spin_unlock_irqrestore(&pdc_lock, irqflags); + + return retval; +} +EXPORT_SYMBOL(pdc_sti_call); + +#ifdef CONFIG_64BIT +/** + * pdc_pat_cell_get_number - Returns the cell number. + * @cell_info: The return buffer. + * + * This PDC call returns the cell number of the cell from which the call + * is made. + */ +int pdc_pat_cell_get_number(struct pdc_pat_cell_num *cell_info) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_cell_module - Retrieve the cell's module information. + * @actcnt: The number of bytes written to mem_addr. + * @ploc: The physical location. + * @mod: The module index. + * @view_type: The view of the address type. + * @mem_addr: The return buffer. + * + * This PDC call returns information about each module attached to the cell + * at the specified location. + */ +int pdc_pat_cell_module(unsigned long *actcnt, unsigned long ploc, unsigned long mod, + 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_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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_cell_info - Retrieve the cell's information. + * @info: The pointer to a struct pdc_pat_cell_info_rtn_block. + * @actcnt: The number of bytes which should be written to info. + * @offset: offset of the structure. + * @cell_number: The cell number which should be asked, or -1 for current cell. + * + * This PDC call returns information about the given cell (or all cells). + */ +int pdc_pat_cell_info(struct pdc_pat_cell_info_rtn_block *info, + unsigned long *actcnt, unsigned long offset, + unsigned long cell_number) +{ + int retval; + unsigned long flags; + struct pdc_pat_cell_info_rtn_block result; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_CELL, PDC_PAT_CELL_GET_INFO, + __pa(pdc_result), __pa(&result), *actcnt, + offset, cell_number); + if (!retval) { + *actcnt = pdc_result[0]; + memcpy(info, &result, *actcnt); + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_cpu_get_number - Retrieve the cpu number. + * @cpu_info: The return buffer. + * @hpa: The Hard Physical Address of the CPU. + * + * Retrieve the cpu number for the cpu at the specified HPA. + */ +int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, unsigned long hpa) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_get_irt_size - Retrieve the number of entries in the cell's interrupt table. + * @num_entries: The return value. + * @cell_num: The target cell. + * + * This PDC function returns the number of entries in the specified cell's + * interrupt table. + */ +int pdc_pat_get_irt_size(unsigned long *num_entries, unsigned long cell_num) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_get_irt - Retrieve the cell's interrupt table. + * @r_addr: The return buffer. + * @cell_num: The target cell. + * + * This PDC function returns the actual interrupt table for the specified cell. + */ +int pdc_pat_get_irt(void *r_addr, unsigned long cell_num) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_pd_get_addr_map - Retrieve information about memory address ranges. + * @actlen: The return buffer. + * @mem_addr: Pointer to the memory buffer. + * @count: The number of bytes to read from the buffer. + * @offset: The offset with respect to the beginning of the buffer. + * + */ +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_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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_pd_get_PDC_interface_revisions - Retrieve PDC interface revisions. + * @legacy_rev: The legacy revision. + * @pat_rev: The PAT revision. + * @pdc_cap: The PDC capabilities. + * + */ +int pdc_pat_pd_get_pdc_revisions(unsigned long *legacy_rev, + unsigned long *pat_rev, unsigned long *pdc_cap) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_PD, PDC_PAT_PD_GET_PDC_INTERF_REV, + __pa(pdc_result)); + if (retval == PDC_OK) { + *legacy_rev = pdc_result[0]; + *pat_rev = pdc_result[1]; + *pdc_cap = pdc_result[2]; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + + +/** + * pdc_pat_io_pci_cfg_read - Read PCI configuration space. + * @pci_addr: PCI configuration space address for which the read request is being made. + * @pci_size: Size of read in bytes. Valid values are 1, 2, and 4. + * @mem_addr: Pointer to return memory buffer. + * + */ +int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *mem_addr) +{ + int retval; + 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) { + case 1: *(u8 *) mem_addr = (u8) pdc_result[0]; break; + case 2: *(u16 *)mem_addr = (u16) pdc_result[0]; break; + case 4: *(u32 *)mem_addr = (u32) pdc_result[0]; break; + } + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_io_pci_cfg_write - Retrieve information about memory address ranges. + * @pci_addr: PCI configuration space address for which the write request is being made. + * @pci_size: Size of write in bytes. Valid values are 1, 2, and 4. + * @value: Pointer to 1, 2, or 4 byte value in low order end of argument to be + * written to PCI Config space. + * + */ +int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val) +{ + int retval; + unsigned long flags; + + 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_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_pdc_info - Retrieve information about page deallocation table + * @rinfo: memory pdt information + * + */ +int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_INFO, + __pa(&pdc_result)); + if (retval == PDC_OK) + memcpy(rinfo, &pdc_result, sizeof(*rinfo)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_pdt_cell_info - Retrieve information about page deallocation + * table of a cell + * @rinfo: memory pdt information + * @cell: cell number + * + */ +int pdc_pat_mem_pdt_cell_info(struct pdc_pat_mem_cell_pdt_retinfo *rinfo, + unsigned long cell) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_CELL_INFO, + __pa(&pdc_result), cell); + if (retval == PDC_OK) + memcpy(rinfo, &pdc_result, sizeof(*rinfo)); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_read_cell_pdt - Read PDT entries from (old) PAT firmware + * @pret: array of PDT entries + * @pdt_entries_ptr: ptr to hold number of PDT entries + * @max_entries: maximum number of entries to be read + * + */ +int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, + unsigned long *pdt_entries_ptr, unsigned long max_entries) +{ + int retval; + unsigned long flags, entries; + + spin_lock_irqsave(&pdc_lock, flags); + /* PDC_PAT_MEM_CELL_READ is available on early PAT machines only */ + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_CELL_READ, + __pa(&pdc_result), parisc_cell_num, + __pa(pdt_entries_ptr)); + + if (retval == PDC_OK) { + /* build up return value as for PDC_PAT_MEM_PD_READ */ + entries = min(pdc_result[0], max_entries); + pret->pdt_entries = entries; + pret->actual_count_bytes = entries * sizeof(unsigned long); + } + + spin_unlock_irqrestore(&pdc_lock, flags); + WARN_ON(retval == PDC_OK && pdc_result[0] > max_entries); + + return retval; +} +/** + * pdc_pat_mem_read_pd_pdt - Read PDT entries from (newer) PAT firmware + * @pret: array of PDT entries + * @pdt_entries_ptr: ptr to hold number of PDT entries + * @count: number of bytes to read + * @offset: offset to start (in bytes) + * + */ +int pdc_pat_mem_read_pd_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, + unsigned long *pdt_entries_ptr, unsigned long count, + unsigned long offset) +{ + int retval; + unsigned long flags, entries; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_READ, + __pa(&pdc_result), __pa(pdt_entries_ptr), + count, offset); + + if (retval == PDC_OK) { + entries = min(pdc_result[0], count); + pret->actual_count_bytes = entries; + pret->pdt_entries = entries / sizeof(unsigned long); + } + + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + +/** + * pdc_pat_mem_get_dimm_phys_location - Get physical DIMM slot via PAT firmware + * @pret: ptr to hold returned information + * @phys_addr: physical address to examine + * + */ +int pdc_pat_mem_get_dimm_phys_location( + struct pdc_pat_mem_phys_mem_location *pret, + unsigned long phys_addr) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_ADDRESS, + __pa(&pdc_result), phys_addr); + + if (retval == PDC_OK) + memcpy(pret, &pdc_result, sizeof(*pret)); + + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} +#endif /* CONFIG_64BIT */ +#endif /* defined(BOOTLOADER) */ + + +/***************** 32-bit real-mode calls ***********/ +/* The struct below is used + * to overlay real_stack (real2.S), preparing a 32-bit call frame. + * real32_call_asm() then uses this stack in narrow real mode + */ + +struct narrow_stack { + /* use int, not long which is 64 bits */ + unsigned int arg13; + unsigned int arg12; + unsigned int arg11; + unsigned int arg10; + unsigned int arg9; + unsigned int arg8; + unsigned int arg7; + unsigned int arg6; + unsigned int arg5; + unsigned int arg4; + unsigned int arg3; + unsigned int arg2; + unsigned int arg1; + unsigned int arg0; + unsigned int frame_marker[8]; + unsigned int sp; + /* in reality, there's nearly 8k of stack after this */ +}; + +long real32_call(unsigned long fn, ...) +{ + va_list args; + extern struct narrow_stack real_stack; + extern unsigned long real32_call_asm(unsigned int *, + unsigned int *, + unsigned int); + + va_start(args, fn); + real_stack.arg0 = va_arg(args, unsigned int); + real_stack.arg1 = va_arg(args, unsigned int); + real_stack.arg2 = va_arg(args, unsigned int); + real_stack.arg3 = va_arg(args, unsigned int); + real_stack.arg4 = va_arg(args, unsigned int); + real_stack.arg5 = va_arg(args, unsigned int); + real_stack.arg6 = va_arg(args, unsigned int); + real_stack.arg7 = va_arg(args, unsigned int); + real_stack.arg8 = va_arg(args, unsigned int); + real_stack.arg9 = va_arg(args, unsigned int); + real_stack.arg10 = va_arg(args, unsigned int); + real_stack.arg11 = va_arg(args, unsigned int); + real_stack.arg12 = va_arg(args, unsigned int); + real_stack.arg13 = va_arg(args, unsigned int); + va_end(args); + + return real32_call_asm(&real_stack.sp, &real_stack.arg0, fn); +} + +#ifdef CONFIG_64BIT +/***************** 64-bit real-mode calls ***********/ + +struct wide_stack { + unsigned long arg0; + unsigned long arg1; + unsigned long arg2; + unsigned long arg3; + unsigned long arg4; + unsigned long arg5; + unsigned long arg6; + unsigned long arg7; + unsigned long arg8; + unsigned long arg9; + unsigned long arg10; + unsigned long arg11; + unsigned long arg12; + unsigned long arg13; + unsigned long frame_marker[2]; /* rp, previous sp */ + unsigned long sp; + /* in reality, there's nearly 8k of stack after this */ +}; + +long real64_call(unsigned long fn, ...) +{ + va_list args; + extern struct wide_stack real64_stack; + extern unsigned long real64_call_asm(unsigned long *, + unsigned long *, + unsigned long); + + va_start(args, fn); + real64_stack.arg0 = va_arg(args, unsigned long); + real64_stack.arg1 = va_arg(args, unsigned long); + real64_stack.arg2 = va_arg(args, unsigned long); + real64_stack.arg3 = va_arg(args, unsigned long); + real64_stack.arg4 = va_arg(args, unsigned long); + real64_stack.arg5 = va_arg(args, unsigned long); + real64_stack.arg6 = va_arg(args, unsigned long); + real64_stack.arg7 = va_arg(args, unsigned long); + real64_stack.arg8 = va_arg(args, unsigned long); + real64_stack.arg9 = va_arg(args, unsigned long); + real64_stack.arg10 = va_arg(args, unsigned long); + real64_stack.arg11 = va_arg(args, unsigned long); + real64_stack.arg12 = va_arg(args, unsigned long); + real64_stack.arg13 = va_arg(args, unsigned long); + va_end(args); + + return real64_call_asm(&real64_stack.sp, &real64_stack.arg0, fn); +} + +#endif /* CONFIG_64BIT */ diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c new file mode 100644 index 000000000..4d392e4ed --- /dev/null +++ b/arch/parisc/kernel/ftrace.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Code for tracing calls in Linux kernel. + * Copyright (C) 2009-2016 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_STACK_TRACER + */ + +#include <linux/init.h> +#include <linux/ftrace.h> +#include <linux/uaccess.h> +#include <linux/kprobes.h> +#include <linux/ptrace.h> +#include <linux/jump_label.h> + +#include <asm/assembly.h> +#include <asm/sections.h> +#include <asm/ftrace.h> +#include <asm/patch.h> + +#define __hot __section(".text.hot") + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +static DEFINE_STATIC_KEY_FALSE(ftrace_graph_enable); + +/* + * Hook the return address and push it in the stack of return addrs + * in current thread info. + */ +static void __hot prepare_ftrace_return(unsigned long *parent, + unsigned long self_addr) +{ + unsigned long old; + extern int parisc_return_to_handler; + + if (unlikely(ftrace_graph_is_dead())) + return; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + old = *parent; + + if (!function_graph_enter(old, self_addr, 0, NULL)) + /* activate parisc_return_to_handler() as return point */ + *parent = (unsigned long) &parisc_return_to_handler; +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ + +static ftrace_func_t ftrace_func; + +void notrace __hot ftrace_function_trampoline(unsigned long parent, + unsigned long self_addr, + unsigned long org_sp_gr3, + struct ftrace_regs *fregs) +{ + extern struct ftrace_ops *function_trace_op; + + ftrace_func(self_addr, parent, function_trace_op, fregs); + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + if (static_branch_unlikely(&ftrace_graph_enable)) { + unsigned long *parent_rp; + + /* calculate pointer to %rp in stack */ + parent_rp = (unsigned long *) (org_sp_gr3 - RP_OFFSET); + /* sanity check: parent_rp should hold parent */ + if (*parent_rp != parent) + return; + + prepare_ftrace_return(parent_rp, self_addr); + return; + } +#endif +} + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +int ftrace_enable_ftrace_graph_caller(void) +{ + static_key_enable(&ftrace_graph_enable.key); + return 0; +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + static_key_enable(&ftrace_graph_enable.key); + return 0; +} +#endif + +#ifdef CONFIG_DYNAMIC_FTRACE +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + ftrace_func = func; + return 0; +} + +int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, + unsigned long addr) +{ + return 0; +} + +unsigned long ftrace_call_adjust(unsigned long addr) +{ + return addr+(FTRACE_PATCHABLE_FUNCTION_SIZE-1)*4; +} + +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + u32 insn[FTRACE_PATCHABLE_FUNCTION_SIZE]; + u32 *tramp; + int size, ret, i; + void *ip; + +#ifdef CONFIG_64BIT + unsigned long addr2 = + (unsigned long)dereference_function_descriptor((void *)addr); + + u32 ftrace_trampoline[] = { + 0x73c10208, /* std,ma r1,100(sp) */ + 0x0c2110c1, /* ldd -10(r1),r1 */ + 0xe820d002, /* bve,n (r1) */ + addr2 >> 32, + addr2 & 0xffffffff, + 0xe83f1fd7, /* b,l,n .-14,r1 */ + }; + + u32 ftrace_trampoline_unaligned[] = { + addr2 >> 32, + addr2 & 0xffffffff, + 0x37de0200, /* ldo 100(sp),sp */ + 0x73c13e01, /* std r1,-100(sp) */ + 0x34213ff9, /* ldo -4(r1),r1 */ + 0x50213fc1, /* ldd -20(r1),r1 */ + 0xe820d002, /* bve,n (r1) */ + 0xe83f1fcf, /* b,l,n .-20,r1 */ + }; + + BUILD_BUG_ON(ARRAY_SIZE(ftrace_trampoline_unaligned) > + FTRACE_PATCHABLE_FUNCTION_SIZE); +#else + u32 ftrace_trampoline[] = { + (u32)addr, + 0x6fc10080, /* stw,ma r1,40(sp) */ + 0x48213fd1, /* ldw -18(r1),r1 */ + 0xe820c002, /* bv,n r0(r1) */ + 0xe83f1fdf, /* b,l,n .-c,r1 */ + }; +#endif + + BUILD_BUG_ON(ARRAY_SIZE(ftrace_trampoline) > + FTRACE_PATCHABLE_FUNCTION_SIZE); + + size = sizeof(ftrace_trampoline); + tramp = ftrace_trampoline; + +#ifdef CONFIG_64BIT + if (rec->ip & 0x4) { + size = sizeof(ftrace_trampoline_unaligned); + tramp = ftrace_trampoline_unaligned; + } +#endif + + ip = (void *)(rec->ip + 4 - size); + + ret = copy_from_kernel_nofault(insn, ip, size); + if (ret) + return ret; + + for (i = 0; i < size / 4; i++) { + if (insn[i] != INSN_NOP) + return -EINVAL; + } + + __patch_text_multiple(ip, tramp, size); + return 0; +} + +int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, + unsigned long addr) +{ + u32 insn[FTRACE_PATCHABLE_FUNCTION_SIZE]; + int i; + + for (i = 0; i < ARRAY_SIZE(insn); i++) + insn[i] = INSN_NOP; + + __patch_text((void *)rec->ip, INSN_NOP); + __patch_text_multiple((void *)rec->ip + 4 - sizeof(insn), + insn, sizeof(insn)-4); + return 0; +} +#endif + +#ifdef CONFIG_KPROBES_ON_FTRACE +void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *ops, struct ftrace_regs *fregs) +{ + struct kprobe_ctlblk *kcb; + struct pt_regs *regs; + struct kprobe *p; + int bit; + + bit = ftrace_test_recursion_trylock(ip, parent_ip); + if (bit < 0) + return; + + regs = ftrace_get_regs(fregs); + p = get_kprobe((kprobe_opcode_t *)ip); + if (unlikely(!p) || kprobe_disabled(p)) + goto out; + + if (kprobe_running()) { + kprobes_inc_nmissed_count(p); + goto out; + } + + __this_cpu_write(current_kprobe, p); + + kcb = get_kprobe_ctlblk(); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + regs->iaoq[0] = ip; + regs->iaoq[1] = ip + 4; + + if (!p->pre_handler || !p->pre_handler(p, regs)) { + regs->iaoq[0] = ip + 4; + regs->iaoq[1] = ip + 8; + + if (unlikely(p->post_handler)) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + p->post_handler(p, regs, 0); + } + } + __this_cpu_write(current_kprobe, NULL); +out: + ftrace_test_recursion_unlock(bit); +} +NOKPROBE_SYMBOL(kprobe_ftrace_handler); + +int arch_prepare_kprobe_ftrace(struct kprobe *p) +{ + p->ainsn.insn = NULL; + return 0; +} +#endif diff --git a/arch/parisc/kernel/hardware.c b/arch/parisc/kernel/hardware.c new file mode 100644 index 000000000..357d9cdab --- /dev/null +++ b/arch/parisc/kernel/hardware.c @@ -0,0 +1,1374 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware descriptions for HP 9000 based hardware, including + * system types, SCSI controllers, DMA controllers, HPPB controllers + * and lots more. + * + * Based on the document "PA-RISC 1.1 I/O Firmware Architecture + * Reference Specification", March 7, 1999, version 0.96. This + * is available at + * https://parisc.wiki.kernel.org/index.php/Technical_Documentation + * + * Copyright 1999 by Alex deVries <alex@onefishtwo.ca> + * and copyright 1999 The Puffin Group Inc. + */ + + +#include <asm/hardware.h> +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> + +/* + * HP PARISC Hardware Database + * Access to this database is only possible during bootup + * so don't reference this table after starting the init process + */ + +static struct hp_hardware hp_hardware_list[] __initdata = { + {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)"}, + {HPHW_NPROC,0xB,0x4,0x01,"Technical Shogun (845, 645)"}, + {HPHW_NPROC,0xF,0x4,0x01,"Commercial Shogun (949)"}, + {HPHW_NPROC,0xC,0x4,0x01,"Cheetah (850, 950)"}, + {HPHW_NPROC,0x80,0x4,0x01,"Cheetah (950S)"}, + {HPHW_NPROC,0x81,0x4,0x01,"Jaguar (855, 955)"}, + {HPHW_NPROC,0x82,0x4,0x01,"Cougar (860, 960)"}, + {HPHW_NPROC,0x83,0x4,0x13,"Panther (865, 870, 980)"}, + {HPHW_NPROC,0x100,0x4,0x01,"Burgundy (810)"}, + {HPHW_NPROC,0x101,0x4,0x01,"SilverFox Low (822, 922)"}, + {HPHW_NPROC,0x102,0x4,0x01,"SilverFox High (832, 932)"}, + {HPHW_NPROC,0x103,0x4,0x01,"Lego, SilverLite (815, 808, 920)"}, + {HPHW_NPROC,0x104,0x4,0x03,"SilverBullet Low (842, 948)"}, + {HPHW_NPROC,0x105,0x4,0x03,"SilverBullet High (852, 958)"}, + {HPHW_NPROC,0x106,0x4,0x81,"Oboe"}, + {HPHW_NPROC,0x180,0x4,0x12,"Dragon"}, + {HPHW_NPROC,0x181,0x4,0x13,"Chimera (890, 990, 992)"}, + {HPHW_NPROC,0x182,0x4,0x91,"TNT 100 (891,T500)"}, + {HPHW_NPROC,0x183,0x4,0x91,"TNT 120 (892,T520)"}, + {HPHW_NPROC,0x184,0x4,0x91,"Jade 180 U (893,T540)"}, + {HPHW_NPROC,0x1FF,0x4,0x91,"Hitachi X Processor"}, + {HPHW_NPROC,0x200,0x4,0x81,"Cobra (720)"}, + {HPHW_NPROC,0x201,0x4,0x81,"Coral (750)"}, + {HPHW_NPROC,0x202,0x4,0x81,"King Cobra (730)"}, + {HPHW_NPROC,0x203,0x4,0x81,"Hardball (735/99)"}, + {HPHW_NPROC,0x204,0x4,0x81,"Coral II (755/99)"}, + {HPHW_NPROC,0x205,0x4,0x81,"Coral II (755/125)"}, + {HPHW_NPROC,0x205,0x4,0x91,"Snake Eagle "}, + {HPHW_NPROC,0x206,0x4,0x81,"Snake Cheetah (735/130)"}, + {HPHW_NPROC,0x280,0x4,0x81,"Nova Low (817, 827, 957, 957LX)"}, + {HPHW_NPROC,0x281,0x4,0x81,"Nova High (837, 847, 857, 967, 967LX)"}, + {HPHW_NPROC,0x282,0x4,0x81,"Nova8 (807, 917, 917LX, 927,927LX, 937, 937LX, 947,947LX)"}, + {HPHW_NPROC,0x283,0x4,0x81,"Nova64 (867, 877, 977)"}, + {HPHW_NPROC,0x284,0x4,0x81,"TNova (887, 897, 987)"}, + {HPHW_NPROC,0x285,0x4,0x81,"TNova64"}, + {HPHW_NPROC,0x286,0x4,0x91,"Hydra64 (Nova)"}, + {HPHW_NPROC,0x287,0x4,0x91,"Hydra96 (Nova)"}, + {HPHW_NPROC,0x288,0x4,0x81,"TNova96"}, + {HPHW_NPROC,0x300,0x4,0x81,"Bushmaster (710)"}, + {HPHW_NPROC,0x302,0x4,0x81,"Flounder (705)"}, + {HPHW_NPROC,0x310,0x4,0x81,"Scorpio (715/50)"}, + {HPHW_NPROC,0x311,0x4,0x81,"Scorpio Jr.(715/33)"}, + {HPHW_NPROC,0x312,0x4,0x81,"Strider-50 (715S/50)"}, + {HPHW_NPROC,0x313,0x4,0x81,"Strider-33 (715S/33)"}, + {HPHW_NPROC,0x314,0x4,0x81,"Trailways-50 (715T/50)"}, + {HPHW_NPROC,0x315,0x4,0x81,"Trailways-33 (715T/33)"}, + {HPHW_NPROC,0x316,0x4,0x81,"Scorpio Sr.(715/75)"}, + {HPHW_NPROC,0x317,0x4,0x81,"Scorpio 100 (715/100)"}, + {HPHW_NPROC,0x318,0x4,0x81,"Spectra (725/50)"}, + {HPHW_NPROC,0x319,0x4,0x81,"Spectra (725/75)"}, + {HPHW_NPROC,0x320,0x4,0x81,"Spectra (725/100)"}, + {HPHW_NPROC,0x401,0x4,0x81,"Pace (745i, 747i)"}, + {HPHW_NPROC,0x402,0x4,0x81,"Sidewinder (742i)"}, + {HPHW_NPROC,0x403,0x4,0x81,"Fast Pace"}, + {HPHW_NPROC,0x480,0x4,0x81,"Orville (E23)"}, + {HPHW_NPROC,0x481,0x4,0x81,"Wilbur (E25)"}, + {HPHW_NPROC,0x482,0x4,0x81,"WB-80 (E35)"}, + {HPHW_NPROC,0x483,0x4,0x81,"WB-96 (E45)"}, + {HPHW_NPROC,0x484,0x4,0x81,"UL Proc L-100 (811/D210,D310)"}, + {HPHW_NPROC,0x485,0x4,0x81,"UL Proc L-75 (801/D200)"}, + {HPHW_NPROC,0x501,0x4,0x81,"Merlin L2 132 (9000/778/B132L)"}, + {HPHW_NPROC,0x502,0x4,0x81,"Merlin L2 160 (9000/778/B160L)"}, + {HPHW_NPROC,0x503,0x4,0x81,"Merlin L2+ 132 (9000/778/B132L)"}, + {HPHW_NPROC,0x504,0x4,0x81,"Merlin L2+ 180 (9000/778/B180L)"}, + {HPHW_NPROC,0x505,0x4,0x81,"Raven L2 132 (9000/778/C132L)"}, + {HPHW_NPROC,0x506,0x4,0x81,"Raven L2 160 (9000/779/C160L)"}, + {HPHW_NPROC,0x507,0x4,0x81,"Raven L2 180 (9000/779/C180L)"}, + {HPHW_NPROC,0x508,0x4,0x81,"Raven L2 160 (9000/779/C160L)"}, + {HPHW_NPROC,0x509,0x4,0x81,"712/132 L2 Upgrade"}, + {HPHW_NPROC,0x50A,0x4,0x81,"712/160 L2 Upgrade"}, + {HPHW_NPROC,0x50B,0x4,0x81,"715/132 L2 Upgrade"}, + {HPHW_NPROC,0x50C,0x4,0x81,"715/160 L2 Upgrade"}, + {HPHW_NPROC,0x50D,0x4,0x81,"Rocky2 L2 120"}, + {HPHW_NPROC,0x50E,0x4,0x81,"Rocky2 L2 150"}, + {HPHW_NPROC,0x50F,0x4,0x81,"Anole L2 132 (744)"}, + {HPHW_NPROC,0x510,0x4,0x81,"Anole L2 165 (744)"}, + {HPHW_NPROC,0x511,0x4,0x81,"Kiji L2 132"}, + {HPHW_NPROC,0x512,0x4,0x81,"UL L2 132 (803/D220,D320)"}, + {HPHW_NPROC,0x513,0x4,0x81,"UL L2 160 (813/D220,D320)"}, + {HPHW_NPROC,0x514,0x4,0x81,"Merlin Jr L2 132"}, + {HPHW_NPROC,0x515,0x4,0x81,"Staccato L2 132"}, + {HPHW_NPROC,0x516,0x4,0x81,"Staccato L2 180 (A Class 180)"}, + {HPHW_NPROC,0x580,0x4,0x81,"KittyHawk DC2-100 (K100)"}, + {HPHW_NPROC,0x581,0x4,0x91,"KittyHawk DC3-120 (K210)"}, + {HPHW_NPROC,0x582,0x4,0x91,"KittyHawk DC3 100 (K400)"}, + {HPHW_NPROC,0x583,0x4,0x91,"KittyHawk DC3 120 (K410)"}, + {HPHW_NPROC,0x584,0x4,0x91,"LighteningHawk T120"}, + {HPHW_NPROC,0x585,0x4,0x91,"SkyHawk 100"}, + {HPHW_NPROC,0x586,0x4,0x91,"SkyHawk 120"}, + {HPHW_NPROC,0x587,0x4,0x81,"UL Proc 1-way T'120"}, + {HPHW_NPROC,0x588,0x4,0x91,"UL Proc 2-way T'120"}, + {HPHW_NPROC,0x589,0x4,0x81,"UL Proc 1-way T'100 (821/D250,D350)"}, + {HPHW_NPROC,0x58A,0x4,0x91,"UL Proc 2-way T'100 (831/D250,D350)"}, + {HPHW_NPROC,0x58B,0x4,0x91,"KittyHawk DC2 100 (K200)"}, + {HPHW_NPROC,0x58C,0x4,0x91,"ThunderHawk DC3- 120 1M (K220)"}, + {HPHW_NPROC,0x58D,0x4,0x91,"ThunderHawk DC3 120 1M (K420)"}, + {HPHW_NPROC,0x58E,0x4,0x81,"Raven 120 T'"}, + {HPHW_NPROC,0x58F,0x4,0x91,"Mohawk 160 U 1M DC3 (K450)"}, + {HPHW_NPROC,0x590,0x4,0x91,"Mohawk 180 U 1M DC3 (K460)"}, + {HPHW_NPROC,0x591,0x4,0x91,"Mohawk 200 U 1M DC3"}, + {HPHW_NPROC,0x592,0x4,0x81,"Raven 100 T'"}, + {HPHW_NPROC,0x593,0x4,0x91,"FireHawk 160 U"}, + {HPHW_NPROC,0x594,0x4,0x91,"FireHawk 180 U"}, + {HPHW_NPROC,0x595,0x4,0x91,"FireHawk 220 U"}, + {HPHW_NPROC,0x596,0x4,0x91,"FireHawk 240 U"}, + {HPHW_NPROC,0x597,0x4,0x91,"SPP2000 processor"}, + {HPHW_NPROC,0x598,0x4,0x81,"Raven U 230 (9000/780/C230)"}, + {HPHW_NPROC,0x599,0x4,0x81,"Raven U 240 (9000/780/C240)"}, + {HPHW_NPROC,0x59A,0x4,0x91,"Unlisted but reserved"}, + {HPHW_NPROC,0x59A,0x4,0x81,"Unlisted but reserved"}, + {HPHW_NPROC,0x59B,0x4,0x81,"Raven U 160 (9000/780/C160)"}, + {HPHW_NPROC,0x59C,0x4,0x81,"Raven U 180 (9000/780/C180)"}, + {HPHW_NPROC,0x59D,0x4,0x81,"Raven U 200 (9000/780/C200)"}, + {HPHW_NPROC,0x59E,0x4,0x91,"ThunderHawk T' 120"}, + {HPHW_NPROC,0x59F,0x4,0x91,"Raven U 180+ (9000/780)"}, + {HPHW_NPROC,0x5A0,0x4,0x81,"UL 1w T120 1MB/1MB (841/D260,D360)"}, + {HPHW_NPROC,0x5A1,0x4,0x91,"UL 2w T120 1MB/1MB (851/D260,D360)"}, + {HPHW_NPROC,0x5A2,0x4,0x81,"UL 1w U160 512K/512K (861/D270,D370)"}, + {HPHW_NPROC,0x5A3,0x4,0x91,"UL 2w U160 512K/512K (871/D270,D370)"}, + {HPHW_NPROC,0x5A4,0x4,0x91,"Mohawk 160 U 1M DC3- (K250)"}, + {HPHW_NPROC,0x5A5,0x4,0x91,"Mohawk 180 U 1M DC3- (K260)"}, + {HPHW_NPROC,0x5A6,0x4,0x91,"Mohawk 200 U 1M DC3-"}, + {HPHW_NPROC,0x5A7,0x4,0x81,"UL proc 1-way U160 1M/1M"}, + {HPHW_NPROC,0x5A8,0x4,0x91,"UL proc 2-way U160 1M/1M"}, + {HPHW_NPROC,0x5A9,0x4,0x81,"UL proc 1-way U180 1M/1M"}, + {HPHW_NPROC,0x5AA,0x4,0x91,"UL proc 2-way U180 1M/1M"}, + {HPHW_NPROC,0x5AB,0x4,0x91,"Obsolete"}, + {HPHW_NPROC,0x5AB,0x4,0x81,"Obsolete"}, + {HPHW_NPROC,0x5AC,0x4,0x91,"Obsolete"}, + {HPHW_NPROC,0x5AC,0x4,0x81,"Obsolete"}, + {HPHW_NPROC,0x5AD,0x4,0x91,"BraveHawk 180MHz DC3-"}, + {HPHW_NPROC,0x5AE,0x4,0x91,"BraveHawk 200MHz DC3- (898/K370)"}, + {HPHW_NPROC,0x5AF,0x4,0x91,"BraveHawk 220MHz DC3-"}, + {HPHW_NPROC,0x5B0,0x4,0x91,"BraveHawk 180MHz DC3"}, + {HPHW_NPROC,0x5B1,0x4,0x91,"BraveHawk 200MHz DC3 (899/K570)"}, + {HPHW_NPROC,0x5B2,0x4,0x91,"BraveHawk 220MHz DC3"}, + {HPHW_NPROC,0x5B3,0x4,0x91,"FireHawk 200"}, + {HPHW_NPROC,0x5B4,0x4,0x91,"SPP2500"}, + {HPHW_NPROC,0x5B5,0x4,0x91,"SummitHawk U+"}, + {HPHW_NPROC,0x5B6,0x4,0x91,"DragonHawk U+ 240 DC3"}, + {HPHW_NPROC,0x5B7,0x4,0x91,"DragonHawk U+ 240 DC3-"}, + {HPHW_NPROC,0x5B8,0x4,0x91,"SPP2250 240 MHz"}, + {HPHW_NPROC,0x5B9,0x4,0x81,"UL 1w U+/240 (350/550)"}, + {HPHW_NPROC,0x5BA,0x4,0x91,"UL 2w U+/240 (350/550)"}, + {HPHW_NPROC,0x5BB,0x4,0x81,"AllegroHigh W"}, + {HPHW_NPROC,0x5BC,0x4,0x91,"AllegroLow W"}, + {HPHW_NPROC,0x5BD,0x4,0x91,"Forte W 2-way"}, + {HPHW_NPROC,0x5BE,0x4,0x91,"Prelude W"}, + {HPHW_NPROC,0x5BF,0x4,0x91,"Forte W 4-way"}, + {HPHW_NPROC,0x5C0,0x4,0x91,"M2250"}, + {HPHW_NPROC,0x5C1,0x4,0x91,"M2500"}, + {HPHW_NPROC,0x5C2,0x4,0x91,"Sonata 440"}, + {HPHW_NPROC,0x5C3,0x4,0x91,"Sonata 360"}, + {HPHW_NPROC,0x5C4,0x4,0x91,"Rhapsody 440"}, + {HPHW_NPROC,0x5C5,0x4,0x91,"Rhapsody 360"}, + {HPHW_NPROC,0x5C6,0x4,0x91,"Raven W 360 (9000/780)"}, + {HPHW_NPROC,0x5C7,0x4,0x91,"Halfdome W 440"}, + {HPHW_NPROC,0x5C8,0x4,0x81,"Lego 360 processor"}, + {HPHW_NPROC,0x5C9,0x4,0x91,"Rhapsody DC- 440"}, + {HPHW_NPROC,0x5CA,0x4,0x91,"Rhapsody DC- 360"}, + {HPHW_NPROC,0x5CB,0x4,0x91,"Crescendo 440"}, + {HPHW_NPROC,0x5CC,0x4,0x91,"Prelude W 440"}, + {HPHW_NPROC,0x5CD,0x4,0x91,"SPP2600"}, + {HPHW_NPROC,0x5CE,0x4,0x91,"M2600"}, + {HPHW_NPROC,0x5CF,0x4,0x81,"Allegro W+"}, + {HPHW_NPROC,0x5D0,0x4,0x81,"Kazoo W+"}, + {HPHW_NPROC,0x5D1,0x4,0x91,"Forte W+ 2w"}, + {HPHW_NPROC,0x5D2,0x4,0x91,"Forte W+ 4w"}, + {HPHW_NPROC,0x5D3,0x4,0x91,"Prelude W+ 540"}, + {HPHW_NPROC,0x5D4,0x4,0x91,"Duet W+"}, + {HPHW_NPROC,0x5D5,0x4,0x91,"Crescendo 550"}, + {HPHW_NPROC,0x5D6,0x4,0x81,"Crescendo DC- 440"}, + {HPHW_NPROC,0x5D7,0x4,0x91,"Keystone W+"}, + {HPHW_NPROC,0x5D8,0x4,0x91,"Rhapsody wave 2 W+ DC-"}, + {HPHW_NPROC,0x5D9,0x4,0x91,"Rhapsody wave 2 W+"}, + {HPHW_NPROC,0x5DA,0x4,0x91,"Marcato W+ DC-"}, + {HPHW_NPROC,0x5DB,0x4,0x91,"Marcato W+"}, + {HPHW_NPROC,0x5DC,0x4,0x91,"Allegro W2"}, + {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"}, + {HPHW_NPROC,0x5E3,0x4,0x91,"Crescendo 750 W2"}, + {HPHW_NPROC,0x5E4,0x4,0x91,"Keystone/Matterhorn W2 750"}, + {HPHW_NPROC,0x5E5,0x4,0x91,"PowerBar W+"}, + {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)"}, + {HPHW_NPROC,0x602,0x4,0x81,"Gecko 100 (712/100)"}, + {HPHW_NPROC,0x603,0x4,0x81,"Anole 64 (743/64)"}, + {HPHW_NPROC,0x604,0x4,0x81,"Anole 100 (743/100)"}, + {HPHW_NPROC,0x605,0x4,0x81,"Gecko 120 (712/120)"}, + {HPHW_NPROC,0x606,0x4,0x81,"Gila 80"}, + {HPHW_NPROC,0x607,0x4,0x81,"Gila 100"}, + {HPHW_NPROC,0x608,0x4,0x81,"Gila 120"}, + {HPHW_NPROC,0x609,0x4,0x81,"Scorpio-L 80"}, + {HPHW_NPROC,0x60A,0x4,0x81,"Mirage Jr (715/64)"}, + {HPHW_NPROC,0x60B,0x4,0x81,"Mirage 100"}, + {HPHW_NPROC,0x60C,0x4,0x81,"Mirage 100+"}, + {HPHW_NPROC,0x60D,0x4,0x81,"Electra 100"}, + {HPHW_NPROC,0x60E,0x4,0x81,"Electra 120"}, + {HPHW_NPROC,0x610,0x4,0x81,"Scorpio-L 100"}, + {HPHW_NPROC,0x611,0x4,0x81,"Scorpio-L 120"}, + {HPHW_NPROC,0x612,0x4,0x81,"Spectra-L 80"}, + {HPHW_NPROC,0x613,0x4,0x81,"Spectra-L 100"}, + {HPHW_NPROC,0x614,0x4,0x81,"Spectra-L 120"}, + {HPHW_NPROC,0x615,0x4,0x81,"Piranha 100"}, + {HPHW_NPROC,0x616,0x4,0x81,"Piranha 120"}, + {HPHW_NPROC,0x617,0x4,0x81,"Jason 50"}, + {HPHW_NPROC,0x618,0x4,0x81,"Jason 100"}, + {HPHW_NPROC,0x619,0x4,0x81,"Mirage 80"}, + {HPHW_NPROC,0x61A,0x4,0x81,"SAIC L-80"}, + {HPHW_NPROC,0x61B,0x4,0x81,"Rocky1 L-60"}, + {HPHW_NPROC,0x61C,0x4,0x81,"Anole T (743/T)"}, + {HPHW_NPROC,0x67E,0x4,0x81,"Hitachi Tiny 80"}, + {HPHW_NPROC,0x67F,0x4,0x81,"Hitachi Tiny 64"}, + {HPHW_NPROC,0x700,0x4,0x91,"NEC Aska Processor"}, + {HPHW_NPROC,0x880,0x4,0x91,"Orca Mako"}, + {HPHW_NPROC,0x881,0x4,0x91,"Everest Mako"}, + {HPHW_NPROC,0x882,0x4,0x91,"Rainier/Medel Mako Slow"}, + {HPHW_NPROC,0x883,0x4,0x91,"Rainier/Medel Mako Fast"}, + {HPHW_NPROC,0x884,0x4,0x91,"Mt. Hamilton"}, + {HPHW_NPROC,0x885,0x4,0x91,"Mt. Hamilton DC-"}, + {HPHW_NPROC,0x886,0x4,0x91,"Storm Peak Slow DC-"}, + {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 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)"}, + {HPHW_A_DIRECT, 0x007, 0x0000D, 0x00, "Dino AP"}, + {HPHW_A_DIRECT, 0x009, 0x0000D, 0x00, "Solaris Direct Connect MUX (J2092A)"}, + {HPHW_A_DIRECT, 0x00A, 0x0000D, 0x00, "Solaris RS-422/423 MUX (J2093A)"}, + {HPHW_A_DIRECT, 0x00B, 0x0000D, 0x00, "Solaris RS-422/423 Quadriloops MUX"}, + {HPHW_A_DIRECT, 0x00C, 0x0000D, 0x00, "Solaris Modem MUX (J2094A)"}, + {HPHW_A_DIRECT, 0x00D, 0x0000D, 0x00, "Twins Direct Connect MUX"}, + {HPHW_A_DIRECT, 0x00E, 0x0000D, 0x00, "Twins Modem MUX"}, + {HPHW_A_DIRECT, 0x00F, 0x0000D, 0x00, "Nautilus RS-485"}, + {HPHW_A_DIRECT, 0x010, 0x0000D, 0x00, "UltraLight CAP/MUX"}, + {HPHW_A_DIRECT, 0x015, 0x0000D, 0x00, "Eole CAP/MUX"}, + {HPHW_A_DIRECT, 0x024, 0x0000D, 0x00, "Sahp Kiuh AP/MUX"}, + {HPHW_A_DIRECT, 0x034, 0x0000D, 0x00, "Sahp Kiuh Low AP/MUX"}, + {HPHW_A_DIRECT, 0x044, 0x0000D, 0x00, "Sahp Baat Kiuh AP/MUX"}, + {HPHW_A_DIRECT, 0x004, 0x0000E, 0x80, "Burgundy RS-232"}, + {HPHW_A_DIRECT, 0x005, 0x0000E, 0x80, "Silverfox RS-232"}, + {HPHW_A_DIRECT, 0x006, 0x0000E, 0x80, "Lego RS-232"}, + {HPHW_A_DIRECT, 0x004, 0x0000F, 0x00, "Peacock Graphics"}, + {HPHW_A_DIRECT, 0x004, 0x00014, 0x80, "Burgundy HIL"}, + {HPHW_A_DIRECT, 0x005, 0x00014, 0x80, "Peacock HIL"}, + {HPHW_A_DIRECT, 0x004, 0x00015, 0x80, "Leonardo"}, + {HPHW_A_DIRECT, 0x004, 0x00016, 0x80, "HP-PB HRM"}, + {HPHW_A_DIRECT, 0x004, 0x00017, 0x80, "HP-PB HRC"}, + {HPHW_A_DIRECT, 0x004, 0x0003A, 0x80, "Skunk Centronics (28655A)"}, + {HPHW_A_DIRECT, 0x024, 0x0003A, 0x80, "Sahp Kiuh Centronics"}, + {HPHW_A_DIRECT, 0x044, 0x0003A, 0x80, "Sahp Baat Kiuh Centronics"}, + {HPHW_A_DIRECT, 0x004, 0x0004E, 0x80, "AT&T DataKit (AMSO)"}, + {HPHW_A_DIRECT, 0x004, 0x0009B, 0x80, "Test&Meas GSC HPIB"}, + {HPHW_A_DIRECT, 0x004, 0x000A8, 0x00, "Rocky2-120 Front Keyboard"}, + {HPHW_A_DIRECT, 0x005, 0x000A8, 0x00, "Rocky2-150 Front Keyboard"}, + {HPHW_A_DIRECT, 0x004, 0x00101, 0x80, "Hitachi Console Module"}, + {HPHW_A_DIRECT, 0x004, 0x00102, 0x80, "Hitachi Boot Module"}, + {HPHW_A_DIRECT, 0x004, 0x00203, 0x80, "MELCO HBMLA MLAIT"}, + {HPHW_A_DIRECT, 0x004, 0x00208, 0x80, "MELCO HBDPC"}, + {HPHW_A_DIRECT, 0x004, 0x00300, 0x00, "DCI TWINAX TERM IO MUX"}, + {HPHW_A_DMA, 0x004, 0x00039, 0x80, "Skunk SCSI (28655A)"}, + {HPHW_A_DMA, 0x005, 0x00039, 0x80, "KittyHawk CSY Core SCSI"}, + {HPHW_A_DMA, 0x014, 0x00039, 0x80, "Diablo SCSI"}, + {HPHW_A_DMA, 0x024, 0x00039, 0x80, "Sahp Kiuh SCSI"}, + {HPHW_A_DMA, 0x034, 0x00039, 0x80, "Sahp Kiuh Low SCSI"}, + {HPHW_A_DMA, 0x044, 0x00039, 0x80, "Sahp Baat Kiuh SCSI"}, + {HPHW_A_DMA, 0x004, 0x0003B, 0x80, "Wizard SCSI"}, + {HPHW_A_DMA, 0x005, 0x0003B, 0x80, "KittyHawk CSY Core FW-SCSI"}, + {HPHW_A_DMA, 0x006, 0x0003B, 0x80, "Symbios EPIC FW-SCSI"}, + {HPHW_A_DMA, 0x004, 0x00040, 0x80, "HP-PB Shazam HPIB (28650A)"}, + {HPHW_A_DMA, 0x005, 0x00040, 0x80, "Burgundy HPIB"}, + {HPHW_A_DMA, 0x004, 0x00041, 0x80, "HP-PB HP-FL"}, + {HPHW_A_DMA, 0x004, 0x00042, 0x80, "HP-PB LoQuix HPIB (28650B)"}, + {HPHW_A_DMA, 0x004, 0x00043, 0x80, "HP-PB Crypt LoQuix"}, + {HPHW_A_DMA, 0x004, 0x00044, 0x80, "HP-PB Shazam GPIO (28651A)"}, + {HPHW_A_DMA, 0x004, 0x00045, 0x80, "HP-PB LoQuix GPIO"}, + {HPHW_A_DMA, 0x004, 0x00046, 0x80, "2-Port X.25 NIO_ACC (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x00047, 0x80, "4-Port X.25 NIO_ACC (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x0004B, 0x80, "LGB Control"}, + {HPHW_A_DMA, 0x004, 0x0004C, 0x80, "Martian RTI (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x0004D, 0x80, "ACC Mux (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x00050, 0x80, "Lanbrusca 802.3 (36967A)"}, + {HPHW_A_DMA, 0x004, 0x00056, 0x80, "HP-PB LoQuix FDDI"}, + {HPHW_A_DMA, 0x004, 0x00057, 0x80, "HP-PB LoQuix FDDI (28670A)"}, + {HPHW_A_DMA, 0x004, 0x0005E, 0x00, "Gecko Add-on Token Ring"}, + {HPHW_A_DMA, 0x012, 0x00089, 0x80, "Barracuda Add-on FW-SCSI"}, + {HPHW_A_DMA, 0x013, 0x00089, 0x80, "Bluefish Add-on FW-SCSI"}, + {HPHW_A_DMA, 0x014, 0x00089, 0x80, "Shrike Add-on FW-SCSI"}, + {HPHW_A_DMA, 0x015, 0x00089, 0x80, "KittyHawk GSY Core FW-SCSI"}, + {HPHW_A_DMA, 0x017, 0x00089, 0x80, "Shrike Jade Add-on FW-SCSI (A3644A)"}, + {HPHW_A_DMA, 0x01F, 0x00089, 0x80, "SkyHawk 100/120 FW-SCSI"}, + {HPHW_A_DMA, 0x027, 0x00089, 0x80, "Piranha 100 FW-SCSI"}, + {HPHW_A_DMA, 0x032, 0x00089, 0x80, "Raven T' Core FW-SCSI"}, + {HPHW_A_DMA, 0x03B, 0x00089, 0x80, "Raven U/L2 Core FW-SCSI"}, + {HPHW_A_DMA, 0x03C, 0x00089, 0x80, "Merlin 132 Core FW-SCSI"}, + {HPHW_A_DMA, 0x03D, 0x00089, 0x80, "Merlin 160 Core FW-SCSI"}, + {HPHW_A_DMA, 0x044, 0x00089, 0x80, "Mohawk Core FW-SCSI"}, + {HPHW_A_DMA, 0x051, 0x00089, 0x80, "Firehawk FW-SCSI"}, + {HPHW_A_DMA, 0x058, 0x00089, 0x80, "FireHawk 200 FW-SCSI"}, + {HPHW_A_DMA, 0x05C, 0x00089, 0x80, "SummitHawk 230 Ultra-SCSI"}, + {HPHW_A_DMA, 0x014, 0x00091, 0x80, "Baby Hugo Add-on Net FC (A3406A)"}, + {HPHW_A_DMA, 0x020, 0x00091, 0x80, "Baby Jade Add-on Net FC (A3638A)"}, + {HPHW_A_DMA, 0x004, 0x00092, 0x80, "GSC+ YLIASTER ATM"}, + {HPHW_A_DMA, 0x004, 0x00095, 0x80, "Hamlyn GSC+ Network Card"}, + {HPHW_A_DMA, 0x004, 0x00098, 0x80, "Lo-fat Emulator"}, + {HPHW_A_DMA, 0x004, 0x0009A, 0x80, "GSC+ Venus ATM"}, + {HPHW_A_DMA, 0x005, 0x0009A, 0x80, "GSC+ Samorobrive ATM"}, + {HPHW_A_DMA, 0x004, 0x0009D, 0x80, "HP HSC-PCI Cards"}, + {HPHW_A_DMA, 0x004, 0x0009E, 0x80, "Alaxis GSC+ 155Mb ATM"}, + {HPHW_A_DMA, 0x005, 0x0009E, 0x80, "Alaxis GSC+ 622Mb ATM"}, + {HPHW_A_DMA, 0x05C, 0x0009F, 0x80, "SummitHawk 230 USB"}, + {HPHW_A_DMA, 0x05C, 0x000A0, 0x80, "SummitHawk 230 100BaseT"}, + {HPHW_A_DMA, 0x015, 0x000A7, 0x80, "Baby Hugo Add-on mass FC (A3404A)"}, + {HPHW_A_DMA, 0x018, 0x000A7, 0x80, "Mombasa GS Add-on mass FC (A3591)"}, + {HPHW_A_DMA, 0x021, 0x000A7, 0x80, "Baby Jade Add-on mass FC (A3636A)"}, + {HPHW_A_DMA, 0x004, 0x00201, 0x80, "MELCO HCMAP"}, + {HPHW_A_DMA, 0x004, 0x00202, 0x80, "MELCO HBMLA MLAMA"}, + {HPHW_A_DMA, 0x004, 0x00205, 0x80, "MELCO HBRFU"}, + {HPHW_A_DMA, 0x004, 0x00380, 0x80, "Interphase NIO-FC"}, + {HPHW_A_DMA, 0x004, 0x00381, 0x80, "Interphase NIO-ATM"}, + {HPHW_A_DMA, 0x004, 0x00382, 0x80, "Interphase NIO-100BaseTX"}, + {HPHW_BA, 0x004, 0x00070, 0x0, "Cobra Core BA"}, + {HPHW_BA, 0x005, 0x00070, 0x0, "Coral Core BA"}, + {HPHW_BA, 0x006, 0x00070, 0x0, "Bushmaster Core BA"}, + {HPHW_BA, 0x007, 0x00070, 0x0, "Scorpio Core BA"}, + {HPHW_BA, 0x008, 0x00070, 0x0, "Flounder Core BA"}, + {HPHW_BA, 0x009, 0x00070, 0x0, "Outfield Core BA"}, + {HPHW_BA, 0x00A, 0x00070, 0x0, "CoralII Core BA"}, + {HPHW_BA, 0x00B, 0x00070, 0x0, "Scorpio Jr. Core BA"}, + {HPHW_BA, 0x00C, 0x00070, 0x0, "Strider-50 Core BA"}, + {HPHW_BA, 0x00D, 0x00070, 0x0, "Strider-33 Core BA"}, + {HPHW_BA, 0x00E, 0x00070, 0x0, "Trailways-50 Core BA"}, + {HPHW_BA, 0x00F, 0x00070, 0x0, "Trailways-33 Core BA"}, + {HPHW_BA, 0x010, 0x00070, 0x0, "Pace Core BA"}, + {HPHW_BA, 0x011, 0x00070, 0x0, "Sidewinder Core BA"}, + {HPHW_BA, 0x019, 0x00070, 0x0, "Scorpio Sr. Core BA"}, + {HPHW_BA, 0x020, 0x00070, 0x0, "Scorpio 100 Core BA"}, + {HPHW_BA, 0x021, 0x00070, 0x0, "Spectra 50 Core BA"}, + {HPHW_BA, 0x022, 0x00070, 0x0, "Spectra 75 Core BA"}, + {HPHW_BA, 0x023, 0x00070, 0x0, "Spectra 100 Core BA"}, + {HPHW_BA, 0x024, 0x00070, 0x0, "Fast Pace Core BA"}, + {HPHW_BA, 0x026, 0x00070, 0x0, "CoralII Jaguar Core BA"}, + {HPHW_BA, 0x004, 0x00076, 0x0, "Cobra EISA BA"}, + {HPHW_BA, 0x005, 0x00076, 0x0, "Coral EISA BA"}, + {HPHW_BA, 0x007, 0x00076, 0x0, "Scorpio EISA BA"}, + {HPHW_BA, 0x00A, 0x00076, 0x0, "CoralII EISA BA"}, + {HPHW_BA, 0x00B, 0x00076, 0x0, "Scorpio Jr. EISA BA"}, + {HPHW_BA, 0x00C, 0x00076, 0x0, "Strider-50 Core EISA"}, + {HPHW_BA, 0x00D, 0x00076, 0x0, "Strider-33 Core EISA"}, + {HPHW_BA, 0x00E, 0x00076, 0x0, "Trailways-50 Core EISA"}, + {HPHW_BA, 0x00F, 0x00076, 0x0, "Trailways-33 Core EISA"}, + {HPHW_BA, 0x010, 0x00076, 0x0, "Pace Core EISA"}, + {HPHW_BA, 0x019, 0x00076, 0x0, "Scorpio Sr. EISA BA"}, + {HPHW_BA, 0x020, 0x00076, 0x0, "Scorpio 100 EISA BA"}, + {HPHW_BA, 0x021, 0x00076, 0x0, "Spectra 50 EISA BA"}, + {HPHW_BA, 0x022, 0x00076, 0x0, "Spectra 75 EISA BA"}, + {HPHW_BA, 0x023, 0x00076, 0x0, "Spectra 100 EISA BA"}, + {HPHW_BA, 0x026, 0x00076, 0x0, "CoralII Jaguar EISA BA"}, + {HPHW_BA, 0x010, 0x00078, 0x0, "Pace VME BA"}, + {HPHW_BA, 0x011, 0x00078, 0x0, "Sidewinder VME BA"}, + {HPHW_BA, 0x01A, 0x00078, 0x0, "Anole 64 VME BA"}, + {HPHW_BA, 0x01B, 0x00078, 0x0, "Anole 100 VME BA"}, + {HPHW_BA, 0x024, 0x00078, 0x0, "Fast Pace VME BA"}, + {HPHW_BA, 0x034, 0x00078, 0x0, "Anole T VME BA"}, + {HPHW_BA, 0x04A, 0x00078, 0x0, "Anole L2 132 VME BA"}, + {HPHW_BA, 0x04C, 0x00078, 0x0, "Anole L2 165 VME BA"}, + {HPHW_BA, 0x011, 0x00081, 0x0, "WB-96 Core BA"}, + {HPHW_BA, 0x012, 0x00081, 0x0, "Orville UX Core BA"}, + {HPHW_BA, 0x013, 0x00081, 0x0, "Wilbur UX Core BA"}, + {HPHW_BA, 0x014, 0x00081, 0x0, "WB-80 Core BA"}, + {HPHW_BA, 0x015, 0x00081, 0x0, "KittyHawk GSY Core BA"}, + {HPHW_BA, 0x016, 0x00081, 0x0, "Gecko Core BA"}, + {HPHW_BA, 0x018, 0x00081, 0x0, "Gecko Optional BA"}, + {HPHW_BA, 0x01A, 0x00081, 0x0, "Anole 64 Core BA"}, + {HPHW_BA, 0x01B, 0x00081, 0x0, "Anole 100 Core BA"}, + {HPHW_BA, 0x01C, 0x00081, 0x0, "Gecko 80 Core BA"}, + {HPHW_BA, 0x01D, 0x00081, 0x0, "Gecko 100 Core BA"}, + {HPHW_BA, 0x01F, 0x00081, 0x0, "SkyHawk 100/120 Core BA"}, + {HPHW_BA, 0x027, 0x00081, 0x0, "Piranha 100 Core BA"}, + {HPHW_BA, 0x028, 0x00081, 0x0, "Mirage Jr Core BA"}, + {HPHW_BA, 0x029, 0x00081, 0x0, "Mirage Core BA"}, + {HPHW_BA, 0x02A, 0x00081, 0x0, "Electra Core BA"}, + {HPHW_BA, 0x02B, 0x00081, 0x0, "Mirage 80 Core BA"}, + {HPHW_BA, 0x02C, 0x00081, 0x0, "Mirage 100+ Core BA"}, + {HPHW_BA, 0x02E, 0x00081, 0x0, "UL 350 Lasi Core BA"}, + {HPHW_BA, 0x02F, 0x00081, 0x0, "UL 550 Lasi Core BA"}, + {HPHW_BA, 0x032, 0x00081, 0x0, "Raven T' Core BA"}, + {HPHW_BA, 0x033, 0x00081, 0x0, "Anole T Core BA"}, + {HPHW_BA, 0x034, 0x00081, 0x0, "SAIC L-80 Core BA"}, + {HPHW_BA, 0x035, 0x00081, 0x0, "PCX-L2 712/132 Core BA"}, + {HPHW_BA, 0x036, 0x00081, 0x0, "PCX-L2 712/160 Core BA"}, + {HPHW_BA, 0x03B, 0x00081, 0x0, "Raven U/L2 Core BA"}, + {HPHW_BA, 0x03C, 0x00081, 0x0, "Merlin 132 Core BA"}, + {HPHW_BA, 0x03D, 0x00081, 0x0, "Merlin 160 Core BA"}, + {HPHW_BA, 0x03E, 0x00081, 0x0, "Merlin+ 132 Core BA"}, + {HPHW_BA, 0x03F, 0x00081, 0x0, "Merlin+ 180 Core BA"}, + {HPHW_BA, 0x044, 0x00081, 0x0, "Mohawk Core BA"}, + {HPHW_BA, 0x045, 0x00081, 0x0, "Rocky1 Core BA"}, + {HPHW_BA, 0x046, 0x00081, 0x0, "Rocky2 120 Core BA"}, + {HPHW_BA, 0x047, 0x00081, 0x0, "Rocky2 150 Core BA"}, + {HPHW_BA, 0x04B, 0x00081, 0x0, "Anole L2 132 Core BA"}, + {HPHW_BA, 0x04D, 0x00081, 0x0, "Anole L2 165 Core BA"}, + {HPHW_BA, 0x04E, 0x00081, 0x0, "Kiji L2 132 Core BA"}, + {HPHW_BA, 0x050, 0x00081, 0x0, "Merlin Jr 132 Core BA"}, + {HPHW_BA, 0x051, 0x00081, 0x0, "Firehawk Core BA"}, + {HPHW_BA, 0x056, 0x00081, 0x0, "Raven+ w SE FWSCSI Core BA"}, + {HPHW_BA, 0x057, 0x00081, 0x0, "Raven+ w Diff FWSCSI Core BA"}, + {HPHW_BA, 0x058, 0x00081, 0x0, "FireHawk 200 Core BA"}, + {HPHW_BA, 0x05C, 0x00081, 0x0, "SummitHawk 230 Core BA"}, + {HPHW_BA, 0x05E, 0x00081, 0x0, "Staccato 132 Core BA"}, + {HPHW_BA, 0x05E, 0x00081, 0x0, "Staccato 180 Core BA"}, + {HPHW_BA, 0x05F, 0x00081, 0x0, "Staccato 180 Lasi"}, + {HPHW_BA, 0x800, 0x00081, 0x0, "Hitachi Tiny 64 Core BA"}, + {HPHW_BA, 0x801, 0x00081, 0x0, "Hitachi Tiny 80 Core BA"}, + {HPHW_BA, 0x004, 0x0008B, 0x0, "Anole Optional PCMCIA BA"}, + {HPHW_BA, 0x004, 0x0008E, 0x0, "GSC ITR Wax BA"}, + {HPHW_BA, 0x00C, 0x0008E, 0x0, "Gecko Optional Wax BA"}, + {HPHW_BA, 0x010, 0x0008E, 0x0, "Pace Wax BA"}, + {HPHW_BA, 0x011, 0x0008E, 0x0, "SuperPace Wax BA"}, + {HPHW_BA, 0x012, 0x0008E, 0x0, "Mirage Jr Wax BA"}, + {HPHW_BA, 0x013, 0x0008E, 0x0, "Mirage Wax BA"}, + {HPHW_BA, 0x014, 0x0008E, 0x0, "Electra Wax BA"}, + {HPHW_BA, 0x017, 0x0008E, 0x0, "Raven Backplane Wax BA"}, + {HPHW_BA, 0x01E, 0x0008E, 0x0, "Raven T' Wax BA"}, + {HPHW_BA, 0x01F, 0x0008E, 0x0, "SkyHawk Wax BA"}, + {HPHW_BA, 0x023, 0x0008E, 0x0, "Rocky1 Wax BA"}, + {HPHW_BA, 0x02B, 0x0008E, 0x0, "Mirage 80 Wax BA"}, + {HPHW_BA, 0x02C, 0x0008E, 0x0, "Mirage 100+ Wax BA"}, + {HPHW_BA, 0x030, 0x0008E, 0x0, "UL 350 Core Wax BA"}, + {HPHW_BA, 0x031, 0x0008E, 0x0, "UL 550 Core Wax BA"}, + {HPHW_BA, 0x034, 0x0008E, 0x0, "SAIC L-80 Wax BA"}, + {HPHW_BA, 0x03A, 0x0008E, 0x0, "Merlin+ Wax BA"}, + {HPHW_BA, 0x040, 0x0008E, 0x0, "Merlin 132 Wax BA"}, + {HPHW_BA, 0x041, 0x0008E, 0x0, "Merlin 160 Wax BA"}, + {HPHW_BA, 0x043, 0x0008E, 0x0, "Merlin 132/160 Wax BA"}, + {HPHW_BA, 0x052, 0x0008E, 0x0, "Raven+ Hi Power Backplane w/EISA Wax BA"}, + {HPHW_BA, 0x054, 0x0008E, 0x0, "Raven+ Lo Power Backplane w/EISA Wax BA"}, + {HPHW_BA, 0x059, 0x0008E, 0x0, "FireHawk 200 Wax BA"}, + {HPHW_BA, 0x05A, 0x0008E, 0x0, "Raven+ L2 Backplane w/EISA Wax BA"}, + {HPHW_BA, 0x05D, 0x0008E, 0x0, "SummitHawk Wax BA"}, + {HPHW_BA, 0x800, 0x0008E, 0x0, "Hitachi Tiny 64 Wax BA"}, + {HPHW_BA, 0x801, 0x0008E, 0x0, "Hitachi Tiny 80 Wax BA"}, + {HPHW_BA, 0x011, 0x00090, 0x0, "SuperPace Wax EISA BA"}, + {HPHW_BA, 0x017, 0x00090, 0x0, "Raven Backplane Wax EISA BA"}, + {HPHW_BA, 0x01E, 0x00090, 0x0, "Raven T' Wax EISA BA"}, + {HPHW_BA, 0x01F, 0x00090, 0x0, "SkyHawk 100/120 Wax EISA BA"}, + {HPHW_BA, 0x027, 0x00090, 0x0, "Piranha 100 Wax EISA BA"}, + {HPHW_BA, 0x028, 0x00090, 0x0, "Mirage Jr Wax EISA BA"}, + {HPHW_BA, 0x029, 0x00090, 0x0, "Mirage Wax EISA BA"}, + {HPHW_BA, 0x02A, 0x00090, 0x0, "Electra Wax EISA BA"}, + {HPHW_BA, 0x02B, 0x00090, 0x0, "Mirage 80 Wax EISA BA"}, + {HPHW_BA, 0x02C, 0x00090, 0x0, "Mirage 100+ Wax EISA BA"}, + {HPHW_BA, 0x030, 0x00090, 0x0, "UL 350 Wax EISA BA"}, + {HPHW_BA, 0x031, 0x00090, 0x0, "UL 550 Wax EISA BA"}, + {HPHW_BA, 0x034, 0x00090, 0x0, "SAIC L-80 Wax EISA BA"}, + {HPHW_BA, 0x03A, 0x00090, 0x0, "Merlin+ Wax EISA BA"}, + {HPHW_BA, 0x040, 0x00090, 0x0, "Merlin 132 Wax EISA BA"}, + {HPHW_BA, 0x041, 0x00090, 0x0, "Merlin 160 Wax EISA BA"}, + {HPHW_BA, 0x043, 0x00090, 0x0, "Merlin 132/160 Wax EISA BA"}, + {HPHW_BA, 0x052, 0x00090, 0x0, "Raven Hi Power Backplane Wax EISA BA"}, + {HPHW_BA, 0x054, 0x00090, 0x0, "Raven Lo Power Backplane Wax EISA BA"}, + {HPHW_BA, 0x059, 0x00090, 0x0, "FireHawk 200 Wax EISA BA"}, + {HPHW_BA, 0x05A, 0x00090, 0x0, "Raven L2 Backplane Wax EISA BA"}, + {HPHW_BA, 0x05D, 0x00090, 0x0, "SummitHawk Wax EISA BA"}, + {HPHW_BA, 0x800, 0x00090, 0x0, "Hitachi Tiny 64 Wax EISA BA"}, + {HPHW_BA, 0x801, 0x00090, 0x0, "Hitachi Tiny 80 Wax EISA BA"}, + {HPHW_BA, 0x01A, 0x00093, 0x0, "Anole 64 TIMI BA"}, + {HPHW_BA, 0x01B, 0x00093, 0x0, "Anole 100 TIMI BA"}, + {HPHW_BA, 0x034, 0x00093, 0x0, "Anole T TIMI BA"}, + {HPHW_BA, 0x04A, 0x00093, 0x0, "Anole L2 132 TIMI BA"}, + {HPHW_BA, 0x04C, 0x00093, 0x0, "Anole L2 165 TIMI BA"}, + {HPHW_BA, 0x582, 0x000A5, 0x00, "Epic PCI Bridge"}, + {HPHW_BCPORT, 0x504, 0x00000, 0x00, "Phantom PseudoBC GSC+ Port"}, + {HPHW_BCPORT, 0x505, 0x00000, 0x00, "Phantom PseudoBC GSC+ Port"}, + {HPHW_BCPORT, 0x503, 0x0000C, 0x00, "Java BC GSC+ Port"}, + {HPHW_BCPORT, 0x57F, 0x0000C, 0x00, "Hitachi Ghostview GSC+ Port"}, + {HPHW_BCPORT, 0x501, 0x0000C, 0x00, "U2-IOA BC GSC+ Port"}, + {HPHW_BCPORT, 0x502, 0x0000C, 0x00, "Uturn-IOA BC GSC+ Port"}, + {HPHW_BCPORT, 0x780, 0x0000C, 0x00, "Astro BC Ropes Port"}, + {HPHW_BCPORT, 0x506, 0x0000C, 0x00, "NEC-IOS BC HSC Port"}, + {HPHW_BCPORT, 0x004, 0x0000C, 0x00, "Cheetah BC SMB Port"}, + {HPHW_BCPORT, 0x006, 0x0000C, 0x00, "Cheetah BC MID_BUS Port"}, + {HPHW_BCPORT, 0x005, 0x0000C, 0x00, "Condor BC MID_BUS Port"}, + {HPHW_BCPORT, 0x100, 0x0000C, 0x00, "Condor BC HP-PB Port"}, + {HPHW_BCPORT, 0x184, 0x0000C, 0x00, "Summit BC Port"}, + {HPHW_BCPORT, 0x101, 0x0000C, 0x00, "Summit BC HP-PB Port"}, + {HPHW_BCPORT, 0x102, 0x0000C, 0x00, "HP-PB Port (prefetch)"}, + {HPHW_BCPORT, 0x500, 0x0000C, 0x00, "Gecko BOA BC GSC+ Port"}, + {HPHW_BCPORT, 0x103, 0x0000C, 0x00, "Gecko BOA BC HP-PB Port"}, + {HPHW_BCPORT, 0x507, 0x0000C, 0x00, "Keyaki BC GSC+ Port"}, + {HPHW_BCPORT, 0x508, 0x0000C, 0x00, "Keyaki-DX BC GSC+ Port"}, + {HPHW_BCPORT, 0x584, 0x0000C, 0x10, "DEW BC Runway Port"}, + {HPHW_BCPORT, 0x800, 0x0000C, 0x10, "DEW BC Merced Port"}, + {HPHW_BCPORT, 0x801, 0x0000C, 0x10, "SMC Bus Interface Merced Bus0"}, + {HPHW_BCPORT, 0x802, 0x0000C, 0x10, "SMC Bus INterface Merced Bus1"}, + {HPHW_BCPORT, 0x803, 0x0000C, 0x10, "IKE I/O BC Merced Port"}, + {HPHW_BCPORT, 0x781, 0x0000C, 0x00, "IKE I/O BC Ropes Port"}, + {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"}, + {HPHW_BRIDGE, 0x583, 0x000A5, 0x00, "Saga PCI Bridge"}, + {HPHW_BRIDGE, 0x783, 0x0000A, 0x00, "Mercury PCI Bridge"}, + {HPHW_BRIDGE, 0x784, 0x0000A, 0x00, "Quicksilver AGP Bridge"}, + {HPHW_B_DMA, 0x004, 0x00018, 0x00, "Parallel I/O"}, + {HPHW_B_DMA, 0x004, 0x00019, 0x00, "Parallel RDB"}, + {HPHW_B_DMA, 0x004, 0x00020, 0x80, "MID_BUS PSI"}, + {HPHW_B_DMA, 0x004, 0x0002F, 0x80, "HP-PB Transit PSI (36960A)"}, + {HPHW_B_DMA, 0x008, 0x00051, 0x80, "HP-PB Transit 802.3"}, + {HPHW_B_DMA, 0x004, 0x00052, 0x80, "Miura LAN/Console (J2146A)"}, + {HPHW_B_DMA, 0x008, 0x00058, 0x80, "HP-PB Transit 802.4"}, + {HPHW_B_DMA, 0x005, 0x00060, 0x80, "KittyHawk CSY Core LAN/Console"}, + {HPHW_B_DMA, 0x014, 0x00060, 0x80, "Diablo LAN/Console"}, + {HPHW_B_DMA, 0x054, 0x00060, 0x80, "Countach LAN/Console"}, + {HPHW_B_DMA, 0x004, 0x00094, 0x80, "KittyHawk GSC+ Exerciser"}, + {HPHW_B_DMA, 0x004, 0x00100, 0x80, "HP-PB HF Interface"}, + {HPHW_B_DMA, 0x000, 0x00206, 0x80, "MELCO HMPHA"}, + {HPHW_B_DMA, 0x005, 0x00206, 0x80, "MELCO HMPHA_10"}, + {HPHW_B_DMA, 0x006, 0x00206, 0x80, "MELCO HMQHA"}, + {HPHW_B_DMA, 0x007, 0x00206, 0x80, "MELCO HMQHA_10"}, + {HPHW_B_DMA, 0x004, 0x207, 0x80, "MELCO HNDWA MDWS-70"}, + {HPHW_CIO, 0x004, 0x00010, 0x00, "VLSI CIO"}, + {HPHW_CIO, 0x005, 0x00010, 0x00, "Silverfox CIO"}, + {HPHW_CIO, 0x006, 0x00010, 0x00, "Emerald CIO"}, + {HPHW_CIO, 0x008, 0x00010, 0x00, "Discrete CIO"}, + {HPHW_CONSOLE, 0x004, 0x0001C, 0x00, "Cheetah console"}, + {HPHW_CONSOLE, 0x005, 0x0001C, 0x00, "Emerald console"}, + {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)"}, + {HPHW_FIO, 0x004, 0x00071, 0x0, "Cobra Core SCSI"}, + {HPHW_FIO, 0x005, 0x00071, 0x0, "Coral Core SCSI"}, + {HPHW_FIO, 0x006, 0x00071, 0x0, "Bushmaster Core SCSI"}, + {HPHW_FIO, 0x007, 0x00071, 0x0, "Scorpio Core SCSI"}, + {HPHW_FIO, 0x008, 0x00071, 0x0, "Flounder Core SCSI"}, + {HPHW_FIO, 0x009, 0x00071, 0x0, "Outfield Core SCSI"}, + {HPHW_FIO, 0x00A, 0x00071, 0x0, "CoralII Core SCSI"}, + {HPHW_FIO, 0x00B, 0x00071, 0x0, "Scorpio Jr. Core SCSI"}, + {HPHW_FIO, 0x00C, 0x00071, 0x0, "Strider-50 Core SCSI"}, + {HPHW_FIO, 0x00D, 0x00071, 0x0, "Strider-33 Core SCSI"}, + {HPHW_FIO, 0x00E, 0x00071, 0x0, "Trailways-50 Core SCSI"}, + {HPHW_FIO, 0x00F, 0x00071, 0x0, "Trailways-33 Core SCSI"}, + {HPHW_FIO, 0x010, 0x00071, 0x0, "Pace Core SCSI"}, + {HPHW_FIO, 0x011, 0x00071, 0x0, "Sidewinder Core SCSI"}, + {HPHW_FIO, 0x019, 0x00071, 0x0, "Scorpio Sr. Core SCSI"}, + {HPHW_FIO, 0x020, 0x00071, 0x0, "Scorpio 100 Core SCSI"}, + {HPHW_FIO, 0x021, 0x00071, 0x0, "Spectra 50 Core SCSI"}, + {HPHW_FIO, 0x022, 0x00071, 0x0, "Spectra 75 Core SCSI"}, + {HPHW_FIO, 0x023, 0x00071, 0x0, "Spectra 100 Core SCSI"}, + {HPHW_FIO, 0x024, 0x00071, 0x0, "Fast Pace Core SCSI"}, + {HPHW_FIO, 0x026, 0x00071, 0x0, "CoralII Jaguar Core SCSI"}, + {HPHW_FIO, 0x004, 0x00072, 0x0, "Cobra Core LAN (802.3)"}, + {HPHW_FIO, 0x005, 0x00072, 0x0, "Coral Core LAN (802.3)"}, + {HPHW_FIO, 0x006, 0x00072, 0x0, "Bushmaster Core LAN (802.3)"}, + {HPHW_FIO, 0x007, 0x00072, 0x0, "Scorpio Core LAN (802.3)"}, + {HPHW_FIO, 0x008, 0x00072, 0x0, "Flounder Core LAN (802.3)"}, + {HPHW_FIO, 0x009, 0x00072, 0x0, "Outfield Core LAN (802.3)"}, + {HPHW_FIO, 0x00A, 0x00072, 0x0, "CoralII Core LAN (802.3)"}, + {HPHW_FIO, 0x00B, 0x00072, 0x0, "Scorpio Jr. Core LAN (802.3)"}, + {HPHW_FIO, 0x00C, 0x00072, 0x0, "Strider-50 Core LAN (802.3)"}, + {HPHW_FIO, 0x00D, 0x00072, 0x0, "Strider-33 Core LAN (802.3)"}, + {HPHW_FIO, 0x00E, 0x00072, 0x0, "Trailways-50 Core LAN (802.3)"}, + {HPHW_FIO, 0x00F, 0x00072, 0x0, "Trailways-33 Core LAN (802.3)"}, + {HPHW_FIO, 0x010, 0x00072, 0x0, "Pace Core LAN (802.3)"}, + {HPHW_FIO, 0x011, 0x00072, 0x0, "Sidewinder Core LAN (802.3)"}, + {HPHW_FIO, 0x019, 0x00072, 0x0, "Scorpio Sr. Core LAN (802.3)"}, + {HPHW_FIO, 0x020, 0x00072, 0x0, "Scorpio 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x021, 0x00072, 0x0, "Spectra 50 Core LAN (802.3)"}, + {HPHW_FIO, 0x022, 0x00072, 0x0, "Spectra 75 Core LAN (802.3)"}, + {HPHW_FIO, 0x023, 0x00072, 0x0, "Spectra 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x024, 0x00072, 0x0, "Fast Pace Core LAN (802.3)"}, + {HPHW_FIO, 0x026, 0x00072, 0x0, "CoralII Jaguar Core LAN (802.3)"}, + {HPHW_FIO, 0x004, 0x00073, 0x0, "Cobra Core HIL"}, + {HPHW_FIO, 0x005, 0x00073, 0x0, "Coral Core HIL"}, + {HPHW_FIO, 0x006, 0x00073, 0x0, "Bushmaster Core HIL"}, + {HPHW_FIO, 0x007, 0x00073, 0x0, "Scorpio Core HIL"}, + {HPHW_FIO, 0x008, 0x00073, 0x0, "Flounder Core HIL"}, + {HPHW_FIO, 0x009, 0x00073, 0x0, "Outfield Core HIL"}, + {HPHW_FIO, 0x00A, 0x00073, 0x0, "CoralII Core HIL"}, + {HPHW_FIO, 0x00B, 0x00073, 0x0, "Scorpio Jr. Core HIL"}, + {HPHW_FIO, 0x00C, 0x00073, 0x0, "Strider-50 Core HIL"}, + {HPHW_FIO, 0x00D, 0x00073, 0x0, "Strider-33 Core HIL"}, + {HPHW_FIO, 0x00E, 0x00073, 0x0, "Trailways-50 Core HIL"}, + {HPHW_FIO, 0x00F, 0x00073, 0x0, "Trailways-33 Core HIL"}, + {HPHW_FIO, 0x010, 0x00073, 0x0, "Pace Core HIL"}, + {HPHW_FIO, 0x011, 0x00073, 0xcc, "SuperPace Wax HIL"}, + {HPHW_FIO, 0x012, 0x00073, 0x0, "Mirage Jr Wax HIL"}, + {HPHW_FIO, 0x013, 0x00073, 0x0, "Mirage 100 Wax HIL"}, + {HPHW_FIO, 0x014, 0x00073, 0x0, "Electra Wax HIL"}, + {HPHW_FIO, 0x017, 0x00073, 0x0, "Raven Backplane Wax HIL"}, + {HPHW_FIO, 0x019, 0x00073, 0x0, "Scorpio Sr. Core HIL"}, + {HPHW_FIO, 0x01E, 0x00073, 0x0, "Raven T' Wax HIL"}, + {HPHW_FIO, 0x01F, 0x00073, 0x0, "SkyHawk 100/120 Wax HIL"}, + {HPHW_FIO, 0x020, 0x00073, 0x0, "Scorpio 100 Core HIL"}, + {HPHW_FIO, 0x021, 0x00073, 0x0, "Spectra 50 Core HIL"}, + {HPHW_FIO, 0x022, 0x00073, 0x0, "Spectra 75 Core HIL"}, + {HPHW_FIO, 0x023, 0x00073, 0x0, "Spectra 100 Core HIL"}, + {HPHW_FIO, 0x024, 0x00073, 0x0, "Fast Pace Core HIL"}, + {HPHW_FIO, 0x026, 0x00073, 0x0, "CoralII Jaguar Core HIL"}, + {HPHW_FIO, 0x02B, 0x00073, 0x0, "Mirage 80 Wax HIL"}, + {HPHW_FIO, 0x02C, 0x00073, 0x0, "Mirage 100+ Wax HIL"}, + {HPHW_FIO, 0x03A, 0x00073, 0x0, "Merlin+ Wax HIL"}, + {HPHW_FIO, 0x040, 0x00073, 0x0, "Merlin 132 Wax HIL"}, + {HPHW_FIO, 0x041, 0x00073, 0x0, "Merlin 160 Wax HIL"}, + {HPHW_FIO, 0x043, 0x00073, 0x0, "Merlin 132/160 Wax HIL"}, + {HPHW_FIO, 0x052, 0x00073, 0x0, "Raven+ Hi Power Backplane w/EISA Wax HIL"}, + {HPHW_FIO, 0x053, 0x00073, 0x0, "Raven+ Hi Power Backplane wo/EISA Wax HIL"}, + {HPHW_FIO, 0x054, 0x00073, 0x0, "Raven+ Lo Power Backplane w/EISA Wax HIL"}, + {HPHW_FIO, 0x055, 0x00073, 0x0, "Raven+ Lo Power Backplane wo/EISA Wax HIL"}, + {HPHW_FIO, 0x059, 0x00073, 0x0, "FireHawk 200 Wax HIL"}, + {HPHW_FIO, 0x05A, 0x00073, 0x0, "Raven+ L2 Backplane w/EISA Wax HIL"}, + {HPHW_FIO, 0x05B, 0x00073, 0x0, "Raven+ L2 Backplane wo/EISA Wax HIL"}, + {HPHW_FIO, 0x05D, 0x00073, 0x0, "SummitHawk Wax HIL"}, + {HPHW_FIO, 0x800, 0x00073, 0x0, "Hitachi Tiny 64 Wax HIL"}, + {HPHW_FIO, 0x801, 0x00073, 0x0, "Hitachi Tiny 80 Wax HIL"}, + {HPHW_FIO, 0x004, 0x00074, 0x0, "Cobra Core Centronics"}, + {HPHW_FIO, 0x005, 0x00074, 0x0, "Coral Core Centronics"}, + {HPHW_FIO, 0x006, 0x00074, 0x0, "Bushmaster Core Centronics"}, + {HPHW_FIO, 0x007, 0x00074, 0x0, "Scorpio Core Centronics"}, + {HPHW_FIO, 0x008, 0x00074, 0x0, "Flounder Core Centronics"}, + {HPHW_FIO, 0x009, 0x00074, 0x0, "Outfield Core Centronics"}, + {HPHW_FIO, 0x00A, 0x00074, 0x0, "CoralII Core Centronics"}, + {HPHW_FIO, 0x00B, 0x00074, 0x0, "Scorpio Jr. Core Centronics"}, + {HPHW_FIO, 0x00C, 0x00074, 0x0, "Strider-50 Core Centronics"}, + {HPHW_FIO, 0x00D, 0x00074, 0x0, "Strider-33 Core Centronics"}, + {HPHW_FIO, 0x00E, 0x00074, 0x0, "Trailways-50 Core Centronics"}, + {HPHW_FIO, 0x00F, 0x00074, 0x0, "Trailways-33 Core Centronics"}, + {HPHW_FIO, 0x010, 0x00074, 0x0, "Pace Core Centronics"}, + {HPHW_FIO, 0x011, 0x00074, 0x0, "Sidewinder Core Centronics"}, + {HPHW_FIO, 0x015, 0x00074, 0x0, "KittyHawk GSY Core Centronics"}, + {HPHW_FIO, 0x016, 0x00074, 0x0, "Gecko Core Centronics"}, + {HPHW_FIO, 0x019, 0x00074, 0x0, "Scorpio Sr. Core Centronics"}, + {HPHW_FIO, 0x01A, 0x00074, 0x0, "Anole 64 Core Centronics"}, + {HPHW_FIO, 0x01B, 0x00074, 0x0, "Anole 100 Core Centronics"}, + {HPHW_FIO, 0x01C, 0x00074, 0x0, "Gecko 80 Core Centronics"}, + {HPHW_FIO, 0x01D, 0x00074, 0x0, "Gecko 100 Core Centronics"}, + {HPHW_FIO, 0x01F, 0x00074, 0x0, "SkyHawk 100/120 Core Centronics"}, + {HPHW_FIO, 0x020, 0x00074, 0x0, "Scorpio 100 Core Centronics"}, + {HPHW_FIO, 0x021, 0x00074, 0x0, "Spectra 50 Core Centronics"}, + {HPHW_FIO, 0x022, 0x00074, 0x0, "Spectra 75 Core Centronics"}, + {HPHW_FIO, 0x023, 0x00074, 0x0, "Spectra 100 Core Centronics"}, + {HPHW_FIO, 0x024, 0x00074, 0x0, "Fast Pace Core Centronics"}, + {HPHW_FIO, 0x026, 0x00074, 0x0, "CoralII Jaguar Core Centronics"}, + {HPHW_FIO, 0x027, 0x00074, 0x0, "Piranha 100 Core Centronics"}, + {HPHW_FIO, 0x028, 0x00074, 0x0, "Mirage Jr Core Centronics"}, + {HPHW_FIO, 0x029, 0x00074, 0x0, "Mirage Core Centronics"}, + {HPHW_FIO, 0x02A, 0x00074, 0x0, "Electra Core Centronics"}, + {HPHW_FIO, 0x02B, 0x00074, 0x0, "Mirage 80 Core Centronics"}, + {HPHW_FIO, 0x02C, 0x00074, 0x0, "Mirage 100+ Core Centronics"}, + {HPHW_FIO, 0x02E, 0x00074, 0x0, "UL 350 Core Centronics"}, + {HPHW_FIO, 0x02F, 0x00074, 0x0, "UL 550 Core Centronics"}, + {HPHW_FIO, 0x032, 0x00074, 0x0, "Raven T' Core Centronics"}, + {HPHW_FIO, 0x033, 0x00074, 0x0, "Anole T Core Centronics"}, + {HPHW_FIO, 0x034, 0x00074, 0x0, "SAIC L-80 Core Centronics"}, + {HPHW_FIO, 0x035, 0x00074, 0x0, "PCX-L2 712/132 Core Centronics"}, + {HPHW_FIO, 0x036, 0x00074, 0x0, "PCX-L2 712/160 Core Centronics"}, + {HPHW_FIO, 0x03B, 0x00074, 0x0, "Raven U/L2 Core Centronics"}, + {HPHW_FIO, 0x03C, 0x00074, 0x0, "Merlin 132 Core Centronics"}, + {HPHW_FIO, 0x03D, 0x00074, 0x0, "Merlin 160 Core Centronics"}, + {HPHW_FIO, 0x03E, 0x00074, 0x0, "Merlin+ 132 Core Centronics"}, + {HPHW_FIO, 0x03F, 0x00074, 0x0, "Merlin+ 180 Core Centronics"}, + {HPHW_FIO, 0x044, 0x00074, 0x0, "Mohawk Core Centronics"}, + {HPHW_FIO, 0x045, 0x00074, 0x0, "Rocky1 Core Centronics"}, + {HPHW_FIO, 0x046, 0x00074, 0x0, "Rocky2 120 Core Centronics"}, + {HPHW_FIO, 0x047, 0x00074, 0x0, "Rocky2 150 Core Centronics"}, + {HPHW_FIO, 0x04B, 0x00074, 0x0, "Anole L2 132 Core Centronics"}, + {HPHW_FIO, 0x04D, 0x00074, 0x0, "Anole L2 165 Core Centronics"}, + {HPHW_FIO, 0x050, 0x00074, 0x0, "Merlin Jr 132 Core Centronics"}, + {HPHW_FIO, 0x051, 0x00074, 0x0, "Firehawk Core Centronics"}, + {HPHW_FIO, 0x056, 0x00074, 0x0, "Raven+ w SE FWSCSI Core Centronics"}, + {HPHW_FIO, 0x057, 0x00074, 0x0, "Raven+ w Diff FWSCSI Core Centronics"}, + {HPHW_FIO, 0x058, 0x00074, 0x0, "FireHawk 200 Core Centronics"}, + {HPHW_FIO, 0x05C, 0x00074, 0x0, "SummitHawk 230 Core Centronics"}, + {HPHW_FIO, 0x800, 0x00074, 0x0, "Hitachi Tiny 64 Core Centronics"}, + {HPHW_FIO, 0x801, 0x00074, 0x0, "Hitachi Tiny 80 Core Centronics"}, + {HPHW_FIO, 0x004, 0x00075, 0x0, "Cobra Core RS-232"}, + {HPHW_FIO, 0x005, 0x00075, 0x0, "Coral Core RS-232"}, + {HPHW_FIO, 0x006, 0x00075, 0x0, "Bushmaster Core RS-232"}, + {HPHW_FIO, 0x007, 0x00075, 0x0, "Scorpio Core RS-232"}, + {HPHW_FIO, 0x008, 0x00075, 0x0, "Flounder Core RS-232"}, + {HPHW_FIO, 0x009, 0x00075, 0x0, "Outfield Core RS-232"}, + {HPHW_FIO, 0x00A, 0x00075, 0x0, "CoralII Core RS-232"}, + {HPHW_FIO, 0x00B, 0x00075, 0x0, "Scorpio Jr. Core RS-232"}, + {HPHW_FIO, 0x00C, 0x00075, 0x0, "Strider-50 Core RS-232"}, + {HPHW_FIO, 0x00D, 0x00075, 0x0, "Strider-33 Core RS-232"}, + {HPHW_FIO, 0x00E, 0x00075, 0x0, "Trailways-50 Core RS-232"}, + {HPHW_FIO, 0x00F, 0x00075, 0x0, "Trailways-33 Core RS-232"}, + {HPHW_FIO, 0x010, 0x00075, 0x0, "Pace Core RS-232"}, + {HPHW_FIO, 0x011, 0x00075, 0x0, "Sidewinder Core RS-232"}, + {HPHW_FIO, 0x019, 0x00075, 0x0, "Scorpio Sr. Core RS-232"}, + {HPHW_FIO, 0x020, 0x00075, 0x0, "Scorpio 100 Core RS-232"}, + {HPHW_FIO, 0x021, 0x00075, 0x0, "Spectra 50 Core RS-232"}, + {HPHW_FIO, 0x022, 0x00075, 0x0, "Spectra 75 Core RS-232"}, + {HPHW_FIO, 0x023, 0x00075, 0x0, "Spectra 100 Core RS-232"}, + {HPHW_FIO, 0x024, 0x00075, 0x0, "Fast Pace Core RS-232"}, + {HPHW_FIO, 0x026, 0x00075, 0x0, "CoralII Jaguar Core RS-232"}, + {HPHW_FIO, 0x004, 0x00077, 0x0, "Coral SGC Graphics"}, + {HPHW_FIO, 0x005, 0x00077, 0x0, "Hyperdrive Optional Graphics"}, + {HPHW_FIO, 0x006, 0x00077, 0x0, "Stinger Optional Graphics"}, + {HPHW_FIO, 0x007, 0x00077, 0x0, "Scorpio Builtin Graphics"}, + {HPHW_FIO, 0x008, 0x00077, 0x0, "Anole Hyperdrive Optional Graphics"}, + {HPHW_FIO, 0x009, 0x00077, 0x0, "Thunder II graphics EISA form"}, + {HPHW_FIO, 0x00A, 0x00077, 0x0, "Thunder II graphics GSA form"}, + {HPHW_FIO, 0x00B, 0x00077, 0x0, "Scorpio Jr Builtin Graphics"}, + {HPHW_FIO, 0x00C, 0x00077, 0x0, "Strider-50 SSC Graphics"}, + {HPHW_FIO, 0x00D, 0x00077, 0x0, "Strider-33 SSC Graphics"}, + {HPHW_FIO, 0x00E, 0x00077, 0x0, "Trailways-50 SSC Graphics"}, + {HPHW_FIO, 0x00F, 0x00077, 0x0, "Trailways-33 SSC Graphics"}, + {HPHW_FIO, 0x010, 0x00077, 0x0, "Pace SGC Graphics"}, + {HPHW_FIO, 0x011, 0x00077, 0x0, "Mohawk Opt. 2D Graphics (Kid)"}, + {HPHW_FIO, 0x012, 0x00077, 0x0, "Raven Opt. 2D Graphics (Goat)"}, + {HPHW_FIO, 0x016, 0x00077, 0x0, "Lego 24 SCG Graphics"}, + {HPHW_FIO, 0x017, 0x00077, 0x0, "Lego 24Z SCG Graphics"}, + {HPHW_FIO, 0x018, 0x00077, 0x0, "Lego 48Z SCG Graphics"}, + {HPHW_FIO, 0x019, 0x00077, 0x0, "Scorpio Sr Builtin Graphics"}, + {HPHW_FIO, 0x020, 0x00077, 0x0, "Scorpio 100 Builtin Graphics"}, + {HPHW_FIO, 0x021, 0x00077, 0x0, "Spectra 50 Builtin Graphics"}, + {HPHW_FIO, 0x022, 0x00077, 0x0, "Spectra 75 Builtin Graphics"}, + {HPHW_FIO, 0x023, 0x00077, 0x0, "Spectra 100 Builtin Graphics"}, + {HPHW_FIO, 0x024, 0x00077, 0x0, "Fast Pace SGC Graphics"}, + {HPHW_FIO, 0x006, 0x0007A, 0x0, "Bushmaster Audio"}, + {HPHW_FIO, 0x008, 0x0007A, 0x0, "Flounder Audio"}, + {HPHW_FIO, 0x004, 0x0007B, 0x0, "UL Optional Audio"}, + {HPHW_FIO, 0x007, 0x0007B, 0x0, "Scorpio Audio"}, + {HPHW_FIO, 0x00B, 0x0007B, 0x0, "Scorpio Jr. Audio"}, + {HPHW_FIO, 0x00C, 0x0007B, 0x0, "Strider-50 Audio"}, + {HPHW_FIO, 0x00D, 0x0007B, 0x0, "Strider-33 Audio"}, + {HPHW_FIO, 0x00E, 0x0007B, 0x0, "Trailways-50 Audio"}, + {HPHW_FIO, 0x00F, 0x0007B, 0x0, "Trailways-33 Audio"}, + {HPHW_FIO, 0x015, 0x0007B, 0x0, "KittyHawk GSY Core Audio"}, + {HPHW_FIO, 0x016, 0x0007B, 0x0, "Gecko Audio"}, + {HPHW_FIO, 0x019, 0x0007B, 0x0, "Scorpio Sr. Audio"}, + {HPHW_FIO, 0x01A, 0x0007B, 0x0, "Anole 64 Audio"}, + {HPHW_FIO, 0x01B, 0x0007B, 0x0, "Anole 100 Audio"}, + {HPHW_FIO, 0x01C, 0x0007B, 0x0, "Gecko 80 Audio"}, + {HPHW_FIO, 0x01D, 0x0007B, 0x0, "Gecko 100 Audio"}, + {HPHW_FIO, 0x01F, 0x0007B, 0x0, "SkyHawk 100/120 Audio"}, + {HPHW_FIO, 0x020, 0x0007B, 0x0, "Scorpio 100 Audio"}, + {HPHW_FIO, 0x021, 0x0007B, 0x0, "Spectra 50 Audio"}, + {HPHW_FIO, 0x022, 0x0007B, 0x0, "Spectra 75 Audio"}, + {HPHW_FIO, 0x023, 0x0007B, 0x0, "Spectra 100 Audio"}, + {HPHW_FIO, 0x028, 0x0007B, 0x0, "Mirage Jr Audio"}, + {HPHW_FIO, 0x029, 0x0007B, 0x0, "Mirage Audio"}, + {HPHW_FIO, 0x02A, 0x0007B, 0x0, "Electra Audio"}, + {HPHW_FIO, 0x02B, 0x0007B, 0x0, "Mirage 80 Audio"}, + {HPHW_FIO, 0x02C, 0x0007B, 0x0, "Mirage 100+ Audio"}, + {HPHW_FIO, 0x032, 0x0007B, 0x0, "Raven T' Audio"}, + {HPHW_FIO, 0x034, 0x0007B, 0x0, "SAIC L-80 Audio"}, + {HPHW_FIO, 0x035, 0x0007B, 0x0, "PCX-L2 712/132 Core Audio"}, + {HPHW_FIO, 0x036, 0x0007B, 0x0, "PCX-L2 712/160 Core Audio"}, + {HPHW_FIO, 0x03B, 0x0007B, 0x0, "Raven U/L2 Core Audio"}, + {HPHW_FIO, 0x03C, 0x0007B, 0x0, "Merlin 132 Core Audio"}, + {HPHW_FIO, 0x03D, 0x0007B, 0x0, "Merlin 160 Core Audio"}, + {HPHW_FIO, 0x03E, 0x0007B, 0x0, "Merlin+ 132 Core Audio"}, + {HPHW_FIO, 0x03F, 0x0007B, 0x0, "Merlin+ 180 Core Audio"}, + {HPHW_FIO, 0x044, 0x0007B, 0x0, "Mohawk Core Audio"}, + {HPHW_FIO, 0x046, 0x0007B, 0x0, "Rocky2 120 Core Audio"}, + {HPHW_FIO, 0x047, 0x0007B, 0x0, "Rocky2 150 Core Audio"}, + {HPHW_FIO, 0x04B, 0x0007B, 0x0, "Anole L2 132 Core Audio"}, + {HPHW_FIO, 0x04D, 0x0007B, 0x0, "Anole L2 165 Core Audio"}, + {HPHW_FIO, 0x04E, 0x0007B, 0x0, "Kiji L2 132 Core Audio"}, + {HPHW_FIO, 0x050, 0x0007B, 0x0, "Merlin Jr 132 Core Audio"}, + {HPHW_FIO, 0x051, 0x0007B, 0x0, "Firehawk Audio"}, + {HPHW_FIO, 0x056, 0x0007B, 0x0, "Raven+ w SE FWSCSI Core Audio"}, + {HPHW_FIO, 0x057, 0x0007B, 0x0, "Raven+ w Diff FWSCSI Core Audio"}, + {HPHW_FIO, 0x058, 0x0007B, 0x0, "FireHawk 200 Audio"}, + {HPHW_FIO, 0x05C, 0x0007B, 0x0, "SummitHawk 230 Core Audio"}, + {HPHW_FIO, 0x800, 0x0007B, 0x0, "Hitachi Tiny 64 Audio"}, + {HPHW_FIO, 0x801, 0x0007B, 0x0, "Hitachi Tiny 80 Audio"}, + {HPHW_FIO, 0x009, 0x0007C, 0x0, "Outfield FW SCSI"}, + {HPHW_FIO, 0x00A, 0x0007C, 0x0, "CoralII FW SCSI"}, + {HPHW_FIO, 0x026, 0x0007C, 0x0, "CoralII Jaguar FW SCSI"}, + {HPHW_FIO, 0x009, 0x0007D, 0x0, "Outfield FDDI"}, + {HPHW_FIO, 0x00A, 0x0007D, 0x0, "CoralII FDDI"}, + {HPHW_FIO, 0x026, 0x0007D, 0x0, "CoralII Jaguar FDDI"}, + {HPHW_FIO, 0x010, 0x0007E, 0x0, "Pace Audio"}, + {HPHW_FIO, 0x024, 0x0007E, 0x0, "Fast Pace Audio"}, + {HPHW_FIO, 0x009, 0x0007F, 0x0, "Outfield Audio"}, + {HPHW_FIO, 0x00A, 0x0007F, 0x0, "CoralII Audio"}, + {HPHW_FIO, 0x026, 0x0007F, 0x0, "CoralII Jaguar Audio"}, + {HPHW_FIO, 0x010, 0x00080, 0x0, "Pace Core HPIB"}, + {HPHW_FIO, 0x024, 0x00080, 0x0, "Fast Pace Core HPIB"}, + {HPHW_FIO, 0x015, 0x00082, 0x0, "KittyHawk GSY Core SCSI"}, + {HPHW_FIO, 0x016, 0x00082, 0x0, "Gecko Core SCSI"}, + {HPHW_FIO, 0x01A, 0x00082, 0x0, "Anole 64 Core SCSI"}, + {HPHW_FIO, 0x01B, 0x00082, 0x0, "Anole 100 Core SCSI"}, + {HPHW_FIO, 0x01C, 0x00082, 0x0, "Gecko 80 Core SCSI"}, + {HPHW_FIO, 0x01D, 0x00082, 0x0, "Gecko 100 Core SCSI"}, + {HPHW_FIO, 0x01F, 0x00082, 0x0, "SkyHawk 100/120 Core SCSI"}, + {HPHW_FIO, 0x027, 0x00082, 0x0, "Piranha 100 Core SCSI"}, + {HPHW_FIO, 0x028, 0x00082, 0x0, "Mirage Jr Core SCSI"}, + {HPHW_FIO, 0x029, 0x00082, 0x0, "Mirage Core SCSI"}, + {HPHW_FIO, 0x02A, 0x00082, 0x0, "Electra Core SCSI"}, + {HPHW_FIO, 0x02B, 0x00082, 0x0, "Mirage 80 Core SCSI"}, + {HPHW_FIO, 0x02C, 0x00082, 0x0, "Mirage 100+ Core SCSI"}, + {HPHW_FIO, 0x02E, 0x00082, 0x0, "UL 350 Core SCSI"}, + {HPHW_FIO, 0x02F, 0x00082, 0x0, "UL 550 Core SCSI"}, + {HPHW_FIO, 0x032, 0x00082, 0x0, "Raven T' Core SCSI"}, + {HPHW_FIO, 0x033, 0x00082, 0x0, "Anole T Core SCSI"}, + {HPHW_FIO, 0x034, 0x00082, 0x0, "SAIC L-80 Core SCSI"}, + {HPHW_FIO, 0x035, 0x00082, 0x0, "PCX-L2 712/132 Core SCSI"}, + {HPHW_FIO, 0x036, 0x00082, 0x0, "PCX-L2 712/160 Core SCSI"}, + {HPHW_FIO, 0x03B, 0x00082, 0x0, "Raven U/L2 Core SCSI"}, + {HPHW_FIO, 0x03C, 0x00082, 0x0, "Merlin 132 Core SCSI"}, + {HPHW_FIO, 0x03D, 0x00082, 0x0, "Merlin 160 Core SCSI"}, + {HPHW_FIO, 0x03E, 0x00082, 0x0, "Merlin+ 132 Core SCSI"}, + {HPHW_FIO, 0x03F, 0x00082, 0x0, "Merlin+ 180 Core SCSI"}, + {HPHW_FIO, 0x044, 0x00082, 0x0, "Mohawk Core SCSI"}, + {HPHW_FIO, 0x045, 0x00082, 0x0, "Rocky1 Core SCSI"}, + {HPHW_FIO, 0x046, 0x00082, 0x0, "Rocky2 120 Core SCSI"}, + {HPHW_FIO, 0x047, 0x00082, 0x0, "Rocky2 150 Core SCSI"}, + {HPHW_FIO, 0x04B, 0x00082, 0x0, "Anole L2 132 Core SCSI"}, + {HPHW_FIO, 0x04D, 0x00082, 0x0, "Anole L2 165 Core SCSI"}, + {HPHW_FIO, 0x04E, 0x00082, 0x0, "Kiji L2 132 Core SCSI"}, + {HPHW_FIO, 0x050, 0x00082, 0x0, "Merlin Jr 132 Core SCSI"}, + {HPHW_FIO, 0x051, 0x00082, 0x0, "Firehawk Core SCSI"}, + {HPHW_FIO, 0x056, 0x00082, 0x0, "Raven+ w SE FWSCSI Core SCSI"}, + {HPHW_FIO, 0x057, 0x00082, 0x0, "Raven+ w Diff FWSCSI Core SCSI"}, + {HPHW_FIO, 0x058, 0x00082, 0x0, "FireHawk 200 Core SCSI"}, + {HPHW_FIO, 0x05C, 0x00082, 0x0, "SummitHawk 230 Core SCSI"}, + {HPHW_FIO, 0x05E, 0x00082, 0x0, "Staccato 132 Core SCSI"}, + {HPHW_FIO, 0x05F, 0x00082, 0x0, "Staccato 180 Core SCSI"}, + {HPHW_FIO, 0x800, 0x00082, 0x0, "Hitachi Tiny 64 Core SCSI"}, + {HPHW_FIO, 0x801, 0x00082, 0x0, "Hitachi Tiny 80 Core SCSI"}, + {HPHW_FIO, 0x016, 0x00083, 0x0, "Gecko Core PC Floppy"}, + {HPHW_FIO, 0x01C, 0x00083, 0x0, "Gecko 80 Core PC Floppy"}, + {HPHW_FIO, 0x01D, 0x00083, 0x0, "Gecko 100 Core PC Floppy"}, + {HPHW_FIO, 0x051, 0x00083, 0x0, "Firehawk Core PC Floppy"}, + {HPHW_FIO, 0x058, 0x00083, 0x0, "FireHawk 200 Core PC Floppy"}, + {HPHW_FIO, 0x027, 0x00083, 0x0, "Piranha 100 Core PC Floppy"}, + {HPHW_FIO, 0x028, 0x00083, 0x0, "Mirage Jr Core PC Floppy"}, + {HPHW_FIO, 0x029, 0x00083, 0x0, "Mirage Core PC Floppy"}, + {HPHW_FIO, 0x02A, 0x00083, 0x0, "Electra Core PC Floppy"}, + {HPHW_FIO, 0x02B, 0x00083, 0x0, "Mirage 80 Core PC Floppy"}, + {HPHW_FIO, 0x02C, 0x00083, 0x0, "Mirage 100+ Core PC Floppy"}, + {HPHW_FIO, 0x02E, 0x00083, 0x0, "UL 350 Core PC Floppy"}, + {HPHW_FIO, 0x02F, 0x00083, 0x0, "UL 550 Core PC Floppy"}, + {HPHW_FIO, 0x032, 0x00083, 0x0, "Raven T' Core PC Floppy"}, + {HPHW_FIO, 0x034, 0x00083, 0x0, "SAIC L-80 Core PC Floppy"}, + {HPHW_FIO, 0x035, 0x00083, 0x0, "PCX-L2 712/132 Core Floppy"}, + {HPHW_FIO, 0x036, 0x00083, 0x0, "PCX-L2 712/160 Core Floppy"}, + {HPHW_FIO, 0x03B, 0x00083, 0x0, "Raven U/L2 Core PC Floppy"}, + {HPHW_FIO, 0x03C, 0x00083, 0x0, "Merlin 132 Core PC Floppy"}, + {HPHW_FIO, 0x03D, 0x00083, 0x0, "Merlin 160 Core PC Floppy"}, + {HPHW_FIO, 0x03E, 0x00083, 0x0, "Merlin+ 132 Core PC Floppy"}, + {HPHW_FIO, 0x03F, 0x00083, 0x0, "Merlin+ 180 Core PC Floppy"}, + {HPHW_FIO, 0x045, 0x00083, 0x0, "Rocky1 Core PC Floppy"}, + {HPHW_FIO, 0x046, 0x00083, 0x0, "Rocky2 120 Core PC Floppy"}, + {HPHW_FIO, 0x047, 0x00083, 0x0, "Rocky2 150 Core PC Floppy"}, + {HPHW_FIO, 0x04E, 0x00083, 0x0, "Kiji L2 132 Core PC Floppy"}, + {HPHW_FIO, 0x050, 0x00083, 0x0, "Merlin Jr 132 Core PC Floppy"}, + {HPHW_FIO, 0x056, 0x00083, 0x0, "Raven+ w SE FWSCSI Core PC Floppy"}, + {HPHW_FIO, 0x057, 0x00083, 0x0, "Raven+ w Diff FWSCSI Core PC Floppy"}, + {HPHW_FIO, 0x800, 0x00083, 0x0, "Hitachi Tiny 64 Core PC Floppy"}, + {HPHW_FIO, 0x801, 0x00083, 0x0, "Hitachi Tiny 80 Core PC Floppy"}, + {HPHW_FIO, 0x015, 0x00084, 0x0, "KittyHawk GSY Core PS/2 Port"}, + {HPHW_FIO, 0x016, 0x00084, 0x0, "Gecko Core PS/2 Port"}, + {HPHW_FIO, 0x018, 0x00084, 0x0, "Gecko Optional PS/2 Port"}, + {HPHW_FIO, 0x01A, 0x00084, 0x0, "Anole 64 Core PS/2 Port"}, + {HPHW_FIO, 0x01B, 0x00084, 0x0, "Anole 100 Core PS/2 Port"}, + {HPHW_FIO, 0x01C, 0x00084, 0x0, "Gecko 80 Core PS/2 Port"}, + {HPHW_FIO, 0x01D, 0x00084, 0x0, "Gecko 100 Core PS/2 Port"}, + {HPHW_FIO, 0x01F, 0x00084, 0x0, "SkyHawk 100/120 Core PS/2 Port"}, + {HPHW_FIO, 0x027, 0x00084, 0x0, "Piranha 100 Core PS/2 Port"}, + {HPHW_FIO, 0x028, 0x00084, 0x0, "Mirage Jr Core PS/2 Port"}, + {HPHW_FIO, 0x029, 0x00084, 0x0, "Mirage Core PS/2 Port"}, + {HPHW_FIO, 0x02A, 0x00084, 0x0, "Electra Core PS/2 Port"}, + {HPHW_FIO, 0x02B, 0x00084, 0x0, "Mirage 80 Core PS/2 Port"}, + {HPHW_FIO, 0x02C, 0x00084, 0x0, "Mirage 100+ Core PS/2 Port"}, + {HPHW_FIO, 0x02E, 0x00084, 0x0, "UL 350 Core PS/2 Port"}, + {HPHW_FIO, 0x02F, 0x00084, 0x0, "UL 550 Core PS/2 Port"}, + {HPHW_FIO, 0x032, 0x00084, 0x0, "Raven T' Core PS/2 Port"}, + {HPHW_FIO, 0x033, 0x00084, 0x0, "Anole T Core PS/2 Port"}, + {HPHW_FIO, 0x034, 0x00084, 0x0, "SAIC L-80 Core PS/2 Port"}, + {HPHW_FIO, 0x035, 0x00084, 0x0, "PCX-L2 712/132 Core PS/2 Port"}, + {HPHW_FIO, 0x036, 0x00084, 0x0, "PCX-L2 712/160 Core PS/2 Port"}, + {HPHW_FIO, 0x03B, 0x00084, 0x0, "Raven U/L2 Core PS/2 Port"}, + {HPHW_FIO, 0x03C, 0x00084, 0x0, "Merlin 132 Core PS/2 Port"}, + {HPHW_FIO, 0x03D, 0x00084, 0x0, "Merlin 160 Core PS/2 Port"}, + {HPHW_FIO, 0x03E, 0x00084, 0x0, "Merlin+ 132 Core PS/2 Port"}, + {HPHW_FIO, 0x03F, 0x00084, 0x0, "Merlin+ 180 Core PS/2 Port"}, + {HPHW_FIO, 0x044, 0x00084, 0x0, "Mohawk Core PS/2 Port"}, + {HPHW_FIO, 0x045, 0x00084, 0x0, "Rocky1 Core PS/2 Port"}, + {HPHW_FIO, 0x046, 0x00084, 0x0, "Rocky2 120 Core PS/2 Port"}, + {HPHW_FIO, 0x047, 0x00084, 0x0, "Rocky2 150 Core PS/2 Port"}, + {HPHW_FIO, 0x048, 0x00084, 0x0, "Rocky2 120 Dino PS/2 Port"}, + {HPHW_FIO, 0x049, 0x00084, 0x0, "Rocky2 150 Dino PS/2 Port"}, + {HPHW_FIO, 0x04B, 0x00084, 0x0, "Anole L2 132 Core PS/2 Port"}, + {HPHW_FIO, 0x04D, 0x00084, 0x0, "Anole L2 165 Core PS/2 Port"}, + {HPHW_FIO, 0x04E, 0x00084, 0x0, "Kiji L2 132 Core PS/2 Port"}, + {HPHW_FIO, 0x050, 0x00084, 0x0, "Merlin Jr 132 Core PS/2 Port"}, + {HPHW_FIO, 0x051, 0x00084, 0x0, "Firehawk Core PS/2 Port"}, + {HPHW_FIO, 0x056, 0x00084, 0x0, "Raven+ w SE FWSCSI Core PS/2 Port"}, + {HPHW_FIO, 0x057, 0x00084, 0x0, "Raven+ w Diff FWSCSI Core PS/2 Port"}, + {HPHW_FIO, 0x058, 0x00084, 0x0, "FireHawk 200 Core PS/2 Port"}, + {HPHW_FIO, 0x05C, 0x00084, 0x0, "SummitHawk 230 Core PS/2 Port"}, + {HPHW_FIO, 0x800, 0x00084, 0x0, "Hitachi Tiny 64 Core PS/2 Port"}, + {HPHW_FIO, 0x801, 0x00084, 0x0, "Hitachi Tiny 80 Core PS/2 Port"}, + {HPHW_FIO, 0x004, 0x00085, 0x0, "Solo GSC Optional Graphics"}, + {HPHW_FIO, 0x005, 0x00085, 0x0, "Duet GSC Optional Graphics"}, + {HPHW_FIO, 0x008, 0x00085, 0x0, "Anole Artist Optional Graphics"}, + {HPHW_FIO, 0x010, 0x00085, 0x0, "Mirage 80 GSC Builtin Graphics"}, + {HPHW_FIO, 0x011, 0x00085, 0x0, "Mirage 100+ GSC Builtin Graphics"}, + {HPHW_FIO, 0x012, 0x00085, 0x0, "Mirage Jr GSC Builtin Graphics"}, + {HPHW_FIO, 0x013, 0x00085, 0x0, "Mirage GSC Builtin Graphics"}, + {HPHW_FIO, 0x014, 0x00085, 0x0, "Electra GSC Builtin Graphics"}, + {HPHW_FIO, 0x016, 0x00085, 0x0, "Gecko GSC Core Graphics"}, + {HPHW_FIO, 0x017, 0x00085, 0x0, "Gecko GSC Optional Graphics"}, + {HPHW_FIO, 0x01A, 0x00085, 0x0, "Anole 64 Artist Builtin Graphics"}, + {HPHW_FIO, 0x01B, 0x00085, 0x0, "Anole 100 Artist Builtin Graphics"}, + {HPHW_FIO, 0x01C, 0x00085, 0x0, "Gecko 80 GSC Core Graphics"}, + {HPHW_FIO, 0x01D, 0x00085, 0x0, "Gecko 100 GSC Core Graphics"}, + {HPHW_FIO, 0x032, 0x00085, 0x0, "Raven T' GSC Core Graphics"}, + {HPHW_FIO, 0x033, 0x00085, 0x0, "Anole T Artist Builtin Graphics"}, + {HPHW_FIO, 0x034, 0x00085, 0x0, "SAIC L-80 GSC Core Graphics"}, + {HPHW_FIO, 0x035, 0x00085, 0x0, "PCX-L2 712/132 Core Graphics"}, + {HPHW_FIO, 0x036, 0x00085, 0x0, "PCX-L2 712/160 Core Graphics"}, + {HPHW_FIO, 0x03B, 0x00085, 0x0, "Raven U/L2 Core Graphics"}, + {HPHW_FIO, 0x03C, 0x00085, 0x0, "Merlin 132 Core Graphics"}, + {HPHW_FIO, 0x03D, 0x00085, 0x0, "Merlin 160 Core Graphics"}, + {HPHW_FIO, 0x03E, 0x00085, 0x0, "Merlin+ 132 Core Graphics"}, + {HPHW_FIO, 0x03F, 0x00085, 0x0, "Merlin+ 180 Core Graphics"}, + {HPHW_FIO, 0x045, 0x00085, 0x0, "Rocky1 Core Graphics"}, + {HPHW_FIO, 0x046, 0x00085, 0x0, "Rocky2 120 Core Graphics"}, + {HPHW_FIO, 0x047, 0x00085, 0x0, "Rocky2 150 Core Graphics"}, + {HPHW_FIO, 0x04B, 0x00085, 0x0, "Anole L2 132 Core Graphics"}, + {HPHW_FIO, 0x04D, 0x00085, 0x0, "Anole L2 165 Core Graphics"}, + {HPHW_FIO, 0x04E, 0x00085, 0x0, "Kiji L2 132 Core Graphics"}, + {HPHW_FIO, 0x050, 0x00085, 0x0, "Merlin Jr 132 Core Graphics"}, + {HPHW_FIO, 0x056, 0x00085, 0x0, "Raven+ w SE FWSCSI Core Graphics"}, + {HPHW_FIO, 0x057, 0x00085, 0x0, "Raven+ w Diff FWSCSI Core Graphics"}, + {HPHW_FIO, 0x800, 0x00085, 0x0, "Hitachi Tiny 64 Core Graphics"}, + {HPHW_FIO, 0x801, 0x00085, 0x0, "Hitachi Tiny 80 Core Graphics"}, + {HPHW_FIO, 0x004, 0x00086, 0x0, "GSC IBM Token Ring"}, + {HPHW_FIO, 0x015, 0x00087, 0x0, "Gecko Optional ISDN"}, + {HPHW_FIO, 0x016, 0x00087, 0x0, "Gecko Core ISDN"}, + {HPHW_FIO, 0x01C, 0x00087, 0x0, "Gecko 80 Core ISDN"}, + {HPHW_FIO, 0x01D, 0x00087, 0x0, "Gecko 100 Core ISDN"}, + {HPHW_FIO, 0x010, 0x00088, 0x0, "Pace VME Networking"}, + {HPHW_FIO, 0x011, 0x00088, 0x0, "Sidewinder VME Networking"}, + {HPHW_FIO, 0x01A, 0x00088, 0x0, "Anole 64 VME Networking"}, + {HPHW_FIO, 0x01B, 0x00088, 0x0, "Anole 100 VME Networking"}, + {HPHW_FIO, 0x024, 0x00088, 0x0, "Fast Pace VME Networking"}, + {HPHW_FIO, 0x034, 0x00088, 0x0, "Anole T VME Networking"}, + {HPHW_FIO, 0x04A, 0x00088, 0x0, "Anole L2 132 VME Networking"}, + {HPHW_FIO, 0x04C, 0x00088, 0x0, "Anole L2 165 VME Networking"}, + {HPHW_FIO, 0x011, 0x0008A, 0x0, "WB-96 Core LAN (802.3)"}, + {HPHW_FIO, 0x012, 0x0008A, 0x0, "Orville Core LAN (802.3)"}, + {HPHW_FIO, 0x013, 0x0008A, 0x0, "Wilbur Core LAN (802.3)"}, + {HPHW_FIO, 0x014, 0x0008A, 0x0, "WB-80 Core LAN (802.3)"}, + {HPHW_FIO, 0x015, 0x0008A, 0x0, "KittyHawk GSY Core LAN (802.3)"}, + {HPHW_FIO, 0x016, 0x0008A, 0x0, "Gecko Core LAN (802.3)"}, + {HPHW_FIO, 0x018, 0x0008A, 0x0, "Gecko Optional LAN (802.3)"}, + {HPHW_FIO, 0x01A, 0x0008A, 0x0, "Anole 64 Core LAN (802.3)"}, + {HPHW_FIO, 0x01B, 0x0008A, 0x0, "Anole 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x01C, 0x0008A, 0x0, "Gecko 80 Core LAN (802.3)"}, + {HPHW_FIO, 0x01D, 0x0008A, 0x0, "Gecko 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x01F, 0x0008A, 0x0, "SkyHawk 100/120 Core LAN (802.3)"}, + {HPHW_FIO, 0x027, 0x0008A, 0x0, "Piranha 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x028, 0x0008A, 0x0, "Mirage Jr Core LAN (802.3)"}, + {HPHW_FIO, 0x029, 0x0008A, 0x0, "Mirage Core LAN (802.3)"}, + {HPHW_FIO, 0x02A, 0x0008A, 0x0, "Electra Core LAN (802.3)"}, + {HPHW_FIO, 0x02B, 0x0008A, 0x0, "Mirage 80 Core LAN (802.3)"}, + {HPHW_FIO, 0x02C, 0x0008A, 0x0, "Mirage 100+ Core LAN (802.3)"}, + {HPHW_FIO, 0x02E, 0x0008A, 0x0, "UL 350 Core LAN (802.3)"}, + {HPHW_FIO, 0x02F, 0x0008A, 0x0, "UL 350 Core LAN (802.3)"}, + {HPHW_FIO, 0x032, 0x0008A, 0x0, "Raven T' Core LAN (802.3)"}, + {HPHW_FIO, 0x033, 0x0008A, 0x0, "Anole T Core LAN (802.3)"}, + {HPHW_FIO, 0x034, 0x0008A, 0x0, "SAIC L-80 Core LAN (802.3)"}, + {HPHW_FIO, 0x035, 0x0008A, 0x0, "PCX-L2 712/132 Core LAN (802.3)"}, + {HPHW_FIO, 0x036, 0x0008A, 0x0, "PCX-L2 712/160 Core LAN (802.3)"}, + {HPHW_FIO, 0x03B, 0x0008A, 0x0, "Raven U/L2 Core LAN (802.3)"}, + {HPHW_FIO, 0x03C, 0x0008A, 0x0, "Merlin 132 Core LAN (802.3)"}, + {HPHW_FIO, 0x03D, 0x0008A, 0x0, "Merlin 160 Core LAN (802.3)"}, + {HPHW_FIO, 0x044, 0x0008A, 0x0, "Mohawk Core LAN (802.3)"}, + {HPHW_FIO, 0x045, 0x0008A, 0x0, "Rocky1 Core LAN (802.3)"}, + {HPHW_FIO, 0x046, 0x0008A, 0x0, "Rocky2 120 Core LAN (802.3)"}, + {HPHW_FIO, 0x047, 0x0008A, 0x0, "Rocky2 150 Core LAN (802.3)"}, + {HPHW_FIO, 0x04B, 0x0008A, 0x0, "Anole L2 132 Core LAN (802.3)"}, + {HPHW_FIO, 0x04D, 0x0008A, 0x0, "Anole L2 165 Core LAN (802.3)"}, + {HPHW_FIO, 0x04E, 0x0008A, 0x0, "Kiji L2 132 Core LAN (802.3)"}, + {HPHW_FIO, 0x050, 0x0008A, 0x0, "Merlin Jr 132 Core LAN (802.3)"}, + {HPHW_FIO, 0x058, 0x0008A, 0x0, "FireHawk 200 Core LAN (802.3)"}, + {HPHW_FIO, 0x800, 0x0008A, 0x0, "Hitachi Tiny 64 Core LAN (802.3)"}, + {HPHW_FIO, 0x801, 0x0008A, 0x0, "Hitachi Tiny 80 Core LAN (802.3)"}, + {HPHW_FIO, 0x004, 0x0008C, 0x0, "SkyHawk 100/120 Wax RS-232"}, + {HPHW_FIO, 0x005, 0x0008C, 0x0, "SAIC L-80 Wax RS-232"}, + {HPHW_FIO, 0x006, 0x0008C, 0x0, "Raven U/L2 Dino RS-232"}, + {HPHW_FIO, 0x007, 0x0008C, 0x0, "Dino RS-232"}, + {HPHW_FIO, 0x008, 0x0008C, 0x0, "Merlin 132 Dino RS-232"}, + {HPHW_FIO, 0x009, 0x0008C, 0x0, "Merlin 160 Dino RS-232"}, + {HPHW_FIO, 0x00A, 0x0008C, 0x0, "Merlin Jr 132 Dino RS-232"}, + {HPHW_FIO, 0x010, 0x0008C, 0x0, "Mirage 80 Wax RS-232"}, + {HPHW_FIO, 0x011, 0x0008C, 0x0, "Mirage 100+ Wax RS-232"}, + {HPHW_FIO, 0x012, 0x0008C, 0x0, "Mirage Jr Wax RS-232"}, + {HPHW_FIO, 0x013, 0x0008C, 0x0, "Mirage Wax RS-232"}, + {HPHW_FIO, 0x014, 0x0008C, 0x0, "Electra Wax RS-232"}, + {HPHW_FIO, 0x015, 0x0008C, 0x0, "KittyHawk GSY Core RS-232"}, + {HPHW_FIO, 0x016, 0x0008C, 0x0, "Gecko Core RS-232"}, + {HPHW_FIO, 0x017, 0x0008C, 0x0, "Raven Backplane RS-232"}, + {HPHW_FIO, 0x018, 0x0008C, 0x0, "Gecko Optional RS-232"}, + {HPHW_FIO, 0x019, 0x0008C, 0x0, "Merlin+ 180 Dino RS-232"}, + {HPHW_FIO, 0x01A, 0x0008C, 0x0, "Anole 64 Core RS-232"}, + {HPHW_FIO, 0x01B, 0x0008C, 0x0, "Anole 100 Core RS-232"}, + {HPHW_FIO, 0x01C, 0x0008C, 0x0, "Gecko 80 Core RS-232"}, + {HPHW_FIO, 0x01D, 0x0008C, 0x0, "Gecko 100 Core RS-232"}, + {HPHW_FIO, 0x01E, 0x0008C, 0x0, "Raven T' Wax RS-232"}, + {HPHW_FIO, 0x01F, 0x0008C, 0x0, "SkyHawk 100/120 Core RS-232"}, + {HPHW_FIO, 0x020, 0x0008C, 0x0, "Anole 64 Timi RS-232"}, + {HPHW_FIO, 0x021, 0x0008C, 0x0, "Anole 100 Timi RS-232"}, + {HPHW_FIO, 0x022, 0x0008C, 0x0, "Merlin+ 132 Dino RS-232"}, + {HPHW_FIO, 0x023, 0x0008C, 0x0, "Rocky1 Wax RS-232"}, + {HPHW_FIO, 0x025, 0x0008C, 0x0, "Armyknife Optional RS-232"}, + {HPHW_FIO, 0x026, 0x0008C, 0x0, "Piranha 100 Wax RS-232"}, + {HPHW_FIO, 0x027, 0x0008C, 0x0, "Piranha 100 Core RS-232"}, + {HPHW_FIO, 0x028, 0x0008C, 0x0, "Mirage Jr Core RS-232"}, + {HPHW_FIO, 0x029, 0x0008C, 0x0, "Mirage Core RS-232"}, + {HPHW_FIO, 0x02A, 0x0008C, 0x0, "Electra Core RS-232"}, + {HPHW_FIO, 0x02B, 0x0008C, 0x0, "Mirage 80 Core RS-232"}, + {HPHW_FIO, 0x02C, 0x0008C, 0x0, "Mirage 100+ Core RS-232"}, + {HPHW_FIO, 0x02E, 0x0008C, 0x0, "UL 350 Lasi Core RS-232"}, + {HPHW_FIO, 0x02F, 0x0008C, 0x0, "UL 550 Lasi Core RS-232"}, + {HPHW_FIO, 0x030, 0x0008C, 0x0, "UL 350 Wax Core RS-232"}, + {HPHW_FIO, 0x031, 0x0008C, 0x0, "UL 550 Wax Core RS-232"}, + {HPHW_FIO, 0x032, 0x0008C, 0x0, "Raven T' Lasi Core RS-232"}, + {HPHW_FIO, 0x033, 0x0008C, 0x0, "Anole T Core RS-232"}, + {HPHW_FIO, 0x034, 0x0008C, 0x0, "SAIC L-80 Core RS-232"}, + {HPHW_FIO, 0x035, 0x0008C, 0x0, "PCX-L2 712/132 Core RS-232"}, + {HPHW_FIO, 0x036, 0x0008C, 0x0, "PCX-L2 712/160 Core RS-232"}, + {HPHW_FIO, 0x03A, 0x0008C, 0x0, "Merlin+ Wax RS-232"}, + {HPHW_FIO, 0x03B, 0x0008C, 0x0, "Raven U/L2 Core RS-232"}, + {HPHW_FIO, 0x03C, 0x0008C, 0x0, "Merlin 132 Core RS-232"}, + {HPHW_FIO, 0x03D, 0x0008C, 0x0, "Merlin 160 Core RS-232"}, + {HPHW_FIO, 0x03E, 0x0008C, 0x0, "Merlin+ 132 Core RS-232"}, + {HPHW_FIO, 0x03F, 0x0008C, 0x0, "Merlin+ 180 Core RS-232"}, + {HPHW_FIO, 0x040, 0x0008C, 0x0, "Merlin 132 Wax RS-232"}, + {HPHW_FIO, 0x041, 0x0008C, 0x0, "Merlin 160 Wax RS-232"}, + {HPHW_FIO, 0x043, 0x0008C, 0x0, "Merlin 132/160 Wax RS-232"}, + {HPHW_FIO, 0x044, 0x0008C, 0x0, "Mohawk Core RS-232"}, + {HPHW_FIO, 0x045, 0x0008C, 0x0, "Rocky1 Core RS-232"}, + {HPHW_FIO, 0x046, 0x0008C, 0x0, "Rocky2 120 Core RS-232"}, + {HPHW_FIO, 0x047, 0x0008C, 0x0, "Rocky2 150 Core RS-232"}, + {HPHW_FIO, 0x048, 0x0008C, 0x0, "Rocky2 120 Dino RS-232"}, + {HPHW_FIO, 0x049, 0x0008C, 0x0, "Rocky2 150 Dino RS-232"}, + {HPHW_FIO, 0x04A, 0x0008C, 0x0, "Anole L2 132 TIMI RS-232"}, + {HPHW_FIO, 0x04B, 0x0008C, 0x0, "Anole L2 l32 Core RS-232"}, + {HPHW_FIO, 0x04C, 0x0008D, 0x0, "Anole L2 165 TIMI RS-232"}, + {HPHW_FIO, 0x04D, 0x0008C, 0x0, "Anole L2 165 Core RS-232"}, + {HPHW_FIO, 0x04E, 0x0008C, 0x0, "Kiji L2 132 Core RS-232"}, + {HPHW_FIO, 0x04F, 0x0008C, 0x0, "Kiji L2 132 Dino RS-232"}, + {HPHW_FIO, 0x050, 0x0008C, 0x0, "Merlin Jr 132 Core RS-232"}, + {HPHW_FIO, 0x051, 0x0008C, 0x0, "Firehawk Core RS-232"}, + {HPHW_FIO, 0x052, 0x0008C, 0x0, "Raven+ Hi Power Backplane w EISA RS-232"}, + {HPHW_FIO, 0x053, 0x0008C, 0x0, "Raven+ Hi Power Backplane w/o EISA RS-232"}, + {HPHW_FIO, 0x054, 0x0008C, 0x0, "Raven+ Lo Power Backplane w EISA RS-232"}, + {HPHW_FIO, 0x055, 0x0008C, 0x0, "Raven+ Lo Power Backplane w/o EISA RS-232"}, + {HPHW_FIO, 0x056, 0x0008C, 0x0, "Raven+ w SE FWSCSI Core RS-232"}, + {HPHW_FIO, 0x057, 0x0008C, 0x0, "Raven+ w Diff FWSCSI Core RS-232"}, + {HPHW_FIO, 0x058, 0x0008C, 0x0, "FireHawk 200 Core RS-232"}, + {HPHW_FIO, 0x059, 0x0008C, 0x0, "FireHawk 200 Wax RS-232"}, + {HPHW_FIO, 0x05A, 0x0008C, 0x0, "Raven+ L2 Backplane w EISA RS-232"}, + {HPHW_FIO, 0x05B, 0x0008C, 0x0, "Raven+ L2 Backplane w/o EISA RS-232"}, + {HPHW_FIO, 0x05D, 0x0008C, 0x0, "SummitHawk Dino RS-232"}, + {HPHW_FIO, 0x05E, 0x0008C, 0x0, "Staccato 132 Core LAN RS-232"}, + {HPHW_FIO, 0x05F, 0x0008C, 0x0, "Staccato 180 Core LAN RS-232"}, + {HPHW_FIO, 0x800, 0x0008C, 0x0, "Hitachi Tiny 64 Core RS-232"}, + {HPHW_FIO, 0x801, 0x0008C, 0x0, "Hitachi Tiny 80 Core RS-232"}, + {HPHW_FIO, 0x015, 0x0008D, 0x0, "Gecko Optional RJ-16"}, + {HPHW_FIO, 0x016, 0x0008D, 0x0, "Gecko Core RJ-16"}, + {HPHW_FIO, 0x01C, 0x0008D, 0x0, "Gecko 80 Core RJ-16"}, + {HPHW_FIO, 0x01D, 0x0008D, 0x0, "Gecko 100 Core RJ-16"}, + {HPHW_FIO, 0x004, 0x0008F, 0x0, "Anole Boot Rom"}, + {HPHW_FIO, 0x005, 0x0008F, 0x0, "Rocky1 Boot Rom"}, + {HPHW_FIO, 0x006, 0x0008F, 0x0, "Rocky2 120 Boot Rom"}, + {HPHW_FIO, 0x007, 0x0008F, 0x0, "Rocky2 150 Boot Rom"}, + {HPHW_FIO, 0x01B, 0x0008F, 0x0, "Anole 100 Boot Rom"}, + {HPHW_FIO, 0x006, 0x00096, 0x0, "Raven U/L2 Dino PS/2 Port"}, + {HPHW_FIO, 0x007, 0x00096, 0x0, "Dino PS/2 Port"}, + {HPHW_FIO, 0x008, 0x00096, 0x0, "Merlin 132 Dino PS/2 Port"}, + {HPHW_FIO, 0x009, 0x00096, 0x0, "Merlin 160 Dino PS/2 Port"}, + {HPHW_FIO, 0x00A, 0x00096, 0x0, "Merlin Jr 132 Dino PS/2 Port"}, + {HPHW_FIO, 0x019, 0x00096, 0x0, "Merlin+ 180 Dino PS/2 Port"}, + {HPHW_FIO, 0x022, 0x00096, 0x0, "Merlin+ 132 Dino PS/2 Port"}, + {HPHW_FIO, 0x004, 0x00097, 0x0, "Cascade EISA 100VG LAN"}, + {HPHW_FIO, 0x023, 0x00099, 0x0, "Rocky1 Wax HPIB"}, + {HPHW_FIO, 0x048, 0x00099, 0x0, "Rocky2 120 Clark/Dino HPIB"}, + {HPHW_FIO, 0x049, 0x00099, 0x0, "Rocky2 150 Clark/Dino HPIB"}, + {HPHW_FIO, 0x004, 0x000A1, 0x0, "SPP2000 Console TTY"}, + {HPHW_FIO, 0x004, 0x000A2, 0x0, "Forte Core PCI 10/100BT LAN"}, + {HPHW_FIO, 0x005, 0x000A2, 0x0, "AllegroLow PCI 10/100BT LAN"}, + {HPHW_FIO, 0x006, 0x000A2, 0x0, "AllegroHIgh Core PCI 10/100BT LAN"}, + {HPHW_FIO, 0x007, 0x000A2, 0x0, "PCI Plug-in LAN"}, + {HPHW_FIO, 0x00A, 0x000A2, 0x0, "Lego 360 Core PCI 10/100BT LAN"}, + {HPHW_FIO, 0x03E, 0x000A2, 0x0, "Merlin+ 132 Core PCI LAN"}, + {HPHW_FIO, 0x03F, 0x000A2, 0x0, "Merlin+ 180 Core PCI LAN"}, + {HPHW_FIO, 0x056, 0x000A2, 0x0, "Raven+ w SE FWSCSI Core PCI LAN"}, + {HPHW_FIO, 0x057, 0x000A2, 0x0, "Raven+ w Diff FWSCSI Core PCI LAN"}, + {HPHW_FIO, 0x05E, 0x000A2, 0x0, "Staccato 132 PCI LAN"}, + {HPHW_FIO, 0x05F, 0x000A2, 0x0, "Staccato 180 PCI LAN"}, + {HPHW_FIO, 0x004, 0x000A3, 0x0, "Forte Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x004, 0x000A3, 0x0, "Forte Core PCI SE UltraSCSI"}, + {HPHW_FIO, 0x004, 0x000A3, 0x0, "Forte Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x005, 0x000A3, 0x0, "AllegroLow Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x005, 0x000A3, 0x0, "AllegroLow Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x006, 0x000A3, 0x0, "AllegroHigh Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x006, 0x000A3, 0x0, "AllegroHigh Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x007, 0x000A3, 0x0, "PCI Plug-in Disk"}, + {HPHW_FIO, 0x008, 0x000A3, 0x0, "A5158A S FC Tachlite HBA"}, + {HPHW_FIO, 0x009, 0x000A3, 0x0, "A5157A D FC HBA"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI NSE UltraSCSI"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI WSE UltraSCSI"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x03E, 0x000A3, 0x0, "Merlin+ 132 Core SE FWSCSI PCI Disk"}, + {HPHW_FIO, 0x03F, 0x000A3, 0x0, "Merlin+ 180 Core SE FWSCSI PCI Disk"}, + {HPHW_FIO, 0x056, 0x000A3, 0x0, "Raven+ w SE FWSCSI Core PCI Disk"}, + {HPHW_FIO, 0x057, 0x000A3, 0x0, "Raven+ w Diff FWSCSI Core PCI Disk"}, + {HPHW_FIO, 0x004, 0x000A4, 0x0, "SPP2000 Core BA"}, + {HPHW_FIO, 0x004, 0x000A6, 0x0, "Sonic Ethernet 802.3 Card"}, + {HPHW_FIO, 0x004, 0x000A9, 0x00, "Forte Core PCI SuperIO RS-232"}, + {HPHW_FIO, 0x004, 0x000A9, 0x00, "Forte Core PCI USB KB"}, + {HPHW_FIO, 0x005, 0x000A9, 0x00, "AllegroLow Core PCI SuperIO RS-232"}, + {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, "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"}, + {HPHW_IOA, 0x581, 0x0000B, 0x10, "Uturn-IOA BC Runway Port"}, + {HPHW_IOA, 0x582, 0x0000B, 0x10, "Astro BC Runway Port"}, + {HPHW_IOA, 0x700, 0x0000B, 0x00, "NEC-IOS BC System Bus Port"}, + {HPHW_IOA, 0x880, 0x0000C, 0x10, "Pluto BC McKinley Port"}, + {HPHW_MEMORY, 0x002, 0x00008, 0x00, "MID_BUS"}, + {HPHW_MEMORY, 0x063, 0x00009, 0x00, "712/132 L2 Upgrade"}, + {HPHW_MEMORY, 0x064, 0x00009, 0x00, "712/160 L2 Upgrade"}, + {HPHW_MEMORY, 0x065, 0x00009, 0x00, "715/132 L2 Upgrade"}, + {HPHW_MEMORY, 0x066, 0x00009, 0x00, "715/160 L2 Upgrade"}, + {HPHW_MEMORY, 0x0AF, 0x00009, 0x00, "Everest Mako Memory"}, + {HPHW_OTHER, 0x004, 0x00030, 0x00, "Master"}, + {HPHW_OTHER, 0x004, 0x00034, 0x00, "Slave"}, + {HPHW_OTHER, 0x004, 0x00038, 0x00, "EDU"}, + {HPHW_OTHER, 0x004, 0x00049, 0x00, "LGB Control"}, + {HPHW_MC, 0x004, 0x000C0, 0x00, "BMC IPMI Mgmt Ctlr"}, + {HPHW_FAULTY, 0, } /* Special Marker for last entry */ +}; + + +static struct hp_cpu_type_mask { + unsigned short model; + unsigned short mask; + enum cpu_type cpu; +} hp_cpu_type_mask_list[] __initdata = { + + { 0x0000, 0x0ff0, pcx }, /* 0x0000 - 0x000f */ + { 0x0048, 0x0ff0, pcxl }, /* 0x0040 - 0x004f */ + { 0x0080, 0x0ff0, pcx }, /* 0x0080 - 0x008f */ + { 0x0100, 0x0ff0, pcx }, /* 0x0100 - 0x010f */ + { 0x0182, 0x0ffe, pcx }, /* 0x0182 - 0x0183 */ + { 0x0182, 0x0ffe, pcxt }, /* 0x0182 - 0x0183 */ + { 0x0184, 0x0fff, pcxu }, /* 0x0184 - 0x0184 */ + { 0x0200, 0x0ffe, pcxs }, /* 0x0200 - 0x0201 */ + { 0x0202, 0x0fff, pcxs }, /* 0x0202 - 0x0202 */ + { 0x0203, 0x0fff, pcxt }, /* 0x0203 - 0x0203 */ + { 0x0204, 0x0ffc, pcxt }, /* 0x0204 - 0x0207 */ + { 0x0280, 0x0ffc, pcxs }, /* 0x0280 - 0x0283 */ + { 0x0284, 0x0ffc, pcxt }, /* 0x0284 - 0x0287 */ + { 0x0288, 0x0fff, pcxt }, /* 0x0288 - 0x0288 */ + { 0x0300, 0x0ffc, pcxs }, /* 0x0300 - 0x0303 */ + { 0x0310, 0x0ff0, pcxt }, /* 0x0310 - 0x031f */ + { 0x0320, 0x0ff0, pcxt }, /* 0x0320 - 0x032f */ + { 0x0400, 0x0ff0, pcxt }, /* 0x0400 - 0x040f */ + { 0x0480, 0x0ff0, pcxl }, /* 0x0480 - 0x048f */ + { 0x0500, 0x0ff0, pcxl2 }, /* 0x0500 - 0x050f */ + { 0x0510, 0x0ff0, pcxl2 }, /* 0x0510 - 0x051f */ + { 0x0580, 0x0ff8, pcxt_ }, /* 0x0580 - 0x0587 */ + { 0x0588, 0x0ffc, pcxt_ }, /* 0x0588 - 0x058b */ + { 0x058c, 0x0ffe, pcxt_ }, /* 0x058c - 0x058d */ + { 0x058e, 0x0fff, pcxt_ }, /* 0x058e - 0x058e */ + { 0x058f, 0x0fff, pcxu }, /* 0x058f - 0x058f */ + { 0x0590, 0x0ffe, pcxu }, /* 0x0590 - 0x0591 */ + { 0x0592, 0x0fff, pcxt_ }, /* 0x0592 - 0x0592 */ + { 0x0593, 0x0fff, pcxu }, /* 0x0593 - 0x0593 */ + { 0x0594, 0x0ffc, pcxu }, /* 0x0594 - 0x0597 */ + { 0x0598, 0x0ffe, pcxu_ }, /* 0x0598 - 0x0599 */ + { 0x059a, 0x0ffe, pcxu }, /* 0x059a - 0x059b */ + { 0x059c, 0x0fff, pcxu }, /* 0x059c - 0x059c */ + { 0x059d, 0x0fff, pcxu_ }, /* 0x059d - 0x059d */ + { 0x059e, 0x0fff, pcxt_ }, /* 0x059e - 0x059e */ + { 0x059f, 0x0fff, pcxu }, /* 0x059f - 0x059f */ + { 0x05a0, 0x0ffe, pcxt_ }, /* 0x05a0 - 0x05a1 */ + { 0x05a2, 0x0ffe, pcxu }, /* 0x05a2 - 0x05a3 */ + { 0x05a4, 0x0ffc, pcxu }, /* 0x05a4 - 0x05a7 */ + { 0x05a8, 0x0ffc, pcxu }, /* 0x05a8 - 0x05ab */ + { 0x05ad, 0x0fff, pcxu_ }, /* 0x05ad - 0x05ad */ + { 0x05ae, 0x0ffe, pcxu_ }, /* 0x05ae - 0x05af */ + { 0x05b0, 0x0ffe, pcxu_ }, /* 0x05b0 - 0x05b1 */ + { 0x05b2, 0x0fff, pcxu_ }, /* 0x05b2 - 0x05b2 */ + { 0x05b3, 0x0fff, pcxu }, /* 0x05b3 - 0x05b3 */ + { 0x05b4, 0x0fff, pcxw }, /* 0x05b4 - 0x05b4 */ + { 0x05b5, 0x0fff, pcxu_ }, /* 0x05b5 - 0x05b5 */ + { 0x05b6, 0x0ffe, pcxu_ }, /* 0x05b6 - 0x05b7 */ + { 0x05b8, 0x0ffe, pcxu_ }, /* 0x05b8 - 0x05b9 */ + { 0x05ba, 0x0fff, pcxu_ }, /* 0x05ba - 0x05ba */ + { 0x05bb, 0x0fff, pcxw }, /* 0x05bb - 0x05bb */ + { 0x05bc, 0x0ffc, pcxw }, /* 0x05bc - 0x05bf */ + { 0x05c0, 0x0ffc, pcxw }, /* 0x05c0 - 0x05c3 */ + { 0x05c4, 0x0ffe, pcxw }, /* 0x05c4 - 0x05c5 */ + { 0x05c6, 0x0fff, pcxw }, /* 0x05c6 - 0x05c6 */ + { 0x05c7, 0x0fff, pcxw_ }, /* 0x05c7 - 0x05c7 */ + { 0x05c8, 0x0ffc, pcxw }, /* 0x05c8 - 0x05cb */ + { 0x05cc, 0x0ffe, pcxw }, /* 0x05cc - 0x05cd */ + { 0x05ce, 0x0ffe, pcxw_ }, /* 0x05ce - 0x05cf */ + { 0x05d0, 0x0ffc, pcxw_ }, /* 0x05d0 - 0x05d3 */ + { 0x05d4, 0x0ffe, pcxw_ }, /* 0x05d4 - 0x05d5 */ + { 0x05d6, 0x0fff, pcxw }, /* 0x05d6 - 0x05d6 */ + { 0x05d7, 0x0fff, pcxw_ }, /* 0x05d7 - 0x05d7 */ + { 0x05d8, 0x0ffc, pcxw_ }, /* 0x05d8 - 0x05db */ + { 0x05dc, 0x0ffe, pcxw2 }, /* 0x05dc - 0x05dd */ + { 0x05de, 0x0fff, pcxw_ }, /* 0x05de - 0x05de */ + { 0x05df, 0x0fff, pcxw2 }, /* 0x05df - 0x05df */ + { 0x05e0, 0x0ffc, pcxw2 }, /* 0x05e0 - 0x05e3 */ + { 0x05e4, 0x0fff, pcxw2 }, /* 0x05e4 - 0x05e4 */ + { 0x05e5, 0x0fff, pcxw_ }, /* 0x05e5 - 0x05e5 */ + { 0x05e6, 0x0ffe, pcxw2 }, /* 0x05e6 - 0x05e7 */ + { 0x05e8, 0x0ff8, pcxw2 }, /* 0x05e8 - 0x05ef */ + { 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 */ +}; + +const char * const cpu_name_version[][2] = { + [pcx] = { "PA7000 (PCX)", "1.0" }, + [pcxs] = { "PA7000 (PCX-S)", "1.1a" }, + [pcxt] = { "PA7100 (PCX-T)", "1.1b" }, + [pcxt_] = { "PA7200 (PCX-T')", "1.1c" }, + [pcxl] = { "PA7100LC (PCX-L)", "1.1d" }, + [pcxl2] = { "PA7300LC (PCX-L2)","1.1e" }, + [pcxu] = { "PA8000 (PCX-U)", "2.0" }, + [pcxu_] = { "PA8200 (PCX-U+)", "2.0" }, + [pcxw] = { "PA8500 (PCX-W)", "2.0" }, + [pcxw_] = { "PA8600 (PCX-W+)", "2.0" }, + [pcxw2] = { "PA8700 (PCX-W2)", "2.0" }, + [mako] = { "PA8800 (Mako)", "2.0" }, + [mako2] = { "PA8900 (Shortfin)","2.0" } +}; + +const char * __init parisc_hardware_description(struct parisc_device_id *id) +{ + struct hp_hardware *listptr; + + for (listptr = hp_hardware_list; listptr->hw_type != HPHW_FAULTY; listptr++) { + if ((listptr->hw_type == id->hw_type) && + (listptr->hversion == id->hversion) && + (listptr->sversion == id->sversion)){ + return listptr->name; + } + } + + /* + * ok, the above hardware table isn't complete, and we haven't found + * our device in this table. So let's now try to find a generic name + * to describe the given hardware... + */ + switch (id->hw_type) { + case HPHW_NPROC: + return "Unknown machine"; + + case HPHW_A_DIRECT: + switch (id->sversion) { + case 0x0D: return "MUX port"; + case 0x0E: return "RS-232 port"; + } + break; + + case HPHW_MEMORY: + return "Memory"; + + } + + return "unknown device"; +} + + +/* Interpret hversion (ret[0]) from PDC_MODEL(4)/PDC_MODEL_INFO(0) */ +enum cpu_type __init +parisc_get_cpu_type(unsigned long hversion) +{ + struct hp_cpu_type_mask *ptr; + unsigned short model = ((unsigned short) (hversion)) >> 4; + + for (ptr = hp_cpu_type_mask_list; 0 != ptr->mask; ptr++) { + if (ptr->model == (model & ptr->mask)) + return ptr->cpu; + } + panic("could not identify CPU type\n"); + + return pcx; /* not reached: */ +} + diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S new file mode 100644 index 000000000..5a7d43c0f --- /dev/null +++ b/arch/parisc/kernel/head.S @@ -0,0 +1,433 @@ +/* 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-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) + * Copyright (C) 2001 Grant Grundler (Hewlett Packard) + * Copyright (C) 2004 Kyle McMartin <kyle@debian.org> + * + * Initial Version 04-23-1999 by Helge Deller <deller@gmx.de> + */ + +#include <asm/asm-offsets.h> +#include <asm/psw.h> +#include <asm/pdc.h> + +#include <asm/assembly.h> + +#include <linux/linkage.h> +#include <linux/init.h> +#include <linux/pgtable.h> + + .level 1.1 + + __INITDATA +ENTRY(boot_args) + .word 0 /* arg0 */ + .word 0 /* arg1 */ + .word 0 /* arg2 */ + .word 0 /* arg3 */ +END(boot_args) + + __HEAD + + .align 4 + .import init_task,data + .import init_stack,data + .import fault_vector_20,code /* IVA parisc 2.0 32 bit */ +#ifndef CONFIG_64BIT + .import fault_vector_11,code /* IVA parisc 1.1 32 bit */ + .import $global$ /* forward declaration */ +#endif /*!CONFIG_64BIT*/ +ENTRY(parisc_kernel_start) + .proc + .callinfo + + /* Make sure sr4-sr7 are set to zero for the kernel address space */ + mtsp %r0,%sr4 + mtsp %r0,%sr5 + mtsp %r0,%sr6 + mtsp %r0,%sr7 + + /* Clear BSS (shouldn't the boot loader do this?) */ + + .import __bss_start,data + .import __bss_stop,data + + load32 PA(__bss_start),%r3 + load32 PA(__bss_stop),%r4 +$bss_loop: + cmpb,<<,n %r3,%r4,$bss_loop + stw,ma %r0,4(%r3) + + /* Save away the arguments the boot loader passed in (32 bit args) */ + load32 PA(boot_args),%r1 + stw,ma %arg0,4(%r1) + stw,ma %arg1,4(%r1) + stw,ma %arg2,4(%r1) + stw,ma %arg3,4(%r1) + +#if defined(CONFIG_PA20) + /* check for 64-bit capable CPU as required by current kernel */ + ldi 32,%r10 + mtctl %r10,%cr11 + .level 2.0 + mfctl,w %cr11,%r10 + .level 1.1 + comib,<>,n 0,%r10,$cpu_ok + + load32 PA(msg1),%arg0 + ldi msg1_end-msg1,%arg1 +$iodc_panic: + copy %arg0, %r10 + copy %arg1, %r11 + load32 PA(init_stack),%sp +#define MEM_CONS 0x3A0 + ldw MEM_CONS+32(%r0),%arg0 // HPA + ldi ENTRY_IO_COUT,%arg1 + ldw MEM_CONS+36(%r0),%arg2 // SPA + ldw MEM_CONS+8(%r0),%arg3 // layers + load32 PA(__bss_start),%r1 + stw %r1,-52(%sp) // arg4 + stw %r0,-56(%sp) // arg5 + stw %r10,-60(%sp) // arg6 = ptr to text + stw %r11,-64(%sp) // arg7 = len + stw %r0,-68(%sp) // arg8 + load32 PA(.iodc_panic_ret), %rp + ldw MEM_CONS+40(%r0),%r1 // ENTRY_IODC + bv,n (%r1) +.iodc_panic_ret: + b . /* wait endless with ... */ + or %r10,%r10,%r10 /* qemu idle sleep */ +msg1: .ascii "Can't boot kernel which was built for PA8x00 CPUs on this machine.\r\n" +msg1_end: + +$cpu_ok: +#endif + + .level PA_ASM_LEVEL + + /* Initialize startup VM. Just map first 16/32 MB of memory */ + load32 PA(swapper_pg_dir),%r4 + mtctl %r4,%cr24 /* Initialize kernel root pointer */ + mtctl %r4,%cr25 /* Initialize user root pointer */ + +#if CONFIG_PGTABLE_LEVELS == 3 + /* Set pmd in pgd */ + load32 PA(pmd0),%r5 + shrd %r5,PxD_VALUE_SHIFT,%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 +#endif + + /* Fill in pmd with enough pte directories */ + load32 PA(pg0),%r1 + SHRREG %r1,PxD_VALUE_SHIFT,%r3 + ldo (PxD_FLAG_PRESENT+PxD_FLAG_VALID)(%r3),%r3 + + ldi ASM_PT_INITIAL,%r1 + +1: + stw %r3,0(%r4) + ldo (PAGE_SIZE >> PxD_VALUE_SHIFT)(%r3),%r3 + addib,> -1,%r1,1b +#if CONFIG_PGTABLE_LEVELS == 3 + ldo ASM_PMD_ENTRY_SIZE(%r4),%r4 +#else + ldo ASM_PGD_ENTRY_SIZE(%r4),%r4 +#endif + + + /* 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 */ + load32 (1<<(KERNEL_INITIAL_ORDER-PAGE_SHIFT)),%r11 /* PFN count */ + load32 PA(pg0),%r1 + +$pgt_fill_loop: + STREGM %r3,ASM_PTE_ENTRY_SIZE(%r1) + 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_parisc,%r11 + + /* And the initial task pointer */ + load32 init_task,%r6 + mtctl %r6,%cr30 + + /* And the stack pointer too */ + load32 init_stack,%sp + tophys_r1 %sp +#if defined(CONFIG_64BIT) && defined(CONFIG_FUNCTION_TRACER) + .import _mcount,data + /* initialize mcount FPTR */ + /* Get the global data pointer */ + loadgp + load32 PA(_mcount), %r10 + std %dp,0x18(%r10) +#endif + +#ifdef CONFIG_64BIT + /* Get PDCE_PROC for monarch CPU. */ +#define MEM_PDC_LO 0x388 +#define MEM_PDC_HI 0x35C + ldw MEM_PDC_LO(%r0),%r3 + ldw MEM_PDC_HI(%r0),%r10 + depd %r10, 31, 32, %r3 /* move to upper word */ +#endif + + +#ifdef CONFIG_SMP + /* 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. + */ + load32 PA(smp_slave_stext),%r10 + stw %r10,0x10(%r0) /* MEM_RENDEZ */ + stw %r0,0x28(%r0) /* MEM_RENDEZ_HI - assume addr < 4GB */ + + /* FALLTHROUGH */ + .procend + +#ifdef CONFIG_HOTPLUG_CPU + /* common_stext is far away in another section... jump there */ + load32 PA(common_stext), %rp + bv,n (%rp) + + /* common_stext and smp_slave_stext needs to be in text section */ + .text +#endif + + /* + ** Code Common to both Monarch and Slave processors. + ** Entry: + ** + ** 1.1: + ** %r11 must contain RFI target address. + ** %r25/%r26 args to pass to target function + ** %r2 in case rfi target decides it didn't like something + ** + ** 2.0w: + ** %r3 PDCE_PROC address + ** %r11 RFI target address + ** + ** Caller must init: SR4-7, %sp, %r10, %cr24/25, + */ +common_stext: + .proc + .callinfo +#else + /* Clear PDC entry point - we won't use it */ + stw %r0,0x10(%r0) /* MEM_RENDEZ */ + stw %r0,0x28(%r0) /* MEM_RENDEZ_HI */ +#endif /*CONFIG_SMP*/ + +#ifdef CONFIG_64BIT + mfctl %cr30,%r6 /* PCX-W2 firmware bug */ + tophys_r1 %r6 + + /* Save the rfi target address */ + STREG %r11, TASK_PT_GR11(%r6) + /* Switch to wide mode Superdome doesn't support narrow PDC + ** calls. + */ +1: mfia %rp /* clear upper part of pcoq */ + ldo 2f-1b(%rp),%rp + depdi 0,31,32,%rp + bv (%rp) + ssm PSW_SM_W,%r0 + + /* Set Wide mode as the "Default" (eg for traps) + ** First trap occurs *right* after (or part of) rfi for slave CPUs. + ** Someday, palo might not do this for the Monarch either. + */ +2: + + ldo PDC_PSW(%r0),%arg0 /* 21 */ + ldo PDC_PSW_SET_DEFAULTS(%r0),%arg1 /* 2 */ + ldo PDC_PSW_WIDE_BIT(%r0),%arg2 /* 2 */ + load32 PA(stext_pdc_ret), %rp + bv (%r3) + copy %r0,%arg3 + +stext_pdc_ret: + LDREG TASK_PT_GR11(%r6), %r11 + tovirt_r1 %r6 + mtctl %r6,%cr30 /* restore task thread info */ +#endif + + /* PARANOID: clear user scratch/user space SR's */ + mtsp %r0,%sr0 + mtsp %r0,%sr1 + mtsp %r0,%sr2 + mtsp %r0,%sr3 + + /* Initialize Protection Registers */ + mtctl %r0,%cr8 + mtctl %r0,%cr9 + mtctl %r0,%cr12 + mtctl %r0,%cr13 + + /* Initialize the global data pointer */ + loadgp + + /* Set up our interrupt table. HPMCs might not work after this! + * + * We need to install the correct iva for PA1.1 or PA2.0. The + * following short sequence of instructions can determine this + * (without being illegal on a PA1.1 machine). + */ +#ifndef CONFIG_64BIT + ldi 32,%r10 + mtctl %r10,%cr11 + .level 2.0 + mfctl,w %cr11,%r10 + .level 1.1 + comib,<>,n 0,%r10,$is_pa20 + ldil L%PA(fault_vector_11),%r10 + b $install_iva + ldo R%PA(fault_vector_11)(%r10),%r10 + +$is_pa20: + .level PA_ASM_LEVEL /* restore 1.1 || 2.0w */ +#endif /*!CONFIG_64BIT*/ + load32 PA(fault_vector_20),%r10 + +$install_iva: + mtctl %r10,%cr14 + + b aligned_rfi /* Prepare to RFI! Man all the cannons! */ + nop + + .align 128 +aligned_rfi: + pcxt_ssm_bug + + copy %r3, %arg0 /* PDCE_PROC for smp_callin() */ + + rsm PSW_SM_QUIET,%r0 /* off troublesome PSW bits */ + /* Don't need NOPs, have 8 compliant insn before rfi */ + + mtctl %r0,%cr17 /* Clear IIASQ tail */ + mtctl %r0,%cr17 /* Clear IIASQ head */ + + /* Load RFI target into PC queue */ + mtctl %r11,%cr18 /* IIAOQ head */ + ldo 4(%r11),%r11 + mtctl %r11,%cr18 /* IIAOQ tail */ + + load32 KERNEL_PSW,%r10 + mtctl %r10,%ipsw + + tovirt_r1 %sp + + /* Jump through hyperspace to Virt Mode */ + rfi + nop + + .procend + +#ifdef CONFIG_SMP + + .import smp_init_current_idle_task,data + .import smp_callin,code + +#ifndef CONFIG_64BIT +smp_callin_rtn: + .proc + .callinfo + break 1,1 /* Break if returned from start_secondary */ + nop + nop + .procend +#endif /*!CONFIG_64BIT*/ + +/*************************************************************************** +* smp_slave_stext is executed by all non-monarch Processors when the Monarch +* pokes the slave CPUs in smp.c:smp_boot_cpus(). +* +* Once here, registers values are initialized in order to branch to virtual +* mode. Once all available/eligible CPUs are in virtual mode, all are +* released and start out by executing their own idle task. +*****************************************************************************/ +smp_slave_stext: + .proc + .callinfo + + /* + ** Initialize Space registers + */ + mtsp %r0,%sr4 + mtsp %r0,%sr5 + mtsp %r0,%sr6 + mtsp %r0,%sr7 + +#ifdef CONFIG_64BIT + /* + * Enable Wide mode early, in case the task_struct for the idle + * task in smp_init_current_idle_task was allocated above 4GB. + */ +1: mfia %rp /* clear upper part of pcoq */ + ldo 2f-1b(%rp),%rp + depdi 0,31,32,%rp + bv (%rp) + ssm PSW_SM_W,%r0 +2: +#endif + + /* Initialize the SP - monarch sets up smp_init_current_idle_task */ + load32 PA(smp_init_current_idle_task),%r6 + LDREG 0(%r6),%r6 + mtctl %r6,%cr30 + tophys_r1 %r6 + LDREG TASK_STACK(%r6),%sp + tophys_r1 %sp + ldo FRAME_SIZE(%sp),%sp + + /* point CPU to kernel page tables */ + load32 PA(swapper_pg_dir),%r4 + mtctl %r4,%cr24 /* Initialize kernel root pointer */ + mtctl %r4,%cr25 /* Initialize user root pointer */ + +#ifdef CONFIG_64BIT + /* Setup PDCE_PROC entry */ + copy %arg0,%r3 +#else + /* Load RFI *return* address in case smp_callin bails */ + load32 smp_callin_rtn,%r2 +#endif + + /* Load RFI target address. */ + load32 smp_callin,%r11 + + /* ok...common code can handle the rest */ + b common_stext + nop + + .procend +#endif /* CONFIG_SMP */ + +#ifndef CONFIG_64BIT + .section .data..ro_after_init + + .align 4 + .export $global$,data + + .type $global$,@object + .size $global$,4 +$global$: + .word 0 +#endif /*!CONFIG_64BIT*/ diff --git a/arch/parisc/kernel/hpmc.S b/arch/parisc/kernel/hpmc.S new file mode 100644 index 000000000..eb2e4bd67 --- /dev/null +++ b/arch/parisc/kernel/hpmc.S @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * HPMC (High Priority Machine Check) handler. + * + * Copyright (C) 1999 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 1999 Hewlett-Packard (Frank Rowand) + * Copyright (C) 2000 Hewlett-Packard (John Marvin) + */ + + +/* + * This HPMC handler retrieves the HPMC pim data, resets IO and + * returns to the default trap handler with code set to 1 (HPMC). + * The default trap handler calls handle interruption, which + * does a stack and register dump. This at least allows kernel + * developers to get back to C code in virtual mode, where they + * have the option to examine and print values from memory that + * would help in debugging an HPMC caused by a software bug. + * + * There is more to do here: + * + * 1) On MP systems we need to synchronize processors + * before calling pdc/iodc. + * 2) We should be checking the system state and not + * returning to the fault handler if things are really + * bad. + * + */ + + .level 1.1 + +#include <asm/assembly.h> +#include <asm/pdc.h> +#include <asm/psw.h> + +#include <linux/linkage.h> +#include <linux/init.h> + + /* + * stack for os_hpmc, the HPMC handler. + * buffer for IODC procedures (for the HPMC handler). + * + * IODC requires 7K byte stack. That leaves 1K byte for os_hpmc. + */ + + .import toc_stack,data +#define hpmc_stack toc_stack /* re-use the TOC stack */ + +#define HPMC_IODC_BUF_SIZE 0x8000 + + __PAGE_ALIGNED_BSS + .align 4096 +hpmc_iodc_buf: + .block HPMC_IODC_BUF_SIZE + + .section .bss + .align 8 +hpmc_raddr: + .block 128 + +#define HPMC_PIM_DATA_SIZE 896 /* Enough to hold all architected 2.0 state */ + + .section .bss + .align 8 +ENTRY(hpmc_pim_data) + .block HPMC_PIM_DATA_SIZE +END(hpmc_pim_data) + + .text + + .import intr_save, code + .align 16 +ENTRY(os_hpmc) +.os_hpmc: + + /* + * registers modified: + * + * Using callee saves registers without saving them. The + * original values are in the pim dump if we need them. + * + * r2 (rp) return pointer + * r3 address of PDCE_PROC + * r4 scratch + * r5 scratch + * r23 (arg3) procedure arg + * r24 (arg2) procedure arg + * r25 (arg1) procedure arg + * r26 (arg0) procedure arg + * r30 (sp) stack pointer + * + * registers read: + * + * r26 contains address of PDCE_PROC on entry + * r28 (ret0) return value from procedure + */ + + copy arg0, %r3 /* save address of PDCE_PROC */ + + /* + * disable nested HPMCs + * + * Increment os_hpmc checksum to invalidate it. + * Do this before turning the PSW M bit off. + */ + + mfctl %cr14, %r4 + ldw 52(%r4),%r5 + addi 1,%r5,%r5 + stw %r5,52(%r4) + + /* MP_FIXME: synchronize all processors. */ + + /* Setup stack pointer. */ + + load32 PA(hpmc_stack),sp + + ldo 128(sp),sp /* leave room for arguments */ + + /* + * Most PDC routines require that the M bit be off. + * So turn on the Q bit and turn off the M bit. + */ + + ldi PSW_SM_Q,%r4 /* PSW Q on, PSW M off */ + mtctl %r4,ipsw + mtctl %r0,pcsq + mtctl %r0,pcsq + load32 PA(os_hpmc_1),%r4 + mtctl %r4,pcoq + ldo 4(%r4),%r4 + mtctl %r4,pcoq + rfi + nop + +os_hpmc_1: + + /* Call PDC_PIM to get HPMC pim info */ + + /* + * Note that on some newer boxes, PDC_PIM must be called + * before PDC_IO if you want IO to be reset. PDC_PIM sets + * a flag that PDC_IO examines. + */ + + ldo PDC_PIM(%r0), arg0 + ldo PDC_PIM_HPMC(%r0),arg1 /* Transfer HPMC data */ + load32 PA(hpmc_raddr),arg2 + load32 PA(hpmc_pim_data),arg3 + load32 HPMC_PIM_DATA_SIZE,%r4 + stw %r4,-52(sp) + + ldil L%PA(os_hpmc_2), rp + bv (r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_2)(rp), rp + +os_hpmc_2: + comib,<> 0,ret0, os_hpmc_fail + + /* Reset IO by calling the hversion dependent PDC_IO routine */ + + ldo PDC_IO(%r0),arg0 + ldo 0(%r0),arg1 /* log IO errors */ + ldo 0(%r0),arg2 /* reserved */ + ldo 0(%r0),arg3 /* reserved */ + stw %r0,-52(sp) /* reserved */ + + ldil L%PA(os_hpmc_3),rp + bv (%r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_3)(rp),rp + +os_hpmc_3: + + /* FIXME? Check for errors from PDC_IO (-1 might be OK) */ + + /* + * Initialize the IODC console device (HPA,SPA, path etc. + * are stored on page 0. + */ + + /* + * Load IODC into hpmc_iodc_buf by calling PDC_IODC. + * Note that PDC_IODC handles flushing the appropriate + * data and instruction cache lines. + */ + + ldo PDC_IODC(%r0),arg0 + ldo PDC_IODC_READ(%r0),arg1 + load32 PA(hpmc_raddr),arg2 + ldw BOOT_CONSOLE_HPA_OFFSET(%r0),arg3 /* console hpa */ + ldo PDC_IODC_RI_INIT(%r0),%r4 + stw %r4,-52(sp) + load32 PA(hpmc_iodc_buf),%r4 + stw %r4,-56(sp) + load32 HPMC_IODC_BUF_SIZE,%r4 + stw %r4,-60(sp) + + ldil L%PA(os_hpmc_4),rp + bv (%r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_4)(rp),rp + +os_hpmc_4: + comib,<> 0,ret0,os_hpmc_fail + + /* Call the entry init (just loaded by PDC_IODC) */ + + ldw BOOT_CONSOLE_HPA_OFFSET(%r0),arg0 /* console hpa */ + ldo ENTRY_INIT_MOD_DEV(%r0), arg1 + ldw BOOT_CONSOLE_SPA_OFFSET(%r0),arg2 /* console spa */ + depi 0,31,11,arg2 /* clear bits 21-31 */ + ldo BOOT_CONSOLE_PATH_OFFSET(%r0),arg3 /* console path */ + load32 PA(hpmc_raddr),%r4 + stw %r4, -52(sp) + stw %r0, -56(sp) /* HV */ + stw %r0, -60(sp) /* HV */ + stw %r0, -64(sp) /* HV */ + stw %r0, -68(sp) /* lang, must be zero */ + + load32 PA(hpmc_iodc_buf),%r5 + ldil L%PA(os_hpmc_5),rp + bv (%r5) + ldo R%PA(os_hpmc_5)(rp),rp + +os_hpmc_5: + comib,<> 0,ret0,os_hpmc_fail + + /* Prepare to call intr_save */ + + /* + * Load kernel page directory (load into user also, since + * we don't intend to ever return to user land anyway) + */ + + load32 PA(swapper_pg_dir),%r4 + mtctl %r4,%cr24 /* Initialize kernel root pointer */ + mtctl %r4,%cr25 /* Initialize user root pointer */ + + /* Clear sr4-sr7 */ + + mtsp %r0, %sr4 + mtsp %r0, %sr5 + mtsp %r0, %sr6 + mtsp %r0, %sr7 + + tovirt_r1 %r30 /* make sp virtual */ + + rsm PSW_SM_Q,%r0 /* Clear Q bit */ + ldi 1,%r8 /* Set trap code to "1" for HPMC */ + load32 PA(intr_save),%r1 + be 0(%sr7,%r1) + nop + +os_hpmc_fail: + + /* + * Reset the system + * + * Some systems may lockup from a broadcast reset, so try the + * hversion PDC_BROADCAST_RESET() first. + * MP_FIXME: reset all processors if more than one central bus. + */ + + /* PDC_BROADCAST_RESET() */ + + ldo PDC_BROADCAST_RESET(%r0),arg0 + ldo 0(%r0),arg1 /* do reset */ + + ldil L%PA(os_hpmc_6),rp + bv (%r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_6)(rp),rp + +os_hpmc_6: + + /* + * possible return values: + * -1 non-existent procedure + * -2 non-existent option + * -16 unaligned stack + * + * If call returned, do a broadcast reset. + */ + + ldil L%0xfffc0000,%r4 /* IO_BROADCAST */ + ldo 5(%r0),%r5 + stw %r5,48(%r4) /* CMD_RESET to IO_COMMAND offset */ + + b . + nop + .align 16 /* make function length multiple of 16 bytes */ diff --git a/arch/parisc/kernel/inventory.c b/arch/parisc/kernel/inventory.c new file mode 100644 index 000000000..7ab2f2a54 --- /dev/null +++ b/arch/parisc/kernel/inventory.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * inventory.c + * + * Copyright (c) 1999 The Puffin Group (David Kennedy and Alex deVries) + * Copyright (c) 2001 Matthew Wilcox for Hewlett-Packard + * + * These are the routines to discover what hardware exists in this box. + * This task is complicated by there being 3 different ways of + * performing an inventory, depending largely on the age of the box. + * The recommended way to do this is to check to see whether the machine + * is a `Snake' first, then try System Map, then try PAT. We try System + * Map before checking for a Snake -- this probably doesn't cause any + * problems, but... + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/platform_device.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/mmzone.h> +#include <asm/pdc.h> +#include <asm/pdcpat.h> +#include <asm/processor.h> +#include <asm/page.h> +#include <asm/parisc-device.h> +#include <asm/tlbflush.h> + +/* +** Debug options +** DEBUG_PAT Dump details which PDC PAT provides about ranges/devices. +*/ +#undef DEBUG_PAT + +int pdc_type __ro_after_init = PDC_TYPE_ILLEGAL; + +/* cell number and location (PAT firmware only) */ +unsigned long parisc_cell_num __ro_after_init; +unsigned long parisc_cell_loc __ro_after_init; +unsigned long parisc_pat_pdc_cap __ro_after_init; + + +void __init setup_pdc(void) +{ + long status; + unsigned int bus_id; + struct pdc_system_map_mod_info module_result; + struct pdc_module_path module_path; + struct pdc_model model; +#ifdef CONFIG_64BIT + struct pdc_pat_cell_num cell_info; +#endif + + /* Determine the pdc "type" used on this machine */ + + printk(KERN_INFO "Determining PDC firmware type: "); + + status = pdc_system_map_find_mods(&module_result, &module_path, 0); + if (status == PDC_OK) { + pdc_type = PDC_TYPE_SYSTEM_MAP; + pr_cont("System Map.\n"); + return; + } + + /* + * If the machine doesn't support PDC_SYSTEM_MAP then either it + * is a pdc pat box, or it is an older box. All 64 bit capable + * machines are either pdc pat boxes or they support PDC_SYSTEM_MAP. + */ + + /* + * TODO: We should test for 64 bit capability and give a + * clearer message. + */ + +#ifdef CONFIG_64BIT + status = pdc_pat_cell_get_number(&cell_info); + if (status == PDC_OK) { + unsigned long legacy_rev, pat_rev; + pdc_type = PDC_TYPE_PAT; + pr_cont("64 bit PAT.\n"); + parisc_cell_num = cell_info.cell_num; + parisc_cell_loc = cell_info.cell_loc; + pr_info("PAT: Running on cell %lu and location %lu.\n", + parisc_cell_num, parisc_cell_loc); + status = pdc_pat_pd_get_pdc_revisions(&legacy_rev, + &pat_rev, &parisc_pat_pdc_cap); + pr_info("PAT: legacy revision 0x%lx, pat_rev 0x%lx, pdc_cap 0x%lx, S-PTLB %d, HPMC_RENDEZ %d.\n", + legacy_rev, pat_rev, parisc_pat_pdc_cap, + parisc_pat_pdc_cap + & PDC_PAT_CAPABILITY_BIT_SIMULTANEOUS_PTLB ? 1:0, + parisc_pat_pdc_cap + & PDC_PAT_CAPABILITY_BIT_PDC_HPMC_RENDEZ ? 1:0); + return; + } +#endif + + /* Check the CPU's bus ID. There's probably a better test. */ + + status = pdc_model_info(&model); + + bus_id = (model.hversion >> (4 + 7)) & 0x1f; + + switch (bus_id) { + case 0x4: /* 720, 730, 750, 735, 755 */ + case 0x6: /* 705, 710 */ + case 0x7: /* 715, 725 */ + case 0x8: /* 745, 747, 742 */ + case 0xA: /* 712 and similar */ + case 0xC: /* 715/64, at least */ + + pdc_type = PDC_TYPE_SNAKE; + pr_cont("Snake.\n"); + return; + + default: /* Everything else */ + + pr_cont("Unsupported.\n"); + panic("If this is a 64-bit machine, please try a 64-bit kernel.\n"); + } +} + +#define PDC_PAGE_ADJ_SHIFT (PAGE_SHIFT - 12) /* pdc pages are always 4k */ + +static void __init +set_pmem_entry(physmem_range_t *pmem_ptr, unsigned long start, + unsigned long pages4k) +{ + /* Rather than aligning and potentially throwing away + * memory, we'll assume that any ranges are already + * nicely aligned with any reasonable page size, and + * panic if they are not (it's more likely that the + * pdc info is bad in this case). + */ + + 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"); + } + + pmem_ptr->start_pfn = (start >> PAGE_SHIFT); + pmem_ptr->pages = (pages4k >> PDC_PAGE_ADJ_SHIFT); +} + +static void __init pagezero_memconfig(void) +{ + unsigned long npages; + + /* Use the 32 bit information from page zero to create a single + * entry in the pmem_ranges[] table. + * + * We currently don't support machines with contiguous memory + * >= 4 Gb, who report that memory using 64 bit only fields + * on page zero. It's not worth doing until it can be tested, + * and it is not clear we can support those machines for other + * reasons. + * + * If that support is done in the future, this is where it + * should be done. + */ + + npages = (PAGE_ALIGN(PAGE0->imm_max_mem) >> PAGE_SHIFT); + set_pmem_entry(pmem_ranges,0UL,npages); + npmem_ranges = 1; +} + +#ifdef CONFIG_64BIT + +/* All of the PDC PAT specific code is 64-bit only */ + +/* +** The module object is filled via PDC_PAT_CELL[Return Cell Module]. +** If a module is found, register module will get the IODC bytes via +** pdc_iodc_read() using the PA view of conf_base_addr for the hpa parameter. +** +** The IO view can be used by PDC_PAT_CELL[Return Cell Module] +** only for SBAs and LBAs. This view will cause an invalid +** argument error for all other cell module types. +** +*/ + +static int __init +pat_query_module(ulong pcell_loc, ulong mod_index) +{ + 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); + + 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)); + if (!dev) { + kfree(pa_pdc_cell); + return PDC_OK; + } + + /* alloc_pa_dev sets dev->hpa */ + + /* + ** save parameters in the parisc_device + ** (The idea being the device driver will call pdc_pat_cell_module() + ** and store the results in its own data structure.) + */ + dev->pcell_loc = pcell_loc; + dev->mod_index = 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->mod0 = pa_pdc_cell->mod[0]; + + register_parisc_device(dev); /* advertise device */ + +#ifdef DEBUG_PAT + /* dump what we see so far... */ + switch (PAT_GET_ENTITY(dev->mod_info)) { + pdc_pat_cell_mod_maddr_block_t io_pdc_cell; + unsigned long i; + + case PAT_ENTITY_PROC: + printk(KERN_DEBUG "PAT_ENTITY_PROC: id_eid 0x%lx\n", + 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]); + break; + case PAT_ENTITY_CA: + printk(KERN_DEBUG "PAT_ENTITY_CA: %ld\n", pcell_loc); + break; + + case PAT_ENTITY_PBC: + printk(KERN_DEBUG "PAT_ENTITY_PBC: "); + goto print_ranges; + + case PAT_ENTITY_SBA: + printk(KERN_DEBUG "PAT_ENTITY_SBA: "); + goto print_ranges; + + case PAT_ENTITY_LBA: + printk(KERN_DEBUG "PAT_ENTITY_LBA: "); + + 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 + " 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) */ + 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) */ + } + printk(KERN_DEBUG "\n"); + break; + } +#endif /* DEBUG_PAT */ + + kfree(pa_pdc_cell); + + return PDC_OK; +} + + +/* pat pdc can return information about a variety of different + * types of memory (e.g. firmware,i/o, etc) but we only care about + * the usable physical ram right now. Since the firmware specific + * information is allocated on the stack, we'll be generous, in + * case there is a lot of other information we don't care about. + */ + +#define PAT_MAX_RANGES (4 * MAX_PHYSMEM_RANGES) + +static void __init pat_memconfig(void) +{ + unsigned long actual_len; + struct pdc_pat_pd_addr_map_entry mem_table[PAT_MAX_RANGES+1]; + struct pdc_pat_pd_addr_map_entry *mtbl_ptr; + physmem_range_t *pmem_ptr; + long status; + int entries; + unsigned long length; + int i; + + length = (PAT_MAX_RANGES + 1) * sizeof(struct pdc_pat_pd_addr_map_entry); + + status = pdc_pat_pd_get_addr_map(&actual_len, mem_table, length, 0L); + + if ((status != PDC_OK) + || ((actual_len % sizeof(struct pdc_pat_pd_addr_map_entry)) != 0)) { + + /* The above pdc call shouldn't fail, but, just in + * case, just use the PAGE0 info. + */ + + printk("\n\n\n"); + printk(KERN_WARNING "WARNING! Could not get full memory configuration. " + "All memory may not be used!\n\n\n"); + pagezero_memconfig(); + return; + } + + entries = actual_len / sizeof(struct pdc_pat_pd_addr_map_entry); + + if (entries > PAT_MAX_RANGES) { + printk(KERN_WARNING "This Machine has more memory ranges than we support!\n"); + printk(KERN_WARNING "Some memory may not be used!\n"); + } + + /* Copy information into the firmware independent pmem_ranges + * array, skipping types we don't care about. Notice we said + * "may" above. We'll use all the entries that were returned. + */ + + npmem_ranges = 0; + mtbl_ptr = mem_table; + pmem_ptr = pmem_ranges; /* Global firmware independent table */ + for (i = 0; i < entries; i++,mtbl_ptr++) { + if ( (mtbl_ptr->entry_type != PAT_MEMORY_DESCRIPTOR) + || (mtbl_ptr->memory_type != PAT_MEMTYPE_MEMORY) + || (mtbl_ptr->pages == 0) + || ( (mtbl_ptr->memory_usage != PAT_MEMUSE_GENERAL) + && (mtbl_ptr->memory_usage != PAT_MEMUSE_GI) + && (mtbl_ptr->memory_usage != PAT_MEMUSE_GNI) ) ) { + + continue; + } + + if (npmem_ranges == MAX_PHYSMEM_RANGES) { + printk(KERN_WARNING "This Machine has more memory ranges than we support!\n"); + printk(KERN_WARNING "Some memory will not be used!\n"); + break; + } + + set_pmem_entry(pmem_ptr++,mtbl_ptr->paddr,mtbl_ptr->pages); + npmem_ranges++; + } +} + +static int __init pat_inventory(void) +{ + int status; + ulong mod_index = 0; + struct pdc_pat_cell_num cell_info; + + /* + ** Note: Prelude (and it's successors: Lclass, A400/500) only + ** implement PDC_PAT_CELL sub-options 0 and 2. + */ + status = pdc_pat_cell_get_number(&cell_info); + if (status != PDC_OK) { + return 0; + } + +#ifdef DEBUG_PAT + printk(KERN_DEBUG "CELL_GET_NUMBER: 0x%lx 0x%lx\n", cell_info.cell_num, + cell_info.cell_loc); +#endif + + while (PDC_OK == pat_query_module(cell_info.cell_loc, mod_index)) { + mod_index++; + } + + return mod_index; +} + +/* We only look for extended memory ranges on a 64 bit capable box */ +static void __init sprockets_memconfig(void) +{ + struct pdc_memory_table_raddr r_addr; + struct pdc_memory_table mem_table[MAX_PHYSMEM_RANGES]; + struct pdc_memory_table *mtbl_ptr; + physmem_range_t *pmem_ptr; + long status; + int entries; + int i; + + status = pdc_mem_mem_table(&r_addr,mem_table, + (unsigned long)MAX_PHYSMEM_RANGES); + + if (status != PDC_OK) { + + /* The above pdc call only works on boxes with sprockets + * firmware (newer B,C,J class). Other non PAT PDC machines + * do support more than 3.75 Gb of memory, but we don't + * support them yet. + */ + + pagezero_memconfig(); + return; + } + + if (r_addr.entries_total > MAX_PHYSMEM_RANGES) { + printk(KERN_WARNING "This Machine has more memory ranges than we support!\n"); + printk(KERN_WARNING "Some memory will not be used!\n"); + } + + entries = (int)r_addr.entries_returned; + + npmem_ranges = 0; + mtbl_ptr = mem_table; + pmem_ptr = pmem_ranges; /* Global firmware independent table */ + for (i = 0; i < entries; i++,mtbl_ptr++) { + set_pmem_entry(pmem_ptr++,mtbl_ptr->paddr,mtbl_ptr->pages); + npmem_ranges++; + } +} + +#else /* !CONFIG_64BIT */ + +#define pat_inventory() do { } while (0) +#define pat_memconfig() do { } while (0) +#define sprockets_memconfig() pagezero_memconfig() + +#endif /* !CONFIG_64BIT */ + + +#ifndef CONFIG_PA20 + +/* Code to support Snake machines (7[2350], 7[235]5, 715/Scorpio) */ + +static struct parisc_device * __init +legacy_create_device(struct pdc_memory_map *r_addr, + struct pdc_module_path *module_path) +{ + struct parisc_device *dev; + int status = pdc_mem_map_hpa(r_addr, module_path); + if (status != PDC_OK) + return NULL; + + dev = alloc_pa_dev(r_addr->hpa, &module_path->path); + if (dev == NULL) + return NULL; + + register_parisc_device(dev); + return dev; +} + +/** + * snake_inventory + * + * Before PDC_SYSTEM_MAP was invented, the PDC_MEM_MAP call was used. + * To use it, we initialise the mod_path.bc to 0xff and try all values of + * mod to get the HPA for the top-level devices. Bus adapters may have + * sub-devices which are discovered by setting bc[5] to 0 and bc[4] to the + * module, then trying all possible functions. + */ +static void __init snake_inventory(void) +{ + int mod; + for (mod = 0; mod < 16; mod++) { + struct parisc_device *dev; + struct pdc_module_path module_path; + struct pdc_memory_map r_addr; + unsigned int func; + + memset(module_path.path.bc, 0xff, 6); + module_path.path.mod = mod; + dev = legacy_create_device(&r_addr, &module_path); + if ((!dev) || (dev->id.hw_type != HPHW_BA)) + continue; + + memset(module_path.path.bc, 0xff, 4); + module_path.path.bc[4] = mod; + + for (func = 0; func < 16; func++) { + module_path.path.bc[5] = 0; + module_path.path.mod = func; + legacy_create_device(&r_addr, &module_path); + } + } +} + +#else /* CONFIG_PA20 */ +#define snake_inventory() do { } while (0) +#endif /* CONFIG_PA20 */ + +/* Common 32/64 bit based code goes here */ + +/** + * add_system_map_addresses - Add additional addresses to the parisc device. + * @dev: The parisc device. + * @num_addrs: Then number of addresses to add; + * @module_instance: The system_map module instance. + * + * This function adds any additional addresses reported by the system_map + * firmware to the parisc device. + */ +static void __init +add_system_map_addresses(struct parisc_device *dev, int num_addrs, + int module_instance) +{ + int i; + long status; + struct pdc_system_map_addr_info addr_result; + + dev->addr = kmalloc_array(num_addrs, sizeof(*dev->addr), GFP_KERNEL); + if(!dev->addr) { + printk(KERN_ERR "%s %s(): memory allocation failure\n", + __FILE__, __func__); + return; + } + + for(i = 1; i <= num_addrs; ++i) { + status = pdc_system_map_find_addrs(&addr_result, + module_instance, i); + if(PDC_OK == status) { + dev->addr[dev->num_addrs] = (unsigned long)addr_result.mod_addr; + dev->num_addrs++; + } else { + printk(KERN_WARNING + "Bad PDC_FIND_ADDRESS status return (%ld) for index %d\n", + status, i); + } + } +} + +/** + * system_map_inventory - Retrieve firmware devices via SYSTEM_MAP. + * + * This function attempts to retrieve and register all the devices firmware + * knows about via the SYSTEM_MAP PDC call. + */ +static void __init system_map_inventory(void) +{ + int i; + long status = PDC_OK; + + for (i = 0; i < 256; i++) { + struct parisc_device *dev; + struct pdc_system_map_mod_info module_result; + struct pdc_module_path module_path; + + status = pdc_system_map_find_mods(&module_result, + &module_path, i); + if ((status == PDC_BAD_PROC) || (status == PDC_NE_MOD)) + break; + if (status != PDC_OK) + continue; + + dev = alloc_pa_dev(module_result.mod_addr, &module_path.path); + if (!dev) + continue; + + register_parisc_device(dev); + + /* if available, get the additional addresses for a module */ + if (!module_result.add_addrs) + continue; + + add_system_map_addresses(dev, module_result.add_addrs, i); + } + + walk_central_bus(); + return; +} + +void __init do_memory_inventory(void) +{ + switch (pdc_type) { + + case PDC_TYPE_PAT: + pat_memconfig(); + break; + + case PDC_TYPE_SYSTEM_MAP: + sprockets_memconfig(); + break; + + case PDC_TYPE_SNAKE: + pagezero_memconfig(); + return; + + default: + panic("Unknown PDC type!\n"); + } + + if (npmem_ranges == 0 || pmem_ranges[0].start_pfn != 0) { + printk(KERN_WARNING "Bad memory configuration returned!\n"); + printk(KERN_WARNING "Some memory may not be used!\n"); + pagezero_memconfig(); + } +} + +void __init do_device_inventory(void) +{ + printk(KERN_INFO "Searching for devices...\n"); + + init_parisc_bus(); + + switch (pdc_type) { + + case PDC_TYPE_PAT: + pat_inventory(); + break; + + case PDC_TYPE_SYSTEM_MAP: + system_map_inventory(); + break; + + case PDC_TYPE_SNAKE: + snake_inventory(); + break; + + default: + panic("Unknown PDC type!\n"); + } + printk(KERN_INFO "Found devices:\n"); + print_parisc_devices(); + +#if defined(CONFIG_64BIT) && defined(CONFIG_SMP) + pa_serialize_tlb_flushes = machine_has_merced_bus(); + if (pa_serialize_tlb_flushes) + pr_info("Merced bus found: Enable PxTLB serialization.\n"); +#endif + +#if defined(CONFIG_FW_CFG_SYSFS) + if (running_on_qemu) { + struct resource res[3] = {0,}; + unsigned int base; + + base = ((unsigned long long) PAGE0->pad0[2] << 32) + | PAGE0->pad0[3]; /* SeaBIOS stored it here */ + + res[0].name = "fw_cfg"; + res[0].start = base; + res[0].end = base + 8 - 1; + res[0].flags = IORESOURCE_MEM; + + res[1].name = "ctrl"; + res[1].start = 0; + res[1].flags = IORESOURCE_REG; + + res[2].name = "data"; + res[2].start = 4; + res[2].flags = IORESOURCE_REG; + + if (base) { + pr_info("Found qemu fw_cfg interface at %#08x\n", base); + platform_device_register_simple("fw_cfg", + PLATFORM_DEVID_NONE, res, 3); + } + } +#endif +} diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c new file mode 100644 index 000000000..9ddb2e397 --- /dev/null +++ b/arch/parisc/kernel/irq.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Code to handle x86 style IRQs plus some generic interrupt stuff. + * + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994, 1995, 1996, 1997, 1998 Ralf Baechle + * Copyright (C) 1999 SuSE GmbH (Philipp Rumpf, prumpf@tux.org) + * Copyright (C) 1999-2000 Grant Grundler + * Copyright (c) 2005 Matthew Wilcox + */ +#include <linux/bitops.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/types.h> +#include <linux/sched/task_stack.h> +#include <asm/io.h> + +#include <asm/softirq_stack.h> +#include <asm/smp.h> +#include <asm/ldcw.h> + +#undef PARISC_IRQ_CR16_COUNTS + +extern irqreturn_t timer_interrupt(int, void *); +extern irqreturn_t ipi_interrupt(int, void *); + +#define EIEM_MASK(irq) (1UL<<(CPU_IRQ_MAX - irq)) + +/* Bits in EIEM correlate with cpu_irq_action[]. +** Numbered *Big Endian*! (ie bit 0 is MSB) +*/ +static volatile unsigned long cpu_eiem = 0; + +/* +** 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 void cpu_mask_irq(struct irq_data *d) +{ + unsigned long eirr_bit = EIEM_MASK(d->irq); + + cpu_eiem &= ~eirr_bit; + /* 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_unmask_irq(unsigned int irq) +{ + unsigned long eirr_bit = EIEM_MASK(irq); + + cpu_eiem |= eirr_bit; + + /* 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 void cpu_unmask_irq(struct irq_data *d) +{ + __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; + + cpu_dest = cpumask_first_and(dest, cpu_online_mask); + if (cpu_dest >= nr_cpu_ids) + cpu_dest = cpumask_first(cpu_online_mask); + + return cpu_dest; +} +#endif + +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, + /* 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 + if (num_online_cpus() > 1) { + 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"); + seq_printf(p, "%*s: ", prec, "CAL"); + for_each_online_cpu(j) + seq_printf(p, "%10u ", irq_stats(j)->irq_call_count); + seq_puts(p, " Function call 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; + unsigned long flags; + + if (i == 0) { + seq_puts(p, " "); + for_each_online_cpu(j) + seq_printf(p, " CPU%d", j); + +#ifdef PARISC_IRQ_CR16_COUNTS + seq_printf(p, " [min/avg/max] (CPU cycle counts)"); +#endif + seq_putc(p, '\n'); + } + + if (i < NR_IRQS) { + struct irq_desc *desc = irq_to_desc(i); + struct irqaction *action; + + raw_spin_lock_irqsave(&desc->lock, flags); + action = desc->action; + if (!action) + goto skip; + seq_printf(p, "%3d: ", i); + + for_each_online_cpu(j) + seq_printf(p, "%10u ", irq_desc_kstat_cpu(desc, j)); + + seq_printf(p, " %14s", irq_desc_get_chip(desc)->name); +#ifndef PARISC_IRQ_CR16_COUNTS + seq_printf(p, " %s", action->name); + + while ((action = action->next)) + seq_printf(p, ", %s", action->name); +#else + for ( ;action; action = action->next) { + unsigned int k, avg, min, max; + + min = max = action->cr16_hist[0]; + + for (avg = k = 0; k < PARISC_CR16_HIST_SIZE; k++) { + int hist = action->cr16_hist[k]; + + if (hist) { + avg += hist; + } else + break; + + if (hist > max) max = hist; + if (hist < min) min = hist; + } + + avg /= k; + seq_printf(p, " %s[%d/%d/%d]", action->name, + min,avg,max); + } +#endif + + seq_putc(p, '\n'); + skip: + raw_spin_unlock_irqrestore(&desc->lock, flags); + } + + if (i == NR_IRQS) + arch_show_interrupts(p, 3); + + return 0; +} + + + +/* +** The following form a "set": Virtual IRQ, Transaction Address, Trans Data. +** Respectively, these map to IRQ region+EIRR, Processor HPA, EIRR bit. +** +** To use txn_XXX() interfaces, get a Virtual IRQ first. +** Then use that to get the Transaction address and data. +*/ + +int cpu_claim_irq(unsigned int irq, struct irq_chip *type, void *data) +{ + if (irq_has_action(irq)) + return -EBUSY; + if (irq_get_chip(irq) != &cpu_interrupt_type) + return -EBUSY; + + /* for iosapic interrupts */ + if (type) { + irq_set_chip_and_handler(irq, type, handle_percpu_irq); + irq_set_chip_data(irq, data); + __cpu_unmask_irq(irq); + } + return 0; +} + +int txn_claim_irq(int irq) +{ + return cpu_claim_irq(irq, NULL, NULL) ? -1 : irq; +} + +/* + * The bits_wide parameter accommodates the limitations of the HW/SW which + * use these bits: + * Legacy PA I/O (GSC/NIO): 5 bits (architected EIM register) + * V-class (EPIC): 6 bits + * N/L/A-class (iosapic): 8 bits + * PCI 2.2 MSI: 16 bits + * Some PCI devices: 32 bits (Symbios SCSI/ATM/HyperFabric) + * + * On the service provider side: + * o PA 1.1 (and PA2.0 narrow mode) 5-bits (width of EIR register) + * o PA 2.0 wide mode 6-bits (per processor) + * o IA64 8-bits (0-256 total) + * + * So a Legacy PA I/O device on a PA 2.0 box can't use all the bits supported + * by the processor...and the N/L-class I/O subsystem supports more bits than + * PA2.0 has. The first case is the problem. + */ +int txn_alloc_irq(unsigned int bits_wide) +{ + int irq; + + /* never return irq 0 cause that's the interval timer */ + for (irq = CPU_IRQ_BASE + 1; irq <= CPU_IRQ_MAX; irq++) { + if (cpu_claim_irq(irq, NULL, NULL) < 0) + continue; + if ((irq - CPU_IRQ_BASE) >= (1 << bits_wide)) + continue; + return irq; + } + + /* unlikely, but be prepared */ + return -1; +} + + +unsigned long txn_affinity_addr(unsigned int irq, int cpu) +{ +#ifdef CONFIG_SMP + struct irq_data *d = irq_get_irq_data(irq); + irq_data_update_affinity(d, 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; + + next_cpu++; /* assign to "next" CPU we want this bugger on */ + + /* validate entry */ + while ((next_cpu < nr_cpu_ids) && + (!per_cpu(cpu_data, next_cpu).txn_addr || + !cpu_online(next_cpu))) + next_cpu++; + + if (next_cpu >= nr_cpu_ids) + next_cpu = 0; /* nothing else, assign monarch */ + + return txn_affinity_addr(virt_irq, next_cpu); +} + + +unsigned int txn_alloc_data(unsigned int virt_irq) +{ + return virt_irq - CPU_IRQ_BASE; +} + +static inline int eirr_to_irq(unsigned long eirr) +{ + int bit = fls_long(eirr); + return (BITS_PER_LONG - bit) + TIMER_IRQ; +} + +#ifdef CONFIG_IRQSTACKS +/* + * IRQ STACK - used for irq handler + */ +#ifdef CONFIG_64BIT +#define IRQ_STACK_SIZE (4096 << 4) /* 64k irq stack size */ +#else +#define IRQ_STACK_SIZE (4096 << 3) /* 32k irq stack size */ +#endif + +union irq_stack_union { + unsigned long stack[IRQ_STACK_SIZE/sizeof(unsigned long)]; + volatile unsigned int slock[4]; + volatile unsigned int lock[1]; +}; + +static 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) + + unsigned long stack_start = (unsigned long) task_stack_page(current); + 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; + + /* exit if already in panic */ + if (sysctl_panic_on_stackoverflow < 0) + 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); + + 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) { + sysctl_panic_on_stackoverflow = -1; /* disable further checks */ + 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), + FRAME_ALIGN); /* 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. + */ + 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); + + /* free up irq stack usage. */ + *irq_stack_in_use = 1; +} + +#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK +void do_softirq_own_stack(void) +{ + execute_on_irq_stack(__do_softirq, 0); +} +#endif +#endif /* CONFIG_IRQSTACKS */ + +/* 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(); + struct irq_data *irq_data; +#ifdef CONFIG_SMP + cpumask_t dest; +#endif + + old_regs = set_irq_regs(regs); + local_irq_disable(); + irq_enter(); + + eirr_val = mfctl(23) & cpu_eiem & per_cpu(local_ack_eiem, cpu); + if (!eirr_val) + goto set_out; + irq = eirr_to_irq(eirr_val); + + irq_data = irq_get_irq_data(irq); + + /* Filter out spurious interrupts, mostly from serial port at bootup */ + if (unlikely(!irq_desc_has_action(irq_data_to_desc(irq_data)))) + goto set_out; + +#ifdef CONFIG_SMP + cpumask_copy(&dest, irq_data_get_affinity_mask(irq_data)); + if (irqd_is_per_cpu(irq_data) && + !cpumask_test_cpu(smp_processor_id(), &dest)) { + int cpu = cpumask_first(&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; + } +#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 void claim_cpu_irqs(void) +{ + unsigned long flags = IRQF_TIMER | IRQF_PERCPU | IRQF_IRQPOLL; + int i; + + for (i = CPU_IRQ_BASE; i <= CPU_IRQ_MAX; i++) { + irq_set_chip_and_handler(i, &cpu_interrupt_type, + handle_percpu_irq); + } + + irq_set_handler(TIMER_IRQ, handle_percpu_irq); + if (request_irq(TIMER_IRQ, timer_interrupt, flags, "timer", NULL)) + pr_err("Failed to register timer interrupt\n"); +#ifdef CONFIG_SMP + irq_set_handler(IPI_IRQ, handle_percpu_irq); + if (request_irq(IPI_IRQ, ipi_interrupt, IRQF_PERCPU, "IPI", NULL)) + pr_err("Failed to register IPI interrupt\n"); +#endif +} + +void init_IRQ(void) +{ + local_irq_disable(); /* PARANOID - should already be disabled */ + mtctl(~0UL, 23); /* EIRR : clear all pending external intr */ +#ifdef CONFIG_SMP + 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 */ +} diff --git a/arch/parisc/kernel/jump_label.c b/arch/parisc/kernel/jump_label.c new file mode 100644 index 000000000..e253b1345 --- /dev/null +++ b/arch/parisc/kernel/jump_label.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Helge Deller <deller@gmx.de> + * + * Based on arch/arm64/kernel/jump_label.c + */ +#include <linux/kernel.h> +#include <linux/jump_label.h> +#include <linux/bug.h> +#include <asm/alternative.h> +#include <asm/patch.h> + +static inline int reassemble_17(int as17) +{ + return (((as17 & 0x10000) >> 16) | + ((as17 & 0x0f800) << 5) | + ((as17 & 0x00400) >> 8) | + ((as17 & 0x003ff) << 3)); +} + +void arch_jump_label_transform(struct jump_entry *entry, + enum jump_label_type type) +{ + void *addr = (void *)jump_entry_code(entry); + u32 insn; + + if (type == JUMP_LABEL_JMP) { + void *target = (void *)jump_entry_target(entry); + int distance = target - addr; + /* + * Encode the PA1.1 "b,n" instruction with a 17-bit + * displacement. In case we hit the BUG(), we could use + * another branch instruction with a 22-bit displacement on + * 64-bit CPUs instead. But this seems sufficient for now. + */ + distance -= 8; + BUG_ON(distance > 262143 || distance < -262144); + insn = 0xe8000002 | reassemble_17(distance >> 2); + } else { + insn = INSN_NOP; + } + + patch_text(addr, insn); +} diff --git a/arch/parisc/kernel/kexec.c b/arch/parisc/kernel/kexec.c new file mode 100644 index 000000000..5eb7f30ed --- /dev/null +++ b/arch/parisc/kernel/kexec.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/kexec.h> +#include <linux/delay.h> +#include <asm/cacheflush.h> +#include <asm/sections.h> + +extern void relocate_new_kernel(unsigned long head, + unsigned long start, + unsigned long phys); + +extern const unsigned int relocate_new_kernel_size; +extern unsigned int kexec_initrd_start_offset; +extern unsigned int kexec_initrd_end_offset; +extern unsigned int kexec_cmdline_offset; +extern unsigned int kexec_free_mem_offset; + +static void kexec_show_segment_info(const struct kimage *kimage, + unsigned long n) +{ + pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n", + n, + kimage->segment[n].mem, + kimage->segment[n].mem + kimage->segment[n].memsz, + (unsigned long)kimage->segment[n].memsz, + (unsigned long)kimage->segment[n].memsz / PAGE_SIZE); +} + +static void kexec_image_info(const struct kimage *kimage) +{ + unsigned long i; + + pr_debug("kexec kimage info:\n"); + pr_debug(" type: %d\n", kimage->type); + pr_debug(" start: %lx\n", kimage->start); + pr_debug(" head: %lx\n", kimage->head); + pr_debug(" nr_segments: %lu\n", kimage->nr_segments); + + for (i = 0; i < kimage->nr_segments; i++) + kexec_show_segment_info(kimage, i); + +#ifdef CONFIG_KEXEC_FILE + if (kimage->file_mode) { + pr_debug("cmdline: %.*s\n", (int)kimage->cmdline_buf_len, + kimage->cmdline_buf); + } +#endif +} + +void machine_kexec_cleanup(struct kimage *kimage) +{ +} + +void machine_crash_shutdown(struct pt_regs *regs) +{ +} + +void machine_shutdown(void) +{ + smp_send_stop(); + while (num_online_cpus() > 1) { + cpu_relax(); + mdelay(1); + } +} + +void machine_kexec(struct kimage *image) +{ +#ifdef CONFIG_64BIT + Elf64_Fdesc desc; +#endif + void (*reloc)(unsigned long head, + unsigned long start, + unsigned long phys); + + unsigned long phys = page_to_phys(image->control_code_page); + void *virt = (void *)__fix_to_virt(FIX_TEXT_KEXEC); + struct kimage_arch *arch = &image->arch; + + set_fixmap(FIX_TEXT_KEXEC, phys); + + flush_cache_all(); + +#ifdef CONFIG_64BIT + reloc = (void *)&desc; + desc.addr = (long long)virt; +#else + reloc = (void *)virt; +#endif + + memcpy(virt, dereference_function_descriptor(relocate_new_kernel), + relocate_new_kernel_size); + + *(unsigned long *)(virt + kexec_cmdline_offset) = arch->cmdline; + *(unsigned long *)(virt + kexec_initrd_start_offset) = arch->initrd_start; + *(unsigned long *)(virt + kexec_initrd_end_offset) = arch->initrd_end; + *(unsigned long *)(virt + kexec_free_mem_offset) = PAGE0->mem_free; + + flush_cache_all(); + flush_tlb_all(); + local_irq_disable(); + + reloc(image->head & PAGE_MASK, image->start, phys); +} + +int machine_kexec_prepare(struct kimage *image) +{ + kexec_image_info(image); + return 0; +} diff --git a/arch/parisc/kernel/kexec_file.c b/arch/parisc/kernel/kexec_file.c new file mode 100644 index 000000000..8c534204f --- /dev/null +++ b/arch/parisc/kernel/kexec_file.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Load ELF vmlinux file for the kexec_file_load syscall. + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + * + */ +#include <linux/elf.h> +#include <linux/kexec.h> +#include <linux/libfdt.h> +#include <linux/module.h> +#include <linux/of_fdt.h> +#include <linux/slab.h> +#include <linux/types.h> + +static void *elf_load(struct kimage *image, char *kernel_buf, + unsigned long kernel_len, char *initrd, + unsigned long initrd_len, char *cmdline, + unsigned long cmdline_len) +{ + int ret, i; + unsigned long kernel_load_addr; + struct elfhdr ehdr; + struct kexec_elf_info elf_info; + struct kexec_buf kbuf = { .image = image, .buf_min = 0, + .buf_max = -1UL, }; + + ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info); + if (ret) + goto out; + + ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr); + if (ret) + goto out; + + image->start = __pa(elf_info.ehdr->e_entry); + + for (i = 0; i < image->nr_segments; i++) + image->segment[i].mem = __pa(image->segment[i].mem); + + pr_debug("Loaded the kernel at 0x%lx, entry at 0x%lx\n", + kernel_load_addr, image->start); + + if (initrd != NULL) { + kbuf.buffer = initrd; + kbuf.bufsz = kbuf.memsz = initrd_len; + kbuf.buf_align = PAGE_SIZE; + kbuf.top_down = false; + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; + ret = kexec_add_buffer(&kbuf); + if (ret) + goto out; + + pr_debug("Loaded initrd at 0x%lx\n", kbuf.mem); + image->arch.initrd_start = kbuf.mem; + image->arch.initrd_end = kbuf.mem + initrd_len; + } + + if (cmdline != NULL) { + kbuf.buffer = cmdline; + kbuf.bufsz = kbuf.memsz = ALIGN(cmdline_len, 8); + kbuf.buf_align = PAGE_SIZE; + kbuf.top_down = false; + kbuf.buf_min = PAGE0->mem_free + PAGE_SIZE; + kbuf.buf_max = kernel_load_addr; + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; + ret = kexec_add_buffer(&kbuf); + if (ret) + goto out; + + pr_debug("Loaded cmdline at 0x%lx\n", kbuf.mem); + image->arch.cmdline = kbuf.mem; + } +out: + return NULL; +} + +const struct kexec_file_ops kexec_elf_ops = { + .probe = kexec_elf_probe, + .load = elf_load, +}; + +const struct kexec_file_ops * const kexec_file_loaders[] = { + &kexec_elf_ops, + NULL +}; diff --git a/arch/parisc/kernel/kgdb.c b/arch/parisc/kernel/kgdb.c new file mode 100644 index 000000000..b16fa9bac --- /dev/null +++ b/arch/parisc/kernel/kgdb.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PA-RISC KGDB support + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + * Copyright (c) 2022 Helge Deller <deller@gmx.de> + * + */ + +#include <linux/kgdb.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/notifier.h> +#include <linux/kdebug.h> +#include <linux/uaccess.h> +#include <asm/ptrace.h> +#include <asm/traps.h> +#include <asm/processor.h> +#include <asm/patch.h> +#include <asm/cacheflush.h> + +const struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = { 0x03, 0xff, 0xa0, 0x1f } +}; + +static int __kgdb_notify(struct die_args *args, unsigned long cmd) +{ + struct pt_regs *regs = args->regs; + + if (kgdb_handle_exception(1, args->signr, cmd, regs)) + return NOTIFY_DONE; + return NOTIFY_STOP; +} + +static int kgdb_notify(struct notifier_block *self, + unsigned long cmd, void *ptr) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + ret = __kgdb_notify(ptr, cmd); + local_irq_restore(flags); + + return ret; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_notify, + .priority = -INT_MAX, +}; + +int kgdb_arch_init(void) +{ + return register_die_notifier(&kgdb_notifier); +} + +void kgdb_arch_exit(void) +{ + unregister_die_notifier(&kgdb_notifier); +} + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + struct parisc_gdb_regs *gr = (struct parisc_gdb_regs *)gdb_regs; + + memset(gr, 0, sizeof(struct parisc_gdb_regs)); + + memcpy(gr->gpr, regs->gr, sizeof(gr->gpr)); + memcpy(gr->fr, regs->fr, sizeof(gr->fr)); + + gr->sr0 = regs->sr[0]; + gr->sr1 = regs->sr[1]; + gr->sr2 = regs->sr[2]; + gr->sr3 = regs->sr[3]; + gr->sr4 = regs->sr[4]; + gr->sr5 = regs->sr[5]; + gr->sr6 = regs->sr[6]; + gr->sr7 = regs->sr[7]; + + gr->sar = regs->sar; + gr->iir = regs->iir; + gr->isr = regs->isr; + gr->ior = regs->ior; + gr->ipsw = regs->ipsw; + gr->cr27 = regs->cr27; + + gr->iaoq_f = regs->iaoq[0]; + gr->iasq_f = regs->iasq[0]; + + gr->iaoq_b = regs->iaoq[1]; + gr->iasq_b = regs->iasq[1]; +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + struct parisc_gdb_regs *gr = (struct parisc_gdb_regs *)gdb_regs; + + + memcpy(regs->gr, gr->gpr, sizeof(regs->gr)); + memcpy(regs->fr, gr->fr, sizeof(regs->fr)); + + regs->sr[0] = gr->sr0; + regs->sr[1] = gr->sr1; + regs->sr[2] = gr->sr2; + regs->sr[3] = gr->sr3; + regs->sr[4] = gr->sr4; + regs->sr[5] = gr->sr5; + regs->sr[6] = gr->sr6; + regs->sr[7] = gr->sr7; + + regs->sar = gr->sar; + regs->iir = gr->iir; + regs->isr = gr->isr; + regs->ior = gr->ior; + regs->ipsw = gr->ipsw; + regs->cr27 = gr->cr27; + + regs->iaoq[0] = gr->iaoq_f; + regs->iasq[0] = gr->iasq_f; + + regs->iaoq[1] = gr->iaoq_b; + regs->iasq[1] = gr->iasq_b; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, + struct task_struct *task) +{ + struct pt_regs *regs = task_pt_regs(task); + unsigned long gr30, iaoq; + + gr30 = regs->gr[30]; + iaoq = regs->iaoq[0]; + + regs->gr[30] = regs->ksp; + regs->iaoq[0] = regs->kpc; + pt_regs_to_gdb_regs(gdb_regs, regs); + + regs->gr[30] = gr30; + regs->iaoq[0] = iaoq; + +} + +static void step_instruction_queue(struct pt_regs *regs) +{ + regs->iaoq[0] = regs->iaoq[1]; + regs->iaoq[1] += 4; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) +{ + regs->iaoq[0] = ip; + regs->iaoq[1] = ip + 4; +} + +int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) +{ + int ret = copy_from_kernel_nofault(bpt->saved_instr, + (char *)bpt->bpt_addr, BREAK_INSTR_SIZE); + if (ret) + return ret; + + __patch_text((void *)bpt->bpt_addr, + *(unsigned int *)&arch_kgdb_ops.gdb_bpt_instr); + return ret; +} + +int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) +{ + __patch_text((void *)bpt->bpt_addr, *(unsigned int *)&bpt->saved_instr); + return 0; +} + +int kgdb_arch_handle_exception(int trap, int signo, + int err_code, char *inbuf, char *outbuf, + struct pt_regs *regs) +{ + unsigned long addr; + char *p = inbuf + 1; + + switch (inbuf[0]) { + case 'D': + case 'c': + case 'k': + kgdb_contthread = NULL; + kgdb_single_step = 0; + + if (kgdb_hex2long(&p, &addr)) + kgdb_arch_set_pc(regs, addr); + else if (trap == 9 && regs->iir == + PARISC_KGDB_COMPILED_BREAK_INSN) + step_instruction_queue(regs); + return 0; + case 's': + kgdb_single_step = 1; + if (kgdb_hex2long(&p, &addr)) { + kgdb_arch_set_pc(regs, addr); + } else if (trap == 9 && regs->iir == + PARISC_KGDB_COMPILED_BREAK_INSN) { + step_instruction_queue(regs); + mtctl(-1, 0); + } else { + mtctl(0, 0); + } + regs->gr[0] |= PSW_R; + return 0; + + } + return -1; +} diff --git a/arch/parisc/kernel/kprobes.c b/arch/parisc/kernel/kprobes.c new file mode 100644 index 000000000..6e0b86652 --- /dev/null +++ b/arch/parisc/kernel/kprobes.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * arch/parisc/kernel/kprobes.c + * + * PA-RISC kprobes implementation + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + * Copyright (c) 2022 Helge Deller <deller@gmx.de> + */ + +#include <linux/types.h> +#include <linux/kprobes.h> +#include <linux/slab.h> +#include <asm/cacheflush.h> +#include <asm/patch.h> + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + if ((unsigned long)p->addr & 3UL) + return -EINVAL; + + p->ainsn.insn = get_insn_slot(); + if (!p->ainsn.insn) + return -ENOMEM; + + /* + * Set up new instructions. Second break instruction will + * trigger call of parisc_kprobe_ss_handler(). + */ + p->opcode = *p->addr; + p->ainsn.insn[0] = p->opcode; + p->ainsn.insn[1] = PARISC_KPROBES_BREAK_INSN2; + + flush_insn_slot(p); + return 0; +} + +void __kprobes arch_remove_kprobe(struct kprobe *p) +{ + if (!p->ainsn.insn) + return; + + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn = NULL; +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + patch_text(p->addr, PARISC_KPROBES_BREAK_INSN); +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + patch_text(p->addr, p->opcode); +} + +static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp = kprobe_running(); + kcb->prev_kprobe.status = kcb->kprobe_status; +} + +static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); + kcb->kprobe_status = kcb->prev_kprobe.status; +} + +static inline void __kprobes set_current_kprobe(struct kprobe *p) +{ + __this_cpu_write(current_kprobe, p); +} + +static void __kprobes setup_singlestep(struct kprobe *p, + struct kprobe_ctlblk *kcb, struct pt_regs *regs) +{ + kcb->iaoq[0] = regs->iaoq[0]; + kcb->iaoq[1] = regs->iaoq[1]; + instruction_pointer_set(regs, (unsigned long)p->ainsn.insn); +} + +int __kprobes parisc_kprobe_break_handler(struct pt_regs *regs) +{ + struct kprobe *p; + struct kprobe_ctlblk *kcb; + + preempt_disable(); + + kcb = get_kprobe_ctlblk(); + p = get_kprobe((unsigned long *)regs->iaoq[0]); + + if (!p) { + preempt_enable_no_resched(); + return 0; + } + + if (kprobe_running()) { + /* + * We have reentered the kprobe_handler, since another kprobe + * was hit while within the handler, we save the original + * kprobes and single step on the instruction of the new probe + * without calling any user handlers to avoid recursive + * kprobes. + */ + save_previous_kprobe(kcb); + set_current_kprobe(p); + kprobes_inc_nmissed_count(p); + setup_singlestep(p, kcb, regs); + kcb->kprobe_status = KPROBE_REENTER; + return 1; + } + + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + /* If we have no pre-handler or it returned 0, we continue with + * normal processing. If we have a pre-handler and it returned + * non-zero - which means user handler setup registers to exit + * to another instruction, we must skip the single stepping. + */ + + if (!p->pre_handler || !p->pre_handler(p, regs)) { + setup_singlestep(p, kcb, regs); + kcb->kprobe_status = KPROBE_HIT_SS; + } else { + reset_current_kprobe(); + preempt_enable_no_resched(); + } + return 1; +} + +int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + struct kprobe *p = kprobe_running(); + + if (!p) + return 0; + + if (regs->iaoq[0] != (unsigned long)p->ainsn.insn+4) + return 0; + + /* restore back original saved kprobe variables and continue */ + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + return 1; + } + + /* for absolute branch instructions we can copy iaoq_b. for relative + * branch instructions we need to calculate the new address based on the + * difference between iaoq_f and iaoq_b. We cannot use iaoq_b without + * modifications because it's based on our ainsn.insn address. + */ + + if (p->post_handler) + p->post_handler(p, regs, 0); + + switch (regs->iir >> 26) { + case 0x38: /* BE */ + case 0x39: /* BE,L */ + case 0x3a: /* BV */ + case 0x3b: /* BVE */ + /* for absolute branches, regs->iaoq[1] has already the right + * address + */ + regs->iaoq[0] = kcb->iaoq[1]; + break; + default: + regs->iaoq[0] = kcb->iaoq[1]; + regs->iaoq[1] = regs->iaoq[0] + 4; + break; + } + kcb->kprobe_status = KPROBE_HIT_SSDONE; + reset_current_kprobe(); + return 1; +} + +void __kretprobe_trampoline(void) +{ + asm volatile("nop"); + asm volatile("nop"); +} + +static int __kprobes trampoline_probe_handler(struct kprobe *p, + struct pt_regs *regs); + +static struct kprobe trampoline_p = { + .pre_handler = trampoline_probe_handler +}; + +static int __kprobes trampoline_probe_handler(struct kprobe *p, + struct pt_regs *regs) +{ + __kretprobe_trampoline_handler(regs, NULL); + + return 1; +} + +void arch_kretprobe_fixup_return(struct pt_regs *regs, + kprobe_opcode_t *correct_ret_addr) +{ + regs->gr[2] = (unsigned long)correct_ret_addr; +} + +void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + ri->ret_addr = (kprobe_opcode_t *)regs->gr[2]; + ri->fp = NULL; + + /* Replace the return addr with trampoline addr. */ + regs->gr[2] = (unsigned long)trampoline_p.addr; +} + +int __kprobes arch_trampoline_kprobe(struct kprobe *p) +{ + return p->addr == trampoline_p.addr; +} + +int __init arch_init_kprobes(void) +{ + trampoline_p.addr = (kprobe_opcode_t *) + dereference_function_descriptor(__kretprobe_trampoline); + return register_kprobe(&trampoline_p); +} diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c new file mode 100644 index 000000000..7df140545 --- /dev/null +++ b/arch/parisc/kernel/module.c @@ -0,0 +1,990 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Kernel dynamically loadable module help for PARISC. + * + * The best reference for this stuff is probably the Processor- + * Specific ELF Supplement for PA-RISC: + * https://parisc.wiki.kernel.org/index.php/File:Elf-pa-hp.pdf + * + * Linux/PA-RISC Project + * Copyright (C) 2003 Randolph Chung <tausq at debian . org> + * Copyright (C) 2008 Helge Deller <deller@gmx.de> + * + * 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 (in_init(me, (void *)val)) + * val -= (uint32_t)me->init_layout.base; + * else + * val -= (uint32_t)me->core_layout.base; + * However, SEGREL32 is used only for PARISC unwind entries, and we want + * those entries to have an absolute address, and not just an offset. + * + * The unwind table mechanism has the ability to specify an offset for + * the unwind table; however, because we split off the init functions into + * a different piece of memory, it is not possible to do this using a + * single offset. Instead, we use the above hack for now. + */ + +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/ftrace.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/bug.h> +#include <linux/mm.h> +#include <linux/slab.h> + +#include <asm/unwind.h> +#include <asm/sections.h> + +#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 (!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; \ + } + +/* Maximum number of GOT entries. We use a long displacement ldd from + * 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. + * 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 in_init(struct module *me, void *loc) +{ + return (loc >= me->init_layout.base && + loc <= (me->init_layout.base + me->init_layout.size)); +} + +static inline int in_core(struct module *me, void *loc) +{ + return (loc >= me->core_layout.base && + loc <= (me->core_layout.base + me->core_layout.size)); +} + +static inline int in_local(struct module *me, void *loc) +{ + return in_init(me, loc) || in_core(me, loc); +} + +#ifndef CONFIG_64BIT +struct got_entry { + Elf32_Addr addr; +}; + +struct stub_entry { + Elf32_Word insns[2]; /* each stub entry has two insns */ +}; +#else +struct got_entry { + Elf64_Addr addr; +}; + +struct stub_entry { + Elf64_Word insns[4]; /* each stub entry has four insns */ +}; +#endif + +/* Field selection types defined by hppa */ +#define rnd(x) (((x)+0x1000)&~0x1fff) +/* fsel: full 32 bits */ +#define fsel(v,a) ((v)+(a)) +/* lsel: select left 21 bits */ +#define lsel(v,a) (((v)+(a))>>11) +/* rsel: select right 11 bits */ +#define rsel(v,a) (((v)+(a))&0x7ff) +/* lrsel with rounding of addend to nearest 8k */ +#define lrsel(v,a) (((v)+rnd(a))>>11) +/* rrsel with rounding of addend to nearest 8k */ +#define rrsel(v,a) ((((v)+rnd(a))&0x7ff)+((a)-rnd(a))) + +#define mask(x,sz) ((x) & ~((1<<(sz))-1)) + + +/* 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) | + ((as17 & 0x0f800) << 5) | + ((as17 & 0x00400) >> 8) | + ((as17 & 0x003ff) << 3)); +} + +static inline int reassemble_21(int as21) +{ + return (((as21 & 0x100000) >> 20) | + ((as21 & 0x0ffe00) >> 8) | + ((as21 & 0x000180) << 7) | + ((as21 & 0x00007c) << 14) | + ((as21 & 0x000003) << 12)); +} + +static inline int reassemble_22(int as22) +{ + return (((as22 & 0x200000) >> 21) | + ((as22 & 0x1f0000) << 5) | + ((as22 & 0x00f800) << 5) | + ((as22 & 0x000400) >> 8) | + ((as22 & 0x0003ff) << 3)); +} + +void *module_alloc(unsigned long 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, + PAGE_KERNEL_RWX, 0, NUMA_NO_NODE, + __builtin_return_address(0)); +} + +#ifndef CONFIG_64BIT +static inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n) +{ + return 0; +} + +static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n) +{ + return 0; +} + +static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n) +{ + unsigned long cnt = 0; + + for (; n > 0; n--, rela++) + { + switch (ELF32_R_TYPE(rela->r_info)) { + case R_PARISC_PCREL17F: + case R_PARISC_PCREL22F: + cnt++; + } + } + + return cnt; +} +#else +static inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n) +{ + unsigned long cnt = 0; + + for (; n > 0; n--, rela++) + { + switch (ELF64_R_TYPE(rela->r_info)) { + case R_PARISC_LTOFF21L: + case R_PARISC_LTOFF14R: + case R_PARISC_PCREL22F: + cnt++; + } + } + + return cnt; +} + +static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n) +{ + unsigned long cnt = 0; + + for (; n > 0; n--, rela++) + { + switch (ELF64_R_TYPE(rela->r_info)) { + case R_PARISC_FPTR64: + cnt++; + } + } + + return cnt; +} + +static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n) +{ + unsigned long cnt = 0; + + for (; n > 0; n--, rela++) + { + switch (ELF64_R_TYPE(rela->r_info)) { + case R_PARISC_PCREL22F: + cnt++; + } + } + + return cnt; +} +#endif + +void module_arch_freeing_init(struct module *mod) +{ + kfree(mod->arch.section); + mod->arch.section = NULL; +} + +/* 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 +int module_frob_arch_sections(CONST Elf_Ehdr *hdr, + CONST Elf_Shdr *sechdrs, + CONST char *secstrings, + struct module *me) +{ + 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 *)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) + me->arch.unwind_section = i; + + if (sechdrs[i].sh_type != SHT_RELA) + continue; + + /* some of these are not relevant for 32-bit/64-bit + * we leave them here to make the code common. the + * compiler will do its thing and optimize out the + * stuff we don't need + */ + gots += count_gots(rels, nrels); + fdescs += count_fdescs(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 */ + me->core_layout.size = ALIGN(me->core_layout.size, 16); + me->arch.got_offset = me->core_layout.size; + me->core_layout.size += gots * sizeof(struct got_entry); + + me->core_layout.size = ALIGN(me->core_layout.size, 16); + me->arch.fdesc_offset = me->core_layout.size; + me->core_layout.size += fdescs * sizeof(Elf_Fdesc); + + me->arch.got_max = gots; + me->arch.fdesc_max = fdescs; + + return 0; +} + +#ifdef CONFIG_64BIT +static Elf64_Word get_got(struct module *me, unsigned long value, long addend) +{ + unsigned int i; + struct got_entry *got; + + value += addend; + + BUG_ON(value == 0); + + got = me->core_layout.base + me->arch.got_offset; + for (i = 0; got[i].addr; i++) + if (got[i].addr == value) + goto out; + + BUG_ON(++me->arch.got_count > me->arch.got_max); + + got[i].addr = value; + out: + pr_debug("GOT ENTRY %d[%lx] val %lx\n", i, i*sizeof(struct got_entry), + value); + return i * sizeof(struct got_entry); +} +#endif /* CONFIG_64BIT */ + +#ifdef CONFIG_64BIT +static Elf_Addr get_fdesc(struct module *me, unsigned long value) +{ + Elf_Fdesc *fdesc = me->core_layout.base + me->arch.fdesc_offset; + + if (!value) { + printk(KERN_ERR "%s: zero OPD requested!\n", me->name); + return 0; + } + + /* Look for existing fdesc entry. */ + while (fdesc->addr) { + if (fdesc->addr == value) + return (Elf_Addr)fdesc; + fdesc++; + } + + BUG_ON(++me->arch.fdesc_count > me->arch.fdesc_max); + + /* Create new one */ + fdesc->addr = value; + fdesc->gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset; + return (Elf_Addr)fdesc; +} +#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, + enum elf_stub_type stub_type, Elf_Addr loc0, unsigned int targetsec) +{ + struct stub_entry *stub; + 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; + } + + /* 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) + */ + //value = *(unsigned long *)((value + addend) & ~3); /* why? */ + + stub->insns[0] = 0x20200000; /* ldil L'XXX,%r1 */ + stub->insns[1] = 0xe0202002; /* be,n R'XXX(%sr4,%r1) */ + + stub->insns[0] |= reassemble_21(lrsel(value, addend)); + stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4); + +#else +/* for 64-bit we have three kinds of stubs: + * for normal function calls: + * ldd 0(%dp),%dp + * ldd 10(%dp), %r1 + * bve (%r1) + * ldd 18(%dp), %dp + * + * for millicode: + * ldil 0, %r1 + * 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) + */ + 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 */ + 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 */ + stub->insns[3] = 0xe820d002; /* bve,n (%r1) */ + + 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; +} + +#ifndef CONFIG_64BIT +int apply_relocate_add(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + int i; + Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + Elf32_Word *loc; + 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"); + + pr_debug("Applying relocate section %u to %u\n", relsec, + targetsec); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + 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); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + //dot = (sechdrs[relsec].sh_addr + rel->r_offset) & ~0x03; + dot = (Elf32_Addr)loc & ~0x03; + + val = sym->st_value; + addend = rel[i].r_addend; + +#if 0 +#define r(t) ELF32_R_TYPE(rel[i].r_info)==t ? #t : + pr_debug("Symbol %s loc 0x%x val 0x%x addend 0x%x: %s\n", + strtab + sym->st_name, + (uint32_t)loc, val, addend, + r(R_PARISC_PLABEL32) + r(R_PARISC_DIR32) + r(R_PARISC_DIR21L) + r(R_PARISC_DIR14R) + r(R_PARISC_SEGREL32) + r(R_PARISC_DPREL21L) + r(R_PARISC_DPREL14R) + r(R_PARISC_PCREL17F) + r(R_PARISC_PCREL22F) + "UNKNOWN"); +#undef r +#endif + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_PARISC_PLABEL32: + /* 32-bit function address */ + /* no function descriptors... */ + *loc = fsel(val, addend); + break; + case R_PARISC_DIR32: + /* direct 32-bit ref */ + *loc = fsel(val, addend); + break; + case R_PARISC_DIR21L: + /* left 21 bits of effective address */ + val = lrsel(val, addend); + *loc = mask(*loc, 21) | reassemble_21(val); + break; + case R_PARISC_DIR14R: + /* right 14 bits of effective address */ + val = rrsel(val, addend); + *loc = mask(*loc, 14) | reassemble_14(val); + break; + case R_PARISC_SEGREL32: + /* 32-bit segment relative address */ + /* See note about special handling of SEGREL32 at + * the beginning of this file. + */ + *loc = fsel(val, addend); + break; + case R_PARISC_SECREL32: + /* 32-bit section relative address. */ + *loc = fsel(val, addend); + break; + case R_PARISC_DPREL21L: + /* left 21 bit of relative address */ + val = lrsel(val - dp, addend); + *loc = mask(*loc, 21) | reassemble_21(val); + break; + case R_PARISC_DPREL14R: + /* right 14 bit of relative address */ + val = rrsel(val - dp, addend); + *loc = mask(*loc, 14) | reassemble_14(val); + break; + case R_PARISC_PCREL17F: + /* 17-bit PC relative address */ + /* calculate direct call offset */ + val += addend; + val = (val - dot - 8)/4; + 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 */ + /* calculate direct call offset */ + val += addend; + 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); + val = (val - dot - 8)/4; + CHECK_RELOC(val, 22); + } + *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); + break; + case R_PARISC_PCREL32: + /* 32-bit PC relative address */ + *loc = val - dot - 8 + addend; + break; + + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + + return 0; +} + +#else +int apply_relocate_add(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + int i; + Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; + Elf64_Sym *sym; + Elf64_Word *loc; + Elf64_Xword *loc64; + Elf64_Addr val; + Elf64_Sxword addend; + Elf64_Addr dot; + Elf_Addr loc0; + unsigned int targetsec = sechdrs[relsec].sh_info; + + pr_debug("Applying relocate section %u to %u\n", relsec, + targetsec); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + 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); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + //dot = (sechdrs[relsec].sh_addr + rel->r_offset) & ~0x03; + dot = (Elf64_Addr)loc & ~0x03; + loc64 = (Elf64_Xword *)loc; + + val = sym->st_value; + addend = rel[i].r_addend; + +#if 0 +#define r(t) ELF64_R_TYPE(rel[i].r_info)==t ? #t : + printk("Symbol %s loc %p val 0x%Lx addend 0x%Lx: %s\n", + strtab + sym->st_name, + loc, val, addend, + r(R_PARISC_LTOFF14R) + r(R_PARISC_LTOFF21L) + r(R_PARISC_PCREL22F) + r(R_PARISC_DIR64) + r(R_PARISC_SEGREL32) + r(R_PARISC_FPTR64) + "UNKNOWN"); +#undef r +#endif + + switch (ELF64_R_TYPE(rel[i].r_info)) { + case R_PARISC_LTOFF21L: + /* LT-relative; left 21 bits */ + val = get_got(me, val, addend); + pr_debug("LTOFF21L Symbol %s loc %p val %llx\n", + strtab + sym->st_name, + loc, val); + val = lrsel(val, 0); + *loc = mask(*loc, 21) | reassemble_21(val); + break; + case R_PARISC_LTOFF14R: + /* L(ltoff(val+addend)) */ + /* LT-relative; right 14 bits */ + val = get_got(me, val, addend); + val = rrsel(val, 0); + pr_debug("LTOFF14R Symbol %s loc %p val %llx\n", + strtab + sym->st_name, + loc, val); + *loc = mask(*loc, 14) | reassemble_14(val); + break; + case R_PARISC_PCREL22F: + /* PC-relative; 22 bits */ + pr_debug("PCREL22F Symbol %s loc %p val %llx\n", + strtab + sym->st_name, + loc, val); + val += addend; + /* can we reach it locally? */ + 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, ELF_STUB_MILLI, + loc0, targetsec); + else + val = get_stub(me, val, addend, ELF_STUB_GOT, + loc0, targetsec); + } + pr_debug("STUB FOR %s loc %px, val %llx+%llx at %llx\n", + strtab + sym->st_name, loc, sym->st_value, + addend, val); + val = (val - dot - 8)/4; + CHECK_RELOC(val, 22); + *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); + break; + case R_PARISC_PCREL32: + /* 32-bit PC relative address */ + *loc = val - dot - 8 + addend; + break; + case R_PARISC_PCREL64: + /* 64-bit PC relative address */ + *loc64 = val - dot - 8 + addend; + break; + case R_PARISC_DIR64: + /* 64-bit effective address */ + *loc64 = val + addend; + break; + case R_PARISC_SEGREL32: + /* 32-bit segment relative address */ + /* See note about special handling of SEGREL32 at + * the beginning of this file. + */ + *loc = fsel(val, addend); + break; + case R_PARISC_SECREL32: + /* 32-bit section relative address. */ + *loc = fsel(val, addend); + break; + case R_PARISC_FPTR64: + /* 64-bit function address */ + if(in_local(me, (void *)(val + addend))) { + *loc64 = get_fdesc(me, val+addend); + pr_debug("FDESC for %s at %llx points to %llx\n", + strtab + sym->st_name, *loc64, + ((Elf_Fdesc *)*loc64)->addr); + } else { + /* if the symbol is not local to this + * module then val+addend is a pointer + * to the function descriptor */ + pr_debug("Non local FPTR64 Symbol %s loc %p val %llx\n", + strtab + sym->st_name, + loc, val); + *loc64 = val + addend; + } + break; + + default: + printk(KERN_ERR "module %s: Unknown relocation: %Lu\n", + me->name, ELF64_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} +#endif + +static void +register_unwind_table(struct module *me, + const Elf_Shdr *sechdrs) +{ + unsigned char *table, *end; + unsigned long gp; + + if (!me->arch.unwind_section) + return; + + table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr; + end = table + sechdrs[me->arch.unwind_section].sh_size; + gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset; + + pr_debug("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n", + me->arch.unwind_section, table, end, gp); + me->arch.unwind = unwind_table_add(me->name, 0, gp, table, end); +} + +static void +deregister_unwind_table(struct module *me) +{ + if (me->arch.unwind) + unwind_table_remove(me->arch.unwind); +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + int i; + unsigned long nsyms; + const char *strtab = NULL; + const Elf_Shdr *s; + char *secstrings; + int symindex = -1; + Elf_Sym *newptr, *oldptr; + Elf_Shdr *symhdr = NULL; +#ifdef DEBUG + Elf_Fdesc *entry; + u32 *addr; + + entry = (Elf_Fdesc *)me->init; + printk("FINALIZE, ->init FPTR is %p, GP %lx ADDR %lx\n", entry, + entry->gp, entry->addr); + addr = (u32 *)entry->addr; + printk("INSNS: %x %x %x %x\n", + addr[0], addr[1], addr[2], addr[3]); + printk("got entries used %ld, gots max %ld\n" + "fdescs used %ld, fdescs max %ld\n", + me->arch.got_count, me->arch.got_max, + me->arch.fdesc_count, me->arch.fdesc_max); +#endif + + register_unwind_table(me, sechdrs); + + /* haven't filled in me->symtab yet, so have to find it + * ourselves */ + for (i = 1; i < hdr->e_shnum; i++) { + if(sechdrs[i].sh_type == SHT_SYMTAB + && (sechdrs[i].sh_flags & SHF_ALLOC)) { + int strindex = sechdrs[i].sh_link; + symindex = i; + /* FIXME: AWFUL HACK + * The cast is to drop the const from + * the sechdrs pointer */ + symhdr = (Elf_Shdr *)&sechdrs[i]; + strtab = (char *)sechdrs[strindex].sh_addr; + break; + } + } + + pr_debug("module %s: strtab %p, symhdr %p\n", + 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); + return -EINVAL; + } + + kfree(me->arch.section); + me->arch.section = NULL; + + /* no symbol table */ + if(symhdr == NULL) + return 0; + + oldptr = (void *)symhdr->sh_addr; + newptr = oldptr + 1; /* we start counting at 1 */ + nsyms = symhdr->sh_size / sizeof(Elf_Sym); + pr_debug("OLD num_symtab %lu\n", nsyms); + + for (i = 1; i < nsyms; i++) { + oldptr++; /* note, count starts at 1 so preincrement */ + if(strncmp(strtab + oldptr->st_name, + ".L", 2) == 0) + continue; + + if(newptr != oldptr) + *newptr++ = *oldptr; + else + newptr++; + + } + nsyms = newptr - (Elf_Sym *)symhdr->sh_addr; + pr_debug("NEW num_symtab %lu\n", nsyms); + symhdr->sh_size = nsyms * sizeof(Elf_Sym); + + /* find .altinstructions section */ + secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { + void *aseg = (void *) s->sh_addr; + char *secname = secstrings + s->sh_name; + + if (!strcmp(".altinstructions", secname)) + /* patch .altinstructions */ + apply_alternatives(aseg, aseg + s->sh_size, me->name); + +#ifdef CONFIG_DYNAMIC_FTRACE + /* For 32 bit kernels we're compiling modules with + * -ffunction-sections so we must relocate the addresses in the + * ftrace callsite section. + */ + if (symindex != -1 && !strcmp(secname, FTRACE_CALLSITE_SECTION)) { + int err; + if (s->sh_type == SHT_REL) + err = apply_relocate((Elf_Shdr *)sechdrs, + strtab, symindex, + s - sechdrs, me); + else if (s->sh_type == SHT_RELA) + err = apply_relocate_add((Elf_Shdr *)sechdrs, + strtab, symindex, + s - sechdrs, me); + if (err) + return err; + } +#endif + } + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ + deregister_unwind_table(mod); +} + +#ifdef CONFIG_64BIT +void *dereference_module_function_descriptor(struct module *mod, void *ptr) +{ + unsigned long start_opd = (Elf64_Addr)mod->core_layout.base + + mod->arch.fdesc_offset; + unsigned long end_opd = start_opd + + mod->arch.fdesc_count * sizeof(Elf64_Fdesc); + + if (ptr < (void *)start_opd || ptr >= (void *)end_opd) + return ptr; + + return dereference_function_descriptor(ptr); +} +#endif diff --git a/arch/parisc/kernel/pa7300lc.c b/arch/parisc/kernel/pa7300lc.c new file mode 100644 index 000000000..0d770ac83 --- /dev/null +++ b/arch/parisc/kernel/pa7300lc.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/parisc/kernel/pa7300lc.c + * - PA7300LC-specific functions + * + * Copyright (C) 2000 Philipp Rumpf */ + +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/smp.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <asm/ptrace.h> +#include <asm/machdep.h> + +/* CPU register indices */ + +#define MIOC_STATUS 0xf040 +#define MIOC_CONTROL 0xf080 +#define MDERRADD 0xf0e0 +#define DMAERR 0xf0e8 +#define DIOERR 0xf0ec +#define HIDMAMEM 0xf0f4 + +/* this returns the HPA of the CPU it was called on */ +static u32 cpu_hpa(void) +{ + return 0xfffb0000; +} + +static void pa7300lc_lpmc(int code, struct pt_regs *regs) +{ + u32 hpa; + printk(KERN_WARNING "LPMC on CPU %d\n", smp_processor_id()); + + show_regs(regs); + + hpa = cpu_hpa(); + printk(KERN_WARNING + "MIOC_CONTROL %08x\n" "MIOC_STATUS %08x\n" + "MDERRADD %08x\n" "DMAERR %08x\n" + "DIOERR %08x\n" "HIDMAMEM %08x\n", + gsc_readl(hpa+MIOC_CONTROL), gsc_readl(hpa+MIOC_STATUS), + gsc_readl(hpa+MDERRADD), gsc_readl(hpa+DMAERR), + gsc_readl(hpa+DIOERR), gsc_readl(hpa+HIDMAMEM)); +} + +void pa7300lc_init(void) +{ + cpu_lpmc = pa7300lc_lpmc; +} diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S new file mode 100644 index 000000000..541370d14 --- /dev/null +++ b/arch/parisc/kernel/pacache.S @@ -0,0 +1,1295 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * PARISC TLB and cache flushing support + * Copyright (C) 2000-2001 Hewlett-Packard (John Marvin) + * Copyright (C) 2001 Matthew Wilcox (willy at parisc-linux.org) + * Copyright (C) 2002 Richard Hirst (rhirst with parisc-linux.org) + */ + +/* + * NOTE: fdc,fic, and pdc instructions that use base register modification + * should only use index and base registers that are not shadowed, + * so that the fast path emulation in the non access miss handler + * can be used. + */ + +#ifdef CONFIG_64BIT + .level 2.0w +#else + .level 2.0 +#endif + +#include <asm/psw.h> +#include <asm/assembly.h> +#include <asm/cache.h> +#include <asm/ldcw.h> +#include <asm/alternative.h> +#include <linux/linkage.h> +#include <linux/init.h> +#include <linux/pgtable.h> + + .section .text.hot + .align 16 + +ENTRY_CFI(flush_tlb_all_local) + /* + * The pitlbe and pdtlbe instructions should only be used to + * flush the entire tlb. Also, there needs to be no intervening + * tlb operations, e.g. tlb misses, so the operation needs + * to happen in real mode with all interruptions disabled. + */ + + /* pcxt_ssm_bug - relied upon translation! PA 2.0 Arch. F-4 and F-5 */ + rsm PSW_SM_I, %r19 /* save I-bit state */ + load32 PA(1f), %r1 + nop + nop + nop + nop + nop + + rsm PSW_SM_Q, %r0 /* prep to load iia queue */ + mtctl %r0, %cr17 /* Clear IIASQ tail */ + mtctl %r0, %cr17 /* Clear IIASQ head */ + mtctl %r1, %cr18 /* IIAOQ head */ + ldo 4(%r1), %r1 + mtctl %r1, %cr18 /* IIAOQ tail */ + load32 REAL_MODE_PSW, %r1 + mtctl %r1, %ipsw + rfi + nop + +1: load32 PA(cache_info), %r1 + + /* Flush Instruction Tlb */ + +88: LDREG ITLB_SID_BASE(%r1), %r20 + LDREG ITLB_SID_STRIDE(%r1), %r21 + LDREG ITLB_SID_COUNT(%r1), %r22 + LDREG ITLB_OFF_BASE(%r1), %arg0 + LDREG ITLB_OFF_STRIDE(%r1), %arg1 + LDREG ITLB_OFF_COUNT(%r1), %arg2 + LDREG ITLB_LOOP(%r1), %arg3 + + addib,COND(=) -1, %arg3, fitoneloop /* Preadjust and test */ + movb,<,n %arg3, %r31, fitdone /* If loop < 0, skip */ + copy %arg0, %r28 /* Init base addr */ + +fitmanyloop: /* Loop if LOOP >= 2 */ + mtsp %r20, %sr1 + add %r21, %r20, %r20 /* increment space */ + copy %arg2, %r29 /* Init middle loop count */ + +fitmanymiddle: /* Loop if LOOP >= 2 */ + addib,COND(>) -1, %r31, fitmanymiddle /* Adjusted inner loop decr */ + pitlbe %r0(%sr1, %r28) + pitlbe,m %arg1(%sr1, %r28) /* Last pitlbe and addr adjust */ + 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,COND(<=),n -1, %r22, fitdone /* Outer loop count decr */ + +fitoneloop: /* Loop if LOOP = 1 */ + mtsp %r20, %sr1 + copy %arg0, %r28 /* init base addr */ + copy %arg2, %r29 /* init middle loop count */ + +fitonemiddle: /* Loop if LOOP = 1 */ + addib,COND(>) -1, %r29, fitonemiddle /* Middle loop count decr */ + pitlbe,m %arg1(%sr1, %r28) /* pitlbe for one loop */ + + addib,COND(>) -1, %r22, fitoneloop /* Outer loop count decr */ + add %r21, %r20, %r20 /* increment space */ + +fitdone: + ALTERNATIVE(88b, fitdone, ALT_COND_NO_SPLIT_TLB, INSN_NOP) + + /* Flush Data Tlb */ + + LDREG DTLB_SID_BASE(%r1), %r20 + LDREG DTLB_SID_STRIDE(%r1), %r21 + LDREG DTLB_SID_COUNT(%r1), %r22 + LDREG DTLB_OFF_BASE(%r1), %arg0 + LDREG DTLB_OFF_STRIDE(%r1), %arg1 + LDREG DTLB_OFF_COUNT(%r1), %arg2 + LDREG DTLB_LOOP(%r1), %arg3 + + addib,COND(=) -1, %arg3, fdtoneloop /* Preadjust and test */ + movb,<,n %arg3, %r31, fdtdone /* If loop < 0, skip */ + copy %arg0, %r28 /* Init base addr */ + +fdtmanyloop: /* Loop if LOOP >= 2 */ + mtsp %r20, %sr1 + add %r21, %r20, %r20 /* increment space */ + copy %arg2, %r29 /* Init middle loop count */ + +fdtmanymiddle: /* Loop if LOOP >= 2 */ + addib,COND(>) -1, %r31, fdtmanymiddle /* Adjusted inner loop decr */ + pdtlbe %r0(%sr1, %r28) + pdtlbe,m %arg1(%sr1, %r28) /* Last pdtlbe and addr adjust */ + 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,COND(<=),n -1, %r22,fdtdone /* Outer loop count decr */ + +fdtoneloop: /* Loop if LOOP = 1 */ + mtsp %r20, %sr1 + copy %arg0, %r28 /* init base addr */ + copy %arg2, %r29 /* init middle loop count */ + +fdtonemiddle: /* Loop if LOOP = 1 */ + addib,COND(>) -1, %r29, fdtonemiddle /* Middle loop count decr */ + pdtlbe,m %arg1(%sr1, %r28) /* pdtlbe for one loop */ + + addib,COND(>) -1, %r22, fdtoneloop /* Outer loop count decr */ + add %r21, %r20, %r20 /* increment space */ + + +fdtdone: + /* + * Switch back to virtual mode + */ + /* pcxt_ssm_bug */ + rsm PSW_SM_I, %r0 + load32 2f, %r1 + nop + nop + nop + nop + nop + + rsm PSW_SM_Q, %r0 /* prep to load iia queue */ + mtctl %r0, %cr17 /* Clear IIASQ tail */ + mtctl %r0, %cr17 /* Clear IIASQ head */ + mtctl %r1, %cr18 /* IIAOQ head */ + ldo 4(%r1), %r1 + mtctl %r1, %cr18 /* IIAOQ tail */ + load32 KERNEL_PSW, %r1 + or %r1, %r19, %r1 /* I-bit to state on entry */ + mtctl %r1, %ipsw /* restore I-bit (entire PSW) */ + rfi + nop + +2: bv %r0(%r2) + nop + + /* + * When running in qemu, drop whole flush_tlb_all_local function and + * replace by one pdtlbe instruction, for which QEMU will drop all + * local TLB entries. + */ +3: pdtlbe %r0(%sr1,%r0) + bv,n %r0(%r2) + ALTERNATIVE_CODE(flush_tlb_all_local, 2, ALT_COND_RUN_ON_QEMU, 3b) +ENDPROC_CFI(flush_tlb_all_local) + + .import cache_info,data + +ENTRY_CFI(flush_instruction_cache_local) +88: load32 cache_info, %r1 + + /* Flush Instruction Cache */ + + LDREG ICACHE_BASE(%r1), %arg0 + 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*/ + 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,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,COND(<=),n -1, %arg2, fisync /* Outer loop decr */ + +fioneloop: /* Loop if LOOP = 1 */ + /* 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: + sync + mtsm %r22 /* restore I-bit */ +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP) + bv %r0(%r2) + nop +ENDPROC_CFI(flush_instruction_cache_local) + + + .import cache_info, data +ENTRY_CFI(flush_data_cache_local) +88: load32 cache_info, %r1 + + /* Flush Data Cache */ + + LDREG DCACHE_BASE(%r1), %arg0 + LDREG DCACHE_STRIDE(%r1), %arg1 + LDREG DCACHE_COUNT(%r1), %arg2 + LDREG DCACHE_LOOP(%r1), %arg3 + 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,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,COND(<=),n -1, %arg2, fdsync /* Outer loop decr */ + +fdoneloop: /* Loop if LOOP = 1 */ + /* 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: + sync + mtsm %r22 /* restore I-bit */ +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + bv %r0(%r2) + nop +ENDPROC_CFI(flush_data_cache_local) + +/* Clear page using kernel mapping. */ + +ENTRY_CFI(clear_page_asm) +#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 +ENDPROC_CFI(clear_page_asm) + +/* Copy page using kernel mapping. */ + +ENTRY_CFI(copy_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. + * Prefetch doesn't improve performance on rp3440. + * GCC probably can do this just as well... + */ + + ldi (PAGE_SIZE / 128), %r1 + +1: ldd 0(%r25), %r19 + ldd 8(%r25), %r20 + + ldd 16(%r25), %r21 + ldd 24(%r25), %r22 + std %r19, 0(%r26) + std %r20, 8(%r26) + + ldd 32(%r25), %r19 + ldd 40(%r25), %r20 + std %r21, 16(%r26) + std %r22, 24(%r26) + + ldd 48(%r25), %r21 + ldd 56(%r25), %r22 + std %r19, 32(%r26) + std %r20, 40(%r26) + + ldd 64(%r25), %r19 + ldd 72(%r25), %r20 + std %r21, 48(%r26) + std %r22, 56(%r26) + + ldd 80(%r25), %r21 + ldd 88(%r25), %r22 + std %r19, 64(%r26) + std %r20, 72(%r26) + + ldd 96(%r25), %r19 + ldd 104(%r25), %r20 + std %r21, 80(%r26) + std %r22, 88(%r26) + + ldd 112(%r25), %r21 + ldd 120(%r25), %r22 + ldo 128(%r25), %r25 + std %r19, 96(%r26) + std %r20, 104(%r26) + + std %r21, 112(%r26) + std %r22, 120(%r26) + + /* Note reverse branch hint for addib is taken. */ + addib,COND(>),n -1, %r1, 1b + ldo 128(%r26), %r26 + +#else + + /* + * This loop is optimized for PCXL/PCXL2 ldw/ldw and stw/stw + * bundles (very restricted rules for bundling). + * Note that until (if) we start saving + * the full 64 bit register values on interrupt, we can't + * use ldd/std on a 32 bit kernel. + */ + ldw 0(%r25), %r19 + ldi (PAGE_SIZE / 64), %r1 + +1: + ldw 4(%r25), %r20 + ldw 8(%r25), %r21 + ldw 12(%r25), %r22 + stw %r19, 0(%r26) + stw %r20, 4(%r26) + stw %r21, 8(%r26) + stw %r22, 12(%r26) + ldw 16(%r25), %r19 + ldw 20(%r25), %r20 + ldw 24(%r25), %r21 + ldw 28(%r25), %r22 + stw %r19, 16(%r26) + stw %r20, 20(%r26) + stw %r21, 24(%r26) + stw %r22, 28(%r26) + ldw 32(%r25), %r19 + ldw 36(%r25), %r20 + ldw 40(%r25), %r21 + ldw 44(%r25), %r22 + stw %r19, 32(%r26) + stw %r20, 36(%r26) + stw %r21, 40(%r26) + stw %r22, 44(%r26) + ldw 48(%r25), %r19 + ldw 52(%r25), %r20 + ldw 56(%r25), %r21 + ldw 60(%r25), %r22 + stw %r19, 48(%r26) + stw %r20, 52(%r26) + ldo 64(%r25), %r25 + stw %r21, 56(%r26) + stw %r22, 60(%r26) + ldo 64(%r26), %r26 + addib,COND(>),n -1, %r1, 1b + ldw 0(%r25), %r19 +#endif + bv %r0(%r2) + nop +ENDPROC_CFI(copy_page_asm) + +/* + * NOTE: Code in clear_user_page has a hard coded dependency on the + * maximum alias boundary being 4 Mb. We've been assured by the + * parisc chip designers that there will not ever be a parisc + * chip with a larger alias boundary (Never say never :-) ). + * + * Yah, what about the PA8800 and PA8900 processors? + * + * Subtle: the dtlb miss handlers support the temp alias region by + * "knowing" that if a dtlb miss happens within the temp alias + * region it must have occurred while in clear_user_page. Since + * this routine makes use of processor local translations, we + * don't want to insert them into the kernel page table. Instead, + * we load up some general registers (they need to be registers + * which aren't shadowed) with the physical page numbers (preshifted + * for tlb insertion) needed to insert the translations. When we + * miss on the translation, the dtlb miss handler inserts the + * translation into the tlb using these values: + * + * %r26 physical address of "to" translation + * %r23 physical address of "from" translation + */ + + /* + * copy_user_page_asm() performs a page copy using mappings + * equivalent to the user page mappings. It can be used to + * implement copy_user_page() but unfortunately both the `from' + * and `to' pages need to be flushed through mappings equivalent + * to the user mappings after the copy because the kernel accesses + * the `from' page through the kmap kernel mapping and the `to' + * page needs to be flushed since code can be copied. As a + * result, this implementation is less efficient than the simpler + * copy using the kernel mapping. It only needs the `from' page + * to flushed via the user mapping. The kunmap routines handle + * the flushes needed for the kernel mapping. + * + * I'm still keeping this around because it may be possible to + * 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. + * + */ + +ENTRY_CFI(copy_user_page_asm) + /* 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 + + ldil L%(TMPALIAS_MAP_START), %r28 + dep_safe %r24, 31,TMPALIAS_SIZE_BITS, %r28 /* Form aliased virtual address 'to' */ + depi_safe 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */ + copy %r28, %r29 + depi_safe 1, 31-TMPALIAS_SIZE_BITS,1, %r29 /* Form aliased virtual address 'from' */ + + /* Purge any old translations */ + +#ifdef CONFIG_PA20 + pdtlb,l %r0(%r28) + pdtlb,l %r0(%r29) +#else +0: pdtlb %r0(%r28) +1: pdtlb %r0(%r29) + ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB) + ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SMP, INSN_PxTLB) +#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. + */ + + 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 + * bundles (very restricted rules for bundling). It probably + * does OK on PCXU and better, but we could do better with + * ldd/std instructions. Note that until (if) we start saving + * the full 64 bit register values on interrupt, we can't + * use ldd/std on a 32 bit kernel. + */ + +1: ldw 0(%r29), %r19 + ldw 4(%r29), %r20 + ldw 8(%r29), %r21 + ldw 12(%r29), %r22 + stw %r19, 0(%r28) + stw %r20, 4(%r28) + stw %r21, 8(%r28) + stw %r22, 12(%r28) + ldw 16(%r29), %r19 + ldw 20(%r29), %r20 + ldw 24(%r29), %r21 + ldw 28(%r29), %r22 + stw %r19, 16(%r28) + stw %r20, 20(%r28) + stw %r21, 24(%r28) + stw %r22, 28(%r28) + ldw 32(%r29), %r19 + ldw 36(%r29), %r20 + ldw 40(%r29), %r21 + ldw 44(%r29), %r22 + stw %r19, 32(%r28) + stw %r20, 36(%r28) + stw %r21, 40(%r28) + stw %r22, 44(%r28) + ldw 48(%r29), %r19 + ldw 52(%r29), %r20 + ldw 56(%r29), %r21 + ldw 60(%r29), %r22 + stw %r19, 48(%r28) + stw %r20, 52(%r28) + stw %r21, 56(%r28) + stw %r22, 60(%r28) + ldo 64(%r28), %r28 + + addib,COND(>) -1, %r1,1b + ldo 64(%r29), %r29 +#endif + + bv %r0(%r2) + nop +ENDPROC_CFI(copy_user_page_asm) + +ENTRY_CFI(clear_user_page_asm) + tophys_r1 %r26 + + ldil L%(TMPALIAS_MAP_START), %r28 + dep_safe %r25, 31,TMPALIAS_SIZE_BITS, %r28 /* Form aliased virtual address 'to' */ + depi_safe 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */ + + /* Purge any old translation */ + +#ifdef CONFIG_PA20 + pdtlb,l %r0(%r28) +#else +0: pdtlb %r0(%r28) + ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB) +#endif + +#ifdef CONFIG_64BIT + ldi (PAGE_SIZE / 128), %r1 + + /* PREFETCH (Write) has not (yet) been proven to help here */ + /* #define PREFETCHW_OP ldd 256(%0), %r0 */ + +1: std %r0, 0(%r28) + std %r0, 8(%r28) + std %r0, 16(%r28) + std %r0, 24(%r28) + std %r0, 32(%r28) + std %r0, 40(%r28) + std %r0, 48(%r28) + std %r0, 56(%r28) + std %r0, 64(%r28) + std %r0, 72(%r28) + std %r0, 80(%r28) + std %r0, 88(%r28) + std %r0, 96(%r28) + std %r0, 104(%r28) + std %r0, 112(%r28) + std %r0, 120(%r28) + addib,COND(>) -1, %r1, 1b + ldo 128(%r28), %r28 + +#else /* ! CONFIG_64BIT */ + ldi (PAGE_SIZE / 64), %r1 + +1: stw %r0, 0(%r28) + stw %r0, 4(%r28) + stw %r0, 8(%r28) + stw %r0, 12(%r28) + stw %r0, 16(%r28) + stw %r0, 20(%r28) + stw %r0, 24(%r28) + stw %r0, 28(%r28) + stw %r0, 32(%r28) + stw %r0, 36(%r28) + stw %r0, 40(%r28) + stw %r0, 44(%r28) + stw %r0, 48(%r28) + stw %r0, 52(%r28) + stw %r0, 56(%r28) + stw %r0, 60(%r28) + addib,COND(>) -1, %r1, 1b + ldo 64(%r28), %r28 +#endif /* CONFIG_64BIT */ + + bv %r0(%r2) + nop +ENDPROC_CFI(clear_user_page_asm) + +ENTRY_CFI(flush_dcache_page_asm) + ldil L%(TMPALIAS_MAP_START), %r28 + dep_safe %r25, 31,TMPALIAS_SIZE_BITS, %r28 /* Form aliased virtual address 'to' */ + depi_safe 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */ + + /* Purge any old translation */ + +#ifdef CONFIG_PA20 + pdtlb,l %r0(%r28) +#else +0: pdtlb %r0(%r28) + ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB) +#endif + +88: ldil L%dcache_stride, %r1 + 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 %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(>>) %r25, %r28, 1b /* predict taken */ + fdc,m r31(%r28) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(flush_dcache_page_asm) + +ENTRY_CFI(purge_dcache_page_asm) + ldil L%(TMPALIAS_MAP_START), %r28 + dep_safe %r25, 31,TMPALIAS_SIZE_BITS, %r28 /* Form aliased virtual address 'to' */ + depi_safe 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */ + + /* Purge any old translation */ + +#ifdef CONFIG_PA20 + pdtlb,l %r0(%r28) +#else +0: pdtlb %r0(%r28) + ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB) +#endif + +88: ldil L%dcache_stride, %r1 + 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 %r28, %r25, %r25 + sub %r25, r31, %r25 + +1: pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + pdc,m r31(%r28) + cmpb,COND(>>) %r25, %r28, 1b /* predict taken */ + pdc,m r31(%r28) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(purge_dcache_page_asm) + +ENTRY_CFI(flush_icache_page_asm) + ldil L%(TMPALIAS_MAP_START), %r28 + dep_safe %r25, 31,TMPALIAS_SIZE_BITS, %r28 /* Form aliased virtual address 'to' */ + depi_safe 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */ + + /* Purge any old translation. Note that the FIC instruction + * may use either the instruction or data TLB. Given that we + * have a flat address space, it's not clear which TLB will be + * used. So, we purge both entries. */ + +#ifdef CONFIG_PA20 + pdtlb,l %r0(%r28) +1: pitlb,l %r0(%sr4,%r28) + ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SPLIT_TLB, INSN_NOP) +#else +0: pdtlb %r0(%r28) +1: pitlb %r0(%sr4,%r28) + ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB) + ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SMP, INSN_PxTLB) + ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SPLIT_TLB, INSN_NOP) +#endif + +88: 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(>>) %r25, %r28, 1b /* predict taken */ + fic,m %r31(%sr4,%r28) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(flush_icache_page_asm) + +ENTRY_CFI(flush_kernel_dcache_page_asm) +88: ldil L%dcache_stride, %r1 + ldw R%dcache_stride(%r1), %r23 + depi_safe 0, 31,PAGE_SHIFT, %r26 /* Clear any offset bits */ + +#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 + +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(>>) %r25, %r26, 1b /* predict taken */ + fdc,m %r23(%r26) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(flush_kernel_dcache_page_asm) + +ENTRY_CFI(purge_kernel_dcache_page_asm) +88: ldil L%dcache_stride, %r1 + ldw R%dcache_stride(%r1), %r23 + depi_safe 0, 31,PAGE_SHIFT, %r26 /* Clear any offset bits */ + +#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 + +1: pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + cmpb,COND(>>) %r25, %r26, 1b /* predict taken */ + pdc,m %r23(%r26) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(purge_kernel_dcache_page_asm) + +ENTRY_CFI(flush_user_dcache_range_asm) +88: ldil L%dcache_stride, %r1 + ldw R%dcache_stride(%r1), %r23 + ldo -1(%r23), %r21 + ANDCM %r26, %r21, %r26 + +#ifdef CONFIG_64BIT + depd,z %r23, 59, 60, %r21 +#else + depw,z %r23, 27, 28, %r21 +#endif + add %r26, %r21, %r22 + cmpb,COND(>>),n %r22, %r25, 2f /* predict not taken */ +1: add %r22, %r21, %r22 + 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,COND(<<=) %r22, %r25, 1b /* predict taken */ + fdc,m %r23(%sr3, %r26) + +2: cmpb,COND(>>),n %r25, %r26, 2b + fdc,m %r23(%sr3, %r26) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(flush_user_dcache_range_asm) + +ENTRY_CFI(flush_kernel_dcache_range_asm) +88: ldil L%dcache_stride, %r1 + ldw R%dcache_stride(%r1), %r23 + ldo -1(%r23), %r21 + ANDCM %r26, %r21, %r26 + +#ifdef CONFIG_64BIT + depd,z %r23, 59, 60, %r21 +#else + depw,z %r23, 27, 28, %r21 +#endif + add %r26, %r21, %r22 + cmpb,COND(>>),n %r22, %r25, 2f /* predict not taken */ +1: add %r22, %r21, %r22 + 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(<<=) %r22, %r25, 1b /* predict taken */ + fdc,m %r23(%r26) + +2: cmpb,COND(>>),n %r25, %r26, 2b /* predict taken */ + fdc,m %r23(%r26) + + sync +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + bv %r0(%r2) + nop +ENDPROC_CFI(flush_kernel_dcache_range_asm) + +ENTRY_CFI(purge_kernel_dcache_range_asm) +88: ldil L%dcache_stride, %r1 + ldw R%dcache_stride(%r1), %r23 + ldo -1(%r23), %r21 + ANDCM %r26, %r21, %r26 + +#ifdef CONFIG_64BIT + depd,z %r23, 59, 60, %r21 +#else + depw,z %r23, 27, 28, %r21 +#endif + add %r26, %r21, %r22 + cmpb,COND(>>),n %r22, %r25, 2f /* predict not taken */ +1: add %r22, %r21, %r22 + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + pdc,m %r23(%r26) + cmpb,COND(<<=) %r22, %r25, 1b /* predict taken */ + pdc,m %r23(%r26) + +2: cmpb,COND(>>),n %r25, %r26, 2b /* predict taken */ + pdc,m %r23(%r26) + + sync +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP) + bv %r0(%r2) + nop +ENDPROC_CFI(purge_kernel_dcache_range_asm) + +ENTRY_CFI(flush_user_icache_range_asm) +88: ldil L%icache_stride, %r1 + ldw R%icache_stride(%r1), %r23 + ldo -1(%r23), %r21 + ANDCM %r26, %r21, %r26 + +#ifdef CONFIG_64BIT + depd,z %r23, 59, 60, %r21 +#else + depw,z %r23, 27, 28, %r21 +#endif + add %r26, %r21, %r22 + cmpb,COND(>>),n %r22, %r25, 2f /* predict not taken */ +1: add %r22, %r21, %r22 + 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,COND(<<=) %r22, %r25, 1b /* predict taken */ + fic,m %r23(%sr3, %r26) + +2: cmpb,COND(>>),n %r25, %r26, 2b + fic,m %r23(%sr3, %r26) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(flush_user_icache_range_asm) + +ENTRY_CFI(flush_kernel_icache_page) +88: ldil L%icache_stride, %r1 + ldw R%icache_stride(%r1), %r23 + +#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 + + +1: fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + cmpb,COND(>>) %r25, %r26, 1b /* predict taken */ + fic,m %r23(%sr4, %r26) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(flush_kernel_icache_page) + +ENTRY_CFI(flush_kernel_icache_range_asm) +88: ldil L%icache_stride, %r1 + ldw R%icache_stride(%r1), %r23 + ldo -1(%r23), %r21 + ANDCM %r26, %r21, %r26 + +#ifdef CONFIG_64BIT + depd,z %r23, 59, 60, %r21 +#else + depw,z %r23, 27, 28, %r21 +#endif + add %r26, %r21, %r22 + cmpb,COND(>>),n %r22, %r25, 2f /* predict not taken */ +1: add %r22, %r21, %r22 + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + fic,m %r23(%sr4, %r26) + cmpb,COND(<<=) %r22, %r25, 1b /* predict taken */ + fic,m %r23(%sr4, %r26) + +2: cmpb,COND(>>),n %r25, %r26, 2b /* predict taken */ + fic,m %r23(%sr4, %r26) + +89: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP) + sync + bv %r0(%r2) + nop +ENDPROC_CFI(flush_kernel_icache_range_asm) + + .text + + /* align should cover use of rfi in disable_sr_hashing_asm and + * srdis_done. + */ + .align 256 +ENTRY_CFI(disable_sr_hashing_asm) + /* + * Switch to real mode + */ + /* pcxt_ssm_bug */ + rsm PSW_SM_I, %r0 + load32 PA(1f), %r1 + nop + nop + nop + nop + nop + + rsm PSW_SM_Q, %r0 /* prep to load iia queue */ + mtctl %r0, %cr17 /* Clear IIASQ tail */ + mtctl %r0, %cr17 /* Clear IIASQ head */ + mtctl %r1, %cr18 /* IIAOQ head */ + ldo 4(%r1), %r1 + mtctl %r1, %cr18 /* IIAOQ tail */ + load32 REAL_MODE_PSW, %r1 + mtctl %r1, %ipsw + rfi + nop + +1: cmpib,=,n SRHASH_PCXST, %r26,srdis_pcxs + cmpib,=,n SRHASH_PCXL, %r26,srdis_pcxl + cmpib,=,n SRHASH_PA20, %r26,srdis_pa20 + b,n srdis_done + +srdis_pcxs: + + /* Disable Space Register Hashing for PCXS,PCXT,PCXT' */ + + .word 0x141c1a00 /* mfdiag %dr0, %r28 */ + .word 0x141c1a00 /* must issue twice */ + depwi 0,18,1, %r28 /* Clear DHE (dcache hash enable) */ + depwi 0,20,1, %r28 /* Clear IHE (icache hash enable) */ + .word 0x141c1600 /* mtdiag %r28, %dr0 */ + .word 0x141c1600 /* must issue twice */ + b,n srdis_done + +srdis_pcxl: + + /* Disable Space Register Hashing for PCXL */ + + .word 0x141c0600 /* mfdiag %dr0, %r28 */ + depwi 0,28,2, %r28 /* Clear DHASH_EN & IHASH_EN */ + .word 0x141c0240 /* mtdiag %r28, %dr0 */ + b,n srdis_done + +srdis_pa20: + + /* Disable Space Register Hashing for PCXU,PCXU+,PCXW,PCXW+,PCXW2 */ + + .word 0x144008bc /* mfdiag %dr2, %r28 */ + depdi 0, 54,1, %r28 /* clear DIAG_SPHASH_ENAB (bit 54) */ + .word 0x145c1840 /* mtdiag %r28, %dr2 */ + + +srdis_done: + /* Switch back to virtual mode */ + rsm PSW_SM_I, %r0 /* prep to load iia queue */ + load32 2f, %r1 + nop + nop + nop + nop + nop + + rsm PSW_SM_Q, %r0 /* prep to load iia queue */ + mtctl %r0, %cr17 /* Clear IIASQ tail */ + mtctl %r0, %cr17 /* Clear IIASQ head */ + mtctl %r1, %cr18 /* IIAOQ head */ + ldo 4(%r1), %r1 + mtctl %r1, %cr18 /* IIAOQ tail */ + load32 KERNEL_PSW, %r1 + mtctl %r1, %ipsw + rfi + nop + +2: bv %r0(%r2) + nop +ENDPROC_CFI(disable_sr_hashing_asm) + + .end diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c new file mode 100644 index 000000000..00297e8e1 --- /dev/null +++ b/arch/parisc/kernel/parisc_ksyms.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Architecture-specific kernel symbols + * + * Copyright (C) 2000-2001 Richard Hirst <rhirst with parisc-linux.org> + * Copyright (C) 2001 Dave Kennedy + * Copyright (C) 2001 Paul Bame <bame at parisc-linux.org> + * 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-2007 Helge Deller <deller with parisc-linux.org> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/syscalls.h> + +#include <linux/string.h> +EXPORT_SYMBOL(memset); + +#include <linux/atomic.h> +EXPORT_SYMBOL(__xchg8); +EXPORT_SYMBOL(__xchg32); +EXPORT_SYMBOL(__cmpxchg_u32); +EXPORT_SYMBOL(__cmpxchg_u64); +#ifdef CONFIG_SMP +EXPORT_SYMBOL(__atomic_hash); +#endif +#ifdef CONFIG_64BIT +EXPORT_SYMBOL(__xchg64); +#endif + +#include <linux/uaccess.h> +EXPORT_SYMBOL(lclear_user); + +#ifndef CONFIG_64BIT +/* Needed so insmod can set dp value */ +extern int $global$; +EXPORT_SYMBOL($global$); +#endif + +#include <asm/io.h> +EXPORT_SYMBOL(memcpy_toio); +EXPORT_SYMBOL(memcpy_fromio); +EXPORT_SYMBOL(memset_io); + +extern void $$divI(void); +extern void $$divU(void); +extern void $$remI(void); +extern void $$remU(void); +extern void $$mulI(void); +extern void $$divU_3(void); +extern void $$divU_5(void); +extern void $$divU_6(void); +extern void $$divU_9(void); +extern void $$divU_10(void); +extern void $$divU_12(void); +extern void $$divU_7(void); +extern void $$divU_14(void); +extern void $$divU_15(void); +extern void $$divI_3(void); +extern void $$divI_5(void); +extern void $$divI_6(void); +extern void $$divI_7(void); +extern void $$divI_9(void); +extern void $$divI_10(void); +extern void $$divI_12(void); +extern void $$divI_14(void); +extern void $$divI_15(void); + +EXPORT_SYMBOL($$divI); +EXPORT_SYMBOL($$divU); +EXPORT_SYMBOL($$remI); +EXPORT_SYMBOL($$remU); +EXPORT_SYMBOL($$mulI); +EXPORT_SYMBOL($$divU_3); +EXPORT_SYMBOL($$divU_5); +EXPORT_SYMBOL($$divU_6); +EXPORT_SYMBOL($$divU_9); +EXPORT_SYMBOL($$divU_10); +EXPORT_SYMBOL($$divU_12); +EXPORT_SYMBOL($$divU_7); +EXPORT_SYMBOL($$divU_14); +EXPORT_SYMBOL($$divU_15); +EXPORT_SYMBOL($$divI_3); +EXPORT_SYMBOL($$divI_5); +EXPORT_SYMBOL($$divI_6); +EXPORT_SYMBOL($$divI_7); +EXPORT_SYMBOL($$divI_9); +EXPORT_SYMBOL($$divI_10); +EXPORT_SYMBOL($$divI_12); +EXPORT_SYMBOL($$divI_14); +EXPORT_SYMBOL($$divI_15); + +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 CONFIG_64BIT +extern void __divdi3(void); +extern void __udivdi3(void); +extern void __umoddi3(void); +extern void __moddi3(void); + +EXPORT_SYMBOL(__divdi3); +EXPORT_SYMBOL(__udivdi3); +EXPORT_SYMBOL(__umoddi3); +EXPORT_SYMBOL(__moddi3); +#endif + +#ifndef CONFIG_64BIT +extern void $$dyncall(void); +EXPORT_SYMBOL($$dyncall); +#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/patch.c b/arch/parisc/kernel/patch.c new file mode 100644 index 000000000..e59574f65 --- /dev/null +++ b/arch/parisc/kernel/patch.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 + /* + * functions to patch RO kernel text during runtime + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/kprobes.h> +#include <linux/mm.h> +#include <linux/stop_machine.h> + +#include <asm/cacheflush.h> +#include <asm/fixmap.h> +#include <asm/patch.h> + +struct patch { + void *addr; + u32 *insn; + unsigned int len; +}; + +static DEFINE_RAW_SPINLOCK(patch_lock); + +static void __kprobes *patch_map(void *addr, int fixmap, unsigned long *flags, + int *need_unmap) +{ + unsigned long uintaddr = (uintptr_t) addr; + bool module = !core_kernel_text(uintaddr); + struct page *page; + + *need_unmap = 0; + if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) + page = vmalloc_to_page(addr); + else if (!module && IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) + page = virt_to_page(addr); + else + return addr; + + *need_unmap = 1; + set_fixmap(fixmap, page_to_phys(page)); + raw_spin_lock_irqsave(&patch_lock, *flags); + + return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK)); +} + +static void __kprobes patch_unmap(int fixmap, unsigned long *flags) +{ + clear_fixmap(fixmap); + + raw_spin_unlock_irqrestore(&patch_lock, *flags); +} + +void __kprobes __patch_text_multiple(void *addr, u32 *insn, unsigned int len) +{ + unsigned long start = (unsigned long)addr; + unsigned long end = (unsigned long)addr + len; + unsigned long flags; + u32 *p, *fixmap; + int mapped; + + /* Make sure we don't have any aliases in cache */ + flush_kernel_dcache_range_asm(start, end); + flush_kernel_icache_range_asm(start, end); + flush_tlb_kernel_range(start, end); + + p = fixmap = patch_map(addr, FIX_TEXT_POKE0, &flags, &mapped); + + while (len >= 4) { + *p++ = *insn++; + addr += sizeof(u32); + len -= sizeof(u32); + if (len && offset_in_page(addr) == 0) { + /* + * We're crossing a page boundary, so + * need to remap + */ + flush_kernel_dcache_range_asm((unsigned long)fixmap, + (unsigned long)p); + flush_tlb_kernel_range((unsigned long)fixmap, + (unsigned long)p); + if (mapped) + patch_unmap(FIX_TEXT_POKE0, &flags); + p = fixmap = patch_map(addr, FIX_TEXT_POKE0, &flags, + &mapped); + } + } + + flush_kernel_dcache_range_asm((unsigned long)fixmap, (unsigned long)p); + flush_tlb_kernel_range((unsigned long)fixmap, (unsigned long)p); + if (mapped) + patch_unmap(FIX_TEXT_POKE0, &flags); +} + +void __kprobes __patch_text(void *addr, u32 insn) +{ + __patch_text_multiple(addr, &insn, sizeof(insn)); +} + +static int __kprobes patch_text_stop_machine(void *data) +{ + struct patch *patch = data; + + __patch_text_multiple(patch->addr, patch->insn, patch->len); + return 0; +} + +void __kprobes patch_text(void *addr, unsigned int insn) +{ + struct patch patch = { + .addr = addr, + .insn = &insn, + .len = sizeof(insn), + }; + + stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL); +} + +void __kprobes patch_text_multiple(void *addr, u32 *insn, unsigned int len) +{ + + struct patch patch = { + .addr = addr, + .insn = insn, + .len = len + }; + + stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL); +} diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c new file mode 100644 index 000000000..71ed5391f --- /dev/null +++ b/arch/parisc/kernel/pci-dma.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +** 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/core-api/dma-api-howto.rst for interface definitions. +** +** (c) Copyright 1999,2000 Hewlett-Packard Company +** (c) Copyright 2000 Grant Grundler +** (c) Copyright 2000 Philipp Rumpf <prumpf@tux.org> +** (c) Copyright 2000 John Marvin +** +** "leveraged" from 2.3.47: arch/ia64/kernel/pci-dma.c. +** (I assume it's from David Mosberger-Tang but there was no Copyright) +** +** AFAIK, all PA7100LC and PA7300LC platforms can use this code. +** +** - ggg +*/ + +#include <linux/init.h> +#include <linux/gfp.h> +#include <linux/mm.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/dma-direct.h> +#include <linux/dma-map-ops.h> + +#include <asm/cacheflush.h> +#include <asm/dma.h> /* for DMA_CHUNK_SIZE */ +#include <asm/io.h> +#include <asm/page.h> /* get_order */ +#include <linux/uaccess.h> +#include <asm/tlbflush.h> /* for purge_tlb_*() macros */ + +static struct proc_dir_entry * proc_gsc_root __read_mostly = NULL; +static unsigned long pcxl_used_bytes __read_mostly; +static unsigned long pcxl_used_pages __read_mostly; + +extern unsigned long pcxl_dma_start; /* Start of pcxl dma mapping area */ +static DEFINE_SPINLOCK(pcxl_res_lock); +static char *pcxl_res_map; +static int pcxl_res_hint; +static int pcxl_res_size; + +#ifdef DEBUG_PCXL_RESOURCE +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + + +/* +** Dump a hex representation of the resource map. +*/ + +#ifdef DUMP_RESMAP +static +void dump_resmap(void) +{ + u_long *res_ptr = (unsigned long *)pcxl_res_map; + u_long i = 0; + + printk("res_map: "); + for(; i < (pcxl_res_size / sizeof(unsigned long)); ++i, ++res_ptr) + printk("%08lx ", *res_ptr); + + printk("\n"); +} +#else +static inline void dump_resmap(void) {;} +#endif + +static inline int map_pte_uncached(pte_t * pte, + unsigned long vaddr, + unsigned long size, unsigned long *paddr_ptr) +{ + unsigned long end; + unsigned long orig_vaddr = vaddr; + + vaddr &= ~PMD_MASK; + end = vaddr + size; + 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"); + purge_tlb_start(flags); + set_pte(pte, __mk_pte(*paddr_ptr, PAGE_KERNEL_UNC)); + pdtlb(SR_KERNEL, orig_vaddr); + purge_tlb_end(flags); + vaddr += PAGE_SIZE; + orig_vaddr += PAGE_SIZE; + (*paddr_ptr) += PAGE_SIZE; + pte++; + } while (vaddr < end); + return 0; +} + +static inline int map_pmd_uncached(pmd_t * pmd, unsigned long vaddr, + unsigned long size, unsigned long *paddr_ptr) +{ + unsigned long end; + unsigned long orig_vaddr = vaddr; + + vaddr &= ~PGDIR_MASK; + end = vaddr + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + pte_t * pte = pte_alloc_kernel(pmd, vaddr); + if (!pte) + return -ENOMEM; + if (map_pte_uncached(pte, orig_vaddr, end - vaddr, paddr_ptr)) + return -ENOMEM; + vaddr = (vaddr + PMD_SIZE) & PMD_MASK; + orig_vaddr += PMD_SIZE; + pmd++; + } while (vaddr < end); + return 0; +} + +static inline int map_uncached_pages(unsigned long vaddr, unsigned long size, + unsigned long paddr) +{ + pgd_t * dir; + unsigned long end = vaddr + size; + + dir = pgd_offset_k(vaddr); + do { + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + + p4d = p4d_offset(dir, vaddr); + pud = pud_offset(p4d, vaddr); + pmd = pmd_alloc(NULL, pud, vaddr); + + if (!pmd) + return -ENOMEM; + if (map_pmd_uncached(pmd, vaddr, end - vaddr, &paddr)) + return -ENOMEM; + vaddr = vaddr + PGDIR_SIZE; + dir++; + } while (vaddr && (vaddr < end)); + return 0; +} + +static inline void unmap_uncached_pte(pmd_t * pmd, unsigned long vaddr, + unsigned long size) +{ + pte_t * pte; + unsigned long end; + unsigned long orig_vaddr = vaddr; + + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + pmd_ERROR(*pmd); + pmd_clear(pmd); + return; + } + pte = pte_offset_map(pmd, vaddr); + vaddr &= ~PMD_MASK; + end = vaddr + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + unsigned long flags; + pte_t page = *pte; + + pte_clear(&init_mm, vaddr, pte); + purge_tlb_start(flags); + pdtlb(SR_KERNEL, orig_vaddr); + purge_tlb_end(flags); + vaddr += PAGE_SIZE; + orig_vaddr += PAGE_SIZE; + pte++; + if (pte_none(page) || pte_present(page)) + continue; + printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n"); + } while (vaddr < end); +} + +static inline void unmap_uncached_pmd(pgd_t * dir, unsigned long vaddr, + unsigned long size) +{ + pmd_t * pmd; + unsigned long end; + unsigned long orig_vaddr = vaddr; + + if (pgd_none(*dir)) + return; + if (pgd_bad(*dir)) { + pgd_ERROR(*dir); + pgd_clear(dir); + return; + } + pmd = pmd_offset(pud_offset(p4d_offset(dir, vaddr), vaddr), vaddr); + vaddr &= ~PGDIR_MASK; + end = vaddr + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + unmap_uncached_pte(pmd, orig_vaddr, end - vaddr); + vaddr = (vaddr + PMD_SIZE) & PMD_MASK; + orig_vaddr += PMD_SIZE; + pmd++; + } while (vaddr < end); +} + +static void unmap_uncached_pages(unsigned long vaddr, unsigned long size) +{ + pgd_t * dir; + unsigned long end = vaddr + size; + + dir = pgd_offset_k(vaddr); + do { + unmap_uncached_pmd(dir, vaddr, end - vaddr); + vaddr = vaddr + PGDIR_SIZE; + dir++; + } while (vaddr && (vaddr < end)); +} + +#define PCXL_SEARCH_LOOP(idx, mask, size) \ + for(; res_ptr < res_end; ++res_ptr) \ + { \ + if(0 == ((*res_ptr) & mask)) { \ + *res_ptr |= mask; \ + idx = (int)((u_long)res_ptr - (u_long)pcxl_res_map); \ + pcxl_res_hint = idx + (size >> 3); \ + goto resource_found; \ + } \ + } + +#define PCXL_FIND_FREE_MAPPING(idx, mask, size) { \ + u##size *res_ptr = (u##size *)&(pcxl_res_map[pcxl_res_hint & ~((size >> 3) - 1)]); \ + u##size *res_end = (u##size *)&pcxl_res_map[pcxl_res_size]; \ + PCXL_SEARCH_LOOP(idx, mask, size); \ + res_ptr = (u##size *)&pcxl_res_map[0]; \ + PCXL_SEARCH_LOOP(idx, mask, size); \ +} + +unsigned long +pcxl_alloc_range(size_t size) +{ + int res_idx; + u_long mask, flags; + unsigned int pages_needed = size >> PAGE_SHIFT; + + mask = (u_long) -1L; + mask >>= BITS_PER_LONG - pages_needed; + + DBG_RES("pcxl_alloc_range() size: %d pages_needed %d pages_mask 0x%08lx\n", + size, pages_needed, mask); + + spin_lock_irqsave(&pcxl_res_lock, flags); + + if(pages_needed <= 8) { + PCXL_FIND_FREE_MAPPING(res_idx, mask, 8); + } else if(pages_needed <= 16) { + PCXL_FIND_FREE_MAPPING(res_idx, mask, 16); + } else if(pages_needed <= 32) { + PCXL_FIND_FREE_MAPPING(res_idx, mask, 32); + } else { + panic("%s: pcxl_alloc_range() Too many pages to map.\n", + __FILE__); + } + + dump_resmap(); + panic("%s: pcxl_alloc_range() out of dma mapping resources\n", + __FILE__); + +resource_found: + + DBG_RES("pcxl_alloc_range() res_idx %d mask 0x%08lx res_hint: %d\n", + res_idx, mask, pcxl_res_hint); + + pcxl_used_pages += pages_needed; + pcxl_used_bytes += ((pages_needed >> 3) ? (pages_needed >> 3) : 1); + + spin_unlock_irqrestore(&pcxl_res_lock, flags); + + dump_resmap(); + + /* + ** return the corresponding vaddr in the pcxl dma map + */ + return (pcxl_dma_start + (res_idx << (PAGE_SHIFT + 3))); +} + +#define PCXL_FREE_MAPPINGS(idx, m, size) \ + u##size *res_ptr = (u##size *)&(pcxl_res_map[(idx) + (((size >> 3) - 1) & (~((size >> 3) - 1)))]); \ + /* BUG_ON((*res_ptr & m) != m); */ \ + *res_ptr &= ~m; + +/* +** clear bits in the pcxl resource map +*/ +static void +pcxl_free_range(unsigned long vaddr, size_t size) +{ + u_long mask, flags; + unsigned int res_idx = (vaddr - pcxl_dma_start) >> (PAGE_SHIFT + 3); + unsigned int pages_mapped = size >> PAGE_SHIFT; + + mask = (u_long) -1L; + mask >>= BITS_PER_LONG - pages_mapped; + + DBG_RES("pcxl_free_range() res_idx: %d size: %d pages_mapped %d mask 0x%08lx\n", + res_idx, size, pages_mapped, mask); + + spin_lock_irqsave(&pcxl_res_lock, flags); + + if(pages_mapped <= 8) { + PCXL_FREE_MAPPINGS(res_idx, mask, 8); + } else if(pages_mapped <= 16) { + PCXL_FREE_MAPPINGS(res_idx, mask, 16); + } else if(pages_mapped <= 32) { + PCXL_FREE_MAPPINGS(res_idx, mask, 32); + } else { + panic("%s: pcxl_free_range() Too many pages to unmap.\n", + __FILE__); + } + + pcxl_used_pages -= (pages_mapped ? pages_mapped : 1); + pcxl_used_bytes -= ((pages_mapped >> 3) ? (pages_mapped >> 3) : 1); + + spin_unlock_irqrestore(&pcxl_res_lock, flags); + + dump_resmap(); +} + +static int __maybe_unused 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 __init +pcxl_dma_init(void) +{ + if (pcxl_dma_start == 0) + return 0; + + pcxl_res_size = PCXL_DMA_MAP_SIZE >> (PAGE_SHIFT + 3); + pcxl_res_hint = 0; + 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", 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 = proc_create_single("pcxl_dma", 0, proc_gsc_root, + proc_pcxl_dma_show); + if (!ent) + printk(KERN_WARNING + "pci-dma.c: Unable to create pcxl_dma /proc entry.\n"); + } + return 0; +} + +__initcall(pcxl_dma_init); + +void *arch_dma_alloc(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs) +{ + unsigned long vaddr; + unsigned long paddr; + int order; + + if (boot_cpu_data.cpu_type != pcxl2 && boot_cpu_data.cpu_type != pcxl) + return NULL; + + order = get_order(size); + size = 1 << (order + PAGE_SHIFT); + vaddr = pcxl_alloc_range(size); + paddr = __get_free_pages(gfp | __GFP_ZERO, order); + flush_kernel_dcache_range(paddr, size); + paddr = __pa(paddr); + map_uncached_pages(vaddr, size, paddr); + *dma_handle = (dma_addr_t) paddr; + +#if 0 +/* This probably isn't needed to support EISA cards. +** ISA cards will certainly only support 24-bit DMA addressing. +** Not clear if we can, want, or need to support ISA. +*/ + if (!dev || *dev->coherent_dma_mask < 0xffffffff) + gfp |= GFP_DMA; +#endif + return (void *)vaddr; +} + +void arch_dma_free(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle, unsigned long attrs) +{ + int order = get_order(size); + + WARN_ON_ONCE(boot_cpu_data.cpu_type != pcxl2 && + boot_cpu_data.cpu_type != pcxl); + + size = 1 << (order + PAGE_SHIFT); + unmap_uncached_pages((unsigned long)vaddr, size); + pcxl_free_range((unsigned long)vaddr, size); + + free_pages((unsigned long)__va(dma_handle), order); +} + +void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + /* + * fdc: The data cache line is written back to memory, if and only if + * it is dirty, and then invalidated from the data cache. + */ + flush_kernel_dcache_range((unsigned long)phys_to_virt(paddr), size); +} + +void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + unsigned long addr = (unsigned long) phys_to_virt(paddr); + + switch (dir) { + case DMA_TO_DEVICE: + case DMA_BIDIRECTIONAL: + flush_kernel_dcache_range(addr, size); + return; + case DMA_FROM_DEVICE: + purge_kernel_dcache_range_asm(addr, addr + size); + return; + default: + BUG(); + } +} diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c new file mode 100644 index 000000000..cf285b17a --- /dev/null +++ b/arch/parisc/kernel/pci.c @@ -0,0 +1,268 @@ +/* + * 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) 1997, 1998 Ralf Baechle + * Copyright (C) 1999 SuSE GmbH + * Copyright (C) 1999-2001 Hewlett-Packard Company + * Copyright (C) 1999-2001 Grant Grundler + */ +#include <linux/eisa.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include <asm/io.h> +#include <asm/superio.h> + +#define DEBUG_RESOURCES 0 +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBGC(x...) printk(KERN_DEBUG x) +#else +# define DBGC(x...) +#endif + + +#if DEBUG_RESOURCES +#define DBG_RES(x...) printk(KERN_DEBUG x) +#else +#define DBG_RES(x...) +#endif + +struct pci_port_ops *pci_port __ro_after_init; +struct pci_bios_ops *pci_bios __ro_after_init; + +static int pci_hba_count __ro_after_init; + +/* parisc_pci_hba used by pci_port->in/out() ops to lookup bus data. */ +#define PCI_HBA_MAX 32 +static struct pci_hba_data *parisc_pci_hba[PCI_HBA_MAX] __ro_after_init; + + +/******************************************************************** +** +** I/O port space support +** +*********************************************************************/ + +/* EISA port numbers and PCI port numbers share the same interface. Some + * machines have both EISA and PCI adapters installed. Rather than turn + * pci_port into an array, we reserve bus 0 for EISA and call the EISA + * routines if the access is to a port on bus 0. We don't want to fix + * EISA and ISA drivers which assume port space is <= 0xffff. + */ + +#ifdef CONFIG_EISA +#define EISA_IN(size) if (EISA_bus && (b == 0)) return eisa_in##size(addr) +#define EISA_OUT(size) if (EISA_bus && (b == 0)) return eisa_out##size(d, addr) +#else +#define EISA_IN(size) +#define EISA_OUT(size) +#endif + +#define PCI_PORT_IN(type, size) \ +u##size in##type (int addr) \ +{ \ + int b = PCI_PORT_HBA(addr); \ + EISA_IN(size); \ + if (!parisc_pci_hba[b]) return (u##size) -1; \ + return pci_port->in##type(parisc_pci_hba[b], PCI_PORT_ADDR(addr)); \ +} \ +EXPORT_SYMBOL(in##type); + +PCI_PORT_IN(b, 8) +PCI_PORT_IN(w, 16) +PCI_PORT_IN(l, 32) + + +#define PCI_PORT_OUT(type, size) \ +void out##type (u##size d, int addr) \ +{ \ + int b = PCI_PORT_HBA(addr); \ + EISA_OUT(size); \ + if (!parisc_pci_hba[b]) return; \ + pci_port->out##type(parisc_pci_hba[b], PCI_PORT_ADDR(addr), d); \ +} \ +EXPORT_SYMBOL(out##type); + +PCI_PORT_OUT(b, 8) +PCI_PORT_OUT(w, 16) +PCI_PORT_OUT(l, 32) + + + +/* + * BIOS32 replacement. + */ +static int __init pcibios_init(void) +{ + if (!pci_bios) + return -1; + + if (pci_bios->init) { + pci_bios->init(); + } 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; +} + + +/* Called from pci_do_scan_bus() *after* walking a bus but before walking PPBs. */ +void pcibios_fixup_bus(struct pci_bus *bus) +{ + if (pci_bios->fixup_bus) { + pci_bios->fixup_bus(bus); + } else { + printk(KERN_WARNING "pci_bios != NULL but fixup_bus() is!\n"); + } +} + + +/* + * Called by pci_set_master() - a driver interface. + * + * Legacy PDC guarantees to set: + * Map Memory BAR's into PA IO space. + * Map Expansion ROM BAR into one common PA IO space per bus. + * Map IO BAR's into PCI IO space. + * Command (see below) + * Cache Line Size + * Latency Timer + * Interrupt Line + * PPB: secondary latency timer, io/mmio base/limit, + * bus numbers, bridge control + * + */ +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + + /* If someone already mucked with this, don't touch it. */ + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat >= 16) return; + + /* + ** HP generally has fewer devices on the bus than other architectures. + ** upper byte is PCI_LATENCY_TIMER. + */ + pci_write_config_word(dev, PCI_CACHE_LINE_SIZE, + (0x80 << 8) | pci_cache_line_size); +} + +/* + * pcibios_init_bridge() initializes cache line and default latency + * for pci controllers and pci-pci bridges + */ +void __ref pcibios_init_bridge(struct pci_dev *dev) +{ + unsigned short bridge_ctl, bridge_ctl_new; + + /* We deal only with pci controllers and pci-pci bridges. */ + if (!dev || (dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) + return; + + /* PCI-PCI bridge - set the cache line and default latency + * (32) for primary and secondary buses. + */ + pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 32); + + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bridge_ctl); + + bridge_ctl_new = bridge_ctl | PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_MASTER_ABORT; + dev_info(&dev->dev, "Changing bridge control from 0x%08x to 0x%08x\n", + bridge_ctl, bridge_ctl_new); + + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bridge_ctl_new); +} + +/* + * pcibios align resources() is called every time generic PCI code + * wants to generate a new address. The process of looking for + * an available address, each candidate is first "aligned" and + * then checked if the resource is available until a match is found. + * + * Since we are just checking candidates, don't use any fields other + * than res->start. + */ +resource_size_t pcibios_align_resource(void *data, const struct resource *res, + resource_size_t size, resource_size_t alignment) +{ + 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)), + res->parent, res->start, res->end, + (int) res->flags, size, alignment); + + /* If it's not IO, then it's gotta be MEM */ + align = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; + + /* Align to largest of MIN or input size */ + mask = max(alignment, align) - 1; + start += mask; + start &= ~mask; + + return start; +} + +/* + * 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. + * We enable the port IO and memory IO bits if the device has any BARs of + * that type, and we enable the PERR and SERR bits unconditionally. + * Drivers that do not need parity (eg graphics and possibly networking) + * can clear these bits if they want. + */ +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int err; + u16 cmd, old_cmd; + + err = pci_enable_resources(dev, mask); + if (err < 0) + return err; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + + cmd |= (PCI_COMMAND_SERR | PCI_COMMAND_PARITY); + +#if 0 + /* If bridge/bus controller has FBB enabled, child must too. */ + if (dev->bus->bridge_ctl & PCI_BRIDGE_CTL_FAST_BACK) + cmd |= PCI_COMMAND_FAST_BACK; +#endif + + 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; +} + + +/* PA-RISC specific */ +void pcibios_register_hba(struct pci_hba_data *hba) +{ + if (pci_hba_count >= PCI_HBA_MAX) { + printk(KERN_ERR "PCI: Too many Host Bus Adapters\n"); + return; + } + + parisc_pci_hba[pci_hba_count] = hba; + hba->hba_num = pci_hba_count++; +} + +subsys_initcall(pcibios_init); diff --git a/arch/parisc/kernel/pdc_chassis.c b/arch/parisc/kernel/pdc_chassis.c new file mode 100644 index 000000000..da154406d --- /dev/null +++ b/arch/parisc/kernel/pdc_chassis.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * interfaces to Chassis Codes via PDC (firmware) + * + * Copyright (C) 2002 Laurent Canet <canetl@esiee.fr> + * Copyright (C) 2002-2006 Thibaut VARENE <varenet@parisc-linux.org> + * + * 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 +#ifdef PDC_CHASSIS_DEBUG +#define DPRINTK(fmt, args...) printk(fmt, ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/panic_notifier.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 unsigned int pdc_chassis_enabled __read_mostly = 1; + + +/** + * pdc_chassis_setup() - Enable/disable pdc_chassis code at boot time. + * @str configuration param: 0 to disable chassis log + * @return 1 + */ + +static int __init pdc_chassis_setup(char *str) +{ + /*panic_timeout = simple_strtoul(str, NULL, 0);*/ + get_option(&str, &pdc_chassis_enabled); + return 1; +} +__setup("pdcchassis=", pdc_chassis_setup); + + +/** + * pdc_chassis_checkold() - Checks for old PDC_CHASSIS compatibility + * @pdc_chassis_old: 1 if old pdc chassis style + * + * 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) { + case 0x480: /* E25 */ + case 0x481: /* E35 */ + case 0x482: /* E45 */ + case 0x483: /* E55 */ + case 0x516: /* A180 */ + break; + + default: + break; + } + 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. + * + * As soon as a panic occurs, we should inform the PDC. + */ + +static int pdc_chassis_panic_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC); + return NOTIFY_DONE; +} + + +static struct notifier_block pdc_chassis_panic_block = { + .notifier_call = pdc_chassis_panic_event, + .priority = INT_MAX, +}; + + +/** + * parisc_reboot_event() - Called by the reboot handler. + * + * As soon as a reboot occurs, we should inform the PDC. + */ + +static int pdc_chassis_reboot_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_SHUTDOWN); + return NOTIFY_DONE; +} + + +static struct notifier_block pdc_chassis_reboot_block = { + .notifier_call = pdc_chassis_reboot_event, + .priority = INT_MAX, +}; +#endif /* CONFIG_PDC_CHASSIS */ + + +/** + * parisc_pdc_chassis_init() - Called at boot time. + */ + +void __init parisc_pdc_chassis_init(void) +{ +#ifdef CONFIG_PDC_CHASSIS + if (likely(pdc_chassis_enabled)) { + DPRINTK(KERN_DEBUG "%s: parisc_pdc_chassis_init()\n", __FILE__); + + /* Let see if we have something to handle... */ + 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 */ +} + + +/** + * pdc_chassis_send_status() - Sends a predefined message to the chassis, + * and changes the front panel LEDs according to the new system state + * @retval: PDC call return value. + * + * Only machines with 64 bits PDC PAT and those reported in + * pdc_chassis_checkold() are supported atm. + * + * returns 0 if no error, -1 if no supported PDC is present or invalid message, + * else returns the appropriate PDC error code. + * + * For a list of predefined messages, see asm-parisc/pdc_chassis.h + */ + +int pdc_chassis_send_status(int message) +{ + /* Maybe we should do that in an other way ? */ + int retval = 0; +#ifdef CONFIG_PDC_CHASSIS + if (likely(pdc_chassis_enabled)) { + + DPRINTK(KERN_DEBUG "%s: pdc_chassis_send_status(%d)\n", __FILE__, message); + +#ifdef CONFIG_64BIT + if (is_pdc_pat()) { + switch(message) { + case PDC_CHASSIS_DIRECT_BSTART: + retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_BSTART, PDC_CHASSIS_LSTATE_RUN_NORMAL); + break; + + case PDC_CHASSIS_DIRECT_BCOMPLETE: + retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_BCOMPLETE, PDC_CHASSIS_LSTATE_RUN_NORMAL); + break; + + case PDC_CHASSIS_DIRECT_SHUTDOWN: + retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_SHUTDOWN, PDC_CHASSIS_LSTATE_NONOS); + break; + + case PDC_CHASSIS_DIRECT_PANIC: + retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_PANIC, PDC_CHASSIS_LSTATE_RUN_CRASHREC); + break; + + case PDC_CHASSIS_DIRECT_LPMC: + retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_LPMC, PDC_CHASSIS_LSTATE_RUN_SYSINT); + break; + + case PDC_CHASSIS_DIRECT_HPMC: + retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_HPMC, PDC_CHASSIS_LSTATE_RUN_NCRIT); + break; + + default: + retval = -1; + } + } else retval = -1; +#else + 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; + + case PDC_CHASSIS_DIRECT_SHUTDOWN: + retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_SHUT)); + break; + + case PDC_CHASSIS_DIRECT_HPMC: + case PDC_CHASSIS_DIRECT_PANIC: + retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_FLT)); + break; + + case PDC_CHASSIS_DIRECT_LPMC: + retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_WARN)); + break; + + default: + retval = -1; + } + } else retval = -1; +#endif /* CONFIG_64BIT */ + } /* if (pdc_chassis_enabled) */ +#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 __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_single("chassis", 0400, NULL, pdc_chassis_warn_show); + 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 new file mode 100644 index 000000000..cf3bf8232 --- /dev/null +++ b/arch/parisc/kernel/pdc_cons.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PDC early console support - use PDC firmware to dump text via boot console + * + * Copyright (C) 2001-2022 Helge Deller <deller@gmx.de> + */ + +#include <linux/console.h> +#include <linux/init.h> +#include <linux/serial_core.h> +#include <linux/kgdb.h> +#include <asm/page.h> /* for PAGE0 */ +#include <asm/pdc.h> /* for iodc_call() proto and friends */ + +static void pdc_console_write(struct console *co, const char *s, unsigned count) +{ + int i = 0; + + do { + i += pdc_iodc_print(s + i, count - i); + } while (i < count); +} + +#ifdef CONFIG_KGDB +static int kgdb_pdc_read_char(void) +{ + int c = pdc_iodc_getc(); + + return (c <= 0) ? NO_POLL_CHAR : c; +} + +static void kgdb_pdc_write_char(u8 chr) +{ + /* no need to print char as it's shown on standard console */ + /* pdc_iodc_print(&chr, 1); */ +} + +static struct kgdb_io kgdb_pdc_io_ops = { + .name = "kgdb_pdc", + .read_char = kgdb_pdc_read_char, + .write_char = kgdb_pdc_write_char, +}; +#endif + +static int __init pdc_earlycon_setup(struct earlycon_device *device, + const char *opt) +{ + struct console *earlycon_console; + + /* If the console is duplex then copy the COUT parameters to CIN. */ + if (PAGE0->mem_cons.cl_class == CL_DUPLEX) + memcpy(&PAGE0->mem_kbd, &PAGE0->mem_cons, sizeof(PAGE0->mem_cons)); + + earlycon_console = device->con; + earlycon_console->write = pdc_console_write; + device->port.iotype = UPIO_MEM32BE; + +#ifdef CONFIG_KGDB + kgdb_register_io_module(&kgdb_pdc_io_ops); +#endif + + return 0; +} + +EARLYCON_DECLARE(pdc, pdc_earlycon_setup); diff --git a/arch/parisc/kernel/pdt.c b/arch/parisc/kernel/pdt.c new file mode 100644 index 000000000..e391b175f --- /dev/null +++ b/arch/parisc/kernel/pdt.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Page Deallocation Table (PDT) support + * + * The Page Deallocation Table (PDT) is maintained by firmware and holds a + * list of memory addresses in which memory errors were detected. + * The list contains both single-bit (correctable) and double-bit + * (uncorrectable) errors. + * + * Copyright 2017 by Helge Deller <deller@gmx.de> + * + * possible future enhancements: + * - add userspace interface via procfs or sysfs to clear PDT + */ + +#include <linux/memblock.h> +#include <linux/seq_file.h> +#include <linux/kthread.h> +#include <linux/initrd.h> +#include <linux/pgtable.h> +#include <linux/swap.h> +#include <linux/swapops.h> + +#include <asm/pdc.h> +#include <asm/pdcpat.h> +#include <asm/sections.h> + +enum pdt_access_type { + PDT_NONE, + PDT_PDC, + PDT_PAT_NEW, + PDT_PAT_CELL +}; + +static enum pdt_access_type pdt_type; + +/* PDT poll interval: 1 minute if errors, 5 minutes if everything OK. */ +#define PDT_POLL_INTERVAL_DEFAULT (5*60*HZ) +#define PDT_POLL_INTERVAL_SHORT (1*60*HZ) +static unsigned long pdt_poll_interval = PDT_POLL_INTERVAL_DEFAULT; + +/* global PDT status information */ +static struct pdc_mem_retinfo pdt_status; + +#define MAX_PDT_TABLE_SIZE PAGE_SIZE +#define MAX_PDT_ENTRIES (MAX_PDT_TABLE_SIZE / sizeof(unsigned long)) +static unsigned long pdt_entry[MAX_PDT_ENTRIES] __page_aligned_bss; + +/* + * Constants for the pdt_entry format: + * A pdt_entry holds the physical address in bits 0-57, bits 58-61 are + * reserved, bit 62 is the perm bit and bit 63 is the error_type bit. + * The perm bit indicates whether the error have been verified as a permanent + * error (value of 1) or has not been verified, and may be transient (value + * of 0). The error_type bit indicates whether the error is a single bit error + * (value of 1) or a multiple bit error. + * On non-PAT machines phys_addr is encoded in bits 0-59 and error_type in bit + * 63. Those machines don't provide the perm bit. + */ + +#define PDT_ADDR_PHYS_MASK (pdt_type != PDT_PDC ? ~0x3f : ~0x0f) +#define PDT_ADDR_PERM_ERR (pdt_type != PDT_PDC ? 2UL : 0UL) +#define PDT_ADDR_SINGLE_ERR 1UL + +/* report PDT entries via /proc/meminfo */ +void arch_report_meminfo(struct seq_file *m) +{ + if (pdt_type == PDT_NONE) + return; + + seq_printf(m, "PDT_max_entries: %7lu\n", + pdt_status.pdt_size); + seq_printf(m, "PDT_cur_entries: %7lu\n", + pdt_status.pdt_entries); +} + +static int get_info_pat_new(void) +{ + struct pdc_pat_mem_retinfo pat_rinfo; + int ret; + + /* newer PAT machines like C8000 report info for all cells */ + if (is_pdc_pat()) + ret = pdc_pat_mem_pdt_info(&pat_rinfo); + else + return PDC_BAD_PROC; + + pdt_status.pdt_size = pat_rinfo.max_pdt_entries; + pdt_status.pdt_entries = pat_rinfo.current_pdt_entries; + pdt_status.pdt_status = 0; + pdt_status.first_dbe_loc = pat_rinfo.first_dbe_loc; + pdt_status.good_mem = pat_rinfo.good_mem; + + return ret; +} + +static int get_info_pat_cell(void) +{ + struct pdc_pat_mem_cell_pdt_retinfo cell_rinfo; + int ret; + + /* older PAT machines like rp5470 report cell info only */ + if (is_pdc_pat()) + ret = pdc_pat_mem_pdt_cell_info(&cell_rinfo, parisc_cell_num); + else + return PDC_BAD_PROC; + + pdt_status.pdt_size = cell_rinfo.max_pdt_entries; + pdt_status.pdt_entries = cell_rinfo.current_pdt_entries; + pdt_status.pdt_status = 0; + pdt_status.first_dbe_loc = cell_rinfo.first_dbe_loc; + pdt_status.good_mem = cell_rinfo.good_mem; + + return ret; +} + +static void report_mem_err(unsigned long pde) +{ + struct pdc_pat_mem_phys_mem_location loc; + unsigned long addr; + char dimm_txt[32]; + + addr = pde & PDT_ADDR_PHYS_MASK; + + /* show DIMM slot description on PAT machines */ + if (is_pdc_pat()) { + pdc_pat_mem_get_dimm_phys_location(&loc, addr); + sprintf(dimm_txt, "DIMM slot %02x, ", loc.dimm_slot); + } else + dimm_txt[0] = 0; + + pr_warn("PDT: BAD MEMORY at 0x%08lx, %s%s%s-bit error.\n", + addr, dimm_txt, + pde & PDT_ADDR_PERM_ERR ? "permanent ":"", + pde & PDT_ADDR_SINGLE_ERR ? "single":"multi"); +} + + +/* + * pdc_pdt_init() + * + * Initialize kernel PDT structures, read initial PDT table from firmware, + * report all current PDT entries and mark bad memory with memblock_reserve() + * to avoid that the kernel will use broken memory areas. + * + */ +void __init pdc_pdt_init(void) +{ + int ret, i; + unsigned long entries; + struct pdc_mem_read_pdt pdt_read_ret; + + pdt_type = PDT_PAT_NEW; + ret = get_info_pat_new(); + + if (ret != PDC_OK) { + pdt_type = PDT_PAT_CELL; + ret = get_info_pat_cell(); + } + + if (ret != PDC_OK) { + pdt_type = PDT_PDC; + /* non-PAT machines provide the standard PDC call */ + ret = pdc_mem_pdt_info(&pdt_status); + } + + if (ret != PDC_OK) { + pdt_type = PDT_NONE; + pr_info("PDT: Firmware does not provide any page deallocation" + " information.\n"); + return; + } + + entries = pdt_status.pdt_entries; + if (WARN_ON(entries > MAX_PDT_ENTRIES)) + entries = pdt_status.pdt_entries = MAX_PDT_ENTRIES; + + pr_info("PDT: type %s, size %lu, entries %lu, status %lu, dbe_loc 0x%lx," + " good_mem %lu MB\n", + pdt_type == PDT_PDC ? __stringify(PDT_PDC) : + pdt_type == PDT_PAT_CELL ? __stringify(PDT_PAT_CELL) + : __stringify(PDT_PAT_NEW), + pdt_status.pdt_size, pdt_status.pdt_entries, + pdt_status.pdt_status, pdt_status.first_dbe_loc, + pdt_status.good_mem / 1024 / 1024); + + if (entries == 0) { + pr_info("PDT: Firmware reports all memory OK.\n"); + return; + } + + if (pdt_status.first_dbe_loc && + pdt_status.first_dbe_loc <= __pa((unsigned long)&_end)) + pr_crit("CRITICAL: Bad memory inside kernel image memory area!\n"); + + pr_warn("PDT: Firmware reports %lu entries of faulty memory:\n", + entries); + + if (pdt_type == PDT_PDC) + ret = pdc_mem_pdt_read_entries(&pdt_read_ret, pdt_entry); + else { +#ifdef CONFIG_64BIT + struct pdc_pat_mem_read_pd_retinfo pat_pret; + + if (pdt_type == PDT_PAT_CELL) + ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry, + MAX_PDT_ENTRIES); + else + ret = pdc_pat_mem_read_pd_pdt(&pat_pret, pdt_entry, + MAX_PDT_TABLE_SIZE, 0); +#else + ret = PDC_BAD_PROC; +#endif + } + + if (ret != PDC_OK) { + pdt_type = PDT_NONE; + pr_warn("PDT: Get PDT entries failed with %d\n", ret); + return; + } + + for (i = 0; i < pdt_status.pdt_entries; i++) { + unsigned long addr; + + report_mem_err(pdt_entry[i]); + + addr = pdt_entry[i] & PDT_ADDR_PHYS_MASK; + if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && + addr >= initrd_start && addr < initrd_end) + pr_crit("CRITICAL: initrd possibly broken " + "due to bad memory!\n"); + + /* mark memory page bad */ + memblock_reserve(pdt_entry[i] & PAGE_MASK, PAGE_SIZE); + num_poisoned_pages_inc(); + } +} + + +/* + * This is the PDT kernel thread main loop. + */ + +static int pdt_mainloop(void *unused) +{ + struct pdc_mem_read_pdt pdt_read_ret; + struct pdc_pat_mem_read_pd_retinfo pat_pret __maybe_unused; + unsigned long old_num_entries; + unsigned long *bad_mem_ptr; + int num, ret; + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + + old_num_entries = pdt_status.pdt_entries; + + schedule_timeout(pdt_poll_interval); + if (kthread_should_stop()) + break; + + /* Do we have new PDT entries? */ + switch (pdt_type) { + case PDT_PAT_NEW: + ret = get_info_pat_new(); + break; + case PDT_PAT_CELL: + ret = get_info_pat_cell(); + break; + default: + ret = pdc_mem_pdt_info(&pdt_status); + break; + } + + if (ret != PDC_OK) { + pr_warn("PDT: unexpected failure %d\n", ret); + return -EINVAL; + } + + /* if no new PDT entries, just wait again */ + num = pdt_status.pdt_entries - old_num_entries; + if (num <= 0) + continue; + + /* decrease poll interval in case we found memory errors */ + if (pdt_status.pdt_entries && + pdt_poll_interval == PDT_POLL_INTERVAL_DEFAULT) + pdt_poll_interval = PDT_POLL_INTERVAL_SHORT; + + /* limit entries to get */ + if (num > MAX_PDT_ENTRIES) { + num = MAX_PDT_ENTRIES; + pdt_status.pdt_entries = old_num_entries + num; + } + + /* get new entries */ + switch (pdt_type) { +#ifdef CONFIG_64BIT + case PDT_PAT_CELL: + if (pdt_status.pdt_entries > MAX_PDT_ENTRIES) { + pr_crit("PDT: too many entries.\n"); + return -ENOMEM; + } + ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry, + MAX_PDT_ENTRIES); + bad_mem_ptr = &pdt_entry[old_num_entries]; + break; + case PDT_PAT_NEW: + ret = pdc_pat_mem_read_pd_pdt(&pat_pret, + pdt_entry, + num * sizeof(unsigned long), + old_num_entries * sizeof(unsigned long)); + bad_mem_ptr = &pdt_entry[0]; + break; +#endif + default: + ret = pdc_mem_pdt_read_entries(&pdt_read_ret, + pdt_entry); + bad_mem_ptr = &pdt_entry[old_num_entries]; + break; + } + + /* report and mark memory broken */ + while (num--) { + unsigned long pde = *bad_mem_ptr++; + + report_mem_err(pde); + +#ifdef CONFIG_MEMORY_FAILURE + if ((pde & PDT_ADDR_PERM_ERR) || + ((pde & PDT_ADDR_SINGLE_ERR) == 0)) + memory_failure(pde >> PAGE_SHIFT, 0); + else + soft_offline_page(pde >> PAGE_SHIFT, 0); +#else + pr_crit("PDT: memory error at 0x%lx ignored.\n" + "Rebuild kernel with CONFIG_MEMORY_FAILURE=y " + "for real handling.\n", + pde & PDT_ADDR_PHYS_MASK); +#endif + + } + } + + return 0; +} + + +static int __init pdt_initcall(void) +{ + struct task_struct *kpdtd_task; + + if (pdt_type == PDT_NONE) + return -ENODEV; + + kpdtd_task = kthread_run(pdt_mainloop, NULL, "kpdtd"); + if (IS_ERR(kpdtd_task)) + return PTR_ERR(kpdtd_task); + + return 0; +} + +late_initcall(pdt_initcall); diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c new file mode 100644 index 000000000..d46b6709e --- /dev/null +++ b/arch/parisc/kernel/perf.c @@ -0,0 +1,838 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Parisc performance counters + * Copyright (C) 2001 Randolph Chung <tausq@debian.org> + * + * This code is derived, with permission, from HP/UX sources. + */ + +/* + * Edited comment from original sources: + * + * 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 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. + * + * Only 1 process is allowed to access the driver at any time, + * so the only protection that is needed is at open and close. + * A variable "perf_enabled" is used to hold the state of the + * driver. The spinlock "perf_lock" is used to protect the + * modification of the state during open/close operations so + * multiple processes don't get into the driver simultaneously. + * + * This driver accesses the processor directly vs going through + * the PDC INTRIGUE calls. This is done to eliminate bugs introduced + * in various PDC revisions. The code is much more maintainable + * and reliable this way vs having to debug on every version of PDC + * on every box. + */ + +#include <linux/capability.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include <linux/uaccess.h> +#include <asm/perf.h> +#include <asm/parisc-device.h> +#include <asm/processor.h> +#include <asm/runway.h> +#include <asm/io.h> /* for __raw_read() */ + +#include "perf_images.h" + +#define MAX_RDR_WORDS 24 +#define PERF_VERSION 2 /* derived from hpux's PI v2 interface */ + +/* definition of RDR regs */ +struct rdr_tbl_ent { + uint16_t width; + uint8_t num_words; + uint8_t write_control; +}; + +static int perf_processor_interface __read_mostly = UNKNOWN_INTF; +static int perf_enabled __read_mostly; +static DEFINE_SPINLOCK(perf_lock); +struct parisc_device *cpu_device __read_mostly; + +/* RDRs to write for PCX-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 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 const struct rdr_tbl_ent perf_rdr_tbl_W[] = { + { 19, 1, 8 }, /* RDR 0 */ + { 16, 1, 16 }, /* RDR 1 */ + { 72, 2, 0 }, /* RDR 2 */ + { 81, 2, 0 }, /* RDR 3 */ + { 328, 6, 0 }, /* RDR 4 */ + { 160, 3, 0 }, /* RDR 5 */ + { 336, 6, 0 }, /* RDR 6 */ + { 164, 3, 0 }, /* RDR 7 */ + { 0, 0, 0 }, /* RDR 8 */ + { 35, 1, 0 }, /* RDR 9 */ + { 6, 1, 0 }, /* RDR 10 */ + { 18, 1, 0 }, /* RDR 11 */ + { 13, 1, 0 }, /* RDR 12 */ + { 8, 1, 0 }, /* RDR 13 */ + { 8, 1, 0 }, /* RDR 14 */ + { 8, 1, 0 }, /* RDR 15 */ + { 1530, 24, 0 }, /* RDR 16 */ + { 16, 1, 0 }, /* RDR 17 */ + { 4, 1, 0 }, /* RDR 18 */ + { 0, 0, 0 }, /* RDR 19 */ + { 152, 3, 24 }, /* RDR 20 */ + { 152, 3, 24 }, /* RDR 21 */ + { 233, 4, 48 }, /* RDR 22 */ + { 233, 4, 48 }, /* RDR 23 */ + { 71, 2, 0 }, /* RDR 24 */ + { 71, 2, 0 }, /* RDR 25 */ + { 11, 1, 0 }, /* RDR 26 */ + { 18, 1, 0 }, /* RDR 27 */ + { 128, 2, 0 }, /* RDR 28 */ + { 0, 0, 0 }, /* RDR 29 */ + { 16, 1, 0 }, /* RDR 30 */ + { 16, 1, 0 }, /* RDR 31 */ +}; + +/* RDR register descriptions for PCX-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 */ + { 0, 0, 0 }, /* RDR 3 */ + { 344, 6, 0 }, /* RDR 4 */ + { 176, 3, 0 }, /* RDR 5 */ + { 336, 6, 0 }, /* RDR 6 */ + { 0, 0, 0 }, /* RDR 7 */ + { 0, 0, 0 }, /* RDR 8 */ + { 0, 0, 0 }, /* RDR 9 */ + { 28, 1, 0 }, /* RDR 10 */ + { 33, 1, 0 }, /* RDR 11 */ + { 0, 0, 0 }, /* RDR 12 */ + { 230, 4, 0 }, /* RDR 13 */ + { 32, 1, 0 }, /* RDR 14 */ + { 128, 2, 0 }, /* RDR 15 */ + { 1494, 24, 0 }, /* RDR 16 */ + { 18, 1, 0 }, /* RDR 17 */ + { 4, 1, 0 }, /* RDR 18 */ + { 0, 0, 0 }, /* RDR 19 */ + { 158, 3, 24 }, /* RDR 20 */ + { 158, 3, 24 }, /* RDR 21 */ + { 194, 4, 48 }, /* RDR 22 */ + { 194, 4, 48 }, /* RDR 23 */ + { 71, 2, 0 }, /* RDR 24 */ + { 71, 2, 0 }, /* RDR 25 */ + { 28, 1, 0 }, /* RDR 26 */ + { 33, 1, 0 }, /* RDR 27 */ + { 88, 2, 0 }, /* RDR 28 */ + { 32, 1, 0 }, /* RDR 29 */ + { 24, 1, 0 }, /* RDR 30 */ + { 16, 1, 0 }, /* RDR 31 */ +}; + +/* + * A non-zero write_control in the above tables is a byte offset into + * this array. + */ +static const uint64_t perf_bitmasks[] = { + 0x0000000000000000ul, /* first dbl word must be zero */ + 0xfdffe00000000000ul, /* RDR0 bitmask */ + 0x003f000000000000ul, /* RDR1 bitmask */ + 0x00fffffffffffffful, /* RDR20-RDR21 bitmask (152 bits) */ + 0xfffffffffffffffful, + 0xfffffffc00000000ul, + 0xfffffffffffffffful, /* RDR22-RDR23 bitmask (233 bits) */ + 0xfffffffffffffffful, + 0xfffffffffffffffcul, + 0xff00000000000000ul +}; + +/* + * Write control bitmasks for Pa-8700 processor given + * some things have changed slightly. + */ +static const uint64_t perf_bitmasks_piranha[] = { + 0x0000000000000000ul, /* first dbl word must be zero */ + 0xfdffe00000000000ul, /* RDR0 bitmask */ + 0x003f000000000000ul, /* RDR1 bitmask */ + 0x00fffffffffffffful, /* RDR20-RDR21 bitmask (158 bits) */ + 0xfffffffffffffffful, + 0xfffffffc00000000ul, + 0xfffffffffffffffful, /* RDR22-RDR23 bitmask (210 bits) */ + 0xfffffffffffffffful, + 0xfffffffffffffffful, + 0xfffc000000000000ul +}; + +static const uint64_t *bitmask_array; /* array of bitmasks to use */ + +/****************************************************************************** + * Function Prototypes + *****************************************************************************/ +static int perf_config(uint32_t *image_ptr); +static int perf_release(struct inode *inode, struct file *file); +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 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 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); +static void perf_rdr_write(uint32_t rdr_num, uint64_t *buffer); + +/* External Assembly Routines */ +extern uint64_t perf_rdr_shift_in_W (uint32_t rdr_num, uint16_t width); +extern uint64_t perf_rdr_shift_in_U (uint32_t rdr_num, uint16_t width); +extern void perf_rdr_shift_out_W (uint32_t rdr_num, uint64_t buffer); +extern void perf_rdr_shift_out_U (uint32_t rdr_num, uint64_t buffer); +extern void perf_intrigue_enable_perf_counters (void); +extern void perf_intrigue_disable_perf_counters (void); + +/****************************************************************************** + * Function Definitions + *****************************************************************************/ + + +/* + * configure: + * + * Configure the cpu with a given data image. First turn off the counters, + * then download the image, then turn the counters back on. + */ +static int perf_config(uint32_t *image_ptr) +{ + long error; + uint32_t raddr[4]; + + /* Stop the counters*/ + error = perf_stop_counters(raddr); + if (error != 0) { + printk("perf_config: perf_stop_counters = %ld\n", error); + return -EINVAL; + } + +printk("Preparing to write image\n"); + /* Write the image to the chip */ + error = perf_write_image((uint64_t *)image_ptr); + if (error != 0) { + printk("perf_config: DOWNLOAD = %ld\n", error); + return -EINVAL; + } + +printk("Preparing to start counters\n"); + + /* Start the counters */ + perf_start_counters(); + + return sizeof(uint32_t); +} + +/* + * Open the device and initialize all of its memory. The device is only + * opened once, but can be "queried" by multiple processes that know its + * file descriptor. + */ +static int perf_open(struct inode *inode, struct file *file) +{ + spin_lock(&perf_lock); + if (perf_enabled) { + spin_unlock(&perf_lock); + return -EBUSY; + } + perf_enabled = 1; + spin_unlock(&perf_lock); + + return 0; +} + +/* + * Close the device. + */ +static int perf_release(struct inode *inode, struct file *file) +{ + spin_lock(&perf_lock); + perf_enabled = 0; + spin_unlock(&perf_lock); + + return 0; +} + +/* + * Read does nothing for this driver + */ +static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos) +{ + return 0; +} + +/* + * write: + * + * This routine downloads the image to the chip. It must be + * called on the processor that the download should happen + * on. + */ +static ssize_t perf_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + size_t image_size; + uint32_t image_type; + uint32_t interface_type; + uint32_t test; + + if (perf_processor_interface == ONYX_INTF) + image_size = PCXU_IMAGE_SIZE; + else if (perf_processor_interface == CUDA_INTF) + image_size = PCXW_IMAGE_SIZE; + else + return -EFAULT; + + if (!perfmon_capable()) + return -EACCES; + + if (count != sizeof(uint32_t)) + return -EIO; + + if (copy_from_user(&image_type, buf, sizeof(uint32_t))) + return -EFAULT; + + /* Get the interface type and test type */ + interface_type = (image_type >> 16) & 0xffff; + test = (image_type & 0xffff); + + /* Make sure everything makes sense */ + + /* First check the machine type is correct for + the requested image */ + if (((perf_processor_interface == CUDA_INTF) && + (interface_type != CUDA_INTF)) || + ((perf_processor_interface == ONYX_INTF) && + (interface_type != ONYX_INTF))) + return -EINVAL; + + /* Next check to make sure the requested image + is valid */ + if (((interface_type == CUDA_INTF) && + (test >= MAX_CUDA_IMAGES)) || + ((interface_type == ONYX_INTF) && + (test >= MAX_ONYX_IMAGES))) + return -EINVAL; + + /* Copy the image into the processor */ + if (interface_type == CUDA_INTF) + return perf_config(cuda_images[test]); + else + return perf_config(onyx_images[test]); + + return count; +} + +/* + * Patch the images that need to know the IVA addresses. + */ +static void perf_patch_images(void) +{ +#if 0 /* FIXME!! */ +/* + * NOTE: this routine is VERY specific to the current TLB image. + * If the image is changed, this routine might also need to be changed. + */ + extern void $i_itlb_miss_2_0(); + extern void $i_dtlb_miss_2_0(); + extern void PA2_0_iva(); + + /* + * We can only use the lower 32-bits, the upper 32-bits should be 0 + * anyway given this is in the kernel + */ + uint32_t itlb_addr = (uint32_t)&($i_itlb_miss_2_0); + uint32_t dtlb_addr = (uint32_t)&($i_dtlb_miss_2_0); + uint32_t IVAaddress = (uint32_t)&PA2_0_iva; + + if (perf_processor_interface == ONYX_INTF) { + /* clear last 2 bytes */ + onyx_images[TLBMISS][15] &= 0xffffff00; + /* set 2 bytes */ + onyx_images[TLBMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); + onyx_images[TLBMISS][16] = (dtlb_addr << 8)&0xffffff00; + onyx_images[TLBMISS][17] = itlb_addr; + + /* clear last 2 bytes */ + onyx_images[TLBHANDMISS][15] &= 0xffffff00; + /* set 2 bytes */ + onyx_images[TLBHANDMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); + onyx_images[TLBHANDMISS][16] = (dtlb_addr << 8)&0xffffff00; + onyx_images[TLBHANDMISS][17] = itlb_addr; + + /* clear last 2 bytes */ + onyx_images[BIG_CPI][15] &= 0xffffff00; + /* set 2 bytes */ + onyx_images[BIG_CPI][15] |= (0x000000ff&((dtlb_addr) >> 24)); + onyx_images[BIG_CPI][16] = (dtlb_addr << 8)&0xffffff00; + onyx_images[BIG_CPI][17] = itlb_addr; + + onyx_images[PANIC][15] &= 0xffffff00; /* clear last 2 bytes */ + onyx_images[PANIC][15] |= (0x000000ff&((IVAaddress) >> 24)); /* set 2 bytes */ + onyx_images[PANIC][16] = (IVAaddress << 8)&0xffffff00; + + + } else if (perf_processor_interface == CUDA_INTF) { + /* Cuda interface */ + cuda_images[TLBMISS][16] = + (cuda_images[TLBMISS][16]&0xffff0000) | + ((dtlb_addr >> 8)&0x0000ffff); + cuda_images[TLBMISS][17] = + ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); + cuda_images[TLBMISS][18] = (itlb_addr << 16)&0xffff0000; + + cuda_images[TLBHANDMISS][16] = + (cuda_images[TLBHANDMISS][16]&0xffff0000) | + ((dtlb_addr >> 8)&0x0000ffff); + cuda_images[TLBHANDMISS][17] = + ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); + cuda_images[TLBHANDMISS][18] = (itlb_addr << 16)&0xffff0000; + + cuda_images[BIG_CPI][16] = + (cuda_images[BIG_CPI][16]&0xffff0000) | + ((dtlb_addr >> 8)&0x0000ffff); + cuda_images[BIG_CPI][17] = + ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); + cuda_images[BIG_CPI][18] = (itlb_addr << 16)&0xffff0000; + } else { + /* Unknown type */ + } +#endif +} + + +/* + * ioctl routine + * All routines effect the processor that they are executed on. Thus you + * must be running on the processor that you wish to change. + */ + +static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long error_start; + uint32_t raddr[4]; + int error = 0; + + switch (cmd) { + + case PA_PERF_ON: + /* Start the counters */ + perf_start_counters(); + 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); + error = -EFAULT; + break; + } + + /* copy out the Counters */ + if (copy_to_user((void __user *)arg, raddr, + sizeof (raddr)) != 0) { + error = -EFAULT; + break; + } + break; + + case PA_PERF_VERSION: + /* Return the version # */ + error = put_user(PERF_VERSION, (int *)arg); + break; + + default: + error = -ENOTTY; + } + + return error; +} + +static const struct file_operations perf_fops = { + .llseek = no_llseek, + .read = perf_read, + .write = perf_write, + .unlocked_ioctl = perf_ioctl, + .compat_ioctl = perf_ioctl, + .open = perf_open, + .release = perf_release +}; + +static struct miscdevice perf_dev = { + MISC_DYNAMIC_MINOR, + PA_PERF_DEV, + &perf_fops +}; + +/* + * Initialize the module + */ +static int __init perf_init(void) +{ + int ret; + + /* Determine correct processor interface to use */ + bitmask_array = perf_bitmasks; + + if (boot_cpu_data.cpu_type == pcxu || + boot_cpu_data.cpu_type == pcxu_) { + perf_processor_interface = ONYX_INTF; + } 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 == mako2) { + perf_processor_interface = CUDA_INTF; + if (boot_cpu_data.cpu_type == pcxw2 || + boot_cpu_data.cpu_type == mako || + boot_cpu_data.cpu_type == mako2) + bitmask_array = perf_bitmasks_piranha; + } else { + perf_processor_interface = UNKNOWN_INTF; + printk("Performance monitoring counters not supported on this processor\n"); + return -ENODEV; + } + + ret = misc_register(&perf_dev); + if (ret) { + printk(KERN_ERR "Performance monitoring counters: " + "cannot register misc device.\n"); + return ret; + } + + /* Patch the images to match the system */ + perf_patch_images(); + + /* TODO: this only lets us access the first cpu.. what to do for SMP? */ + cpu_device = per_cpu(cpu_data, 0).dev; + printk("Performance monitoring counters enabled for %s\n", + per_cpu(cpu_data, 0).dev->name); + + return 0; +} +device_initcall(perf_init); + +/* + * perf_start_counters(void) + * + * Start the counters. + */ +static void perf_start_counters(void) +{ + /* Enable performance monitor counters */ + perf_intrigue_enable_perf_counters(); +} + +/* + * perf_stop_counters + * + * Stop the performance counters and save counts + * in a per_processor array. + */ +static int perf_stop_counters(uint32_t *raddr) +{ + uint64_t userbuf[MAX_RDR_WORDS]; + + /* Disable performance counters */ + perf_intrigue_disable_perf_counters(); + + if (perf_processor_interface == ONYX_INTF) { + uint64_t tmp64; + /* + * Read the counters + */ + if (!perf_rdr_read_ubuf(16, userbuf)) + return -13; + + /* 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 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 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 to 1528 */ + tmp64 = (userbuf[23] >> 7) & 0x00000000ffffffff; + /* OR sticky3 (bit 1529) to counter3 bit 32 */ + tmp64 |= (userbuf[23] << 25) & 0x0000000080000000; + raddr[3] = (uint32_t)tmp64; + + /* + * Zero out the counters + */ + + /* + * The counters and sticky-bits comprise the last 132 bits + * (1398 - 1529) of RDR16 on a U chip. We'll zero these + * out the easy way: zero out last 10 bits of dword 21, + * all of dword 22 and 58 bits (plus 6 don't care bits) of + * dword 23. + */ + userbuf[21] &= 0xfffffffffffffc00ul; /* 0 to last 10 bits */ + userbuf[22] = 0; + userbuf[23] = 0; + + /* + * Write back the zeroed bytes + the image given + * the read was destructive. + */ + perf_rdr_write(16, userbuf); + } else { + + /* + * Read RDR-15 which contains the counters and sticky bits + */ + if (!perf_rdr_read_ubuf(15, userbuf)) { + return -13; + } + + /* + * Clear out the counters + */ + perf_rdr_clear(15); + + /* + * Copy the counters + */ + raddr[0] = (uint32_t)((userbuf[0] >> 32) & 0x00000000ffffffffUL); + raddr[1] = (uint32_t)(userbuf[0] & 0x00000000ffffffffUL); + raddr[2] = (uint32_t)((userbuf[1] >> 32) & 0x00000000ffffffffUL); + raddr[3] = (uint32_t)(userbuf[1] & 0x00000000ffffffffUL); + } + + return 0; +} + +/* + * perf_rdr_get_entry + * + * Retrieve a pointer to the description of what this + * RDR contains. + */ +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]; + } else { + return &perf_rdr_tbl_W[rdr_num]; + } +} + +/* + * perf_rdr_read_ubuf + * + * Read the RDR value into the buffer specified. + */ +static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer) +{ + uint64_t data, data_mask = 0; + uint32_t width, xbits, i; + const struct rdr_tbl_ent *tentry; + + tentry = perf_rdr_get_entry(rdr_num); + if ((width = tentry->width) == 0) + return 0; + + /* Clear out buffer */ + i = tentry->num_words; + while (i--) { + buffer[i] = 0; + } + + /* Check for bits an even number of 64 */ + if ((xbits = width & 0x03f) != 0) { + data_mask = 1; + data_mask <<= (64 - xbits); + data_mask--; + } + + /* Grab all of the data */ + i = tentry->num_words; + while (i--) { + + if (perf_processor_interface == ONYX_INTF) { + data = perf_rdr_shift_in_U(rdr_num, width); + } else { + data = perf_rdr_shift_in_W(rdr_num, width); + } + if (xbits) { + buffer[i] |= (data << (64 - xbits)); + if (i) { + buffer[i-1] |= ((data >> xbits) & data_mask); + } + } else { + buffer[i] = data; + } + } + + return 1; +} + +/* + * perf_rdr_clear + * + * Zero out the given RDR register + */ +static int perf_rdr_clear(uint32_t rdr_num) +{ + const struct rdr_tbl_ent *tentry; + int32_t i; + + tentry = perf_rdr_get_entry(rdr_num); + + if (tentry->width == 0) { + return -1; + } + + i = tentry->num_words; + while (i--) { + if (perf_processor_interface == ONYX_INTF) { + perf_rdr_shift_out_U(rdr_num, 0UL); + } else { + perf_rdr_shift_out_W(rdr_num, 0UL); + } + } + + return 0; +} + + +/* + * perf_write_image + * + * Write the given image out to the processor + */ +static int perf_write_image(uint64_t *memaddr) +{ + uint64_t buffer[MAX_RDR_WORDS]; + uint64_t *bptr; + uint32_t dwords; + const uint32_t *intrigue_rdr; + const uint64_t *intrigue_bitmask; + uint64_t tmp64; + void __iomem *runway; + const struct rdr_tbl_ent *tentry; + int i; + + /* Clear out counters */ + if (perf_processor_interface == ONYX_INTF) { + + perf_rdr_clear(16); + + /* Toggle performance monitor */ + perf_intrigue_enable_perf_counters(); + perf_intrigue_disable_perf_counters(); + + intrigue_rdr = perf_rdrs_U; + } else { + perf_rdr_clear(15); + intrigue_rdr = perf_rdrs_W; + } + + /* Write all RDRs */ + while (*intrigue_rdr != -1) { + tentry = perf_rdr_get_entry(*intrigue_rdr); + perf_rdr_read_ubuf(*intrigue_rdr, buffer); + bptr = &buffer[0]; + dwords = tentry->num_words; + if (tentry->write_control) { + intrigue_bitmask = &bitmask_array[tentry->write_control >> 3]; + while (dwords--) { + tmp64 = *intrigue_bitmask & *memaddr++; + tmp64 |= (~(*intrigue_bitmask++)) & *bptr; + *bptr++ = tmp64; + } + } else { + while (dwords--) { + *bptr++ = *memaddr++; + } + } + + perf_rdr_write(*intrigue_rdr, buffer); + intrigue_rdr++; + } + + /* + * Now copy out the Runway stuff which is not in RDRs + */ + + if (cpu_device == NULL) + { + printk(KERN_ERR "write_image: cpu_device not yet initialized!\n"); + return -1; + } + + runway = ioremap(cpu_device->hpa.start, 4096); + if (!runway) { + pr_err("perf_write_image: ioremap failed!\n"); + return -ENOMEM; + } + + /* Merge intrigue bits into Runway STATUS 0 */ + tmp64 = __raw_readq(runway + RUNWAY_STATUS) & 0xffecfffffffffffful; + __raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul), + runway + RUNWAY_STATUS); + + /* Write RUNWAY DEBUG registers */ + for (i = 0; i < 8; i++) { + __raw_writeq(*memaddr++, runway + RUNWAY_DEBUG); + } + + return 0; +} + +/* + * perf_rdr_write + * + * Write the given RDR register with the contents + * of the given buffer. + */ +static void perf_rdr_write(uint32_t rdr_num, uint64_t *buffer) +{ + const struct rdr_tbl_ent *tentry; + int32_t i; + +printk("perf_rdr_write\n"); + tentry = perf_rdr_get_entry(rdr_num); + if (tentry->width == 0) { return; } + + i = tentry->num_words; + while (i--) { + if (perf_processor_interface == ONYX_INTF) { + perf_rdr_shift_out_U(rdr_num, buffer[i]); + } else { + perf_rdr_shift_out_W(rdr_num, buffer[i]); + } + } +printk("perf_rdr_write done\n"); +} diff --git a/arch/parisc/kernel/perf_asm.S b/arch/parisc/kernel/perf_asm.S new file mode 100644 index 000000000..8fceabb1a --- /dev/null +++ b/arch/parisc/kernel/perf_asm.S @@ -0,0 +1,1679 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* low-level asm for "intrigue" (PA8500-8700 CPU perf counters) + * + * Copyright (C) 2001 Randolph Chung <tausq at parisc-linux.org> + * Copyright (C) 2001 Hewlett-Packard (Grant Grundler) + */ + +#include <asm/assembly.h> + +#include <linux/init.h> +#include <linux/linkage.h> + +#ifdef CONFIG_64BIT + .level 2.0w +#endif /* CONFIG_64BIT */ + +#define MTDIAG_1(gr) .word 0x14201840 + gr*0x10000 +#define MTDIAG_2(gr) .word 0x14401840 + gr*0x10000 +#define MFDIAG_1(gr) .word 0x142008A0 + gr +#define MFDIAG_2(gr) .word 0x144008A0 + gr +#define STDIAG(dr) .word 0x14000AA0 + dr*0x200000 +#define SFDIAG(dr) .word 0x14000BA0 + dr*0x200000 +#define DR2_SLOW_RET 53 + + +; +; Enable the performance counters +; +; The coprocessor only needs to be enabled when +; starting/stopping the coprocessor with the pmenb/pmdis. +; + .text + +ENTRY(perf_intrigue_enable_perf_counters) + .proc + .callinfo frame=0,NO_CALLS + .entry + + ldi 0x20,%r25 ; load up perfmon bit + mfctl ccr,%r26 ; get coprocessor register + or %r25,%r26,%r26 ; set bit + mtctl %r26,ccr ; turn on performance coprocessor + pmenb ; enable performance monitor + ssm 0,0 ; dummy op to ensure completion + sync ; follow ERS + andcm %r26,%r25,%r26 ; clear bit now + mtctl %r26,ccr ; turn off performance coprocessor + nop ; NOPs as specified in ERS + nop + nop + nop + nop + nop + nop + bve (%r2) + nop + .exit + .procend +ENDPROC(perf_intrigue_enable_perf_counters) + +ENTRY(perf_intrigue_disable_perf_counters) + .proc + .callinfo frame=0,NO_CALLS + .entry + ldi 0x20,%r25 ; load up perfmon bit + mfctl ccr,%r26 ; get coprocessor register + or %r25,%r26,%r26 ; set bit + mtctl %r26,ccr ; turn on performance coprocessor + pmdis ; disable performance monitor + ssm 0,0 ; dummy op to ensure completion + andcm %r26,%r25,%r26 ; clear bit now + bve (%r2) + mtctl %r26,ccr ; turn off performance coprocessor + .exit + .procend +ENDPROC(perf_intrigue_disable_perf_counters) + +;*********************************************************************** +;* +;* Name: perf_rdr_shift_in_W +;* +;* Description: +;* This routine shifts data in from the RDR in arg0 and returns +;* the result in ret0. If the RDR is <= 64 bits in length, it +;* is shifted shifted backup immediately. This is to compensate +;* for RDR10 which has bits that preclude PDC stack operations +;* when they are in the wrong state. +;* +;* Arguments: +;* arg0 : rdr to be read +;* arg1 : bit length of rdr +;* +;* Returns: +;* ret0 = next 64 bits of rdr data from staging register +;* +;* Register usage: +;* arg0 : rdr to be read +;* arg1 : bit length of rdr +;* %r24 - original DR2 value +;* %r1 - scratch +;* %r29 - scratch +;* +;* Returns: +;* ret0 = RDR data (right justified) +;* +;*********************************************************************** + +ENTRY(perf_rdr_shift_in_W) + .proc + .callinfo frame=0,NO_CALLS + .entry +; +; read(shift in) the RDR. +; + +; NOTE: The PCX-W ERS states that DR2_SLOW_RET must be set before any +; shifting is done, from or to, remote diagnose registers. +; + + depdi,z 1,DR2_SLOW_RET,1,%r29 + MFDIAG_2 (24) + or %r24,%r29,%r29 + MTDIAG_2 (29) ; set DR2_SLOW_RET + + nop + nop + nop + nop + +; +; Cacheline start (32-byte cacheline) +; + nop + nop + nop + extrd,u arg1,63,6,%r1 ; setup shift amount by bits to move + + mtsar %r1 + shladd arg0,2,%r0,%r1 ; %r1 = 4 * RDR number + blr %r1,%r0 ; branch to 8-instruction sequence + nop + +; +; Cacheline start (32-byte cacheline) +; + + ; + ; RDR 0 sequence + ; + SFDIAG (0) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) ; mtdiag %dr1, %r1 + STDIAG (0) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 1 sequence + ; + sync + ssm 0,0 + SFDIAG (1) + ssm 0,0 + MFDIAG_1 (28) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + nop + + ; + ; RDR 2 read sequence + ; + SFDIAG (2) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (2) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 3 read sequence + ; + b,n perf_rdr_shift_in_W_leave + nop + nop + nop + nop + nop + nop + nop + + ; + ; RDR 4 read sequence + ; + sync + ssm 0,0 + SFDIAG (4) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 5 read sequence + ; + sync + ssm 0,0 + SFDIAG (5) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 6 read sequence + ; + sync + ssm 0,0 + SFDIAG (6) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 7 read sequence + ; + b,n perf_rdr_shift_in_W_leave + nop + nop + nop + nop + nop + nop + nop + + ; + ; RDR 8 read sequence + ; + b,n perf_rdr_shift_in_W_leave + nop + nop + nop + nop + nop + nop + nop + + ; + ; RDR 9 read sequence + ; + b,n perf_rdr_shift_in_W_leave + nop + nop + nop + nop + nop + nop + nop + + ; + ; RDR 10 read sequence + ; + SFDIAG (10) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (10) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 11 read sequence + ; + SFDIAG (11) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (11) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 12 read sequence + ; + b,n perf_rdr_shift_in_W_leave + nop + nop + nop + nop + nop + nop + nop + + ; + ; RDR 13 read sequence + ; + sync + ssm 0,0 + SFDIAG (13) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 14 read sequence + ; + SFDIAG (14) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (14) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 15 read sequence + ; + sync + ssm 0,0 + SFDIAG (15) + ssm 0,0 + MFDIAG_1 (28) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + nop + + ; + ; RDR 16 read sequence + ; + sync + ssm 0,0 + SFDIAG (16) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 17 read sequence + ; + SFDIAG (17) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (17) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 18 read sequence + ; + SFDIAG (18) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (18) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 19 read sequence + ; + b,n perf_rdr_shift_in_W_leave + nop + nop + nop + nop + nop + nop + nop + + ; + ; RDR 20 read sequence + ; + sync + ssm 0,0 + SFDIAG (20) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 21 read sequence + ; + sync + ssm 0,0 + SFDIAG (21) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 22 read sequence + ; + sync + ssm 0,0 + SFDIAG (22) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 23 read sequence + ; + sync + ssm 0,0 + SFDIAG (23) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 24 read sequence + ; + sync + ssm 0,0 + SFDIAG (24) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 25 read sequence + ; + sync + ssm 0,0 + SFDIAG (25) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 26 read sequence + ; + SFDIAG (26) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (26) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 27 read sequence + ; + SFDIAG (27) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (27) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 28 read sequence + ; + sync + ssm 0,0 + SFDIAG (28) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 29 read sequence + ; + sync + ssm 0,0 + SFDIAG (29) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_W_leave + ssm 0,0 + nop + + ; + ; RDR 30 read sequence + ; + SFDIAG (30) + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (30) + ssm 0,0 + b,n perf_rdr_shift_in_W_leave + + ; + ; RDR 31 read sequence + ; + sync + ssm 0,0 + SFDIAG (31) + ssm 0,0 + MFDIAG_1 (28) + nop + ssm 0,0 + nop + + ; + ; Fallthrough + ; + +perf_rdr_shift_in_W_leave: + bve (%r2) + .exit + MTDIAG_2 (24) ; restore DR2 + .procend +ENDPROC(perf_rdr_shift_in_W) + + +;*********************************************************************** +;* +;* Name: perf_rdr_shift_out_W +;* +;* Description: +;* This routine moves data to the RDR's. The double-word that +;* arg1 points to is loaded and moved into the staging register. +;* Then the STDIAG instruction for the RDR # in arg0 is called +;* to move the data to the RDR. +;* +;* Arguments: +;* arg0 = rdr number +;* arg1 = 64-bit value to write +;* %r24 - DR2 | DR2_SLOW_RET +;* %r23 - original DR2 value +;* +;* Returns: +;* None +;* +;* Register usage: +;* +;*********************************************************************** + +ENTRY(perf_rdr_shift_out_W) + .proc + .callinfo frame=0,NO_CALLS + .entry +; +; NOTE: The PCX-W ERS states that DR2_SLOW_RET must be set before any +; shifting is done, from or to, the remote diagnose registers. +; + + depdi,z 1,DR2_SLOW_RET,1,%r24 + MFDIAG_2 (23) + or %r24,%r23,%r24 + MTDIAG_2 (24) ; set DR2_SLOW_RET + MTDIAG_1 (25) ; data to the staging register + shladd arg0,2,%r0,%r1 ; %r1 = 4 * RDR number + blr %r1,%r0 ; branch to 8-instruction sequence + nop + + ; + ; RDR 0 write sequence + ; + sync ; RDR 0 write sequence + ssm 0,0 + STDIAG (0) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 1 write sequence + ; + sync + ssm 0,0 + STDIAG (1) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 2 write sequence + ; + sync + ssm 0,0 + STDIAG (2) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 3 write sequence + ; + sync + ssm 0,0 + STDIAG (3) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 4 write sequence + ; + sync + ssm 0,0 + STDIAG (4) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 5 write sequence + ; + sync + ssm 0,0 + STDIAG (5) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 6 write sequence + ; + sync + ssm 0,0 + STDIAG (6) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 7 write sequence + ; + sync + ssm 0,0 + STDIAG (7) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 8 write sequence + ; + sync + ssm 0,0 + STDIAG (8) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 9 write sequence + ; + sync + ssm 0,0 + STDIAG (9) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 10 write sequence + ; + sync + ssm 0,0 + STDIAG (10) + STDIAG (26) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + ssm 0,0 + nop + + ; + ; RDR 11 write sequence + ; + sync + ssm 0,0 + STDIAG (11) + STDIAG (27) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + ssm 0,0 + nop + + ; + ; RDR 12 write sequence + ; + sync + ssm 0,0 + STDIAG (12) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 13 write sequence + ; + sync + ssm 0,0 + STDIAG (13) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 14 write sequence + ; + sync + ssm 0,0 + STDIAG (14) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 15 write sequence + ; + sync + ssm 0,0 + STDIAG (15) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 16 write sequence + ; + sync + ssm 0,0 + STDIAG (16) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 17 write sequence + ; + sync + ssm 0,0 + STDIAG (17) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 18 write sequence + ; + sync + ssm 0,0 + STDIAG (18) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 19 write sequence + ; + sync + ssm 0,0 + STDIAG (19) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 20 write sequence + ; + sync + ssm 0,0 + STDIAG (20) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 21 write sequence + ; + sync + ssm 0,0 + STDIAG (21) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 22 write sequence + ; + sync + ssm 0,0 + STDIAG (22) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 23 write sequence + ; + sync + ssm 0,0 + STDIAG (23) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 24 write sequence + ; + sync + ssm 0,0 + STDIAG (24) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 25 write sequence + ; + sync + ssm 0,0 + STDIAG (25) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 26 write sequence + ; + sync + ssm 0,0 + STDIAG (10) + STDIAG (26) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + ssm 0,0 + nop + + ; + ; RDR 27 write sequence + ; + sync + ssm 0,0 + STDIAG (11) + STDIAG (27) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + ssm 0,0 + nop + + ; + ; RDR 28 write sequence + ; + sync + ssm 0,0 + STDIAG (28) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 29 write sequence + ; + sync + ssm 0,0 + STDIAG (29) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 30 write sequence + ; + sync + ssm 0,0 + STDIAG (30) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + + ; + ; RDR 31 write sequence + ; + sync + ssm 0,0 + STDIAG (31) + ssm 0,0 + b,n perf_rdr_shift_out_W_leave + nop + ssm 0,0 + nop + +perf_rdr_shift_out_W_leave: + bve (%r2) + .exit + MTDIAG_2 (23) ; restore DR2 + .procend +ENDPROC(perf_rdr_shift_out_W) + + +;*********************************************************************** +;* +;* Name: rdr_shift_in_U +;* +;* Description: +;* This routine shifts data in from the RDR in arg0 and returns +;* the result in ret0. If the RDR is <= 64 bits in length, it +;* is shifted shifted backup immediately. This is to compensate +;* for RDR10 which has bits that preclude PDC stack operations +;* when they are in the wrong state. +;* +;* Arguments: +;* arg0 : rdr to be read +;* arg1 : bit length of rdr +;* +;* Returns: +;* ret0 = next 64 bits of rdr data from staging register +;* +;* Register usage: +;* arg0 : rdr to be read +;* arg1 : bit length of rdr +;* %r24 - original DR2 value +;* %r23 - DR2 | DR2_SLOW_RET +;* %r1 - scratch +;* +;*********************************************************************** + +ENTRY(perf_rdr_shift_in_U) + .proc + .callinfo frame=0,NO_CALLS + .entry + +; read(shift in) the RDR. +; +; NOTE: The PCX-U ERS states that DR2_SLOW_RET must be set before any +; shifting is done, from or to, remote diagnose registers. + + depdi,z 1,DR2_SLOW_RET,1,%r29 + MFDIAG_2 (24) + or %r24,%r29,%r29 + MTDIAG_2 (29) ; set DR2_SLOW_RET + + nop + nop + nop + nop + +; +; Start of next 32-byte cacheline +; + nop + nop + nop + extrd,u arg1,63,6,%r1 + + mtsar %r1 + shladd arg0,2,%r0,%r1 ; %r1 = 4 * RDR number + blr %r1,%r0 ; branch to 8-instruction sequence + nop + +; +; Start of next 32-byte cacheline +; + SFDIAG (0) ; RDR 0 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (0) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (1) ; RDR 1 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (1) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + sync ; RDR 2 read sequence + ssm 0,0 + SFDIAG (4) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 3 read sequence + ssm 0,0 + SFDIAG (3) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 4 read sequence + ssm 0,0 + SFDIAG (4) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 5 read sequence + ssm 0,0 + SFDIAG (5) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 6 read sequence + ssm 0,0 + SFDIAG (6) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 7 read sequence + ssm 0,0 + SFDIAG (7) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + b,n perf_rdr_shift_in_U_leave + nop + nop + nop + nop + nop + nop + nop + + SFDIAG (9) ; RDR 9 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (9) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (10) ; RDR 10 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (10) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (11) ; RDR 11 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (11) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (12) ; RDR 12 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (12) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (13) ; RDR 13 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (13) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (14) ; RDR 14 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (14) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (15) ; RDR 15 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (15) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + sync ; RDR 16 read sequence + ssm 0,0 + SFDIAG (16) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + SFDIAG (17) ; RDR 17 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (17) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (18) ; RDR 18 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (18) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + b,n perf_rdr_shift_in_U_leave + nop + nop + nop + nop + nop + nop + nop + + sync ; RDR 20 read sequence + ssm 0,0 + SFDIAG (20) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 21 read sequence + ssm 0,0 + SFDIAG (21) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 22 read sequence + ssm 0,0 + SFDIAG (22) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 23 read sequence + ssm 0,0 + SFDIAG (23) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 24 read sequence + ssm 0,0 + SFDIAG (24) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + sync ; RDR 25 read sequence + ssm 0,0 + SFDIAG (25) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + SFDIAG (26) ; RDR 26 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (26) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (27) ; RDR 27 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (27) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + sync ; RDR 28 read sequence + ssm 0,0 + SFDIAG (28) + ssm 0,0 + MFDIAG_1 (28) + b,n perf_rdr_shift_in_U_leave + ssm 0,0 + nop + + b,n perf_rdr_shift_in_U_leave + nop + nop + nop + nop + nop + nop + nop + + SFDIAG (30) ; RDR 30 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (30) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + + SFDIAG (31) ; RDR 31 read sequence + ssm 0,0 + MFDIAG_1 (28) + shrpd ret0,%r0,%sar,%r1 + MTDIAG_1 (1) + STDIAG (31) + ssm 0,0 + b,n perf_rdr_shift_in_U_leave + nop + +perf_rdr_shift_in_U_leave: + bve (%r2) + .exit + MTDIAG_2 (24) ; restore DR2 + .procend +ENDPROC(perf_rdr_shift_in_U) + +;*********************************************************************** +;* +;* Name: rdr_shift_out_U +;* +;* Description: +;* This routine moves data to the RDR's. The double-word that +;* arg1 points to is loaded and moved into the staging register. +;* Then the STDIAG instruction for the RDR # in arg0 is called +;* to move the data to the RDR. +;* +;* Arguments: +;* arg0 = rdr target +;* arg1 = buffer pointer +;* +;* Returns: +;* None +;* +;* Register usage: +;* arg0 = rdr target +;* arg1 = buffer pointer +;* %r24 - DR2 | DR2_SLOW_RET +;* %r23 - original DR2 value +;* +;*********************************************************************** + +ENTRY(perf_rdr_shift_out_U) + .proc + .callinfo frame=0,NO_CALLS + .entry + +; +; NOTE: The PCX-U ERS states that DR2_SLOW_RET must be set before any +; shifting is done, from or to, the remote diagnose registers. +; + + depdi,z 1,DR2_SLOW_RET,1,%r24 + MFDIAG_2 (23) + or %r24,%r23,%r24 + MTDIAG_2 (24) ; set DR2_SLOW_RET + + MTDIAG_1 (25) ; data to the staging register + shladd arg0,2,%r0,%r1 ; %r1 = 4 * RDR number + blr %r1,%r0 ; branch to 8-instruction sequence + nop + +; +; 32-byte cachline aligned +; + + sync ; RDR 0 write sequence + ssm 0,0 + STDIAG (0) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 1 write sequence + ssm 0,0 + STDIAG (1) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 2 write sequence + ssm 0,0 + STDIAG (2) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 3 write sequence + ssm 0,0 + STDIAG (3) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 4 write sequence + ssm 0,0 + STDIAG (4) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 5 write sequence + ssm 0,0 + STDIAG (5) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 6 write sequence + ssm 0,0 + STDIAG (6) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 7 write sequence + ssm 0,0 + STDIAG (7) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 8 write sequence + ssm 0,0 + STDIAG (8) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 9 write sequence + ssm 0,0 + STDIAG (9) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 10 write sequence + ssm 0,0 + STDIAG (10) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 11 write sequence + ssm 0,0 + STDIAG (11) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 12 write sequence + ssm 0,0 + STDIAG (12) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 13 write sequence + ssm 0,0 + STDIAG (13) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 14 write sequence + ssm 0,0 + STDIAG (14) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 15 write sequence + ssm 0,0 + STDIAG (15) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 16 write sequence + ssm 0,0 + STDIAG (16) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 17 write sequence + ssm 0,0 + STDIAG (17) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 18 write sequence + ssm 0,0 + STDIAG (18) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 19 write sequence + ssm 0,0 + STDIAG (19) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 20 write sequence + ssm 0,0 + STDIAG (20) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 21 write sequence + ssm 0,0 + STDIAG (21) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 22 write sequence + ssm 0,0 + STDIAG (22) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 23 write sequence + ssm 0,0 + STDIAG (23) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 24 write sequence + ssm 0,0 + STDIAG (24) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 25 write sequence + ssm 0,0 + STDIAG (25) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 26 write sequence + ssm 0,0 + STDIAG (26) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 27 write sequence + ssm 0,0 + STDIAG (27) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 28 write sequence + ssm 0,0 + STDIAG (28) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 29 write sequence + ssm 0,0 + STDIAG (29) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 30 write sequence + ssm 0,0 + STDIAG (30) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + + sync ; RDR 31 write sequence + ssm 0,0 + STDIAG (31) + ssm 0,0 + b,n perf_rdr_shift_out_U_leave + nop + ssm 0,0 + nop + +perf_rdr_shift_out_U_leave: + bve (%r2) + .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 new file mode 100644 index 000000000..7afa2fd55 --- /dev/null +++ b/arch/parisc/kernel/perf_images.h @@ -0,0 +1,3125 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Imagine for use with the Onyx (PCX-U) CPU interface + * + * Copyright (C) 2001 Randolph Chung <tausq at parisc-linux.org> + * Copyright (C) 2001 Hewlett-Packard (Grant Grundler) + */ +#ifndef PERF_IMAGES_H +#define PERF_IMAGES_H + +/* Magic numbers taken without modification from HPUX stuff */ + +#define PCXU_IMAGE_SIZE 584 + +static uint32_t onyx_images[][PCXU_IMAGE_SIZE/sizeof(uint32_t)] __ro_after_init = { +/* + * CPI: + * + * Counts the following: + * + * ctr0 : total cycles + * ctr1 : total cycles where nothing retired + * ctr2 : total instructions retired, including nullified + * ctr3 : total instructions retired, less nullified instructions + */ + { + 0x4c00c000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0x004e0004, 0x07ffffff, 0xffc01380, + 0x0101ffff, 0xfffff104, 0xe000c07f, 0xfffffffc, + 0x01380010, 0x1fffffff, 0xff000000, 0x00000000, + 0x00000fff, 0xff00000f, 0xffff0000, 0x0fffff00, + 0x000fffff, 0x00000000, 0x00000000, 0x00ffffff, + 0xfffff000, 0x0000000f, 0xffffffff, 0xff000000, + 0x0000ffff, 0xfffffff0, 0x00000000, 0x0fffffff, + 0xffff0000, 0x00000000, 0x6fffffff, 0xffffffff, + 0xfff55fff, 0xffffffff, 0xffffffff, 0xf0000000, + 0xf0000030, 0x00003c00, 0x067f080c, 0x02019fc0, + 0x02804067, 0xf0009030, 0x19fc002c, 0x40067f08, + 0x0c12019f, 0xc0028440, 0x67f00091, 0x3019fc00, + 0x2fc007ff, 0xf800f001, 0xfffe003c, 0x007fff80, + 0x0f001fff, 0xe003c007, 0xfff800f0, 0x01fffe00, + 0x3c007fff, 0x800f001f, 0xffe003c0, 0x07fff800, + 0xf001fffe, 0x003c007f, 0xff800f00, 0x1fffe003, + 0xc007fff8, 0x00f001ff, 0xfe003c00, 0x7fff800f, + 0x001fffe0, 0x03c007ff, 0xf800f001, 0xfffe003c, + 0x007fff80, 0x0f001fff, 0xe003c007, 0xfff800f0, + 0x01fffe00, 0x3c007fff, 0x800f001f, 0xffe00000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x6fff0000, 0x00000000, 0x60000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff}, + +/* Bus utilization image (bus_util) + * + * ctr0 : counts address valid cycles + * ctr1 : counts data valid cycles + * ctr2 : counts overflow from counter 0 + * ctr3 : counts overflow from counter 1 + */ + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xefefefef, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf0000000, + 0x0000000c, 0x00003c00, 0x07930000, 0x0041e4c0, + 0x01002079, 0x3000800c, 0x1e4c0030, 0x00279300, + 0x010049e4, 0xc0014022, 0x79300090, 0x0c9e4c00, + 0x34004793, 0x00020051, 0xe4c00180, 0x24793000, + 0xa00d1e4c, 0x00380067, 0x93000300, 0x59e4c001, + 0xc0267930, 0x00b00d9e, 0x4c003fff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffc00, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffff0000, 0x00000000, 0xf0000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00100000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff }, + +/* + * TLB counts (same as tlbStats image): + * + * Counts the following: + * + * ctr0: DTLB misses + * ctr1: ITLB misses + * ctr2: total cycles in the miss handlers + * ctr3: total cycles + */ + + { + 0x0c00c000, 0x00000000, 0x00060000, 0x00000000, + 0xe7e7e0e0, 0x004e0004, 0x07ffffff, 0xffc01380, + 0x0101ffff, 0xfffff104, 0xe000c06a, 0xafffc85c, + 0x01380010, 0x1fffffff, 0xff000000, 0x00000000, + 0x01b9e000, 0x0001b8c0, 0x00000000, 0x0fffff00, + 0x000fffff, 0x00000000, 0x00000000, 0x00400000, + 0x00001000, 0x00000004, 0x00000000, 0x01000000, + 0x0000ffff, 0xfffffff0, 0x00000000, 0x0fffffff, + 0xffff0000, 0x00000000, 0x6fffffff, 0xffffffff, + 0xfff55ff5, 0xffffffff, 0xffffffff, 0xf0000000, + 0xf0000000, 0x00003c00, 0x01ff0001, 0x08007fc2, + 0x02c1001f, 0xf0807100, 0x1bfc200c, 0x4806ff00, + 0x03f001ff, 0xfe003c00, 0x7fff800f, 0x001fffe0, + 0x03c007ff, 0xf800f001, 0xfffe003c, 0x007fff80, + 0x0f001fff, 0xe003c007, 0xfff800f0, 0x01fffe00, + 0x3c007fff, 0x800f001f, 0xffe003c0, 0x07fff800, + 0xf001fffe, 0x003c007f, 0xff800f00, 0x1fffe003, + 0xc007fff8, 0x00f001ff, 0xfe003c00, 0x7fff800f, + 0x001fffe0, 0x03c007ff, 0xf800f001, 0xfffe003c, + 0x007fff80, 0x0f001fff, 0xe003c007, 0xfff800f0, + 0x01fffe00, 0x3c007fff, 0x800f001f, 0xffe00000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x6fff0000, 0x00000000, 0x60000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff }, + +/* tlbHandMiss + * + * ctr0: counts TLB misses + * ctr1: counts dmisses inside tlb miss handlers + * ctr2: counts cycles in the tlb miss handlers + * ctr3: counts overflows of ctr2 + */ +{ +0x1c00c000,00000000,0x00060000,00000000, +0xe7e7e0e0,0x004e0004,0x07ffffff,0xffc01380, +0x0101ffff,0xfffff104,0xe000c06a,0xafffc85c, +0x01380010,0x1fffffff,0xff000000,00000000, +0x01b9e000,0x0001b8c0,00000000,0x0fffff00, +0x000fffff,00000000,00000000,0x00400000, +0x00001000,0x00000004,00000000,0x01000000, +0x0000ffff,0xfffffff0,00000000,0x0fffffff, +0xffff0000,00000000,0x6fffffff,0xffffffff, +0xfff55ff5,0xffffffff,0xffffffff,0xf0000000, +0xf0000000,0x00003c00,0x01fd0000,0x08007f42, +0x0281001f,0xd080a100,0x19f42008,0x44067d08, +0x0612019f,0x400084c0,0x67d00060,0x0047f400, +0x042011fd,0x080b0404,0x7f4202c4,0x0167d080, +0x311059f4,0x201c4816,0x7d000313,0x059f4001, +0xfc007fff,0x800f001f,0xffe003c0,0x07fff800, +0xf001fffe,0x003c007f,0xff800f00,0x1fffe003, +0xc007fff8,0x00f001ff,0xfe003c00,0x7fff800f, +0x001fffe0,0x03c007ff,0xf800f001,0xfffe003c, +0x007fff80,0x0f001fff,0xe003c007,0xfff800f0, +0x01fffe00,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0x6fff0000,00000000,0x60000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff}, + +/* branch_taken image (ptkn image) + * + * ctr0: overflow for ctr1 + * ctr1: predicted taken branches, actually taken + * ctr2: all predicted taken branches (nullfied or not) + * ctr3: overflow for ctr2 + */ + + { + 0xcc01e000, 0x00000000, 0x00060000, 0x00000000, + 0xa08080a0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf0000000, + 0xf0000000, 0x00003c00, 0x04f90000, 0x02013e40, + 0x0081004f, 0x90004060, 0x13e40018, 0x0024f900, + 0x0802093e, 0x40028102, 0x4f9000c0, 0x6093e400, + 0x380014f9, 0x00010205, 0x3e4000c1, 0x014f9000, + 0x506053e4, 0x001c0034, 0xf9000902, 0x0d3e4002, + 0xc1034f90, 0x00d060d3, 0xe4003fff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffc00, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffff0000, 0x00000000, 0xf0000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff }, + +/* branch_nottaken (pntkn image) + * + * ctr0: overflow for ctr1 + * ctr1: counts branches predicted not-taken, but actually taken + * ctr2: counts all predictable branches predicted not-taken + * ctr3: overflow for ctr2 + */ +{ +0xcc01e000,00000000,0x00060000,00000000, +0xc0c0c0e0,0xffb1fffb,0xfff7ffff,0xffffffff, +0xffffffff,0xfffffffb,0x1fffbfff,0x7fffffff, +0xfcc7ffff,0xffdffffa,0x5f000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff}, + + +/* imiss image + * + * ctr0 : counts imiss aligned on 0 + * ctr1 : counts imiss aligned on 4 + * ctr2 : counts imiss aligned on 8 + * ctr3 : counts imiss aligned on C + */ + { + 0x0c00c000, 0x00000000, 0x00010000, 0x00000000, + 0xe7ebedee, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x6fffffff, 0xffffffff, + 0xfff55fff, 0xffffffff, 0xffffffff, 0xf0000000, + 0xf0000000, 0x00003c00, 0x007f0000, 0x01001fc0, + 0x00408007, 0xf0002030, 0x01fc000c, 0x10007f00, + 0x0405001f, 0xc0014180, 0x07f00060, 0x7001fc00, + 0x1c20007f, 0x00080900, 0x1fc00242, 0x8007f000, + 0xa0b001fc, 0x002c3000, 0x7f000c0d, 0x001fc003, + 0x438007f0, 0x00e0f001, 0xfc003fff, 0xfffff800, + 0xfffffffe, 0x003fffff, 0xff800fff, 0xffffe003, + 0xfffffff8, 0x00ffffff, 0xfe003fff, 0xffff800f, + 0xffffffe0, 0x03ffffff, 0xf800ffff, 0xfffe003f, + 0xffffff80, 0x0fffffff, 0xe003ffff, 0xfff800ff, + 0xfffffe00, 0x3fffffff, 0x800fffff, 0xffe00000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x6fff0000, 0x00000000, 0x60000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff}, + +/* dmiss image + * + * ctr0 : counts cycles + * ctr1 : counts cycles where something retired + * ctr2 : counts dmisses + * ctr3 : (same as ctr2) + */ + { + 0x3c00c000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x6fffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf0000000, + 0xf0000000, 0x00003c04, 0x007f0009, 0x02001fc0, + 0x0280c007, 0xf000b040, 0x01fc0030, 0x14007f00, + 0x0d06001f, 0xc00381c0, 0x07f000f0, 0x8001fc00, + 0x2024007f, 0x00090a00, 0x1fc00282, 0xc007f000, + 0xb0c001fc, 0x00303400, 0x7f000d0e, 0x001fc003, + 0x83c007f0, 0x00f00001, 0xfc0023ff, 0xfffff800, + 0xfffffffe, 0x003fffff, 0xff800fff, 0xffffe003, + 0xfffffff8, 0x00ffffff, 0xfe003fff, 0xffff800f, + 0xffffffe0, 0x03ffffff, 0xf800ffff, 0xfffe003f, + 0xffffff80, 0x0fffffff, 0xe003ffff, 0xfff800ff, + 0xfffffe00, 0x3fffffff, 0x800fffff, 0xffe00000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x6fff0000, 0x00000000, 0x60000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff }, + +/* dcmiss + * + * ctr0: counts store instructions retired + * ctr1: counts load instructions retired + * ctr2: counts dmisses + * ctr3: counts READ_SHARED_OR_PRIV and READ_PRIVATE transactions on Runway + */ +{ +0x2c90c000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x6fffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf00000e8,0x00003c02,0x00bf0001,0x02002fc0, +0x0080a00b,0xf0003040,0x02fc0010,0x1200bf00, +0x0506002f,0xc00181a0,0x0bf00070,0x8002fc00, +0x202200bf,0x00090a00,0x2fc00282,0xa00bf000, +0xb0c002fc,0x00303200,0xbf000d0e,0x002fc003, +0x83a00bf0,0x00ffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0x6fff0000,00000000,0x60000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0x55555555,0xd5555555, +0x55555555,0x75555555,0x5e1ffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00100000,00000000,0xf8000000,00000000, +00000000,00000000,0xf4000000,00000000, +0xffffffff,0xffffffff,0x00ffffff,0xffffffff, +00000000,00000000,0x00ffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* big_cpi + * + * ctr0: counts total cycles + * ctr1: counts overflows of ctr0 (for greater than 32-bit values) + * ctr2: counts overflows of ctr3 (for greater than 32-bit values) + * ctr3: counts unnullified instructions retired + */ +{ +0x0c00c000,00000000,0x00060000,00000000, +0xe7e7e0e0,0x004e0004,0x07ffffff,0xffc01380, +0x0101ffff,0xfffff104,0xe000c06a,0xafffc85c, +0x01380010,0x1fffffff,0xff000000,00000000, +0x01b9e000,0x0001b8c0,00000000,0x0fffff00, +0x000fffff,00000000,00000000,0x00400000, +0x00001000,0x00000004,00000000,0x01000000, +0x0000ffff,0xfffffff0,00000000,0x0fffffff, +0xffff0000,00000000,0x6fffffff,0xffffffff, +0xfff55ff5,0xffffffff,0xffffffff,0xf0000000, +0xf0000010,0x00003c00,0x01760008,0x00025d80, +0x02800417,0x6000c001,0x25d80038,0x04017600, +0x0901025d,0x8002c044,0x176000d0,0x1125d800, +0x3c2001f6,0x08080400,0x7d820203,0x001f6080, +0x804027d8,0x20282009,0xf6080a0c,0x027d8202, +0x81041f60,0x80c08107,0xd8203030,0x41f6080c, +0x04127d82,0x0382049f,0x6080e0c1,0x27d82038, +0x4006f608,0x081011bd,0x82030400,0xef6080a1, +0x013bd820,0x384806f6,0x00081211,0xbd800304, +0x80ef6000,0xa1213bd8,0x003bc007,0xfff800f0, +0x01fffe00,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0x6fff0000,00000000,0x60000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* big_ls + * + * ctr0:counts the total number of cycles for which local_stall_A1 is asserted. + * ctr1: is the overflow for counter 0. + * ctr2: counts IFLUSH_AV + * ctr3: is the overflow for counter 2. + */ +{ +0x0c000000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x0fffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +00000000,0x00029408,0x02f50002,0x0800bd40, +0x0202802f,0x5000a000,0x4bd40004,0x0812f500, +0x030804bd,0x40024281,0x2f5000b0,0x010bd400, +0x100842f5,0x00060810,0xbd400302,0x842f5000, +0xe0014bd4,0x00140852,0xf5000708,0x14bd4003, +0x42852f50,0x00ff001f,0xffe003c0,0x07fff800, +0xf001fffe,0x003c007f,0xff800f00,0x1fffe003, +0xc007fff8,0x00f001ff,0xfe003c00,0x7fff800f, +0x001fffe0,0x03c007ff,0xf800f001,0xfffe003c, +0x007fff80,0x0f001fff,0xe003c007,0xfff800f0, +0x01fffe00,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0x0df70000,00000000,00000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* br_abort + * + * ctr0: counts BRAD_STALLH + * ctr1: counts ONE_QUAD + * ctr2: counts BR0_ABRT + * ctr3: counts BR1_ABRT + */ +{ +0x0c002000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x1fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000e,0x01001fc0, +0x03c08007,0xf000c030,0x01fc0034,0x10007f00, +0x0a05001f,0xc002c180,0x07f00080,0x7001fc00, +0x2420007f,0x00060900,0x1fc001c2,0x8007f000, +0x40b001fc,0x00143000,0x7f00020d,0x001fc000, +0xc38007f0,0x0000f001,0xfc0007ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x1a250000,00000000,0x10000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff}, + +/* isnt + * + * ctr0: counts the total number of cycles for which iside_notrans is asserted + * ctr1: counts the number of times iside_notrans is asserted for 1-4 cycles + * ctr2: counts the number of times iside_notrans is asserted for 5-7 cycles + * ctr3: counts the number of times iside_notrans is asserted for > 7 cycles + */ +{ +0x0c018000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xcfffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +00000000,0x00021c20,0x03ff0808,0x1800ffc4, +0x0204003f,0xf0004280,0x0ffc6020,0x8003ff00, +0x043800ff,0xc8020c00,0x3ff00044,0x800ffca0, +0x210003ff,0x00045800,0xffcc0214,0x003ff000, +0x26800ffc,0xe0218003,0xff000278,0x00ffd002, +0x1c003ff0,0x0028800f,0xfd002200,0x03ff0001, +0xf001fffe,0x003c007f,0xff800f00,0x1fffe003, +0xc007fff8,0x00f001ff,0xfe003c00,0x7fff800f, +0x001fffe0,0x03c007ff,0xf800f001,0xfffe003c, +0x007fff80,0x0f001fff,0xe003c007,0xfff800f0, +0x01fffe00,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0xcdff0000,00000000,0xc0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff}, + +/* quadrant + * + * ctr0: Total number of instructions in quadrant 0 + * ctr1: Total number of instructions in quadrant 1 + * ctr2: Total number of instructions in quadrant 2 + * ctr3: Total number of instructions in quadrant 3 + * Works only with 32-bit + */ + + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0x004e0004, 0x07ffffff, 0xffc01380, + 0x0101ffff, 0xfffff004, 0xe000407f, 0xfffffffc, + 0x01380010, 0x1fffffff, 0xff000000, 0x00000000, + 0x00000fff, 0xff00000f, 0xffff0000, 0x0fffff00, + 0x000fffff, 0x00000000, 0x00000000, 0x00ffffff, + 0xffcff000, 0x0000040f, 0xfffffffc, 0xff000000, + 0x0080ffff, 0xffffcff0, 0x0000000c, 0x0fffffff, + 0xfcff0000, 0x00000000, 0xffffffff, 0xffffffff, + 0xfff55ff5, 0x5fffffff, 0xffffffff, 0xf0000000, + 0xf00000f0, 0x00003c00, 0x007f0000, 0x01001fc0, + 0x00408007, 0xf0002030, 0x01fc000c, 0x10007f00, + 0x0405001f, 0xc0014180, 0x07f00060, 0x7001fc00, + 0x1c20007f, 0x00080900, 0x1fc00242, 0x8007f000, + 0xa0b001fc, 0x002c3000, 0x7f000c0d, 0x001fc003, + 0x438007f0, 0x00e0f001, 0xfc003fff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffc00, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffff0000, 0x00000000, 0xf0000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff}, + +/* rw_pdfet (READ_PRIV transactions) + * + * ctr0: counts address valid cycles + * ctr1: counts *all* data valid cycles + * ctr2: is the overflow from counter 0 + * ctr3: is the overflow from counter 1 + */ +{ +0x0c01e000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0x0000000c,0x00003c00,0x07930000,0x0041e4c0, +0x01002079,0x3000800c,0x1e4c0030,0x00279300, +0x010049e4,0xc0014022,0x79300090,0x0c9e4c00, +0x34004793,0x00020051,0xe4c00180,0x24793000, +0xa00d1e4c,0x00380067,0x93000300,0x59e4c001, +0xc0267930,0x00b00d9e,0x4c003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00100000,00000000,0xf8000000,00000000, +00000000,00000000,00000000,00000000, +0xffffffff,0xffffffff,0x00ffffff,0xffffffff, +00000000,00000000,00000000,00000000, +0xffffffff,0xffffffff}, + +/* rw_wdfet (WRITEBACKS) + * + * ctr0: counts address valid cycles + * ctr1: counts *all* data valid cycles + * ctr2: is the overflow from counter 0 + * ctr3: is the overflow from counter 1 + */ +{ +0x0c01e000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0x0000000c,0x00003c00,0x07930000,0x0041e4c0, +0x01002079,0x3000800c,0x1e4c0030,0x00279300, +0x010049e4,0xc0014022,0x79300090,0x0c9e4c00, +0x34004793,0x00020051,0xe4c00180,0x24793000, +0xa00d1e4c,0x00380067,0x93000300,0x59e4c001, +0xc0267930,0x00b00d9e,0x4c003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00100000,00000000,0x98000000,00000000, +00000000,00000000,00000000,00000000, +0xffffffff,0xffffffff,0x00ffffff,0xffffffff, +00000000,00000000,00000000,00000000, +0xffffffff,0xffffffff}, + +/* shlib_cpi + * + * ctr0: Total number of instructions in quad 0 + * ctr1: Total number of CPU clock cycles in quad 0 + * ctr2: total instructions without nullified + * ctr3: total number of CPU clock cycles + */ + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0x004e0004, 0x07ffffff, 0xffc01380, + 0x0101ffff, 0xfffff004, 0xe000407f, 0xfffffffc, + 0x01380010, 0x1fffffff, 0xff000000, 0x00000000, + 0x00000fff, 0xff00000f, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0x00000000, 0x00ffffff, + 0xffcff000, 0x0000000f, 0xfffffffc, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0xffffffff, 0xffffffff, + 0xfff77ff5, 0x7fffffff, 0xffffffff, 0xf0000000, + 0xf00000a0, 0x00003c00, 0x01ff0005, 0x08007fc0, + 0x03c1001f, 0xf08030c0, 0x07fc203c, 0x4001ff08, + 0x0118007f, 0xc003c500, 0x1ff08031, 0xc007fc00, + 0x3fffffff, 0xf800ffff, 0xfffe003f, 0xffffff80, + 0x0fffffff, 0xe003ffff, 0xfff800ff, 0xfffffe00, + 0x3fffffff, 0x800fffff, 0xffe003ff, 0xfffff800, + 0xfffffffe, 0x003fffff, 0xff800fff, 0xffffe003, + 0xfffffff8, 0x00ffffff, 0xfe003fff, 0xffff800f, + 0xffffffe0, 0x03ffffff, 0xf800ffff, 0xfffe003f, + 0xffffff80, 0x0fffffff, 0xe003ffff, 0xfff800ff, + 0xfffffe00, 0x3fffffff, 0x800fffff, 0xffe00000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffff0000, 0x00000000, 0xf0000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff}, + + +/* addr_inv_abort_alu + * + * ctr0: counts ABORT_ALU0L + * ctr1: counts ABORT_ALU1L + * ctr2: counts ADDR0_INVALID + * ctr3: counts ADDR1_INVALID + */ + +{ +0x0c00c000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x6fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000d,0x01001fc0, +0x03008007,0xf000f030,0x01fc0038,0x10007f00, +0x0905001f,0xc0020180,0x07f000b0,0x7001fc00, +0x2820007f,0x00050900,0x1fc00102,0x8007f000, +0x70b001fc,0x00183000,0x7f00010d,0x001fc000, +0x038007f0,0x0030f001,0xfc000bff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x65380000,00000000,0x60000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + + + +/* brad_stall + * + * ctr0: counts the total number of cycles for which brad_stall is asserted + * ctr1: counts the number of times brad_stall is asserted for 1-4 cycles + * ctr2: counts the number of times brad_stall is asserted for 5-7 cycles + * ctr3: counts the number of times brad_stall is asserted for > 7 cycles + */ +{ +0x0c002000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x1fffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +00000000,0x00021c20,0x03ff0808,0x1800ffc4, +0x0204003f,0xf0004280,0x0ffc6020,0x8003ff00, +0x043800ff,0xc8020c00,0x3ff00044,0x800ffca0, +0x210003ff,0x00045800,0xffcc0214,0x003ff000, +0x26800ffc,0xe0218003,0xff000278,0x00ffd002, +0x1c003ff0,0x0028800f,0xfd002200,0x03ff0001, +0xf001fffe,0x003c007f,0xff800f00,0x1fffe003, +0xc007fff8,0x00f001ff,0xfe003c00,0x7fff800f, +0x001fffe0,0x03c007ff,0xf800f001,0xfffe003c, +0x007fff80,0x0f001fff,0xe003c007,0xfff800f0, +0x01fffe00,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0x1bff0000,00000000,0x10000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff}, + +/* cntl_in_pipel + * + * ctr0: counts the total number of cycles for which cntl_in_pipel is asserted + * ctr1: counts the number of times cntl_in_pipel is asserted for 1-4 cycles + * ctr2: counts the number of times cntl_in_pipel is asserted for 5-7 cycles + * ctr3: counts the number of times cntl_in_pipel is asserted for > 7 cycles + */ +{ +0x0c006000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x3fffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +00000000,0x00021c00,0x03ff0808,0x1000ffc4, +0x0206003f,0xf0004200,0x0ffc6020,0xa003ff00, +0x043000ff,0xc8020e00,0x3ff00044,0x000ffca0, +0x212003ff,0x00045000,0xffcc0216,0x003ff000, +0x26000ffc,0xe021a003,0xff000270,0x00ffd002, +0x1e003ff0,0x0028000f,0xfd002220,0x03ff0001, +0xf001fffe,0x003c007f,0xff800f00,0x1fffe003, +0xc007fff8,0x00f001ff,0xfe003c00,0x7fff800f, +0x001fffe0,0x03c007ff,0xf800f001,0xfffe003c, +0x007fff80,0x0f001fff,0xe003c007,0xfff800f0, +0x01fffe00,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0x3fff0000,00000000,0x30000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + + +/* dsnt_xfh + * + * ctr0: counts dside_notrans + * ctr1: counts xfhang + * ctr2: is the overflow for ctr0 + * ctr3: is the overflow for ctr1 + */ +{ +0x0c018000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xcfffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +00000000,0x00030000,0x01f30000,0x00087cc0, +0x0040041f,0x30002001,0x87cc000c,0x1001f300, +0x0404087c,0xc0014104,0x1f300060,0x4187cc00, +0x1c2001f3,0x00080808,0x7cc00242,0x041f3000, +0xa08187cc,0x002c3001,0xf3000c0c,0x087cc003, +0x43041f30,0x00e0c187,0xcc003fc0,0x07fff800, +0xf001fffe,0x003c007f,0xff800f00,0x1fffe003, +0xc007fff8,0x00f001ff,0xfe003c00,0x7fff800f, +0x001fffe0,0x03c007ff,0xf800f001,0xfffe003c, +0x007fff80,0x0f001fff,0xe003c007,0xfff800f0, +0x01fffe00,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0xcb3f0000,00000000,0xc0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* fet_sig1 + * + * ctr0: counts ICORE_AV + * ctr1: counts ITRANS_STALL + * ctr2: counts SEL_PCQH + * ctr3: counts OUT_OF_CONTEXT + */ +{ +0x0c000000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x0fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000e,0x01001fc0, +0x03c08007,0xf000c030,0x01fc0034,0x10007f00, +0x0a05001f,0xc002c180,0x07f00080,0x7001fc00, +0x2420007f,0x00060900,0x1fc001c2,0x8007f000, +0x40b001fc,0x00143000,0x7f00020d,0x001fc000, +0xc38007f0,0x0000f001,0xfc0007ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x07c10000,00000000,00000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff}, + +/* fet_sig2 + * + * ctr0: counts ICORE_AV + * ctr1: counts IRTN_AV + * ctr2: counts ADDRESS_INC + * ctr3: counts ADDRESS_DEC + */ +{ +0x0c000000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x0fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000e,0x01001fc0, +0x03c08007,0xf000c030,0x01fc0034,0x10007f00, +0x0a05001f,0xc002c180,0x07f00080,0x7001fc00, +0x2420007f,0x00060900,0x1fc001c2,0x8007f000, +0x40b001fc,0x00143000,0x7f00020d,0x001fc000, +0xc38007f0,0x0000f001,0xfc0007ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x06930000,00000000,00000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* g7_1 + * + * ctr0: counts HIT_RETRY0 + * ctr1: counts HIT_RETRY1 + * ctr2: counts GO_TAG_E + * ctr3: counts GO_TAG_O + */ +{ +0x0c00e000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x7fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000e,0x01001fc0, +0x03c08007,0xf000c030,0x01fc0034,0x10007f00, +0x0a05001f,0xc002c180,0x07f00080,0x7001fc00, +0x2420007f,0x00060900,0x1fc001c2,0x8007f000, +0x40b001fc,0x00143000,0x7f00020d,0x001fc000, +0xc38007f0,0x0000f001,0xfc0007ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x71c10000,00000000,0x70000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* g7_2 + * + * ctr0: counts HIT_DM0 + * ctr1: counts HIT_DM1 + * ctr2: counts GO_STORE_E + * ctr3: counts GO_STORE_O + */ +{ +0x0c00e000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x7fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000e,0x01001fc0, +0x03c08007,0xf000c030,0x01fc0034,0x10007f00, +0x0a05001f,0xc002c180,0x07f00080,0x7001fc00, +0x2420007f,0x00060900,0x1fc001c2,0x8007f000, +0x40b001fc,0x00143000,0x7f00020d,0x001fc000, +0xc38007f0,0x0000f001,0xfc0007ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x72930000,00000000,0x70000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* g7_3 + * + * ctr0: counts HIT_DV0 + * ctr1: counts HIT_DV1 + * ctr2: counts STBYPT_E (load bypasses from store queue) + * ctr3: counts STBYPT_O + */ +{ +0x0c00e000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x7fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f0002,0x01001fc0, +0x00c08007,0xf0000030,0x01fc0004,0x10007f00, +0x0605001f,0xc001c180,0x07f00040,0x7001fc00, +0x1420007f,0x000a0900,0x1fc002c2,0x8007f000, +0x80b001fc,0x00243000,0x7f000e0d,0x001fc003, +0xc38007f0,0x00c0f001,0xfc0037ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x77250000,00000000,0x70000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* g7_4 + * + * ctr0: counts HIT_DIRTY0 + * ctr1: counts HIT_DIRTY1 + * ctr2: counts CA_BYP_E (quick launch) + * ctr3: counts CA_BYP_O + */ +{ +0x0c00e000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x7fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000e,0x01001fc0, +0x03c08007,0xf000c030,0x01fc0034,0x10007f00, +0x0a05001f,0xc002c180,0x07f00080,0x7001fc00, +0x2420007f,0x00060900,0x1fc001c2,0x8007f000, +0x40b001fc,0x00143000,0x7f00020d,0x001fc000, +0xc38007f0,0x0000f001,0xfc0007ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x7bb70000,00000000,0x70000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + + +/* mpb_labort + * + * ctr0: counts L_ABORT_ALU0L + * ctr1: counts L_ABORT_ALU1L + * ctr2: counts MPB0H + * ctr3: counts MPB1H + */ +{ +0x0c00c000,00000000,0x00060000,00000000, +0xe0e0e0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffa5ffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x6fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +00000000,0x0003f800,0x007f000e,0x01001fc0, +0x03c08007,0xf000c030,0x01fc0034,0x10007f00, +0x0a05001f,0xc002c180,0x07f00080,0x7001fc00, +0x2420007f,0x00060900,0x1fc001c2,0x8007f000, +0x40b001fc,0x00143000,0x7f00020d,0x001fc000, +0xc38007f0,0x0000f001,0xfc0007ff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0x605c0000,00000000,0x60000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffaaaa,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* panic + * + * ctr0: is the overflow for counter 1 + * ctr1: counts traps and RFI's + * ctr2: counts panic traps + * ctr3: is the overflow for counter 2 + */ +{ +0x0c002000,00000000,0x00060000,00000000, +0xe7efe0e0,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffffc, +0x41380030,0x1aabfff2,0x17000000,00000000, +0x01b80000,0x3effffff,0xffffffff,0xffffffff, +0xffffffff,00000000,00000000,0x00400000, +0x00001fff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x1fffffff,0xffffffff, +0xfff7fff7,0xffffffff,0xffffffff,0xf0000000, +0xb0000000,0x00012c04,0x05790804,0x14013e44, +0x0008004f,0x90000040,0x15e46000,0xc0047920, +0x004a003e,0x40011080,0x0f900024,0x4003e460, +0x00c80479,0x00023301,0x1e400100,0x4157d080, +0x514053f4,0x40048014,0xfd000104,0x055f4600, +0x4c0147d2,0x0014a043,0xf4001508,0x10fd0003, +0x44043f46,0x004c8147,0xd0003330,0x51f40014, +0x04257908,0x0c14093e,0x44020802,0x4f900080, +0x4095e460,0x20c02479,0x20084a08,0x3e400310, +0x820f9000,0xa44083e4,0x6020c824,0x79000a33, +0x091e4003,0x3c007fff,0x800f001f,0xffe00000, +00000000,00000000,00000000,00000000, +0x10400000,00000000,0x10000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* rare_inst + * + * ctr0: counts sync and syncdma instructions + * ctr1: counts pxtlbx,x instructions + * ctr2: counts ixtlbt instructions + * ctr3: counts cycles + */ +{ +0x0c01e000,00000000,0x00060000,00000000, +0xe0e0e0e0,0x004e000c,0x000843fc,0x85c09380, +0x0121ebfd,0xff217124,0xe0004000,0x943fc85f, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xe00000e0,0x00003c00,0x007f0001,0x01001fc0, +0x00408007,0xf0003030,0x01fc000c,0x10007f00, +0x0505001f,0xc0014180,0x07f00070,0x7001fc00, +0x1c20007f,0x00090900,0x1fc00242,0x8007f000, +0xb0b001fc,0x002c3000,0x7f000d0d,0x001fc003, +0x438007f0,0x00f0f001,0xfc003fff,0xfffff800, +0xfffffffe,0x003fffff,0xff800fff,0xffffe003, +0xfffffff8,0x00ffffff,0xfe003fff,0xffff800f, +0xffffffe0,0x03ffffff,0xf800ffff,0xfffe003f, +0xffffff80,0x0fffffff,0xe003ffff,0xfff800ff, +0xfffffe00,0x3fffffff,0x800fffff,0xffe00000, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* rw_dfet (for D-cache misses and writebacks) + * + * ctr0: counts address valid cycles + * ctr1: counts *all* data valid cycles + * ctr2: is the overflow from counter 0 + * ctr3: is the overflow from counter 1 + */ +{ +0x0c01e000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0x0000000c,0x00003c00,0x07930000,0x0041e4c0, +0x01002079,0x3000800c,0x1e4c0030,0x00279300, +0x010049e4,0xc0014022,0x79300090,0x0c9e4c00, +0x34004793,0x00020051,0xe4c00180,0x24793000, +0xa00d1e4c,0x00380067,0x93000300,0x59e4c001, +0xc0267930,0x00b00d9e,0x4c003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00100000,00000000,0xf0000000,00000000, +00000000,00000000,0x98000000,00000000, +0xffffffff,0xffffffff,0x0fffffff,0xffffffff, +00000000,00000000,0x00ffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* rw_ifet (I-cache misses -- actually dumb READ transactions) + * + * ctr0: counts address valid cycles + * ctr1: counts *all* data valid cycles + * ctr2: is the overflow from counter 0 + * ctr3: is the overflow from counter 1 + */ +{ +0x0c01e000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0x0000000c,0x00003c00,0x07930000,0x0041e4c0, +0x01002079,0x3000800c,0x1e4c0030,0x00279300, +0x010049e4,0xc0014022,0x79300090,0x0c9e4c00, +0x34004793,0x00020051,0xe4c00180,0x24793000, +0xa00d1e4c,0x00380067,0x93000300,0x59e4c001, +0xc0267930,0x00b00d9e,0x4c003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00100000,00000000,0xd0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0x00ffffff,0xffffffff, +0xffffffff,0xffffffff,00000000,00000000, +0xffffffff,0xffffffff }, + + +/* rw_sdfet (READ_SHARED_OR_PRIVATE transactions) + * + * ctr0: counts address valid cycles + * ctr1: counts *all* data valid cycles + * ctr2: is the overflow from counter 0 + * ctr3: is the overflow from counter 1 + */ +{ +0x0c01e000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0x0000000c,0x00003c00,0x07930000,0x0041e4c0, +0x01002079,0x3000800c,0x1e4c0030,0x00279300, +0x010049e4,0xc0014022,0x79300090,0x0c9e4c00, +0x34004793,0x00020051,0xe4c00180,0x24793000, +0xa00d1e4c,0x00380067,0x93000300,0x59e4c001, +0xc0267930,0x00b00d9e,0x4c003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00100000,00000000,0xf4000000,00000000, +00000000,00000000,00000000,00000000, +0xffffffff,0xffffffff,0x00ffffff,0xffffffff, +00000000,00000000,00000000,00000000, +0xffffffff,0xffffffff }, + + +/* spec_ifet + * + * ICORE_AV fires for every request which the Instruction Fetch Unit sends + * to the Runway Interface Block. Hence, this counts all I-misses, speculative + * or not, but does *not* include I-cache prefetches, which are generated by + * RIB. + * IRTN_AV fires twice for every I-cache miss returning from RIB to the IFU. + * It will not fire if a second I-cache miss is issued from the IFU to RIB + * before the first returns. Therefore, if the IRTN_AV count is much less + * than 2x the ICORE_AV count, many speculative I-cache misses are occurring + * which are "discovered" to be incorrect fairly quickly. + * The ratio of I-cache miss transactions on Runway to the ICORE_AV count is + * a measure of the effectiveness of instruction prefetching. This ratio + * should be between 1 and 2. If it is close to 1, most prefetches are + * eventually called for by the IFU; if it is close to 2, almost no prefetches + * are useful and they are wasted bus traffic. + * + * ctr0: counts ICORE_AV + * ctr1: counts IRTN_AV + * ctr2: counts all non-coherent READ transactions on Runway. (TTYPE D0) + * This should be just I-cache miss and I-prefetch transactions. + * ctr3: counts total processor cycles + */ +{ +0x0c000000,00000000,0x00060000,00000000, +0xefefefef,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0x0fffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0x00000008,0x00030c00,0x01bf0001,0x00806fc0, +0x00c1001b,0xf0005048,0x06fc001c,0x2001bf00, +0x0908806f,0xc002c300,0x1bf000d0,0xc806fc00, +0x3fffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0x06bf0000,00000000,00000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00110000,00000000,0xd0ffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0x00ffffff,0xffffffff, +0xffffffff,0xffffffff,00000000,00000000, +0xffffffff,0xffffffff }, + +/* st_cond0 + * + * ctr0: is the overflow for ctr1 + * ctr1: counts major ops 0C and 0E (fp ops, not fmac or fmpyadd) + * ctr2: counts B,L (including long and push) and GATE (including nullified), + * predicted not-taken + * ctr3: is the overflow for ctr2 + */ +{ +0x4c01e000,00000000,0x00060000,00000000, +0xe0e0c0e0,0xffffffff,0xffffffff,0xffc13380, +0x0101ffff,0xffa1f057,0xe000407f,0xdfffc87f, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* st_cond1 + * + * ctr0: is the overflow for ctr1 + * ctr1: counts major ops 1x (most of the load/stores) + * ctr2: counts CMPB (dw) predicted not-taken + * ctr3: is the overflow for ctr2 + */ +{ +0x4c01e000,00000000,0x00060000,00000000, +0xe0e0c0e0,0xffffffff,0xffffffff,0xffc01b80, +0x0101ffff,0xffb7f03d,0xe000407f,0xffffc8ff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* st_cond2 + * + * ctr0: is the overflow for ctr1 + * ctr1: counts major op 03 + * ctr2: counts CMPIB (dw) predicted not taken. + * ctr3: is the overflow for ctr2 + */ +{ +0x4c01e000,00000000,0x00060000,00000000, +0xe0e0c0e0,0xffffffff,0xffffffff,0xffc09780, +0x0101ffff,0xff21f077,0xe000407f,0xffffc87f, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* st_cond3 + * + * ctr0: is the overflow for ctr1 + * ctr1: counts major ops 06 & 26 + * ctr2: counts BB, BVB, MOVB, MOVIB (incl. nullified) predicted not-taken + * ctr3: is the overflow for ctr2 + */ +{ +0x4c01e000,00000000,0x00060000,00000000, +0xe0e0c0e0,0xffffffff,0xffffffff,0xffc03780, +0x0101ffff,0xff29f016,0xe000407f,0xffffe97f, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* st_cond4 + * + * ctr0: is the overflow for ctr1 + * ctr1: counts major op 2E + * ctr2: counts CMPB, CMPIB, ADDB, ADDIB (incl. nullified) predicted not-taken + * ctr3: is the overflow for ctr2 + */ +{ +0x4c01e000,00000000,0x00060000,00000000, +0xe0e0c0e0,0xffffffff,0xffffffff,0xffc17780, +0x0101ffff,0xff21f014,0xe000407f,0xffffe9ff, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* st_unpred0 + * + * ctr0: is the overflow for ctr1 + * ctr1: counts BE and BE,L + * ctr2: counts BE and BE,L including nullified + * ctr3: is the overflow for ctr2 + */ +{ +0x4c01e000,00000000,0x00060000,00000000, +0xe0c0c0e0,0xffffffff,0xffffffff,0xffdf5bbf, +0xffffffff,0xff25f7d6,0xefffffff,0xffffc97f, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* st_unpred1 + * + * ctr0: is the overflow for ctr1 + * ctr1: counts BLR, BV, BVE, BVE,L + * ctr2: counts BLR, BV, BVE, BVE,L including nullified + * ctr3: is the overflow for ctr2 + */ +{ +0x4c01e000,00000000,0x00060000,00000000, +0xe0c0c0e0,0xffffffff,0xffffffff,0xffc15f80, +0x0501ff7f,0xff21f057,0xe001407f,0xdfffc87f, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf0000060,0x00003c00,0x04f90000,0x02013e40, +0x0081004f,0x90004060,0x13e40018,0x0024f900, +0x0802093e,0x40028102,0x4f9000c0,0x6093e400, +0x380014f9,0x00010205,0x3e4000c1,0x014f9000, +0x506053e4,0x001c0034,0xf9000902,0x0d3e4002, +0xc1034f90,0x00d060d3,0xe4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + +/* unpred + * + * ctr0: counts non-nullified unpredictable branches + * ctr1: is the overflow for ctr0 + * ctr2: counts all unpredictable branches (nullified or not) + * ctr3: is the overflow for ctr2 + */ +{ +0xcc01e000,00000000,0x00060000,00000000, +0x20202020,0xff31ffff,0xfff7fffe,0x97ffcc7f, +0xfffffdff,0xffa5fff3,0x1fffffff,0x7fffe97f, +0xffffffff,0xffffffff,0xff000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffff0000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xf0000000, +0xf00000a0,0x00003c00,0x02f50000,0x0004bd40, +0x0040802f,0x50002020,0x4bd4000c,0x0042f500, +0x040014bd,0x40014084,0x2f500060,0x214bd400, +0x1c2002f5,0x00080804,0xbd400242,0x802f5000, +0xa0a04bd4,0x002c2042,0xf5000c08,0x14bd4003, +0x42842f50,0x00e0a14b,0xd4003fff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xfffffc00, +00000000,00000000,00000000,00000000, +0xffff0000,00000000,0xf0000000,00000000, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xfffffc00,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xfffffc00,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xffffffff,0xf3ffffff,0xffffffff, +0xfdffffff,0xffffffff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0xffffffff,0xfffff9ff,0xfe000000,00000000, +0x00030000,00000000,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff,0xffffffff,0xffffffff, +0xffffffff,0xffffffff }, + + +/* go_store + * + * ctr0: Overflow for counter 2 + * ctr1: Overflow for counter 3 + * ctr2: count of GO_STORE_E signal + * ctr3: count of GO_STORE_O signal + */ + + { + 0x0c00e000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffa5ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x7fffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf0000000, + 0x00000000, 0x0000c000, 0x067c0000, 0x01019f00, + 0x00408067, 0xc0002030, 0x19f0000c, 0x000e7c00, + 0x0401039f, 0x00014080, 0xe7c00060, 0x3039f000, + 0x1c00167c, 0x00080105, 0x9f000240, 0x8167c000, + 0xa03059f0, 0x002c001e, 0x7c000c01, 0x079f0003, + 0x4081e7c0, 0x00e03079, 0xf0003fc0, 0x07fff800, + 0xf001fffe, 0x003c007f, 0xff800f00, 0x1fffe003, + 0xc007fff8, 0x00f001ff, 0xfe003c00, 0x7fff800f, + 0x001fffe0, 0x03c007ff, 0xf800f001, 0xfffe003c, + 0x007fff80, 0x0f001fff, 0xe003c007, 0xfff800f0, + 0x01fffe00, 0x3c007fff, 0x800f001f, 0xffe00000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x70130000, 0x00000000, 0x70000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffaaaa, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff + }, + + +/* shlib_call + * + * ctr0: SharedLib call Depth1 + * ctr1: SharedLib call Depth2 + * ctr2: SharedLib call Depth3 + * ctr3: SharedLib call Depth>3 + */ + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0xc76fa005, 0x07dd7e9c, 0x87115b80, + 0x01100200, 0x07200004, 0xe000407f, 0xfffffffc, + 0x01380010, 0x1fffffff, 0xff000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf0000000, + 0xf0000000, 0x00003c20, 0x01ff0808, 0x04007fc0, + 0x0003001f, 0xf0000180, 0x07fc4010, 0x5001ff00, + 0x001c007f, 0xc2000a00, 0x1ff18022, 0x4007fc20, + 0x00b001ff, 0x10003800, 0x7fc8004d, 0x001ff100, + 0x03c007fc, 0x60012001, 0xff280144, 0x007fc600, + 0x13001ff2, 0x00058007, 0xfcc00550, 0x01ff2000, + 0x5c007fca, 0x001a001f, 0xf3801640, 0x07fca001, + 0xb001ff30, 0x0078007f, 0xd0005d00, 0x1ff30007, + 0xc007fce0, 0x022001ff, 0x48018400, 0x7fce0023, + 0x001ff400, 0x098007fd, 0x20065001, 0xff40009c, + 0x007fd200, 0x3fffffff, 0x800fffff, 0xffe00000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffff0000, 0x00000000, 0xf0000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffc00, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc00, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xf3ffffff, 0xffffffff, + 0xfdffffff, 0xffffffff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0xffffffff, 0xfffff9ff, 0xfe000000, 0x00000000, + 0x00030000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff + } +}; +#define PCXW_IMAGE_SIZE 576 + +static uint32_t cuda_images[][PCXW_IMAGE_SIZE/sizeof(uint32_t)] __ro_after_init = { +/* + * CPI: FROM CPI.IDF (Image 0) + * + * Counts the following: + * + * ctr0 : total cycles + * ctr1 : total cycles where nothing retired + * ctr2 : total instructions retired, including nullified + * ctr3 : total instructions retired, less nullified instructions + */ + { + 0x4c00c000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0x00001fff, 0xfc00007f, 0xfff00001, + 0xffffc000, 0x07ffff00, 0x07ffffff, 0x6007ffff, + 0xff0007ff, 0xffff0007, 0xffffff00, 0x00000000, + 0x60f00000, 0x0fffff00, 0x000fffff, 0x00000fff, + 0xff00000f, 0xffff0000, 0x00000000, 0x00ffffff, + 0xfffff000, 0x0000000f, 0xffffffff, 0xff000000, + 0x0000ffff, 0xfffffff0, 0x00000000, 0x0fffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00270000, 0x00000055, + 0x0200000e, 0x4d300000, 0x00000000, 0x0ff00002, + 0x70000000, 0x00000020, 0x0000e400, 0x00000ff0, + 0x00000000, 0x00000000, 0x00000055, 0xffffff00, + 0x00000000, 0x0000ff00, 0x00000000, 0x0f000000, + 0x0000055f, 0xfffff000, 0x00000000, 0x000ff000, + 0x00000000, 0x00000000, 0x000055ff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0xf0000000, + 0x000055ff, 0xffff0000, 0x00000000, 0x00ff0000, + 0x00000000, 0x00000000, 0x00055fff, 0xfff00000, + 0x00000000, 0x0ff00000, 0x00000030, 0x00000000, + 0x00157fff, 0xffc00000, 0x034c0000, 0x00000000, + 0x03fc0000, 0x00000000, 0x6fff0000, 0x00000000, + 0x60000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* Bus utilization image FROM BUS_UTIL.IDF (Image 1) + * + * ctr0 : counts address valid cycles + * ctr1 : counts data valid cycles + * ctr2 : counts overflow from counter 0 + * ctr3 : counts overflow from counter 1 + */ + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xefefefef, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00001b00, 0xaa000000, + 0x00000001, 0x30700000, 0x00055aaf, 0xf0000000, + 0x01b00000, 0x00000000, 0x00001037, 0x00000000, + 0x55aaff00, 0x00c00000, 0x1b55aa00, 0x00000000, + 0x0001fff0, 0xcfffff00, 0x00000000, 0x0f0fffff, + 0xffffffff, 0xffffffff, 0x30ffff0c, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xfffffff3, + 0x0ffff0cf, 0xffff0000, 0x00000000, 0x00ffffff, + 0xffffffff, 0xfffffff3, 0x0ffff0cf, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffff30, + 0xfff70000, 0x000055aa, 0xff000000, 0x000006d5, + 0x40000000, 0x00000000, 0x731c0000, 0x000156ab, + 0xfc000000, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00100000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* + * TLB counts: FROM TLBSTATS.IDF (Image 2) + * + * Counts the following: + * + * ctr0: DTLB misses + * ctr1: ITLB misses + * ctr2: total cycles in the miss handlers + * ctr3: total cycles + */ + + { + 0x0c00c000, 0x00000000, 0x00060000, 0x00000000, + 0xe7e7e0e0, 0x00001fff, 0xfc00007f, 0xfff00001, + 0xfff00000, 0x07ffff00, 0x07ffffff, 0x6007ffff, + 0xa00007ff, 0xffff0007, 0xffffff00, 0x00000000, + 0x603001c1, 0xe0000001, 0xc0c00000, 0x00000fff, + 0xff00000f, 0xffff0000, 0x00000000, 0x00400000, + 0x00001000, 0x00000004, 0x00000000, 0x01000000, + 0x0000ffff, 0xfffffff0, 0x00000000, 0x0fffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00800000, 0x00153f7f, + 0x55000000, 0xaf800000, 0xc0000000, 0x0403f240, + 0x00000000, 0x00001010, 0x00004700, 0x00000ff0, + 0x00000000, 0x00000000, 0x00000055, 0xffffff00, + 0x00000000, 0x0000ff00, 0x00000000, 0x0f000000, + 0x0000055f, 0xfffff000, 0x00000000, 0x000ff000, + 0x00000000, 0x00000000, 0x000055ff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0xf0000000, + 0x000055ff, 0xffff0000, 0x00000000, 0x00ff0000, + 0x00000000, 0x00000000, 0x00055fff, 0xfff00000, + 0x00000000, 0x0ff00000, 0x00000000, 0x00000000, + 0x00157fff, 0xffc00000, 0x00000000, 0x3fc00000, + 0x00040000, 0x00000000, 0x6fff0000, 0x00000000, + 0x60000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* tlbhandler FROM tlbHandMiss.idf (Image 3) + * + * ctr0: TLB misses + * ctr1: dmisses inside the TLB miss handler + * ctr2: cycles in the TLB miss handler + * ctr3: overflow of ctr2 + */ + { + 0x1c00c000, 0x00000000, 0x00060000, 0x00000000, + 0xe7e7e0e0, 0x00001fff, 0xfc00007f, 0xfff00001, + 0xfff00000, 0x07ffff00, 0x07ffffff, 0x6007ffff, + 0xa00007ff, 0xffff0007, 0xffffff00, 0x00000000, + 0x603001c1, 0xe0000001, 0xc0c00000, 0x00000fff, + 0xff00000f, 0xffff0000, 0x00000000, 0x00400000, + 0x00001000, 0x00000004, 0x00000000, 0x01000000, + 0x0000ffff, 0xfffffff0, 0x00000000, 0x0fffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x006c0000, 0x01000054, + 0x02000002, 0xc3200000, 0xc00aa000, 0x0c03f240, + 0x00000000, 0x00001010, 0x000044f4, 0x00000c00, + 0xaa0000f0, 0x0f0000b0, 0x00005005, 0x0f5f0000, + 0x0001f000, 0x0000ff00, 0x00000000, 0x0f000000, + 0x0000055f, 0xfffff000, 0x00000000, 0x000ff000, + 0x00000000, 0x00000000, 0x000055ff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0xf0000000, + 0x000055ff, 0xffff0000, 0x00000000, 0x00ff0000, + 0x00000000, 0x00000000, 0x00055fff, 0xfff00000, + 0x00000000, 0x0ff00a00, 0x000f0000, 0x24004000, + 0x15400001, 0x40c00003, 0x3da00000, 0x0002a800, + 0x00ff0000, 0x00000000, 0x6fff0000, 0x00000000, + 0x60000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* branch_taken image FROM PTKN.IDF (Image 4) + * + * ctr0: mispredicted branches + * ctr1: predicted taken branches, actually taken + * ctr2: predicted taken branches (includes nullfied) + * ctr3: all branches + */ + + { + 0xcc01e000, 0x00000000, 0x00000000, 0x00000000, + 0xa08080a0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xfffffeff, 0xfffeffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf4ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xd22d0000, 0x00000000, + 0x0000000b, 0x46000000, 0x00000000, 0x0ffff900, + 0x90000000, 0x00000000, 0x0000907e, 0x00000000, + 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000, + 0x000dbfff, 0xffffff00, 0x00000000, 0x0f0fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffff5555, 0x55500000, 0x003f3ff0, 0x2766c000, + 0x00000000, 0x00000002, 0x67840000, 0x00000000, + 0x03fffc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* branch_nottaken FROM PNTKN.IDF (Image 5) + * + * ctr0: mispredicted branches + * ctr1: branches predicted not-taken, but actually taken + * ctr2: branches predicted not-taken (includes nullified) + * ctr3: all branches + */ + { + 0xcc01e000, 0x00000000, 0x00000000, 0x00000000, + 0xe0c0c0e0, 0xffffffff, 0xffffffff, 0xffefffff, + 0xffffbfff, 0xfffffeff, 0xfffeffff, 0xfffffeff, + 0xfffffffe, 0xffffffff, 0xffffff00, 0x00000000, + 0xf4ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xd22d0000, 0x00000000, + 0x0000000b, 0x46000000, 0x00000000, 0x0ffff900, + 0x90000000, 0x00000000, 0x0000907e, 0x00000000, + 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000, + 0x000dbfff, 0xffffff00, 0x00000000, 0x0f0fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffff5555, 0x55500000, 0x003f3ff0, 0x2766c000, + 0x00000000, 0x00000002, 0x67840000, 0x00000000, + 0x03fffc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* IMISS image (Image 6) + * + * ctr0 : icache misses for retired instructions + * ctr1 : total cycles + * ctr2 : dcache misses for retired instructions + * ctr3 : number of retired instructions + */ + { + 0x2801e000, 0x00000000, 0x00010000, 0x00000000, + 0x00001000, 0xffffffff, 0xffffffff, 0xfff00fff, + 0xfffa3fff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xf2fdf0f0, 0xf0f0f0f0, + 0xffffffff, 0xf6c00000, 0x00000000, 0x0ff55800, + 0x90000000, 0x00000000, 0x0000b0ff, 0xfffffff0, + 0x00000003, 0x0100bfff, 0x3f3f3f3f, 0x3f3f5555, + 0x555fffff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xfff00000, 0x000301b0, 0x2fefcfcf, + 0xcfcfcfcf, 0xd5555557, 0xf7b40000, 0x00000000, + 0x03c14000, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00130000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* DMISS image (Image 7) + * + * ctr0 : icache misses for retired instructions + * ctr1 : total cycles + * ctr2 : dcache misses for retired instructions + * ctr3 : number of retired instructions + */ + { + 0x2801e000, 0x00000000, 0x00010000, 0x00000000, + 0x00001000, 0xffffffff, 0xffffffff, 0xfff00fff, + 0xfffa3fff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xf2fdf0f0, 0xf0f0f0f0, + 0xffffffff, 0xf6c00000, 0x00000000, 0x0ff55800, + 0x90000000, 0x00000000, 0x0000b0ff, 0xfffffff0, + 0x00000003, 0x0100bfff, 0x3f3f3f3f, 0x3f3f5555, + 0x555fffff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xfff00000, 0x000301b0, 0x2fefcfcf, + 0xcfcfcfcf, 0xd5555557, 0xf7b40000, 0x00000000, + 0x03c14000, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00130000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* dmiss_access image FROM DMISS_RATIO.IDF (Image 8) + * + * ctr0 : all loads and stores that retire (even lines) + * ctr1 : all loads and stores that retire (odd lines) + * ctr2 : dcache misses of retired loads/stores + * ctr3 : all READ_PRIV and READ_SHAR_OR_PRIV on Runway + * (Speculative and Non-Speculative) + */ + { + 0x2d81e000, 0x00000000, 0x00000000, 0x00000000, + 0x10101010, 0x00ffffff, 0xa003ffff, 0xfe800fff, + 0xfffa003f, 0xffffe8ff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xd2280a00, 0x00000000, + 0x0000000b, 0x46000000, 0x00000005, 0x555ff900, + 0x80200000, 0x00000000, 0x0000907e, 0x00000000, + 0x00005555, 0xff80bf8b, 0xab030303, 0x03030000, + 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffff5555, 0x55500000, 0x15153fe0, 0x27628880, + 0x00000000, 0x00000002, 0x67840000, 0x00000001, + 0x5557fc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00110000, 0x00000000, + 0xf4ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xf8ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + + +/* big_cpi image (Image 9) + * + * ctr0 : Total number of CPU clock cycles. + * ctr1 : Unused + * ctr2 : Unused + * ctr3 : Total number of Non-Nullified instructions retired. + */ + { + 0x0c00c000, 0x00000000, 0x00060000, 0x00000000, + 0xe7e7e0e0, 0x00001fff, 0xfc00007f, 0xfff00001, + 0xfff00000, 0x07ffff00, 0x07ffffff, 0x6007ffff, + 0xa00007ff, 0xffff0007, 0xffffff00, 0x00000000, + 0x603001c1, 0xe0000001, 0xc0c00000, 0x00000fff, + 0xff00000f, 0xffff0000, 0x00000000, 0x00400000, + 0x00001000, 0x00000004, 0x00000000, 0x01000000, + 0x0000ffff, 0xfffffff0, 0x00000000, 0x0fffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00550005, 0x00220000, + 0x0000000c, 0x71f00000, 0x00f00aa0, 0x0aaff000, + 0x00005002, 0x20000000, 0x0000c413, 0x00000c0f, + 0x00aa0000, 0xff00b600, 0x000500a0, 0x00000300, + 0x000cc3f0, 0x0000c0f0, 0x0aa0000f, 0xff000000, + 0x011000a0, 0x05503000, 0x00d03700, 0x00000f00, + 0xaa005500, 0x00000000, 0x000055ff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0xf000aa00, + 0x11000a00, 0x55000000, 0x0d037000, 0x00c0f00a, + 0xa0055000, 0x0db00005, 0x5002a000, 0x00300000, + 0xf40f0000, 0x0c0f00aa, 0x0000ff10, 0x27400000, + 0x00008000, 0x00c00003, 0x037c0000, 0x003c02a8, + 0x02abfc00, 0x00000000, 0x6fff0000, 0x00000000, + 0x60000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* big_ls image (Image 10) + * + * ctr0 : Total number of CPU clock cycles during which local_stall_A1 is asserted + * ctr1 : Overflow of Counter 0 + * ctr2 : Total number of IFLUSH_AV + * ctr3 : Overflow of Counter 2 + */ + { + 0x0c000000, 0x00000000, 0x00060000, 0x00000000, + 0xefefefef, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0x00ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x28880001, 0x54000000, + 0x00000004, 0xb6200000, 0x000aaaa0, 0x05555288, + 0x80000010, 0x00000000, 0x0000486e, 0x00000000, + 0xaaaa0055, 0x55002888, 0x00545401, 0x03030000, + 0x0007b000, 0x0000ff00, 0x00000000, 0x05000000, + 0x0000055f, 0xfffff000, 0x00000000, 0x000ff000, + 0x00000000, 0x00000000, 0x000055ff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0x00000000, + 0x000055ff, 0xffff0000, 0x00000000, 0x00ff0000, + 0x00000000, 0xa0000000, 0x00055fff, 0xfff00000, + 0x00aa0000, 0x05502a2a, 0x00151500, 0x0a220015, + 0x40400000, 0x00000001, 0xe2980000, 0x0002aaa8, + 0x01555400, 0x00000000, 0x0df70000, 0x00000000, + 0x00000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* br_abort image (Image 12) + * + * ctr0 : Total number of BRAD_STALLH + * ctr1 : Total number of ONE_QUAD + * ctr2 : Total number of BR0_ABRT + * ctr3 : Total number of BR1_ABRT + */ + + { + 0x0c002000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0xffffffff, 0xffffffff, 0xff0fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0x1077ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x551b0000, 0x00000000, + 0x0000000c, 0xd4f00000, 0x00000000, 0x0ffff001, + 0xb0000000, 0x00000000, 0x0000fd4c, 0x00000000, + 0x000000ff, 0xff00ff1b, 0x00000000, 0x00000000, + 0x0000d000, 0x0000ff00, 0x00000000, 0x0e0fffff, + 0xffffffff, 0xfffff000, 0x00000000, 0x000ff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0x00ffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0x00ff0000, + 0x00000000, 0xffffffff, 0xffffffff, 0xfff00000, + 0x00400000, 0x00000000, 0x00ffff00, 0x2a86c000, + 0x00000000, 0x00000000, 0xf50c0000, 0x00000000, + 0x03fffc00, 0x00000000, 0x1a250000, 0x00000000, + 0x10000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffafff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + + +/* isnt image (Image 13) + * + * ctr0 : Total number of cycles for which iside_notrans is asserted. + * ctr1 : Total number of times iside_notrans is asserted for 1-4 cycles. + * ctr2 : Total number of times iside_notrans is asserted for 5-7 cycles. + * ctr3 : Total number of times iside_notrans is asserted for > 7 cycles. + */ + + { + 0x0c018000, 0x00000000, 0x00060000, 0x00000000, + 0xefefefef, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xc0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x22000000, 0x000001bc, + 0x10000006, 0x00900000, 0x50000000, 0x00055a20, + 0x00000000, 0x00016060, 0x0000c021, 0x00000540, + 0x00000000, 0x55002200, 0x00000000, 0x56bc4000, + 0x00048000, 0x0000ff00, 0x00000000, 0x17000000, + 0x0000055f, 0xfffff000, 0x00000000, 0x000ff000, + 0x00000000, 0x00000000, 0x000055ff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0x00000000, + 0x000055ff, 0xffff0000, 0x00000000, 0x00ff0000, + 0x00000000, 0x80000000, 0x00015bf3, 0xf5500000, + 0x02210000, 0x00100000, 0x00005500, 0x08800000, + 0x00001545, 0x85000001, 0x80240000, 0x11000000, + 0x00015400, 0x00000000, 0xcdff0000, 0x00000000, + 0xc0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* quadrant image (image 14) + * + * ctr0 : Total number of instructions in quadrant 0. + * ctr1 : Total number of instructions in quadrant 1. + * ctr2 : Total number of instructions in quadrant 2. + * ctr3 : Total number of instructions in quadrant 3. + * + * Only works for 32-bit applications. + */ + + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0x00001fff, 0xfc00007f, 0xfff00001, + 0xffffc000, 0x07ffff00, 0x07ffffff, 0x0007ffff, + 0xff0007ff, 0xffff0007, 0xffffff00, 0x00000000, + 0xf0000000, 0x0fffff00, 0x000fffff, 0x00000fff, + 0xff00000f, 0xffff0000, 0x00000000, 0x00ffffff, + 0xffcff000, 0x0000040f, 0xfffffffc, 0xff000000, + 0x0080ffff, 0xffffcff0, 0x0000000c, 0x0fffffff, + 0xfcff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x551b0000, 0x00000000, + 0x00000003, 0x17000000, 0x00000000, 0x0ffff001, + 0xb0000000, 0x00000000, 0x00000173, 0x00000000, + 0x000000ff, 0xff00ff1b, 0x00000000, 0x00000000, + 0x000f1ff0, 0xcfffff00, 0x00000000, 0x0f0fffff, + 0xffffffff, 0xffffffff, 0x30ffff0c, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xfffffff3, + 0x0ffff0cf, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xfffffff3, 0x0ffff0cf, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffff30, + 0xff7f0000, 0x00000000, 0x00fffff0, 0x2a86c000, + 0x00000000, 0x00000003, 0x05f00000, 0x00000000, + 0x03fffc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* rw_pdfet image (Image 15) + * + * ctr0 : Total of all READ_PRIV address valid cycles. + * ctr1 : Total of all READ_PRIV data valid cycles. + * ctr2 : Overflow of Counter 0. + * ctr3 : Overflow of Counter 1. + */ + + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xefefefef, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00001b00, 0xaa000000, + 0x00000001, 0x30700000, 0x00055aaf, 0xf0000000, + 0x01b00000, 0x00000000, 0x00001037, 0x00000000, + 0x55aaff00, 0x00c00000, 0x1b55aa00, 0x00000000, + 0x0001fff0, 0xcfffff00, 0x00000000, 0x0f0fffff, + 0xffffffff, 0xffffffff, 0x30ffff0c, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xfffffff3, + 0x0ffff0cf, 0xffff0000, 0x00000000, 0x00ffffff, + 0xffffffff, 0xfffffff3, 0x0ffff0cf, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffff30, + 0xfff70000, 0x000055aa, 0xff000000, 0x000006d5, + 0x40000000, 0x00000000, 0x731c0000, 0x000156ab, + 0xfc000000, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00100000, 0x00000000, + 0xf8000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00ffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + }, + + +/* rw_wdfet image (Image 16) + * + * ctr0 : Counts total number of writeback transactions. + * ctr1 : Total number of data valid Runway cycles. + * ctr2 : Overflow of Counter 0. + * ctr3 : Overflow of Counter 1. + */ + + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xefefefef, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00001b00, 0xaa000000, + 0x00000001, 0x30700000, 0x00055aaf, 0xf0000000, + 0x01b00000, 0x00000000, 0x00001037, 0x00000000, + 0x55aaff00, 0x00c00000, 0x1b55aa00, 0x00000000, + 0x0001fff0, 0xcfffff00, 0x00000000, 0x0f0fffff, + 0xffffffff, 0xffffffff, 0x30ffff0c, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xfffffff3, + 0x0ffff0cf, 0xffff0000, 0x00000000, 0x00ffffff, + 0xffffffff, 0xfffffff3, 0x0ffff0cf, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffff30, + 0xfff70000, 0x000055aa, 0xff000000, 0x000006d5, + 0x40000000, 0x00000000, 0x731c0000, 0x000156ab, + 0xfc000000, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00100000, 0x00000000, + 0x98000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00ffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + }, + +/* shlib_cpi image (Image 17) + * + * ctr0 : Total number of instructions in quadrant 0. + * ctr1 : Total number of CPU clock cycles in quadrant 0. + * ctr2 : Total number of Non-Nullified instructions retired. + * ctr3 : Total number of CPU clock cycles. + * + * Only works for 32-bit shared libraries. + */ + + { + 0x0c01e000, 0x00000000, 0x00060000, 0x00000000, + 0xe0e0e0e0, 0x00001fff, 0xfc00007f, 0xfff00001, + 0xffffc000, 0x07ffff00, 0x07ffffff, 0x0007ffff, + 0xff0007ff, 0xffff0007, 0xffffff00, 0x00000000, + 0xf0150000, 0x0fffff00, 0x000fffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0x00ffffff, + 0xffcff000, 0x0000000f, 0xfffffffc, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x27000000, 0x00000055, + 0x02000005, 0x7f500000, 0xc0000000, 0x000ff270, + 0x00000000, 0x00000000, 0x00007700, 0x00000ff0, + 0x00000000, 0x0000ffff, 0xffffffff, 0xffffff00, + 0x00000000, 0x0000ff00, 0x00000000, 0x0f0fffff, + 0xffffffff, 0xfffff000, 0x00000000, 0x000ff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x00ff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0x00ff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xfff00000, + 0x00000000, 0x0ff00000, 0x000000a0, 0x3fffffff, + 0xffffffff, 0xffc00000, 0x03d40000, 0x20000000, + 0x0003fc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff7fbfc, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff7fbfc, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00030000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* flop image (Image 18) + * + * ctr0 : Total number of floating point instructions (opcode = 0xc). + * ctr1 : Total number of floating point instructions (opcode = 0xe, 0x6, 0x2e, 0x26). + * ctr2 : Unused + * ctr3 : Unused + */ + + { + 0x0001e000, 0x00000000, 0x00000000, 0x00000000, + 0x00001010, 0x33ffffff, 0x006fffff, 0xfc5fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xd22d0000, 0x00000000, + 0x0000000b, 0x46000000, 0x00000000, 0x0ffff900, + 0x90000000, 0x00000000, 0x0000907e, 0x00000000, + 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000, + 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffff5555, 0x55500000, 0x003f3ff0, 0x2766c000, + 0x00000000, 0x00000002, 0x67840000, 0x00000000, + 0x03fffc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00130000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* cachemiss image FROM I_D_MISSES.IDF (Image 19) + * + * ctr0 : icache misses for retired instructions + * ctr1 : total cycles + * ctr2 : dcache misses for retired instructions + * ctr3 : number of retired instructions + */ + { + 0x2801e000, 0x00000000, 0x00010000, 0x00000000, + 0x00001000, 0xffffffff, 0xffffffff, 0xfff00fff, + 0xfffa3fff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xf2fdf0f0, 0xf0f0f0f0, + 0xffffffff, 0xf6c00000, 0x00000000, 0x0ff55800, + 0x90000000, 0x00000000, 0x0000b0ff, 0xfffffff0, + 0x00000003, 0x0100bfff, 0x3f3f3f3f, 0x3f3f5555, + 0x555fffff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xfff00000, 0x000301b0, 0x2fefcfcf, + 0xcfcfcfcf, 0xd5555557, 0xf7b40000, 0x00000000, + 0x03c14000, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00130000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* branch FROM br_report3.idf + * + * ctr0 : Total number of mispredicted branches. + * ctr1 : Some Non-Nullified unpredictable branches. + * ctr2 : Total number of branches (Nullified + Non-Nullified) + * (Unpredicted+ Predicted Taken +Predicted Not Taken). + * Total of All Branches. + * ctr3 : Remaining Non-Nullified unpredictable branches. + */ + { + 0x4001e000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xffffffff, 0xff9fffff, 0xfe0fffff, + 0xffffbaff, 0xfdffc0ff, 0xfffdffff, 0xfffffeff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf4ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xd22d0000, 0x00000000, + 0x0000000b, 0x46000000, 0x00000000, 0x0ffff900, + 0x90000000, 0x00000000, 0x0000907e, 0x00000000, + 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000, + 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffff5555, 0x55500000, 0x003f3ff0, 0x2766c000, + 0x00000000, 0x00000002, 0x67840000, 0x00000000, + 0x03fffc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00130000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* crstack FROM crs_report.idf + * + * ctr0: correctly predicted branches by the pop_latch + * ctr1: some procedure returns + * ctr2: all branches, (includes nullified) + * ctr3: remaining procedure returns + */ + { + 0x4001e000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xffffffff, 0xffa10300, 0x000fffff, + 0xffffbaf8, 0x3000007f, 0xffffffff, 0xfffffeff, + 0xff7fffff, 0xffffffff, 0xffffff00, 0x00000000, + 0xf2ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xd22d0000, 0x00000000, + 0x0000000b, 0x46000000, 0x00000000, 0x0ffff900, + 0x90000000, 0x00000000, 0x0000907e, 0x00000000, + 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000, + 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0xf0ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x0fffffff, 0xffffffff, 0xffffffff, + 0xffff5555, 0x55500000, 0x003f3ff0, 0x2766c000, + 0x00000000, 0x00000002, 0x67840000, 0x00000000, + 0x03fffc00, 0x00000000, 0xffff0000, 0x00000000, + 0xf0000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00130000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + +/* icache_report image + * + * ctr0 : Icache misses actually used by the core. + * ctr1 : ICORE_AV (Icache misses the core THINKS it needs, including fetching down speculative paths). + * ctr2 : READs on Runway (Icache misses that made it out to Runway, including + * prefetches). + * ctr3 : Prefetch returns (1x and 2x). + */ + { + 0x00000000, 0x00000000, 0x00010000, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffff00, 0x00000000, + 0x00ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xd2002d00, 0x00000000, + 0x0000000b, 0x46000000, 0x0000000f, 0xf00ff900, + 0x00900000, 0x00000000, 0x0000907e, 0x00000000, + 0x0000ff00, 0xff83bf03, 0xdf030303, 0x03030000, + 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000, + 0x00000000, 0x00ffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0x00000000, 0x80ffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, + 0x00000000, 0x4fffffff, 0xffffffff, 0xffffffff, + 0xffff5555, 0x55500000, 0x3f003f80, 0x274026c0, + 0x00000000, 0x00000002, 0x67840000, 0x00000003, + 0xfc03fc00, 0x00000000, 0x0eff0000, 0x00000000, + 0x00000000, 0x00000000, 0x00ffffff, 0xff3fffff, + 0xffffffff, 0xffcfffff, 0xfff6fb7c, 0x00000000, + 0x00ffffff, 0xff3fffff, 0xffffffff, 0xffcfffff, + 0xfff6fb7c, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffff0fff, 0xffffff3f, + 0xffffffff, 0xffffff7f, 0xffffffff, 0xfffffefc, + 0x00000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0xffffffff, 0xfffff9ff, + 0xfe000000, 0x00000000, 0x00130000, 0x00000000, + 0xd0ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00ffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + + } + +}; + +#endif diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c new file mode 100644 index 000000000..b62d60009 --- /dev/null +++ b/arch/parisc/kernel/process.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PARISC Architecture-dependent parts of process handling + * based on the work for i386 + * + * Copyright (C) 1999-2003 Matthew Wilcox <willy at parisc-linux.org> + * Copyright (C) 2000 Martin K Petersen <mkp at mkp.net> + * Copyright (C) 2000 John Marvin <jsm at parisc-linux.org> + * Copyright (C) 2000 David Huggins-Daines <dhd with pobox.org> + * Copyright (C) 2000-2003 Paul Bame <bame at parisc-linux.org> + * Copyright (C) 2000 Philipp Rumpf <prumpf with tux.org> + * Copyright (C) 2000 David Kennedy <dkennedy with linuxcare.com> + * Copyright (C) 2000 Richard Hirst <rhirst with parisc-linux.org> + * 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-2014 Helge Deller <deller@gmx.de> + * Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org> + */ +#include <linux/elf.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/cpu.h> +#include <linux/module.h> +#include <linux/personality.h> +#include <linux/ptrace.h> +#include <linux/reboot.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/sched/task.h> +#include <linux/sched/task_stack.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 <linux/nmi.h> +#include <linux/sched/hotplug.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/unwind.h> +#include <asm/sections.h> +#include <asm/cacheflush.h> + +#define COMMAND_GLOBAL F_EXTEND(0xfffe0030) +#define CMD_RESET 5 /* reset any module */ + +/* +** The Wright Brothers and Gecko systems have a H/W problem +** (Lasi...'nuf said) may cause a broadcast reset to lockup +** the system. An HVERSION dependent PDC call was developed +** to perform a "safe", platform specific broadcast reset instead +** of kludging up all the code. +** +** Older machines which do not implement PDC_BROADCAST_RESET will +** return (with an error) and the regular broadcast reset can be +** issued. Obviously, if the PDC does implement PDC_BROADCAST_RESET +** the PDC call will not return (the system will be reset). +*/ +void machine_restart(char *cmd) +{ +#ifdef FASTBOOT_SELFTEST_SUPPORT + /* + ** If user has modified the Firmware Selftest Bitmap, + ** run the tests specified in the bitmap after the + ** system is rebooted w/PDC_DO_RESET. + ** + ** ftc_bitmap = 0x1AUL "Skip destructive memory tests" + ** + ** Using "directed resets" at each processor with the MEM_TOC + ** vector cleared will also avoid running destructive + ** memory self tests. (Not implemented yet) + */ + if (ftc_bitmap) { + pdc_do_firm_test_reset(ftc_bitmap); + } +#endif + /* set up a new led state on systems shipped with a LED State panel */ + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_SHUTDOWN); + + /* "Normal" system reset */ + pdc_do_reset(); + + /* Nope...box should reset with just CMD_RESET now */ + gsc_writel(CMD_RESET, COMMAND_GLOBAL); + + /* Wait for RESET to lay us to rest. */ + while (1) ; + +} + +void (*chassis_power_off)(void); + +/* + * This routine is called from sys_reboot to actually turn off the + * machine + */ +void machine_power_off(void) +{ + /* If there is a registered power off handler, call it. */ + 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 + * following call will immediately power off. */ + pdc_soft_power_button(0); + + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_SHUTDOWN); + + /* ipmi_poweroff may have been installed. */ + do_kernel_power_off(); + + /* It seems we have no way to power the system off via + * software. The user has to press the button himself. */ + + printk("Power off or press RETURN to reboot.\n"); + + /* prevent soft lockup/stalled CPU messages for endless loop. */ + rcu_sysrq_start(); + lockup_detector_soft_poweroff(); + while (1) { + /* reboot if user presses RETURN key */ + if (pdc_iodc_getc() == 13) { + printk("Rebooting...\n"); + machine_restart(NULL); + } + } +} + +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + +void machine_halt(void) +{ + machine_power_off(); +} + +void flush_thread(void) +{ + /* Only needs to handle fpu stuff or perf monitors. + ** REVISIT: several arches implement a "lazy fpu state". + */ +} + +/* + * Idle thread support + * + * Detect when running on QEMU with SeaBIOS PDC Firmware and let + * QEMU idle the host too. + */ + +int running_on_qemu __ro_after_init; +EXPORT_SYMBOL(running_on_qemu); + +/* + * Called from the idle thread for the CPU which has been shutdown. + */ +void arch_cpu_idle_dead(void) +{ +#ifdef CONFIG_HOTPLUG_CPU + idle_task_exit(); + + local_irq_disable(); + + /* Tell __cpu_die() that this CPU is now safe to dispose of. */ + (void)cpu_report_death(); + + /* Ensure that the cache lines are written out. */ + flush_cache_all_local(); + flush_tlb_all_local(NULL); + + /* Let PDC firmware put CPU into firmware idle loop. */ + __pdc_cpu_rendezvous(); + + pr_warn("PDC does not provide rendezvous function.\n"); +#endif + while (1); +} + +void __cpuidle arch_cpu_idle(void) +{ + raw_local_irq_enable(); + + /* nop on real hardware, qemu will idle sleep. */ + asm volatile("or %%r10,%%r10,%%r10\n":::); +} + +static int __init parisc_idle_init(void) +{ + if (!running_on_qemu) + cpu_idle_poll_ctrl(1); + + return 0; +} +arch_initcall(parisc_idle_init); + +/* + * Copy architecture-specific thread state + */ +int +copy_thread(struct task_struct *p, const struct kernel_clone_args *args) +{ + unsigned long clone_flags = args->flags; + unsigned long usp = args->stack; + unsigned long tls = args->tls; + 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. + * Make them const so the compiler knows they live in .text */ + extern void * const ret_from_kernel_thread; + extern void * const child_return; + + if (unlikely(args->fn)) { + /* kernel thread */ + memset(cregs, 0, sizeof(struct pt_regs)); + if (args->idle) /* idle thread */ + return 0; + /* Must exit via ret_from_kernel_thread in order + * to call schedule_tail() + */ + cregs->ksp = (unsigned long) stack + FRAME_SIZE + PT_SZ_ALGN; + cregs->kpc = (unsigned long) &ret_from_kernel_thread; + /* + * Copy function and argument to be called from + * ret_from_kernel_thread. + */ +#ifdef CONFIG_64BIT + cregs->gr[27] = ((unsigned long *)args->fn)[3]; + cregs->gr[26] = ((unsigned long *)args->fn)[2]; +#else + cregs->gr[26] = (unsigned long) args->fn; +#endif + cregs->gr[25] = (unsigned long) args->fn_arg; + } else { + /* user thread */ + /* 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 + FRAME_SIZE; + cregs->kpc = (unsigned long) &child_return; + + /* Setup thread TLS area */ + if (clone_flags & CLONE_SETTLS) + cregs->cr27 = tls; + } + + return 0; +} + +unsigned long +__get_wchan(struct task_struct *p) +{ + struct unwind_frame_info info; + unsigned long ip; + int count = 0; + + /* + * These bracket the sleeping functions.. + */ + + unwind_frame_init_from_blocked_task(&info, p); + do { + if (unwind_once(&info) < 0) + return 0; + if (task_is_running(p)) + return 0; + ip = info.ip; + if (!in_sched_functions(ip)) + return ip; + } while (count++ < MAX_UNWIND_ENTRIES); + return 0; +} + +static inline unsigned long brk_rnd(void) +{ + return (get_random_u32() & BRK_RND_MASK) << 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 new file mode 100644 index 000000000..1f6c776d8 --- /dev/null +++ b/arch/parisc/kernel/processor.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Initial setup-routines for HP 9000 based hardware. + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * 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> + * Modifications copyright 2001 Ryan Bradetich <rbradetich@uswest.net> + * + * Initial PA-RISC Version: 04-23-1999 by Helge Deller + */ +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/cpu.h> +#include <asm/topology.h> +#include <asm/param.h> +#include <asm/cache.h> +#include <asm/hardware.h> /* for register_parisc_driver() stuff */ +#include <asm/processor.h> +#include <asm/page.h> +#include <asm/pdc.h> +#include <asm/pdcpat.h> +#include <asm/irq.h> /* for struct irq_region */ +#include <asm/parisc-device.h> + +struct system_cpuinfo_parisc boot_cpu_data __ro_after_init; +EXPORT_SYMBOL(boot_cpu_data); +#ifdef CONFIG_PA8X00 +int _parisc_requires_coherency __ro_after_init; +EXPORT_SYMBOL(_parisc_requires_coherency); +#endif + +DEFINE_PER_CPU(struct cpuinfo_parisc, cpu_data); + +/* +** PARISC CPU driver - claim "device" and initialize CPU data structures. +** +** Consolidate per CPU initialization into (mostly) one module. +** Monarch CPU will initialize boot_cpu_data which shouldn't +** change once the system has booted. +** +** The callback *should* do per-instance initialization of +** everything including the monarch. "Per CPU" init code in +** setup.c:start_parisc() has migrated here and start_parisc() +** 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 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) +{ +} + + +/** + * processor_probe - Determine if processor driver should claim this device. + * @dev: The device which has been found. + * + * Determine if processor driver should claim this chip (return 0) or not + * (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) +{ + unsigned long txn_addr; + unsigned long cpuid; + struct cpuinfo_parisc *p; + struct pdc_pat_cpu_num cpu_info = { }; + +#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; + } +#endif + + /* logical CPU ID and update global counter + * May get overwritten by PAT code. + */ + cpuid = boot_cpu_data.cpu_count; + txn_addr = dev->hpa.start; /* for legacy PDC */ + cpu_info.cpu_num = cpu_info.cpu_loc = cpuid; + +#ifdef CONFIG_64BIT + if (is_pdc_pat()) { + ulong status; + unsigned long bytecnt; + pdc_pat_cell_mod_maddr_block_t *pa_pdc_cell; + + 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); + + 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); + + txn_addr = pa_pdc_cell->mod[0]; /* id_eid for IO sapic */ + + kfree(pa_pdc_cell); + + /* get the cpu number */ + status = pdc_pat_cpu_get_number(&cpu_info, dev->hpa.start); + BUG_ON(PDC_OK != status); + + pr_info("Logical CPU #%lu is physical cpu #%lu at location " + "0x%lx with hpa %pa\n", + cpuid, cpu_info.cpu_num, cpu_info.cpu_loc, + &dev->hpa.start); + +#undef USE_PAT_CPUID +#ifdef USE_PAT_CPUID +/* We need contiguous numbers for cpuid. Firmware's notion + * of cpuid is for physical CPUs and we just don't care yet. + * We'll care when we need to query PAT PDC about a CPU *after* + * boot time (ie shutdown a CPU from an OS perspective). + */ + if (cpu_info.cpu_num >= NR_CPUS) { + printk(KERN_WARNING "IGNORING CPU at %pa," + " cpu_slot_id > NR_CPUS" + " (%ld > %d)\n", + &dev->hpa.start, cpu_info.cpu_num, NR_CPUS); + /* Ignore CPU since it will only crash */ + boot_cpu_data.cpu_count--; + return 1; + } else { + cpuid = cpu_info.cpu_num; + } +#endif + } +#endif + + p = &per_cpu(cpu_data, cpuid); + boot_cpu_data.cpu_count++; + + /* initialize counters - CPU 0 gets it_value set in time_init() */ + if (cpuid) + memset(p, 0, sizeof(struct cpuinfo_parisc)); + + p->dev = dev; /* Save IODC data in case we need it */ + p->hpa = dev->hpa.start; /* save CPU hpa */ + p->cpuid = cpuid; /* save CPU id */ + p->txn_addr = txn_addr; /* save CPU IRQ address */ + p->cpu_num = cpu_info.cpu_num; + p->cpu_loc = cpu_info.cpu_loc; + + set_cpu_possible(cpuid, true); + store_cpu_topology(cpuid); + +#ifdef CONFIG_SMP + /* + ** FIXME: review if any other initialization is clobbered + ** for boot_cpu by the above memset(). + */ + init_percpu_prof(cpuid); +#endif + + /* + ** 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; + */ + +#if 0 + /* CPU 0 IRQ table is statically allocated/initialized */ + if (cpuid) { + struct irqaction actions[]; + + /* + ** itimer and ipi IRQ handlers are statically initialized in + ** arch/parisc/kernel/irq.c. ie Don't need to register them. + */ + actions = kmalloc(sizeof(struct irqaction)*MAX_CPU_IRQ, GFP_ATOMIC); + if (!actions) { + /* not getting it's own table, share with monarch */ + actions = cpu_irq_actions[0]; + } + + cpu_irq_actions[cpuid] = actions; + } +#endif + + /* + * Bring this CPU up now! (ignore bootstrap cpuid == 0) + */ +#ifdef CONFIG_SMP + if (cpuid) { + set_cpu_present(cpuid, true); + add_cpu(cpuid); + } +#endif + + return 0; +} + +/** + * collect_boot_cpu_data - Fill the boot_cpu_data structure. + * + * This function collects and stores the generic processor information + * in the boot_cpu_data structure. + */ +void __init collect_boot_cpu_data(void) +{ + unsigned long cr16_seed; + char orig_prod_num[64], current_prod_num[64], serial_no[64]; + + memset(&boot_cpu_data, 0, sizeof(boot_cpu_data)); + + cr16_seed = get_cycles(); + add_device_randomness(&cr16_seed, sizeof(cr16_seed)); + + boot_cpu_data.cpu_hz = 100 * PAGE0->mem_10msec; /* Hz of this PARISC */ + + /* get CPU-Model Information... */ +#define p ((unsigned long *)&boot_cpu_data.pdc.model) + if (pdc_model_info(&boot_cpu_data.pdc.model) == PDC_OK) { + printk(KERN_INFO + "model %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); + + add_device_randomness(&boot_cpu_data.pdc.model, + sizeof(boot_cpu_data.pdc.model)); + } +#undef p + + if (pdc_model_versions(&boot_cpu_data.pdc.versions, 0) == PDC_OK) { + printk(KERN_INFO "vers %08lx\n", + boot_cpu_data.pdc.versions); + + add_device_randomness(&boot_cpu_data.pdc.versions, + sizeof(boot_cpu_data.pdc.versions)); + } + + if (pdc_model_cpuid(&boot_cpu_data.pdc.cpuid) == PDC_OK) { + printk(KERN_INFO "CPUID vers %ld rev %ld (0x%08lx)\n", + (boot_cpu_data.pdc.cpuid >> 5) & 127, + boot_cpu_data.pdc.cpuid & 31, + boot_cpu_data.pdc.cpuid); + + add_device_randomness(&boot_cpu_data.pdc.cpuid, + sizeof(boot_cpu_data.pdc.cpuid)); + } + + if (pdc_model_capabilities(&boot_cpu_data.pdc.capabilities) == PDC_OK) + printk(KERN_INFO "capabilities 0x%lx\n", + boot_cpu_data.pdc.capabilities); + + if (pdc_model_sysmodel(boot_cpu_data.pdc.sys_model_name) == PDC_OK) + printk(KERN_INFO "model %s\n", + boot_cpu_data.pdc.sys_model_name); + + dump_stack_set_arch_desc("%s", boot_cpu_data.pdc.sys_model_name); + + boot_cpu_data.hversion = boot_cpu_data.pdc.model.hversion; + boot_cpu_data.sversion = boot_cpu_data.pdc.model.sversion; + + boot_cpu_data.cpu_type = parisc_get_cpu_type(boot_cpu_data.hversion); + boot_cpu_data.cpu_name = cpu_name_version[boot_cpu_data.cpu_type][0]; + boot_cpu_data.family_name = cpu_name_version[boot_cpu_data.cpu_type][1]; + +#ifdef CONFIG_PA8X00 + _parisc_requires_coherency = (boot_cpu_data.cpu_type == mako) || + (boot_cpu_data.cpu_type == mako2); +#endif + + if (pdc_model_platform_info(orig_prod_num, current_prod_num, serial_no) == PDC_OK) { + printk(KERN_INFO "product %s, original product %s, S/N: %s\n", + current_prod_num[0] ? current_prod_num : "n/a", + orig_prod_num, serial_no); + add_device_randomness(orig_prod_num, strlen(orig_prod_num)); + add_device_randomness(current_prod_num, strlen(current_prod_num)); + add_device_randomness(serial_no, strlen(serial_no)); + } +} + + +/** + * init_per_cpu - Handle individual processor initializations. + * @cpunum: logical processor number. + * + * This function handles initialization for *every* CPU + * in the system: + * + * o Set "default" CPU width for trap handlers + * + * o Enable FP coprocessor + * REVISIT: this could be done in the "code 22" trap handler. + * (frowands idea - that way we know which processes need FP + * registers saved on the interrupt stack.) + * NEWS FLASH: wide kernels need FP coprocessor enabled to handle + * formatted printing of %lx for example (double divides I think) + * + * o Enable CPU profiling hooks. + */ +int init_per_cpu(int cpunum) +{ + int ret; + struct pdc_coproc_cfg coproc_cfg; + + set_firmware_width(); + ret = pdc_coproc_cfg(&coproc_cfg); + + if(ret >= 0 && coproc_cfg.ccr_functional) { + mtctl(coproc_cfg.ccr_functional, 10); /* 10 == Coprocessor Control Reg */ + + /* FWIW, FP rev/model is a more accurate way to determine + ** CPU type. CPU rev/model has some ambiguous cases. + */ + per_cpu(cpu_data, cpunum).fp_rev = coproc_cfg.revision; + per_cpu(cpu_data, cpunum).fp_model = coproc_cfg.model; + + if (cpunum == 0) + printk(KERN_INFO "FP[%d] enabled: Rev %ld Model %ld\n", + cpunum, coproc_cfg.revision, coproc_cfg.model); + + /* + ** store status register to stack (hopefully aligned) + ** and clear the T-bit. + */ + asm volatile ("fstd %fr0,8(%sp)"); + + } else { + printk(KERN_WARNING "WARNING: No FP CoProcessor?!" + " (coproc_cfg.ccr_functional == 0x%lx, expected 0xc0)\n" +#ifdef CONFIG_64BIT + "Halting Machine - FP required\n" +#endif + , coproc_cfg.ccr_functional); +#ifdef CONFIG_64BIT + mdelay(100); /* previous chars get pushed to console */ + panic("FP CoProc not reported"); +#endif + } + + /* FUTURE: Enable Performance Monitor : ccr bit 0x20 */ + init_percpu_prof(cpunum); + + return ret; +} + +/* + * Display CPU info for all CPUs. + */ +int +show_cpuinfo (struct seq_file *m, void *v) +{ + unsigned long cpu; + char cpu_name[60], *p; + + /* strip PA path from CPU name to not confuse lscpu */ + strlcpy(cpu_name, per_cpu(cpu_data, 0).dev->name, sizeof(cpu_name)); + p = strrchr(cpu_name, '['); + if (p) + *(--p) = 0; + + for_each_online_cpu(cpu) { +#ifdef CONFIG_SMP + const struct cpuinfo_parisc *cpuinfo = &per_cpu(cpu_data, cpu); + + if (0 == cpuinfo->hpa) + continue; +#endif + seq_printf(m, "processor\t: %lu\n" + "cpu family\t: PA-RISC %s\n", + cpu, boot_cpu_data.family_name); + + seq_printf(m, "cpu\t\t: %s\n", boot_cpu_data.cpu_name ); + + /* cpu MHz */ + seq_printf(m, "cpu MHz\t\t: %d.%06d\n", + boot_cpu_data.cpu_hz / 1000000, + boot_cpu_data.cpu_hz % 1000000 ); + +#ifdef CONFIG_GENERIC_ARCH_TOPOLOGY + seq_printf(m, "physical id\t: %d\n", + topology_physical_package_id(cpu)); + seq_printf(m, "siblings\t: %d\n", + cpumask_weight(topology_core_cpumask(cpu))); + seq_printf(m, "core id\t\t: %d\n", topology_core_id(cpu)); +#endif + + 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 - %s\n", + boot_cpu_data.pdc.sys_model_name, + cpu_name); + + seq_printf(m, "hversion\t: 0x%08x\n" + "sversion\t: 0x%08x\n", + boot_cpu_data.hversion, + boot_cpu_data.sversion ); + + /* print cachesize info */ + show_cache_info(m); + + seq_printf(m, "bogomips\t: %lu.%02lu\n", + loops_per_jiffy / (500000 / HZ), + loops_per_jiffy / (5000 / HZ) % 100); + + seq_printf(m, "software id\t: %ld\n\n", + boot_cpu_data.pdc.model.sw_id); + } + return 0; +} + +static const struct parisc_device_id processor_tbl[] __initconst = { + { HPHW_NPROC, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, SVERSION_ANY_ID }, + { 0, } +}; + +static struct parisc_driver cpu_driver __refdata = { + .name = "CPU", + .id_table = processor_tbl, + .probe = processor_probe +}; + +/** + * processor_init - Processor initialization procedure. + * + * Register this driver. + */ +void __init processor_init(void) +{ + unsigned int cpu; + + reset_cpu_topology(); + + /* reset possible mask. We will mark those which are possible. */ + for_each_possible_cpu(cpu) + set_cpu_possible(cpu, false); + + register_parisc_driver(&cpu_driver); +} diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c new file mode 100644 index 000000000..9af11abee --- /dev/null +++ b/arch/parisc/kernel/ptrace.c @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kernel support for the ptrace() and syscall tracing interfaces. + * + * 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-2016 Helge Deller <deller@gmx.de> + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/elf.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/personality.h> +#include <linux/regset.h> +#include <linux/security.h> +#include <linux/seccomp.h> +#include <linux/compat.h> +#include <linux/signal.h> +#include <linux/audit.h> + +#include <linux/uaccess.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_B | PSW_V | PSW_CB) + +#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + +/* + * These are our native regset flavors. + */ +enum parisc_regset { + REGSET_GENERAL, + REGSET_FP +}; + +/* + * 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) { + /* 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. */ + force_sig_fault_to_task(SIGTRAP, TRAP_TRACE, + (void __user *) (task_regs(task)->iaoq[0] & ~3), + 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 __user *datap = (unsigned long __user *)data; + unsigned long tmp; + long ret = -EIO; + + unsigned long user_regs_struct_size = sizeof(struct user_regs_struct); +#ifdef CONFIG_64BIT + if (is_compat_task()) + user_regs_struct_size /= 2; +#endif + + 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, datap); + 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_IAOQ0 || addr == PT_IAOQ1) { + data |= PRIV_USER; /* ensure userspace privilege */ + } + 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; + + case PTRACE_GETREGS: /* Get all gp regs from the child. */ + return copy_regset_to_user(child, + task_user_regset_view(current), + REGSET_GENERAL, + 0, user_regs_struct_size, + datap); + + case PTRACE_SETREGS: /* Set all gp regs in the child. */ + return copy_regset_from_user(child, + task_user_regset_view(current), + REGSET_GENERAL, + 0, user_regs_struct_size, + datap); + + case PTRACE_GETFPREGS: /* Get the child FPU state. */ + return copy_regset_to_user(child, + task_user_regset_view(current), + REGSET_FP, + 0, sizeof(struct user_fp_struct), + datap); + + case PTRACE_SETFPREGS: /* Set the child FPU state. */ + return copy_regset_from_user(child, + task_user_regset_view(current), + REGSET_FP, + 0, sizeof(struct user_fp_struct), + datap); + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + + return ret; +} + + +#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 + * will request offset 12 if it wants gr3, but the lower 32 bits of + * the 64 bit kernels view of gr3 will be at offset 28 (3*8 + 4). + * This code relies on a 32 bit pt_regs being comprised of 32 bit values + * except for the fp registers which (a) are 64 bits, and (b) follow + * the gr registers at the start of pt_regs. The 32 bit pt_regs should + * be half the size of the 64 bit pt_regs, plus 32*4 to allow for fr[] + * being 64 bit in both cases. + */ + +static compat_ulong_t translate_usr_offset(compat_ulong_t offset) +{ + compat_ulong_t pos; + + if (offset < 32*4) /* gr[0..31] */ + pos = offset * 2 + 4; + else if (offset < 32*4+32*8) /* fr[0] ... fr[31] */ + pos = (offset - 32*4) + PT_FR0; + else if (offset < sizeof(struct pt_regs)/2 + 32*4) /* sr[0] ... ipsw */ + pos = (offset - 32*4 - 32*8) * 2 + PT_SR0 + 4; + else + pos = sizeof(struct pt_regs); + + return pos; +} + +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t addr, compat_ulong_t data) +{ + compat_uint_t tmp; + long ret = -EIO; + + switch (request) { + + case PTRACE_PEEKUSR: + if (addr & (sizeof(compat_uint_t)-1)) + break; + addr = translate_usr_offset(addr); + if (addr >= sizeof(struct pt_regs)) + break; + + 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. + 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) { + /* Since PT_PSW==0, it is valid for 32 bit processes + * under 64 bit kernels as well. + */ + 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_IAOQ0+4 || addr == PT_IAOQ1+4) { + data |= PRIV_USER; /* ensure userspace privilege */ + } + if (addr >= PT_FR0 && addr <= PT_FR31 + 4) { + /* Special case, fp regs are 64 bits anyway */ + *(__u32 *) ((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 */ + *(__u32 *) ((char *) task_regs(child) + addr - 4) = 0; + *(__u32 *) ((char *) task_regs(child) + addr) = data; + ret = 0; + } + } + break; + case PTRACE_GETREGS: + case PTRACE_SETREGS: + case PTRACE_GETFPREGS: + case PTRACE_SETFPREGS: + return arch_ptrace(child, request, addr, data); + + default: + ret = compat_ptrace_request(child, request, addr, data); + break; + } + + return ret; +} +#endif + +long do_syscall_trace_enter(struct pt_regs *regs) +{ + if (test_thread_flag(TIF_SYSCALL_TRACE)) { + int rc = ptrace_report_syscall_entry(regs); + + /* + * As tracesys_next does not set %r28 to -ENOSYS + * when %r20 is set to -1, initialize it here. + */ + regs->gr[28] = -ENOSYS; + + if (rc) { + /* + * A nonzero return code from + * ptrace_report_syscall_entry() tells us + * to prevent the syscall execution. Skip + * the syscall call and the syscall restart handling. + * + * Note that the tracer may also just change + * regs->gr[20] to an invalid syscall number, + * that is handled by tracesys_next. + */ + regs->gr[20] = -1UL; + return -1; + } + } + + /* Do the secure computing check after ptrace. */ + if (secure_computing() == -1) + return -1; + +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) + trace_sys_enter(regs, regs->gr[20]); +#endif + +#ifdef CONFIG_64BIT + if (!is_compat_task()) + audit_syscall_entry(regs->gr[20], regs->gr[26], regs->gr[25], + regs->gr[24], regs->gr[23]); + else +#endif + audit_syscall_entry(regs->gr[20] & 0xffffffff, + regs->gr[26] & 0xffffffff, + regs->gr[25] & 0xffffffff, + regs->gr[24] & 0xffffffff, + regs->gr[23] & 0xffffffff); + + /* + * Sign extend the syscall number to 64bit since it may have been + * modified by a compat ptrace call + */ + return (int) ((u32) 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); + +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) + trace_sys_exit(regs, regs->gr[20]); +#endif + + if (stepping || test_thread_flag(TIF_SYSCALL_TRACE)) + ptrace_report_syscall_exit(regs, stepping); +} + + +/* + * regset functions. + */ + +static int fpr_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct pt_regs *regs = task_regs(target); + + return membuf_write(&to, regs->fr, ELF_NFPREG * sizeof(__u64)); +} + +static int fpr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_regs(target); + const __u64 *k = kbuf; + const __u64 __user *u = ubuf; + __u64 reg; + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) + for (; count > 0 && pos < ELF_NFPREG; --count) + regs->fr[pos++] = *k++; + else + for (; count > 0 && pos < ELF_NFPREG; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + regs->fr[pos++] = reg; + } + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + ELF_NFPREG * sizeof(reg), -1); +} + +#define RI(reg) (offsetof(struct user_regs_struct,reg) / sizeof(long)) + +static unsigned long get_reg(struct pt_regs *regs, int num) +{ + switch (num) { + case RI(gr[0]) ... RI(gr[31]): return regs->gr[num - RI(gr[0])]; + case RI(sr[0]) ... RI(sr[7]): return regs->sr[num - RI(sr[0])]; + case RI(iasq[0]): return regs->iasq[0]; + case RI(iasq[1]): return regs->iasq[1]; + case RI(iaoq[0]): return regs->iaoq[0]; + case RI(iaoq[1]): return regs->iaoq[1]; + case RI(sar): return regs->sar; + case RI(iir): return regs->iir; + case RI(isr): return regs->isr; + case RI(ior): return regs->ior; + case RI(ipsw): return regs->ipsw; + case RI(cr27): return regs->cr27; + case RI(cr0): return mfctl(0); + case RI(cr24): return mfctl(24); + case RI(cr25): return mfctl(25); + case RI(cr26): return mfctl(26); + case RI(cr28): return mfctl(28); + case RI(cr29): return mfctl(29); + case RI(cr30): return mfctl(30); + case RI(cr31): return mfctl(31); + case RI(cr8): return mfctl(8); + case RI(cr9): return mfctl(9); + case RI(cr12): return mfctl(12); + case RI(cr13): return mfctl(13); + case RI(cr10): return mfctl(10); + case RI(cr15): return mfctl(15); + default: return 0; + } +} + +static void set_reg(struct pt_regs *regs, int num, unsigned long val) +{ + switch (num) { + case RI(gr[0]): /* + * PSW is in gr[0]. + * 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. + */ + val &= USER_PSW_BITS; + regs->gr[0] &= ~USER_PSW_BITS; + regs->gr[0] |= val; + return; + case RI(gr[1]) ... RI(gr[31]): + regs->gr[num - RI(gr[0])] = val; + return; + case RI(iaoq[0]): + case RI(iaoq[1]): + /* set 2 lowest bits to ensure userspace privilege: */ + regs->iaoq[num - RI(iaoq[0])] = val | PRIV_USER; + return; + case RI(sar): regs->sar = val; + return; + default: return; +#if 0 + /* do not allow to change any of the following registers (yet) */ + case RI(sr[0]) ... RI(sr[7]): return regs->sr[num - RI(sr[0])]; + case RI(iasq[0]): return regs->iasq[0]; + case RI(iasq[1]): return regs->iasq[1]; + case RI(iir): return regs->iir; + case RI(isr): return regs->isr; + case RI(ior): return regs->ior; + case RI(ipsw): return regs->ipsw; + case RI(cr27): return regs->cr27; + case cr0, cr24, cr25, cr26, cr27, cr28, cr29, cr30, cr31; + case cr8, cr9, cr12, cr13, cr10, cr15; +#endif + } +} + +static int gpr_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct pt_regs *regs = task_regs(target); + unsigned int pos; + + for (pos = 0; pos < ELF_NGREG; pos++) + membuf_store(&to, get_reg(regs, pos)); + return 0; +} + +static int gpr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_regs(target); + const unsigned long *k = kbuf; + const unsigned long __user *u = ubuf; + unsigned long reg; + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) + for (; count > 0 && pos < ELF_NGREG; --count) + set_reg(regs, pos++, *k++); + else + for (; count > 0 && pos < ELF_NGREG; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + set_reg(regs, pos++, reg); + } + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + ELF_NGREG * sizeof(reg), -1); +} + +static const struct user_regset native_regsets[] = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(long), .align = sizeof(long), + .regset_get = gpr_get, .set = gpr_set + }, + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(__u64), .align = sizeof(__u64), + .regset_get = fpr_get, .set = fpr_set + } +}; + +static const struct user_regset_view user_parisc_native_view = { + .name = "parisc", .e_machine = ELF_ARCH, .ei_osabi = ELFOSABI_LINUX, + .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) +}; + +#ifdef CONFIG_64BIT +static int gpr32_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct pt_regs *regs = task_regs(target); + unsigned int pos; + + for (pos = 0; pos < ELF_NGREG; pos++) + membuf_store(&to, (compat_ulong_t)get_reg(regs, pos)); + + return 0; +} + +static int gpr32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_regs(target); + const compat_ulong_t *k = kbuf; + const compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) + for (; count > 0 && pos < ELF_NGREG; --count) + set_reg(regs, pos++, *k++); + else + for (; count > 0 && pos < ELF_NGREG; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + set_reg(regs, pos++, reg); + } + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + ELF_NGREG * sizeof(reg), -1); +} + +/* + * These are the regset flavors matching the 32bit native set. + */ +static const struct user_regset compat_regsets[] = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), + .regset_get = gpr32_get, .set = gpr32_set + }, + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(__u64), .align = sizeof(__u64), + .regset_get = fpr_get, .set = fpr_set + } +}; + +static const struct user_regset_view user_parisc_compat_view = { + .name = "parisc", .e_machine = EM_PARISC, .ei_osabi = ELFOSABI_LINUX, + .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets) +}; +#endif /* CONFIG_64BIT */ + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + BUILD_BUG_ON(sizeof(struct user_regs_struct)/sizeof(long) != ELF_NGREG); + BUILD_BUG_ON(sizeof(struct user_fp_struct)/sizeof(__u64) != ELF_NFPREG); +#ifdef CONFIG_64BIT + if (is_compat_task()) + return &user_parisc_compat_view; +#endif + return &user_parisc_native_view; +} + + +/* HAVE_REGS_AND_STACK_ACCESS_API feature */ + +struct pt_regs_offset { + const char *name; + int offset; +}; + +#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)} +#define REG_OFFSET_INDEX(r,i) {.name = #r#i, .offset = offsetof(struct pt_regs, r[i])} +#define REG_OFFSET_END {.name = NULL, .offset = 0} + +static const struct pt_regs_offset regoffset_table[] = { + REG_OFFSET_INDEX(gr,0), + REG_OFFSET_INDEX(gr,1), + REG_OFFSET_INDEX(gr,2), + REG_OFFSET_INDEX(gr,3), + REG_OFFSET_INDEX(gr,4), + REG_OFFSET_INDEX(gr,5), + REG_OFFSET_INDEX(gr,6), + REG_OFFSET_INDEX(gr,7), + REG_OFFSET_INDEX(gr,8), + REG_OFFSET_INDEX(gr,9), + REG_OFFSET_INDEX(gr,10), + REG_OFFSET_INDEX(gr,11), + REG_OFFSET_INDEX(gr,12), + REG_OFFSET_INDEX(gr,13), + REG_OFFSET_INDEX(gr,14), + REG_OFFSET_INDEX(gr,15), + REG_OFFSET_INDEX(gr,16), + REG_OFFSET_INDEX(gr,17), + REG_OFFSET_INDEX(gr,18), + REG_OFFSET_INDEX(gr,19), + REG_OFFSET_INDEX(gr,20), + REG_OFFSET_INDEX(gr,21), + REG_OFFSET_INDEX(gr,22), + REG_OFFSET_INDEX(gr,23), + REG_OFFSET_INDEX(gr,24), + REG_OFFSET_INDEX(gr,25), + REG_OFFSET_INDEX(gr,26), + REG_OFFSET_INDEX(gr,27), + REG_OFFSET_INDEX(gr,28), + REG_OFFSET_INDEX(gr,29), + REG_OFFSET_INDEX(gr,30), + REG_OFFSET_INDEX(gr,31), + REG_OFFSET_INDEX(sr,0), + REG_OFFSET_INDEX(sr,1), + REG_OFFSET_INDEX(sr,2), + REG_OFFSET_INDEX(sr,3), + REG_OFFSET_INDEX(sr,4), + REG_OFFSET_INDEX(sr,5), + REG_OFFSET_INDEX(sr,6), + REG_OFFSET_INDEX(sr,7), + REG_OFFSET_INDEX(iasq,0), + REG_OFFSET_INDEX(iasq,1), + REG_OFFSET_INDEX(iaoq,0), + REG_OFFSET_INDEX(iaoq,1), + REG_OFFSET_NAME(cr27), + REG_OFFSET_NAME(ksp), + REG_OFFSET_NAME(kpc), + REG_OFFSET_NAME(sar), + REG_OFFSET_NAME(iir), + REG_OFFSET_NAME(isr), + REG_OFFSET_NAME(ior), + REG_OFFSET_NAME(ipsw), + REG_OFFSET_END, +}; + +/** + * regs_query_register_offset() - query register offset from its name + * @name: the name of a register + * + * regs_query_register_offset() returns the offset of a register in struct + * pt_regs from its name. If the name is invalid, this returns -EINVAL; + */ +int regs_query_register_offset(const char *name) +{ + const struct pt_regs_offset *roff; + for (roff = regoffset_table; roff->name != NULL; roff++) + if (!strcmp(roff->name, name)) + return roff->offset; + return -EINVAL; +} + +/** + * regs_query_register_name() - query register name from its offset + * @offset: the offset of a register in struct pt_regs. + * + * regs_query_register_name() returns the name of a register from its + * offset in struct pt_regs. If the @offset is invalid, this returns NULL; + */ +const char *regs_query_register_name(unsigned int offset) +{ + const struct pt_regs_offset *roff; + for (roff = regoffset_table; roff->name != NULL; roff++) + if (roff->offset == offset) + return roff->name; + return NULL; +} + +/** + * regs_within_kernel_stack() - check the address in the stack + * @regs: pt_regs which contains kernel stack pointer. + * @addr: address which is checked. + * + * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). + * If @addr is within the kernel stack, it returns true. If not, returns false. + */ +int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) +{ + return ((addr & ~(THREAD_SIZE - 1)) == + (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))); +} + +/** + * regs_get_kernel_stack_nth() - get Nth entry of the stack + * @regs: pt_regs which contains kernel stack pointer. + * @n: stack entry number. + * + * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which + * is specified by @regs. If the @n th entry is NOT in the kernel stack, + * this returns 0. + */ +unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) +{ + unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs); + + addr -= n; + + if (!regs_within_kernel_stack(regs, (unsigned long)addr)) + return 0; + + return *addr; +} diff --git a/arch/parisc/kernel/real2.S b/arch/parisc/kernel/real2.S new file mode 100644 index 000000000..c37010a13 --- /dev/null +++ b/arch/parisc/kernel/real2.S @@ -0,0 +1,305 @@ +/* + * + * 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) 2000 Hewlett Packard (Paul Bame bame@puffin.external.hp.com) + * + */ + +#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 + .align 64 +real_stack: +real32_stack: +real64_stack: + .block 8192 + +#define N_SAVED_REGS 9 + +save_cr_space: + .block REG_SZ * N_SAVED_REGS +save_cr_end: + + +/************************ 32-bit real-mode calls ***********************/ +/* This can be called in both narrow and wide kernels */ + + .text + + /* unsigned long real32_call_asm(unsigned int *sp, + * unsigned int *arg0p, + * unsigned int iodc_fn) + * sp is value of stack pointer to adopt before calling PDC (virt) + * arg0p points to where saved arg values may be found + * iodc_fn is the IODC function to call + */ + +ENTRY_CFI(real32_call_asm) + STREG %rp, -RP_OFFSET(%sp) /* save RP */ +#ifdef CONFIG_64BIT + callee_save + ldo 2*REG_SZ(%sp), %sp /* room for a couple more saves */ + STREG %r27, -1*REG_SZ(%sp) + STREG %r29, -2*REG_SZ(%sp) +#endif + STREG %sp, -REG_SZ(%arg0) /* save SP on real-mode stack */ + copy %arg0, %sp /* adopt the real-mode SP */ + + /* save iodc_fn */ + copy %arg2, %r31 + + /* load up the arg registers from the saved arg area */ + /* 32-bit calling convention passes first 4 args in registers */ + ldw 0(%arg1), %arg0 /* note overwriting arg0 */ + ldw -8(%arg1), %arg2 + ldw -12(%arg1), %arg3 + ldw -4(%arg1), %arg1 /* obviously must do this one last! */ + + tophys_r1 %sp + + b,l rfi_virt2real,%r2 + nop + + b,l save_control_regs,%r2 /* modifies r1, r2, r28 */ + nop + +#ifdef CONFIG_64BIT + rsm PSW_SM_W, %r0 /* go narrow */ +#endif + + load32 PA(ric_ret), %r2 + bv 0(%r31) + nop +ric_ret: +#ifdef CONFIG_64BIT + ssm PSW_SM_W, %r0 /* go wide */ +#endif + /* restore CRs before going virtual in case we page fault */ + b,l restore_control_regs, %r2 /* modifies r1, r2, r26 */ + nop + + b,l rfi_real2virt,%r2 + nop + + tovirt_r1 %sp + LDREG -REG_SZ(%sp), %sp /* restore SP */ +#ifdef CONFIG_64BIT + LDREG -1*REG_SZ(%sp), %r27 + LDREG -2*REG_SZ(%sp), %r29 + ldo -2*REG_SZ(%sp), %sp + callee_rest +#endif + LDREG -RP_OFFSET(%sp), %rp /* restore RP */ + bv 0(%rp) + nop +ENDPROC_CFI(real32_call_asm) + + +# define PUSH_CR(r, where) mfctl r, %r1 ! STREG,ma %r1, REG_SZ(where) +# define POP_CR(r, where) LDREG,mb -REG_SZ(where), %r1 ! mtctl %r1, r + + .text +ENTRY_CFI(save_control_regs) + load32 PA(save_cr_space), %r28 + PUSH_CR(%cr24, %r28) + PUSH_CR(%cr25, %r28) + PUSH_CR(%cr26, %r28) + PUSH_CR(%cr27, %r28) + PUSH_CR(%cr28, %r28) + PUSH_CR(%cr29, %r28) + PUSH_CR(%cr30, %r28) + PUSH_CR(%cr31, %r28) + PUSH_CR(%cr15, %r28) + bv 0(%r2) + nop +ENDPROC_CFI(save_control_regs) + +ENTRY_CFI(restore_control_regs) + load32 PA(save_cr_end), %r26 + POP_CR(%cr15, %r26) + POP_CR(%cr31, %r26) + POP_CR(%cr30, %r26) + POP_CR(%cr29, %r26) + POP_CR(%cr28, %r26) + POP_CR(%cr27, %r26) + POP_CR(%cr26, %r26) + POP_CR(%cr25, %r26) + POP_CR(%cr24, %r26) + bv 0(%r2) + nop +ENDPROC_CFI(restore_control_regs) + +/* rfi_virt2real() and rfi_real2virt() could perhaps be adapted for + * more general-purpose use by the several places which need RFIs + */ + .text + .align 128 +ENTRY_CFI(rfi_virt2real) +#if !defined(BOOTLOADER) + /* switch to real mode... */ + rsm PSW_SM_I,%r0 + load32 PA(rfi_v2r_1), %r1 + nop + nop + nop + nop + nop + + rsm PSW_SM_Q,%r0 /* disable Q & I bits to load iia queue */ + mtctl %r0, %cr17 /* Clear IIASQ tail */ + mtctl %r0, %cr17 /* Clear IIASQ head */ + mtctl %r1, %cr18 /* IIAOQ head */ + ldo 4(%r1), %r1 + mtctl %r1, %cr18 /* IIAOQ tail */ + load32 REAL_MODE_PSW, %r1 + mtctl %r1, %cr22 + rfi + + nop + nop + nop + nop + nop + nop + nop + nop +rfi_v2r_1: + tophys_r1 %r2 +#endif /* defined(BOOTLOADER) */ + bv 0(%r2) + nop +ENDPROC_CFI(rfi_virt2real) + + .text + .align 128 +ENTRY_CFI(rfi_real2virt) +#if !defined(BOOTLOADER) + rsm PSW_SM_I,%r0 + load32 (rfi_r2v_1), %r1 + nop + nop + nop + nop + nop + + rsm PSW_SM_Q,%r0 /* disable Q bit to load iia queue */ + mtctl %r0, %cr17 /* Clear IIASQ tail */ + mtctl %r0, %cr17 /* Clear IIASQ head */ + mtctl %r1, %cr18 /* IIAOQ head */ + ldo 4(%r1), %r1 + mtctl %r1, %cr18 /* IIAOQ tail */ + load32 KERNEL_PSW, %r1 + mtctl %r1, %cr22 + rfi + + nop + nop + nop + nop + nop + nop + nop + nop +rfi_r2v_1: + tovirt_r1 %r2 +#endif /* defined(BOOTLOADER) */ + bv 0(%r2) + nop +ENDPROC_CFI(rfi_real2virt) + +#ifdef CONFIG_64BIT + +/************************ 64-bit real-mode calls ***********************/ +/* This is only usable in wide kernels right now and will probably stay so */ + .text + /* unsigned long real64_call_asm(unsigned long *sp, + * unsigned long *arg0p, + * unsigned long fn) + * sp is value of stack pointer to adopt before calling PDC (virt) + * arg0p points to where saved arg values may be found + * iodc_fn is the IODC function to call + */ +ENTRY_CFI(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 */ + + /* save fn */ + copy %arg2, %r31 + + /* load up the arg registers from the saved arg area */ + /* 32-bit calling convention passes first 4 args in registers */ + ldd 0*REG_SZ(%arg1), %arg0 /* note overwriting arg0 */ + ldd 2*REG_SZ(%arg1), %arg2 + ldd 3*REG_SZ(%arg1), %arg3 + ldd 4*REG_SZ(%arg1), %r22 + ldd 5*REG_SZ(%arg1), %r21 + ldd 6*REG_SZ(%arg1), %r20 + ldd 7*REG_SZ(%arg1), %r19 + ldd 1*REG_SZ(%arg1), %arg1 /* do this one last! */ + + /* set up real-mode stack and real-mode ap */ + tophys_r1 %sp + ldo -16(%sp), %r29 /* Reference param save area */ + + b,l rfi_virt2real,%r2 + nop + + b,l save_control_regs,%r2 /* modifies r1, r2, r28 */ + nop + + load32 PA(r64_ret), %r2 + bv 0(%r31) + nop +r64_ret: + /* restore CRs before going virtual in case we page fault */ + b,l restore_control_regs, %r2 /* modifies r1, r2, r26 */ + nop + + b,l rfi_real2virt,%r2 + nop + + tovirt_r1 %sp + ldd -8(%sp), %sp /* restore SP */ + ldd -0x10(%sp), %rp /* restore RP */ + bv 0(%rp) + nop +ENDPROC_CFI(real64_call_asm) + +#endif + + .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. + */ +ENTRY_CFI(__canonicalize_funcptr_for_compare) +#ifdef CONFIG_64BIT + bve (%r2) +#else + bv %r0(%r2) +#endif + copy %r26,%r28 +ENDPROC_CFI(__canonicalize_funcptr_for_compare) + diff --git a/arch/parisc/kernel/relocate_kernel.S b/arch/parisc/kernel/relocate_kernel.S new file mode 100644 index 000000000..2561e52b8 --- /dev/null +++ b/arch/parisc/kernel/relocate_kernel.S @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/linkage.h> +#include <linux/kexec.h> + +#include <asm/assembly.h> +#include <asm/asm-offsets.h> +#include <asm/page.h> +#include <asm/setup.h> +#include <asm/psw.h> + +.level PA_ASM_LEVEL + +.macro kexec_param name +.align 8 +ENTRY(kexec\()_\name) +#ifdef CONFIG_64BIT + .dword 0 +#else + .word 0 +#endif + +ENTRY(kexec\()_\name\()_offset) + .word kexec\()_\name - relocate_new_kernel +.endm + +.text + +/* args: + * r26 - kimage->head + * r25 - start address of kernel + * r24 - physical address of relocate code + */ + +ENTRY_CFI(relocate_new_kernel) +0: copy %arg1, %rp + /* disable I and Q bit, so we are allowed to execute RFI */ + rsm PSW_SM_I, %r0 + nop + nop + nop + nop + nop + nop + nop + + rsm PSW_SM_Q, %r0 + nop + nop + nop + nop + nop + nop + nop + + /* + * After return-from-interrupt, we want to run without Code/Data + * translation enabled just like on a normal boot. + */ + + /* calculate new physical execution address */ + ldo 1f-0b(%arg2), %r1 + mtctl %r0, %cr17 /* IIASQ */ + mtctl %r0, %cr17 /* IIASQ */ + mtctl %r1, %cr18 /* IIAOQ */ + ldo 4(%r1),%r1 + mtctl %r1, %cr18 /* IIAOQ */ +#ifdef CONFIG_64BIT + depdi,z 1, PSW_W_BIT, 1, %r1 + mtctl %r1, %cr22 /* IPSW */ +#else + mtctl %r0, %cr22 /* IPSW */ +#endif + /* lets go... */ + rfi +1: nop + nop + +.Lloop: + LDREG,ma REG_SZ(%arg0), %r3 + /* If crash kernel, no copy needed */ + cmpib,COND(=),n 0,%r3,boot + + bb,<,n %r3, 31 - IND_DONE_BIT, boot + bb,>=,n %r3, 31 - IND_INDIRECTION_BIT, .Lnotind + /* indirection, load and restart */ + movb %r3, %arg0, .Lloop + depi 0, 31, PAGE_SHIFT, %arg0 + +.Lnotind: + bb,>=,n %r3, 31 - IND_DESTINATION_BIT, .Lnotdest + b .Lloop + copy %r3, %r20 + +.Lnotdest: + bb,>= %r3, 31 - IND_SOURCE_BIT, .Lloop + depi 0, 31, PAGE_SHIFT, %r3 + copy %r3, %r21 + + /* copy page */ + copy %r0, %r18 + zdepi 1, 31 - PAGE_SHIFT, 1, %r18 + add %r20, %r18, %r17 + + depi 0, 31, PAGE_SHIFT, %r20 +.Lcopy: + copy %r20, %r12 + LDREG,ma REG_SZ(%r21), %r8 + LDREG,ma REG_SZ(%r21), %r9 + LDREG,ma REG_SZ(%r21), %r10 + LDREG,ma REG_SZ(%r21), %r11 + STREG,ma %r8, REG_SZ(%r20) + STREG,ma %r9, REG_SZ(%r20) + STREG,ma %r10, REG_SZ(%r20) + STREG,ma %r11, REG_SZ(%r20) + +#ifndef CONFIG_64BIT + LDREG,ma REG_SZ(%r21), %r8 + LDREG,ma REG_SZ(%r21), %r9 + LDREG,ma REG_SZ(%r21), %r10 + LDREG,ma REG_SZ(%r21), %r11 + STREG,ma %r8, REG_SZ(%r20) + STREG,ma %r9, REG_SZ(%r20) + STREG,ma %r10, REG_SZ(%r20) + STREG,ma %r11, REG_SZ(%r20) +#endif + + fdc %r0(%r12) + cmpb,COND(<<) %r20,%r17,.Lcopy + fic (%sr4, %r12) + b,n .Lloop + +boot: + mtctl %r0, %cr15 + + LDREG kexec_free_mem-0b(%arg2), %arg0 + LDREG kexec_cmdline-0b(%arg2), %arg1 + LDREG kexec_initrd_end-0b(%arg2), %arg3 + LDREG kexec_initrd_start-0b(%arg2), %arg2 + bv,n %r0(%rp) + +ENDPROC_CFI(relocate_new_kernel); + +ENTRY(relocate_new_kernel_size) + .word relocate_new_kernel_size - relocate_new_kernel + +kexec_param cmdline +kexec_param initrd_start +kexec_param initrd_end +kexec_param free_mem diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c new file mode 100644 index 000000000..375f38d6e --- /dev/null +++ b/arch/parisc/kernel/setup.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 copyright 1999 SuSE GmbH (Philipp Rumpf) + * Modifications copyright 2000 Martin K. Petersen <mkp@mkp.net> + * Modifications copyright 2000 Philipp Rumpf <prumpf@tux.org> + * Modifications copyright 2001 Ryan Bradetich <rbradetich@uswest.net> + * + * Initial PA-RISC Version: 04-23-1999 by Helge Deller + */ + +#include <linux/kernel.h> +#include <linux/initrd.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/seq_file.h> +#define PCI_DEBUG +#include <linux/pci.h> +#undef PCI_DEBUG +#include <linux/proc_fs.h> +#include <linux/export.h> +#include <linux/sched.h> +#include <linux/sched/clock.h> +#include <linux/start_kernel.h> + +#include <asm/cacheflush.h> +#include <asm/processor.h> +#include <asm/sections.h> +#include <asm/pdc.h> +#include <asm/led.h> +#include <asm/machdep.h> /* for pa7300lc_init() proto */ +#include <asm/pdc_chassis.h> +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/unwind.h> +#include <asm/smp.h> + +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 __read_mostly = NULL; +struct proc_dir_entry * proc_gsc_root __read_mostly = NULL; +struct proc_dir_entry * proc_mckinley_root __read_mostly = NULL; + +void __init setup_cmdline(char **cmdline_p) +{ + extern unsigned int boot_args[]; + char *p; + + /* Collect stuff passed in from the boot loader */ + + /* 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 */ + boot_command_line[0] = '\0'; + } else { + strscpy(boot_command_line, (char *)__va(boot_args[1]), + COMMAND_LINE_SIZE); + + /* autodetect console type (if not done by palo yet) */ + p = boot_command_line; + if (!str_has_prefix(p, "console=") && !strstr(p, " console=")) { + strlcat(p, " console=", COMMAND_LINE_SIZE); + if (PAGE0->mem_cons.cl_class == CL_DUPLEX) + strlcat(p, "ttyS0", COMMAND_LINE_SIZE); + else + strlcat(p, "tty0", COMMAND_LINE_SIZE); + } + + /* default to use early console */ + if (!strstr(p, "earlycon")) + strlcat(p, " earlycon=pdc", COMMAND_LINE_SIZE); + +#ifdef CONFIG_BLK_DEV_INITRD + if (boot_args[2] != 0) /* did palo pass us a ramdisk? */ + { + initrd_start = (unsigned long)__va(boot_args[2]); + initrd_end = (unsigned long)__va(boot_args[3]); + } +#endif + } + + strscpy(command_line, boot_command_line, COMMAND_LINE_SIZE); + *cmdline_p = command_line; +} + +#ifdef CONFIG_PA11 +void __init dma_ops_init(void) +{ + switch (boot_cpu_data.cpu_type) { + case pcx: + /* + * We've got way too many dependencies on 1.1 semantics + * to support 1.0 boxes at this point. + */ + panic( "PA-RISC Linux currently only supports machines that conform to\n" + "the PA-RISC 1.1 or 2.0 architecture specification.\n"); + + case pcxl2: + pa7300lc_init(); + break; + default: + break; + } +} +#endif + +extern void collect_boot_cpu_data(void); + +void __init setup_arch(char **cmdline_p) +{ +#ifdef CONFIG_64BIT + extern int parisc_narrow_firmware; +#endif + unwind_init(); + + init_per_cpu(smp_processor_id()); /* Set Modes & Enable FP */ + +#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 "Kernel default page size is %d KB. Huge pages ", + (int)(PAGE_SIZE / 1024)); +#ifdef CONFIG_HUGETLB_PAGE + printk(KERN_CONT "enabled with %d MB physical and %d MB virtual size", + 1 << (REAL_HPAGE_SHIFT - 20), 1 << (HPAGE_SHIFT - 20)); +#else + printk(KERN_CONT "disabled"); +#endif + printk(KERN_CONT ".\n"); + + /* + * Check if initial kernel page mappings are sufficient. + * panic early if not, else we may access kernel functions + * and variables which can't be reached. + */ + if (__pa((unsigned long) &_end) >= KERNEL_INITIAL_SIZE) + panic("KERNEL_INITIAL_ORDER too small!"); + +#ifdef CONFIG_64BIT + if(parisc_narrow_firmware) { + printk(KERN_INFO "Kernel is using PDC in 32-bit mode.\n"); + } +#endif + setup_pdc(); + setup_cmdline(cmdline_p); + collect_boot_cpu_data(); + do_memory_inventory(); /* probe for physical memory */ + parisc_cache_init(); + paging_init(); + +#ifdef CONFIG_CHASSIS_LCD_LED + /* initialize the LCD/LED after boot_cpu_data is available ! */ + led_init(); /* LCD/LED initialization */ +#endif + +#ifdef CONFIG_PA11 + dma_ops_init(); +#endif + + clear_sched_clock_stable(); +} + +/* + * Display CPU info for all CPUs. + * for parisc this is in processor.c + */ +extern int show_cpuinfo (struct seq_file *m, void *v); + +static void * +c_start (struct seq_file *m, loff_t *pos) +{ + /* Looks like the caller will call repeatedly until we return + * 0, signaling EOF perhaps. This could be used to sequence + * through CPUs for example. Since we print all cpu info in our + * show_cpuinfo() disregarding 'pos' (which I assume is 'v' above) + * we only allow for one "position". */ + return ((long)*pos < 1) ? (void *)1 : NULL; +} + +static void * +c_next (struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void +c_stop (struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo +}; + +static void __init parisc_proc_mkdir(void) +{ + /* + ** Can't call proc_mkdir() until after proc_root_init() has been + ** called by start_kernel(). In other words, this code can't + ** live in arch/.../setup.c because start_parisc() calls + ** start_kernel(). + */ + switch (boot_cpu_data.cpu_type) { + case pcxl: + case pcxl2: + if (NULL == proc_gsc_root) + { + proc_gsc_root = proc_mkdir("bus/gsc", NULL); + } + break; + case pcxt_: + case pcxu: + case pcxu_: + case pcxw: + case pcxw_: + case pcxw2: + if (NULL == proc_runway_root) + { + proc_runway_root = proc_mkdir("bus/runway", NULL); + } + break; + case mako: + case mako2: + if (NULL == proc_mckinley_root) + { + proc_mckinley_root = proc_mkdir("bus/mckinley", NULL); + } + break; + default: + /* FIXME: this was added to prevent the compiler + * complaining about missing pcx, pcxs and pcxt + * I'm assuming they have neither gsc nor runway */ + break; + } +} + +static struct resource central_bus = { + .name = "Central Bus", + .start = F_EXTEND(0xfff80000), + .end = F_EXTEND(0xfffaffff), + .flags = IORESOURCE_MEM, +}; + +static struct resource local_broadcast = { + .name = "Local Broadcast", + .start = F_EXTEND(0xfffb0000), + .end = F_EXTEND(0xfffdffff), + .flags = IORESOURCE_MEM, +}; + +static struct resource global_broadcast = { + .name = "Global Broadcast", + .start = F_EXTEND(0xfffe0000), + .end = F_EXTEND(0xffffffff), + .flags = IORESOURCE_MEM, +}; + +static int __init parisc_init_resources(void) +{ + int result; + + result = request_resource(&iomem_resource, ¢ral_bus); + if (result < 0) { + printk(KERN_ERR + "%s: failed to claim %s address space!\n", + __FILE__, central_bus.name); + return result; + } + + result = request_resource(&iomem_resource, &local_broadcast); + if (result < 0) { + printk(KERN_ERR + "%s: failed to claim %s address space!\n", + __FILE__, local_broadcast.name); + return result; + } + + result = request_resource(&iomem_resource, &global_broadcast); + if (result < 0) { + printk(KERN_ERR + "%s: failed to claim %s address space!\n", + __FILE__, global_broadcast.name); + return result; + } + + return 0; +} + +extern void gsc_init(void); +extern void processor_init(void); +extern void ccio_init(void); +extern void hppb_init(void); +extern void dino_init(void); +extern void iosapic_init(void); +extern void lba_init(void); +extern void sba_init(void); +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 */ + + parisc_pdc_chassis_init(); + + /* 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)); + + /* start with known state */ + flush_cache_all_local(); + flush_tlb_all_local(NULL); + + processor_init(); +#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 ); + +#if defined(CONFIG_64BIT) && defined(CONFIG_SMP) + /* Don't serialize TLB flushes if we run on one CPU only. */ + if (num_online_cpus() == 1) + pa_serialize_tlb_flushes = 0; +#endif + + apply_alternatives_all(); + parisc_setup_cache_timing(); + + /* These are in a non-obvious order, will fix when we have an iotree */ +#if defined(CONFIG_IOSAPIC) + iosapic_init(); +#endif +#if defined(CONFIG_IOMMU_SBA) + sba_init(); +#endif +#if defined(CONFIG_PCI_LBA) + lba_init(); +#endif + + /* CCIO before any potential subdevices */ +#if defined(CONFIG_IOMMU_CCIO) + ccio_init(); +#endif + + /* + * Need to register Asp & Wax before the EISA adapters for the IRQ + * regions. EISA must come before PCI to be sure it gets IRQ region + * 0. + */ +#if defined(CONFIG_GSC_LASI) || defined(CONFIG_GSC_WAX) + gsc_init(); +#endif +#ifdef CONFIG_EISA + eisa_init(); +#endif + +#if defined(CONFIG_HPPB) + hppb_init(); +#endif + +#if defined(CONFIG_GSC_DINO) + dino_init(); +#endif + +#ifdef CONFIG_CHASSIS_LCD_LED + register_led_regions(); /* register LED port info in procfs */ +#endif + + return 0; +} +arch_initcall(parisc_init); + +void __init start_parisc(void) +{ + extern void early_trap_init(void); + + int ret, cpunum; + struct pdc_coproc_cfg coproc_cfg; + + /* check QEMU/SeaBIOS marker in PAGE0 */ + running_on_qemu = (memcmp(&PAGE0->pad0, "SeaBIOS", 8) == 0); + + cpunum = smp_processor_id(); + + init_cpu_topology(); + + 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"); + } + + early_trap_init(); /* initialize checksum of fault_vector */ + + start_kernel(); + // not reached +} diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c new file mode 100644 index 000000000..8bc0ddaa6 --- /dev/null +++ b/arch/parisc/kernel/signal.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PA-RISC architecture-specific signal handling support. + * + * Copyright (C) 2000 David Huggins-Daines <dhd@debian.org> + * Copyright (C) 2000 Linuxcare, Inc. + * Copyright (C) 2000-2022 Helge Deller <deller@gmx.de> + * Copyright (C) 2022 John David Anglin <dave.anglin@bell.net> + * + * Based on the ia64, i386, and alpha versions. + */ + +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/resume_user_mode.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/compat.h> +#include <linux/elf.h> +#include <asm/ucontext.h> +#include <asm/rt_sigframe.h> +#include <linux/uaccess.h> +#include <asm/cacheflush.h> +#include <asm/asm-offsets.h> +#include <asm/vdso.h> + +#ifdef CONFIG_COMPAT +#include "signal32.h" +#endif + +#define DEBUG_SIG 0 +#define DEBUG_SIG_LEVEL 2 + +#if DEBUG_SIG +#define DBG(LEVEL, ...) \ + ((DEBUG_SIG_LEVEL >= LEVEL) \ + ? printk(__VA_ARGS__) : (void) 0) +#else +#define DBG(LEVEL, ...) +#endif + +/* 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 + * integer of the same size first. The A() macro accomplishes + * this. */ +#define A(__x) ((unsigned long)(__x)) + +/* + * Do a signal return - restore sigcontext. + */ + +static long +restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs) +{ + long err = 0; + + err |= __copy_from_user(regs->gr, sc->sc_gr, sizeof(regs->gr)); + err |= __copy_from_user(regs->fr, sc->sc_fr, sizeof(regs->fr)); + 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, "%s: iaoq is %#lx / %#lx\n", + __func__, regs->iaoq[0], regs->iaoq[1]); + DBG(2, "%s: r28 is %ld\n", __func__, regs->gr[28]); + return err; +} + +void +sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) +{ + struct rt_sigframe __user *frame; + sigset_t set; + unsigned long usp = (regs->gr[30] & ~(0x01UL)); + unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE; +#ifdef CONFIG_64BIT + struct compat_rt_sigframe __user * compat_frame; + + if (is_compat_task()) + sigframe_size = PARISC_RT_SIGFRAME_SIZE32; +#endif + + current->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, "%s: frame is %p pid %d\n", __func__, frame, task_pid_nr(current)); + + regs->orig_r28 = 1; /* no restarts for sigreturn */ + +#ifdef CONFIG_64BIT + compat_frame = (struct compat_rt_sigframe __user *)frame; + + if (is_compat_task()) { + if (get_compat_sigset(&set, &compat_frame->uc.uc_sigmask)) + goto give_sigsegv; + } else +#endif + { + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto give_sigsegv; + } + + set_current_blocked(&set); + + /* Good thing we saved the old gr[30], eh? */ +#ifdef CONFIG_64BIT + if (is_compat_task()) { + DBG(1, "%s: compat_frame->uc.uc_mcontext 0x%p\n", + __func__, &compat_frame->uc.uc_mcontext); +// FIXME: Load upper half from register file + if (restore_sigcontext32(&compat_frame->uc.uc_mcontext, + &compat_frame->regs, regs)) + goto give_sigsegv; + DBG(1, "%s: usp %#08lx stack 0x%p\n", + __func__, usp, &compat_frame->uc.uc_stack); + if (compat_restore_altstack(&compat_frame->uc.uc_stack)) + goto give_sigsegv; + } else +#endif + { + DBG(1, "%s: frame->uc.uc_mcontext 0x%p\n", + __func__, &frame->uc.uc_mcontext); + if (restore_sigcontext(&frame->uc.uc_mcontext, regs)) + goto give_sigsegv; + DBG(1, "%s: usp %#08lx stack 0x%p\n", + __func__, usp, &frame->uc.uc_stack); + if (restore_altstack(&frame->uc.uc_stack)) + goto give_sigsegv; + } + + + + /* If we are on the syscall path IAOQ will not be restored, and + * if we are on the interrupt path we must not corrupt gr31. + */ + if (in_syscall) + regs->gr[31] = regs->iaoq[0]; + + return; + +give_sigsegv: + DBG(1, "%s: Sending SIGSEGV\n", __func__); + force_sig(SIGSEGV); + return; +} + +/* + * Set up a signal frame. + */ + +static inline void __user * +get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) +{ + /*FIXME: ELF32 vs. ELF64 has different frame_size, but since we + don't use the parameter it doesn't matter */ + + DBG(1, "%s: ka = %#lx, sp = %#lx, frame_size = %zu\n", + __func__, (unsigned long)ka, sp, frame_size); + + /* 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, "%s: Returning sp = %#lx\n", __func__, (unsigned long)sp); + return (void __user *) sp; /* Stacks grow up. Fun. */ +} + +static long +setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, int in_syscall) + +{ + unsigned long flags = 0; + long err = 0; + + if (on_sig_stack((unsigned long) sc)) + flags |= PARISC_SC_FLAG_ONSTACK; + if (in_syscall) { + flags |= PARISC_SC_FLAG_IN_SYSCALL; + /* regs->iaoq is undefined in the syscall return path */ + err |= __put_user(regs->gr[31], &sc->sc_iaoq[0]); + err |= __put_user(regs->gr[31]+4, &sc->sc_iaoq[1]); + err |= __put_user(regs->sr[3], &sc->sc_iasq[0]); + err |= __put_user(regs->sr[3], &sc->sc_iasq[1]); + DBG(1, "%s: iaoq %#lx / %#lx (in syscall)\n", + __func__, regs->gr[31], regs->gr[31]+4); + } else { + err |= __copy_to_user(sc->sc_iaoq, regs->iaoq, sizeof(regs->iaoq)); + err |= __copy_to_user(sc->sc_iasq, regs->iasq, sizeof(regs->iasq)); + DBG(1, "%s: iaoq %#lx / %#lx (not in syscall)\n", + __func__, regs->iaoq[0], regs->iaoq[1]); + } + + err |= __put_user(flags, &sc->sc_flags); + err |= __copy_to_user(sc->sc_gr, regs->gr, sizeof(regs->gr)); + err |= __copy_to_user(sc->sc_fr, regs->fr, sizeof(regs->fr)); + err |= __put_user(regs->sar, &sc->sc_sar); + DBG(1, "%s: r28 is %ld\n", __func__, regs->gr[28]); + + return err; +} + +static long +setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs, + int in_syscall) +{ + struct rt_sigframe __user *frame; + unsigned long rp, usp; + unsigned long haddr, sigframe_size; + unsigned long start; + int err = 0; +#ifdef CONFIG_64BIT + struct compat_rt_sigframe __user * compat_frame; +#endif + + usp = (regs->gr[30] & ~(0x01UL)); + sigframe_size = PARISC_RT_SIGFRAME_SIZE; +#ifdef CONFIG_64BIT + if (is_compat_task()) { + /* The gcc alloca implementation leaves garbage in the upper 32 bits of sp */ + usp = (compat_uint_t)usp; + sigframe_size = PARISC_RT_SIGFRAME_SIZE32; + } +#endif + frame = get_sigframe(&ksig->ka, usp, sigframe_size); + + DBG(1, "%s: frame %p info %p\n", __func__, frame, &ksig->info); + + start = (unsigned long) frame; + if (start >= TASK_SIZE_MAX - sigframe_size) + return -EFAULT; + +#ifdef CONFIG_64BIT + + compat_frame = (struct compat_rt_sigframe __user *)frame; + + if (is_compat_task()) { + DBG(1, "%s: frame->info = 0x%p\n", __func__, &compat_frame->info); + err |= copy_siginfo_to_user32(&compat_frame->info, &ksig->info); + err |= __compat_save_altstack( &compat_frame->uc.uc_stack, regs->gr[30]); + DBG(1, "%s: frame->uc = 0x%p\n", __func__, &compat_frame->uc); + DBG(1, "%s: frame->uc.uc_mcontext = 0x%p\n", + __func__, &compat_frame->uc.uc_mcontext); + err |= setup_sigcontext32(&compat_frame->uc.uc_mcontext, + &compat_frame->regs, regs, in_syscall); + err |= put_compat_sigset(&compat_frame->uc.uc_sigmask, set, + sizeof(compat_sigset_t)); + } else +#endif + { + DBG(1, "%s: frame->info = 0x%p\n", __func__, &frame->info); + err |= copy_siginfo_to_user(&frame->info, &ksig->info); + err |= __save_altstack(&frame->uc.uc_stack, regs->gr[30]); + DBG(1, "%s: frame->uc = 0x%p\n", __func__, &frame->uc); + DBG(1, "%s: frame->uc.uc_mcontext = 0x%p\n", + __func__, &frame->uc.uc_mcontext); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, in_syscall); + /* FIXME: Should probably be converted as well for the compat case */ + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + } + + if (err) + return -EFAULT; + +#ifdef CONFIG_64BIT + if (!is_compat_task()) + rp = VDSO64_SYMBOL(current, sigtramp_rt); + else +#endif + rp = VDSO32_SYMBOL(current, sigtramp_rt); + + if (in_syscall) + rp += 4*4; /* skip 4 instructions and start at ldi 1,%r25 */ + + haddr = A(ksig->ka.sa.sa_handler); + /* The sa_handler may be a pointer to a function descriptor */ +#ifdef CONFIG_64BIT + if (is_compat_task()) { +#endif + if (haddr & PA_PLABEL_FDESC) { + Elf32_Fdesc fdesc; + Elf32_Fdesc __user *ufdesc = (Elf32_Fdesc __user *)A(haddr & ~3); + + err = __copy_from_user(&fdesc, ufdesc, sizeof(fdesc)); + + if (err) + return -EFAULT; + + haddr = fdesc.addr; + regs->gr[19] = fdesc.gp; + } +#ifdef CONFIG_64BIT + } else { + Elf64_Fdesc fdesc; + Elf64_Fdesc __user *ufdesc = (Elf64_Fdesc __user *)A(haddr & ~3); + + err = __copy_from_user(&fdesc, ufdesc, sizeof(fdesc)); + + if (err) + return -EFAULT; + + haddr = fdesc.addr; + regs->gr[19] = fdesc.gp; + DBG(1, "%s: 64 bit signal, exe=%#lx, r19=%#lx, in_syscall=%d\n", + __func__, haddr, regs->gr[19], in_syscall); + } +#endif + + /* The syscall return path will create IAOQ values from r31. + */ + if (in_syscall) { + regs->gr[31] = haddr; +#ifdef CONFIG_64BIT + if (!test_thread_flag(TIF_32BIT)) + sigframe_size |= 1; /* XXX ???? */ +#endif + } else { + unsigned long psw = USER_PSW; +#ifdef CONFIG_64BIT + if (!test_thread_flag(TIF_32BIT)) + psw |= PSW_W; +#endif + + /* If we are singlestepping, arrange a trap to be delivered + when we return to userspace. Note the semantics -- we + should trap before the first insn in the handler is + executed. Ref: + http://sources.redhat.com/ml/gdb/2004-11/msg00245.html + */ + if (pa_psw(current)->r) { + pa_psw(current)->r = 0; + psw |= PSW_R; + mtctl(-1, 0); + } + + regs->gr[0] = psw; + regs->iaoq[0] = haddr | PRIV_USER; + regs->iaoq[1] = regs->iaoq[0] + 4; + } + + regs->gr[2] = rp; /* userland return pointer */ + regs->gr[26] = ksig->sig; /* signal number */ + +#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 +#endif + { + regs->gr[25] = A(&frame->info); /* siginfo pointer */ + regs->gr[24] = A(&frame->uc); /* ucontext pointer */ + } + + DBG(1, "%s: making sigreturn frame: %#lx + %#lx = %#lx\n", __func__, + regs->gr[30], sigframe_size, + regs->gr[30] + sigframe_size); + /* Raise the user stack pointer to make a proper call frame. */ + regs->gr[30] = (A(frame) + sigframe_size); + + + DBG(1, "%s: sig deliver (%s,%d) frame=0x%p sp=%#lx iaoq=%#lx/%#lx rp=%#lx\n", + __func__, current->comm, current->pid, frame, regs->gr[30], + regs->iaoq[0], regs->iaoq[1], rp); + + return 0; +} + +/* + * OK, we're invoking a handler. + */ + +static void +handle_signal(struct ksignal *ksig, struct pt_regs *regs, int in_syscall) +{ + int ret; + sigset_t *oldset = sigmask_to_save(); + + DBG(1, "%s: sig=%d, ka=%p, info=%p, oldset=%p, regs=%p\n", + __func__, ksig->sig, &ksig->ka, &ksig->info, oldset, regs); + + /* Set up the stack frame */ + ret = setup_rt_frame(ksig, oldset, regs, in_syscall); + + signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP) || + test_thread_flag(TIF_BLOCKSTEP)); + + DBG(1, "%s: Exit (success), regs->gr[28] = %ld\n", + __func__, regs->gr[28]); +} + +/* + * Check how the syscall number gets loaded into %r20 within + * the delay branch in userspace and adjust as needed. + */ + +static void check_syscallno_in_delay_branch(struct pt_regs *regs) +{ + u32 opcode, source_reg; + u32 __user *uaddr; + int err; + + /* Usually we don't have to restore %r20 (the system call number) + * because it gets loaded in the delay slot of the branch external + * instruction via the ldi instruction. + * In some cases a register-to-register copy instruction might have + * been used instead, in which case we need to copy the syscall + * number into the source register before returning to userspace. + */ + + /* A syscall is just a branch, so all we have to do is fiddle the + * return pointer so that the ble instruction gets executed again. + */ + regs->gr[31] -= 8; /* delayed branching */ + + /* Get assembler opcode of code in delay branch */ + uaddr = (unsigned int *) ((regs->gr[31] & ~3) + 4); + err = get_user(opcode, uaddr); + if (err) + return; + + /* Check if delay branch uses "ldi int,%r20" */ + if ((opcode & 0xffff0000) == 0x34140000) + return; /* everything ok, just return */ + + /* Check if delay branch uses "nop" */ + if (opcode == INSN_NOP) + return; + + /* Check if delay branch uses "copy %rX,%r20" */ + if ((opcode & 0xffe0ffff) == 0x08000254) { + source_reg = (opcode >> 16) & 31; + regs->gr[source_reg] = regs->gr[20]; + return; + } + + pr_warn("syscall restart: %s (pid %d): unexpected opcode 0x%08x\n", + current->comm, task_pid_nr(current), opcode); +} + +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 */ + + DBG(1, "%s: orig_r28 = %ld pid %d r20 %ld\n", + __func__, regs->orig_r28, task_pid_nr(current), regs->gr[20]); + + /* Check the return code */ + switch (regs->gr[28]) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + DBG(1, "%s: ERESTARTNOHAND: returning -EINTR\n", __func__); + regs->gr[28] = -EINTR; + break; + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + DBG(1, "%s: ERESTARTSYS: putting -EINTR pid %d\n", + __func__, task_pid_nr(current)); + regs->gr[28] = -EINTR; + break; + } + fallthrough; + case -ERESTARTNOINTR: + DBG(1, "%s: %ld\n", __func__, regs->gr[28]); + check_syscallno_in_delay_branch(regs); + break; + } +} + +static inline void +insert_restart_trampoline(struct pt_regs *regs) +{ + if (regs->orig_r28) + return; + regs->orig_r28 = 1; /* no more restarts */ + + DBG(2, "%s: gr28 = %ld pid %d\n", + __func__, regs->gr[28], task_pid_nr(current)); + + switch (regs->gr[28]) { + case -ERESTART_RESTARTBLOCK: { + /* Restart the system call - no handlers present */ + unsigned int *usp = (unsigned int *)regs->gr[30]; + unsigned long rp; + long err = 0; + + /* check that we don't exceed the stack */ + if (A(&usp[0]) >= TASK_SIZE_MAX - 5 * sizeof(int)) + return; + + /* Call trampoline in vdso to restart the syscall + * with __NR_restart_syscall. + * Original return addresses are on stack like this: + * + * 0: <return address (orig r31)> + * 4: <2nd half for 64-bit> + */ +#ifdef CONFIG_64BIT + if (!is_compat_task()) { + err |= put_user(regs->gr[31] >> 32, &usp[0]); + err |= put_user(regs->gr[31] & 0xffffffff, &usp[1]); + rp = VDSO64_SYMBOL(current, restart_syscall); + } else +#endif + { + err |= put_user(regs->gr[31], &usp[0]); + rp = VDSO32_SYMBOL(current, restart_syscall); + } + WARN_ON(err); + + regs->gr[31] = rp; + DBG(1, "%s: ERESTART_RESTARTBLOCK\n", __func__); + return; + } + case -EINTR: + /* ok, was handled before and should be returned. */ + break; + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: + DBG(1, "%s: Type %ld\n", __func__, regs->gr[28]); + check_syscallno_in_delay_branch(regs); + return; + default: + break; + } +} + +/* + * We need to be able to restore the syscall arguments (r21-r26) to + * restart syscalls. Thus, the syscall path should save them in the + * pt_regs structure (it's okay to do so since they are caller-save + * registers). As noted below, the syscall number gets restored for + * us due to the magic of delayed branching. + */ +static void do_signal(struct pt_regs *regs, long in_syscall) +{ + struct ksignal ksig; + int restart_syscall; + bool has_handler; + + has_handler = get_signal(&ksig); + + restart_syscall = 0; + if (in_syscall) + restart_syscall = 1; + + if (has_handler) { + /* Restart a system call if necessary. */ + if (restart_syscall) + syscall_restart(regs, &ksig.ka); + + handle_signal(&ksig, regs, in_syscall); + DBG(1, "%s: Handled signal pid %d\n", + __func__, task_pid_nr(current)); + return; + } + + /* Do we need to restart the system call? */ + if (restart_syscall) + insert_restart_trampoline(regs); + + DBG(1, "%s: Exit (not delivered), regs->gr[28] = %ld orig_r28 = %ld pid %d\n", + __func__, regs->gr[28], regs->orig_r28, task_pid_nr(current)); + + restore_saved_sigmask(); +} + +void do_notify_resume(struct pt_regs *regs, long in_syscall) +{ + if (test_thread_flag(TIF_SIGPENDING) || + test_thread_flag(TIF_NOTIFY_SIGNAL)) + do_signal(regs, in_syscall); + + if (test_thread_flag(TIF_NOTIFY_RESUME)) + resume_user_mode_work(regs); +} diff --git a/arch/parisc/kernel/signal32.c b/arch/parisc/kernel/signal32.c new file mode 100644 index 000000000..9a5ba57ad --- /dev/null +++ b/arch/parisc/kernel/signal32.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* 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. + */ + +#include <linux/compat.h> +#include <linux/module.h> +#include <linux/unistd.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/syscalls.h> +#include <linux/types.h> +#include <linux/errno.h> + +#include <linux/uaccess.h> + +#include "signal32.h" + +#define DEBUG_COMPAT_SIG 0 +#define DEBUG_COMPAT_SIG_LEVEL 2 + +#if DEBUG_COMPAT_SIG +#define DBG(LEVEL, ...) \ + ((DEBUG_COMPAT_SIG_LEVEL >= LEVEL) \ + ? printk(__VA_ARGS__) : (void) 0) +#else +#define DBG(LEVEL, ...) +#endif + +long +restore_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __user * rf, + struct pt_regs *regs) +{ + long err = 0; + compat_uint_t compat_reg; + compat_uint_t compat_regt; + int regn; + + /* When loading 32-bit values into 64-bit registers make + sure to clear the upper 32-bits */ + DBG(2,"restore_sigcontext32: PER_LINUX32 process\n"); + DBG(2,"restore_sigcontext32: sc = 0x%p, rf = 0x%p, regs = 0x%p\n", sc, rf, regs); + DBG(2,"restore_sigcontext32: compat_sigcontext is %#lx bytes\n", sizeof(*sc)); + for(regn=0; regn < 32; regn++){ + err |= __get_user(compat_reg,&sc->sc_gr[regn]); + regs->gr[regn] = compat_reg; + /* Load upper half */ + err |= __get_user(compat_regt,&rf->rf_gr[regn]); + regs->gr[regn] = ((u64)compat_regt << 32) | (u64)compat_reg; + DBG(3,"restore_sigcontext32: gr%02d = %#lx (%#x / %#x)\n", + regn, regs->gr[regn], compat_regt, compat_reg); + } + DBG(2,"restore_sigcontext32: sc->sc_fr = 0x%p (%#lx)\n",sc->sc_fr, sizeof(sc->sc_fr)); + /* XXX: BE WARNED FR's are 64-BIT! */ + err |= __copy_from_user(regs->fr, sc->sc_fr, sizeof(regs->fr)); + + /* Better safe than sorry, pass __get_user two things of + the same size and let gcc do the upward conversion to + 64-bits */ + err |= __get_user(compat_reg, &sc->sc_iaoq[0]); + /* Load upper half */ + err |= __get_user(compat_regt, &rf->rf_iaoq[0]); + regs->iaoq[0] = ((u64)compat_regt << 32) | (u64)compat_reg; + DBG(2,"restore_sigcontext32: upper half of iaoq[0] = %#lx\n", compat_regt); + DBG(2,"restore_sigcontext32: sc->sc_iaoq[0] = %p => %#x\n", + &sc->sc_iaoq[0], compat_reg); + + err |= __get_user(compat_reg, &sc->sc_iaoq[1]); + /* Load upper half */ + err |= __get_user(compat_regt, &rf->rf_iaoq[1]); + regs->iaoq[1] = ((u64)compat_regt << 32) | (u64)compat_reg; + DBG(2,"restore_sigcontext32: upper half of iaoq[1] = %#lx\n", compat_regt); + DBG(2,"restore_sigcontext32: sc->sc_iaoq[1] = %p => %#x\n", + &sc->sc_iaoq[1],compat_reg); + DBG(2,"restore_sigcontext32: iaoq is %#lx / %#lx\n", + regs->iaoq[0],regs->iaoq[1]); + + err |= __get_user(compat_reg, &sc->sc_iasq[0]); + /* Load the upper half for iasq */ + err |= __get_user(compat_regt, &rf->rf_iasq[0]); + regs->iasq[0] = ((u64)compat_regt << 32) | (u64)compat_reg; + DBG(2,"restore_sigcontext32: upper half of iasq[0] = %#lx\n", compat_regt); + + err |= __get_user(compat_reg, &sc->sc_iasq[1]); + /* Load the upper half for iasq */ + err |= __get_user(compat_regt, &rf->rf_iasq[1]); + regs->iasq[1] = ((u64)compat_regt << 32) | (u64)compat_reg; + DBG(2,"restore_sigcontext32: upper half of iasq[1] = %#lx\n", compat_regt); + DBG(2,"restore_sigcontext32: iasq is %#lx / %#lx\n", + regs->iasq[0],regs->iasq[1]); + + err |= __get_user(compat_reg, &sc->sc_sar); + /* Load the upper half for sar */ + err |= __get_user(compat_regt, &rf->rf_sar); + regs->sar = ((u64)compat_regt << 32) | (u64)compat_reg; + DBG(2,"restore_sigcontext32: upper_half & sar = %#lx\n", compat_regt); + DBG(2,"restore_sigcontext32: sar is %#lx\n", regs->sar); + DBG(2,"restore_sigcontext32: r28 is %ld\n", regs->gr[28]); + + return err; +} + +/* + * Set up the sigcontext structure for this process. + * This is not an easy task if the kernel is 64-bit, it will require + * that we examine the process personality to determine if we need to + * truncate for a 32-bit userspace. + */ +long +setup_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __user * rf, + struct pt_regs *regs, int in_syscall) +{ + compat_int_t flags = 0; + long err = 0; + compat_uint_t compat_reg; + compat_uint_t compat_regb; + int regn; + + if (on_sig_stack((unsigned long) sc)) + flags |= PARISC_SC_FLAG_ONSTACK; + + if (in_syscall) { + + DBG(1,"setup_sigcontext32: in_syscall\n"); + + flags |= PARISC_SC_FLAG_IN_SYSCALL; + /* Truncate gr31 */ + compat_reg = (compat_uint_t)(regs->gr[31]); + /* regs->iaoq is undefined in the syscall return path */ + err |= __put_user(compat_reg, &sc->sc_iaoq[0]); + DBG(2,"setup_sigcontext32: sc->sc_iaoq[0] = %p <= %#x\n", + &sc->sc_iaoq[0], compat_reg); + + /* Store upper half */ + 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); + + + compat_reg = (compat_uint_t)(regs->gr[31]+4); + err |= __put_user(compat_reg, &sc->sc_iaoq[1]); + 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[31]+4) >> 32); + err |= __put_user(compat_reg, &rf->rf_iaoq[1]); + DBG(2,"setup_sigcontext32: upper half iaoq[1] = %#x\n", compat_reg); + + /* Truncate sr3 */ + compat_reg = (compat_uint_t)(regs->sr[3]); + err |= __put_user(compat_reg, &sc->sc_iasq[0]); + err |= __put_user(compat_reg, &sc->sc_iasq[1]); + + /* Store upper half */ + compat_reg = (compat_uint_t)(regs->sr[3] >> 32); + err |= __put_user(compat_reg, &rf->rf_iasq[0]); + err |= __put_user(compat_reg, &rf->rf_iasq[1]); + + DBG(2,"setup_sigcontext32: upper half iasq[0] = %#x\n", compat_reg); + DBG(2,"setup_sigcontext32: upper half iasq[1] = %#x\n", compat_reg); + DBG(1,"setup_sigcontext32: iaoq %#lx / %#lx\n", + regs->gr[31], regs->gr[31]+4); + + } else { + + compat_reg = (compat_uint_t)(regs->iaoq[0]); + err |= __put_user(compat_reg, &sc->sc_iaoq[0]); + DBG(2,"setup_sigcontext32: sc->sc_iaoq[0] = %p <= %#x\n", + &sc->sc_iaoq[0], compat_reg); + /* Store upper half */ + compat_reg = (compat_uint_t)(regs->iaoq[0] >> 32); + err |= __put_user(compat_reg, &rf->rf_iaoq[0]); + DBG(2,"setup_sigcontext32: upper half iaoq[0] = %#x\n", compat_reg); + + compat_reg = (compat_uint_t)(regs->iaoq[1]); + err |= __put_user(compat_reg, &sc->sc_iaoq[1]); + 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->iaoq[1] >> 32); + err |= __put_user(compat_reg, &rf->rf_iaoq[1]); + DBG(2,"setup_sigcontext32: upper half iaoq[1] = %#x\n", compat_reg); + + + compat_reg = (compat_uint_t)(regs->iasq[0]); + err |= __put_user(compat_reg, &sc->sc_iasq[0]); + DBG(2,"setup_sigcontext32: sc->sc_iasq[0] = %p <= %#x\n", + &sc->sc_iasq[0], compat_reg); + /* Store upper half */ + compat_reg = (compat_uint_t)(regs->iasq[0] >> 32); + err |= __put_user(compat_reg, &rf->rf_iasq[0]); + DBG(2,"setup_sigcontext32: upper half iasq[0] = %#x\n", compat_reg); + + + compat_reg = (compat_uint_t)(regs->iasq[1]); + err |= __put_user(compat_reg, &sc->sc_iasq[1]); + DBG(2,"setup_sigcontext32: sc->sc_iasq[1] = %p <= %#x\n", + &sc->sc_iasq[1], compat_reg); + /* Store upper half */ + compat_reg = (compat_uint_t)(regs->iasq[1] >> 32); + err |= __put_user(compat_reg, &rf->rf_iasq[1]); + DBG(2,"setup_sigcontext32: upper half iasq[1] = %#x\n", compat_reg); + + /* Print out the IAOQ for debugging */ + DBG(1,"setup_sigcontext32: ia0q %#lx / %#lx\n", + regs->iaoq[0], regs->iaoq[1]); + } + + err |= __put_user(flags, &sc->sc_flags); + + DBG(1,"setup_sigcontext32: Truncating general registers.\n"); + + for(regn=0; regn < 32; regn++){ + /* Truncate a general register */ + compat_reg = (compat_uint_t)(regs->gr[regn]); + err |= __put_user(compat_reg, &sc->sc_gr[regn]); + /* Store upper half */ + compat_regb = (compat_uint_t)(regs->gr[regn] >> 32); + err |= __put_user(compat_regb, &rf->rf_gr[regn]); + + /* DEBUG: Write out the "upper / lower" register data */ + DBG(2,"setup_sigcontext32: gr%02d = %#x / %#x\n", regn, + compat_regb, compat_reg); + } + + /* Copy the floating point registers (same size) + XXX: BE WARNED FR's are 64-BIT! */ + DBG(1,"setup_sigcontext32: Copying from regs to sc, " + "sc->sc_fr size = %#lx, regs->fr size = %#lx\n", + sizeof(regs->fr), sizeof(sc->sc_fr)); + err |= __copy_to_user(sc->sc_fr, regs->fr, sizeof(regs->fr)); + + compat_reg = (compat_uint_t)(regs->sar); + err |= __put_user(compat_reg, &sc->sc_sar); + DBG(2,"setup_sigcontext32: sar is %#x\n", compat_reg); + /* Store upper half */ + compat_reg = (compat_uint_t)(regs->sar >> 32); + err |= __put_user(compat_reg, &rf->rf_sar); + DBG(2,"setup_sigcontext32: upper half sar = %#x\n", compat_reg); + DBG(1,"setup_sigcontext32: r28 is %ld\n", regs->gr[28]); + + return err; +} diff --git a/arch/parisc/kernel/signal32.h b/arch/parisc/kernel/signal32.h new file mode 100644 index 000000000..c03eb1ed4 --- /dev/null +++ b/arch/parisc/kernel/signal32.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2001 Matthew Wilcox <willy at parisc-linux.org> + * Copyright (C) 2003 Carlos O'Donell <carlos at parisc-linux.org> + */ +#ifndef _PARISC64_KERNEL_SIGNAL32_H +#define _PARISC64_KERNEL_SIGNAL32_H + +#include <linux/compat.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 */ + +/* 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; +}; + +struct compat_rt_sigframe { + unsigned int tramp[2]; /* holds original return address */ + 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) + +long restore_sigcontext32(struct compat_sigcontext __user *sc, + struct compat_regfile __user *rf, + struct pt_regs *regs); +long setup_sigcontext32(struct compat_sigcontext __user *sc, + struct compat_regfile __user *rf, + struct pt_regs *regs, int in_syscall); + +#endif diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c new file mode 100644 index 000000000..e37ec0548 --- /dev/null +++ b/arch/parisc/kernel/smp.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +** SMP Support +** +** Copyright (C) 1999 Walt Drummond <drummond@valinux.com> +** Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> +** Copyright (C) 2001,2004 Grant Grundler <grundler@parisc-linux.org> +** +** 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 a lot from their work. +** -grant (1/12/2001) +** +*/ +#include <linux/types.h> +#include <linux/spinlock.h> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched/mm.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#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 <linux/kgdb.h> +#include <linux/sched/hotplug.h> + +#include <linux/atomic.h> +#include <asm/current.h> +#include <asm/delay.h> +#include <asm/tlbflush.h> + +#include <asm/io.h> +#include <asm/irq.h> /* for CPU_IRQ_REGION and friends */ +#include <asm/mmu_context.h> +#include <asm/page.h> +#include <asm/processor.h> +#include <asm/ptrace.h> +#include <asm/unistd.h> +#include <asm/cacheflush.h> + +#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; + +/* track which CPU is booting */ +static volatile int cpu_now_booting; + +static DEFINE_PER_CPU(spinlock_t, ipi_lock); + +enum ipi_message_type { + IPI_NOP=0, + IPI_RESCHEDULE=1, + IPI_CALL_FUNC, + IPI_CPU_START, + IPI_CPU_STOP, + IPI_CPU_TEST, +#ifdef CONFIG_KGDB + IPI_ENTER_KGDB, +#endif +}; + + +/********** SMP inter processor interrupt and communication routines */ + +#undef PER_CPU_IRQ_REGION +#ifdef PER_CPU_IRQ_REGION +/* XXX REVISIT Ignore for now. +** *May* need this "hook" to register IPI handler +** once we have perCPU ExtIntr switch tables. +*/ +static void +ipi_init(int cpuid) +{ +#error verify IRQ_OFFSET(IPI_IRQ) is ipi_interrupt() in new IRQ region + + if(cpu_online(cpuid) ) + { + switch_to_idle_task(current); + } + + return; +} +#endif + + +/* +** Yoink this CPU from the runnable list... +** +*/ +static void +halt_processor(void) +{ + /* REVISIT : redirect I/O Interrupts to another CPU? */ + /* REVISIT : does PM *know* this CPU isn't available? */ + set_cpu_online(smp_processor_id(), false); + local_irq_disable(); + __pdc_cpu_rendezvous(); + for (;;) + ; +} + + +irqreturn_t __irq_entry +ipi_interrupt(int irq, void *dev_id) +{ + int this_cpu = smp_processor_id(); + struct cpuinfo_parisc *p = &per_cpu(cpu_data, this_cpu); + unsigned long ops; + unsigned long flags; + + for (;;) { + 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(lock, flags); + + mb(); /* Order bit clearing and data access. */ + + if (!ops) + break; + + 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: + 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: + smp_debug(100, KERN_DEBUG "CPU%d IPI_CALL_FUNC\n", this_cpu); + inc_irq_stat(irq_call_count); + generic_smp_call_function_interrupt(); + break; + + case IPI_CPU_START: + smp_debug(100, KERN_DEBUG "CPU%d IPI_CPU_START\n", this_cpu); + break; + + case IPI_CPU_STOP: + smp_debug(100, KERN_DEBUG "CPU%d IPI_CPU_STOP\n", this_cpu); + halt_processor(); + break; + + case IPI_CPU_TEST: + smp_debug(100, KERN_DEBUG "CPU%d is alive!\n", this_cpu); + break; +#ifdef CONFIG_KGDB + case IPI_ENTER_KGDB: + smp_debug(100, KERN_DEBUG "CPU%d ENTER_KGDB\n", this_cpu); + kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); + break; +#endif + default: + printk(KERN_CRIT "Unknown IPI num on CPU%d: %lu\n", + this_cpu, which); + return IRQ_NONE; + } /* Switch */ + + /* before doing more, let in any pending interrupts */ + if (ops) { + local_irq_enable(); + local_irq_disable(); + } + } /* while (ops) */ + } + return IRQ_HANDLED; +} + + +static inline void +ipi_send(int cpu, enum ipi_message_type op) +{ + struct cpuinfo_parisc *p = &per_cpu(cpu_data, cpu); + spinlock_t *lock = &per_cpu(ipi_lock, cpu); + unsigned long flags; + + spin_lock_irqsave(lock, flags); + p->pending_ipi |= 1 << op; + 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) +{ + BUG_ON(dest_cpu == NO_PROC_ID); + + ipi_send(dest_cpu, op); +} + +static inline void +send_IPI_allbutself(enum ipi_message_type op) +{ + int i; + + preempt_disable(); + for_each_online_cpu(i) { + if (i != smp_processor_id()) + send_IPI_single(i, op); + } + preempt_enable(); +} + +#ifdef CONFIG_KGDB +void kgdb_roundup_cpus(void) +{ + send_IPI_allbutself(IPI_ENTER_KGDB); +} +#endif + +inline void +smp_send_stop(void) { send_IPI_allbutself(IPI_CPU_STOP); } + +void +smp_send_reschedule(int cpu) { send_IPI_single(cpu, IPI_RESCHEDULE); } + +void +smp_send_all_nop(void) +{ + send_IPI_allbutself(IPI_NOP); +} + +void arch_send_call_function_ipi_mask(const struct cpumask *mask) +{ + send_IPI_mask(mask, IPI_CALL_FUNC); +} + +void arch_send_call_function_single_ipi(int cpu) +{ + send_IPI_single(cpu, IPI_CALL_FUNC); +} + +/* + * Called by secondaries to update state and initialize CPU registers. + */ +static void +smp_cpu_init(int cpunum) +{ + 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 */ + init_per_cpu(cpunum); + + disable_sr_hashing(); + + mb(); + + /* Well, support 2.4 linux scheme as well. */ + 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 */ + mmgrab(&init_mm); + current->active_mm = &init_mm; + BUG_ON(current->mm); + enter_lazy_tlb(&init_mm, current); + + init_IRQ(); /* make sure no IRQs are enabled or pending */ + start_cpu_itimer(); +} + + +/* + * Slaves start using C here. Indirectly called from smp_slave_stext. + * Do what start_kernel() and main() do for boot strap processor (aka monarch) + */ +void smp_callin(unsigned long pdce_proc) +{ + int slave_id = cpu_now_booting; + +#ifdef CONFIG_64BIT + WARN_ON(((unsigned long)(PAGE0->mem_pdc_hi) << 32 + | PAGE0->mem_pdc) != pdce_proc); +#endif + + smp_cpu_init(slave_id); + + flush_cache_all_local(); /* start with known state */ + flush_tlb_all_local(NULL); + + local_irq_enable(); /* Interrupts have been off until now */ + + cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); + + /* NOTREACHED */ + panic("smp_callin() AAAAaaaaahhhh....\n"); +} + +/* + * Bring one cpu online. + */ +static int smp_boot_one_cpu(int cpuid, struct task_struct *idle) +{ + const struct cpuinfo_parisc *p = &per_cpu(cpu_data, cpuid); + long timeout; + +#ifdef CONFIG_HOTPLUG_CPU + int i; + + /* reset irq statistics for this CPU */ + memset(&per_cpu(irq_stat, cpuid), 0, sizeof(irq_cpustat_t)); + for (i = 0; i < NR_IRQS; i++) { + struct irq_desc *desc = irq_to_desc(i); + + if (desc && desc->kstat_irqs) + *per_cpu_ptr(desc->kstat_irqs, cpuid) = 0; + } +#endif + + /* wait until last booting CPU has started. */ + while (cpu_now_booting) + ; + + /* Let _start know what logical CPU we're booting + ** (offset into init_tasks[],cpu_data[]) + */ + cpu_now_booting = cpuid; + + /* + ** boot strap code needs to know the task address since + ** it also contains the process stack. + */ + smp_init_current_idle_task = idle ; + mb(); + + 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. + ** + ** From the PA-RISC 2.0 Firmware Architecture Reference Specification: + ** "The MEM_RENDEZ vector specifies the location of OS_RENDEZ which + ** is executed after receiving the rendezvous signal (an interrupt to + ** 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, p->hpa); + mb(); + + /* + * OK, wait a bit for that CPU to finish staggering about. + * Slave will set a bit when it reaches smp_cpu_init(). + * Once the "monarch CPU" sees the bit change, it can move on. + */ + for (timeout = 0; timeout < 10000; timeout++) { + if(cpu_online(cpuid)) { + /* Which implies Slave has started up */ + cpu_now_booting = 0; + goto alive ; + } + udelay(100); + barrier(); + } + printk(KERN_CRIT "SMP: CPU:%d is stuck.\n", cpuid); + return -1; + +alive: + /* Remember the Slave data */ + smp_debug(100, KERN_DEBUG "SMP: CPU:%d came alive after %ld _us\n", + cpuid, timeout * 100); + return 0; +} + +void __init smp_prepare_boot_cpu(void) +{ + int bootstrap_processor = per_cpu(cpu_data, 0).cpuid; + + /* Setup BSP mappings */ + printk(KERN_INFO "SMP: bootstrap CPU ID is %d\n", bootstrap_processor); + + 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 CPUs until later. +*/ +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int cpu; + + for_each_possible_cpu(cpu) + spin_lock_init(&per_cpu(ipi_lock, cpu)); + + init_cpu_present(cpumask_of(0)); +} + + +void __init smp_cpus_done(unsigned int cpu_max) +{ +} + + +int __cpu_up(unsigned int cpu, struct task_struct *tidle) +{ + if (cpu_online(cpu)) + return 0; + + if (num_online_cpus() < nr_cpu_ids && + num_online_cpus() < setup_max_cpus && + smp_boot_one_cpu(cpu, tidle)) + return -EIO; + + return cpu_online(cpu) ? 0 : -EIO; +} + +/* + * __cpu_disable runs on the processor to be shutdown. + */ +int __cpu_disable(void) +{ +#ifdef CONFIG_HOTPLUG_CPU + unsigned int cpu = smp_processor_id(); + + remove_cpu_topology(cpu); + + /* + * Take this CPU offline. Once we clear this, we can't return, + * and we must not schedule until we're ready to give up the cpu. + */ + set_cpu_online(cpu, false); + + /* Find a new timesync master */ + if (cpu == time_keeper_id) { + time_keeper_id = cpumask_first(cpu_online_mask); + pr_info("CPU %d is now promoted to time-keeper master\n", time_keeper_id); + } + + disable_percpu_irq(IPI_IRQ); + + irq_migrate_all_off_this_cpu(); + + flush_cache_all_local(); + flush_tlb_all_local(NULL); + + /* disable all irqs, including timer irq */ + local_irq_disable(); + + /* wait for next timer irq ... */ + mdelay(1000/HZ+100); + + /* ... and then clear all pending external irqs */ + set_eiem(0); + mtctl(~0UL, CR_EIRR); + mfctl(CR_EIRR); + mtctl(0, CR_EIRR); +#endif + return 0; +} + +/* + * called on the thread which is asking for a CPU to be shutdown - + * waits until shutdown has completed, or it is timed out. + */ +void __cpu_die(unsigned int cpu) +{ + pdc_cpu_rendezvous_lock(); + + if (!cpu_wait_death(cpu, 5)) { + pr_crit("CPU%u: cpu didn't die\n", cpu); + return; + } + pr_info("CPU%u: is shutting down\n", cpu); + + /* set task's state to interruptible sleep */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((IS_ENABLED(CONFIG_64BIT) ? 8:2) * HZ); + + pdc_cpu_rendezvous_unlock(); +} diff --git a/arch/parisc/kernel/stacktrace.c b/arch/parisc/kernel/stacktrace.c new file mode 100644 index 000000000..023834ef5 --- /dev/null +++ b/arch/parisc/kernel/stacktrace.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Stack trace management functions + * + * Copyright (C) 2009-2021 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/kernel.h> +#include <linux/stacktrace.h> + +#include <asm/unwind.h> + +static void notrace walk_stackframe(struct task_struct *task, + struct pt_regs *regs, bool (*fn)(void *, unsigned long), void *cookie) +{ + struct unwind_frame_info info; + + unwind_frame_init_task(&info, task, NULL); + while (1) { + if (unwind_once(&info) < 0 || info.ip == 0) + break; + + if (__kernel_text_address(info.ip)) + if (!fn(cookie, info.ip)) + break; + } +} + +void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, + struct task_struct *task, struct pt_regs *regs) +{ + walk_stackframe(task, regs, consume_entry, cookie); +} + +int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, void *cookie, + struct task_struct *task) +{ + walk_stackframe(task, NULL, consume_entry, cookie); + return 1; +} diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c new file mode 100644 index 000000000..9915062d5 --- /dev/null +++ b/arch/parisc/kernel/sys_parisc.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * PARISC specific syscalls + * + * 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-2020 Helge Deller <deller@gmx.de> + */ + +#include <linux/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/sched/signal.h> +#include <linux/sched/mm.h> +#include <linux/shm.h> +#include <linux/syscalls.h> +#include <linux/utsname.h> +#include <linux/personality.h> +#include <linux/random.h> +#include <linux/compat.h> +#include <linux/elf-randomize.h> + +/* + * Construct an artificial page offset for the mapping based on the physical + * address of the kernel file mapping variable. + */ +#define GET_FILP_PGOFF(filp) \ + (filp ? (((unsigned long) filp->f_mapping) >> 8) \ + & ((SHM_COLOUR-1) >> PAGE_SHIFT) : 0UL) + +static unsigned long shared_align_offset(unsigned long filp_pgoff, + unsigned long pgoff) +{ + return (filp_pgoff + pgoff) << PAGE_SHIFT; +} + +static inline unsigned long COLOR_ALIGN(unsigned long addr, + unsigned long filp_pgoff, unsigned long pgoff) +{ + unsigned long base = (addr+SHM_COLOUR-1) & ~(SHM_COLOUR-1); + unsigned long off = (SHM_COLOUR-1) & + shared_align_offset(filp_pgoff, pgoff); + return base + off; +} + + +#define STACK_SIZE_DEFAULT (USER_WIDE_MODE \ + ? (1 << 30) /* 1 GB */ \ + : (CONFIG_STACK_MAX_DEFAULT_SIZE_MB*1024*1024)) + +unsigned long calc_max_stack_size(unsigned long stack_max) +{ +#ifdef CONFIG_COMPAT + if (!USER_WIDE_MODE && (stack_max == COMPAT_RLIM_INFINITY)) + stack_max = STACK_SIZE_DEFAULT; + else +#endif + if (stack_max == RLIM_INFINITY) + stack_max = STACK_SIZE_DEFAULT; + + return stack_max; +} + + +/* + * Top of mmap area (just below the process stack). + */ + +/* + * When called from arch_get_unmapped_area(), rlim_stack will be NULL, + * indicating that "current" should be used instead of a passed-in + * value from the exec bprm as done with arch_pick_mmap_layout(). + */ +static unsigned long mmap_upper_limit(struct rlimit *rlim_stack) +{ + unsigned long stack_base; + + /* Limit stack size - see setup_arg_pages() in fs/exec.c */ + stack_base = rlim_stack ? rlim_stack->rlim_max + : rlimit_max(RLIMIT_STACK); + + stack_base = calc_max_stack_size(stack_base); + + /* Add space for stack randomization. */ + if (current->flags & PF_RANDOMIZE) + stack_base += (STACK_RND_MASK << PAGE_SHIFT); + + return PAGE_ALIGN(STACK_TOP - stack_base); +} + +enum mmap_allocation_direction {UP, DOWN}; + +static unsigned long arch_get_unmapped_area_common(struct file *filp, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags, enum mmap_allocation_direction dir) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma, *prev; + unsigned long filp_pgoff; + int do_color_align; + struct vm_unmapped_area_info info; + + if (unlikely(len > TASK_SIZE)) + return -ENOMEM; + + do_color_align = 0; + if (filp || (flags & MAP_SHARED)) + do_color_align = 1; + filp_pgoff = GET_FILP_PGOFF(filp); + + if (flags & MAP_FIXED) { + /* Even MAP_FIXED mappings must reside within TASK_SIZE */ + if (TASK_SIZE - len < addr) + return -EINVAL; + + if ((flags & MAP_SHARED) && filp && + (addr - shared_align_offset(filp_pgoff, pgoff)) + & (SHM_COLOUR - 1)) + return -EINVAL; + return addr; + } + + if (addr) { + if (do_color_align) + addr = COLOR_ALIGN(addr, filp_pgoff, pgoff); + else + addr = PAGE_ALIGN(addr); + + vma = find_vma_prev(mm, addr, &prev); + if (TASK_SIZE - len >= addr && + (!vma || addr + len <= vm_start_gap(vma)) && + (!prev || addr >= vm_end_gap(prev))) + return addr; + } + + info.length = len; + info.align_mask = do_color_align ? (PAGE_MASK & (SHM_COLOUR - 1)) : 0; + info.align_offset = shared_align_offset(filp_pgoff, pgoff); + + if (dir == DOWN) { + info.flags = VM_UNMAPPED_AREA_TOPDOWN; + info.low_limit = PAGE_SIZE; + info.high_limit = mm->mmap_base; + addr = vm_unmapped_area(&info); + if (!(addr & ~PAGE_MASK)) + return 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. + */ + } + + info.flags = 0; + info.low_limit = mm->mmap_legacy_base; + info.high_limit = mmap_upper_limit(NULL); + return vm_unmapped_area(&info); +} + +unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + return arch_get_unmapped_area_common(filp, + addr, len, pgoff, flags, UP); +} + +unsigned long arch_get_unmapped_area_topdown(struct file *filp, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + return arch_get_unmapped_area_common(filp, + addr, len, pgoff, flags, DOWN); +} + +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) +{ + unsigned long rnd = 0; + + if (current->flags & PF_RANDOMIZE) + rnd = get_random_u32() & MMAP_RND_MASK; + + return rnd << PAGE_SHIFT; +} + +unsigned long arch_mmap_rnd(void) +{ + return (get_random_u32() & MMAP_RND_MASK) << PAGE_SHIFT; +} + +static unsigned long mmap_legacy_base(void) +{ + return TASK_UNMAPPED_BASE + mmap_rnd(); +} + +/* + * 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, struct rlimit *rlim_stack) +{ + mm->mmap_legacy_base = mmap_legacy_base(); + mm->mmap_base = mmap_upper_limit(rlim_stack); + + 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 ksys_mmap_pgoff(addr, len, prot, flags, fd, + pgoff >> (PAGE_SHIFT - 12)); +} + +asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, unsigned long fd, + unsigned long offset) +{ + if (!(offset & ~PAGE_MASK)) { + return ksys_mmap_pgoff(addr, len, prot, flags, fd, + offset >> PAGE_SHIFT); + } else { + return -EINVAL; + } +} + +/* Fucking broken ABI */ + +#ifdef CONFIG_64BIT +asmlinkage long parisc_truncate64(const char __user * path, + unsigned int high, unsigned int low) +{ + return ksys_truncate(path, (long)high << 32 | low); +} + +asmlinkage long parisc_ftruncate64(unsigned int fd, + unsigned int high, unsigned int low) +{ + return ksys_ftruncate(fd, (long)high << 32 | low); +} + +/* stubs for the benefit of the syscall_table since truncate64 and truncate + * are identical on LP64 */ +asmlinkage long sys_truncate64(const char __user * path, unsigned long length) +{ + return ksys_truncate(path, length); +} +asmlinkage long sys_ftruncate64(unsigned int fd, unsigned long length) +{ + return ksys_ftruncate(fd, length); +} +asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return sys_fcntl(fd, cmd, arg); +} +#else + +asmlinkage long parisc_truncate64(const char __user * path, + unsigned int high, unsigned int low) +{ + return ksys_truncate(path, (loff_t)high << 32 | low); +} + +asmlinkage long parisc_ftruncate64(unsigned int fd, + unsigned int high, unsigned int low) +{ + return sys_ftruncate64(fd, (loff_t)high << 32 | low); +} +#endif + +asmlinkage ssize_t parisc_pread64(unsigned int fd, char __user *buf, size_t count, + unsigned int high, unsigned int low) +{ + return ksys_pread64(fd, buf, count, (loff_t)high << 32 | low); +} + +asmlinkage ssize_t parisc_pwrite64(unsigned int fd, const char __user *buf, + size_t count, unsigned int high, unsigned int low) +{ + return ksys_pwrite64(fd, buf, count, (loff_t)high << 32 | low); +} + +asmlinkage ssize_t parisc_readahead(int fd, unsigned int high, unsigned int low, + size_t count) +{ + return ksys_readahead(fd, (loff_t)high << 32 | low, count); +} + +asmlinkage long parisc_fadvise64_64(int fd, + unsigned int high_off, unsigned int low_off, + unsigned int high_len, unsigned int low_len, int advice) +{ + return ksys_fadvise64_64(fd, (loff_t)high_off << 32 | low_off, + (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 ksys_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 ksys_fallocate(fd, mode, ((u64)offhi << 32) | offlo, + ((u64)lenhi << 32) | lenlo); +} + +asmlinkage 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; +} + +/* + * Up to kernel v5.9 we defined O_NONBLOCK as 000200004, + * since then O_NONBLOCK is defined as 000200000. + * + * The following wrapper functions mask out the old + * O_NDELAY bit from calls which use O_NONBLOCK. + * + * XXX: Remove those in year 2022 (or later)? + */ + +#define O_NONBLOCK_OLD 000200004 +#define O_NONBLOCK_MASK_OUT (O_NONBLOCK_OLD & ~O_NONBLOCK) + +static int FIX_O_NONBLOCK(int flags) +{ + if ((flags & O_NONBLOCK_MASK_OUT) && + !test_thread_flag(TIF_NONBLOCK_WARNING)) { + set_thread_flag(TIF_NONBLOCK_WARNING); + pr_warn("%s(%d) uses a deprecated O_NONBLOCK value." + " Please recompile with newer glibc.\n", + current->comm, current->pid); + } + return flags & ~O_NONBLOCK_MASK_OUT; +} + +asmlinkage long parisc_timerfd_create(int clockid, int flags) +{ + flags = FIX_O_NONBLOCK(flags); + return sys_timerfd_create(clockid, flags); +} + +asmlinkage long parisc_signalfd4(int ufd, sigset_t __user *user_mask, + size_t sizemask, int flags) +{ + flags = FIX_O_NONBLOCK(flags); + return sys_signalfd4(ufd, user_mask, sizemask, flags); +} + +#ifdef CONFIG_COMPAT +asmlinkage long parisc_compat_signalfd4(int ufd, + compat_sigset_t __user *user_mask, + compat_size_t sizemask, int flags) +{ + flags = FIX_O_NONBLOCK(flags); + return compat_sys_signalfd4(ufd, user_mask, sizemask, flags); +} +#endif + +asmlinkage long parisc_eventfd2(unsigned int count, int flags) +{ + flags = FIX_O_NONBLOCK(flags); + return sys_eventfd2(count, flags); +} + +asmlinkage long parisc_userfaultfd(int flags) +{ + flags = FIX_O_NONBLOCK(flags); + return sys_userfaultfd(flags); +} + +asmlinkage long parisc_pipe2(int __user *fildes, int flags) +{ + flags = FIX_O_NONBLOCK(flags); + return sys_pipe2(fildes, flags); +} + +asmlinkage long parisc_inotify_init1(int flags) +{ + flags = FIX_O_NONBLOCK(flags); + return sys_inotify_init1(flags); +} + +/* + * madvise() wrapper + * + * Up to kernel v6.1 parisc has different values than all other + * platforms for the MADV_xxx flags listed below. + * To keep binary compatibility with existing userspace programs + * translate the former values to the new values. + * + * XXX: Remove this wrapper in year 2025 (or later) + */ + +asmlinkage notrace long parisc_madvise(unsigned long start, size_t len_in, int behavior) +{ + switch (behavior) { + case 65: behavior = MADV_MERGEABLE; break; + case 66: behavior = MADV_UNMERGEABLE; break; + case 67: behavior = MADV_HUGEPAGE; break; + case 68: behavior = MADV_NOHUGEPAGE; break; + case 69: behavior = MADV_DONTDUMP; break; + case 70: behavior = MADV_DODUMP; break; + case 71: behavior = MADV_WIPEONFORK; break; + case 72: behavior = MADV_KEEPONFORK; break; + case 73: behavior = MADV_COLLAPSE; break; + } + + return sys_madvise(start, len_in, behavior); +} diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c new file mode 100644 index 000000000..2a12a547b --- /dev/null +++ b/arch/parisc/kernel/sys_parisc32.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sys_parisc32.c: Conversion between 32bit and 64bit native syscalls. + * + * 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/compat.h> +#include <linux/kernel.h> +#include <linux/syscalls.h> + + +asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23, + int r22, int r21, int r20) +{ + printk(KERN_ERR "%s(%d): Unimplemented 32 on 64 syscall #%d!\n", + current->comm, current->pid, r20); + return -ENOSYS; +} + +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_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 new file mode 100644 index 000000000..1373e5129 --- /dev/null +++ b/arch/parisc/kernel/syscall.S @@ -0,0 +1,1341 @@ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * System call entry code / Linux gateway page + * Copyright (c) Matthew Wilcox 1999 <willy@infradead.org> + * Licensed under the GNU GPL. + * thanks to Philipp Rumpf, Mike Shaver and various others + * sorry about the wall, puffin.. + */ + +/* +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 + * userspace application. + */ +#define KILL_INSN break 0,0 + + .level PA_ASM_LEVEL + + .macro lws_pagefault_disable reg1,reg2 + mfctl %cr30, \reg2 + ldo TASK_PAGEFAULT_DISABLED(\reg2), \reg2 + ldw 0(%sr2,\reg2), \reg1 + ldo 1(\reg1), \reg1 + stw \reg1, 0(%sr2,\reg2) + .endm + + .macro lws_pagefault_enable reg1,reg2 + mfctl %cr30, \reg2 + ldo TASK_PAGEFAULT_DISABLED(\reg2), \reg2 + ldw 0(%sr2,\reg2), \reg1 + ldo -1(\reg1), \reg1 + stw \reg1, 0(%sr2,\reg2) + .endm + + .text + + .import syscall_exit,code + .import syscall_exit_rfi,code + + /* Linux gateway page is aliased to virtual page 0 in the kernel + * address space. Since it is a gateway page it cannot be + * dereferenced, so null pointers will still fault. We start + * the actual entry point at 0x100. We put break instructions + * at the beginning of the page to trap null indirect function + * pointers. + */ + + .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 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 (5) + +lws_entry: + gate lws_start, %r0 /* increase privilege */ + depi PRIV_USER, 31, 2, %r31 /* Ensure we return into user mode. */ + + /* Fill from 0xb8 to 0xe0 */ + .rept 10 + KILL_INSN + .endr + + /* This function MUST be located at 0xe0 for glibc's threading + mechanism to work. DO NOT MOVE THIS CODE EVER! */ +set_thread_pointer: + gate .+8, %r0 /* increase privilege */ + depi PRIV_USER, 31, 2, %r31 /* Ensure we return into user mode. */ + be 0(%sr7,%r31) /* return to user space */ + mtctl %r26, %cr27 /* move arg0 to the control register */ + + /* Increase the chance of trapping if random jumps occur to this + address, fill from 0xf0 to 0x100 */ + .rept 4 + KILL_INSN + .endr + +/* This address must remain fixed at 0x100 for glibc's syscalls to work */ + .align LINUX_GATEWAY_ADDR +linux_gateway_entry: + gate .+8, %r0 /* become privileged */ + mtsp %r0,%sr4 /* get kernel space into sr4 */ + mtsp %r0,%sr5 /* get kernel space into sr5 */ + mtsp %r0,%sr6 /* get kernel space into sr6 */ + +#ifdef CONFIG_64BIT + /* Store W bit on entry to the syscall in case it's a wide userland + * process. */ + ssm PSW_SM_W, %r1 + extrd,u %r1,PSW_W_BIT,1,%r1 + /* sp must be aligned on 4, so deposit the W bit setting into + * the bottom of sp temporarily */ + or,ev %r1,%r30,%r30 + b,n 1f + /* The top halves of argument registers must be cleared on syscall + * entry from narrow executable. + */ + depdi 0, 31, 32, %r26 + depdi 0, 31, 32, %r25 + depdi 0, 31, 32, %r24 + depdi 0, 31, 32, %r23 + depdi 0, 31, 32, %r22 + depdi 0, 31, 32, %r21 +1: +#endif + + /* We use a rsm/ssm pair to prevent sr3 from being clobbered + * by external interrupts. + */ + mfsp %sr7,%r1 /* save user sr7 */ + rsm PSW_SM_I, %r0 /* disable interrupts */ + mtsp %r1,%sr3 /* and store it in sr3 */ + + mfctl %cr30,%r1 + xor %r1,%r30,%r30 /* ye olde xor trick */ + xor %r1,%r30,%r1 + xor %r1,%r30,%r30 + + LDREG TASK_STACK(%r30),%r30 /* set up kernel stack */ + ldo FRAME_SIZE(%r30),%r30 + /* N.B.: It is critical that we don't set sr7 to 0 until r30 + * contains a valid kernel stack pointer. It is also + * critical that we don't start using the kernel stack + * until after sr7 has been set to 0. + */ + + mtsp %r0,%sr7 /* get kernel space into sr7 */ + ssm PSW_SM_I, %r0 /* enable interrupts */ + STREGM %r1,FRAME_SIZE(%r30) /* save r1 (usp) here for now */ + mfctl %cr30,%r1 /* get task ptr in %r1 */ + + /* Save some registers for sigcontext and potential task + switch (see entry.S for the details of which ones are + saved/restored). TASK_PT_PSW is zeroed so we can see whether + a process is on a syscall or not. For an interrupt the real + PSW value is stored. This is needed for gdb and sys_ptrace. */ + STREG %r0, TASK_PT_PSW(%r1) + STREG %r2, TASK_PT_GR2(%r1) /* preserve rp */ + STREG %r19, TASK_PT_GR19(%r1) + + LDREGM -FRAME_SIZE(%r30), %r2 /* get users sp back */ +#ifdef CONFIG_64BIT + extrd,u %r2,63,1,%r19 /* W hidden in bottom bit */ +#if 0 + xor %r19,%r2,%r2 /* clear bottom bit */ + depd,z %r19,1,1,%r19 + std %r19,TASK_PT_PSW(%r1) +#endif +#endif + STREG %r2, TASK_PT_GR30(%r1) /* ... and save it */ + + 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 */ + STREG %r24, TASK_PT_GR24(%r1) /* 3rd argument */ + STREG %r25, TASK_PT_GR25(%r1) /* 2nd argument */ + 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 %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 */ + + ldo TASK_PT_FR0(%r1), %r27 /* save fpregs from the kernel */ + save_fp %r27 /* or potential task switch */ + + mfctl %cr11, %r27 /* i.e. SAR */ + STREG %r27, TASK_PT_SAR(%r1) + + loadgp + +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ + copy %r19,%r2 /* W bit back to r2 */ +#else + /* no need to save these on stack in wide mode because the first 8 + * args are passed in registers */ + stw %r22, -52(%r30) /* 5th argument */ + stw %r21, -56(%r30) /* 6th argument */ +#endif + + /* Are we being ptraced? */ + mfctl %cr30, %r1 + LDREG TASK_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. */ + +#ifdef CONFIG_64BIT + ldil L%sys_call_table, %r1 + or,= %r2,%r2,%r2 + addil L%(sys_call_table64-sys_call_table), %r1 + ldo R%sys_call_table(%r1), %r19 + or,= %r2,%r2,%r2 + ldo R%sys_call_table64(%r1), %r19 +#else + load32 sys_call_table, %r19 +#endif + comiclr,>> __NR_Linux_syscalls, %r20, %r0 + b,n .Lsyscall_nosys + + LDREGX %r20(%r19), %r19 + + /* If this is a sys_rt_sigreturn call, and the signal was received + * when not in_syscall, then we want to return via syscall_exit_rfi, + * not syscall_exit. Signal no. in r20, in_syscall in r25 (see + * trampoline code in signal.c). + */ + ldi __NR_rt_sigreturn,%r2 + comb,= %r2,%r20,.Lrt_sigreturn +.Lin_syscall: + ldil L%syscall_exit,%r2 + be 0(%sr7,%r19) + ldo R%syscall_exit(%r2),%r2 +.Lrt_sigreturn: + comib,<> 0,%r25,.Lin_syscall + ldil L%syscall_exit_rfi,%r2 + be 0(%sr7,%r19) + ldo R%syscall_exit_rfi(%r2),%r2 + + /* Note! Because we are not running where we were linked, any + calls to functions external to this file must be indirect. To + be safe, we apply the opposite rule to functions within this + file, with local labels given to them to ensure correctness. */ + +.Lsyscall_nosys: +syscall_nosys: + ldil L%syscall_exit,%r1 + be R%syscall_exit(%sr7,%r1) + ldo -ENOSYS(%r0),%r28 /* set errno */ + + +/* Warning! This trace code is a virtual duplicate of the code above so be + * sure to maintain both! */ +.Ltracesys: +tracesys: + /* Need to save more registers so the debugger can see where we + * are. This saves only the lower 8 bits of PSW, so that the C + * bit is still clear on syscalls, and the D bit is set if this + * full register save path has been executed. We check the D + * bit on syscall_return_rfi to determine which registers to + * restore. An interrupt results in a full PSW saved with the + * C bit set, a non-straced syscall entry results in C and D clear + * in the saved PSW. + */ + mfctl %cr30,%r1 /* get task ptr */ + ssm 0,%r2 + STREG %r2,TASK_PT_PSW(%r1) /* Lower 8 bits only!! */ + mfsp %sr0,%r2 + STREG %r2,TASK_PT_SR0(%r1) + mfsp %sr1,%r2 + STREG %r2,TASK_PT_SR1(%r1) + mfsp %sr2,%r2 + STREG %r2,TASK_PT_SR2(%r1) + mfsp %sr3,%r2 + STREG %r2,TASK_PT_SR3(%r1) + STREG %r2,TASK_PT_SR4(%r1) + STREG %r2,TASK_PT_SR5(%r1) + STREG %r2,TASK_PT_SR6(%r1) + STREG %r2,TASK_PT_SR7(%r1) + STREG %r2,TASK_PT_IASQ0(%r1) + STREG %r2,TASK_PT_IASQ1(%r1) + LDREG TASK_PT_GR31(%r1),%r2 + STREG %r2,TASK_PT_IAOQ0(%r1) + ldo 4(%r2),%r2 + STREG %r2,TASK_PT_IAOQ1(%r1) + ldo TASK_REGS(%r1),%r2 + /* reg_save %r2 */ + STREG %r3,PT_GR3(%r2) + STREG %r4,PT_GR4(%r2) + STREG %r5,PT_GR5(%r2) + STREG %r6,PT_GR6(%r2) + STREG %r7,PT_GR7(%r2) + STREG %r8,PT_GR8(%r2) + STREG %r9,PT_GR9(%r2) + STREG %r10,PT_GR10(%r2) + STREG %r11,PT_GR11(%r2) + STREG %r12,PT_GR12(%r2) + STREG %r13,PT_GR13(%r2) + STREG %r14,PT_GR14(%r2) + STREG %r15,PT_GR15(%r2) + STREG %r16,PT_GR16(%r2) + STREG %r17,PT_GR17(%r2) + STREG %r18,PT_GR18(%r2) + /* Finished saving things for the debugger */ + + copy %r2,%r26 + ldil L%do_syscall_trace_enter,%r1 + ldil L%tracesys_next,%r2 + be R%do_syscall_trace_enter(%sr7,%r1) + ldo R%tracesys_next(%r2),%r2 + +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 + + mfctl %cr30,%r1 /* get task ptr */ + LDREG TASK_PT_GR28(%r1), %r28 /* Restore return value */ + 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 + 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 + + cmpib,COND(=),n -1,%r20,tracesys_exit /* seccomp may have returned -1 */ + comiclr,>> __NR_Linux_syscalls, %r20, %r0 + b,n .Ltracesys_nosys + + /* Note! We cannot use the syscall table that is mapped + nearby since the gateway page is mapped execute-only. */ + +#ifdef CONFIG_64BIT + LDREG TASK_PT_GR30(%r1), %r19 /* get users sp back */ + extrd,u %r19,63,1,%r2 /* W hidden in bottom bit */ + + ldil L%sys_call_table, %r1 + or,= %r2,%r2,%r2 + addil L%(sys_call_table64-sys_call_table), %r1 + ldo R%sys_call_table(%r1), %r19 + or,= %r2,%r2,%r2 + ldo R%sys_call_table64(%r1), %r19 +#else + load32 sys_call_table, %r19 +#endif + + LDREGX %r20(%r19), %r19 + + /* If this is a sys_rt_sigreturn call, and the signal was received + * when not in_syscall, then we want to return via syscall_exit_rfi, + * not syscall_exit. Signal no. in r20, in_syscall in r25 (see + * trampoline code in signal.c). + */ + ldi __NR_rt_sigreturn,%r2 + comb,= %r2,%r20,.Ltrace_rt_sigreturn +.Ltrace_in_syscall: + ldil L%tracesys_exit,%r2 + be 0(%sr7,%r19) + ldo R%tracesys_exit(%r2),%r2 + +.Ltracesys_nosys: + ldo -ENOSYS(%r0),%r28 /* set errno */ + + /* Do *not* call this function on the gateway page, because it + makes a direct call to syscall_trace. */ + +tracesys_exit: + mfctl %cr30,%r1 /* get task ptr */ +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ +#endif + ldo TASK_REGS(%r1),%r26 + BL do_syscall_trace_exit,%r2 + STREG %r28,TASK_PT_GR28(%r1) /* save return value now */ + mfctl %cr30,%r1 /* get task ptr */ + LDREG TASK_PT_GR28(%r1), %r28 /* Restore return val. */ + + ldil L%syscall_exit,%r1 + be,n R%syscall_exit(%sr7,%r1) + +.Ltrace_rt_sigreturn: + comib,<> 0,%r25,.Ltrace_in_syscall + ldil L%tracesys_sigexit,%r2 + be 0(%sr7,%r19) + ldo R%tracesys_sigexit(%r2),%r2 + +tracesys_sigexit: + mfctl %cr30,%r1 /* get task ptr */ +#ifdef CONFIG_64BIT + ldo -16(%r30),%r29 /* Reference param save area */ +#endif + 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) + + + /********************************************************* + 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. + + Caller-saves registers + - %r1, %r27, %r29 + - %r2 (return pointer) + - %r31 (ble link register) + * In asm clobber. + + Callee-saves registers + - %r3-%r18 + - %r30 (stack pointer) + * Not in asm clobber. + + 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: + + ENOSYS - r20 was an invalid LWS number. + + *********************************************************/ +lws_start: + +#ifdef CONFIG_64BIT + ssm PSW_SM_W, %r1 + extrd,u %r1,PSW_W_BIT,1,%r1 + /* sp must be aligned on 4, so deposit the W bit setting into + * the bottom of sp temporarily */ + or,od %r1,%r30,%r30 + + /* Clip LWS number to a 32-bit value for 32-bit processes */ + depdi 0, 31, 32, %r20 +#endif + + /* Is the lws entry number valid? */ + comiclr,>> __NR_lws_entries, %r20, %r0 + b,n lws_exit_nosys + + /* Load table start */ + ldil L%lws_table, %r1 + ldo R%lws_table(%r1), %r28 /* Scratch use of r28 */ + LDREGX %r20(%sr2,r28), %r21 /* Scratch use of r21 */ + + /* Jump to lws, lws table pointers already relocated */ + be,n 0(%sr2,%r21) + +lws_exit_noerror: + lws_pagefault_enable %r1,%r21 + stw,ma %r20, 0(%sr2,%r20) + ssm PSW_SM_I, %r0 + b lws_exit + copy %r0, %r21 + +lws_wouldblock: + ssm PSW_SM_I, %r0 + ldo 2(%r0), %r28 + b lws_exit + ldo -EAGAIN(%r0), %r21 + +lws_pagefault: + lws_pagefault_enable %r1,%r21 + stw,ma %r20, 0(%sr2,%r20) + ssm PSW_SM_I, %r0 + ldo 3(%r0),%r28 + b lws_exit + ldo -EAGAIN(%r0),%r21 + +lws_fault: + ldo 1(%r0),%r28 + b lws_exit + ldo -EFAULT(%r0),%r21 + +lws_exit_nosys: + ldo -ENOSYS(%r0),%r21 + /* Fall through: Return to userspace */ + +lws_exit: +#ifdef CONFIG_64BIT + /* decide whether to reset the wide mode bit + * + * For a syscall, the W bit is stored in the lowest bit + * of sp. Extract it and reset W if it is zero */ + extrd,u,*<> %r30,63,1,%r1 + rsm PSW_SM_W, %r0 + /* now reset the lowest bit of sp if it was set */ + xor %r30,%r1,%r30 +#endif + be,n 0(%sr7, %r31) + + + + /*************************************************** + Implementing 32bit CAS as an atomic operation: + + %r26 - Address to examine + %r25 - Old value to check (old) + %r24 - New value to set (new) + %r28 - Return prev through this register. + %r21 - Kernel error code + + %r21 returns the following error codes: + EAGAIN - CAS is busy, ldcw failed, try again. + EFAULT - Read or write failed. + + If EAGAIN is returned, %r28 indicates the busy reason: + r28 == 1 - CAS is busy. lock contended. + r28 == 2 - CAS is busy. ldcw failed. + r28 == 3 - CAS is busy. page fault. + + Scratch: r20, r28, r1 + + ****************************************************/ + + /* ELF64 Process entry path */ +lws_compare_and_swap64: +#ifdef CONFIG_64BIT + b,n lws_compare_and_swap +#else + /* If we are not a 64-bit kernel, then we don't + * have 64-bit input registers, and calling + * the 64-bit LWS CAS returns ENOSYS. + */ + b,n lws_exit_nosys +#endif + + /* ELF32/ELF64 Process entry path */ +lws_compare_and_swap32: +#ifdef CONFIG_64BIT + /* Wide mode user process? */ + bb,<,n %sp, 31, lws_compare_and_swap + + /* Clip all the input registers for 32-bit processes */ + depdi 0, 31, 32, %r26 + depdi 0, 31, 32, %r25 + depdi 0, 31, 32, %r24 +#endif + +lws_compare_and_swap: + /* Trigger memory reference interruptions without writing to memory */ +1: ldw 0(%r26), %r28 +2: stbys,e %r0, 0(%r26) + + /* Calculate 8-bit hash index from virtual address */ + extru_safe %r26, 27, 8, %r20 + + /* Load start of lock table */ + ldil L%lws_lock_start, %r28 + ldo R%lws_lock_start(%r28), %r28 + + /* Find lock to use, the hash index is one of 0 to + 255, multiplied by 16 (keep it 16-byte aligned) + and add to the lock table offset. */ + shlw %r20, 4, %r20 + add %r20, %r28, %r20 + + rsm PSW_SM_I, %r0 /* Disable interrupts */ + + /* Try to acquire the lock */ + LDCW 0(%sr2,%r20), %r28 + comclr,<> %r0, %r28, %r0 + b,n lws_wouldblock + + /* Disable page faults to prevent sleeping in critical region */ + lws_pagefault_disable %r21,%r28 + + /* + prev = *addr; + if ( prev == old ) + *addr = new; + return prev; + */ + + /* NOTES: + This all works because intr_do_signal + and schedule both check the return iasq + and see that we are on the kernel page + so this process is never scheduled off + or is ever sent any signal of any sort, + thus it is wholly atomic from usrspace's + perspective + */ + /* The load and store could fail */ +3: ldw 0(%r26), %r28 + sub,<> %r28, %r25, %r0 +4: stw %r24, 0(%r26) + b,n lws_exit_noerror + + /* A fault occurred on load or stbys,e store */ +5: b,n lws_fault + ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 5b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 5b-linux_gateway_page) + + /* A page fault occurred in critical region */ +6: b,n lws_pagefault + ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 6b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 6b-linux_gateway_page) + + + /*************************************************** + New CAS implementation which uses pointers and variable size + information. The value pointed by old and new MUST NOT change + while performing CAS. The lock only protects the value at %r26. + + %r26 - Address to examine + %r25 - Pointer to the value to check (old) + %r24 - Pointer to the value to set (new) + %r23 - Size of the variable (0/1/2/3 for 8/16/32/64 bit) + %r28 - Return non-zero on failure + %r21 - Kernel error code + + %r21 returns the following error codes: + EAGAIN - CAS is busy, ldcw failed, try again. + EFAULT - Read or write failed. + + If EAGAIN is returned, %r28 indicates the busy reason: + r28 == 1 - CAS is busy. lock contended. + r28 == 2 - CAS is busy. ldcw failed. + r28 == 3 - CAS is busy. page fault. + + Scratch: r20, r22, r28, r29, r1, fr4 (32bit for 64bit CAS only) + + ****************************************************/ + +lws_compare_and_swap_2: +#ifdef CONFIG_64BIT + /* Wide mode user process? */ + bb,<,n %sp, 31, cas2_begin + + /* Clip the input registers for 32-bit processes. We don't + need to clip %r23 as we only use it for word operations */ + depdi 0, 31, 32, %r26 + depdi 0, 31, 32, %r25 + depdi 0, 31, 32, %r24 +#endif + +cas2_begin: + /* Check the validity of the size pointer */ + subi,>>= 3, %r23, %r0 + b,n lws_exit_nosys + + /* Jump to the functions which will load the old and new values into + registers depending on the their size */ + shlw %r23, 2, %r29 + blr %r29, %r0 + nop + + /* 8-bit load */ +1: ldb 0(%r25), %r25 + b cas2_lock_start +2: ldb 0(%r24), %r24 + nop + nop + nop + nop + nop + + /* 16-bit load */ +3: ldh 0(%r25), %r25 + b cas2_lock_start +4: ldh 0(%r24), %r24 + nop + nop + nop + nop + nop + + /* 32-bit load */ +5: ldw 0(%r25), %r25 + b cas2_lock_start +6: ldw 0(%r24), %r24 + nop + nop + nop + nop + nop + + /* 64-bit load */ +#ifdef CONFIG_64BIT +7: ldd 0(%r25), %r25 +8: ldd 0(%r24), %r24 +#else + /* Load old value into r22/r23 - high/low */ +7: ldw 0(%r25), %r22 +8: ldw 4(%r25), %r23 + /* Load new value into fr4 for atomic store later */ +9: flddx 0(%r24), %fr4 +#endif + +cas2_lock_start: + /* Trigger memory reference interruptions without writing to memory */ + copy %r26, %r28 + depi_safe 0, 31, 2, %r28 +10: ldw 0(%r28), %r1 +11: stbys,e %r0, 0(%r28) + + /* Calculate 8-bit hash index from virtual address */ + extru_safe %r26, 27, 8, %r20 + + /* Load start of lock table */ + ldil L%lws_lock_start, %r28 + ldo R%lws_lock_start(%r28), %r28 + + /* Find lock to use, the hash index is one of 0 to + 255, multiplied by 16 (keep it 16-byte aligned) + and add to the lock table offset. */ + shlw %r20, 4, %r20 + add %r20, %r28, %r20 + + rsm PSW_SM_I, %r0 /* Disable interrupts */ + + /* Try to acquire the lock */ + LDCW 0(%sr2,%r20), %r28 + comclr,<> %r0, %r28, %r0 + b,n lws_wouldblock + + /* Disable page faults to prevent sleeping in critical region */ + lws_pagefault_disable %r21,%r28 + + /* + prev = *addr; + if ( prev == old ) + *addr = new; + return prev; + */ + + /* NOTES: + This all works because intr_do_signal + and schedule both check the return iasq + and see that we are on the kernel page + so this process is never scheduled off + or is ever sent any signal of any sort, + thus it is wholly atomic from usrspace's + perspective + */ + + /* Jump to the correct function */ + blr %r29, %r0 + /* Set %r28 as non-zero for now */ + ldo 1(%r0),%r28 + + /* 8-bit CAS */ +12: ldb 0(%r26), %r29 + sub,= %r29, %r25, %r0 + b,n lws_exit_noerror +13: stb %r24, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + nop + nop + + /* 16-bit CAS */ +14: ldh 0(%r26), %r29 + sub,= %r29, %r25, %r0 + b,n lws_exit_noerror +15: sth %r24, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + nop + nop + + /* 32-bit CAS */ +16: ldw 0(%r26), %r29 + sub,= %r29, %r25, %r0 + b,n lws_exit_noerror +17: stw %r24, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + nop + nop + + /* 64-bit CAS */ +#ifdef CONFIG_64BIT +18: ldd 0(%r26), %r29 + sub,*= %r29, %r25, %r0 + b,n lws_exit_noerror +19: std %r24, 0(%r26) + copy %r0, %r28 +#else + /* Compare first word */ +18: ldw 0(%r26), %r29 + sub,= %r29, %r22, %r0 + b,n lws_exit_noerror + /* Compare second word */ +19: ldw 4(%r26), %r29 + sub,= %r29, %r23, %r0 + b,n lws_exit_noerror + /* Perform the store */ +20: fstdx %fr4, 0(%r26) + copy %r0, %r28 +#endif + b lws_exit_noerror + copy %r0, %r28 + + /* A fault occurred on load or stbys,e store */ +30: b,n lws_fault + ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 30b-linux_gateway_page) +#ifndef CONFIG_64BIT + ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 30b-linux_gateway_page) +#endif + + ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 30b-linux_gateway_page) + + /* A page fault occurred in critical region */ +31: b,n lws_pagefault + ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 31b-linux_gateway_page) +#ifndef CONFIG_64BIT + ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 31b-linux_gateway_page) +#endif + + + /*************************************************** + LWS atomic exchange. + + %r26 - Exchange address + %r25 - Size of the variable (0/1/2/3 for 8/16/32/64 bit) + %r24 - Address of new value + %r23 - Address of old value + %r28 - Return non-zero on failure + %r21 - Kernel error code + + %r21 returns the following error codes: + EAGAIN - CAS is busy, ldcw failed, try again. + EFAULT - Read or write failed. + + If EAGAIN is returned, %r28 indicates the busy reason: + r28 == 1 - CAS is busy. lock contended. + r28 == 2 - CAS is busy. ldcw failed. + r28 == 3 - CAS is busy. page fault. + + Scratch: r20, r1 + + ****************************************************/ + +lws_atomic_xchg: +#ifdef CONFIG_64BIT + /* Wide mode user process? */ + bb,<,n %sp, 31, atomic_xchg_begin + + /* Clip the input registers for 32-bit processes. We don't + need to clip %r23 as we only use it for word operations */ + depdi 0, 31, 32, %r26 + depdi 0, 31, 32, %r25 + depdi 0, 31, 32, %r24 + depdi 0, 31, 32, %r23 +#endif + +atomic_xchg_begin: + /* Check the validity of the size pointer */ + subi,>>= 3, %r25, %r0 + b,n lws_exit_nosys + + /* Jump to the functions which will load the old and new values into + registers depending on the their size */ + shlw %r25, 2, %r1 + blr %r1, %r0 + nop + + /* Perform exception checks */ + + /* 8-bit exchange */ +1: ldb 0(%r24), %r20 + copy %r23, %r20 + depi_safe 0, 31, 2, %r20 + b atomic_xchg_start +2: stbys,e %r0, 0(%r20) + nop + nop + nop + + /* 16-bit exchange */ +3: ldh 0(%r24), %r20 + copy %r23, %r20 + depi_safe 0, 31, 2, %r20 + b atomic_xchg_start +4: stbys,e %r0, 0(%r20) + nop + nop + nop + + /* 32-bit exchange */ +5: ldw 0(%r24), %r20 + b atomic_xchg_start +6: stbys,e %r0, 0(%r23) + nop + nop + nop + nop + nop + + /* 64-bit exchange */ +#ifdef CONFIG_64BIT +7: ldd 0(%r24), %r20 +8: stdby,e %r0, 0(%r23) +#else +7: ldw 0(%r24), %r20 +8: ldw 4(%r24), %r20 + copy %r23, %r20 + depi_safe 0, 31, 2, %r20 +9: stbys,e %r0, 0(%r20) +10: stbys,e %r0, 4(%r20) +#endif + +atomic_xchg_start: + /* Trigger memory reference interruptions without writing to memory */ + copy %r26, %r28 + depi_safe 0, 31, 2, %r28 +11: ldw 0(%r28), %r1 +12: stbys,e %r0, 0(%r28) + + /* Calculate 8-bit hash index from virtual address */ + extru_safe %r26, 27, 8, %r20 + + /* Load start of lock table */ + ldil L%lws_lock_start, %r28 + ldo R%lws_lock_start(%r28), %r28 + + /* Find lock to use, the hash index is one of 0 to + 255, multiplied by 16 (keep it 16-byte aligned) + and add to the lock table offset. */ + shlw %r20, 4, %r20 + add %r20, %r28, %r20 + + rsm PSW_SM_I, %r0 /* Disable interrupts */ + + /* Try to acquire the lock */ + LDCW 0(%sr2,%r20), %r28 + comclr,<> %r0, %r28, %r0 + b,n lws_wouldblock + + /* Disable page faults to prevent sleeping in critical region */ + lws_pagefault_disable %r21,%r28 + + /* NOTES: + This all works because intr_do_signal + and schedule both check the return iasq + and see that we are on the kernel page + so this process is never scheduled off + or is ever sent any signal of any sort, + thus it is wholly atomic from userspace's + perspective + */ + + /* Jump to the correct function */ + blr %r1, %r0 + /* Set %r28 as non-zero for now */ + ldo 1(%r0),%r28 + + /* 8-bit exchange */ +14: ldb 0(%r26), %r1 +15: stb %r1, 0(%r23) +15: ldb 0(%r24), %r1 +17: stb %r1, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + nop + nop + + /* 16-bit exchange */ +18: ldh 0(%r26), %r1 +19: sth %r1, 0(%r23) +20: ldh 0(%r24), %r1 +21: sth %r1, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + nop + nop + + /* 32-bit exchange */ +22: ldw 0(%r26), %r1 +23: stw %r1, 0(%r23) +24: ldw 0(%r24), %r1 +25: stw %r1, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + nop + nop + + /* 64-bit exchange */ +#ifdef CONFIG_64BIT +26: ldd 0(%r26), %r1 +27: std %r1, 0(%r23) +28: ldd 0(%r24), %r1 +29: std %r1, 0(%r26) +#else +26: flddx 0(%r26), %fr4 +27: fstdx %fr4, 0(%r23) +28: flddx 0(%r24), %fr4 +29: fstdx %fr4, 0(%r26) +#endif + b lws_exit_noerror + copy %r0, %r28 + + /* A fault occurred on load or stbys,e store */ +30: b,n lws_fault + ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 30b-linux_gateway_page) +#ifndef CONFIG_64BIT + ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 30b-linux_gateway_page) +#endif + + ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 30b-linux_gateway_page) + + /* A page fault occurred in critical region */ +31: b,n lws_pagefault + ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(21b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(22b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(23b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(24b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(25b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(26b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(27b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(28b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(29b-linux_gateway_page, 31b-linux_gateway_page) + + /*************************************************** + LWS atomic store. + + %r26 - Address to store + %r25 - Size of the variable (0/1/2/3 for 8/16/32/64 bit) + %r24 - Address of value to store + %r28 - Return non-zero on failure + %r21 - Kernel error code + + %r21 returns the following error codes: + EAGAIN - CAS is busy, ldcw failed, try again. + EFAULT - Read or write failed. + + If EAGAIN is returned, %r28 indicates the busy reason: + r28 == 1 - CAS is busy. lock contended. + r28 == 2 - CAS is busy. ldcw failed. + r28 == 3 - CAS is busy. page fault. + + Scratch: r20, r1 + + ****************************************************/ + +lws_atomic_store: +#ifdef CONFIG_64BIT + /* Wide mode user process? */ + bb,<,n %sp, 31, atomic_store_begin + + /* Clip the input registers for 32-bit processes. We don't + need to clip %r23 as we only use it for word operations */ + depdi 0, 31, 32, %r26 + depdi 0, 31, 32, %r25 + depdi 0, 31, 32, %r24 +#endif + +atomic_store_begin: + /* Check the validity of the size pointer */ + subi,>>= 3, %r25, %r0 + b,n lws_exit_nosys + + shlw %r25, 1, %r1 + blr %r1, %r0 + nop + + /* Perform exception checks */ + + /* 8-bit store */ +1: ldb 0(%r24), %r20 + b,n atomic_store_start + nop + nop + + /* 16-bit store */ +2: ldh 0(%r24), %r20 + b,n atomic_store_start + nop + nop + + /* 32-bit store */ +3: ldw 0(%r24), %r20 + b,n atomic_store_start + nop + nop + + /* 64-bit store */ +#ifdef CONFIG_64BIT +4: ldd 0(%r24), %r20 +#else +4: ldw 0(%r24), %r20 +5: ldw 4(%r24), %r20 +#endif + +atomic_store_start: + /* Trigger memory reference interruptions without writing to memory */ + copy %r26, %r28 + depi_safe 0, 31, 2, %r28 +6: ldw 0(%r28), %r1 +7: stbys,e %r0, 0(%r28) + + /* Calculate 8-bit hash index from virtual address */ + extru_safe %r26, 27, 8, %r20 + + /* Load start of lock table */ + ldil L%lws_lock_start, %r28 + ldo R%lws_lock_start(%r28), %r28 + + /* Find lock to use, the hash index is one of 0 to + 255, multiplied by 16 (keep it 16-byte aligned) + and add to the lock table offset. */ + shlw %r20, 4, %r20 + add %r20, %r28, %r20 + + rsm PSW_SM_I, %r0 /* Disable interrupts */ + + /* Try to acquire the lock */ + LDCW 0(%sr2,%r20), %r28 + comclr,<> %r0, %r28, %r0 + b,n lws_wouldblock + + /* Disable page faults to prevent sleeping in critical region */ + lws_pagefault_disable %r21,%r28 + + /* NOTES: + This all works because intr_do_signal + and schedule both check the return iasq + and see that we are on the kernel page + so this process is never scheduled off + or is ever sent any signal of any sort, + thus it is wholly atomic from userspace's + perspective + */ + + /* Jump to the correct function */ + blr %r1, %r0 + /* Set %r28 as non-zero for now */ + ldo 1(%r0),%r28 + + /* 8-bit store */ +9: ldb 0(%r24), %r1 +10: stb %r1, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + + /* 16-bit store */ +11: ldh 0(%r24), %r1 +12: sth %r1, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + + /* 32-bit store */ +13: ldw 0(%r24), %r1 +14: stw %r1, 0(%r26) + b lws_exit_noerror + copy %r0, %r28 + + /* 64-bit store */ +#ifdef CONFIG_64BIT +15: ldd 0(%r24), %r1 +16: std %r1, 0(%r26) +#else +15: flddx 0(%r24), %fr4 +16: fstdx %fr4, 0(%r26) +#endif + b lws_exit_noerror + copy %r0, %r28 + + /* A fault occurred on load or stbys,e store */ +30: b,n lws_fault + ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page) +#ifndef CONFIG_64BIT + ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page) +#endif + + ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page) + + /* A page fault occurred in critical region */ +31: b,n lws_pagefault + ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page) + ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page) + + /* Make sure nothing else is placed on this 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 */ + +#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. */ +ENTRY(lws_table) + LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic 32bit CAS */ + LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic 32bit CAS */ + LWS_ENTRY(compare_and_swap_2) /* 2 - Atomic 64bit CAS */ + LWS_ENTRY(atomic_xchg) /* 3 - Atomic Exchange */ + LWS_ENTRY(atomic_store) /* 4 - Atomic Store */ +END(lws_table) + /* End of lws table */ + +#ifdef CONFIG_64BIT +#define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, compat) +#else +#define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, native) +#endif +#define __SYSCALL(nr, entry) ASM_ULONG_INSN entry + .align 8 +ENTRY(sys_call_table) + .export sys_call_table,data +#include <asm/syscall_table_32.h> /* 32-bit syscalls */ +END(sys_call_table) + +#ifdef CONFIG_64BIT + .align 8 +ENTRY(sys_call_table64) +#include <asm/syscall_table_64.h> /* 64-bit syscalls */ +END(sys_call_table64) +#endif + + /* + 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 L1_CACHE_BYTES +ENTRY(lws_lock_start) + /* lws locks */ + .rept 256 + /* Keep locks aligned at 16-bytes */ + .word 1 + .word 0 + .word 0 + .word 0 + .endr +END(lws_lock_start) + .previous + +.end diff --git a/arch/parisc/kernel/syscalls/Makefile b/arch/parisc/kernel/syscalls/Makefile new file mode 100644 index 000000000..8440c16df --- /dev/null +++ b/arch/parisc/kernel/syscalls/Makefile @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0 +kapi := arch/$(SRCARCH)/include/generated/asm +uapi := arch/$(SRCARCH)/include/generated/uapi/asm + +$(shell mkdir -p $(uapi) $(kapi)) + +syscall := $(src)/syscall.tbl +syshdr := $(srctree)/scripts/syscallhdr.sh +systbl := $(srctree)/scripts/syscalltbl.sh + +quiet_cmd_syshdr = SYSHDR $@ + cmd_syshdr = $(CONFIG_SHELL) $(syshdr) --emit-nr --abis common,$* $< $@ + +quiet_cmd_systbl = SYSTBL $@ + cmd_systbl = $(CONFIG_SHELL) $(systbl) --abis common,$* $< $@ + +$(uapi)/unistd_%.h: $(syscall) $(syshdr) FORCE + $(call if_changed,syshdr) + +$(kapi)/syscall_table_%.h: $(syscall) $(systbl) FORCE + $(call if_changed,systbl) + +uapisyshdr-y += unistd_32.h unistd_64.h +kapisyshdr-y += syscall_table_32.h \ + syscall_table_64.h + +uapisyshdr-y := $(addprefix $(uapi)/, $(uapisyshdr-y)) +kapisyshdr-y := $(addprefix $(kapi)/, $(kapisyshdr-y)) +targets += $(addprefix ../../../../, $(uapisyshdr-y) $(kapisyshdr-y)) + +PHONY += all +all: $(uapisyshdr-y) $(kapisyshdr-y) + @: diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl new file mode 100644 index 000000000..0e42fceb2 --- /dev/null +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -0,0 +1,450 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for parisc +# +# The format is: +# <number> <abi> <name> <entry point> <compat entry point> +# +# The <abi> can be common, 64, or 32 for this file. +# +0 common restart_syscall sys_restart_syscall +1 common exit sys_exit +2 common fork sys_fork_wrapper +3 common read sys_read +4 common write sys_write +5 common open sys_open compat_sys_open +6 common close sys_close +7 common waitpid sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 common execve sys_execve compat_sys_execve +12 common chdir sys_chdir +13 32 time sys_time32 +13 64 time sys_time +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common lchown sys_lchown +17 common socket sys_socket +18 common stat sys_newstat compat_sys_newstat +19 common lseek sys_lseek compat_sys_lseek +20 common getpid sys_getpid +21 common mount sys_mount +22 common bind sys_bind +23 common setuid sys_setuid +24 common getuid sys_getuid +25 32 stime sys_stime32 +25 64 stime sys_stime +26 common ptrace sys_ptrace compat_sys_ptrace +27 common alarm sys_alarm +28 common fstat sys_newfstat compat_sys_newfstat +29 common pause sys_pause +30 32 utime sys_utime32 +30 64 utime sys_utime +31 common connect sys_connect +32 common listen sys_listen +33 common access sys_access +34 common nice sys_nice +35 common accept sys_accept +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_pipe +43 common times sys_times compat_sys_times +44 common getsockname sys_getsockname +45 common brk sys_brk +46 common setgid sys_setgid +47 common getgid sys_getgid +48 common signal sys_signal +49 common geteuid sys_geteuid +50 common getegid sys_getegid +51 common acct sys_acct +52 common umount2 sys_umount +53 common getpeername sys_getpeername +54 common ioctl sys_ioctl compat_sys_ioctl +55 common fcntl sys_fcntl compat_sys_fcntl +56 common socketpair sys_socketpair +57 common setpgid sys_setpgid +58 common send sys_send +59 common uname sys_newuname +60 common umask sys_umask +61 common chroot sys_chroot +62 common ustat sys_ustat compat_sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 common pivot_root sys_pivot_root +68 common sgetmask sys_sgetmask sys32_unimplemented +69 common ssetmask sys_ssetmask sys32_unimplemented +70 common setreuid sys_setreuid +71 common setregid sys_setregid +72 common mincore sys_mincore +73 common sigpending sys_sigpending compat_sys_sigpending +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit compat_sys_setrlimit +76 common getrlimit sys_getrlimit compat_sys_getrlimit +77 common getrusage sys_getrusage compat_sys_getrusage +78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday +79 common settimeofday sys_settimeofday compat_sys_settimeofday +80 common getgroups sys_getgroups +81 common setgroups sys_setgroups +82 common sendto sys_sendto +83 common symlink sys_symlink +84 common lstat sys_newlstat compat_sys_newlstat +85 common readlink sys_readlink +86 common uselib sys_ni_syscall +87 common swapon sys_swapon +88 common reboot sys_reboot +89 common mmap2 sys_mmap2 +90 common mmap sys_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate compat_sys_truncate +93 common ftruncate sys_ftruncate compat_sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +98 common recv sys_recv +99 common statfs sys_statfs compat_sys_statfs +100 common fstatfs sys_fstatfs compat_sys_fstatfs +101 common stat64 sys_stat64 +# 102 was socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer compat_sys_setitimer +105 common getitimer sys_getitimer compat_sys_getitimer +106 common capget sys_capget +107 common capset sys_capset +108 32 pread64 parisc_pread64 +108 64 pread64 sys_pread64 +109 32 pwrite64 parisc_pwrite64 +109 64 pwrite64 sys_pwrite64 +110 common getcwd sys_getcwd +111 common vhangup sys_vhangup +112 common fstat64 sys_fstat64 +113 common vfork sys_vfork_wrapper +114 common wait4 sys_wait4 compat_sys_wait4 +115 common swapoff sys_swapoff +116 common sysinfo sys_sysinfo compat_sys_sysinfo +117 common shutdown sys_shutdown +118 common fsync sys_fsync +119 common madvise parisc_madvise +120 common clone sys_clone_wrapper +121 common setdomainname sys_setdomainname +122 common sendfile sys_sendfile compat_sys_sendfile +123 common recvfrom sys_recvfrom +124 32 adjtimex sys_adjtimex_time32 +124 64 adjtimex sys_adjtimex +125 common mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask compat_sys_sigprocmask +# 127 was create_module +128 common init_module sys_init_module +129 common delete_module sys_delete_module +# 130 was get_kernel_syms +131 common quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_ni_syscall +135 common sysfs sys_sysfs +136 32 personality parisc_personality +136 64 personality sys_personality +# 137 was afs_syscall +138 common setfsuid sys_setfsuid +139 common setfsgid sys_setfsgid +140 common _llseek sys_llseek +141 common getdents sys_getdents compat_sys_getdents +142 common _newselect sys_select compat_sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv +146 common writev sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 common _sysctl sys_ni_syscall +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +161 64 sched_rr_get_interval sys_sched_rr_get_interval +162 32 nanosleep sys_nanosleep_time32 +162 64 nanosleep sys_nanosleep +163 common mremap sys_mremap +164 common setresuid sys_setresuid +165 common getresuid sys_getresuid +166 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +# 167 was query_module +168 common poll sys_poll +# 169 was nfsservctl +170 common setresgid sys_setresgid +171 common getresgid sys_getresgid +172 common prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn_wrapper +174 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +177 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +177 64 rt_sigtimedwait sys_rt_sigtimedwait +178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +180 common chown sys_chown +181 common setsockopt sys_setsockopt sys_setsockopt +182 common getsockopt sys_getsockopt sys_getsockopt +183 common sendmsg sys_sendmsg compat_sys_sendmsg +184 common recvmsg sys_recvmsg compat_sys_recvmsg +185 common semop sys_semop +186 common semget sys_semget +187 common semctl sys_semctl compat_sys_semctl +188 common msgsnd sys_msgsnd compat_sys_msgsnd +189 common msgrcv sys_msgrcv compat_sys_msgrcv +190 common msgget sys_msgget +191 common msgctl sys_msgctl compat_sys_msgctl +192 common shmat sys_shmat compat_sys_shmat +193 common shmdt sys_shmdt +194 common shmget sys_shmget +195 common shmctl sys_shmctl compat_sys_shmctl +# 196 was getpmsg +# 197 was putpmsg +198 common lstat64 sys_lstat64 +199 32 truncate64 parisc_truncate64 +199 64 truncate64 sys_truncate64 +200 32 ftruncate64 parisc_ftruncate64 +200 64 ftruncate64 sys_ftruncate64 +201 common getdents64 sys_getdents64 +202 common fcntl64 sys_fcntl64 compat_sys_fcntl64 +# 203 was attrctl +# 204 was acl_get +# 205 was acl_set +206 common gettid sys_gettid +207 32 readahead parisc_readahead +207 64 readahead sys_readahead +208 common tkill sys_tkill +209 common sendfile64 sys_sendfile64 compat_sys_sendfile64 +210 32 futex sys_futex_time32 +210 64 futex sys_futex +211 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +212 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +# 213 was set_thread_area +# 214 was get_thread_area +215 common io_setup sys_io_setup compat_sys_io_setup +216 common io_destroy sys_io_destroy +217 32 io_getevents sys_io_getevents_time32 +217 64 io_getevents sys_io_getevents +218 common io_submit sys_io_submit compat_sys_io_submit +219 common io_cancel sys_io_cancel +# 220 was alloc_hugepages +# 221 was free_hugepages +222 common exit_group sys_exit_group +223 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +224 common epoll_create sys_epoll_create +225 common epoll_ctl sys_epoll_ctl +226 common epoll_wait sys_epoll_wait +227 common remap_file_pages sys_remap_file_pages +228 32 semtimedop sys_semtimedop_time32 +228 64 semtimedop sys_semtimedop +229 common mq_open sys_mq_open compat_sys_mq_open +230 common mq_unlink sys_mq_unlink +231 32 mq_timedsend sys_mq_timedsend_time32 +231 64 mq_timedsend sys_mq_timedsend +232 32 mq_timedreceive sys_mq_timedreceive_time32 +232 64 mq_timedreceive sys_mq_timedreceive +233 common mq_notify sys_mq_notify compat_sys_mq_notify +234 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +235 common waitid sys_waitid compat_sys_waitid +236 32 fadvise64_64 parisc_fadvise64_64 +236 64 fadvise64_64 sys_fadvise64_64 +237 common set_tid_address sys_set_tid_address +238 common setxattr sys_setxattr +239 common lsetxattr sys_lsetxattr +240 common fsetxattr sys_fsetxattr +241 common getxattr sys_getxattr +242 common lgetxattr sys_lgetxattr +243 common fgetxattr sys_fgetxattr +244 common listxattr sys_listxattr +245 common llistxattr sys_llistxattr +246 common flistxattr sys_flistxattr +247 common removexattr sys_removexattr +248 common lremovexattr sys_lremovexattr +249 common fremovexattr sys_fremovexattr +250 common timer_create sys_timer_create compat_sys_timer_create +251 32 timer_settime sys_timer_settime32 +251 64 timer_settime sys_timer_settime +252 32 timer_gettime sys_timer_gettime32 +252 64 timer_gettime sys_timer_gettime +253 common timer_getoverrun sys_timer_getoverrun +254 common timer_delete sys_timer_delete +255 32 clock_settime sys_clock_settime32 +255 64 clock_settime sys_clock_settime +256 32 clock_gettime sys_clock_gettime32 +256 64 clock_gettime sys_clock_gettime +257 32 clock_getres sys_clock_getres_time32 +257 64 clock_getres sys_clock_getres +258 32 clock_nanosleep sys_clock_nanosleep_time32 +258 64 clock_nanosleep sys_clock_nanosleep +259 common tgkill sys_tgkill +260 common mbind sys_mbind +261 common get_mempolicy sys_get_mempolicy +262 common set_mempolicy sys_set_mempolicy +# 263 was vserver +264 common add_key sys_add_key +265 common request_key sys_request_key +266 common keyctl sys_keyctl compat_sys_keyctl +267 common ioprio_set sys_ioprio_set +268 common ioprio_get sys_ioprio_get +269 common inotify_init sys_inotify_init +270 common inotify_add_watch sys_inotify_add_watch +271 common inotify_rm_watch sys_inotify_rm_watch +272 common migrate_pages sys_migrate_pages +273 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +273 64 pselect6 sys_pselect6 +274 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +274 64 ppoll sys_ppoll +275 common openat sys_openat compat_sys_openat +276 common mkdirat sys_mkdirat +277 common mknodat sys_mknodat +278 common fchownat sys_fchownat +279 32 futimesat sys_futimesat_time32 +279 64 futimesat sys_futimesat +280 common fstatat64 sys_fstatat64 +281 common unlinkat sys_unlinkat +282 common renameat sys_renameat +283 common linkat sys_linkat +284 common symlinkat sys_symlinkat +285 common readlinkat sys_readlinkat +286 common fchmodat sys_fchmodat +287 common faccessat sys_faccessat +288 common unshare sys_unshare +289 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +290 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +291 common splice sys_splice +292 32 sync_file_range parisc_sync_file_range +292 64 sync_file_range sys_sync_file_range +293 common tee sys_tee +294 common vmsplice sys_vmsplice +295 common move_pages sys_move_pages +296 common getcpu sys_getcpu +297 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +298 common statfs64 sys_statfs64 compat_sys_statfs64 +299 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +300 common kexec_load sys_kexec_load compat_sys_kexec_load +301 32 utimensat sys_utimensat_time32 +301 64 utimensat sys_utimensat +302 common signalfd sys_signalfd compat_sys_signalfd +# 303 was timerfd +304 common eventfd sys_eventfd +305 32 fallocate parisc_fallocate +305 64 fallocate sys_fallocate +306 common timerfd_create parisc_timerfd_create +307 32 timerfd_settime sys_timerfd_settime32 +307 64 timerfd_settime sys_timerfd_settime +308 32 timerfd_gettime sys_timerfd_gettime32 +308 64 timerfd_gettime sys_timerfd_gettime +309 common signalfd4 parisc_signalfd4 parisc_compat_signalfd4 +310 common eventfd2 parisc_eventfd2 +311 common epoll_create1 sys_epoll_create1 +312 common dup3 sys_dup3 +313 common pipe2 parisc_pipe2 +314 common inotify_init1 parisc_inotify_init1 +315 common preadv sys_preadv compat_sys_preadv +316 common pwritev sys_pwritev compat_sys_pwritev +317 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +318 common perf_event_open sys_perf_event_open +319 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +319 64 recvmmsg sys_recvmmsg +320 common accept4 sys_accept4 +321 common prlimit64 sys_prlimit64 +322 common fanotify_init sys_fanotify_init +323 common fanotify_mark sys_fanotify_mark sys32_fanotify_mark +324 32 clock_adjtime sys_clock_adjtime32 +324 64 clock_adjtime sys_clock_adjtime +325 common name_to_handle_at sys_name_to_handle_at +326 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +327 common syncfs sys_syncfs +328 common setns sys_setns +329 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +330 common process_vm_readv sys_process_vm_readv +331 common process_vm_writev sys_process_vm_writev +332 common kcmp sys_kcmp +333 common finit_module sys_finit_module +334 common sched_setattr sys_sched_setattr +335 common sched_getattr sys_sched_getattr +336 32 utimes sys_utimes_time32 +336 64 utimes sys_utimes +337 common renameat2 sys_renameat2 +338 common seccomp sys_seccomp +339 common getrandom sys_getrandom +340 common memfd_create sys_memfd_create +341 common bpf sys_bpf +342 common execveat sys_execveat compat_sys_execveat +343 common membarrier sys_membarrier +344 common userfaultfd parisc_userfaultfd +345 common mlock2 sys_mlock2 +346 common copy_file_range sys_copy_file_range +347 common preadv2 sys_preadv2 compat_sys_preadv2 +348 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +349 common statx sys_statx +350 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +350 64 io_pgetevents sys_io_pgetevents +351 common pkey_mprotect sys_pkey_mprotect +352 common pkey_alloc sys_pkey_alloc +353 common pkey_free sys_pkey_free +354 common rseq sys_rseq +355 common kexec_file_load sys_kexec_file_load sys_kexec_file_load +# up to 402 is unassigned and reserved for arch specific syscalls +403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime +404 32 clock_settime64 sys_clock_settime sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime +409 32 timer_settime64 sys_timer_settime sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3_wrapper +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 +440 common process_madvise sys_process_madvise +441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 +442 common mount_setattr sys_mount_setattr +443 common quotactl_fd sys_quotactl_fd +444 common landlock_create_ruleset sys_landlock_create_ruleset +445 common landlock_add_rule sys_landlock_add_rule +446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c new file mode 100644 index 000000000..9714fbd7c --- /dev/null +++ b/arch/parisc/kernel/time.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/parisc/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Modifications for ARM (C) 1994, 1995, 1996,1997 Russell King + * Copyright (C) 1999 SuSE GmbH, (Philipp Rumpf, prumpf@tux.org) + * + * 1994-07-02 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1998-12-20 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + */ +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/sched.h> +#include <linux/sched/clock.h> +#include <linux/sched_clock.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#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 <linux/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> + +int time_keeper_id __read_mostly; /* CPU used for timekeeping. */ + +static unsigned long clocktick __ro_after_init; /* timer cycles per tick */ + +/* + * 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; + unsigned long next_tick; + unsigned long ticks_elapsed = 0; + unsigned int cpu = smp_processor_id(); + struct cpuinfo_parisc *cpuinfo = &per_cpu(cpu_data, cpu); + + /* gcc can optimize for "read-only" case with a local clocktick */ + unsigned long cpt = clocktick; + + /* Initialize next_tick to the old expected tick time. */ + next_tick = cpuinfo->it_value; + + /* Calculate how many ticks have elapsed. */ + now = mfctl(16); + do { + ++ticks_elapsed; + next_tick += cpt; + } while (next_tick - now > cpt); + + /* Store (in CR16 cycles) up to when we are accounting right now. */ + cpuinfo->it_value = next_tick; + + /* Go do system house keeping. */ + if (IS_ENABLED(CONFIG_SMP) && (cpu != time_keeper_id)) + ticks_elapsed = 0; + legacy_timer_tick(ticks_elapsed); + + /* Skip clockticks on purpose if we know we would miss those. + * 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 + * ticks on the next timer interrupt. + * 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" 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. + */ + now = mfctl(16); + while (next_tick - now > cpt) + next_tick += cpt; + + /* Program the IT when to deliver the next interrupt. + * Only bottom 32-bits of next_tick are writable in CR16! + * Timer interrupt will be delivered at least a few hundred cycles + * after the IT fires, so if we are too close (<= 8000 cycles) to the + * next cycle, simply skip it. + */ + if (next_tick - now <= 8000) + next_tick += cpt; + mtctl(next_tick, 16); + + return IRQ_HANDLED; +} + + +unsigned long profile_pc(struct pt_regs *regs) +{ + unsigned long pc = instruction_pointer(regs); + + if (regs->gr[0] & PSW_N) + pc -= 4; + +#ifdef CONFIG_SMP + if (in_lock_functions(pc)) + pc = regs->gr[2]; +#endif + + return pc; +} +EXPORT_SYMBOL(profile_pc); + + +/* clock source code */ + +static u64 notrace read_cr16(struct clocksource *cs) +{ + return get_cycles(); +} + +static struct clocksource clocksource_cr16 = { + .name = "cr16", + .rating = 300, + .read = read_cr16, + .mask = CLOCKSOURCE_MASK(BITS_PER_LONG), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +void start_cpu_itimer(void) +{ + unsigned int cpu = smp_processor_id(); + unsigned long next_tick = mfctl(16) + clocktick; + + mtctl(next_tick, 16); /* kick off Interval Timer (CR16) */ + + per_cpu(cpu_data, cpu).it_value = next_tick; +} + +#if IS_ENABLED(CONFIG_RTC_DRV_GENERIC) +static int rtc_generic_get_time(struct device *dev, struct rtc_time *tm) +{ + struct pdc_tod tod_data; + + memset(tm, 0, sizeof(*tm)); + if (pdc_tod_read(&tod_data) < 0) + return -EOPNOTSUPP; + + /* we treat tod_sec as unsigned, so this can work until year 2106 */ + rtc_time64_to_tm(tod_data.tod_sec, tm); + return 0; +} + +static int rtc_generic_set_time(struct device *dev, struct rtc_time *tm) +{ + time64_t secs = rtc_tm_to_time64(tm); + int ret; + + /* hppa has Y2K38 problem: pdc_tod_set() takes an u32 value! */ + ret = pdc_tod_set(secs, 0); + if (ret != 0) { + pr_warn("pdc_tod_set(%lld) returned error %d\n", secs, ret); + if (ret == PDC_INVALID_ARG) + return -EINVAL; + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct rtc_class_ops rtc_generic_ops = { + .read_time = rtc_generic_get_time, + .set_time = rtc_generic_set_time, +}; + +static int __init rtc_init(void) +{ + struct platform_device *pdev; + + pdev = platform_device_register_data(NULL, "rtc-generic", -1, + &rtc_generic_ops, + sizeof(rtc_generic_ops)); + + return PTR_ERR_OR_ZERO(pdev); +} +device_initcall(rtc_init); +#endif + +void read_persistent_clock64(struct timespec64 *ts) +{ + 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; + } +} + + +static u64 notrace read_cr16_sched_clock(void) +{ + return get_cycles(); +} + + +/* + * timer interrupt and sched_clock() initialization + */ + +void __init time_init(void) +{ + unsigned long cr16_hz; + + clocktick = (100 * PAGE0->mem_10msec) / HZ; + start_cpu_itimer(); /* get CPU 0 started */ + + cr16_hz = 100 * PAGE0->mem_10msec; /* Hz */ + + /* register as sched_clock source */ + sched_clock_register(read_cr16_sched_clock, BITS_PER_LONG, cr16_hz); +} + +static int __init init_cr16_clocksource(void) +{ + /* + * The cr16 interval timers are not synchronized across CPUs. + */ + if (num_online_cpus() > 1 && !running_on_qemu) { + clocksource_cr16.name = "cr16_unstable"; + clocksource_cr16.flags = CLOCK_SOURCE_UNSTABLE; + clocksource_cr16.rating = 0; + } + + /* register at clocksource framework */ + clocksource_register_hz(&clocksource_cr16, + 100 * PAGE0->mem_10msec); + + return 0; +} + +device_initcall(init_cr16_clocksource); diff --git a/arch/parisc/kernel/toc.c b/arch/parisc/kernel/toc.c new file mode 100644 index 000000000..e4b48d07a --- /dev/null +++ b/arch/parisc/kernel/toc.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/kernel.h> +#include <linux/kgdb.h> +#include <linux/printk.h> +#include <linux/sched/debug.h> +#include <linux/delay.h> +#include <linux/reboot.h> + +#include <asm/pdc.h> +#include <asm/pdc_chassis.h> +#include <asm/ldcw.h> +#include <asm/processor.h> + +static unsigned int __aligned(16) toc_lock = 1; +DEFINE_PER_CPU_PAGE_ALIGNED(char [16384], toc_stack) __visible; + +static void toc20_to_pt_regs(struct pt_regs *regs, struct pdc_toc_pim_20 *toc) +{ + int i; + + regs->gr[0] = (unsigned long)toc->cr[22]; + + for (i = 1; i < 32; i++) + regs->gr[i] = (unsigned long)toc->gr[i]; + + for (i = 0; i < 8; i++) + regs->sr[i] = (unsigned long)toc->sr[i]; + + regs->iasq[0] = (unsigned long)toc->cr[17]; + regs->iasq[1] = (unsigned long)toc->iasq_back; + regs->iaoq[0] = (unsigned long)toc->cr[18]; + regs->iaoq[1] = (unsigned long)toc->iaoq_back; + + regs->sar = (unsigned long)toc->cr[11]; + regs->iir = (unsigned long)toc->cr[19]; + regs->isr = (unsigned long)toc->cr[20]; + regs->ior = (unsigned long)toc->cr[21]; +} + +static void toc11_to_pt_regs(struct pt_regs *regs, struct pdc_toc_pim_11 *toc) +{ + int i; + + regs->gr[0] = toc->cr[22]; + + for (i = 1; i < 32; i++) + regs->gr[i] = toc->gr[i]; + + for (i = 0; i < 8; i++) + regs->sr[i] = toc->sr[i]; + + regs->iasq[0] = toc->cr[17]; + regs->iasq[1] = toc->iasq_back; + regs->iaoq[0] = toc->cr[18]; + regs->iaoq[1] = toc->iaoq_back; + + regs->sar = toc->cr[11]; + regs->iir = toc->cr[19]; + regs->isr = toc->cr[20]; + regs->ior = toc->cr[21]; +} + +void notrace __noreturn __cold toc_intr(struct pt_regs *regs) +{ + struct pdc_toc_pim_20 pim_data20; + struct pdc_toc_pim_11 pim_data11; + + /* verify we wrote regs to the correct stack */ + BUG_ON(regs != (struct pt_regs *)&per_cpu(toc_stack, raw_smp_processor_id())); + + if (boot_cpu_data.cpu_type >= pcxu) { + if (pdc_pim_toc20(&pim_data20)) + panic("Failed to get PIM data"); + toc20_to_pt_regs(regs, &pim_data20); + } else { + if (pdc_pim_toc11(&pim_data11)) + panic("Failed to get PIM data"); + toc11_to_pt_regs(regs, &pim_data11); + } + +#ifdef CONFIG_KGDB + nmi_enter(); + + if (atomic_read(&kgdb_active) != -1) + kgdb_nmicallback(raw_smp_processor_id(), regs); + kgdb_handle_exception(9, SIGTRAP, 0, regs); +#endif + + /* serialize output, otherwise all CPUs write backtrace at once */ + while (__ldcw(&toc_lock) == 0) + ; /* wait */ + show_regs(regs); + toc_lock = 1; /* release lock for next CPU */ + + if (raw_smp_processor_id() != 0) + while (1) ; /* all but monarch CPU will wait endless. */ + + /* give other CPUs time to show their backtrace */ + mdelay(2000); + + machine_restart("TOC"); + + /* should never reach this */ + panic("TOC"); +} + +static __init int setup_toc(void) +{ + unsigned int csum = 0; + unsigned long toc_code = (unsigned long)dereference_function_descriptor(toc_handler); + int i; + + PAGE0->vec_toc = __pa(toc_code) & 0xffffffff; +#ifdef CONFIG_64BIT + PAGE0->vec_toc_hi = __pa(toc_code) >> 32; +#endif + PAGE0->vec_toclen = toc_handler_size; + + for (i = 0; i < toc_handler_size/4; i++) + csum += ((u32 *)toc_code)[i]; + toc_handler_csum = -csum; + pr_info("TOC handler registered\n"); + return 0; +} +early_initcall(setup_toc); diff --git a/arch/parisc/kernel/toc_asm.S b/arch/parisc/kernel/toc_asm.S new file mode 100644 index 000000000..570f5cef5 --- /dev/null +++ b/arch/parisc/kernel/toc_asm.S @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* TOC (Transfer of Control) handler. */ + + .level 1.1 + +#include <asm/assembly.h> +#include <linux/threads.h> +#include <linux/linkage.h> + + .text + .import toc_intr,code + .import toc_stack,data + .align 16 +ENTRY_CFI(toc_handler) + load32 PA(toc_stack),%sp + +#ifdef CONFIG_SMP + /* get per-cpu toc_stack address. */ + mfctl %cr30, %r1 + tophys %r1,%r2 /* task_struct */ + LDREG TASK_TI_CPU(%r2),%r4 /* cpu */ + load32 PA(__per_cpu_offset),%r1 + LDREGX %r4(%r1),%r4 + add %r4,%sp,%sp +#endif + + /* + * setup pt_regs on stack and save the + * floating point registers. PIM_TOC doesn't + * save fp registers, so we're doing it here. + */ + copy %sp,%arg0 + ldo PT_SZ_ALGN(%sp), %sp + + /* clear pt_regs */ + copy %arg0,%r1 +0: cmpb,<<,n %r1,%sp,0b + stw,ma %r0,4(%r1) + + ldo PT_FR0(%arg0),%r25 + save_fp %r25 + + /* go virtual */ + load32 PA(swapper_pg_dir),%r4 + mtctl %r4,%cr24 + mtctl %r4,%cr25 + + /* Clear sr4-sr7 */ + mtsp %r0, %sr4 + mtsp %r0, %sr5 + mtsp %r0, %sr6 + mtsp %r0, %sr7 + + tovirt_r1 %sp + tovirt_r1 %arg0 + virt_map + + loadgp + +#ifdef CONFIG_64BIT + ldo -16(%sp),%r29 +#endif + load32 toc_intr,%r1 + be 0(%sr7,%r1) + nop +ENDPROC_CFI(toc_handler) + + /* + * keep this checksum here, as it is part of the toc_handler + * spanned by toc_handler_size (all words in toc_handler are + * added in PDC and the sum must equal to zero. + */ +SYM_DATA(toc_handler_csum, .long 0) +SYM_DATA(toc_handler_size, .long . - toc_handler) diff --git a/arch/parisc/kernel/topology.c b/arch/parisc/kernel/topology.c new file mode 100644 index 000000000..b9d845e31 --- /dev/null +++ b/arch/parisc/kernel/topology.c @@ -0,0 +1,87 @@ +/* + * arch/parisc/kernel/topology.c + * + * Copyright (C) 2017 Helge Deller <deller@gmx.de> + * + * based on arch/arm/kernel/topology.c + * + * 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. + */ + +#include <linux/percpu.h> +#include <linux/sched.h> +#include <linux/sched/topology.h> +#include <linux/cpu.h> + +#include <asm/topology.h> +#include <asm/sections.h> + +static DEFINE_PER_CPU(struct cpu, cpu_devices); + +/* + * store_cpu_topology is called at boot when only one cpu is running + * and with the mutex cpu_hotplug.lock locked, when several cpus have booted, + * which prevents simultaneous write access to cpu_topology array + */ +void store_cpu_topology(unsigned int cpuid) +{ + struct cpu_topology *cpuid_topo = &cpu_topology[cpuid]; + struct cpuinfo_parisc *p; + int max_socket = -1; + unsigned long cpu; + + /* If the cpu topology has been already set, just return */ + if (cpuid_topo->core_id != -1) + return; + +#ifdef CONFIG_HOTPLUG_CPU + per_cpu(cpu_devices, cpuid).hotpluggable = 1; +#endif + if (register_cpu(&per_cpu(cpu_devices, cpuid), cpuid)) + pr_warn("Failed to register CPU%d device", cpuid); + + /* create cpu topology mapping */ + cpuid_topo->thread_id = -1; + cpuid_topo->core_id = 0; + + p = &per_cpu(cpu_data, cpuid); + for_each_online_cpu(cpu) { + const struct cpuinfo_parisc *cpuinfo = &per_cpu(cpu_data, cpu); + + if (cpu == cpuid) /* ignore current cpu */ + continue; + + if (cpuinfo->cpu_loc == p->cpu_loc) { + cpuid_topo->core_id = cpu_topology[cpu].core_id; + if (p->cpu_loc) { + cpuid_topo->core_id++; + cpuid_topo->package_id = cpu_topology[cpu].package_id; + continue; + } + } + + if (cpuid_topo->package_id == -1) + max_socket = max(max_socket, cpu_topology[cpu].package_id); + } + + if (cpuid_topo->package_id == -1) + cpuid_topo->package_id = max_socket + 1; + + update_siblings_masks(cpuid); + + pr_info("CPU%u: cpu core %d of socket %d\n", + cpuid, + cpu_topology[cpuid].core_id, + cpu_topology[cpuid].package_id); +} + +/* + * init_cpu_topology is called at boot when only one cpu is running + * which prevent simultaneous write access to cpu_topology array + */ +void __init init_cpu_topology(void) +{ + reset_cpu_topology(); +} diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c new file mode 100644 index 000000000..67b51841d --- /dev/null +++ b/arch/parisc/kernel/traps.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/parisc/traps.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1999, 2000 Philipp Rumpf <prumpf@tux.org> + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. + */ + +#include <linux/sched.h> +#include <linux/sched/debug.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/spinlock.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/console.h> +#include <linux/bug.h> +#include <linux/ratelimit.h> +#include <linux/uaccess.h> +#include <linux/kdebug.h> +#include <linux/kfence.h> + +#include <asm/assembly.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/unaligned.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 <linux/kgdb.h> +#include <linux/kprobes.h> + +#include "../math-emu/math-emu.h" /* for handle_fpe() */ + +static void parisc_show_stack(struct task_struct *task, + struct pt_regs *regs, const char *loglvl); + +static int printbinary(char *buf, unsigned long x, int nbits) +{ + unsigned long mask = 1UL << (nbits - 1); + while (mask != 0) { + *buf++ = (mask & x ? '1' : '0'); + mask >>= 1; + } + *buf = '\0'; + + return nbits; +} + +#ifdef CONFIG_64BIT +#define RFMT "%016lx" +#else +#define RFMT "%08lx" +#endif +#define FFMT "%016llx" /* fpregs are 64-bit always */ + +#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(const char *level, struct pt_regs *regs) +{ + int i; + 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) + PRINTREGS(level, regs->gr, "r", RFMT, i); +} + +static void print_fr(const 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. + * 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); + printbinary(buf, s.sw[0], 32); + printk("%sFPSR: %s\n", level, buf); + 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) + PRINTREGS(level, regs->fr, "fr", FFMT, i); +} + +void show_regs(struct pt_regs *regs) +{ + int i, user; + const 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); + printk("%s\n", level); + printk("%sIASQ: " RFMT " " RFMT " IAOQ: " RFMT " " RFMT "\n", + level, regs->iasq[0], regs->iasq[1], regs->iaoq[0], regs->iaoq[1]); + printk("%s IIR: %08lx ISR: " RFMT " IOR: " RFMT "\n", + level, regs->iir, regs->isr, regs->ior); + printk("%s CPU: %8d CR30: " RFMT " CR31: " RFMT "\n", + level, task_cpu(current), cr30, cr31); + printk("%s ORIG_R28: " RFMT "\n", level, regs->orig_r28); + + 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, regs, KERN_DEFAULT); + } +} + +static DEFINE_RATELIMIT_STATE(_hppa_rs, + DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); + +#define parisc_printk_ratelimited(critical, regs, fmt, ...) { \ + if ((critical || show_unhandled_signals) && __ratelimit(&_hppa_rs)) { \ + printk(fmt, ##__VA_ARGS__); \ + show_regs(regs); \ + } \ +} + + +static void do_show_stack(struct unwind_frame_info *info, const char *loglvl) +{ + int i = 1; + + printk("%sBacktrace:\n", loglvl); + while (i <= MAX_UNWIND_ENTRIES) { + if (unwind_once(info) < 0 || info->ip == 0) + break; + + if (__kernel_text_address(info->ip)) { + printk("%s [<" RFMT ">] %pS\n", + loglvl, info->ip, (void *) info->ip); + i++; + } + } + printk("%s\n", loglvl); +} + +static void parisc_show_stack(struct task_struct *task, + struct pt_regs *regs, const char *loglvl) +{ + struct unwind_frame_info info; + + unwind_frame_init_task(&info, task, regs); + + do_show_stack(&info, loglvl); +} + +void show_stack(struct task_struct *t, unsigned long *sp, const char *loglvl) +{ + parisc_show_stack(t, NULL, loglvl); +} + +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 */ + + 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; + } + + bust_spinlocks(1); + + oops_enter(); + + /* Amuse the user in a SPARC fashion */ + 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(); + + 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", __func__); + local_irq_enable(); + while (1); + } + current->thread.flags |= PARISC_KERNEL_DEATH; + + show_regs(regs); + dump_stack(); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); + + if (in_interrupt()) + panic("Fatal exception in interrupt"); + + if (panic_on_oops) + panic("Fatal exception"); + + oops_exit(); + make_task_dead(SIGSEGV); +} + +/* gdb uses break 4,8 */ +#define GDB_BREAK_INSN 0x10004 +static void handle_gdb_break(struct pt_regs *regs, int wot) +{ + force_sig_fault(SIGTRAP, wot, + (void __user *) (regs->iaoq[0] & ~3)); +} + +static void handle_break(struct pt_regs *regs) +{ + 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); + } + +#ifdef CONFIG_KPROBES + if (unlikely(iir == PARISC_KPROBES_BREAK_INSN && !user_mode(regs))) { + parisc_kprobe_break_handler(regs); + return; + } + if (unlikely(iir == PARISC_KPROBES_BREAK_INSN2 && !user_mode(regs))) { + parisc_kprobe_ss_handler(regs); + return; + } +#endif + +#ifdef CONFIG_KGDB + if (unlikely((iir == PARISC_KGDB_COMPILED_BREAK_INSN || + iir == PARISC_KGDB_BREAK_INSN)) && !user_mode(regs)) { + kgdb_handle_exception(9, SIGTRAP, 0, regs); + return; + } +#endif + + 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); + + /* send standard GDB signal */ + handle_gdb_break(regs, TRAP_BRKPT); +} + +static void default_trap(int code, struct pt_regs *regs) +{ + printk(KERN_ERR "Trap %d on CPU %d\n", code, smp_processor_id()); + show_regs(regs); +} + +void (*cpu_lpmc) (int code, struct pt_regs *regs) __read_mostly = default_trap; + + +void transfer_pim_to_trap_frame(struct pt_regs *regs) +{ + register int i; + extern unsigned int hpmc_pim_data[]; + struct pdc_hpmc_pim_11 *pim_narrow; + struct pdc_hpmc_pim_20 *pim_wide; + + if (boot_cpu_data.cpu_type >= pcxu) { + + pim_wide = (struct pdc_hpmc_pim_20 *)hpmc_pim_data; + + /* + * Note: The following code will probably generate a + * bunch of truncation error warnings from the compiler. + * Could be handled with an ifdef, but perhaps there + * is a better way. + */ + + regs->gr[0] = pim_wide->cr[22]; + + for (i = 1; i < 32; i++) + regs->gr[i] = pim_wide->gr[i]; + + for (i = 0; i < 32; i++) + regs->fr[i] = pim_wide->fr[i]; + + for (i = 0; i < 8; i++) + regs->sr[i] = pim_wide->sr[i]; + + regs->iasq[0] = pim_wide->cr[17]; + regs->iasq[1] = pim_wide->iasq_back; + regs->iaoq[0] = pim_wide->cr[18]; + regs->iaoq[1] = pim_wide->iaoq_back; + + regs->sar = pim_wide->cr[11]; + regs->iir = pim_wide->cr[19]; + regs->isr = pim_wide->cr[20]; + regs->ior = pim_wide->cr[21]; + } + else { + pim_narrow = (struct pdc_hpmc_pim_11 *)hpmc_pim_data; + + regs->gr[0] = pim_narrow->cr[22]; + + for (i = 1; i < 32; i++) + regs->gr[i] = pim_narrow->gr[i]; + + for (i = 0; i < 32; i++) + regs->fr[i] = pim_narrow->fr[i]; + + for (i = 0; i < 8; i++) + regs->sr[i] = pim_narrow->sr[i]; + + regs->iasq[0] = pim_narrow->cr[17]; + regs->iasq[1] = pim_narrow->iasq_back; + regs->iaoq[0] = pim_narrow->cr[18]; + regs->iaoq[1] = pim_narrow->iaoq_back; + + regs->sar = pim_narrow->cr[11]; + regs->iir = pim_narrow->cr[19]; + regs->isr = pim_narrow->cr[20]; + regs->ior = pim_narrow->cr[21]; + } + + /* + * The following fields only have meaning if we came through + * another path. So just zero them here. + */ + + regs->ksp = 0; + regs->kpc = 0; + regs->orig_r28 = 0; +} + + +/* + * This routine is called as a last resort when everything else + * has gone clearly wrong. We get called for faults in kernel space, + * and HPMC's. + */ +void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long offset) +{ + static DEFINE_SPINLOCK(terminate_lock); + + (void)notify_die(DIE_OOPS, msg, regs, 0, code, SIGTRAP); + bust_spinlocks(1); + + set_eiem(0); + local_irq_disable(); + spin_lock(&terminate_lock); + + /* unlock the pdc lock if necessary */ + pdc_emergency_unlock(); + + /* Not all paths will gutter the processor... */ + switch(code){ + + case 1: + transfer_pim_to_trap_frame(regs); + break; + + default: + break; + + } + + { + /* show_stack(NULL, (unsigned long *)regs->gr[30]); */ + struct unwind_frame_info info; + unwind_frame_init(&info, current, regs); + do_show_stack(&info, KERN_CRIT); + } + + printk("\n"); + pr_crit("%s: Code=%d (%s) at addr " RFMT "\n", + msg, code, trap_name(code), offset); + show_regs(regs); + + spin_unlock(&terminate_lock); + + /* put soft power button back under hardware control; + * if the user had pressed it once at any time, the + * system will shut down immediately right here. */ + pdc_soft_power_button(0); + + /* Call kernel panic() so reboot timeouts work properly + * FIXME: This function should be on the list of + * panic notifiers, and we should call panic + * directly from the location that we wish. + * e.g. We should not call panic from + * parisc_terminate, but rather the other way around. + * This hack works, prints the panic message twice, + * and it enables reboot timers! + */ + panic(msg); +} + +void notrace handle_interruption(int code, struct pt_regs *regs) +{ + unsigned long fault_address = 0; + unsigned long fault_space = 0; + int si_code; + + if (!irqs_disabled_flags(regs->gr[0])) + local_irq_enable(); + + /* Security check: + * If the priority level is still user, and the + * faulting space is not equal to the active space + * then the user is attempting something in a space + * that does not belong to them. Kill the process. + * + * This is normally the situation when the user + * attempts to jump into the kernel space at the + * wrong offset, be it at the gateway page or a + * random location. + * + * We cannot normally signal the process because it + * could *be* on the gateway page, and processes + * executing on the gateway page can't have signals + * delivered. + * + * We merely readjust the address into the users + * space, at a destination address of zero, and + * allow processing to continue. + */ + 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; + regs->iaoq[1] = regs->iaoq[0] + 4; + regs->iasq[0] = regs->iasq[1] = regs->sr[7]; + regs->gr[0] &= ~PSW_B; + return; + } + +#if 0 + printk(KERN_CRIT "Interruption # %d\n", code); +#endif + + switch(code) { + + case 1: + /* High-priority machine check (HPMC) */ + + /* 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)", + regs, code, 0); + /* NOT REACHED */ + + case 2: + /* Power failure interrupt */ + printk(KERN_CRIT "Power failure interrupt !\n"); + return; + + case 3: + /* Recovery counter trap */ + regs->gr[0] &= ~PSW_R; + +#ifdef CONFIG_KGDB + if (kgdb_single_step) { + kgdb_handle_exception(0, SIGTRAP, 0, regs); + return; + } +#endif + + if (user_space(regs)) + handle_gdb_break(regs, TRAP_TRACE); + /* else this must be the start of a syscall - just let it run */ + return; + + case 5: + /* Low-priority machine check */ + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_LPMC); + + flush_cache_all(); + flush_tlb_all(); + cpu_lpmc(5, regs); + return; + + case PARISC_ITLB_TRAP: + /* Instruction TLB miss fault/Instruction page fault */ + fault_address = regs->iaoq[0]; + fault_space = regs->iasq[0]; + break; + + case 8: + /* Illegal instruction trap */ + die_if_kernel("Illegal instruction", regs, code); + si_code = ILL_ILLOPC; + goto give_sigill; + + case 9: + /* Break instruction trap */ + handle_break(regs); + return; + + case 10: + /* Privileged operation trap */ + die_if_kernel("Privileged operation", regs, code); + si_code = ILL_PRVOPC; + goto give_sigill; + + case 11: + /* Privileged register trap */ + if ((regs->iir & 0xffdfffe0) == 0x034008a0) { + + /* This is a MFCTL cr26/cr27 to gr instruction. + * PCXS traps on this, so we need to emulate it. + */ + + if (regs->iir & 0x00200000) + regs->gr[regs->iir & 0x1f] = mfctl(27); + else + regs->gr[regs->iir & 0x1f] = mfctl(26); + + regs->iaoq[0] = regs->iaoq[1]; + regs->iaoq[1] += 4; + regs->iasq[0] = regs->iasq[1]; + return; + } + + die_if_kernel("Privileged register usage", regs, code); + si_code = ILL_PRVREG; + give_sigill: + force_sig_fault(SIGILL, si_code, + (void __user *) regs->iaoq[0]); + return; + + case 12: + /* Overflow Trap, let the userland signal handler do the cleanup */ + force_sig_fault(SIGFPE, FPE_INTOVF, + (void __user *) regs->iaoq[0]); + return; + + case 13: + /* Conditional Trap + The condition succeeds in an instruction which traps + on condition */ + if(user_mode(regs)){ + /* Let userspace app figure it out from the insn pointed + * to by si_addr. + */ + force_sig_fault(SIGFPE, FPE_CONDTRAP, + (void __user *) regs->iaoq[0]); + return; + } + /* The kernel doesn't want to handle condition codes */ + break; + + 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 */ + fallthrough; + case 16: + /* Non-access instruction TLB miss fault */ + /* The instruction TLB entry needed for the target address of the FIC + is absent, and hardware can't find it, so we get to cleanup */ + fallthrough; + 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 + 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 + */ + if (code == 17 && handle_nadtlb_fault(regs)) + return; + fault_address = regs->ior; + fault_space = regs->isr; + break; + + case 18: + /* PCXS only -- later cpu's split this into types 26,27 & 28 */ + /* Check for unaligned access */ + if (check_unaligned(regs)) { + handle_unaligned(regs); + return; + } + fallthrough; + case 26: + /* PCXL: Data memory access rights trap */ + fault_address = regs->ior; + fault_space = regs->isr; + break; + + case 19: + /* Data memory break trap */ + regs->gr[0] |= PSW_X; /* So we can single-step over the trap */ + fallthrough; + case 21: + /* Page reference trap */ + handle_gdb_break(regs, TRAP_HWBKPT); + return; + + case 25: + /* Taken branch trap */ + regs->gr[0] &= ~PSW_T; + if (user_space(regs)) + handle_gdb_break(regs, TRAP_BRANCH); + /* else this must be the start of a syscall - just let it + * run. + */ + return; + + case 7: + /* Instruction access rights */ + /* PCXL: Instruction memory protection trap */ + + /* + * This could be caused by either: 1) a process attempting + * to execute within a vma that does not have execute + * permission, or 2) an access rights violation caused by a + * flush only translation set up by ptep_get_and_clear(). + * So we check the vma permissions to differentiate the two. + * If the vma indicates we have execute permission, then + * the cause is the latter one. In this case, we need to + * call do_page_fault() to fix the problem. + */ + + if (user_mode(regs)) { + struct vm_area_struct *vma; + + mmap_read_lock(current->mm); + vma = find_vma(current->mm,regs->iaoq[0]); + if (vma && (regs->iaoq[0] >= vma->vm_start) + && (vma->vm_flags & VM_EXEC)) { + + fault_address = regs->iaoq[0]; + fault_space = regs->iasq[0]; + + mmap_read_unlock(current->mm); + break; /* call do_page_fault() */ + } + mmap_read_unlock(current->mm); + } + /* CPU could not fetch instruction, so clear stale IIR value. */ + regs->iir = 0xbaadf00d; + fallthrough; + 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); + force_sig_fault(SIGSEGV, SEGV_MAPERR, + (code == 7)? + ((void __user *) regs->iaoq[0]) : + ((void __user *) regs->ior)); + return; + + case 28: + /* Unaligned data reference trap */ + handle_unaligned(regs); + return; + + default: + if (user_mode(regs)) { + 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. */ + force_sig_fault(SIGBUS, BUS_OBJERR, + (void __user *)regs->ior); + return; + } + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC); + + parisc_terminate("Unexpected interruption", regs, code, 0); + /* NOT REACHED */ + } + + if (user_mode(regs)) { + if ((fault_space >> SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) { + 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); + force_sig_fault(SIGSEGV, SEGV_MAPERR, + (void __user *)regs->ior); + return; + } + } + else { + + /* + * The kernel should never fault on its own address space, + * unless pagefault_disable() was called before. + */ + + if (faulthandler_disabled() || fault_space == 0) + { + /* Clean up and return if in exception table. */ + if (fixup_exception(regs)) + return; + /* Clean up and return if handled by kfence. */ + if (kfence_handle_page_fault(fault_address, + parisc_acctyp(code, regs->iir) == VM_WRITE, regs)) + return; + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC); + parisc_terminate("Kernel Fault", regs, code, fault_address); + } + } + + do_page_fault(regs, code, fault_address); +} + + +void __init initialize_ivt(const void *iva) +{ + extern const u32 os_hpmc[]; + + int i; + u32 check = 0; + u32 *ivap; + u32 *hpmcp; + u32 instr; + + if (strcmp((const char *)iva, "cows can fly")) + panic("IVT invalid"); + + ivap = (u32 *)iva; + + for (i = 0; i < 8; i++) + *ivap++ = 0; + + /* + * Use PDC_INSTR firmware function to get instruction that invokes + * PDCE_CHECK in HPMC handler. See programming note at page 1-31 of + * the PA 1.1 Firmware Architecture document. + */ + if (pdc_instr(&instr) == PDC_OK) + ivap[0] = instr; + + /* + * Rules for the checksum of the HPMC handler: + * 1. The IVA does not point to PDC/PDH space (ie: the OS has installed + * its own IVA). + * 2. The word at IVA + 32 is nonzero. + * 3. If Length (IVA + 60) is not zero, then Length (IVA + 60) and + * Address (IVA + 56) are word-aligned. + * 4. The checksum of the 8 words starting at IVA + 32 plus the sum of + * the Length/4 words starting at Address is zero. + */ + + /* Setup IVA and compute checksum for HPMC handler */ + ivap[6] = (u32)__pa(os_hpmc); + + hpmcp = (u32 *)os_hpmc; + + for (i=0; i<8; i++) + check += ivap[i]; + + ivap[5] = -check; + pr_debug("initialize_ivt: IVA[6] = 0x%08x\n", ivap[6]); +} + + +/* early_trap_init() is called before we set up kernel mappings and + * write-protect the kernel */ +void __init early_trap_init(void) +{ + extern const void fault_vector_20; + +#ifndef CONFIG_64BIT + extern const void fault_vector_11; + initialize_ivt(&fault_vector_11); +#endif + + initialize_ivt(&fault_vector_20); +} diff --git a/arch/parisc/kernel/unaligned.c b/arch/parisc/kernel/unaligned.c new file mode 100644 index 000000000..e8a4d77cf --- /dev/null +++ b/arch/parisc/kernel/unaligned.c @@ -0,0 +1,673 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unaligned memory access handler + * + * Copyright (C) 2001 Randolph Chung <tausq@debian.org> + * Copyright (C) 2022 Helge Deller <deller@gmx.de> + * Significantly tweaked by LaMont Jones <lamont@debian.org> + */ + +#include <linux/sched/signal.h> +#include <linux/signal.h> +#include <linux/ratelimit.h> +#include <linux/uaccess.h> +#include <asm/hardirq.h> +#include <asm/traps.h> + +/* #define DEBUG_UNALIGNED 1 */ + +#ifdef DEBUG_UNALIGNED +#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 + +#define RFMT "%#08lx" + +/* 1111 1100 0000 0000 0001 0011 1100 0000 */ +#define OPCODE1(a,b,c) ((a)<<26|(b)<<12|(c)<<6) +#define OPCODE2(a,b) ((a)<<26|(b)<<1) +#define OPCODE3(a,b) ((a)<<26|(b)<<2) +#define OPCODE4(a) ((a)<<26) +#define OPCODE1_MASK OPCODE1(0x3f,1,0xf) +#define OPCODE2_MASK OPCODE2(0x3f,1) +#define OPCODE3_MASK OPCODE3(0x3f,1) +#define OPCODE4_MASK OPCODE4(0x3f) + +/* skip LDB - never unaligned (index) */ +#define OPCODE_LDH_I OPCODE1(0x03,0,0x1) +#define OPCODE_LDW_I OPCODE1(0x03,0,0x2) +#define OPCODE_LDD_I OPCODE1(0x03,0,0x3) +#define OPCODE_LDDA_I OPCODE1(0x03,0,0x4) +#define OPCODE_LDCD_I OPCODE1(0x03,0,0x5) +#define OPCODE_LDWA_I OPCODE1(0x03,0,0x6) +#define OPCODE_LDCW_I OPCODE1(0x03,0,0x7) +/* skip LDB - never unaligned (short) */ +#define OPCODE_LDH_S OPCODE1(0x03,1,0x1) +#define OPCODE_LDW_S OPCODE1(0x03,1,0x2) +#define OPCODE_LDD_S OPCODE1(0x03,1,0x3) +#define OPCODE_LDDA_S OPCODE1(0x03,1,0x4) +#define OPCODE_LDCD_S OPCODE1(0x03,1,0x5) +#define OPCODE_LDWA_S OPCODE1(0x03,1,0x6) +#define OPCODE_LDCW_S OPCODE1(0x03,1,0x7) +/* skip STB - never unaligned */ +#define OPCODE_STH OPCODE1(0x03,1,0x9) +#define OPCODE_STW OPCODE1(0x03,1,0xa) +#define OPCODE_STD OPCODE1(0x03,1,0xb) +/* skip STBY - never unaligned */ +/* skip STDBY - never unaligned */ +#define OPCODE_STWA OPCODE1(0x03,1,0xe) +#define OPCODE_STDA OPCODE1(0x03,1,0xf) + +#define OPCODE_FLDWX OPCODE1(0x09,0,0x0) +#define OPCODE_FLDWXR OPCODE1(0x09,0,0x1) +#define OPCODE_FSTWX OPCODE1(0x09,0,0x8) +#define OPCODE_FSTWXR OPCODE1(0x09,0,0x9) +#define OPCODE_FLDWS OPCODE1(0x09,1,0x0) +#define OPCODE_FLDWSR OPCODE1(0x09,1,0x1) +#define OPCODE_FSTWS OPCODE1(0x09,1,0x8) +#define OPCODE_FSTWSR OPCODE1(0x09,1,0x9) +#define OPCODE_FLDDX OPCODE1(0x0b,0,0x0) +#define OPCODE_FSTDX OPCODE1(0x0b,0,0x8) +#define OPCODE_FLDDS OPCODE1(0x0b,1,0x0) +#define OPCODE_FSTDS OPCODE1(0x0b,1,0x8) + +#define OPCODE_LDD_L OPCODE2(0x14,0) +#define OPCODE_FLDD_L OPCODE2(0x14,1) +#define OPCODE_STD_L OPCODE2(0x1c,0) +#define OPCODE_FSTD_L OPCODE2(0x1c,1) + +#define OPCODE_LDW_M OPCODE3(0x17,1) +#define OPCODE_FLDW_L OPCODE3(0x17,0) +#define OPCODE_FSTW_L OPCODE3(0x1f,0) +#define OPCODE_STW_M OPCODE3(0x1f,1) + +#define OPCODE_LDH_L OPCODE4(0x11) +#define OPCODE_LDW_L OPCODE4(0x12) +#define OPCODE_LDWM OPCODE4(0x13) +#define OPCODE_STH_L OPCODE4(0x19) +#define OPCODE_STW_L OPCODE4(0x1A) +#define OPCODE_STWM OPCODE4(0x1B) + +#define MAJOR_OP(i) (((i)>>26)&0x3f) +#define R1(i) (((i)>>21)&0x1f) +#define R2(i) (((i)>>16)&0x1f) +#define R3(i) ((i)&0x1f) +#define FR3(i) ((((i)&0x1f)<<1)|(((i)>>6)&1)) +#define IM(i,n) (((i)>>1&((1<<(n-1))-1))|((i)&1?((0-1L)<<(n-1)):0)) +#define IM5_2(i) IM((i)>>16,5) +#define IM5_3(i) IM((i),5) +#define IM14(i) IM((i),14) + +#define ERR_NOTHANDLED -1 + +int unaligned_enabled __read_mostly = 1; + +static int emulate_ldh(struct pt_regs *regs, int toreg) +{ + unsigned long saddr = regs->ior; + unsigned long val = 0, temp1; + ASM_EXCEPTIONTABLE_VAR(ret); + + DPRINTF("load " RFMT ":" RFMT " to r%d for 2 bytes\n", + regs->isr, regs->ior, toreg); + + __asm__ __volatile__ ( +" mtsp %4, %%sr1\n" +"1: ldbs 0(%%sr1,%3), %2\n" +"2: ldbs 1(%%sr1,%3), %0\n" +" depw %2, 23, 24, %0\n" +"3: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b) + : "+r" (val), "+r" (ret), "=&r" (temp1) + : "r" (saddr), "r" (regs->isr) ); + + DPRINTF("val = " RFMT "\n", val); + + if (toreg) + regs->gr[toreg] = val; + + return ret; +} + +static int emulate_ldw(struct pt_regs *regs, int toreg, int flop) +{ + unsigned long saddr = regs->ior; + unsigned long val = 0, temp1, temp2; + ASM_EXCEPTIONTABLE_VAR(ret); + + DPRINTF("load " RFMT ":" RFMT " to r%d for 4 bytes\n", + regs->isr, regs->ior, toreg); + + __asm__ __volatile__ ( +" zdep %4,28,2,%2\n" /* r19=(ofs&3)*8 */ +" mtsp %5, %%sr1\n" +" depw %%r0,31,2,%4\n" +"1: ldw 0(%%sr1,%4),%0\n" +"2: ldw 4(%%sr1,%4),%3\n" +" subi 32,%2,%2\n" +" mtctl %2,11\n" +" vshd %0,%3,%0\n" +"3: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b) + : "+r" (val), "+r" (ret), "=&r" (temp1), "=&r" (temp2) + : "r" (saddr), "r" (regs->isr) ); + + DPRINTF("val = " RFMT "\n", val); + + if (flop) + ((__u32*)(regs->fr))[toreg] = val; + else if (toreg) + regs->gr[toreg] = val; + + return ret; +} +static int emulate_ldd(struct pt_regs *regs, int toreg, int flop) +{ + unsigned long saddr = regs->ior; + __u64 val = 0; + ASM_EXCEPTIONTABLE_VAR(ret); + + DPRINTF("load " RFMT ":" RFMT " to r%d for 8 bytes\n", + regs->isr, regs->ior, toreg); + + if (!IS_ENABLED(CONFIG_64BIT) && !flop) + return ERR_NOTHANDLED; + +#ifdef CONFIG_64BIT + __asm__ __volatile__ ( +" depd,z %3,60,3,%%r19\n" /* r19=(ofs&7)*8 */ +" mtsp %4, %%sr1\n" +" depd %%r0,63,3,%3\n" +"1: ldd 0(%%sr1,%3),%0\n" +"2: ldd 8(%%sr1,%3),%%r20\n" +" subi 64,%%r19,%%r19\n" +" mtsar %%r19\n" +" shrpd %0,%%r20,%%sar,%0\n" +"3: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b) + : "=r" (val), "+r" (ret) + : "0" (val), "r" (saddr), "r" (regs->isr) + : "r19", "r20" ); +#else + { + unsigned long shift, temp1; + __asm__ __volatile__ ( +" zdep %2,29,2,%3\n" /* r19=(ofs&3)*8 */ +" mtsp %5, %%sr1\n" +" dep %%r0,31,2,%2\n" +"1: ldw 0(%%sr1,%2),%0\n" +"2: ldw 4(%%sr1,%2),%R0\n" +"3: ldw 8(%%sr1,%2),%4\n" +" subi 32,%3,%3\n" +" mtsar %3\n" +" vshd %0,%R0,%0\n" +" vshd %R0,%4,%R0\n" +"4: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 4b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 4b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 4b) + : "+r" (val), "+r" (ret), "+r" (saddr), "=&r" (shift), "=&r" (temp1) + : "r" (regs->isr) ); + } +#endif + + DPRINTF("val = 0x%llx\n", val); + + if (flop) + regs->fr[toreg] = val; + else if (toreg) + regs->gr[toreg] = val; + + return ret; +} + +static int emulate_sth(struct pt_regs *regs, int frreg) +{ + unsigned long val = regs->gr[frreg], temp1; + ASM_EXCEPTIONTABLE_VAR(ret); + + if (!frreg) + val = 0; + + DPRINTF("store r%d (" RFMT ") to " RFMT ":" RFMT " for 2 bytes\n", frreg, + val, regs->isr, regs->ior); + + __asm__ __volatile__ ( +" mtsp %4, %%sr1\n" +" extrw,u %2, 23, 8, %1\n" +"1: stb %1, 0(%%sr1, %3)\n" +"2: stb %2, 1(%%sr1, %3)\n" +"3: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b) + : "+r" (ret), "=&r" (temp1) + : "r" (val), "r" (regs->ior), "r" (regs->isr) ); + + return ret; +} + +static int emulate_stw(struct pt_regs *regs, int frreg, int flop) +{ + unsigned long val; + ASM_EXCEPTIONTABLE_VAR(ret); + + if (flop) + val = ((__u32*)(regs->fr))[frreg]; + else if (frreg) + val = regs->gr[frreg]; + else + val = 0; + + DPRINTF("store r%d (" RFMT ") to " RFMT ":" RFMT " for 4 bytes\n", frreg, + val, regs->isr, regs->ior); + + + __asm__ __volatile__ ( +" mtsp %3, %%sr1\n" +" zdep %2, 28, 2, %%r19\n" +" dep %%r0, 31, 2, %2\n" +" mtsar %%r19\n" +" depwi,z -2, %%sar, 32, %%r19\n" +"1: ldw 0(%%sr1,%2),%%r20\n" +"2: ldw 4(%%sr1,%2),%%r21\n" +" vshd %%r0, %1, %%r22\n" +" vshd %1, %%r0, %%r1\n" +" and %%r20, %%r19, %%r20\n" +" andcm %%r21, %%r19, %%r21\n" +" or %%r22, %%r20, %%r20\n" +" or %%r1, %%r21, %%r21\n" +" stw %%r20,0(%%sr1,%2)\n" +" stw %%r21,4(%%sr1,%2)\n" +"3: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b) + : "+r" (ret) + : "r" (val), "r" (regs->ior), "r" (regs->isr) + : "r19", "r20", "r21", "r22", "r1" ); + + return ret; +} +static int emulate_std(struct pt_regs *regs, int frreg, int flop) +{ + __u64 val; + ASM_EXCEPTIONTABLE_VAR(ret); + + if (flop) + val = regs->fr[frreg]; + else if (frreg) + val = regs->gr[frreg]; + else + val = 0; + + DPRINTF("store r%d (0x%016llx) to " RFMT ":" RFMT " for 8 bytes\n", frreg, + val, regs->isr, regs->ior); + + if (!IS_ENABLED(CONFIG_64BIT) && !flop) + return ERR_NOTHANDLED; + +#ifdef CONFIG_64BIT + __asm__ __volatile__ ( +" mtsp %3, %%sr1\n" +" depd,z %2, 60, 3, %%r19\n" +" depd %%r0, 63, 3, %2\n" +" mtsar %%r19\n" +" depdi,z -2, %%sar, 64, %%r19\n" +"1: ldd 0(%%sr1,%2),%%r20\n" +"2: ldd 8(%%sr1,%2),%%r21\n" +" shrpd %%r0, %1, %%sar, %%r22\n" +" shrpd %1, %%r0, %%sar, %%r1\n" +" and %%r20, %%r19, %%r20\n" +" andcm %%r21, %%r19, %%r21\n" +" or %%r22, %%r20, %%r20\n" +" or %%r1, %%r21, %%r21\n" +"3: std %%r20,0(%%sr1,%2)\n" +"4: std %%r21,8(%%sr1,%2)\n" +"5: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 5b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 5b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 5b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 5b) + : "+r" (ret) + : "r" (val), "r" (regs->ior), "r" (regs->isr) + : "r19", "r20", "r21", "r22", "r1" ); +#else + { + unsigned long valh=(val>>32),vall=(val&0xffffffffl); + __asm__ __volatile__ ( +" mtsp %4, %%sr1\n" +" zdep %2, 29, 2, %%r19\n" +" dep %%r0, 31, 2, %3\n" +" mtsar %%r19\n" +" zvdepi -2, 32, %%r19\n" +"1: ldw 0(%%sr1,%3),%%r20\n" +"2: ldw 8(%%sr1,%3),%%r21\n" +" vshd %1, %2, %%r1\n" +" vshd %%r0, %1, %1\n" +" vshd %2, %%r0, %2\n" +" and %%r20, %%r19, %%r20\n" +" andcm %%r21, %%r19, %%r21\n" +" or %1, %%r20, %1\n" +" or %2, %%r21, %2\n" +"3: stw %1,0(%%sr1,%3)\n" +"4: stw %%r1,4(%%sr1,%3)\n" +"5: stw %2,8(%%sr1,%3)\n" +"6: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 6b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 6b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 6b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 6b) + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(5b, 6b) + : "+r" (ret) + : "r" (valh), "r" (vall), "r" (regs->ior), "r" (regs->isr) + : "r19", "r20", "r21", "r1" ); + } +#endif + + return ret; +} + +void handle_unaligned(struct pt_regs *regs) +{ + 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; + + __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 (!(current->thread.flags & PARISC_UAC_NOPRINT) && + __ratelimit(&ratelimit)) { + printk(KERN_WARNING "%s(%d): unaligned access to " RFMT + " at ip " RFMT " (iir " RFMT ")\n", + current->comm, task_pid_nr(current), regs->ior, + regs->iaoq[0], regs->iir); +#ifdef DEBUG_UNALIGNED + show_regs(regs); +#endif + } + + if (!unaligned_enabled) + goto force_sigbus; + } + + /* handle modification - OK, it's ugly, see the instruction manual */ + switch (MAJOR_OP(regs->iir)) + { + case 0x03: + case 0x09: + case 0x0b: + if (regs->iir&0x20) + { + modify = 1; + if (regs->iir&0x1000) /* short loads */ + if (regs->iir&0x200) + newbase += IM5_3(regs->iir); + else + newbase += IM5_2(regs->iir); + else if (regs->iir&0x2000) /* scaled indexed */ + { + int shift=0; + switch (regs->iir & OPCODE1_MASK) + { + case OPCODE_LDH_I: + shift= 1; break; + case OPCODE_LDW_I: + shift= 2; break; + case OPCODE_LDD_I: + case OPCODE_LDDA_I: + shift= 3; break; + } + newbase += (R2(regs->iir)?regs->gr[R2(regs->iir)]:0)<<shift; + } else /* simple indexed */ + newbase += (R2(regs->iir)?regs->gr[R2(regs->iir)]:0); + } + break; + case 0x13: + case 0x1b: + modify = 1; + newbase += IM14(regs->iir); + break; + case 0x14: + case 0x1c: + if (regs->iir&8) + { + modify = 1; + newbase += IM14(regs->iir&~0xe); + } + break; + case 0x16: + case 0x1e: + modify = 1; + newbase += IM14(regs->iir&6); + break; + case 0x17: + case 0x1f: + if (regs->iir&4) + { + modify = 1; + newbase += IM14(regs->iir&~4); + } + break; + } + + /* TODO: make this cleaner... */ + switch (regs->iir & OPCODE1_MASK) + { + case OPCODE_LDH_I: + case OPCODE_LDH_S: + ret = emulate_ldh(regs, R3(regs->iir)); + break; + + case OPCODE_LDW_I: + case OPCODE_LDWA_I: + case OPCODE_LDW_S: + case OPCODE_LDWA_S: + ret = emulate_ldw(regs, R3(regs->iir),0); + break; + + case OPCODE_STH: + ret = emulate_sth(regs, R2(regs->iir)); + break; + + case OPCODE_STW: + case OPCODE_STWA: + ret = emulate_stw(regs, R2(regs->iir),0); + break; + +#ifdef CONFIG_64BIT + case OPCODE_LDD_I: + case OPCODE_LDDA_I: + case OPCODE_LDD_S: + case OPCODE_LDDA_S: + ret = emulate_ldd(regs, R3(regs->iir),0); + break; + + case OPCODE_STD: + case OPCODE_STDA: + ret = emulate_std(regs, R2(regs->iir),0); + break; +#endif + + case OPCODE_FLDWX: + case OPCODE_FLDWS: + case OPCODE_FLDWXR: + case OPCODE_FLDWSR: + ret = emulate_ldw(regs,FR3(regs->iir),1); + break; + + case OPCODE_FLDDX: + case OPCODE_FLDDS: + ret = emulate_ldd(regs,R3(regs->iir),1); + break; + + case OPCODE_FSTWX: + case OPCODE_FSTWS: + case OPCODE_FSTWXR: + case OPCODE_FSTWSR: + ret = emulate_stw(regs,FR3(regs->iir),1); + break; + + case OPCODE_FSTDX: + case OPCODE_FSTDS: + ret = emulate_std(regs,R3(regs->iir),1); + break; + + case OPCODE_LDCD_I: + case OPCODE_LDCW_I: + case OPCODE_LDCD_S: + case OPCODE_LDCW_S: + ret = ERR_NOTHANDLED; /* "undefined", but lets kill them. */ + break; + } + switch (regs->iir & OPCODE2_MASK) + { + case OPCODE_FLDD_L: + ret = emulate_ldd(regs,R2(regs->iir),1); + break; + case OPCODE_FSTD_L: + ret = emulate_std(regs, R2(regs->iir),1); + break; +#ifdef CONFIG_64BIT + 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 + } + switch (regs->iir & OPCODE3_MASK) + { + case OPCODE_FLDW_L: + ret = emulate_ldw(regs, R2(regs->iir), 1); + break; + case OPCODE_LDW_M: + ret = emulate_ldw(regs, R2(regs->iir), 0); + break; + + case OPCODE_FSTW_L: + ret = emulate_stw(regs, R2(regs->iir),1); + break; + case OPCODE_STW_M: + ret = emulate_stw(regs, R2(regs->iir),0); + break; + } + switch (regs->iir & OPCODE4_MASK) + { + case OPCODE_LDH_L: + ret = emulate_ldh(regs, R2(regs->iir)); + break; + case OPCODE_LDW_L: + case OPCODE_LDWM: + ret = emulate_ldw(regs, R2(regs->iir),0); + break; + case OPCODE_STH_L: + ret = emulate_sth(regs, R2(regs->iir)); + break; + case OPCODE_STW_L: + case OPCODE_STWM: + ret = emulate_stw(regs, R2(regs->iir),0); + break; + } + + if (ret == 0 && modify && R1(regs->iir)) + regs->gr[R1(regs->iir)] = newbase; + + + if (ret == ERR_NOTHANDLED) + printk(KERN_CRIT "Not-handled unaligned insn 0x%08lx\n", regs->iir); + + DPRINTF("ret = %d\n", ret); + + if (ret) + { + /* + * The unaligned handler failed. + * If we were called by __get_user() or __put_user() jump + * to it's exception fixup handler instead of crashing. + */ + if (!user_mode(regs) && fixup_exception(regs)) + return; + + printk(KERN_CRIT "Unaligned handler failed, ret = %d\n", ret); + die_if_kernel("Unaligned data reference", regs, 28); + + if (ret == -EFAULT) + { + force_sig_fault(SIGSEGV, SEGV_MAPERR, + (void __user *)regs->ior); + } + else + { +force_sigbus: + /* couldn't handle it ... */ + force_sig_fault(SIGBUS, BUS_ADRALN, + (void __user *)regs->ior); + } + + return; + } + + /* else we handled it, let life go on. */ + regs->gr[0]|=PSW_N; +} + +/* + * NB: check_unaligned() is only used for PCXS processors right + * now, so we only check for PA1.1 encodings at this point. + */ + +int +check_unaligned(struct pt_regs *regs) +{ + unsigned long align_mask; + + /* Get alignment mask */ + + align_mask = 0UL; + switch (regs->iir & OPCODE1_MASK) { + + case OPCODE_LDH_I: + case OPCODE_LDH_S: + case OPCODE_STH: + align_mask = 1UL; + break; + + case OPCODE_LDW_I: + case OPCODE_LDWA_I: + case OPCODE_LDW_S: + case OPCODE_LDWA_S: + case OPCODE_STW: + case OPCODE_STWA: + align_mask = 3UL; + break; + + default: + switch (regs->iir & OPCODE4_MASK) { + case OPCODE_LDH_L: + case OPCODE_STH_L: + align_mask = 1UL; + break; + case OPCODE_LDW_L: + case OPCODE_LDWM: + case OPCODE_STW_L: + case OPCODE_STWM: + align_mask = 3UL; + break; + } + break; + } + + return (int)(regs->ior & align_mask); +} + diff --git a/arch/parisc/kernel/unwind.c b/arch/parisc/kernel/unwind.c new file mode 100644 index 000000000..42acc3b52 --- /dev/null +++ b/arch/parisc/kernel/unwind.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kernel unwinding support + * + * (c) 2002-2004 Randolph Chung <tausq@debian.org> + * + * Derived partially from the IA64 implementation. The PA-RISC + * Runtime Architecture Document is also a useful reference to + * understand what is happening here + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/sched/task_stack.h> + +#include <linux/uaccess.h> +#include <asm/assembly.h> +#include <asm/asm-offsets.h> +#include <asm/ptrace.h> + +#include <asm/unwind.h> +#include <asm/switch_to.h> +#include <asm/sections.h> + +/* #define DEBUG 1 */ +#ifdef DEBUG +#define dbg(x...) pr_debug(x) +#else +#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[]; + +static DEFINE_SPINLOCK(unwind_lock); +/* + * the kernel unwind block is not dynamically allocated so that + * 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 __ro_after_init; +static LIST_HEAD(unwind_tables); + +static inline const struct unwind_table_entry * +find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr) +{ + const struct unwind_table_entry *e = NULL; + unsigned long lo, hi, mid; + + lo = 0; + hi = table->length - 1; + + while (lo <= hi) { + mid = (hi - lo) / 2 + lo; + e = &table->table[mid]; + if (addr < e->region_start) + hi = mid - 1; + else if (addr > e->region_end) + lo = mid + 1; + else + return e; + } + + return NULL; +} + +static const struct unwind_table_entry * +find_unwind_entry(unsigned long addr) +{ + struct unwind_table *table; + const struct unwind_table_entry *e = NULL; + + if (addr >= kernel_unwind_table.start && + addr <= kernel_unwind_table.end) + e = find_unwind_entry_in_table(&kernel_unwind_table, addr); + else { + unsigned long flags; + + spin_lock_irqsave(&unwind_lock, flags); + list_for_each_entry(table, &unwind_tables, list) { + if (addr >= table->start && + addr <= table->end) + e = find_unwind_entry_in_table(table, addr); + if (e) { + /* Move-to-front to exploit common traces */ + list_move(&table->list, &unwind_tables); + break; + } + } + spin_unlock_irqrestore(&unwind_lock, flags); + } + + return e; +} + +static void +unwind_table_init(struct unwind_table *table, const char *name, + unsigned long base_addr, unsigned long gp, + void *table_start, void *table_end) +{ + struct unwind_table_entry *start = table_start; + struct unwind_table_entry *end = + (struct unwind_table_entry *)table_end - 1; + + table->name = name; + table->base_addr = base_addr; + table->gp = gp; + table->start = base_addr + start->region_start; + table->end = base_addr + end->region_end; + table->table = (struct unwind_table_entry *)table_start; + table->length = end - start + 1; + INIT_LIST_HEAD(&table->list); + + for (; start <= end; start++) { + if (start < end && + start->region_end > (start+1)->region_start) { + pr_warn("Out of order unwind entry! %px and %px\n", + start, start+1); + } + + start->region_start += base_addr; + start->region_end += base_addr; + } +} + +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) +{ + sort(start, finish - start, sizeof(struct unwind_table_entry), + cmp_unwind_table_entry, NULL); +} + +struct unwind_table * +unwind_table_add(const char *name, unsigned long base_addr, + unsigned long gp, + void *start, void *end) +{ + struct unwind_table *table; + unsigned long flags; + struct unwind_table_entry *s = (struct unwind_table_entry *)start; + struct unwind_table_entry *e = (struct unwind_table_entry *)end; + + unwind_table_sort(s, e); + + table = kmalloc(sizeof(struct unwind_table), GFP_USER); + if (table == NULL) + return NULL; + unwind_table_init(table, name, base_addr, gp, start, end); + spin_lock_irqsave(&unwind_lock, flags); + list_add_tail(&table->list, &unwind_tables); + spin_unlock_irqrestore(&unwind_lock, flags); + + return table; +} + +void unwind_table_remove(struct unwind_table *table) +{ + unsigned long flags; + + spin_lock_irqsave(&unwind_lock, flags); + list_del(&table->list); + spin_unlock_irqrestore(&unwind_lock, flags); + + kfree(table); +} + +/* Called from setup_arch to import the kernel unwind info */ +int __init unwind_init(void) +{ + long start, stop; + register unsigned long gp __asm__ ("r27"); + + start = (long)&__start___unwind[0]; + stop = (long)&__stop___unwind[0]; + + dbg("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", + start, stop, + (stop - start) / sizeof(struct unwind_table_entry)); + + unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START, + gp, + &__start___unwind[0], &__stop___unwind[0]); +#if 0 + { + int i; + for (i = 0; i < 10; i++) + { + printk("region 0x%x-0x%x\n", + __start___unwind[i].region_start, + __start___unwind[i].region_end); + } + } +#endif + return 0; +} + +static bool pc_is_kernel_fn(unsigned long pc, void *fn) +{ + return (unsigned long)dereference_kernel_function_descriptor(fn) == pc; +} + +static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size) +{ + /* + * We have to use void * instead of a function pointer, because + * function pointers aren't a pointer to the function on 64-bit. + * Make them const so the compiler knows they live in .text + * Note: We could use dereference_kernel_function_descriptor() + * instead but we want to keep it simple here. + */ + extern void * const handle_interruption; + extern void * const ret_from_kernel_thread; + extern void * const syscall_exit; + extern void * const intr_return; + extern void * const _switch_to_ret; +#ifdef CONFIG_IRQSTACKS + extern void * const _call_on_stack; +#endif /* CONFIG_IRQSTACKS */ + + if (pc_is_kernel_fn(pc, handle_interruption)) { + 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; + } + + if (pc_is_kernel_fn(pc, ret_from_kernel_thread) || + pc_is_kernel_fn(pc, syscall_exit)) { + info->prev_sp = info->prev_ip = 0; + return 1; + } + + if (pc_is_kernel_fn(pc, intr_return)) { + struct pt_regs *regs; + + dbg("Found intr_return()\n"); + regs = (struct pt_regs *)(info->sp - PT_SZ_ALGN); + info->prev_sp = regs->gr[30]; + info->prev_ip = regs->iaoq[0]; + info->rp = regs->gr[2]; + return 1; + } + + if (pc_is_kernel_fn(pc, _switch_to) || + pc_is_kernel_fn(pc, _switch_to_ret)) { + info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE; + info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET); + return 1; + } + +#ifdef CONFIG_IRQSTACKS + if (pc_is_kernel_fn(pc, _call_on_stack)) { + info->prev_sp = *(unsigned long *)(info->sp - FRAME_SIZE - REG_SZ); + info->prev_ip = *(unsigned long *)(info->sp - FRAME_SIZE - RP_OFFSET); + return 1; + } +#endif + return 0; +} + +static void unwind_frame_regs(struct unwind_frame_info *info) +{ + const struct unwind_table_entry *e; + unsigned long npc; + unsigned int insn; + long frame_size = 0; + int looking_for_rp, rpoffset = 0; + + e = find_unwind_entry(info->ip); + if (e == NULL) { + unsigned long sp; + + dbg("Cannot find unwind entry for %pS; forced unwinding\n", + (void *) info->ip); + + /* Since we are doing the unwinding blind, we don't know if + we are adjusting the stack correctly or extracting the rp + correctly. The rp is checked to see if it belongs to the + kernel text section, if not we assume we don't have a + correct stack frame and we continue to unwind the stack. + This is not quite correct, and will fail for loadable + modules. */ + sp = info->sp & ~63; + do { + unsigned long tmp; + + info->prev_sp = sp - 64; + info->prev_ip = 0; + + /* Check if stack is inside kernel stack area */ + if ((info->prev_sp - (unsigned long) task_stack_page(info->t)) + >= THREAD_SIZE) { + info->prev_sp = 0; + break; + } + + if (copy_from_kernel_nofault(&tmp, + (void *)info->prev_sp - RP_OFFSET, sizeof(tmp))) + break; + info->prev_ip = tmp; + sp = info->prev_sp; + } while (!kernel_text_address(info->prev_ip)); + + info->rp = 0; + + dbg("analyzing func @ %lx with no unwind info, setting " + "prev_sp=%lx prev_ip=%lx\n", info->ip, + info->prev_sp, info->prev_ip); + } else { + dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, " + "Save_RP = %d, Millicode = %d size = %u\n", + e->region_start, e->region_end, e->Save_SP, e->Save_RP, + e->Millicode, e->Total_frame_size); + + looking_for_rp = e->Save_RP; + + for (npc = e->region_start; + (frame_size < (e->Total_frame_size << 3) || + looking_for_rp) && + npc < info->ip; + npc += 4) { + + insn = *(unsigned int *)npc; + + if ((insn & 0xffffc001) == 0x37de0000 || + (insn & 0xffe00001) == 0x6fc00000) { + /* ldo X(sp), sp, or stwm X,D(sp) */ + frame_size += (insn & 0x3fff) >> 1; + dbg("analyzing func @ %lx, insn=%08x @ " + "%lx, frame_size = %ld\n", info->ip, + insn, npc, frame_size); + } else if ((insn & 0xffe00009) == 0x73c00008) { + /* std,ma X,D(sp) */ + frame_size += ((insn >> 4) & 0x3ff) << 3; + dbg("analyzing func @ %lx, insn=%08x @ " + "%lx, frame_size = %ld\n", info->ip, + insn, npc, frame_size); + } else if (insn == 0x6bc23fd9) { + /* stw rp,-20(sp) */ + rpoffset = 20; + looking_for_rp = 0; + dbg("analyzing func @ %lx, insn=stw rp," + "-20(sp) @ %lx\n", info->ip, npc); + } else if (insn == 0x0fc212c1) { + /* std rp,-16(sr0,sp) */ + rpoffset = 16; + looking_for_rp = 0; + dbg("analyzing func @ %lx, insn=std rp," + "-16(sp) @ %lx\n", info->ip, npc); + } + } + + if (frame_size > e->Total_frame_size << 3) + frame_size = e->Total_frame_size << 3; + + 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, + info->prev_ip, npc); + } +} + +void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, + struct pt_regs *regs) +{ + memset(info, 0, sizeof(struct unwind_frame_info)); + info->t = t; + info->sp = regs->gr[30]; + info->ip = regs->iaoq[0]; + info->rp = regs->gr[2]; + info->r31 = regs->gr[31]; + + dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", + t ? (int)t->pid : -1, info->sp, info->ip); +} + +void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t) +{ + struct pt_regs *r = &t->thread.regs; + struct pt_regs *r2; + + r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC); + if (!r2) + return; + *r2 = *r; + r2->gr[30] = r->ksp; + r2->iaoq[0] = r->kpc; + unwind_frame_init(info, t, r2); + kfree(r2); +} + +#define get_parisc_stackpointer() ({ \ + unsigned long sp; \ + __asm__("copy %%r30, %0" : "=r"(sp)); \ + (sp); \ +}) + +void unwind_frame_init_task(struct unwind_frame_info *info, + struct task_struct *task, struct pt_regs *regs) +{ + task = task ? task : current; + + if (task == current) { + struct pt_regs r; + + if (!regs) { + memset(&r, 0, sizeof(r)); + r.iaoq[0] = _THIS_IP_; + r.gr[2] = _RET_IP_; + r.gr[30] = get_parisc_stackpointer(); + regs = &r; + } + unwind_frame_init(info, task, regs); + } else { + unwind_frame_init_from_blocked_task(info, task); + } +} + +int unwind_once(struct unwind_frame_info *next_frame) +{ + unwind_frame_regs(next_frame); + + if (next_frame->prev_sp == 0 || + next_frame->prev_ip == 0) + return -1; + + next_frame->sp = next_frame->prev_sp; + next_frame->ip = next_frame->prev_ip; + next_frame->prev_sp = 0; + next_frame->prev_ip = 0; + + dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", + next_frame->t ? (int)next_frame->t->pid : -1, + next_frame->sp, next_frame->ip); + + return 0; +} + +int unwind_to_user(struct unwind_frame_info *info) +{ + int ret; + + do { + ret = unwind_once(info); + } while (!ret && !(info->ip & 3)); + + return ret; +} + +unsigned long return_address(unsigned int level) +{ + struct unwind_frame_info info; + + /* initialize unwind info */ + unwind_frame_init_task(&info, current, NULL); + + /* unwind stack */ + level += 2; + 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/vdso.c b/arch/parisc/kernel/vdso.c new file mode 100644 index 000000000..47e5960a2 --- /dev/null +++ b/arch/parisc/kernel/vdso.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Helge Deller <deller@gmx.de> + * + * based on arch/s390/kernel/vdso.c which is + * Copyright IBM Corp. 2008 + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + */ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/elf.h> +#include <linux/timekeeper_internal.h> +#include <linux/compat.h> +#include <linux/nsproxy.h> +#include <linux/time_namespace.h> +#include <linux/random.h> + +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/sections.h> +#include <asm/vdso.h> +#include <asm/cacheflush.h> + +extern char vdso32_start, vdso32_end; +extern char vdso64_start, vdso64_end; + +static int vdso_mremap(const struct vm_special_mapping *sm, + struct vm_area_struct *vma) +{ + current->mm->context.vdso_base = vma->vm_start; + return 0; +} + +#ifdef CONFIG_64BIT +static struct vm_special_mapping vdso64_mapping = { + .name = "[vdso]", + .mremap = vdso_mremap, +}; +#endif + +static struct vm_special_mapping vdso32_mapping = { + .name = "[vdso]", + .mremap = vdso_mremap, +}; + +/* + * This is called from binfmt_elf, we create the special vma for the + * vDSO and insert it into the mm struct tree + */ +int arch_setup_additional_pages(struct linux_binprm *bprm, + int executable_stack) +{ + + unsigned long vdso_text_start, vdso_text_len, map_base; + struct vm_special_mapping *vdso_mapping; + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + int rc; + + if (mmap_write_lock_killable(mm)) + return -EINTR; + +#ifdef CONFIG_64BIT + if (!is_compat_task()) { + vdso_text_len = &vdso64_end - &vdso64_start; + vdso_mapping = &vdso64_mapping; + } else +#endif + { + vdso_text_len = &vdso32_end - &vdso32_start; + vdso_mapping = &vdso32_mapping; + } + + map_base = mm->mmap_base; + if (current->flags & PF_RANDOMIZE) + map_base -= prandom_u32_max(0x20) * PAGE_SIZE; + + vdso_text_start = get_unmapped_area(NULL, map_base, vdso_text_len, 0, 0); + + /* VM_MAYWRITE for COW so gdb can set breakpoints */ + vma = _install_special_mapping(mm, vdso_text_start, vdso_text_len, + VM_READ|VM_EXEC| + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, + vdso_mapping); + if (IS_ERR(vma)) { + do_munmap(mm, vdso_text_start, PAGE_SIZE, NULL); + rc = PTR_ERR(vma); + } else { + current->mm->context.vdso_base = vdso_text_start; + rc = 0; + } + + mmap_write_unlock(mm); + return rc; +} + +static struct page ** __init vdso_setup_pages(void *start, void *end) +{ + int pages = (end - start) >> PAGE_SHIFT; + struct page **pagelist; + int i; + + pagelist = kcalloc(pages + 1, sizeof(struct page *), GFP_KERNEL); + if (!pagelist) + panic("%s: Cannot allocate page list for VDSO", __func__); + for (i = 0; i < pages; i++) + pagelist[i] = virt_to_page(start + i * PAGE_SIZE); + return pagelist; +} + +static int __init vdso_init(void) +{ +#ifdef CONFIG_64BIT + vdso64_mapping.pages = vdso_setup_pages(&vdso64_start, &vdso64_end); +#endif + if (IS_ENABLED(CONFIG_COMPAT) || !IS_ENABLED(CONFIG_64BIT)) + vdso32_mapping.pages = vdso_setup_pages(&vdso32_start, &vdso32_end); + return 0; +} +arch_initcall(vdso_init); diff --git a/arch/parisc/kernel/vdso32/Makefile b/arch/parisc/kernel/vdso32/Makefile new file mode 100644 index 000000000..4459a48d2 --- /dev/null +++ b/arch/parisc/kernel/vdso32/Makefile @@ -0,0 +1,53 @@ +# List of files in the vdso, has to be asm only for now + +obj-vdso32 = note.o sigtramp.o restart_syscall.o + +# Build rules + +targets := $(obj-vdso32) vdso32.so +obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32)) + +ccflags-y := -shared -fno-common -fbuiltin -mno-fast-indirect-calls -O2 -mno-long-calls +# -march=1.1 -mschedule=7100LC +ccflags-y += -nostdlib -Wl,-soname=linux-vdso32.so.1 \ + $(call ld-option, -Wl$(comma)--hash-style=sysv) +asflags-y := -D__VDSO32__ -s + +KBUILD_AFLAGS += -DBUILD_VDSO +KBUILD_CFLAGS += -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING + +VDSO_LIBGCC := $(shell $(CROSS32CC) -print-libgcc-file-name) + +obj-y += vdso32_wrapper.o +extra-y += vdso32.lds +CPPFLAGS_vdso32.lds += -P -C # -U$(ARCH) + +$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so FORCE + +# Force dependency (incbin is bad) +# link rule for the .so file, .lds has to be first +$(obj)/vdso32.so: $(src)/vdso32.lds $(obj-vdso32) $(obj-cvdso32) $(VDSO_LIBGCC) FORCE + $(call if_changed,vdso32ld) + +# assembly rules for the .S files +$(obj-vdso32): %.o: %.S FORCE + $(call if_changed_dep,vdso32as) + +$(obj-cvdso32): %.o: %.c FORCE + $(call if_changed_dep,vdso32cc) + +# actual build commands +quiet_cmd_vdso32ld = VDSO32L $@ + cmd_vdso32ld = $(CROSS32CC) $(c_flags) -Wl,-T $(filter-out FORCE, $^) -o $@ +quiet_cmd_vdso32as = VDSO32A $@ + cmd_vdso32as = $(CROSS32CC) $(a_flags) -c -o $@ $< +quiet_cmd_vdso32cc = VDSO32C $@ + cmd_vdso32cc = $(CROSS32CC) $(c_flags) -c -fPIC -mno-fast-indirect-calls -o $@ $< + +# Generate VDSO offsets using helper script +gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh +quiet_cmd_vdsosym = VDSOSYM $@ + cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ + +include/generated/vdso32-offsets.h: $(obj)/vdso32.so FORCE + $(call if_changed,vdsosym) diff --git a/arch/parisc/kernel/vdso32/gen_vdso_offsets.sh b/arch/parisc/kernel/vdso32/gen_vdso_offsets.sh new file mode 100755 index 000000000..da39d6cff --- /dev/null +++ b/arch/parisc/kernel/vdso32/gen_vdso_offsets.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +# +# Match symbols in the DSO that look like VDSO_*; produce a header file +# of constant offsets into the shared object. +# +# Doing this inside the Makefile will break the $(filter-out) function, +# causing Kbuild to rebuild the vdso-offsets header file every time. +# +# Inspired by arm64 version. +# + +LC_ALL=C +sed -n 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso32_offset_\2\t0x\1/p' diff --git a/arch/parisc/kernel/vdso32/note.S b/arch/parisc/kernel/vdso32/note.S new file mode 100644 index 000000000..bb350918b --- /dev/null +++ b/arch/parisc/kernel/vdso32/note.S @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text. + * Here we can supply some information useful to userland. + */ + +#include <linux/uts.h> +#include <linux/version.h> + +#define ASM_ELF_NOTE_BEGIN(name, flags, vendor, type) \ + .section name, flags; \ + .balign 4; \ + .long 1f - 0f; /* name length */ \ + .long 3f - 2f; /* data length */ \ + .long type; /* note type */ \ +0: .asciz vendor; /* vendor name */ \ +1: .balign 4; \ +2: + +#define ASM_ELF_NOTE_END \ +3: .balign 4; /* pad out section */ \ + .previous + + ASM_ELF_NOTE_BEGIN(".note.kernel-version", "a", UTS_SYSNAME, 0) + .long LINUX_VERSION_CODE + ASM_ELF_NOTE_END diff --git a/arch/parisc/kernel/vdso32/restart_syscall.S b/arch/parisc/kernel/vdso32/restart_syscall.S new file mode 100644 index 000000000..7e82008d7 --- /dev/null +++ b/arch/parisc/kernel/vdso32/restart_syscall.S @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Syscall restart trampoline for 32 and 64 bits processes. + * + * Copyright (C) 2018-2022 Helge Deller <deller@gmx.de> + * Copyright (C) 2022 John David Anglin <dave.anglin@bell.net> + */ + +#include <asm/unistd.h> +#include <asm/vdso.h> + +#include <linux/linkage.h> + + .text + +ENTRY_CFI(__kernel_restart_syscall) + /* + * Setup a trampoline to restart the syscall + * with __NR_restart_syscall + */ + + /* load return pointer */ +#if defined(__VDSO64__) + ldd 0(%sp), %r31 +#elif defined(__VDSO32__) + ldw 0(%sp), %r31 +#endif + + be 0x100(%sr2, %r0) + ldi __NR_restart_syscall, %r20 + +ENDPROC_CFI(__kernel_restart_syscall) diff --git a/arch/parisc/kernel/vdso32/sigtramp.S b/arch/parisc/kernel/vdso32/sigtramp.S new file mode 100644 index 000000000..192da7077 --- /dev/null +++ b/arch/parisc/kernel/vdso32/sigtramp.S @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Signal trampolines for 32 bit processes. + * + * Copyright (C) 2006 Randolph Chung <tausq@debian.org> + * Copyright (C) 2018-2022 Helge Deller <deller@gmx.de> + * Copyright (C) 2022 John David Anglin <dave.anglin@bell.net> + */ +#include <asm/unistd.h> +#include <linux/linkage.h> +#include <generated/asm-offsets.h> + + .text + +/* Gdb expects the trampoline is on the stack and the pc is offset from + a 64-byte boundary by 0, 4 or 5 instructions. Since the vdso trampoline + is not on the stack, we need a new variant with different offsets and + data to tell gdb where to find the signal context on the stack. + + Here we put the offset to the context data at the start of the trampoline + region and offset the first trampoline by 2 instructions. Please do + not change the trampoline as the code in gdb depends on the following + instruction sequence exactly. + */ + .align 64 + .word SIGFRAME_CONTEXT_REGS32 + +/* The nop here is a hack. The dwarf2 unwind routines subtract 1 from + the return address to get an address in the middle of the presumed + call instruction. Since we don't have a call here, we artifically + extend the range covered by the unwind info by adding a nop before + the real start. + */ + nop + + .globl __kernel_sigtramp_rt + .type __kernel_sigtramp_rt, @function +__kernel_sigtramp_rt: + .proc + .callinfo FRAME=ASM_SIGFRAME_SIZE32,CALLS,SAVE_RP + .entry + +.Lsigrt_start = . - 4 +0: ldi 0, %r25 /* (in_syscall=0) */ + ldi __NR_rt_sigreturn, %r20 + ble 0x100(%sr2, %r0) + nop + +1: ldi 1, %r25 /* (in_syscall=1) */ + ldi __NR_rt_sigreturn, %r20 + ble 0x100(%sr2, %r0) + nop +.Lsigrt_end: + .exit + .procend + .size __kernel_sigtramp_rt,.-__kernel_sigtramp_rt + + + .section .eh_frame,"a",@progbits + +/* This is where the mcontext_t struct can be found on the stack. */ +#define PTREGS SIGFRAME_CONTEXT_REGS32 /* 32-bit process offset is -672 */ + +/* Register REGNO can be found at offset OFS of the mcontext_t structure. */ + .macro rsave regno,ofs + .byte 0x05 /* DW_CFA_offset_extended */ + .uleb128 \regno; /* regno */ + .uleb128 \ofs /* factored offset */ + .endm + +.Lcie: + .long .Lcie_end - .Lcie_start +.Lcie_start: + .long 0 /* CIE ID */ + .byte 1 /* Version number */ + .stringz "zRS" /* NUL-terminated augmentation string */ + .uleb128 4 /* Code alignment factor */ + .sleb128 4 /* Data alignment factor */ + .byte 89 /* Return address register column, iaoq[0] */ + .uleb128 1 /* Augmentation value length */ + .byte 0x1b /* DW_EH_PE_pcrel | DW_EH_PE_sdata4. */ + .byte 0x0f /* DW_CFA_def_cfa_expresion */ + .uleb128 9f - 1f /* length */ +1: + .byte 0x8e /* DW_OP_breg30 */ + .sleb128 PTREGS +9: + .balign 4 +.Lcie_end: + + .long .Lfde0_end - .Lfde0_start +.Lfde0_start: + .long .Lfde0_start - .Lcie /* CIE pointer. */ + .long .Lsigrt_start - . /* PC start, length */ + .long .Lsigrt_end - .Lsigrt_start + .uleb128 0 /* Augmentation */ + + /* General registers */ + rsave 1, 2 + rsave 2, 3 + rsave 3, 4 + rsave 4, 5 + rsave 5, 6 + rsave 6, 7 + rsave 7, 8 + rsave 8, 9 + rsave 9, 10 + rsave 10, 11 + rsave 11, 12 + rsave 12, 13 + rsave 13, 14 + rsave 14, 15 + rsave 15, 16 + rsave 16, 17 + rsave 17, 18 + rsave 18, 19 + rsave 19, 20 + rsave 20, 21 + rsave 21, 22 + rsave 22, 23 + rsave 23, 24 + rsave 24, 25 + rsave 25, 26 + rsave 26, 27 + rsave 27, 28 + rsave 28, 29 + rsave 29, 30 + rsave 30, 31 + rsave 31, 32 + + /* Floating-point registers */ + rsave 32, 42 + rsave 33, 43 + rsave 34, 44 + rsave 35, 45 + rsave 36, 46 + rsave 37, 47 + rsave 38, 48 + rsave 39, 49 + rsave 40, 50 + rsave 41, 51 + rsave 42, 52 + rsave 43, 53 + rsave 44, 54 + rsave 45, 55 + rsave 46, 56 + rsave 47, 57 + rsave 48, 58 + rsave 49, 59 + rsave 50, 60 + rsave 51, 61 + rsave 52, 62 + rsave 53, 63 + rsave 54, 64 + rsave 55, 65 + rsave 56, 66 + rsave 57, 67 + rsave 58, 68 + rsave 59, 69 + rsave 60, 70 + rsave 61, 71 + rsave 62, 72 + rsave 63, 73 + rsave 64, 74 + rsave 65, 75 + rsave 66, 76 + rsave 67, 77 + rsave 68, 78 + rsave 69, 79 + rsave 70, 80 + rsave 71, 81 + rsave 72, 82 + rsave 73, 83 + rsave 74, 84 + rsave 75, 85 + rsave 76, 86 + rsave 77, 87 + rsave 78, 88 + rsave 79, 89 + rsave 80, 90 + rsave 81, 91 + rsave 82, 92 + rsave 83, 93 + rsave 84, 94 + rsave 85, 95 + rsave 86, 96 + rsave 87, 97 + + /* SAR register */ + rsave 88, 102 + + /* iaoq[0] return address register */ + rsave 89, 100 + .balign 4 +.Lfde0_end: diff --git a/arch/parisc/kernel/vdso32/vdso32.lds.S b/arch/parisc/kernel/vdso32/vdso32.lds.S new file mode 100644 index 000000000..d4aff3af5 --- /dev/null +++ b/arch/parisc/kernel/vdso32/vdso32.lds.S @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This is the infamous ld script for the 32 bits vdso library + */ +#include <asm/vdso.h> +#include <asm/page.h> + +/* Default link addresses for the vDSOs */ +OUTPUT_FORMAT("elf32-hppa-linux") +OUTPUT_ARCH(hppa) +ENTRY(_start) + +SECTIONS +{ + . = VDSO_LBASE + SIZEOF_HEADERS; + .hash : { *(.hash) } :text + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + .note : { *(.note.*) } :text :note + + . = ALIGN (16); + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + /* Other stuff is appended to the text segment: */ + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .rodata2 : { *(.data.rel.ro) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + .gcc_except_table : { *(.gcc_except_table) } + .fixup : { *(.fixup) } + + .dynamic : { *(.dynamic) } :text :dynamic + .plt : { *(.plt) } + .got : { *(.got) } + + _end = .; + __end = .; + PROVIDE (end = .); + + + /* Stabs debugging sections are here too + */ + .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) } + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + /DISCARD/ : { *(.note.GNU-stack) } + /DISCARD/ : { *(.data .data.* .gnu.linkonce.d.* .sdata*) } + /DISCARD/ : { *(.bss .sbss .dynbss .dynsbss) } +} + + +PHDRS +{ + text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */ + note PT_NOTE FLAGS(4); /* PF_R */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + eh_frame_hdr PT_GNU_EH_FRAME; +} + + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + VDSO_VERSION_STRING { + global: + __kernel_sigtramp_rt32; + __kernel_restart_syscall32; + local: *; + }; +} diff --git a/arch/parisc/kernel/vdso32/vdso32_wrapper.S b/arch/parisc/kernel/vdso32/vdso32_wrapper.S new file mode 100644 index 000000000..5ac06093e --- /dev/null +++ b/arch/parisc/kernel/vdso32/vdso32_wrapper.S @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/linkage.h> +#include <asm/page.h> + + __PAGE_ALIGNED_DATA + + .globl vdso32_start, vdso32_end + .balign PAGE_SIZE +vdso32_start: + .incbin "arch/parisc/kernel/vdso32/vdso32.so" + .balign PAGE_SIZE +vdso32_end: + + .previous diff --git a/arch/parisc/kernel/vdso64/Makefile b/arch/parisc/kernel/vdso64/Makefile new file mode 100644 index 000000000..f3d604579 --- /dev/null +++ b/arch/parisc/kernel/vdso64/Makefile @@ -0,0 +1,48 @@ +# List of files in the vdso, has to be asm only for now + +obj-vdso64 = note.o sigtramp.o restart_syscall.o + +# Build rules + +targets := $(obj-vdso64) vdso64.so +obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64)) + + +ccflags-y := -shared -fno-common -fno-builtin +ccflags-y += -nostdlib -Wl,-soname=linux-vdso64.so.1 \ + $(call ld-option, -Wl$(comma)--hash-style=sysv) +asflags-y := -D__VDSO64__ -s + +KBUILD_AFLAGS += -DBUILD_VDSO +KBUILD_CFLAGS += -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING + +VDSO_LIBGCC := $(shell $(CC) -print-libgcc-file-name) + +obj-y += vdso64_wrapper.o +extra-y += vdso64.lds +CPPFLAGS_vdso64.lds += -P -C -U$(ARCH) + +$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so FORCE + +# Force dependency (incbin is bad) +# link rule for the .so file, .lds has to be first +$(obj)/vdso64.so: $(src)/vdso64.lds $(obj-vdso64) $(VDSO_LIBGCC) FORCE + $(call if_changed,vdso64ld) + +# assembly rules for the .S files +$(obj-vdso64): %.o: %.S FORCE + $(call if_changed_dep,vdso64as) + +# actual build commands +quiet_cmd_vdso64ld = VDSO64L $@ + cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $(filter-out FORCE, $^) -o $@ +quiet_cmd_vdso64as = VDSO64A $@ + cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $< + +# Generate VDSO offsets using helper script +gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh +quiet_cmd_vdsosym = VDSOSYM $@ + cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ + +include/generated/vdso64-offsets.h: $(obj)/vdso64.so FORCE + $(call if_changed,vdsosym) diff --git a/arch/parisc/kernel/vdso64/gen_vdso_offsets.sh b/arch/parisc/kernel/vdso64/gen_vdso_offsets.sh new file mode 100755 index 000000000..37f05cb38 --- /dev/null +++ b/arch/parisc/kernel/vdso64/gen_vdso_offsets.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +# +# Match symbols in the DSO that look like VDSO_*; produce a header file +# of constant offsets into the shared object. +# +# Doing this inside the Makefile will break the $(filter-out) function, +# causing Kbuild to rebuild the vdso-offsets header file every time. +# +# Inspired by arm64 version. +# + +LC_ALL=C +sed -n 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso64_offset_\2\t0x\1/p' diff --git a/arch/parisc/kernel/vdso64/note.S b/arch/parisc/kernel/vdso64/note.S new file mode 100644 index 000000000..bd1fa2359 --- /dev/null +++ b/arch/parisc/kernel/vdso64/note.S @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "../vdso32/note.S" diff --git a/arch/parisc/kernel/vdso64/restart_syscall.S b/arch/parisc/kernel/vdso64/restart_syscall.S new file mode 100644 index 000000000..83004451f --- /dev/null +++ b/arch/parisc/kernel/vdso64/restart_syscall.S @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include "../vdso32/restart_syscall.S" diff --git a/arch/parisc/kernel/vdso64/sigtramp.S b/arch/parisc/kernel/vdso64/sigtramp.S new file mode 100644 index 000000000..66a6d2b24 --- /dev/null +++ b/arch/parisc/kernel/vdso64/sigtramp.S @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Signal trampolines for 64 bit processes. + * + * Copyright (C) 2006 Randolph Chung <tausq@debian.org> + * Copyright (C) 2018-2022 Helge Deller <deller@gmx.de> + * Copyright (C) 2022 John David Anglin <dave.anglin@bell.net> + */ +#include <asm/unistd.h> +#include <linux/linkage.h> +#include <generated/asm-offsets.h> + + .text + +/* Gdb expects the trampoline is on the stack and the pc is offset from + a 64-byte boundary by 0, 4 or 5 instructions. Since the vdso trampoline + is not on the stack, we need a new variant with different offsets and + data to tell gdb where to find the signal context on the stack. + + Here we put the offset to the context data at the start of the trampoline + region and offset the first trampoline by 2 instructions. Please do + not change the trampoline as the code in gdb depends on the following + instruction sequence exactly. + */ + .align 64 + .word SIGFRAME_CONTEXT_REGS + +/* The nop here is a hack. The dwarf2 unwind routines subtract 1 from + the return address to get an address in the middle of the presumed + call instruction. Since we don't have a call here, we artifically + extend the range covered by the unwind info by adding a nop before + the real start. + */ + nop + + .globl __kernel_sigtramp_rt + .type __kernel_sigtramp_rt, @function +__kernel_sigtramp_rt: + .proc + .callinfo FRAME=ASM_SIGFRAME_SIZE,CALLS,SAVE_RP + .entry + +.Lsigrt_start = . - 4 +0: ldi 0, %r25 /* (in_syscall=0) */ + ldi __NR_rt_sigreturn, %r20 + ble 0x100(%sr2, %r0) + nop + +1: ldi 1, %r25 /* (in_syscall=1) */ + ldi __NR_rt_sigreturn, %r20 + ble 0x100(%sr2, %r0) + nop +.Lsigrt_end: + .exit + .procend + .size __kernel_sigtramp_rt,.-__kernel_sigtramp_rt + + .section .eh_frame,"a",@progbits + +/* This is where the mcontext_t struct can be found on the stack. */ +#define PTREGS SIGFRAME_CONTEXT_REGS /* 64-bit process offset is -720 */ + +/* Register REGNO can be found at offset OFS of the mcontext_t structure. */ + .macro rsave regno,ofs + .byte 0x05 /* DW_CFA_offset_extended */ + .uleb128 \regno; /* regno */ + .uleb128 \ofs /* factored offset */ + .endm + +.Lcie: + .long .Lcie_end - .Lcie_start +.Lcie_start: + .long 0 /* CIE ID */ + .byte 1 /* Version number */ + .stringz "zRS" /* NUL-terminated augmentation string */ + .uleb128 4 /* Code alignment factor */ + .sleb128 8 /* Data alignment factor */ + .byte 61 /* Return address register column, iaoq[0] */ + .uleb128 1 /* Augmentation value length */ + .byte 0x1b /* DW_EH_PE_pcrel | DW_EH_PE_sdata4. */ + .byte 0x0f /* DW_CFA_def_cfa_expresion */ + .uleb128 9f - 1f /* length */ +1: + .byte 0x8e /* DW_OP_breg30 */ + .sleb128 PTREGS +9: + .balign 8 +.Lcie_end: + + .long .Lfde0_end - .Lfde0_start +.Lfde0_start: + .long .Lfde0_start - .Lcie /* CIE pointer. */ + .long .Lsigrt_start - . /* PC start, length */ + .long .Lsigrt_end - .Lsigrt_start + .uleb128 0 /* Augmentation */ + + /* General registers */ + rsave 1, 2 + rsave 2, 3 + rsave 3, 4 + rsave 4, 5 + rsave 5, 6 + rsave 6, 7 + rsave 7, 8 + rsave 8, 9 + rsave 9, 10 + rsave 10, 11 + rsave 11, 12 + rsave 12, 13 + rsave 13, 14 + rsave 14, 15 + rsave 15, 16 + rsave 16, 17 + rsave 17, 18 + rsave 18, 19 + rsave 19, 20 + rsave 20, 21 + rsave 21, 22 + rsave 22, 23 + rsave 23, 24 + rsave 24, 25 + rsave 25, 26 + rsave 26, 27 + rsave 27, 28 + rsave 28, 29 + rsave 29, 30 + rsave 30, 31 + rsave 31, 32 + + /* Floating-point registers */ + rsave 32, 36 + rsave 33, 37 + rsave 34, 38 + rsave 35, 39 + rsave 36, 40 + rsave 37, 41 + rsave 38, 42 + rsave 39, 43 + rsave 40, 44 + rsave 41, 45 + rsave 42, 46 + rsave 43, 47 + rsave 44, 48 + rsave 45, 49 + rsave 46, 50 + rsave 47, 51 + rsave 48, 52 + rsave 49, 53 + rsave 50, 54 + rsave 51, 55 + rsave 52, 56 + rsave 53, 57 + rsave 54, 58 + rsave 55, 59 + rsave 56, 60 + rsave 57, 61 + rsave 58, 62 + rsave 59, 63 + + /* SAR register */ + rsave 60, 67 + + /* iaoq[0] return address register */ + rsave 61, 65 + .balign 8 +.Lfde0_end: diff --git a/arch/parisc/kernel/vdso64/vdso64.lds.S b/arch/parisc/kernel/vdso64/vdso64.lds.S new file mode 100644 index 000000000..de1fb4b19 --- /dev/null +++ b/arch/parisc/kernel/vdso64/vdso64.lds.S @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This is the infamous ld script for the 64 bits vdso library + */ +#include <asm/vdso.h> + +/* Default link addresses for the vDSOs */ +OUTPUT_FORMAT("elf64-hppa-linux") +OUTPUT_ARCH(hppa:hppa2.0w) +ENTRY(_start) + +SECTIONS +{ + . = VDSO_LBASE + SIZEOF_HEADERS; + .hash : { *(.hash) } :text + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + .note : { *(.note.*) } :text :note + + . = ALIGN (16); + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + /* Other stuff is appended to the text segment: */ + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + .gcc_except_table : { *(.gcc_except_table) } + .fixup : { *(.fixup) } + + .dynamic : { *(.dynamic) } :text :dynamic + .plt : { *(.plt) } + .got : { *(.got) } + + _end = .; + __end = .; + PROVIDE (end = .); + + + /* Stabs debugging sections are here too + */ + .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) } + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + /DISCARD/ : { *(.note.GNU-stack) } + /DISCARD/ : { *(.data .data.* .gnu.linkonce.d.* .sdata*) } + /DISCARD/ : { *(.bss .sbss .dynbss .dynsbss) } +} + + +PHDRS +{ + text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */ + note PT_NOTE FLAGS(4); /* PF_R */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + eh_frame_hdr PT_GNU_EH_FRAME; +} + + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + VDSO_VERSION_STRING { + global: + __kernel_sigtramp_rt64; + __kernel_restart_syscall64; + local: *; + }; +} diff --git a/arch/parisc/kernel/vdso64/vdso64_wrapper.S b/arch/parisc/kernel/vdso64/vdso64_wrapper.S new file mode 100644 index 000000000..66f929482 --- /dev/null +++ b/arch/parisc/kernel/vdso64/vdso64_wrapper.S @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/linkage.h> +#include <asm/page.h> + + __PAGE_ALIGNED_DATA + + .globl vdso64_start, vdso64_end + .balign PAGE_SIZE +vdso64_start: + .incbin "arch/parisc/kernel/vdso64/vdso64.so" + .balign PAGE_SIZE +vdso64_end: + + .previous diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S new file mode 100644 index 000000000..ad3a3239e --- /dev/null +++ b/arch/parisc/kernel/vmlinux.lds.S @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Kernel link layout for various "sections" + * + * Copyright (C) 1999-2003 Matthew Wilcox <willy at parisc-linux.org> + * Copyright (C) 2000-2003 Paul Bame <bame at parisc-linux.org> + * Copyright (C) 2000 John Marvin <jsm at parisc-linux.org> + * 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> + * 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). + */ +#define BSS_FIRST_SECTIONS *(.data..vm0.pmd) \ + *(.data..vm0.pgd) \ + *(.data..vm0.pte) + +#define CC_USING_PATCHABLE_FUNCTION_ENTRY +#define RO_EXCEPTION_TABLE_ALIGN 8 + +#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 +OUTPUT_FORMAT("elf32-hppa-linux") +OUTPUT_ARCH(hppa) +#else +OUTPUT_FORMAT("elf64-hppa-linux") +OUTPUT_ARCH(hppa:hppa2.0w) +#endif + +#define EXIT_TEXT_SECTIONS() .exit.text : { EXIT_TEXT } +#if !defined(CONFIG_64BIT) || defined(CONFIG_MLONGCALLS) +#define MLONGCALL_KEEP(x) +#define MLONGCALL_DISCARD(x) x +#else +#define MLONGCALL_KEEP(x) x +#define MLONGCALL_DISCARD(x) +#endif + +ENTRY(parisc_kernel_start) +#ifndef CONFIG_64BIT +jiffies = jiffies_64 + 4; +#else +jiffies = jiffies_64; +#endif +SECTIONS +{ + . = KERNEL_BINARY_TEXT_START; + + __init_begin = .; + HEAD_TEXT_SECTION + MLONGCALL_DISCARD(INIT_TEXT_SECTION(8)) + + . = ALIGN(PAGE_SIZE); + INIT_DATA_SECTION(PAGE_SIZE) + MLONGCALL_DISCARD(EXIT_TEXT_SECTIONS()) + .exit.data : + { + EXIT_DATA + } + PERCPU_SECTION(8) + . = ALIGN(4); + .altinstructions : { + __alt_instructions = .; + *(.altinstructions) + __alt_instructions_end = .; + } + . = ALIGN(HUGEPAGE_SIZE); + __init_end = .; + /* freed after init ends here */ + + _text = .; /* Text and read-only data */ + _stext = .; + MLONGCALL_KEEP(INIT_TEXT_SECTION(8)) + .text ALIGN(PAGE_SIZE) : { + TEXT_TEXT + LOCK_TEXT + SCHED_TEXT + CPUIDLE_TEXT + KPROBES_TEXT + IRQENTRY_TEXT + SOFTIRQENTRY_TEXT + *(.text.do_softirq) + *(.text.sys_exit) + *(.text.do_sigaltstack) + *(.text.do_fork) + *(.text.div) + *($$*) /* millicode routines */ + *(.text.*) + *(.fixup) + *(.lock.text) /* out-of-line lock text */ + *(.gnu.warning) + } + MLONGCALL_KEEP(EXIT_TEXT_SECTIONS()) + . = ALIGN(PAGE_SIZE); + _etext = .; + /* End of text section */ + + /* Start of data section */ + _sdata = .; + + /* Architecturally we need to keep __gp below 0x1000000 and thus + * in front of RO_DATA() which stores lots of tracepoint + * and ftrace symbols. */ +#ifdef CONFIG_64BIT + . = ALIGN(16); + /* Linkage tables */ + .opd : { + __start_opd = .; + *(.opd) + __end_opd = .; + } PROVIDE (__gp = .); + .plt : { + *(.plt) + } + .dlt : { + *(.dlt) + } +#endif + + RO_DATA(8) + + /* unwind info */ + . = ALIGN(4); + .PARISC.unwind : { + __start___unwind = .; + *(.PARISC.unwind) + __stop___unwind = .; + } + + /* writeable */ + /* Make sure this is page aligned so + * that we can properly leave these + * as writable + */ + . = ALIGN(HUGEPAGE_SIZE); + data_start = .; + + /* Data */ + RW_DATA(L1_CACHE_BYTES, PAGE_SIZE, PAGE_SIZE) + + /* PA-RISC locks requires 16-byte alignment */ + . = ALIGN(16); + .data..lock_aligned : { + *(.data..lock_aligned) + } + + /* End of data section */ + _edata = .; + + /* BSS */ + BSS_SECTION(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE) + + . = ALIGN(HUGEPAGE_SIZE); + _end = . ; + + STABS_DEBUG + ELF_DETAILS + .note 0 : { *(.note) } + + /* 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) + *(.gnu.hash) +#endif + } +} |