diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-30 09:22:00 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-30 09:22:00 -0700 |
| commit | 034dd340b08be1f2f0477ad16131d609f9dbd53c (patch) | |
| tree | 2536f4b2d7893ccd916b5abdf3c454ad6edc03c2 /arch/parisc | |
| download | linux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.tar.gz linux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.zip | |
Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt:
- Fix error output of boot instance creation failure
Currently if a boot instance creation fails, instead of printing out
the name of the instance that failed, it prints "(null)". That is
because it prints "cur_str" that had already been processed by
strsep(). Print the saved name instead.
While at it, print the error code of the failure.
- Fix use-after-free for same named historgrams
Histograms can be named so that they can be used in multiple events.
But if the named histogram has a variable attached, the second event
that uses the named histogram which duplicates it and needs to free
the original after duplication leaves the old variable in place and
still visible. If another histogram uses than variable, it will use
the stale one which will try to reference the freed duplicate
histogram and crash the kernel.
Free the duplicate variables along with the duplicated histogram
data.
- Check return value of kthread_run() in event self test
The events self tests uses a kthread for testing but does not check
if it succeeded in creating a kthread. If the kthread creation were
to fail, the code will still try to call kthread_stop() on the error
returned.
- Fix race between reading trace_pipe and updating subbuffer size
If a user is reading the trace_pipe file at the same time they update
the ring buffer sub-buffer size, can cause the trace_pipe read to
read stale data. Add trace_access_lock() around updating the ring
buffer sub-buffer size.
- Fix eventfs_inode on failure path in creation of the events directory
In the creation of the "events" directory, if after allocating the
eventfs_inode a failure is detected, it calls cleanup_ei() which
calls free_ei(). The free_ei() will test if eventfs_inode being freed
has no children. It is a bug if it does. But on the failure case of
the creation of the "events" directory, the children lists have not
yet been initialized and the free will trigger a warning because
list_empty() on an uninitialized list returns false.
Move the initialization into init_ei() where it makes more sense and
makes sure that a created eventfs_inode has its lists initialized
upon creation.
- Check return value of kthread_run() in ftrace direct sample code
The sample code that shows how to use the ftrace direct calls does
not test the return of kthread_run() to see if it succeeds. Return a
failure if the kthread_run() doesn't succeed.
- Clear user events state on fork in case of alloc failure
On fork, the child gets a pointer to the parent's user events state.
It makes a copy of it then updates the child's pointer to it. But if
the allocation fails, the duplication function leaves the child with
a pointer to its parent's descriptor. When the child cleans up its
data, it will free the parent's descriptor while the parent is still
using it.
In the duplication function, set the child's user_event_mm to NULL
before testing if the allocation succeeded, and when it exits it will
not free the parent's descriptor.
- Fix retry exhaustion in simple ring buffer reader swap
simple_ring_buffer_swap_reader_page() starts with retry set to 8 and
post-decrements it only after a failed link replacement. On the final
attempt, a successful replacement leaves retry at zero, while a
failed replacement leaves it at -1.
But the check for success expects the retry value to be non-zero and
exits with an error on zero. This is the opposite result. Fix it.
- Fail nicely when the remote swap_reader_page() returns an error
Currently, if the swap_reader_page() of a remote buffer fails, it
triggers a WARN_ON_ONCE() and continues normally. Instead, have it
exit with an error and a pr_warn() print instead of a full WARNING.
* tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
ring-buffer: Stop remote reader update when page swap fails
tracing: Fix retry exhaustion in simple ring buffer reader swap
tracing/user_events: Clear copied tracing state before fork duplication
samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify
samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify
eventfs: Initialize ei->children and ei->list in init_ei()
tracing: Fix use-after-free in trace_pipe read on sub-buffer order change
tracing: Fix crash passing ERR_PTR to kthread_stop()
tracing: Fix use-after-free with same-name named triggers
tracing: Fix logged instance name on creation failure
Diffstat (limited to 'arch/parisc')
277 files changed, 65499 insertions, 0 deletions
diff --git a/arch/parisc/Kbuild b/arch/parisc/Kbuild new file mode 100644 index 000000000..749b195f2 --- /dev/null +++ b/arch/parisc/Kbuild @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += mm/ kernel/ math-emu/ net/ + +# for cleaning +subdir- += boot diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig new file mode 100644 index 000000000..d3afac2f0 --- /dev/null +++ b/arch/parisc/Kconfig @@ -0,0 +1,388 @@ +# SPDX-License-Identifier: GPL-2.0 +config PARISC + def_bool y + select ALTERNATE_USER_ADDRESS_SPACE + select ARCH_32BIT_OFF_T if !64BIT + select ARCH_MIGHT_HAVE_PC_PARPORT + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER + select HAVE_SYSCALL_TRACEPOINTS + select ARCH_WANT_FRAME_POINTERS + select ARCH_WANTS_MODULES_TEXT_SECTIONS if !64BIT + select ARCH_HAS_CPU_CACHE_ALIASING + select ARCH_HAS_DMA_ALLOC if PA11 + select ARCH_HAS_DMA_OPS + select ARCH_HAS_ELF_RANDOMIZE + select ARCH_HAS_STRICT_KERNEL_RWX + select ARCH_HAS_STRICT_MODULE_RWX + select ARCH_HAS_UBSAN + select ARCH_HAS_PTE_SPECIAL + select ARCH_NO_SG_CHAIN + select ARCH_SPLIT_ARG64 if !64BIT + select ARCH_SUPPORTS_HUGETLBFS if PA20 + select ARCH_SUPPORTS_MEMORY_FAILURE + select ARCH_STACKWALK + select ARCH_HAS_CACHE_LINE_SIZE + select ARCH_HAS_DEBUG_VM_PGTABLE + select HAVE_RELIABLE_STACKTRACE + select RTC_CLASS + select RTC_DRV_GENERIC + select INIT_ALL_POSSIBLE + select BUG + select HAVE_KERNEL_UNCOMPRESSED + select HAVE_PCI + select HAVE_PERF_EVENTS + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP + select PERF_USE_VMALLOC + select HAVE_KERNEL_BZIP2 + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_LZ4 + select HAVE_KERNEL_LZMA + select HAVE_KERNEL_LZO + select HAVE_KERNEL_XZ + select GENERIC_ATOMIC64 if !64BIT + select GENERIC_IRQ_PROBE + select GENERIC_PCI_IOMAP + select GENERIC_IOREMAP + select ARCH_HAVE_NMI_SAFE_CMPXCHG + select GENERIC_SMP_IDLE_THREAD + select GENERIC_ARCH_TOPOLOGY if SMP + select ARCH_SUPPORTS_SCHED_MC if SMP && PA8X00 + select GENERIC_CPU_DEVICES if !SMP + select GENERIC_LIB_DEVMEM_IS_ALLOWED + select SYSCTL_ARCH_UNALIGN_ALLOW + select SYSCTL_ARCH_UNALIGN_NO_WARN + select SYSCTL_EXCEPTION_TRACE + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA + select CLONE_BACKWARDS + select TTY # Needed for pdc_cons.c + select HAS_IOPORT if PCI || EISA + select HAVE_DEBUG_STACKOVERFLOW + select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT + select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT + select HAVE_ARCH_MMAP_RND_BITS + select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_HASH + # select HAVE_ARCH_JUMP_LABEL + # select HAVE_ARCH_JUMP_LABEL_RELATIVE + select HAVE_ARCH_KFENCE + select HAVE_ARCH_SECCOMP_FILTER + select HAVE_ARCH_TRACEHOOK + select HAVE_EBPF_JIT + select ARCH_WANT_DEFAULT_BPF_JIT + select HAVE_REGS_AND_STACK_ACCESS_API + select HOTPLUG_CORE_SYNC_DEAD if HOTPLUG_CPU + select GENERIC_SCHED_CLOCK + select GENERIC_IRQ_MIGRATION if SMP + select HAVE_UNSTABLE_SCHED_CLOCK if SMP + select GENERIC_CLOCKEVENTS + select CPU_NO_EFFICIENT_FFS + select THREAD_INFO_IN_TASK + select MMU_GATHER_RCU_TABLE_FREE + select NEED_DMA_MAP_STATE + select NEED_SG_DMA_LENGTH + select HAVE_ARCH_KGDB + select HAVE_KPROBES + select HAVE_KRETPROBES + select HAVE_DYNAMIC_FTRACE if $(cc-option,-fpatchable-function-entry=1,1) + select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY if DYNAMIC_FTRACE + select HAVE_KPROBES_ON_FTRACE + select HAVE_DYNAMIC_FTRACE_WITH_REGS + select HAVE_SOFTIRQ_ON_OWN_STACK if IRQSTACKS + select TRACE_IRQFLAGS_SUPPORT + select HAVE_FUNCTION_DESCRIPTORS if 64BIT + select PCI_MSI_ARCH_FALLBACKS if PCI_MSI + + help + The PA-RISC microprocessor is designed by Hewlett-Packard and used + in many of their workstations & servers (HP9000 700 and 800 series, + and later HP3000 series). The PA-RISC Linux project home page is + at <https://parisc.wiki.kernel.org>. + +config CPU_BIG_ENDIAN + def_bool y + +config MMU + def_bool y + +config STACK_GROWSUP + def_bool y + +config GENERIC_LOCKBREAK + bool + default y + depends on SMP && PREEMPTION + +config ARCH_HAS_ILOG2_U32 + bool + default n + +config ARCH_HAS_ILOG2_U64 + bool + default n + +config GENERIC_BUG + def_bool y + depends on BUG + select GENERIC_BUG_RELATIVE_POINTERS if 64BIT + +config GENERIC_BUG_RELATIVE_POINTERS + bool + +config GENERIC_CSUM + def_bool y + +config GENERIC_HWEIGHT + bool + default y + +config GENERIC_CALIBRATE_DELAY + bool + default y + +config TIME_LOW_RES + bool + depends on SMP + default y + +config ARCH_MMAP_RND_BITS_MIN + default 18 if 64BIT + default 8 + +config ARCH_MMAP_RND_COMPAT_BITS_MIN + default 8 + +config ARCH_MMAP_RND_BITS_MAX + default 18 if 64BIT + default 13 + +config ARCH_MMAP_RND_COMPAT_BITS_MAX + default 13 + +# unless you want to implement ACPI on PA-RISC ... ;-) +config PM + bool + +config STACKTRACE_SUPPORT + def_bool y + +config LOCKDEP_SUPPORT + bool + default y + +config ISA_DMA_API + bool + +config ARCH_MAY_HAVE_PC_FDC + bool + depends on BROKEN + default y + +config PGTABLE_LEVELS + int + default 3 if 64BIT && PARISC_PAGE_SIZE_4KB + default 2 + +menu "Processor type and features" + +choice + prompt "Processor type" + default PA7000 if "$(ARCH)" = "parisc" + +config PA7000 + bool "PA7000/PA7100" if "$(ARCH)" = "parisc" + help + This is the processor type of your CPU. This information is + used for optimizing purposes. In order to compile a kernel + that can run on all 32-bit PA CPUs (albeit not optimally fast), + you can specify "PA7000" here. + + Specifying "PA8000" here will allow you to select a 64-bit kernel + which is required on some machines. + +config PA7100LC + bool "PA7100LC" if "$(ARCH)" = "parisc" + help + Select this option for the PCX-L processor, as used in the + 712, 715/64, 715/80, 715/100, 715/100XC, 725/100, 743, 748, + D200, D210, D300, D310 and E-class + +config PA7200 + bool "PA7200" if "$(ARCH)" = "parisc" + help + Select this option for the PCX-T' processor, as used in the + C100, C110, J100, J110, J210XC, D250, D260, D350, D360, + K100, K200, K210, K220, K400, K410 and K420 + +config PA7300LC + bool "PA7300LC" if "$(ARCH)" = "parisc" + help + Select this option for the PCX-L2 processor, as used in the + 744, A180, B132L, B160L, B180L, C132L, C160L, C180L, + D220, D230, D320 and D330. + +config PA8X00 + bool "PA8000 and up" + help + Select this option for PCX-U to PCX-W2 processors. + +endchoice + +# Define implied options from the CPU selection here + +config PA20 + def_bool y + depends on PA8X00 + +config PA11 + def_bool y + depends on PA7000 || PA7100LC || PA7200 || PA7300LC + select ARCH_HAS_SYNC_DMA_FOR_CPU + select ARCH_HAS_SYNC_DMA_FOR_DEVICE + +config PREFETCH + def_bool y + depends on PA8X00 || PA7200 + +config PARISC_HUGE_KERNEL + def_bool y if !MODULES || UBSAN || FTRACE || COMPILE_TEST + +config MLONGCALLS + bool "Enable the -mlong-calls compiler option for big kernels" if !PARISC_HUGE_KERNEL + depends on PA8X00 + default PARISC_HUGE_KERNEL + help + If you configure the kernel to include many drivers built-in instead + as modules, the kernel executable may become too big, so that the + linker will not be able to resolve some long branches and fails to link + your vmlinux kernel. In that case enabling this option will help you + to overcome this limit by using the -mlong-calls compiler option. + + Usually you want to say N here, unless you e.g. want to build + a kernel which includes all necessary drivers built-in and which can + be used for TFTP booting without the need to have an initrd ramdisk. + + Enabling this option will probably slow down your kernel. + +config 64BIT + bool "64-bit kernel" if "$(ARCH)" = "parisc" + depends on PA8X00 + default "$(ARCH)" = "parisc64" + help + Enable this if you want to support 64bit kernel on PA-RISC platform. + + At the moment, only people willing to use more than 2GB of RAM, + or having a 64bit-only capable PA-RISC machine should say Y here. + + Since there is no 64bit userland on PA-RISC, there is no point to + enable this option otherwise. The 64bit kernel is significantly bigger + and slower than the 32bit one. + +choice + prompt "Kernel page size" + default PARISC_PAGE_SIZE_4KB + +config PARISC_PAGE_SIZE_4KB + bool "4KB" + select HAVE_PAGE_SIZE_4KB + help + This lets you select the page size of the kernel. For best + performance, a page size of 16KB is recommended. For best + compatibility with 32bit applications, a page size of 4KB should be + selected (the vast majority of 32bit binaries work perfectly fine + with a larger page size). + + 4KB For best 32bit compatibility + 16KB For best performance + 64KB For best performance, might give more overhead. + + If you don't know what to do, choose 4KB. + +config PARISC_PAGE_SIZE_16KB + bool "16KB" + select HAVE_PAGE_SIZE_16KB + depends on PA8X00 && BROKEN && !KFENCE + +config PARISC_PAGE_SIZE_64KB + bool "64KB" + select HAVE_PAGE_SIZE_64KB + depends on PA8X00 && BROKEN && !KFENCE + +endchoice + +config SMP + bool "Symmetric multi-processing support" + help + This enables support for systems with more than one CPU. If you have + a system with only one CPU, say N. If you have a system with more + than one CPU, say Y. + + If you say N here, the kernel will run on uni- and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. + On a uniprocessor machine, the kernel will run faster if you say N. + + See also <file:Documentation/admin-guide/lockup-watchdogs.rst> and the SMP-HOWTO + available at <https://www.tldp.org/docs.html#howto>. + + If you don't know what to do here, say N. + +config IRQSTACKS + bool "Use separate kernel stacks when processing interrupts" + default y + help + If you say Y here the kernel will use separate kernel stacks + for handling hard and soft interrupts. This can help avoid + overflowing the process kernel stacks. + +config HOTPLUG_CPU + bool + default y if SMP + +config ARCH_SELECT_MEMORY_MODEL + def_bool y + depends on 64BIT + +config ARCH_SPARSEMEM_ENABLE + def_bool y + depends on 64BIT + +config ARCH_FLATMEM_ENABLE + def_bool y + +config ARCH_SPARSEMEM_DEFAULT + def_bool y + depends on ARCH_SPARSEMEM_ENABLE + +source "kernel/Kconfig.hz" + +config COMPAT + bool "Kernel support for 32-bit binaries" + default 64BIT + depends on 64BIT + +config AUDIT_ARCH + def_bool y + +config NR_CPUS + int "Maximum number of CPUs (2-32)" + range 2 32 + depends on SMP + default "8" if 64BIT + default "16" + +endmenu + +config ARCH_SUPPORTS_KEXEC + def_bool y + +config ARCH_SUPPORTS_KEXEC_FILE + def_bool y + +config ARCH_SELECTS_KEXEC_FILE + def_bool y + depends on KEXEC_FILE + select KEXEC_ELF + +source "drivers/parisc/Kconfig" diff --git a/arch/parisc/Kconfig.debug b/arch/parisc/Kconfig.debug new file mode 100644 index 000000000..f4f164eb1 --- /dev/null +++ b/arch/parisc/Kconfig.debug @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# +config LIGHTWEIGHT_SPINLOCK_CHECK + bool "Enable lightweight spinlock checks" + depends on DEBUG_KERNEL && SMP && !DEBUG_SPINLOCK + default y + help + Add checks with low performance impact to the spinlock functions + to catch memory overwrites at runtime. For more advanced + spinlock debugging you should choose the DEBUG_SPINLOCK option + which will detect unitialized spinlocks too. + If unsure say Y here. + +config TLB_PTLOCK + bool "Use page table locks in TLB fault handler" + depends on DEBUG_KERNEL && SMP + default n + help + Select this option to enable page table locking in the TLB + fault handler. This ensures that page table entries are + updated consistently on SMP machines at the expense of some + loss in performance. + diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile new file mode 100644 index 000000000..439178352 --- /dev/null +++ b/arch/parisc/Makefile @@ -0,0 +1,213 @@ +# +# parisc/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. +# +# 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) 1994 by Linus Torvalds +# Portions Copyright (C) 1999 The Puffin Group +# +# Modified for PA-RISC Linux by Paul Lahaie, Alex deVries, +# Mike Shaver, Helge Deller and Martin K. Petersen +# + +boot := arch/parisc/boot +KBUILD_IMAGE := $(boot)/bzImage + +CHECKFLAGS += -D__hppa__=1 + +ifdef CONFIG_64BIT +UTS_MACHINE := parisc64 +CHECKFLAGS += -D__LP64__=1 +LD_BFD := elf64-hppa-linux +else # 32-bit +LD_BFD := elf32-hppa-linux +endif + +# select defconfig based on actual architecture +ifeq ($(ARCH),parisc64) + KBUILD_DEFCONFIG := generic-64bit_defconfig + CC_ARCHES := hppa64 +else + KBUILD_DEFCONFIG := generic-32bit_defconfig + CC_ARCHES := hppa hppa2.0 hppa1.1 +endif + +export LD_BFD + +# Set default 32 bits cross compilers for vdso. +# This means that for 64BIT, both the 64-bit tools and the 32-bit tools +# need to be in the path. +CC_ARCHES_32 = hppa hppa2.0 hppa1.1 +CC_SUFFIXES = linux linux-gnu unknown-linux-gnu suse-linux +CROSS32_COMPILE := $(call cc-cross-prefix, \ + $(foreach a,$(CC_ARCHES_32), \ + $(foreach s,$(CC_SUFFIXES),$(a)-$(s)-))) +CROSS32CC := $(CROSS32_COMPILE)gcc +export CROSS32CC + +# Set default cross compiler for kernel build +ifdef cross_compiling + ifeq ($(CROSS_COMPILE),) + CC_SUFFIXES = linux linux-gnu unknown-linux-gnu suse-linux + CROSS_COMPILE := $(call cc-cross-prefix, \ + $(foreach a,$(CC_ARCHES), \ + $(foreach s,$(CC_SUFFIXES),$(a)-$(s)-))) + endif +endif + +ifdef CONFIG_DYNAMIC_FTRACE +ifdef CONFIG_64BIT +NOP_COUNT := 8 +else +NOP_COUNT := 5 +endif + +export CC_USING_RECORD_MCOUNT:=1 +export CC_USING_PATCHABLE_FUNCTION_ENTRY:=1 + +KBUILD_AFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY=1 +KBUILD_CFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY=1 \ + -DFTRACE_PATCHABLE_FUNCTION_SIZE=$(NOP_COUNT) + +CC_FLAGS_FTRACE := -fpatchable-function-entry=$(NOP_COUNT),$(shell echo $$(($(NOP_COUNT)-1))) +endif + +OBJCOPY_FLAGS =-O binary -R .note -R .comment -S + +cflags-y := -pipe + +# These flags should be implied by an hppa-linux configuration, but they +# are not in gcc 3.2. +cflags-y += -mno-space-regs + +# -mfast-indirect-calls is only relevant for 32-bit kernels. +ifndef CONFIG_64BIT +cflags-y += -mfast-indirect-calls +endif + +# Currently we save and restore fpregs on all kernel entry/interruption paths. +# If that gets optimized, we might need to disable the use of fpregs in the +# kernel. +cflags-y += -mdisable-fpregs + +# Use long jumps instead of long branches (needed if your linker fails to +# link a too big vmlinux executable). Not enabled for building modules. +ifdef CONFIG_MLONGCALLS +KBUILD_CFLAGS_KERNEL += -mlong-calls +endif + +# Without this, "ld -r" results in .text sections that are too big (> 0x40000) +# for branches to reach stubs. And multiple .text sections trigger a warning +# when creating the sysfs module information section. +ifndef CONFIG_64BIT +KBUILD_CFLAGS_MODULE += -ffunction-sections +endif + +# select which processor to optimise for +cflags-$(CONFIG_PA7000) += -march=1.1 -mschedule=7100 +cflags-$(CONFIG_PA7200) += -march=1.1 -mschedule=7200 +cflags-$(CONFIG_PA7100LC) += -march=1.1 -mschedule=7100LC +cflags-$(CONFIG_PA7300LC) += -march=1.1 -mschedule=7300 +cflags-$(CONFIG_PA8X00) += -march=2.0 -mschedule=8000 + +KBUILD_CFLAGS += $(cflags-y) +LIBGCC := $(shell $(CC) -print-libgcc-file-name) +export LIBGCC + +libs-y += arch/parisc/lib/ $(LIBGCC) + +drivers-$(CONFIG_VIDEO) += arch/parisc/video/ + +boot := arch/parisc/boot + +PALO := $(shell if (which palo 2>&1); then : ; \ + elif [ -x /sbin/palo ]; then echo /sbin/palo; \ + fi) + +PALOCONF := $(shell if [ -f $(srctree)/palo.conf ]; then echo $(srctree)/palo.conf; \ + else echo $(objtree)/palo.conf; \ + fi) + +palo lifimage: vmlinuz + @if test ! -x "$(PALO)"; then \ + echo 'ERROR: Please install palo first (apt-get install palo)';\ + echo 'or build it from source and install it somewhere in your $$PATH';\ + false; \ + fi + @if test ! -f "$(PALOCONF)"; then \ + cp $(srctree)/arch/parisc/defpalo.conf $(objtree)/palo.conf; \ + echo 'A generic palo config file ($(objtree)/palo.conf) has been created for you.'; \ + echo 'You should check it and re-run "make palo".'; \ + echo 'WARNING: the "lifimage" file is now placed in this directory by default!'; \ + false; \ + fi + $(PALO) -f $(PALOCONF) + +BOOT_TARGETS = zImage Image palo lifimage +INSTALL_TARGETS = zinstall install + +PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS) + +# Default kernel to build +all: bzImage + +zImage: vmlinuz +Image: vmlinux + +bzImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +vmlinuz: bzImage + $(OBJCOPY) $(boot)/bzImage $@ + +ifeq ($(KBUILD_EXTMOD),) +# We need to generate vdso-offsets.h before compiling certain files in kernel/. +# In order to do that, we should use the archprepare target, but we can't since +# asm-offsets.h is included in some files used to generate vdso-offsets.h, and +# asm-offsets.h is built in prepare0, for which archprepare is a dependency. +# Therefore we need to generate the header after prepare0 has been made, hence +# this hack. +prepare: vdso_prepare +vdso_prepare: prepare0 +ifdef CONFIG_64BIT + $(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso64 include/generated/vdso64-offsets.h + $(if $(CONFIG_COMPAT),$(Q)$(MAKE) \ + $(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h) +else + $(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h +endif +endif + +ifdef CONFIG_64BIT +vdso-install-y += arch/parisc/kernel/vdso64/vdso64.so +vdso-install-$(CONFIG_COMPAT) += arch/parisc/kernel/vdso32/vdso32.so +else +vdso-install-y += arch/parisc/kernel/vdso32/vdso32.so +endif + +install: KBUILD_IMAGE := vmlinux +zinstall: KBUILD_IMAGE := vmlinuz +install zinstall: + $(call cmd,install) + +CLEAN_FILES += lifimage +MRPROPER_FILES += palo.conf + +define archhelp + @echo '* vmlinux - Uncompressed kernel image (./vmlinux)' + @echo ' vmlinuz - Compressed kernel image (./vmlinuz)' + @echo ' palo - Bootable image (./lifimage)' + @echo ' install - Install uncompressed vmlinux kernel using' + @echo ' (your) ~/bin/$(INSTALLKERNEL) or' + @echo ' (distribution) /sbin/$(INSTALLKERNEL) or' + @echo ' copy to $$(INSTALL_PATH)' + @echo ' zinstall - Install compressed vmlinuz kernel' +endef + +archheaders: + $(Q)$(MAKE) $(build)=arch/parisc/kernel/syscalls all diff --git a/arch/parisc/boot/.gitignore b/arch/parisc/boot/.gitignore new file mode 100644 index 000000000..adf2ae0e7 --- /dev/null +++ b/arch/parisc/boot/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +image +bzImage diff --git a/arch/parisc/boot/Makefile b/arch/parisc/boot/Makefile new file mode 100644 index 000000000..657f96724 --- /dev/null +++ b/arch/parisc/boot/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the linux parisc-specific parts of the boot image creator. +# + +targets := image +targets += bzImage +subdir- := compressed + +$(obj)/image: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/bzImage: $(if $(CONFIG_KERNEL_UNCOMPRESSED),$(objtree)/vmlinux,$(obj)/compressed/vmlinux) FORCE + $(call if_changed,objcopy) + +$(obj)/compressed/vmlinux: FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ diff --git a/arch/parisc/boot/compressed/.gitignore b/arch/parisc/boot/compressed/.gitignore new file mode 100644 index 000000000..a5839aa16 --- /dev/null +++ b/arch/parisc/boot/compressed/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +sizes.h +vmlinux +vmlinux.lds diff --git a/arch/parisc/boot/compressed/Makefile b/arch/parisc/boot/compressed/Makefile new file mode 100644 index 000000000..14eefb5ed --- /dev/null +++ b/arch/parisc/boot/compressed/Makefile @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# linux/arch/parisc/boot/compressed/Makefile +# +# create a compressed self-extracting vmlinux image from the original vmlinux +# + +OBJECTS := head.o real2.o firmware.o misc.o piggy.o +targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 +targets += vmlinux.bin.xz vmlinux.bin.lzma vmlinux.bin.lzo vmlinux.bin.lz4 +targets += $(OBJECTS) sizes.h + +KBUILD_CFLAGS := -D__KERNEL__ -O2 -DBOOTLOADER +KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING +KBUILD_CFLAGS += -fno-strict-aliasing +KBUILD_CFLAGS += $(cflags-y) -fno-delete-null-pointer-checks -fno-builtin-printf +KBUILD_CFLAGS += -fno-PIE -mno-space-regs -mdisable-fpregs -Os +ifndef CONFIG_64BIT +KBUILD_CFLAGS += -mfast-indirect-calls +endif +KBUILD_CFLAGS += $(CC_FLAGS_DIALECT) + +LDFLAGS_vmlinux := -X -e startup --as-needed -T +$(obj)/vmlinux: $(obj)/vmlinux.lds $(addprefix $(obj)/, $(OBJECTS)) $(LIBGCC) FORCE + $(call if_changed,ld) + +sed-sizes := -e 's/^\([0-9a-fA-F]*\) . \(__bss_start\|_end\|parisc_kernel_start\)$$/\#define SZ\2 0x\1/p' + +quiet_cmd_sizes = GEN $@ + cmd_sizes = $(NM) $< | sed -n $(sed-sizes) > $@ + +$(obj)/sizes.h: vmlinux FORCE + $(call if_changed,sizes) + +AFLAGS_head.o += -I$(objtree)/$(obj) -DBOOTLOADER +$(obj)/head.o: $(obj)/sizes.h + +CFLAGS_misc.o += -I$(objtree)/$(obj) +$(obj)/misc.o: $(obj)/sizes.h + +AFLAGS_real2.o += -DBOOTLOADER + +CPPFLAGS_vmlinux.lds += -I$(objtree)/$(obj) -DBOOTLOADER +$(obj)/vmlinux.lds: $(obj)/sizes.h + +OBJCOPYFLAGS_vmlinux.bin := -R .comment -R .note -S +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +suffix-$(CONFIG_KERNEL_GZIP) := gz +suffix-$(CONFIG_KERNEL_BZIP2) := bz2 +suffix-$(CONFIG_KERNEL_LZ4) := lz4 +suffix-$(CONFIG_KERNEL_LZMA) := lzma +suffix-$(CONFIG_KERNEL_LZO) := lzo +suffix-$(CONFIG_KERNEL_XZ) := xz + +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) +$(obj)/vmlinux.bin.bz2: $(obj)/vmlinux.bin FORCE + $(call if_changed,bzip2_with_size) +$(obj)/vmlinux.bin.lz4: $(obj)/vmlinux.bin FORCE + $(call if_changed,lz4_with_size) +$(obj)/vmlinux.bin.lzma: $(obj)/vmlinux.bin FORCE + $(call if_changed,lzma_with_size) +$(obj)/vmlinux.bin.lzo: $(obj)/vmlinux.bin FORCE + $(call if_changed,lzo_with_size) +$(obj)/vmlinux.bin.xz: $(obj)/vmlinux.bin FORCE + $(call if_changed,xzkern_with_size) + +LDFLAGS_piggy.o := -r --format binary --oformat $(LD_BFD) -T +$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.$(suffix-y) FORCE + $(call if_changed,ld) diff --git a/arch/parisc/boot/compressed/firmware.c b/arch/parisc/boot/compressed/firmware.c new file mode 100644 index 000000000..16a07137f --- /dev/null +++ b/arch/parisc/boot/compressed/firmware.c @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "../../kernel/firmware.c" diff --git a/arch/parisc/boot/compressed/head.S b/arch/parisc/boot/compressed/head.S new file mode 100644 index 000000000..e8b798fd0 --- /dev/null +++ b/arch/parisc/boot/compressed/head.S @@ -0,0 +1,85 @@ +/* + * Startup glue code to uncompress the kernel + * + * (C) 2017 Helge Deller <deller@gmx.de> + */ + +#include <linux/init.h> +#include <linux/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/page.h> +#include <asm/psw.h> +#include <asm/pdc.h> +#include <asm/assembly.h> +#include "sizes.h" + +#define BOOTADDR(x) (x) + +#ifndef CONFIG_64BIT + .import $global$ /* forward declaration */ +#endif /*!CONFIG_64BIT*/ + + __HEAD + +ENTRY(startup) + .level PA_ASM_LEVEL + +#define PSW_W_SM 0x200 +#define PSW_W_BIT 36 + + ;! nuke the W bit, saving original value + .level 2.0 + rsm PSW_W_SM, %r1 + + .level 1.1 + extrw,u %r1, PSW_W_BIT-32, 1, %r1 + copy %r1, %arg0 + + /* 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 */ + + .import _bss,data + .import _ebss,data + + load32 BOOTADDR(_bss),%r3 + load32 BOOTADDR(_ebss),%r4 + ldo FRAME_SIZE(%r4),%sp /* stack at end of bss */ +$bss_loop: + cmpb,<<,n %r3,%r4,$bss_loop + stw,ma %r0,4(%r3) + + /* Initialize the global data pointer */ + loadgp + + /* arg0..arg4 were set by palo. */ + copy %arg1, %r6 /* command line */ + copy %arg2, %r7 /* rd-start */ + copy %arg3, %r8 /* rd-end */ + load32 BOOTADDR(decompress_kernel),%r3 + +#ifdef CONFIG_64BIT + .level PA_ASM_LEVEL + ssm PSW_W_SM, %r0 /* set W-bit */ + depdi 0, 31, 32, %r3 +#endif + load32 BOOTADDR(startup_continue), %r2 + bv,n 0(%r3) + +startup_continue: +#ifdef CONFIG_64BIT + .level PA_ASM_LEVEL + rsm PSW_W_SM, %r0 /* clear W-bit */ +#endif + + load32 KERNEL_BINARY_TEXT_START, %arg0 /* free mem */ + copy %r6, %arg1 /* command line */ + copy %r7, %arg2 /* rd-start */ + copy %r8, %arg3 /* rd-end */ + + bv,n 0(%ret0) +END(startup) diff --git a/arch/parisc/boot/compressed/misc.c b/arch/parisc/boot/compressed/misc.c new file mode 100644 index 000000000..111f26723 --- /dev/null +++ b/arch/parisc/boot/compressed/misc.c @@ -0,0 +1,383 @@ +/* + * Definitions and wrapper functions for kernel decompressor + * + * (C) 2017 Helge Deller <deller@gmx.de> + */ + +#include <linux/uaccess.h> +#include <linux/elf.h> +#include <linux/unaligned.h> +#include <asm/page.h> +#include "sizes.h" + +/* + * gzip declarations + */ +#define STATIC static + +#undef memmove +#define memmove memmove +#define memzero(s, n) memset((s), 0, (n)) + +#define malloc malloc_gzip +#define free free_gzip + +/* Symbols defined by linker scripts */ +extern char input_data[]; +extern int input_len; +/* output_len is inserted by the linker possibly at an unaligned address */ +extern char output_len; +extern char _text, _end; +extern char _bss, _ebss; +extern char _startcode_end; +extern void startup_continue(void *entry, unsigned long cmdline, + unsigned long rd_start, unsigned long rd_end) __noreturn; + +void error(char *m) __noreturn; + +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; + +#ifdef CONFIG_KERNEL_GZIP +#include "../../../../lib/decompress_inflate.c" +#endif + +#ifdef CONFIG_KERNEL_BZIP2 +#include "../../../../lib/decompress_bunzip2.c" +#endif + +#ifdef CONFIG_KERNEL_LZ4 +#include "../../../../lib/decompress_unlz4.c" +#endif + +#ifdef CONFIG_KERNEL_LZMA +#include "../../../../lib/decompress_unlzma.c" +#endif + +#ifdef CONFIG_KERNEL_LZO +#include "../../../../lib/decompress_unlzo.c" +#endif + +#ifdef CONFIG_KERNEL_XZ +#include "../../../../lib/decompress_unxz.c" +#endif + +void *memmove(void *dest, const void *src, size_t n) +{ + const char *s = src; + char *d = dest; + + if (d <= s) { + while (n--) + *d++ = *s++; + } else { + d += n; + s += n; + while (n--) + *--d = *--s; + } + return dest; +} + +void *memset(void *s, int c, size_t count) +{ + char *xs = (char *)s; + + while (count--) + *xs++ = c; + return s; +} + +void *memcpy(void *d, const void *s, size_t len) +{ + char *dest = (char *)d; + const char *source = (const char *)s; + + while (len--) + *dest++ = *source++; + return d; +} + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + ; + return sc - s; +} + +char *strchr(const char *s, int c) +{ + while (*s) { + if (*s == (char)c) + return (char *)s; + ++s; + } + return NULL; +} + +static int puts(const char *s) +{ + const char *nuline = s; + + while ((nuline = strchr(s, '\n')) != NULL) { + if (nuline != s) + pdc_iodc_print(s, nuline - s); + pdc_iodc_print("\r\n", 2); + s = nuline + 1; + } + if (*s != '\0') + pdc_iodc_print(s, strlen(s)); + + return 0; +} + +static int putchar(int c) +{ + char buf[2]; + + buf[0] = c; + buf[1] = '\0'; + puts(buf); + return c; +} + +void __noreturn error(char *x) +{ + if (x) puts(x); + puts("\n -- System halted\n"); + while (1) /* wait forever */ + ; +} + +static int print_num(unsigned long num, int base) +{ + const char hex[] = "0123456789abcdef"; + char str[40]; + int i = sizeof(str)-1; + + str[i--] = '\0'; + do { + str[i--] = hex[num % base]; + num = num / base; + } while (num); + + if (base == 16) { + str[i--] = 'x'; + str[i] = '0'; + } else i++; + puts(&str[i]); + + return 0; +} + +static int printf(const char *fmt, ...) +{ + va_list args; + int i = 0; + + va_start(args, fmt); + + while (fmt[i]) { + if (fmt[i] != '%') { +put: + putchar(fmt[i++]); + continue; + } + + if (fmt[++i] == '%') + goto put; + print_num(va_arg(args, unsigned long), + fmt[i] == 'x' ? 16:10); + ++i; + } + + va_end(args); + return 0; +} + +/* helper functions for libgcc */ +void abort(void) +{ + error("aborted."); +} + +#undef malloc +static void *malloc(size_t size) +{ + return malloc_gzip(size); +} + +#undef free +static void free(void *ptr) +{ + return free_gzip(ptr); +} + + +static void flush_data_cache(char *start, unsigned long length) +{ + char *end = start + length; + + do { + asm volatile("fdc 0(%0)" : : "r" (start)); + asm volatile("fic 0(%%sr0,%0)" : : "r" (start)); + start += 16; + } while (start < end); + asm volatile("fdc 0(%0)" : : "r" (end)); + + asm ("sync"); +} + +static void parse_elf(void *output) +{ +#ifdef CONFIG_64BIT + Elf64_Ehdr ehdr; + Elf64_Phdr *phdrs, *phdr; +#else + Elf32_Ehdr ehdr; + Elf32_Phdr *phdrs, *phdr; +#endif + void *dest; + int i; + + memcpy(&ehdr, output, sizeof(ehdr)); + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || + ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || + ehdr.e_ident[EI_MAG3] != ELFMAG3) { + error("Kernel is not a valid ELF file"); + return; + } + +#ifdef DEBUG + printf("Parsing ELF... "); +#endif + + phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum); + if (!phdrs) + error("Failed to allocate space for phdrs"); + + memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum); + + for (i = 0; i < ehdr.e_phnum; i++) { + phdr = &phdrs[i]; + + switch (phdr->p_type) { + case PT_LOAD: + dest = (void *)((unsigned long) phdr->p_paddr & + (__PAGE_OFFSET_DEFAULT-1)); + memmove(dest, output + phdr->p_offset, phdr->p_filesz); + break; + default: + break; + } + } + + free(phdrs); +} + +/* + * The regular get_unaligned_le32 uses __builtin_memcpy which can trigger + * warnings when reading a byte/char output_len as an integer, as the size of a + * char is less than that of an integer. Use type punning and the packed + * attribute, which requires -fno-strict-aliasing, to work around the problem. + */ +static u32 punned_get_unaligned_le32(const void *p) +{ + const struct { __le32 x; } __packed * __get_pptr = p; + + return le32_to_cpu(__get_pptr->x); +} + +asmlinkage unsigned long __visible decompress_kernel(unsigned int started_wide, + unsigned int command_line, + const unsigned int rd_start, + const unsigned int rd_end) +{ + char *output; + unsigned long vmlinux_addr, vmlinux_len; + unsigned long kernel_addr, kernel_len; + +#ifdef CONFIG_64BIT + parisc_narrow_firmware = 0; +#endif + + set_firmware_width_unlocked(); + + putchar('D'); /* if you get this D and no more, string storage */ + /* in $GLOBAL$ is wrong or %dp is wrong */ + puts("ecompressing Linux... "); + + /* where the final bits are stored */ + kernel_addr = KERNEL_BINARY_TEXT_START; + kernel_len = __pa(SZ_end) - __pa(SZparisc_kernel_start); + if ((unsigned long) &_startcode_end > kernel_addr) + error("Bootcode overlaps kernel code"); + + /* + * Calculate addr to where the vmlinux ELF file shall be decompressed. + * Assembly code in head.S positioned the stack directly behind bss, so + * leave 2 MB for the stack. + */ + vmlinux_addr = (unsigned long) &_ebss + 2*1024*1024; + vmlinux_len = punned_get_unaligned_le32(&output_len); + output = (char *) vmlinux_addr; + + /* + * Initialize free_mem_ptr and free_mem_end_ptr. + */ + free_mem_ptr = vmlinux_addr + vmlinux_len; + + /* Limit memory for bootoader to 1GB */ + #define ARTIFICIAL_LIMIT (1*1024*1024*1024) + free_mem_end_ptr = PAGE0->imm_max_mem; + if (free_mem_end_ptr > ARTIFICIAL_LIMIT) + free_mem_end_ptr = ARTIFICIAL_LIMIT; + +#ifdef CONFIG_BLK_DEV_INITRD + /* if we have ramdisk this is at end of memory */ + if (rd_start && rd_start < free_mem_end_ptr) + free_mem_end_ptr = rd_start; +#endif + + if (free_mem_ptr >= free_mem_end_ptr) { + int free_ram; + free_ram = (free_mem_ptr >> 20) + 1; + if (free_ram < 32) + free_ram = 32; + printf("\nKernel requires at least %d MB RAM.\n", + free_ram); + error(NULL); + } + +#ifdef DEBUG + printf("\n"); + printf("startcode_end = %x\n", &_startcode_end); + printf("commandline = %x\n", command_line); + printf("rd_start = %x\n", rd_start); + printf("rd_end = %x\n", rd_end); + + printf("free_ptr = %x\n", free_mem_ptr); + printf("free_ptr_end = %x\n", free_mem_end_ptr); + + printf("input_data = %x\n", input_data); + printf("input_len = %x\n", input_len); + printf("output = %x\n", output); + printf("output_len = %x\n", vmlinux_len); + printf("kernel_addr = %x\n", kernel_addr); + printf("kernel_len = %x\n", kernel_len); +#endif + + __decompress(input_data, input_len, NULL, NULL, + output, 0, NULL, error); + parse_elf(output); + + output = (char *) kernel_addr; + flush_data_cache(output, kernel_len); + + printf("done.\nBooting the kernel.\n"); + + return (unsigned long) output; +} diff --git a/arch/parisc/boot/compressed/real2.S b/arch/parisc/boot/compressed/real2.S new file mode 100644 index 000000000..cdc6a4da3 --- /dev/null +++ b/arch/parisc/boot/compressed/real2.S @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "../../kernel/real2.S" diff --git a/arch/parisc/boot/compressed/vmlinux.lds.S b/arch/parisc/boot/compressed/vmlinux.lds.S new file mode 100644 index 000000000..87d24cc82 --- /dev/null +++ b/arch/parisc/boot/compressed/vmlinux.lds.S @@ -0,0 +1,107 @@ +#include <asm-generic/vmlinux.lds.h> +#include <asm/page.h> +#include "sizes.h" + +#ifndef CONFIG_64BIT +OUTPUT_FORMAT("elf32-hppa-linux") +OUTPUT_ARCH(hppa) +#else +OUTPUT_FORMAT("elf64-hppa-linux") +OUTPUT_ARCH(hppa:hppa2.0w) +#endif + +ENTRY(startup) + +SECTIONS +{ + /* palo loads at 0x60000 */ + /* loaded kernel will move to 0x10000 */ + . = 0xe0000; /* should not overwrite palo code */ + + .head.text : { + _head = . ; + HEAD_TEXT + _ehead = . ; + } + + /* keep __gp below 0x1000000 */ +#ifdef CONFIG_64BIT + . = ALIGN(16); + /* Linkage tables */ + .opd : { + __start_opd = .; + *(.opd) + __end_opd = .; + } PROVIDE (__gp = .); + .plt : { + *(.plt) + } + .dlt : { + *(.dlt) + } +#endif + _startcode_end = .; + + /* vmlinux.bin.gz is here */ + . = ALIGN(8); + .rodata.compressed : { + *(.rodata.compressed) + } + + /* bootloader code and data starts at least behind area of extracted kernel */ + . = MAX(ABSOLUTE(.), (SZ_end - SZparisc_kernel_start + KERNEL_BINARY_TEXT_START)); + + /* align on next page boundary */ + . = ALIGN(4096); + .text : { + _text = .; /* Text */ + *(.text) + *(.text.*) + _etext = . ; + } + . = ALIGN(8); + .data : { + _data = . ; + *(.data) + *(.data.*) + _edata = . ; + } + . = ALIGN(8); + .rodata : { + _rodata = . ; + *(.rodata) /* read-only data */ + *(.rodata.*) + _erodata = . ; + } + . = ALIGN(8); + .bss : { + _bss = . ; + *(.bss) + *(.bss.*) + *(COMMON) + . = ALIGN(4096); + _ebss = .; + } + + STABS_DEBUG + ELF_DETAILS + .note 0 : { *(.note) } + + /* Sections to be discarded */ + DISCARDS + /DISCARD/ : { + *(.modinfo) +#ifdef CONFIG_64BIT + /* temporary hack until binutils is fixed to not emit these + * for static binaries + */ + *(.PARISC.unwind) /* no unwind data */ + *(.interp) + *(.dynsym) + *(.dynstr) + *(.dynamic) + *(.hash) + *(.gnu.hash) +#endif + } +} diff --git a/arch/parisc/boot/compressed/vmlinux.scr b/arch/parisc/boot/compressed/vmlinux.scr new file mode 100644 index 000000000..dac2d142b --- /dev/null +++ b/arch/parisc/boot/compressed/vmlinux.scr @@ -0,0 +1,10 @@ +SECTIONS +{ + .rodata.compressed : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + output_len = . - 4; /* can be at unaligned address */ + input_data_end = .; + } +} diff --git a/arch/parisc/configs/generic-32bit_defconfig b/arch/parisc/configs/generic-32bit_defconfig new file mode 100644 index 000000000..3572a32f7 --- /dev/null +++ b/arch/parisc/configs/generic-32bit_defconfig @@ -0,0 +1,276 @@ +CONFIG_LOCALVERSION="-32bit" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_CGROUPS=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +CONFIG_PA7100LC=y +CONFIG_SMP=y +CONFIG_HZ_100=y +CONFIG_IOMMU_CCIO=y +CONFIG_GSC_LASI=y +CONFIG_GSC_WAX=y +CONFIG_GSC_DINO=y +CONFIG_PCI_LBA=y +# CONFIG_PDC_CHASSIS is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=m +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=m +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_DIAG=m +CONFIG_LLC2=m +# CONFIG_WIRELESS is not set +CONFIG_EISA=y +CONFIG_PCI=y +CONFIG_PCCARD=m +CONFIG_YENTA=m +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_PARPORT=y +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_1284=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=6144 +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_CHR_DEV_SG=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_SCSI_LASI700=y +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_ZALON=y +CONFIG_SCSI_DH=y +CONFIG_ATA=y +CONFIG_PATA_NS87415=y +CONFIG_ATA_GENERIC=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=m +CONFIG_DUMMY=m +CONFIG_TUN=m +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CISCO is not set +CONFIG_NET_TULIP=y +CONFIG_TULIP=y +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +CONFIG_LASI_82596=y +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +# CONFIG_NET_VENDOR_QLOGIC is not set +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_VENDOR_RDC is not set +# CONFIG_NET_VENDOR_REALTEK is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SILAN is not set +# CONFIG_NET_VENDOR_SIS is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_VIA is not set +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPPOE=m +# CONFIG_WLAN is not set +CONFIG_KEYBOARD_HIL_OLD=m +CONFIG_KEYBOARD_HIL=m +CONFIG_MOUSE_SERIAL=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=m +CONFIG_LEGACY_PTY_COUNT=64 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=8 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_PRINTER=m +CONFIG_PPDEV=m +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_HWMON=m +CONFIG_DRM=m +CONFIG_DRM_DISPLAY_DP_AUX_CEC=y +CONFIG_DRM_RADEON=m +CONFIG_DRM_NOUVEAU=m +# CONFIG_DRM_NOUVEAU_BACKLIGHT is not set +# CONFIG_DRM_NOUVEAU_CH7006 is not set +# CONFIG_DRM_NOUVEAU_SIL164 is not set +CONFIG_DRM_VGEM=m +CONFIG_DRM_UDL=m +CONFIG_DRM_MGAG200=m +CONFIG_FB=y +CONFIG_FB_FOREIGN_ENDIAN=y +CONFIG_FB_PM2=m +CONFIG_FB_PM2_FIFO_DISCONNECT=y +CONFIG_FB_NVIDIA=m +CONFIG_FB_NVIDIA_I2C=y +# CONFIG_FB_NVIDIA_BACKLIGHT is not set +CONFIG_FB_RIVA=m +CONFIG_FB_RIVA_I2C=y +# CONFIG_FB_RIVA_BACKLIGHT is not set +CONFIG_FB_MATROX=m +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G=y +CONFIG_FB_MATROX_I2C=m +CONFIG_FB_MATROX_MAVEN=m +CONFIG_FB_ATY128=m +# CONFIG_FB_ATY128_BACKLIGHT is not set +CONFIG_FB_ATY=m +CONFIG_FB_ATY_CT=y +CONFIG_FB_ATY_GX=y +# CONFIG_FB_ATY_BACKLIGHT is not set +CONFIG_FB_S3=m +CONFIG_FB_SAVAGE=m +CONFIG_FB_SAVAGE_I2C=y +CONFIG_FB_SAVAGE_ACCEL=y +CONFIG_FB_SIS=m +CONFIG_FB_SIS_300=y +CONFIG_FB_SIS_315=y +CONFIG_FB_VOODOO1=m +CONFIG_FB_TRIDENT=m +CONFIG_FB_SMSCUFX=m +CONFIG_FB_UDL=m +CONFIG_DUMMY_CONSOLE_COLUMNS=128 +CONFIG_DUMMY_CONSOLE_ROWS=48 +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +# CONFIG_LOGO_LINUX_CLUT224 is not set +CONFIG_SOUND=m +CONFIG_SND=m +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SEQUENCER=m +CONFIG_SND_AD1889=m +CONFIG_SND_HARMONY=m +CONFIG_HIDRAW=y +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_HID_EZKEY=y +CONFIG_HID_KYE=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=m +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_ZEROPLUS=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_MON=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_UHCI_HCD=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_DMADEVICES=y +CONFIG_AUXDISPLAY=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y +CONFIG_AUTOFS_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_NFS_FS=m +# CONFIG_NFS_V2 is not set +CONFIG_NFSD=m +CONFIG_CIFS=m +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG is not set +CONFIG_CRYPTO_BENCHMARK=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_DEFLATE=y +CONFIG_FONTS=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_DEBUG_SHIRQ=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_LATENCYTOP=y +CONFIG_LKDTM=m diff --git a/arch/parisc/configs/generic-64bit_defconfig b/arch/parisc/configs/generic-64bit_defconfig new file mode 100644 index 000000000..406f8174a --- /dev/null +++ b/arch/parisc/configs/generic-64bit_defconfig @@ -0,0 +1,295 @@ +CONFIG_LOCALVERSION="-64bit" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZ4=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_CGROUP_PIDS=y +CONFIG_CPUSETS=y +CONFIG_USER_NS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SMP=y +CONFIG_HPPB=y +CONFIG_IOMMU_CCIO=y +CONFIG_GSC_LASI=y +CONFIG_GSC_WAX=y +CONFIG_GSC_DINO=y +CONFIG_PCI_LBA=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BINFMT_MISC=m +# CONFIG_COMPAT_BRK is not set +# CONFIG_COMPACTION is not set +CONFIG_MEMORY_FAILURE=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=m +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XFRM_MIGRATE=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_DIAG=m +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_ADVANCED is not set +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_DCB=y +# CONFIG_WIRELESS is not set +CONFIG_PCI=y +CONFIG_PCI_STUB=m +CONFIG_PCI_IOV=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_BLK_DEV_LOOP=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_SCSI_ISCSI_ATTRS=y +CONFIG_SCSI_SRP_ATTRS=y +CONFIG_ISCSI_BOOT_SYSFS=y +CONFIG_SCSI_MPT2SAS=y +CONFIG_SCSI_LASI700=y +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_ZALON=y +CONFIG_SCSI_QLA_ISCSI=m +CONFIG_SCSI_DH=y +CONFIG_ATA=y +CONFIG_SATA_SIL=y +CONFIG_SATA_SIS=y +CONFIG_SATA_VIA=y +CONFIG_PATA_NS87415=y +CONFIG_PATA_SIL680=y +CONFIG_ATA_GENERIC=y +CONFIG_MD=y +CONFIG_MD_LINEAR=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_RAID=m +CONFIG_DM_UEVENT=y +CONFIG_DM_AUDIT=y +CONFIG_FUSION=y +CONFIG_FUSION_SPI=y +CONFIG_FUSION_SAS=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CISCO is not set +CONFIG_NET_TULIP=y +CONFIG_TULIP=y +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +CONFIG_LASI_82596=y +CONFIG_E1000=y +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +CONFIG_QLA3XXX=m +CONFIG_QLCNIC=m +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_VENDOR_RDC is not set +# CONFIG_NET_VENDOR_REALTEK is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SILAN is not set +# CONFIG_NET_VENDOR_SIS is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PHYLIB=y +CONFIG_BROADCOM_PHY=m +CONFIG_CICADA_PHY=m +CONFIG_DAVICOM_PHY=m +CONFIG_ICPLUS_PHY=m +CONFIG_LXT_PHY=m +CONFIG_LSI_ET1011C_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_NATIONAL_PHY=m +CONFIG_QSEMI_PHY=m +CONFIG_REALTEK_PHY=m +CONFIG_SMSC_PHY=m +CONFIG_STE10XP=m +CONFIG_VITESSE_PHY=m +CONFIG_MDIO_BITBANG=m +CONFIG_SLIP=m +# CONFIG_WLAN is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_KEYBOARD_HIL_OLD is not set +# CONFIG_KEYBOARD_HIL is not set +# CONFIG_MOUSE_PS2 is not set +CONFIG_INPUT_MISC=y +CONFIG_SERIO_SERPORT=m +# CONFIG_HP_SDC is not set +CONFIG_SERIO_RAW=m +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=8 +CONFIG_SERIAL_8250_RUNTIME_UARTS=8 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_JSM=m +CONFIG_NOZOMI=m +CONFIG_IPMI_HANDLER=y +CONFIG_IPMI_DEVICE_INTERFACE=y +CONFIG_IPMI_SI=y +# CONFIG_HW_RANDOM is not set +CONFIG_TCG_TPM=m +CONFIG_TCG_ATMEL=m +CONFIG_PTP_1588_CLOCK=m +CONFIG_SENSORS_I5K_AMB=m +CONFIG_SENSORS_F71882FG=m +CONFIG_SENSORS_PC87427=m +CONFIG_SENSORS_VT1211=m +CONFIG_SENSORS_VT8231=m +CONFIG_SENSORS_W83627EHF=m +CONFIG_WATCHDOG=y +CONFIG_SOFT_WATCHDOG=m +CONFIG_SSB=m +CONFIG_SSB_DRIVER_PCICORE=y +CONFIG_LPC_SCH=m +CONFIG_MFD_SM501=m +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=m +CONFIG_REGULATOR_USERSPACE_CONSUMER=m +CONFIG_MEDIA_SUPPORT=m +CONFIG_AGP=y +CONFIG_AGP_PARISC=y +CONFIG_DRM=y +CONFIG_DRM_RADEON=y +CONFIG_DRM_NOUVEAU=m +# CONFIG_DRM_NOUVEAU_BACKLIGHT is not set +# CONFIG_DRM_NOUVEAU_CH7006 is not set +# CONFIG_DRM_NOUVEAU_SIL164 is not set +CONFIG_DRM_MGAG200=m +CONFIG_FB=y +CONFIG_FB_PM2=m +CONFIG_FB_PM2_FIFO_DISCONNECT=y +CONFIG_FB_NVIDIA=m +CONFIG_FB_NVIDIA_I2C=y +# CONFIG_FB_NVIDIA_BACKLIGHT is not set +CONFIG_FB_RIVA=m +CONFIG_FB_RIVA_I2C=y +# CONFIG_FB_RIVA_BACKLIGHT is not set +CONFIG_FB_MATROX=m +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G=y +CONFIG_FB_MATROX_I2C=m +CONFIG_FB_MATROX_MAVEN=m +CONFIG_FB_RADEON=y +# CONFIG_FB_RADEON_BACKLIGHT is not set +CONFIG_FB_ATY128=m +# CONFIG_FB_ATY128_BACKLIGHT is not set +CONFIG_FB_ATY=m +CONFIG_FB_ATY_CT=y +CONFIG_FB_ATY_GX=y +# CONFIG_FB_ATY_BACKLIGHT is not set +CONFIG_FB_S3=m +CONFIG_FB_SAVAGE=m +CONFIG_FB_SAVAGE_I2C=y +CONFIG_FB_SAVAGE_ACCEL=y +CONFIG_FB_SIS=m +CONFIG_FB_SIS_300=y +CONFIG_FB_SIS_315=y +CONFIG_FB_VOODOO1=m +CONFIG_FB_TRIDENT=m +CONFIG_FB_SMSCUFX=m +CONFIG_FB_UDL=m +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_CLUT224 is not set +CONFIG_HIDRAW=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_UIO=y +CONFIG_UIO_PDRV_GENIRQ=m +CONFIG_UIO_AEC=m +CONFIG_UIO_SERCOS3=m +CONFIG_UIO_PCI_GENERIC=m +CONFIG_STAGING=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_XFS_FS=m +CONFIG_BTRFS_FS=m +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y +CONFIG_AUTOFS_FS=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_ISO9660_FS=y +CONFIG_UDF_FS=y +CONFIG_VFAT_FS=m +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_CONFIGFS_FS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V4=m +CONFIG_NFS_V4_1=y +CONFIG_NFSD=m +CONFIG_NFSD_V4=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_UTF8=m +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_DEFLATE=m +# CONFIG_CRYPTO_HW is not set +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_KERNEL=y +CONFIG_STRIP_ASM_SYMS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_SCHED_DEBUG is not set diff --git a/arch/parisc/defpalo.conf b/arch/parisc/defpalo.conf new file mode 100644 index 000000000..208ff3b41 --- /dev/null +++ b/arch/parisc/defpalo.conf @@ -0,0 +1,21 @@ +# This a generic Palo configuration file. For more information about how +# it works try 'palo -?'. +# +# Most people using 'make palo' want a bootable file, usable for +# network or tape booting for example. +--init-tape=lifimage +--recoverykernel=vmlinuz + +########## Pick your ROOT here! ########## +# You need at least one 'root='! +# +# If you want a root ramdisk, use the next 2 lines +# (Edit the ramdisk image name!!!!) +--ramdisk=ram-disk-image-file +--commandline=0/vmlinuz HOME=/ root=/dev/ram initrd=0/ramdisk panic_timeout=60 panic=-1 + +# If you want NFS root, use the following command line (Edit the HOSTNAME!!!) +#--commandline=0/vmlinuz HOME=/ root=/dev/nfs nfsroot=HOSTNAME ip=bootp + +# If you have root on a disk partition, use this (Edit the partition name!!!) +#--commandline=0/vmlinuz HOME=/ root=/dev/sda1 diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild new file mode 100644 index 000000000..d48d158f7 --- /dev/null +++ b/arch/parisc/include/asm/Kbuild @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +generated-y += syscall_table_32.h +generated-y += syscall_table_64.h +generic-y += agp.h +generic-y += kvm_para.h +generic-y += mcs_spinlock.h +generic-y += ring_buffer.h +generic-y += user.h diff --git a/arch/parisc/include/asm/alternative.h b/arch/parisc/include/asm/alternative.h new file mode 100644 index 000000000..1601ae4b8 --- /dev/null +++ b/arch/parisc/include/asm/alternative.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_PARISC_ALTERNATIVE_H +#define __ASM_PARISC_ALTERNATIVE_H + +#define ALT_COND_ALWAYS 0x80 /* always replace instruction */ +#define ALT_COND_NO_SMP 0x01 /* when running UP instead of SMP */ +#define ALT_COND_NO_DCACHE 0x02 /* if system has no d-cache */ +#define ALT_COND_NO_ICACHE 0x04 /* if system has no i-cache */ +#define ALT_COND_NO_SPLIT_TLB 0x08 /* if split_tlb == 0 */ +#define ALT_COND_NO_IOC_FDC 0x10 /* if I/O cache does not need flushes */ +#define ALT_COND_RUN_ON_QEMU 0x20 /* if running on QEMU */ + +#define INSN_PxTLB 0x02 /* modify pdtlb, pitlb */ +#define INSN_NOP 0x08000240 /* nop */ + +#ifndef __ASSEMBLER__ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/stddef.h> +#include <linux/stringify.h> + +struct alt_instr { + s32 orig_offset; /* offset to original instructions */ + s16 len; /* end of original instructions */ + u16 cond; /* see ALT_COND_XXX */ + u32 replacement; /* replacement instruction or code */ +} __packed; + +void set_kernel_text_rw(int enable_read_write); +void apply_alternatives_all(void); +void apply_alternatives(struct alt_instr *start, struct alt_instr *end, + const char *module_name); + +/* Alternative SMP implementation. */ +#define ALTERNATIVE(cond, replacement) "!0:" \ + ".section .altinstructions, \"a\" !" \ + ".align 4 !" \ + ".word (0b-4-.) !" \ + ".hword 1, " __stringify(cond) " !" \ + ".word " __stringify(replacement) " !" \ + ".previous" + +#else + +/* to replace one single instructions by a new instruction */ +#define ALTERNATIVE(from, to, cond, replacement)\ + .section .altinstructions, "a" ! \ + .align 4 ! \ + .word (from - .) ! \ + .hword (to - from)/4, cond ! \ + .word replacement ! \ + .previous + +/* to replace multiple instructions by new code */ +#define ALTERNATIVE_CODE(from, num_instructions, cond, new_instr_ptr)\ + .section .altinstructions, "a" ! \ + .align 4 ! \ + .word (from - .) ! \ + .hword -num_instructions, cond ! \ + .word (new_instr_ptr - .) ! \ + .previous + +#endif /* __ASSEMBLER__ */ + +#endif /* __ASM_PARISC_ALTERNATIVE_H */ diff --git a/arch/parisc/include/asm/asm-offsets.h b/arch/parisc/include/asm/asm-offsets.h new file mode 100644 index 000000000..d370ee36a --- /dev/null +++ b/arch/parisc/include/asm/asm-offsets.h @@ -0,0 +1 @@ +#include <generated/asm-offsets.h> diff --git a/arch/parisc/include/asm/asmregs.h b/arch/parisc/include/asm/asmregs.h new file mode 100644 index 000000000..81d8029d8 --- /dev/null +++ b/arch/parisc/include/asm/asmregs.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 1999 Hewlett-Packard (Frank Rowand) + */ + +#ifndef _PARISC_ASMREGS_H +#define _PARISC_ASMREGS_H + +;! General Registers + +rp: .reg %r2 +arg3: .reg %r23 +arg2: .reg %r24 +arg1: .reg %r25 +arg0: .reg %r26 +dp: .reg %r27 +ret0: .reg %r28 +ret1: .reg %r29 +sl: .reg %r29 +sp: .reg %r30 + +#if 0 +/* PA20_REVISIT */ +arg7: .reg r19 +arg6: .reg r20 +arg5: .reg r21 +arg4: .reg r22 +gp: .reg r27 +ap: .reg r29 +#endif + + +r0: .reg %r0 +r1: .reg %r1 +r2: .reg %r2 +r3: .reg %r3 +r4: .reg %r4 +r5: .reg %r5 +r6: .reg %r6 +r7: .reg %r7 +r8: .reg %r8 +r9: .reg %r9 +r10: .reg %r10 +r11: .reg %r11 +r12: .reg %r12 +r13: .reg %r13 +r14: .reg %r14 +r15: .reg %r15 +r16: .reg %r16 +r17: .reg %r17 +r18: .reg %r18 +r19: .reg %r19 +r20: .reg %r20 +r21: .reg %r21 +r22: .reg %r22 +r23: .reg %r23 +r24: .reg %r24 +r25: .reg %r25 +r26: .reg %r26 +r27: .reg %r27 +r28: .reg %r28 +r29: .reg %r29 +r30: .reg %r30 +r31: .reg %r31 + + +;! Space Registers + +sr0: .reg %sr0 +sr1: .reg %sr1 +sr2: .reg %sr2 +sr3: .reg %sr3 +sr4: .reg %sr4 +sr5: .reg %sr5 +sr6: .reg %sr6 +sr7: .reg %sr7 + + +;! Floating Point Registers + +fr0: .reg %fr0 +fr1: .reg %fr1 +fr2: .reg %fr2 +fr3: .reg %fr3 +fr4: .reg %fr4 +fr5: .reg %fr5 +fr6: .reg %fr6 +fr7: .reg %fr7 +fr8: .reg %fr8 +fr9: .reg %fr9 +fr10: .reg %fr10 +fr11: .reg %fr11 +fr12: .reg %fr12 +fr13: .reg %fr13 +fr14: .reg %fr14 +fr15: .reg %fr15 +fr16: .reg %fr16 +fr17: .reg %fr17 +fr18: .reg %fr18 +fr19: .reg %fr19 +fr20: .reg %fr20 +fr21: .reg %fr21 +fr22: .reg %fr22 +fr23: .reg %fr23 +fr24: .reg %fr24 +fr25: .reg %fr25 +fr26: .reg %fr26 +fr27: .reg %fr27 +fr28: .reg %fr28 +fr29: .reg %fr29 +fr30: .reg %fr30 +fr31: .reg %fr31 + + +;! Control Registers + +rctr: .reg %cr0 +pidr1: .reg %cr8 +pidr2: .reg %cr9 +ccr: .reg %cr10 +sar: .reg %cr11 +pidr3: .reg %cr12 +pidr4: .reg %cr13 +iva: .reg %cr14 +eiem: .reg %cr15 +itmr: .reg %cr16 +pcsq: .reg %cr17 +pcoq: .reg %cr18 +iir: .reg %cr19 +isr: .reg %cr20 +ior: .reg %cr21 +ipsw: .reg %cr22 +eirr: .reg %cr23 +tr0: .reg %cr24 +tr1: .reg %cr25 +tr2: .reg %cr26 +tr3: .reg %cr27 +tr4: .reg %cr28 +tr5: .reg %cr29 +tr6: .reg %cr30 +tr7: .reg %cr31 + + +cr0: .reg %cr0 +cr8: .reg %cr8 +cr9: .reg %cr9 +cr10: .reg %cr10 +cr11: .reg %cr11 +cr12: .reg %cr12 +cr13: .reg %cr13 +cr14: .reg %cr14 +cr15: .reg %cr15 +cr16: .reg %cr16 +cr17: .reg %cr17 +cr18: .reg %cr18 +cr19: .reg %cr19 +cr20: .reg %cr20 +cr21: .reg %cr21 +cr22: .reg %cr22 +cr23: .reg %cr23 +cr24: .reg %cr24 +cr25: .reg %cr25 +cr26: .reg %cr26 +cr27: .reg %cr27 +cr28: .reg %cr28 +cr29: .reg %cr29 +cr30: .reg %cr30 +cr31: .reg %cr31 + +#endif diff --git a/arch/parisc/include/asm/assembly.h b/arch/parisc/include/asm/assembly.h new file mode 100644 index 000000000..c20261604 --- /dev/null +++ b/arch/parisc/include/asm/assembly.h @@ -0,0 +1,586 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 1999 Hewlett-Packard (Frank Rowand) + * Copyright (C) 1999 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 1999 SuSE GmbH + * Copyright (C) 2021 Helge Deller <deller@gmx.de> + */ + +#ifndef _PARISC_ASSEMBLY_H +#define _PARISC_ASSEMBLY_H + +#ifdef CONFIG_64BIT +#define RP_OFFSET 16 +#define FRAME_SIZE 128 +#define CALLEE_REG_FRAME_SIZE 144 +#define REG_SZ 8 +#define ASM_ULONG_INSN .dword +#else /* CONFIG_64BIT */ +#define RP_OFFSET 20 +#define FRAME_SIZE 64 +#define CALLEE_REG_FRAME_SIZE 128 +#define REG_SZ 4 +#define ASM_ULONG_INSN .word +#endif + +/* Frame alignment for 32- and 64-bit */ +#define FRAME_ALIGN 64 + +#define CALLEE_FLOAT_FRAME_SIZE 80 +#define CALLEE_SAVE_FRAME_SIZE (CALLEE_REG_FRAME_SIZE + CALLEE_FLOAT_FRAME_SIZE) + +#ifdef CONFIG_PA20 +#define LDCW ldcw,co +#define BL b,l +# ifdef CONFIG_64BIT +# define PA_ASM_LEVEL 2.0w +# else +# define PA_ASM_LEVEL 2.0 +# endif +#else +#define LDCW ldcw +#define BL bl +#define PA_ASM_LEVEL 1.1 +#endif + +/* Privilege level field in the rightmost two bits of the IA queues */ +#define PRIV_USER 3 +#define PRIV_KERNEL 0 + +/* Space register used inside kernel */ +#define SR_KERNEL 0 +#define SR_TEMP1 1 +#define SR_TEMP2 2 +#define SR_USER 3 + +#ifdef __ASSEMBLER__ + +#ifdef CONFIG_64BIT +#define LDREG ldd +#define STREG std +#define LDREGX ldd,s +#define LDREGM ldd,mb +#define STREGM std,ma +#define SHRREG shrd +#define SHLREG shld +#define ANDCM andcm,* +#define COND(x) * ## x +#else /* CONFIG_64BIT */ +#define LDREG ldw +#define STREG stw +#define LDREGX ldwx,s +#define LDREGM ldwm +#define STREGM stwm +#define SHRREG shr +#define SHLREG shlw +#define ANDCM andcm +#define COND(x) x +#endif + +#ifdef CONFIG_64BIT +/* the 64-bit pa gnu assembler unfortunately defaults to .level 1.1 or 2.0 so + * work around that for now... */ + .level 2.0w +#endif + +#include <asm/asm-offsets.h> +#include <asm/page.h> +#include <asm/types.h> + +#include <asm/asmregs.h> +#include <asm/psw.h> + + /* + * We provide two versions of each macro to convert from physical + * to virtual and vice versa. The "_r1" versions take one argument + * register, but trashes r1 to do the conversion. The other + * version takes two arguments: a src and destination register. + * However, the source and destination registers can not be + * the same register. + * + * We use add,l to avoid clobbering the C/B bits in the PSW. + */ + + .macro tophys grvirt, grphys + ldil L%(-__PAGE_OFFSET), \grphys + addl \grvirt, \grphys, \grphys + .endm + + .macro tovirt grphys, grvirt + ldil L%(__PAGE_OFFSET), \grvirt + addl \grphys, \grvirt, \grvirt + .endm + + .macro tophys_r1 gr + ldil L%(-__PAGE_OFFSET), %r1 + addl \gr, %r1, \gr + .endm + + .macro tovirt_r1 gr + ldil L%(__PAGE_OFFSET), %r1 + addl \gr, %r1, \gr + .endm + + .macro delay value + ldil L%\value, 1 + ldo R%\value(1), 1 + addib,UV,n -1,1,. + addib,NUV,n -1,1,.+8 + nop + .endm + + .macro debug value + .endm + + .macro shlw r, sa, t + zdep \r, 31-(\sa), 32-(\sa), \t + .endm + + /* And the PA 2.0W shift left */ + .macro shld r, sa, t + depd,z \r, 63-(\sa), 64-(\sa), \t + .endm + + /* Shift Right for 32-bit. Clobbers upper 32-bit on PA2.0. */ + .macro shr r, sa, t + extru \r, 31-(\sa), 32-(\sa), \t + .endm + + /* pa20w version of shift right */ + .macro shrd r, sa, t + extrd,u \r, 63-(\sa), 64-(\sa), \t + .endm + + /* Extract unsigned for 32- and 64-bit + * The extru instruction leaves the most significant 32 bits of the + * target register in an undefined state on PA 2.0 systems. */ + .macro extru_safe r, p, len, t +#ifdef CONFIG_64BIT + extrd,u \r, 32+(\p), \len, \t +#else + extru \r, \p, \len, \t +#endif + .endm + + /* The depi instruction leaves the most significant 32 bits of the + * target register in an undefined state on PA 2.0 systems. */ + .macro depi_safe i, p, len, t +#ifdef CONFIG_64BIT + depdi \i, 32+(\p), \len, \t +#else + depi \i, \p, \len, \t +#endif + .endm + + /* The depw instruction leaves the most significant 32 bits of the + * target register in an undefined state on PA 2.0 systems. */ + .macro dep_safe i, p, len, t +#ifdef CONFIG_64BIT + depd \i, 32+(\p), \len, \t +#else + depw \i, \p, \len, \t +#endif + .endm + + /* load 32-bit 'value' into 'reg' compensating for the ldil + * sign-extension when running in wide mode. + * WARNING!! neither 'value' nor 'reg' can be expressions + * containing '.'!!!! */ + .macro load32 value, reg + ldil L%\value, \reg + ldo R%\value(\reg), \reg + .endm + + .macro loadgp +#ifdef CONFIG_64BIT + ldil L%__gp, %r27 + ldo R%__gp(%r27), %r27 +#else + ldil L%$global$, %r27 + ldo R%$global$(%r27), %r27 +#endif + .endm + +#define SAVE_SP(r, where) mfsp r, %r1 ! STREG %r1, where +#define REST_SP(r, where) LDREG where, %r1 ! mtsp %r1, r +#define SAVE_CR(r, where) mfctl r, %r1 ! STREG %r1, where +#define REST_CR(r, where) LDREG where, %r1 ! mtctl %r1, r + + .macro save_general regs + STREG %r1, PT_GR1 (\regs) + STREG %r2, PT_GR2 (\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) + STREG %r19, PT_GR19(\regs) + STREG %r20, PT_GR20(\regs) + STREG %r21, PT_GR21(\regs) + STREG %r22, PT_GR22(\regs) + STREG %r23, PT_GR23(\regs) + STREG %r24, PT_GR24(\regs) + STREG %r25, PT_GR25(\regs) + /* r26 is saved in get_stack and used to preserve a value across virt_map */ + STREG %r27, PT_GR27(\regs) + STREG %r28, PT_GR28(\regs) + /* r29 is saved in get_stack and used to point to saved registers */ + /* r30 stack pointer saved in get_stack */ + STREG %r31, PT_GR31(\regs) + .endm + + .macro rest_general regs + /* r1 used as a temp in rest_stack and is restored there */ + LDREG PT_GR2 (\regs), %r2 + 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 + LDREG PT_GR19(\regs), %r19 + LDREG PT_GR20(\regs), %r20 + LDREG PT_GR21(\regs), %r21 + LDREG PT_GR22(\regs), %r22 + LDREG PT_GR23(\regs), %r23 + LDREG PT_GR24(\regs), %r24 + LDREG PT_GR25(\regs), %r25 + LDREG PT_GR26(\regs), %r26 + LDREG PT_GR27(\regs), %r27 + LDREG PT_GR28(\regs), %r28 + /* r29 points to register save area, and is restored in rest_stack */ + /* r30 stack pointer restored in rest_stack */ + LDREG PT_GR31(\regs), %r31 + .endm + + .macro save_fp regs + fstd,ma %fr0, 8(\regs) + fstd,ma %fr1, 8(\regs) + fstd,ma %fr2, 8(\regs) + fstd,ma %fr3, 8(\regs) + fstd,ma %fr4, 8(\regs) + fstd,ma %fr5, 8(\regs) + fstd,ma %fr6, 8(\regs) + fstd,ma %fr7, 8(\regs) + fstd,ma %fr8, 8(\regs) + fstd,ma %fr9, 8(\regs) + fstd,ma %fr10, 8(\regs) + fstd,ma %fr11, 8(\regs) + fstd,ma %fr12, 8(\regs) + fstd,ma %fr13, 8(\regs) + fstd,ma %fr14, 8(\regs) + fstd,ma %fr15, 8(\regs) + fstd,ma %fr16, 8(\regs) + fstd,ma %fr17, 8(\regs) + fstd,ma %fr18, 8(\regs) + fstd,ma %fr19, 8(\regs) + fstd,ma %fr20, 8(\regs) + fstd,ma %fr21, 8(\regs) + fstd,ma %fr22, 8(\regs) + fstd,ma %fr23, 8(\regs) + fstd,ma %fr24, 8(\regs) + fstd,ma %fr25, 8(\regs) + fstd,ma %fr26, 8(\regs) + fstd,ma %fr27, 8(\regs) + fstd,ma %fr28, 8(\regs) + fstd,ma %fr29, 8(\regs) + fstd,ma %fr30, 8(\regs) + fstd %fr31, 0(\regs) + .endm + + .macro rest_fp regs + fldd 0(\regs), %fr31 + fldd,mb -8(\regs), %fr30 + fldd,mb -8(\regs), %fr29 + fldd,mb -8(\regs), %fr28 + fldd,mb -8(\regs), %fr27 + fldd,mb -8(\regs), %fr26 + fldd,mb -8(\regs), %fr25 + fldd,mb -8(\regs), %fr24 + fldd,mb -8(\regs), %fr23 + fldd,mb -8(\regs), %fr22 + fldd,mb -8(\regs), %fr21 + fldd,mb -8(\regs), %fr20 + fldd,mb -8(\regs), %fr19 + fldd,mb -8(\regs), %fr18 + fldd,mb -8(\regs), %fr17 + fldd,mb -8(\regs), %fr16 + fldd,mb -8(\regs), %fr15 + fldd,mb -8(\regs), %fr14 + fldd,mb -8(\regs), %fr13 + fldd,mb -8(\regs), %fr12 + fldd,mb -8(\regs), %fr11 + fldd,mb -8(\regs), %fr10 + fldd,mb -8(\regs), %fr9 + fldd,mb -8(\regs), %fr8 + fldd,mb -8(\regs), %fr7 + fldd,mb -8(\regs), %fr6 + fldd,mb -8(\regs), %fr5 + fldd,mb -8(\regs), %fr4 + fldd,mb -8(\regs), %fr3 + fldd,mb -8(\regs), %fr2 + fldd,mb -8(\regs), %fr1 + fldd,mb -8(\regs), %fr0 + .endm + + .macro callee_save_float + fstd,ma %fr12, 8(%r30) + fstd,ma %fr13, 8(%r30) + fstd,ma %fr14, 8(%r30) + fstd,ma %fr15, 8(%r30) + fstd,ma %fr16, 8(%r30) + fstd,ma %fr17, 8(%r30) + fstd,ma %fr18, 8(%r30) + fstd,ma %fr19, 8(%r30) + fstd,ma %fr20, 8(%r30) + fstd,ma %fr21, 8(%r30) + .endm + + .macro callee_rest_float + fldd,mb -8(%r30), %fr21 + fldd,mb -8(%r30), %fr20 + fldd,mb -8(%r30), %fr19 + fldd,mb -8(%r30), %fr18 + fldd,mb -8(%r30), %fr17 + fldd,mb -8(%r30), %fr16 + fldd,mb -8(%r30), %fr15 + fldd,mb -8(%r30), %fr14 + fldd,mb -8(%r30), %fr13 + fldd,mb -8(%r30), %fr12 + .endm + +#ifdef CONFIG_64BIT + .macro callee_save + std,ma %r3, CALLEE_REG_FRAME_SIZE(%r30) + mfctl %cr27, %r3 + std %r4, -136(%r30) + std %r5, -128(%r30) + std %r6, -120(%r30) + std %r7, -112(%r30) + std %r8, -104(%r30) + std %r9, -96(%r30) + std %r10, -88(%r30) + std %r11, -80(%r30) + std %r12, -72(%r30) + std %r13, -64(%r30) + std %r14, -56(%r30) + std %r15, -48(%r30) + std %r16, -40(%r30) + std %r17, -32(%r30) + std %r18, -24(%r30) + std %r3, -16(%r30) + .endm + + .macro callee_rest + ldd -16(%r30), %r3 + ldd -24(%r30), %r18 + ldd -32(%r30), %r17 + ldd -40(%r30), %r16 + ldd -48(%r30), %r15 + ldd -56(%r30), %r14 + ldd -64(%r30), %r13 + ldd -72(%r30), %r12 + ldd -80(%r30), %r11 + ldd -88(%r30), %r10 + ldd -96(%r30), %r9 + ldd -104(%r30), %r8 + ldd -112(%r30), %r7 + ldd -120(%r30), %r6 + ldd -128(%r30), %r5 + ldd -136(%r30), %r4 + mtctl %r3, %cr27 + ldd,mb -CALLEE_REG_FRAME_SIZE(%r30), %r3 + .endm + +#else /* ! CONFIG_64BIT */ + + .macro callee_save + stw,ma %r3, CALLEE_REG_FRAME_SIZE(%r30) + mfctl %cr27, %r3 + stw %r4, -124(%r30) + stw %r5, -120(%r30) + stw %r6, -116(%r30) + stw %r7, -112(%r30) + stw %r8, -108(%r30) + stw %r9, -104(%r30) + stw %r10, -100(%r30) + stw %r11, -96(%r30) + stw %r12, -92(%r30) + stw %r13, -88(%r30) + stw %r14, -84(%r30) + stw %r15, -80(%r30) + stw %r16, -76(%r30) + stw %r17, -72(%r30) + stw %r18, -68(%r30) + stw %r3, -64(%r30) + .endm + + .macro callee_rest + ldw -64(%r30), %r3 + ldw -68(%r30), %r18 + ldw -72(%r30), %r17 + ldw -76(%r30), %r16 + ldw -80(%r30), %r15 + ldw -84(%r30), %r14 + ldw -88(%r30), %r13 + ldw -92(%r30), %r12 + ldw -96(%r30), %r11 + ldw -100(%r30), %r10 + ldw -104(%r30), %r9 + ldw -108(%r30), %r8 + ldw -112(%r30), %r7 + ldw -116(%r30), %r6 + ldw -120(%r30), %r5 + ldw -124(%r30), %r4 + mtctl %r3, %cr27 + ldw,mb -CALLEE_REG_FRAME_SIZE(%r30), %r3 + .endm +#endif /* ! CONFIG_64BIT */ + + .macro save_specials regs + + SAVE_SP (%sr0, PT_SR0 (\regs)) + SAVE_SP (%sr1, PT_SR1 (\regs)) + SAVE_SP (%sr2, PT_SR2 (\regs)) + SAVE_SP (%sr3, PT_SR3 (\regs)) + SAVE_SP (%sr4, PT_SR4 (\regs)) + SAVE_SP (%sr5, PT_SR5 (\regs)) + SAVE_SP (%sr6, PT_SR6 (\regs)) + + SAVE_CR (%cr17, PT_IASQ0(\regs)) + mtctl %r0, %cr17 + SAVE_CR (%cr17, PT_IASQ1(\regs)) + + SAVE_CR (%cr18, PT_IAOQ0(\regs)) + mtctl %r0, %cr18 + SAVE_CR (%cr18, PT_IAOQ1(\regs)) + +#ifdef CONFIG_64BIT + /* cr11 (sar) is a funny one. 5 bits on PA1.1 and 6 bit on PA2.0 + * For PA2.0 mtsar or mtctl always write 6 bits, but mfctl only + * reads 5 bits. Use mfctl,w to read all six bits. Otherwise + * we lose the 6th bit on a save/restore over interrupt. + */ + mfctl,w %cr11, %r1 + STREG %r1, PT_SAR (\regs) +#else + SAVE_CR (%cr11, PT_SAR (\regs)) +#endif + SAVE_CR (%cr19, PT_IIR (\regs)) + + /* + * Code immediately following this macro (in intr_save) relies + * on r8 containing ipsw. + */ + mfctl %cr22, %r8 + STREG %r8, PT_PSW(\regs) + .endm + + .macro rest_specials regs + + REST_SP (%sr0, PT_SR0 (\regs)) + REST_SP (%sr1, PT_SR1 (\regs)) + REST_SP (%sr2, PT_SR2 (\regs)) + REST_SP (%sr3, PT_SR3 (\regs)) + REST_SP (%sr4, PT_SR4 (\regs)) + REST_SP (%sr5, PT_SR5 (\regs)) + REST_SP (%sr6, PT_SR6 (\regs)) + REST_SP (%sr7, PT_SR7 (\regs)) + + REST_CR (%cr17, PT_IASQ0(\regs)) + REST_CR (%cr17, PT_IASQ1(\regs)) + + REST_CR (%cr18, PT_IAOQ0(\regs)) + REST_CR (%cr18, PT_IAOQ1(\regs)) + + REST_CR (%cr11, PT_SAR (\regs)) + + REST_CR (%cr22, PT_PSW (\regs)) + .endm + + + /* First step to create a "relied upon translation" + * See PA 2.0 Arch. page F-4 and F-5. + * + * The ssm was originally necessary due to a "PCxT bug". + * But someone decided it needed to be added to the architecture + * and this "feature" went into rev3 of PA-RISC 1.1 Arch Manual. + * It's been carried forward into PA 2.0 Arch as well. :^( + * + * "ssm 0,%r0" is a NOP with side effects (prefetch barrier). + * rsm/ssm prevents the ifetch unit from speculatively fetching + * instructions past this line in the code stream. + * PA 2.0 processor will single step all insn in the same QUAD (4 insn). + */ + .macro pcxt_ssm_bug + rsm PSW_SM_I,%r0 + nop /* 1 */ + nop /* 2 */ + nop /* 3 */ + nop /* 4 */ + nop /* 5 */ + nop /* 6 */ + nop /* 7 */ + .endm + + /* Switch to virtual mapping, trashing only %r1 */ + .macro virt_map + /* pcxt_ssm_bug */ + rsm PSW_SM_I, %r0 /* barrier for "Relied upon Translation */ + mtsp %r0, %sr4 + mtsp %r0, %sr5 + mtsp %r0, %sr6 + tovirt_r1 %r29 + load32 KERNEL_PSW, %r1 + + rsm PSW_SM_QUIET,%r0 /* second "heavy weight" ctl op */ + mtctl %r0, %cr17 /* Clear IIASQ tail */ + mtctl %r0, %cr17 /* Clear IIASQ head */ + mtctl %r1, %ipsw + load32 4f, %r1 + mtctl %r1, %cr18 /* Set IIAOQ tail */ + ldo 4(%r1), %r1 + mtctl %r1, %cr18 /* Set IIAOQ head */ + rfir + nop +4: + .endm + + + /* + * ASM_EXCEPTIONTABLE_ENTRY + * + * Creates an exception table entry. + * Do not convert to a assembler macro. This won't work. + */ +#define ASM_EXCEPTIONTABLE_ENTRY(fault_addr, except_addr) \ + .section __ex_table,"aw" ! \ + .align 4 ! \ + .word (fault_addr - .), (except_addr - .) ! \ + or %r0,%r0,%r0 ! \ + .previous + + +#endif /* __ASSEMBLER__ */ +#endif diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h new file mode 100644 index 000000000..d4f023887 --- /dev/null +++ b/arch/parisc/include/asm/atomic.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2006 Kyle McMartin <kyle@parisc-linux.org> + */ + +#ifndef _ASM_PARISC_ATOMIC_H_ +#define _ASM_PARISC_ATOMIC_H_ + +#include <linux/types.h> +#include <asm/cmpxchg.h> +#include <asm/barrier.h> + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + * + * And probably incredibly slow on parisc. OTOH, we don't + * have to write any serious assembly. prumpf + */ + +#ifdef CONFIG_SMP +#include <asm/spinlock.h> +#include <asm/cache.h> /* we use L1_CACHE_BYTES */ + +/* Use an array of spinlocks for our atomic_ts. + * Hash function to index into a different SPINLOCK. + * Since "a" is usually an address, use one spinlock per cacheline. + */ +# define ATOMIC_HASH_SIZE 4 +# define ATOMIC_HASH(a) (&(__atomic_hash[ (((unsigned long) (a))/L1_CACHE_BYTES) & (ATOMIC_HASH_SIZE-1) ])) + +extern arch_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned; + +/* Can't use raw_spin_lock_irq because of #include problems, so + * this is the substitute */ +#define _atomic_spin_lock_irqsave(l,f) do { \ + arch_spinlock_t *s = ATOMIC_HASH(l); \ + local_irq_save(f); \ + arch_spin_lock(s); \ +} while(0) + +#define _atomic_spin_unlock_irqrestore(l,f) do { \ + arch_spinlock_t *s = ATOMIC_HASH(l); \ + arch_spin_unlock(s); \ + local_irq_restore(f); \ +} while(0) + + +#else +# define _atomic_spin_lock_irqsave(l,f) do { local_irq_save(f); } while (0) +# define _atomic_spin_unlock_irqrestore(l,f) do { local_irq_restore(f); } while (0) +#endif + +/* + * Note that we need not lock read accesses - aligned word writes/reads + * are atomic, so a reader never sees inconsistent values. + */ + +static __inline__ void arch_atomic_set(atomic_t *v, int i) +{ + unsigned long flags; + _atomic_spin_lock_irqsave(v, flags); + + v->counter = i; + + _atomic_spin_unlock_irqrestore(v, flags); +} + +#define arch_atomic_set_release(v, i) arch_atomic_set((v), (i)) + +static __inline__ int arch_atomic_read(const atomic_t *v) +{ + return READ_ONCE((v)->counter); +} + +#define ATOMIC_OP(op, c_op) \ +static __inline__ void arch_atomic_##op(int i, atomic_t *v) \ +{ \ + unsigned long flags; \ + \ + _atomic_spin_lock_irqsave(v, flags); \ + v->counter c_op i; \ + _atomic_spin_unlock_irqrestore(v, flags); \ +} + +#define ATOMIC_OP_RETURN(op, c_op) \ +static __inline__ int arch_atomic_##op##_return(int i, atomic_t *v) \ +{ \ + unsigned long flags; \ + int ret; \ + \ + _atomic_spin_lock_irqsave(v, flags); \ + ret = (v->counter c_op i); \ + _atomic_spin_unlock_irqrestore(v, flags); \ + \ + return ret; \ +} + +#define ATOMIC_FETCH_OP(op, c_op) \ +static __inline__ int arch_atomic_fetch_##op(int i, atomic_t *v) \ +{ \ + unsigned long flags; \ + int ret; \ + \ + _atomic_spin_lock_irqsave(v, flags); \ + ret = v->counter; \ + v->counter c_op i; \ + _atomic_spin_unlock_irqrestore(v, flags); \ + \ + return ret; \ +} + +#define ATOMIC_OPS(op, c_op) \ + ATOMIC_OP(op, c_op) \ + ATOMIC_OP_RETURN(op, c_op) \ + ATOMIC_FETCH_OP(op, c_op) + +ATOMIC_OPS(add, +=) +ATOMIC_OPS(sub, -=) + +#define arch_atomic_add_return arch_atomic_add_return +#define arch_atomic_sub_return arch_atomic_sub_return +#define arch_atomic_fetch_add arch_atomic_fetch_add +#define arch_atomic_fetch_sub arch_atomic_fetch_sub + +#undef ATOMIC_OPS +#define ATOMIC_OPS(op, c_op) \ + ATOMIC_OP(op, c_op) \ + ATOMIC_FETCH_OP(op, c_op) + +ATOMIC_OPS(and, &=) +ATOMIC_OPS(or, |=) +ATOMIC_OPS(xor, ^=) + +#define arch_atomic_fetch_and arch_atomic_fetch_and +#define arch_atomic_fetch_or arch_atomic_fetch_or +#define arch_atomic_fetch_xor arch_atomic_fetch_xor + +#undef ATOMIC_OPS +#undef ATOMIC_FETCH_OP +#undef ATOMIC_OP_RETURN +#undef ATOMIC_OP + +#ifdef CONFIG_64BIT + +#define ATOMIC64_INIT(i) { (i) } + +#define ATOMIC64_OP(op, c_op) \ +static __inline__ void arch_atomic64_##op(s64 i, atomic64_t *v) \ +{ \ + unsigned long flags; \ + \ + _atomic_spin_lock_irqsave(v, flags); \ + v->counter c_op i; \ + _atomic_spin_unlock_irqrestore(v, flags); \ +} + +#define ATOMIC64_OP_RETURN(op, c_op) \ +static __inline__ s64 arch_atomic64_##op##_return(s64 i, atomic64_t *v) \ +{ \ + unsigned long flags; \ + s64 ret; \ + \ + _atomic_spin_lock_irqsave(v, flags); \ + ret = (v->counter c_op i); \ + _atomic_spin_unlock_irqrestore(v, flags); \ + \ + return ret; \ +} + +#define ATOMIC64_FETCH_OP(op, c_op) \ +static __inline__ s64 arch_atomic64_fetch_##op(s64 i, atomic64_t *v) \ +{ \ + unsigned long flags; \ + s64 ret; \ + \ + _atomic_spin_lock_irqsave(v, flags); \ + ret = v->counter; \ + v->counter c_op i; \ + _atomic_spin_unlock_irqrestore(v, flags); \ + \ + return ret; \ +} + +#define ATOMIC64_OPS(op, c_op) \ + ATOMIC64_OP(op, c_op) \ + ATOMIC64_OP_RETURN(op, c_op) \ + ATOMIC64_FETCH_OP(op, c_op) + +ATOMIC64_OPS(add, +=) +ATOMIC64_OPS(sub, -=) + +#define arch_atomic64_add_return arch_atomic64_add_return +#define arch_atomic64_sub_return arch_atomic64_sub_return +#define arch_atomic64_fetch_add arch_atomic64_fetch_add +#define arch_atomic64_fetch_sub arch_atomic64_fetch_sub + +#undef ATOMIC64_OPS +#define ATOMIC64_OPS(op, c_op) \ + ATOMIC64_OP(op, c_op) \ + ATOMIC64_FETCH_OP(op, c_op) + +ATOMIC64_OPS(and, &=) +ATOMIC64_OPS(or, |=) +ATOMIC64_OPS(xor, ^=) + +#define arch_atomic64_fetch_and arch_atomic64_fetch_and +#define arch_atomic64_fetch_or arch_atomic64_fetch_or +#define arch_atomic64_fetch_xor arch_atomic64_fetch_xor + +#undef ATOMIC64_OPS +#undef ATOMIC64_FETCH_OP +#undef ATOMIC64_OP_RETURN +#undef ATOMIC64_OP + +static __inline__ void +arch_atomic64_set(atomic64_t *v, s64 i) +{ + unsigned long flags; + _atomic_spin_lock_irqsave(v, flags); + + v->counter = i; + + _atomic_spin_unlock_irqrestore(v, flags); +} + +#define arch_atomic64_set_release(v, i) arch_atomic64_set((v), (i)) + +static __inline__ s64 +arch_atomic64_read(const atomic64_t *v) +{ + return READ_ONCE((v)->counter); +} + +#endif /* !CONFIG_64BIT */ + + +#endif /* _ASM_PARISC_ATOMIC_H_ */ diff --git a/arch/parisc/include/asm/barrier.h b/arch/parisc/include/asm/barrier.h new file mode 100644 index 000000000..519b1903c --- /dev/null +++ b/arch/parisc/include/asm/barrier.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_BARRIER_H +#define __ASM_BARRIER_H + +#include <asm/alternative.h> + +#ifndef __ASSEMBLER__ + +/* The synchronize caches instruction executes as a nop on systems in + which all memory references are performed in order. */ +#define synchronize_caches() asm volatile("sync" \ + ALTERNATIVE(ALT_COND_NO_SMP, INSN_NOP) \ + : : : "memory") + +#if defined(CONFIG_SMP) +#define mb() do { synchronize_caches(); } while (0) +#define rmb() mb() +#define wmb() mb() +#define dma_rmb() mb() +#define dma_wmb() mb() +#else +#define mb() barrier() +#define rmb() barrier() +#define wmb() barrier() +#define dma_rmb() barrier() +#define dma_wmb() barrier() +#endif + +#define __smp_mb() mb() +#define __smp_rmb() mb() +#define __smp_wmb() mb() + +#define __smp_store_release(p, v) \ +do { \ + typeof(p) __p = (p); \ + union { typeof(*p) __val; char __c[1]; } __u = \ + { .__val = (__force typeof(*p)) (v) }; \ + compiletime_assert_atomic_type(*p); \ + switch (sizeof(*p)) { \ + case 1: \ + asm volatile("stb,ma %0,0(%1)" \ + : : "r"(*(__u8 *)__u.__c), "r"(__p) \ + : "memory"); \ + break; \ + case 2: \ + asm volatile("sth,ma %0,0(%1)" \ + : : "r"(*(__u16 *)__u.__c), "r"(__p) \ + : "memory"); \ + break; \ + case 4: \ + asm volatile("stw,ma %0,0(%1)" \ + : : "r"(*(__u32 *)__u.__c), "r"(__p) \ + : "memory"); \ + break; \ + case 8: \ + if (IS_ENABLED(CONFIG_64BIT)) \ + asm volatile("std,ma %0,0(%1)" \ + : : "r"(*(__u64 *)__u.__c), "r"(__p) \ + : "memory"); \ + break; \ + } \ +} while (0) + +#define __smp_load_acquire(p) \ +({ \ + union { typeof(*p) __val; char __c[1]; } __u; \ + typeof(p) __p = (p); \ + compiletime_assert_atomic_type(*p); \ + switch (sizeof(*p)) { \ + case 1: \ + asm volatile("ldb,ma 0(%1),%0" \ + : "=r"(*(__u8 *)__u.__c) : "r"(__p) \ + : "memory"); \ + break; \ + case 2: \ + asm volatile("ldh,ma 0(%1),%0" \ + : "=r"(*(__u16 *)__u.__c) : "r"(__p) \ + : "memory"); \ + break; \ + case 4: \ + asm volatile("ldw,ma 0(%1),%0" \ + : "=r"(*(__u32 *)__u.__c) : "r"(__p) \ + : "memory"); \ + break; \ + case 8: \ + if (IS_ENABLED(CONFIG_64BIT)) \ + asm volatile("ldd,ma 0(%1),%0" \ + : "=r"(*(__u64 *)__u.__c) : "r"(__p) \ + : "memory"); \ + break; \ + } \ + __u.__val; \ +}) +#include <asm-generic/barrier.h> + +#endif /* !__ASSEMBLER__ */ +#endif /* __ASM_BARRIER_H */ diff --git a/arch/parisc/include/asm/bitops.h b/arch/parisc/include/asm/bitops.h new file mode 100644 index 000000000..bd1280a8a --- /dev/null +++ b/arch/parisc/include/asm/bitops.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_BITOPS_H +#define _PARISC_BITOPS_H + +#ifndef _LINUX_BITOPS_H +#error only <linux/bitops.h> can be included directly +#endif + +#include <linux/compiler.h> +#include <asm/types.h> +#include <asm/byteorder.h> +#include <asm/barrier.h> +#include <linux/atomic.h> + +/* See http://marc.theaimsgroup.com/?t=108826637900003 for discussion + * on use of volatile and __*_bit() (set/clear/change): + * *_bit() want use of volatile. + * __*_bit() are "relaxed" and don't use spinlock or volatile. + */ + +static __inline__ void set_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long flags; + + addr += BIT_WORD(nr); + _atomic_spin_lock_irqsave(addr, flags); + *addr |= mask; + _atomic_spin_unlock_irqrestore(addr, flags); +} + +static __inline__ void clear_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long flags; + + addr += BIT_WORD(nr); + _atomic_spin_lock_irqsave(addr, flags); + *addr &= ~mask; + _atomic_spin_unlock_irqrestore(addr, flags); +} + +static __inline__ void change_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long flags; + + addr += BIT_WORD(nr); + _atomic_spin_lock_irqsave(addr, flags); + *addr ^= mask; + _atomic_spin_unlock_irqrestore(addr, flags); +} + +static __inline__ int test_and_set_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long old; + unsigned long flags; + int set; + + addr += BIT_WORD(nr); + _atomic_spin_lock_irqsave(addr, flags); + old = *addr; + set = (old & mask) ? 1 : 0; + if (!set) + *addr = old | mask; + _atomic_spin_unlock_irqrestore(addr, flags); + + return set; +} + +static __inline__ int test_and_clear_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long old; + unsigned long flags; + int set; + + addr += BIT_WORD(nr); + _atomic_spin_lock_irqsave(addr, flags); + old = *addr; + set = (old & mask) ? 1 : 0; + if (set) + *addr = old & ~mask; + _atomic_spin_unlock_irqrestore(addr, flags); + + return set; +} + +static __inline__ int test_and_change_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long oldbit; + unsigned long flags; + + addr += BIT_WORD(nr); + _atomic_spin_lock_irqsave(addr, flags); + oldbit = *addr; + *addr = oldbit ^ mask; + _atomic_spin_unlock_irqrestore(addr, flags); + + return (oldbit & mask) ? 1 : 0; +} + +#include <asm-generic/bitops/non-atomic.h> + +/** + * __ffs - find first bit in word. returns 0 to "BITS_PER_LONG-1". + * @word: The word to search + * + * __ffs() return is undefined if no bit is set. + * + * 32-bit fast __ffs by LaMont Jones "lamont At hp com". + * 64-bit enhancement by Grant Grundler "grundler At parisc-linux org". + * (with help from willy/jejb to get the semantics right) + * + * This algorithm avoids branches by making use of nullification. + * One side effect of "extr" instructions is it sets PSW[N] bit. + * How PSW[N] (nullify next insn) gets set is determined by the + * "condition" field (eg "<>" or "TR" below) in the extr* insn. + * Only the 1st and one of either the 2cd or 3rd insn will get executed. + * Each set of 3 insn will get executed in 2 cycles on PA8x00 vs 16 or so + * cycles for each mispredicted branch. + */ + +static __inline__ __attribute_const__ unsigned long __ffs(unsigned long x) +{ + unsigned long ret; + + __asm__( +#ifdef CONFIG_64BIT + " ldi 63,%1\n" + " extrd,u,*<> %0,63,32,%%r0\n" + " extrd,u,*TR %0,31,32,%0\n" /* move top 32-bits down */ + " addi -32,%1,%1\n" +#else + " ldi 31,%1\n" +#endif + " extru,<> %0,31,16,%%r0\n" + " extru,TR %0,15,16,%0\n" /* xxxx0000 -> 0000xxxx */ + " addi -16,%1,%1\n" + " extru,<> %0,31,8,%%r0\n" + " extru,TR %0,23,8,%0\n" /* 0000xx00 -> 000000xx */ + " addi -8,%1,%1\n" + " extru,<> %0,31,4,%%r0\n" + " extru,TR %0,27,4,%0\n" /* 000000x0 -> 0000000x */ + " addi -4,%1,%1\n" + " extru,<> %0,31,2,%%r0\n" + " extru,TR %0,29,2,%0\n" /* 0000000y, 1100b -> 0011b */ + " addi -2,%1,%1\n" + " extru,= %0,31,1,%%r0\n" /* check last bit */ + " addi -1,%1,%1\n" + : "+r" (x), "=r" (ret) ); + return ret; +} + +#include <asm-generic/bitops/ffz.h> + +/* + * ffs: find first bit set. returns 1 to BITS_PER_LONG or 0 (if none set) + * This is defined the same way as the libc and compiler builtin + * ffs routines, therefore differs in spirit from the above ffz (man ffs). + */ +static __inline__ __attribute_const__ int ffs(int x) +{ + return x ? (__ffs((unsigned long)x) + 1) : 0; +} + +/* + * fls: find last (most significant) bit set. + * fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + +static __inline__ __attribute_const__ int fls(unsigned int x) +{ + int ret; + if (!x) + return 0; + + __asm__( + " ldi 1,%1\n" + " extru,<> %0,15,16,%%r0\n" + " zdep,TR %0,15,16,%0\n" /* xxxx0000 */ + " addi 16,%1,%1\n" + " extru,<> %0,7,8,%%r0\n" + " zdep,TR %0,23,24,%0\n" /* xx000000 */ + " addi 8,%1,%1\n" + " extru,<> %0,3,4,%%r0\n" + " zdep,TR %0,27,28,%0\n" /* x0000000 */ + " addi 4,%1,%1\n" + " extru,<> %0,1,2,%%r0\n" + " zdep,TR %0,29,30,%0\n" /* y0000000 (y&3 = 0) */ + " addi 2,%1,%1\n" + " extru,= %0,0,1,%%r0\n" + " addi 1,%1,%1\n" /* if y & 8, add 1 */ + : "+r" (x), "=r" (ret) ); + + return ret; +} + +#include <asm-generic/bitops/__fls.h> +#include <asm-generic/bitops/fls64.h> +#include <asm-generic/bitops/hweight.h> +#include <asm-generic/bitops/lock.h> +#include <asm-generic/bitops/sched.h> +#include <asm-generic/bitops/le.h> +#include <asm-generic/bitops/ext2-atomic-setbit.h> + +#endif /* _PARISC_BITOPS_H */ diff --git a/arch/parisc/include/asm/bug.h b/arch/parisc/include/asm/bug.h new file mode 100644 index 000000000..5cf35489a --- /dev/null +++ b/arch/parisc/include/asm/bug.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_BUG_H +#define _PARISC_BUG_H + +/* + * Tell the user there is some problem. + * The offending file and line are encoded in the __bug_table section. + */ + +#ifdef CONFIG_BUG +#define HAVE_ARCH_BUG +#define HAVE_ARCH_WARN_ON + +/* the break instruction is used as BUG() marker. */ +#define PARISC_BUG_BREAK_ASM "break 0x1f, 0x1fff" +#define PARISC_BUG_BREAK_INSN 0x03ffe01f /* PARISC_BUG_BREAK_ASM */ + +#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS +# define __BUG_REL(val) ".word " __stringify(val) " - ." +#else +# define __BUG_REL(val) ".word " __stringify(val) +#endif + + +#ifdef CONFIG_DEBUG_BUGVERBOSE +#define BUG() \ + do { \ + asm volatile("\n" \ + "1:\t" PARISC_BUG_BREAK_ASM "\n" \ + "\t.pushsection __bug_table,\"a\"\n" \ + "\t.align 4\n" \ + "2:\t" __BUG_REL(1b) "\n" \ + "\t" __BUG_REL(%c0) "\n" \ + "\t.short %1, %2\n" \ + "\t.blockz %3-2*4-2*2\n" \ + "\t.popsection" \ + : : "i" (__FILE__), "i" (__LINE__), \ + "i" (0), "i" (sizeof(struct bug_entry)) ); \ + unreachable(); \ + } while(0) + +#else +#define BUG() \ + do { \ + asm volatile(PARISC_BUG_BREAK_ASM : : ); \ + unreachable(); \ + } while(0) +#endif + +#ifdef CONFIG_DEBUG_BUGVERBOSE +#define __WARN_FLAGS(cond_str, flags) \ + do { \ + asm volatile("\n" \ + "1:\t" PARISC_BUG_BREAK_ASM "\n" \ + "\t.pushsection __bug_table,\"a\"\n" \ + "\t.align 4\n" \ + "2:\t" __BUG_REL(1b) "\n" \ + "\t" __BUG_REL(%c0) "\n" \ + "\t.short %1, %2\n" \ + "\t.blockz %3-2*4-2*2\n" \ + "\t.popsection" \ + : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \ + "i" (BUGFLAG_WARNING|(flags)), \ + "i" (sizeof(struct bug_entry)) ); \ + } while(0) +#else +#define __WARN_FLAGS(cond_str, flags) \ + do { \ + asm volatile("\n" \ + "1:\t" PARISC_BUG_BREAK_ASM "\n" \ + "\t.pushsection __bug_table,\"a\"\n" \ + "\t.align 4\n" \ + "2:\t" __BUG_REL(1b) "\n" \ + "\t.short %0\n" \ + "\t.blockz %1-4-2\n" \ + "\t.popsection" \ + : : "i" (BUGFLAG_WARNING|(flags)), \ + "i" (sizeof(struct bug_entry)) ); \ + } while(0) +#endif + + +#define WARN_ON(x) ({ \ + int __ret_warn_on = !!(x); \ + if (__builtin_constant_p(__ret_warn_on)) { \ + if (__ret_warn_on) \ + __WARN(); \ + } else { \ + if (unlikely(__ret_warn_on)) \ + __WARN(); \ + } \ + unlikely(__ret_warn_on); \ +}) + +#endif + +#include <asm-generic/bug.h> +#endif + diff --git a/arch/parisc/include/asm/cache.h b/arch/parisc/include/asm/cache.h new file mode 100644 index 000000000..3f8d3be6e --- /dev/null +++ b/arch/parisc/include/asm/cache.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * include/asm-parisc/cache.h + */ + +#ifndef __ARCH_PARISC_CACHE_H +#define __ARCH_PARISC_CACHE_H + +#include <asm/alternative.h> + +/* + * PA 2.0 processors have 64 and 128-byte L2 cachelines; PA 1.1 processors + * have 32-byte cachelines. The L1 length appears to be 16 bytes but this + * is not clearly documented. + */ +#define L1_CACHE_BYTES 16 +#define L1_CACHE_SHIFT 4 + +#ifndef __ASSEMBLER__ + +#define SMP_CACHE_BYTES L1_CACHE_BYTES + +#ifdef CONFIG_PA20 +#define ARCH_DMA_MINALIGN 128 +#else +#define ARCH_DMA_MINALIGN 32 +#endif +#define ARCH_KMALLOC_MINALIGN 16 /* ldcw requires 16-byte alignment */ + +#define arch_slab_minalign() ((unsigned)dcache_stride) +#define cache_line_size() dcache_stride +#define dma_get_cache_alignment cache_line_size + +#define __read_mostly __section(".data..read_mostly") + +void parisc_cache_init(void); /* initializes cache-flushing */ +void disable_sr_hashing_asm(int); /* low level support for above */ +void disable_sr_hashing(void); /* turns off space register hashing */ +void free_sid(unsigned long); +unsigned long alloc_sid(void); + +struct seq_file; +extern void show_cache_info(struct seq_file *m); + +extern int split_tlb; +extern int dcache_stride; +extern int icache_stride; +extern struct pdc_cache_info cache_info; +extern struct pdc_btlb_info btlb_info; +void parisc_setup_cache_timing(void); + +#define pdtlb(sr, addr) asm volatile("pdtlb 0(%%sr%0,%1)" \ + ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \ + : : "i"(sr), "r" (addr) : "memory") +#define pitlb(sr, addr) asm volatile("pitlb 0(%%sr%0,%1)" \ + ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \ + ALTERNATIVE(ALT_COND_NO_SPLIT_TLB, INSN_NOP) \ + : : "i"(sr), "r" (addr) : "memory") + +#define asm_io_fdc(addr) asm volatile("fdc %%r0(%0)" \ + ALTERNATIVE(ALT_COND_NO_DCACHE, INSN_NOP) \ + ALTERNATIVE(ALT_COND_NO_IOC_FDC, INSN_NOP) \ + : : "r" (addr) : "memory") +#define asm_io_sync() asm volatile("sync" \ + ALTERNATIVE(ALT_COND_NO_DCACHE, INSN_NOP) \ + ALTERNATIVE(ALT_COND_NO_IOC_FDC, INSN_NOP) :::"memory") +#define asm_syncdma() asm volatile("syncdma" :::"memory") + +#endif /* ! __ASSEMBLER__ */ + +/* Classes of processor wrt: disabling space register hashing */ + +#define SRHASH_PCXST 0 /* pcxs, pcxt, pcxt_ */ +#define SRHASH_PCXL 1 /* pcxl */ +#define SRHASH_PA20 2 /* pcxu, pcxu_, pcxw, pcxw_ */ + +#endif diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h new file mode 100644 index 000000000..839471887 --- /dev/null +++ b/arch/parisc/include/asm/cacheflush.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_CACHEFLUSH_H +#define _PARISC_CACHEFLUSH_H + +#include <linux/mm.h> +#include <linux/uaccess.h> +#include <asm/tlbflush.h> + +/* The usual comment is "Caches aren't brain-dead on the <architecture>". + * Unfortunately, that doesn't apply to PA-RISC. */ + +#include <linux/jump_label.h> + +DECLARE_STATIC_KEY_TRUE(parisc_has_cache); +DECLARE_STATIC_KEY_TRUE(parisc_has_dcache); +DECLARE_STATIC_KEY_TRUE(parisc_has_icache); + +#define flush_cache_dup_mm(mm) flush_cache_mm(mm) + +void flush_user_icache_range_asm(unsigned long, unsigned long); +void flush_kernel_icache_range_asm(unsigned long, unsigned long); +void flush_user_dcache_range_asm(unsigned long, unsigned long); +void flush_kernel_dcache_range_asm(unsigned long, unsigned long); +void purge_kernel_dcache_range_asm(unsigned long, unsigned long); +void flush_kernel_dcache_page_asm(const void *addr); +void flush_kernel_icache_page(void *); + +/* Cache flush operations */ + +void flush_cache_all_local(void); +void flush_cache_all(void); +void flush_cache_mm(struct mm_struct *mm); + +#define flush_kernel_dcache_range(start,size) \ + flush_kernel_dcache_range_asm((start), (start)+(size)); + +/* The only way to flush a vmap range is to flush whole cache */ +#define ARCH_IMPLEMENTS_FLUSH_KERNEL_VMAP_RANGE 1 +void flush_kernel_vmap_range(void *vaddr, int size); +void invalidate_kernel_vmap_range(void *vaddr, int size); + +void flush_cache_vmap(unsigned long start, unsigned long end); +#define flush_cache_vmap_early(start, end) do { } while (0) +void flush_cache_vunmap(unsigned long start, unsigned long end); + +void flush_dcache_folio(struct folio *folio); +#define flush_dcache_folio flush_dcache_folio +#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 +static inline void flush_dcache_page(struct page *page) +{ + flush_dcache_folio(page_folio(page)); +} + +#define flush_dcache_mmap_lock(mapping) xa_lock_irq(&mapping->i_pages) +#define flush_dcache_mmap_unlock(mapping) xa_unlock_irq(&mapping->i_pages) +#define flush_dcache_mmap_lock_irqsave(mapping, flags) \ + xa_lock_irqsave(&mapping->i_pages, flags) +#define flush_dcache_mmap_unlock_irqrestore(mapping, flags) \ + xa_unlock_irqrestore(&mapping->i_pages, flags) + +void flush_icache_pages(struct vm_area_struct *vma, struct page *page, + unsigned int nr); +#define flush_icache_pages flush_icache_pages + +#define flush_icache_range(s,e) do { \ + flush_kernel_dcache_range_asm(s,e); \ + flush_kernel_icache_range_asm(s,e); \ +} while (0) + +void copy_to_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, void *dst, void *src, int len); +void copy_from_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, void *dst, void *src, int len); +void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, + unsigned long pfn); +void flush_cache_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end); + +#define ARCH_HAS_FLUSH_ANON_PAGE +void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr); + +#define ARCH_HAS_FLUSH_ON_KUNMAP +void kunmap_flush_on_unmap(const void *addr); + +#endif /* _PARISC_CACHEFLUSH_H */ + diff --git a/arch/parisc/include/asm/cachetype.h b/arch/parisc/include/asm/cachetype.h new file mode 100644 index 000000000..e0868a1d3 --- /dev/null +++ b/arch/parisc/include/asm/cachetype.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_PARISC_CACHETYPE_H +#define __ASM_PARISC_CACHETYPE_H + +#include <linux/types.h> + +#define cpu_dcache_is_aliasing() true + +#endif diff --git a/arch/parisc/include/asm/checksum.h b/arch/parisc/include/asm/checksum.h new file mode 100644 index 000000000..382758808 --- /dev/null +++ b/arch/parisc/include/asm/checksum.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_CHECKSUM_H +#define _PARISC_CHECKSUM_H + +#include <linux/in6.h> + +#define csum_tcpudp_nofold csum_tcpudp_nofold +static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, + __u32 len, __u8 proto, + __wsum sum) +{ + __asm__( + " add %1, %0, %0\n" + " addc %2, %0, %0\n" + " addc %3, %0, %0\n" + " addc %%r0, %0, %0\n" + : "=r" (sum) + : "r" (daddr), "r"(saddr), "r"(proto+len), "0"(sum)); + return sum; +} + +#include <asm-generic/checksum.h> + +#define _HAVE_ARCH_IPV6_CSUM +static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr, + const struct in6_addr *daddr, + __u32 len, __u8 proto, + __wsum sum) +{ + unsigned long t0, t1, t2, t3; + + len += proto; /* add 16-bit proto + len */ + + __asm__ __volatile__ ( + +#if BITS_PER_LONG > 32 + + /* + ** We can execute two loads and two adds per cycle on PA 8000. + ** But add insn's get serialized waiting for the carry bit. + ** Try to keep 4 registers with "live" values ahead of the ALU. + */ + +" depdi 0, 31, 32, %0\n"/* clear upper half of incoming checksum */ +" ldd,ma 8(%1), %4\n" /* get 1st saddr word */ +" ldd,ma 8(%2), %5\n" /* get 1st daddr word */ +" add %4, %0, %0\n" +" ldd,ma 8(%1), %6\n" /* 2nd saddr */ +" ldd,ma 8(%2), %7\n" /* 2nd daddr */ +" add,dc %5, %0, %0\n" +" add,dc %6, %0, %0\n" +" add,dc %7, %0, %0\n" +" add,dc %3, %0, %0\n" /* fold in proto+len | carry bit */ +" extrd,u %0, 31, 32, %4\n"/* copy upper half down */ +" depdi 0, 31, 32, %0\n"/* clear upper half */ +" add,dc %4, %0, %0\n" /* fold into 32-bits, plus carry */ +" addc 0, %0, %0\n" /* add final carry */ + +#else + + /* + ** For PA 1.x, the insn order doesn't matter as much. + ** Insn stream is serialized on the carry bit here too. + ** result from the previous operation (eg r0 + x) + */ +" ldw,ma 4(%1), %4\n" /* get 1st saddr word */ +" ldw,ma 4(%2), %5\n" /* get 1st daddr word */ +" add %4, %0, %0\n" +" ldw,ma 4(%1), %6\n" /* 2nd saddr */ +" addc %5, %0, %0\n" +" ldw,ma 4(%2), %7\n" /* 2nd daddr */ +" addc %6, %0, %0\n" +" ldw,ma 4(%1), %4\n" /* 3rd saddr */ +" addc %7, %0, %0\n" +" ldw,ma 4(%2), %5\n" /* 3rd daddr */ +" addc %4, %0, %0\n" +" ldw,ma 4(%1), %6\n" /* 4th saddr */ +" addc %5, %0, %0\n" +" ldw,ma 4(%2), %7\n" /* 4th daddr */ +" addc %6, %0, %0\n" +" addc %7, %0, %0\n" +" addc %3, %0, %0\n" /* fold in proto+len */ +" addc 0, %0, %0\n" /* add carry */ + +#endif + : "=r" (sum), "=r" (saddr), "=r" (daddr), "=r" (len), + "=r" (t0), "=r" (t1), "=r" (t2), "=r" (t3) + : "0" (sum), "1" (saddr), "2" (daddr), "3" (len) + : "memory"); + return csum_fold(sum); +} + +#endif + diff --git a/arch/parisc/include/asm/cmpxchg.h b/arch/parisc/include/asm/cmpxchg.h new file mode 100644 index 000000000..bf0a0f118 --- /dev/null +++ b/arch/parisc/include/asm/cmpxchg.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * forked from parisc asm/atomic.h which was: + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2006 Kyle McMartin <kyle@parisc-linux.org> + */ + +#ifndef _ASM_PARISC_CMPXCHG_H_ +#define _ASM_PARISC_CMPXCHG_H_ + +/* This should get optimized out since it's never called. +** Or get a link error if xchg is used "wrong". +*/ +extern void __xchg_called_with_bad_pointer(void); + +/* __xchg32/64 defined in arch/parisc/lib/bitops.c */ +extern unsigned long __xchg8(char, volatile char *); +extern unsigned long __xchg32(int, volatile int *); +#ifdef CONFIG_64BIT +extern unsigned long __xchg64(unsigned long, volatile unsigned long *); +#endif + +/* optimizer better get rid of switch since size is a constant */ +static inline unsigned long +__arch_xchg(unsigned long x, volatile void *ptr, int size) +{ + switch (size) { +#ifdef CONFIG_64BIT + case 8: return __xchg64(x, (volatile unsigned long *) ptr); +#endif + case 4: return __xchg32((int) x, (volatile int *) ptr); + case 1: return __xchg8((char) x, (volatile char *) ptr); + } + __xchg_called_with_bad_pointer(); + return x; +} + +/* +** REVISIT - Abandoned use of LDCW in xchg() for now: +** o need to test sizeof(*ptr) to avoid clearing adjacent bytes +** o and while we are at it, could CONFIG_64BIT code use LDCD too? +** +** if (__builtin_constant_p(x) && (x == NULL)) +** if (((unsigned long)p & 0xf) == 0) +** return __ldcw(p); +*/ +#define arch_xchg(ptr, x) \ +({ \ + __typeof__(*(ptr)) __ret; \ + __typeof__(*(ptr)) _x_ = (x); \ + __ret = (__typeof__(*(ptr))) \ + __arch_xchg((unsigned long)_x_, (ptr), sizeof(*(ptr))); \ + __ret; \ +}) + +/* bug catcher for when unsupported size is used - won't link */ +extern void __cmpxchg_called_with_bad_pointer(void); + +/* __cmpxchg_u... defined in arch/parisc/lib/bitops.c */ +extern u8 __cmpxchg_u8(volatile u8 *ptr, u8 old, u8 new_); +extern u16 __cmpxchg_u16(volatile u16 *ptr, u16 old, u16 new_); +extern u32 __cmpxchg_u32(volatile u32 *m, u32 old, u32 new_); +extern u64 __cmpxchg_u64(volatile u64 *ptr, u64 old, u64 new_); + +/* don't worry...optimizer will get rid of most of this */ +static inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size) +{ + return +#ifdef CONFIG_64BIT + size == 8 ? __cmpxchg_u64(ptr, old, new_) : +#endif + size == 4 ? __cmpxchg_u32(ptr, old, new_) : + size == 2 ? __cmpxchg_u16(ptr, old, new_) : + size == 1 ? __cmpxchg_u8(ptr, old, new_) : + (__cmpxchg_called_with_bad_pointer(), old); +} + +#define arch_cmpxchg(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, sizeof(*(ptr))); \ +}) + +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new_, int size) +{ + switch (size) { +#ifdef CONFIG_64BIT + case 8: return __cmpxchg_u64((u64 *)ptr, old, new_); +#endif + case 4: return __cmpxchg_u32(ptr, old, new_); + default: + return __generic_cmpxchg_local(ptr, old, new_, size); + } +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define arch_cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#ifdef CONFIG_64BIT +#define arch_cmpxchg64_local(ptr, o, n) \ +({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ +}) +#else +#define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n)) +#endif + +#define arch_cmpxchg64(ptr, o, n) __cmpxchg_u64(ptr, o, n) + +#endif /* _ASM_PARISC_CMPXCHG_H_ */ diff --git a/arch/parisc/include/asm/compat.h b/arch/parisc/include/asm/compat.h new file mode 100644 index 000000000..13a5a55de --- /dev/null +++ b/arch/parisc/include/asm/compat.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_COMPAT_H +#define _ASM_PARISC_COMPAT_H +/* + * Architecture specific compatibility types + */ +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/thread_info.h> + +#define compat_mode_t compat_mode_t +typedef u16 compat_mode_t; + +#define compat_ipc_pid_t compat_ipc_pid_t +typedef u16 compat_ipc_pid_t; + +#define compat_ipc64_perm compat_ipc64_perm + +#include <asm-generic/compat.h> + +#define COMPAT_UTS_MACHINE "parisc\0\0" + +typedef u16 compat_nlink_t; + +struct compat_stat { + compat_dev_t st_dev; /* dev_t is 32 bits on parisc */ + compat_ino_t st_ino; /* 32 bits */ + compat_mode_t st_mode; /* 16 bits */ + compat_nlink_t st_nlink; /* 16 bits */ + u16 st_reserved1; /* old st_uid */ + u16 st_reserved2; /* old st_gid */ + compat_dev_t st_rdev; + compat_off_t st_size; + old_time32_t st_atime; + u32 st_atime_nsec; + old_time32_t st_mtime; + u32 st_mtime_nsec; + old_time32_t st_ctime; + u32 st_ctime_nsec; + s32 st_blksize; + s32 st_blocks; + u32 __unused1; /* ACL stuff */ + compat_dev_t __unused2; /* network */ + compat_ino_t __unused3; /* network */ + u32 __unused4; /* cnodes */ + u16 __unused5; /* netsite */ + short st_fstype; + compat_dev_t st_realdev; + u16 st_basemode; + u16 st_spareshort; + __compat_uid32_t st_uid; + __compat_gid32_t st_gid; + u32 st_spare4[3]; +}; + +struct compat_sigcontext { + compat_int_t sc_flags; + compat_int_t sc_gr[32]; /* PSW in sc_gr[0] */ + u64 sc_fr[32]; + compat_int_t sc_iasq[2]; + compat_int_t sc_iaoq[2]; + compat_int_t sc_sar; /* cr11 */ +}; + +struct compat_ipc64_perm { + compat_key_t key; + __compat_uid_t uid; + __compat_gid_t gid; + __compat_uid_t cuid; + __compat_gid_t cgid; + unsigned short int __pad1; + compat_mode_t mode; + unsigned short int __pad2; + unsigned short int seq; + unsigned int __pad3; + unsigned long __unused1; /* yes they really are 64bit pads */ + unsigned long __unused2; +}; + +struct compat_semid64_ds { + struct compat_ipc64_perm sem_perm; + unsigned int sem_otime_high; + unsigned int sem_otime; + unsigned int sem_ctime_high; + unsigned int sem_ctime; + compat_ulong_t sem_nsems; + compat_ulong_t __unused3; + compat_ulong_t __unused4; +}; + +struct compat_msqid64_ds { + struct compat_ipc64_perm msg_perm; + unsigned int msg_stime_high; + unsigned int msg_stime; + unsigned int msg_rtime_high; + unsigned int msg_rtime; + unsigned int msg_ctime_high; + unsigned int msg_ctime; + compat_ulong_t msg_cbytes; + compat_ulong_t msg_qnum; + compat_ulong_t msg_qbytes; + compat_pid_t msg_lspid; + compat_pid_t msg_lrpid; + compat_ulong_t __unused4; + compat_ulong_t __unused5; +}; + +struct compat_shmid64_ds { + struct compat_ipc64_perm shm_perm; + unsigned int shm_atime_high; + unsigned int shm_atime; + unsigned int shm_dtime_high; + unsigned int shm_dtime; + unsigned int shm_ctime_high; + unsigned int shm_ctime; + unsigned int __unused4; + compat_size_t shm_segsz; + compat_pid_t shm_cpid; + compat_pid_t shm_lpid; + compat_ulong_t shm_nattch; + compat_ulong_t __unused5; + compat_ulong_t __unused6; +}; + +/* + * The type of struct elf_prstatus.pr_reg in compatible core dumps. + */ +#define COMPAT_ELF_NGREG 80 +typedef compat_ulong_t compat_elf_gregset_t[COMPAT_ELF_NGREG]; + +static inline int __is_compat_task(struct task_struct *t) +{ + return IS_ENABLED(CONFIG_COMPAT) && test_tsk_thread_flag(t, TIF_32BIT); +} + +static inline int is_compat_task(void) +{ + return __is_compat_task(current); +} + +#endif /* _ASM_PARISC_COMPAT_H */ diff --git a/arch/parisc/include/asm/compat_ucontext.h b/arch/parisc/include/asm/compat_ucontext.h new file mode 100644 index 000000000..c606f1bc8 --- /dev/null +++ b/arch/parisc/include/asm/compat_ucontext.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_COMPAT_UCONTEXT_H +#define _ASM_PARISC_COMPAT_UCONTEXT_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 */ +}; + +#endif /* !_ASM_PARISC_COMPAT_UCONTEXT_H */ diff --git a/arch/parisc/include/asm/current.h b/arch/parisc/include/asm/current.h new file mode 100644 index 000000000..2814529a4 --- /dev/null +++ b/arch/parisc/include/asm/current.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_CURRENT_H +#define _ASM_PARISC_CURRENT_H + +#ifndef __ASSEMBLER__ +struct task_struct; + +static __always_inline struct task_struct *get_current(void) +{ + struct task_struct *ts; + + /* do not use mfctl() macro as it is marked volatile */ + asm( "mfctl %%cr30,%0" : "=r" (ts) ); + return ts; +} + +#define current get_current() + +#endif /* __ASSEMBLER__ */ + +#endif /* _ASM_PARISC_CURRENT_H */ diff --git a/arch/parisc/include/asm/delay.h b/arch/parisc/include/asm/delay.h new file mode 100644 index 000000000..841b506b7 --- /dev/null +++ b/arch/parisc/include/asm/delay.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_DELAY_H +#define _ASM_PARISC_DELAY_H + +static __inline__ void __delay(unsigned long loops) { + asm volatile( + " .balignl 64,0x34000034\n" + " addib,UV -1,%0,.\n" + " nop\n" + : "=r" (loops) : "0" (loops)); +} + +extern void __udelay(unsigned long usecs); +extern void __udelay_bad(unsigned long usecs); + +static inline void udelay(unsigned long usecs) +{ + if (__builtin_constant_p(usecs) && (usecs) > 20000) + __udelay_bad(usecs); + __udelay(usecs); +} + +#endif /* _ASM_PARISC_DELAY_H */ diff --git a/arch/parisc/include/asm/dma-mapping.h b/arch/parisc/include/asm/dma-mapping.h new file mode 100644 index 000000000..635665004 --- /dev/null +++ b/arch/parisc/include/asm/dma-mapping.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_DMA_MAPPING_H +#define _PARISC_DMA_MAPPING_H + +/* +** We need to support 4 different coherent dma models with one binary: +** +** I/O MMU consistent method dma_sync behavior +** ============= ====================== ======================= +** a) PA-7x00LC uncachable host memory flush/purge +** b) U2/Uturn cachable host memory NOP +** c) Ike/Astro cachable host memory NOP +** d) EPIC/SAGA memory on EPIC/SAGA flush/reset DMA channel +** +** PA-7[13]00LC processors have a GSC bus interface and no I/O MMU. +** +** Systems (eg PCX-T workstations) that don't fall into the above +** categories will need to modify the needed drivers to perform +** flush/purge and allocate "regular" cacheable pages for everything. +*/ + +extern const struct dma_map_ops *hppa_dma_ops; + +static inline const struct dma_map_ops *get_arch_dma_ops(void) +{ + return hppa_dma_ops; +} + +#endif diff --git a/arch/parisc/include/asm/dma.h b/arch/parisc/include/asm/dma.h new file mode 100644 index 000000000..582fb5d1a --- /dev/null +++ b/arch/parisc/include/asm/dma.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* asm/dma.h: Defines for using and allocating dma channels. + * Written by Hennus Bergman, 1992. + * High DMA channel support & info by Hannu Savolainen + * and John Boyd, Nov. 1992. + * (c) Copyright 2000, Grant Grundler + */ + +#ifndef _ASM_DMA_H +#define _ASM_DMA_H + +#include <asm/io.h> /* need byte IO */ + +#define dma_outb outb +#define dma_inb inb + +extern unsigned long pcxl_dma_start; + +/* +** DMA_CHUNK_SIZE is used by the SCSI mid-layer to break up +** (or rather not merge) DMAs into manageable chunks. +** On parisc, this is more of the software/tuning constraint +** rather than the HW. I/O MMU allocation algorithms can be +** faster with smaller sizes (to some degree). +*/ +#define DMA_CHUNK_SIZE (BITS_PER_LONG*PAGE_SIZE) + +/* The maximum address that we can perform a DMA transfer to on this platform +** New dynamic DMA interfaces should obsolete this.... +*/ +#define MAX_DMA_ADDRESS (~0UL) + +/* +** We don't have DMA channels... well V-class does but the +** Dynamic DMA Mapping interface will support them... right? :^) +** Note: this is not relevant right now for PA-RISC, but we cannot +** leave this as undefined because some things (e.g. sound) +** won't compile :-( +*/ +#define MAX_DMA_CHANNELS 8 +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ + +#define DMA_AUTOINIT 0x10 + +/* 8237 DMA controllers */ +#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ +#define IO_DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */ + +/* DMA controller registers */ +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ +#define DMA1_EXT_MODE_REG (0x400 | DMA1_MODE_REG) + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ +#define DMA2_EXT_MODE_REG (0x400 | DMA2_MODE_REG) + +static __inline__ unsigned long claim_dma_lock(void) +{ + return 0; +} + +static __inline__ void release_dma_lock(unsigned long flags) +{ +} + + +/* Get DMA residue count. After a DMA transfer, this + * should return zero. Reading this while a DMA transfer is + * still in progress will return unpredictable results. + * If called before the channel has been used, it may return 1. + * Otherwise, it returns the number of _bytes_ left to transfer. + * + * Assumes DMA flip-flop is clear. + */ +static __inline__ int get_dma_residue(unsigned int dmanr) +{ + unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE + : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE; + + /* using short to get 16-bit wrap around */ + unsigned short count; + + count = 1 + dma_inb(io_port); + count += dma_inb(io_port) << 8; + + return (dmanr<=3)? count : (count<<1); +} + +/* enable/disable a specific DMA channel */ +static __inline__ void enable_dma(unsigned int dmanr) +{ +#ifdef CONFIG_SUPERIO + if (dmanr<=3) + dma_outb(dmanr, DMA1_MASK_REG); + else + dma_outb(dmanr & 3, DMA2_MASK_REG); +#endif +} + +static __inline__ void disable_dma(unsigned int dmanr) +{ +#ifdef CONFIG_SUPERIO + if (dmanr<=3) + dma_outb(dmanr | 4, DMA1_MASK_REG); + else + dma_outb((dmanr & 3) | 4, DMA2_MASK_REG); +#endif +} + +/* reserve a DMA channel */ +#define request_dma(dmanr, device_id) (0) + +/* Clear the 'DMA Pointer Flip Flop'. + * Write 0 for LSB/MSB, 1 for MSB/LSB access. + * Use this once to initialize the FF to a known state. + * After that, keep track of it. :-) + * --- In order to do that, the DMA routines below should --- + * --- only be used while holding the DMA lock ! --- + */ +static __inline__ void clear_dma_ff(unsigned int dmanr) +{ +} + +/* set mode (above) for a specific DMA channel */ +static __inline__ void set_dma_mode(unsigned int dmanr, char mode) +{ +} + +/* Set only the page register bits of the transfer address. + * This is used for successive transfers when we know the contents of + * the lower 16 bits of the DMA current address register, but a 64k boundary + * may have been crossed. + */ +static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) +{ +} + + +/* Set transfer address & page bits for specific DMA channel. + * Assumes dma flipflop is clear. + */ +static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a) +{ +} + + +/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for + * a specific DMA channel. + * You must ensure the parameters are valid. + * NOTE: from a manual: "the number of transfers is one more + * than the initial word count"! This is taken into account. + * Assumes dma flip-flop is clear. + * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7. + */ +static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count) +{ +} + + +#define free_dma(dmanr) + +#endif /* _ASM_DMA_H */ diff --git a/arch/parisc/include/asm/dwarf.h b/arch/parisc/include/asm/dwarf.h new file mode 100644 index 000000000..526f4a792 --- /dev/null +++ b/arch/parisc/include/asm/dwarf.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2016 Helge Deller <deller@gmx.de> + */ + +#ifndef _ASM_PARISC_DWARF_H +#define _ASM_PARISC_DWARF_H + +#ifdef __ASSEMBLER__ + +#define CFI_STARTPROC .cfi_startproc +#define CFI_ENDPROC .cfi_endproc +#define CFI_DEF_CFA .cfi_def_cfa +#define CFI_REGISTER .cfi_register +#define CFI_REL_OFFSET .cfi_rel_offset +#define CFI_UNDEFINED .cfi_undefined + +#endif /* __ASSEMBLER__ */ + +#endif /* _ASM_PARISC_DWARF_H */ diff --git a/arch/parisc/include/asm/eisa_bus.h b/arch/parisc/include/asm/eisa_bus.h new file mode 100644 index 000000000..55dba9cd4 --- /dev/null +++ b/arch/parisc/include/asm/eisa_bus.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * eisa_bus.h interface between the eisa BA driver and the bus enumerator + * + * Copyright (c) 2002 Daniel Engstrom <5116@telia.com> + */ + +#ifndef ASM_EISA_H +#define ASM_EISA_H + +extern void eisa_make_irq_level(int num); +extern void eisa_make_irq_edge(int num); +extern int eisa_enumerator(unsigned long eeprom_addr, + struct resource *io_parent, + struct resource *mem_parent); +extern int eisa_eeprom_init(unsigned long addr); + +#endif diff --git a/arch/parisc/include/asm/eisa_eeprom.h b/arch/parisc/include/asm/eisa_eeprom.h new file mode 100644 index 000000000..fdac7fc94 --- /dev/null +++ b/arch/parisc/include/asm/eisa_eeprom.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * eisa_eeprom.h - provide support for EISA adapters in PA-RISC machines + * + * Copyright (c) 2001, 2002 Daniel Engstrom <5116@telia.com> + */ + +#ifndef ASM_EISA_EEPROM_H +#define ASM_EISA_EEPROM_H + +extern void __iomem *eisa_eeprom_addr; + +#define HPEE_MAX_LENGTH 0x2000 /* maximum eeprom length */ + +#define HPEE_SLOT_INFO(slot) (20+(48*slot)) + +struct eeprom_header +{ + + u_int32_t num_writes; /* number of writes */ + u_int8_t flags; /* flags, usage? */ + u_int8_t ver_maj; + u_int8_t ver_min; + u_int8_t num_slots; /* number of EISA slots in system */ + u_int16_t csum; /* checksum, I don't know how to calculate this */ + u_int8_t pad[10]; +} __attribute__ ((packed)); + + +struct eeprom_eisa_slot_info +{ + u_int32_t eisa_slot_id; + u_int32_t config_data_offset; + u_int32_t num_writes; + u_int16_t csum; + u_int16_t num_functions; + u_int16_t config_data_length; + + /* bits 0..3 are the duplicate slot id */ +#define HPEE_SLOT_INFO_EMBEDDED 0x10 +#define HPEE_SLOT_INFO_VIRTUAL 0x20 +#define HPEE_SLOT_INFO_NO_READID 0x40 +#define HPEE_SLOT_INFO_DUPLICATE 0x80 + u_int8_t slot_info; + +#define HPEE_SLOT_FEATURES_ENABLE 0x01 +#define HPEE_SLOT_FEATURES_IOCHK 0x02 +#define HPEE_SLOT_FEATURES_CFG_INCOMPLETE 0x80 + u_int8_t slot_features; + + u_int8_t ver_min; + u_int8_t ver_maj; + +#define HPEE_FUNCTION_INFO_HAVE_TYPE 0x01 +#define HPEE_FUNCTION_INFO_HAVE_MEMORY 0x02 +#define HPEE_FUNCTION_INFO_HAVE_IRQ 0x04 +#define HPEE_FUNCTION_INFO_HAVE_DMA 0x08 +#define HPEE_FUNCTION_INFO_HAVE_PORT 0x10 +#define HPEE_FUNCTION_INFO_HAVE_PORT_INIT 0x20 +/* I think there are two slighty different + * versions of the function_info field + * one int the fixed header and one optional + * in the parsed slot data area */ +#define HPEE_FUNCTION_INFO_HAVE_FUNCTION 0x01 +#define HPEE_FUNCTION_INFO_F_DISABLED 0x80 +#define HPEE_FUNCTION_INFO_CFG_FREE_FORM 0x40 + u_int8_t function_info; + +#define HPEE_FLAG_BOARD_IS_ISA 0x01 /* flag and minor version for isa board */ + u_int8_t flags; + u_int8_t pad[24]; +} __attribute__ ((packed)); + + +#define HPEE_MEMORY_MAX_ENT 9 +/* memory descriptor: byte 0 */ +#define HPEE_MEMORY_WRITABLE 0x01 +#define HPEE_MEMORY_CACHABLE 0x02 +#define HPEE_MEMORY_TYPE_MASK 0x18 +#define HPEE_MEMORY_TYPE_SYS 0x00 +#define HPEE_MEMORY_TYPE_EXP 0x08 +#define HPEE_MEMORY_TYPE_VIR 0x10 +#define HPEE_MEMORY_TYPE_OTH 0x18 +#define HPEE_MEMORY_SHARED 0x20 +#define HPEE_MEMORY_MORE 0x80 + +/* memory descriptor: byte 1 */ +#define HPEE_MEMORY_WIDTH_MASK 0x03 +#define HPEE_MEMORY_WIDTH_BYTE 0x00 +#define HPEE_MEMORY_WIDTH_WORD 0x01 +#define HPEE_MEMORY_WIDTH_DWORD 0x02 +#define HPEE_MEMORY_DECODE_MASK 0x0c +#define HPEE_MEMORY_DECODE_20BITS 0x00 +#define HPEE_MEMORY_DECODE_24BITS 0x04 +#define HPEE_MEMORY_DECODE_32BITS 0x08 +/* byte 2 and 3 are a 16bit LE value + * containing the memory size in kilobytes */ +/* byte 4,5,6 are a 24bit LE value + * containing the memory base address */ + + +#define HPEE_IRQ_MAX_ENT 7 +/* Interrupt entry: byte 0 */ +#define HPEE_IRQ_CHANNEL_MASK 0xf +#define HPEE_IRQ_TRIG_LEVEL 0x20 +#define HPEE_IRQ_MORE 0x80 +/* byte 1 seems to be unused */ + +#define HPEE_DMA_MAX_ENT 4 + +/* dma entry: byte 0 */ +#define HPEE_DMA_CHANNEL_MASK 7 +#define HPEE_DMA_SIZE_MASK 0xc +#define HPEE_DMA_SIZE_BYTE 0x0 +#define HPEE_DMA_SIZE_WORD 0x4 +#define HPEE_DMA_SIZE_DWORD 0x8 +#define HPEE_DMA_SHARED 0x40 +#define HPEE_DMA_MORE 0x80 + +/* dma entry: byte 1 */ +#define HPEE_DMA_TIMING_MASK 0x30 +#define HPEE_DMA_TIMING_ISA 0x0 +#define HPEE_DMA_TIMING_TYPEA 0x10 +#define HPEE_DMA_TIMING_TYPEB 0x20 +#define HPEE_DMA_TIMING_TYPEC 0x30 + +#define HPEE_PORT_MAX_ENT 20 +/* port entry byte 0 */ +#define HPEE_PORT_SIZE_MASK 0x1f +#define HPEE_PORT_SHARED 0x40 +#define HPEE_PORT_MORE 0x80 +/* byte 1 and 2 is a 16bit LE value + * containing the start port number */ + +#define HPEE_PORT_INIT_MAX_LEN 60 /* in bytes here */ +/* port init entry byte 0 */ +#define HPEE_PORT_INIT_WIDTH_MASK 0x3 +#define HPEE_PORT_INIT_WIDTH_BYTE 0x0 +#define HPEE_PORT_INIT_WIDTH_WORD 0x1 +#define HPEE_PORT_INIT_WIDTH_DWORD 0x2 +#define HPEE_PORT_INIT_MASK 0x4 +#define HPEE_PORT_INIT_MORE 0x80 + +#define HPEE_SELECTION_MAX_ENT 26 + +#define HPEE_TYPE_MAX_LEN 80 + +#endif diff --git a/arch/parisc/include/asm/elf.h b/arch/parisc/include/asm/elf.h new file mode 100644 index 000000000..2d73d3c3c --- /dev/null +++ b/arch/parisc/include/asm/elf.h @@ -0,0 +1,368 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASMPARISC_ELF_H +#define __ASMPARISC_ELF_H + +/* + * ELF register definitions.. + */ + +#include <linux/types.h> + +#define EM_PARISC 15 + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indices. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_HIRESERVE 255 + +#define PA_PLABEL_FDESC 0x02 /* bit set if PLABEL points to + * a function descriptor, not + * an address */ + +/* The following are PA function descriptors + * + * addr: the absolute address of the function + * gp: either the data pointer (r27) for non-PIC code or + * the PLT pointer (r19) for PIC code */ + +/* Format for the Elf32 Function descriptor */ +typedef struct elf32_fdesc { + __u32 addr; + __u32 gp; +} Elf32_Fdesc; + +/* Format for the Elf64 Function descriptor */ +typedef struct elf64_fdesc { + __u64 dummy[2]; /* used by 64-bit eBPF and tracing functions */ + __u64 addr; + __u64 gp; +} Elf64_Fdesc; + +#ifdef CONFIG_64BIT +#define Elf_Fdesc Elf64_Fdesc +#else +#define Elf_Fdesc Elf32_Fdesc +#endif /*CONFIG_64BIT*/ + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + +/* + * This yields a string that ld.so will use to load implementation + * specific libraries for optimization. This is more specific in + * intent than poking at uname or /proc/cpuinfo. + */ + +#define ELF_PLATFORM ("PARISC") + +/* + * The following definitions are those for 32-bit ELF binaries on a 32-bit + * kernel and for 64-bit binaries on a 64-bit kernel. To run 32-bit binaries + * on a 64-bit kernel, fs/compat_binfmt_elf.c defines ELF_CLASS and then + * #includes binfmt_elf.c, which then includes this file. + */ +#ifndef ELF_CLASS + +#ifdef CONFIG_64BIT +#define ELF_CLASS ELFCLASS64 +#else +#define ELF_CLASS ELFCLASS32 +#endif + +typedef unsigned long elf_greg_t; + +#define SET_PERSONALITY(ex) \ +({ \ + set_personality((current->personality & ~PER_MASK) | PER_LINUX); \ + clear_thread_flag(TIF_32BIT); \ + current->thread.map_base = DEFAULT_MAP_BASE; \ + current->thread.task_size = DEFAULT_TASK_SIZE; \ + }) + +#endif /* ! ELF_CLASS */ + +#define COMPAT_SET_PERSONALITY(ex) \ +({ \ + if ((ex).e_ident[EI_CLASS] == ELFCLASS32) { \ + set_thread_flag(TIF_32BIT); \ + current->thread.map_base = DEFAULT_MAP_BASE32; \ + current->thread.task_size = DEFAULT_TASK_SIZE32; \ + } else clear_thread_flag(TIF_32BIT); \ + }) + +/* + * Fill in general registers in a core dump. This saves pretty + * much the same registers as hp-ux, although in a different order. + * Registers marked # below are not currently saved in pt_regs, so + * we use their current values here. + * + * gr0..gr31 + * sr0..sr7 + * iaoq0..iaoq1 + * iasq0..iasq1 + * cr11 (sar) + * cr19 (iir) + * cr20 (isr) + * cr21 (ior) + * # cr22 (ipsw) + * # cr0 (recovery counter) + * # cr24..cr31 (temporary registers) + * # cr8,9,12,13 (protection IDs) + * # cr10 (scr/ccr) + * # cr15 (ext int enable mask) + * + */ + +#define ELF_CORE_COPY_REGS(dst, pt) \ + memset(dst, 0, sizeof(dst)); /* don't leak any "random" bits */ \ + { int i; \ + for (i = 0; i < 32; i++) dst[i] = pt->gr[i]; \ + for (i = 0; i < 8; i++) dst[32 + i] = pt->sr[i]; \ + } \ + dst[40] = pt->iaoq[0]; dst[41] = pt->iaoq[1]; \ + dst[42] = pt->iasq[0]; dst[43] = pt->iasq[1]; \ + dst[44] = pt->sar; dst[45] = pt->iir; \ + dst[46] = pt->isr; dst[47] = pt->ior; \ + dst[48] = mfctl(22); dst[49] = mfctl(0); \ + dst[50] = mfctl(24); dst[51] = mfctl(25); \ + dst[52] = mfctl(26); dst[53] = mfctl(27); \ + dst[54] = mfctl(28); dst[55] = mfctl(29); \ + dst[56] = mfctl(30); dst[57] = mfctl(31); \ + dst[58] = mfctl( 8); dst[59] = mfctl( 9); \ + dst[60] = mfctl(12); dst[61] = mfctl(13); \ + dst[62] = mfctl(10); dst[63] = mfctl(15); + +#define CORE_DUMP_USE_REGSET + +#define ELF_NGREG 80 /* We only need 64 at present, but leave space + for expansion. */ +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +#define ELF_NFPREG 32 +typedef double elf_fpreg_t; +typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; + +struct task_struct; + +struct pt_regs; /* forward declaration... */ + + +#define elf_check_arch(x) \ + ((x)->e_machine == EM_PARISC && (x)->e_ident[EI_CLASS] == ELF_CLASS) +#define compat_elf_check_arch(x) \ + ((x)->e_machine == EM_PARISC && (x)->e_ident[EI_CLASS] == ELFCLASS32) + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_PARISC +#define ELF_OSABI ELFOSABI_LINUX + +/* %r23 is set by ld.so to a pointer to a function which might be + registered using atexit. This provides a means for the dynamic + linker to call DT_FINI functions for shared libraries that have + been loaded before the code runs. + + So that we can use the same startup file with static executables, + we start programs with a value of 0 to indicate that there is no + such function. */ +#define ELF_PLAT_INIT(_r, load_addr) _r->gr[23] = 0 + +#define ELF_EXEC_PAGESIZE 4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. + + (2 * TASK_SIZE / 3) turns into something undefined when run through a + 32 bit preprocessor and in some cases results in the kernel trying to map + ld.so to the kernel virtual base. Use a sane value instead. /Jes + */ + +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x01000000) + +/* This yields a mask that user programs can use to figure out what + instruction set this CPU supports. This could be done in user space, + but it's not easy, and we've already done it here. */ + +#define ELF_HWCAP 0 + +#define STACK_RND_MASK 0x7ff /* 8MB of VA */ + +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 +struct linux_binprm; +extern int arch_setup_additional_pages(struct linux_binprm *bprm, + int executable_stack); +#define VDSO_AUX_ENT(a, b) NEW_AUX_ENT(a, b) +#define VDSO_CURRENT_BASE current->mm->context.vdso_base + +#define ARCH_DLINFO \ +do { \ + if (VDSO_CURRENT_BASE) { \ + NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE);\ + } \ +} while (0) + +#endif diff --git a/arch/parisc/include/asm/extable.h b/arch/parisc/include/asm/extable.h new file mode 100644 index 000000000..4ea23e3d7 --- /dev/null +++ b/arch/parisc/include/asm/extable.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_EXTABLE_H +#define __PARISC_EXTABLE_H + +#include <asm/ptrace.h> +#include <linux/compiler.h> + +/* + * The exception table consists of three addresses: + * + * - A relative address to the instruction that is allowed to fault. + * - A relative address at which the program should continue (fixup routine) + * - An asm statement which specifies which CPU register will + * receive -EFAULT when an exception happens if the lowest bit in + * the fixup address is set. + * + * Note: The register specified in the err_opcode instruction will be + * modified at runtime if a fault happens. Register %r0 will be ignored. + * + * Since relative addresses are used, 32bit values are sufficient even on + * 64bit kernel. + */ + +struct pt_regs; +int fixup_exception(struct pt_regs *regs); + +#define ARCH_HAS_RELATIVE_EXTABLE +struct exception_table_entry { + int insn; /* relative address of insn that is allowed to fault. */ + int fixup; /* relative address of fixup routine */ + int err_opcode; /* sample opcode with register which holds error code */ +}; + +#define ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr, opcode )\ + ".section __ex_table,\"aw\"\n" \ + ".align 4\n" \ + ".word (" #fault_addr " - .), (" #except_addr " - .)\n" \ + opcode "\n" \ + ".previous\n" + +/* + * ASM_EXCEPTIONTABLE_ENTRY_EFAULT() creates a special exception table entry + * (with lowest bit set) for which the fault handler in fixup_exception() will + * load -EFAULT on fault into the register specified by the err_opcode instruction, + * and zeroes the target register in case of a read fault in get_user(). + */ +#define ASM_EXCEPTIONTABLE_VAR(__err_var) \ + int __err_var = 0 +#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr, register )\ + ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1, "or %%r0,%%r0," register) + +static inline void swap_ex_entry_fixup(struct exception_table_entry *a, + struct exception_table_entry *b, + struct exception_table_entry tmp, + int delta) +{ + a->fixup = b->fixup + delta; + b->fixup = tmp.fixup - delta; + a->err_opcode = b->err_opcode; + b->err_opcode = tmp.err_opcode; +} +#define swap_ex_entry_fixup swap_ex_entry_fixup + +#endif diff --git a/arch/parisc/include/asm/fixmap.h b/arch/parisc/include/asm/fixmap.h new file mode 100644 index 000000000..9cafa449c --- /dev/null +++ b/arch/parisc/include/asm/fixmap.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_FIXMAP_H +#define _ASM_FIXMAP_H + +/* + * This file defines the locations of the fixed mappings on parisc. + * + * All of the values in this file are machine virtual addresses. + * + * All of the values in this file must be <4GB (because of assembly + * loading restrictions). If you place this region anywhere above + * __PAGE_OFFSET, you must adjust the memory map accordingly + */ + +/* + * The tmpalias region is used in kernel space to copy/clear/flush data + * from pages congruently mapped with user space. It is comprised of + * a pair regions. The size of these regions is determined by the largest + * cache aliasing boundary for machines that support equivalent aliasing. + * + * The c3750 with PA8700 processor returns an alias value of 11. This + * indicates that it has an alias boundary of 4 MB. It also supports + * non-equivalent aliasing without a performance penalty. + * + * Machines with PA8800/PA8900 processors return an alias value of 0. + * This indicates the alias boundary is unknown and may be larger than + * 16 MB. Non-equivalent aliasing is not supported. + * + * Here we assume the maximum alias boundary is 4 MB. + */ +#define TMPALIAS_SIZE_BITS 22 /* 4 MB */ +#define TMPALIAS_MAP_START ((__PAGE_OFFSET) - (2 << TMPALIAS_SIZE_BITS)) + +#define FIXMAP_SIZE (FIX_BITMAP_COUNT << PAGE_SHIFT) +#define FIXMAP_START (TMPALIAS_MAP_START - FIXMAP_SIZE) +/* This is the kernel area for all maps (vmalloc, dma etc.) most + * usually, it extends up to TMPALIAS_MAP_START. Virtual addresses + * 0..GATEWAY_PAGE_SIZE are reserved for the gateway page */ +#define KERNEL_MAP_START (GATEWAY_PAGE_SIZE) +#define KERNEL_MAP_END (FIXMAP_START) + +#ifndef __ASSEMBLER__ + + +enum fixed_addresses { + /* Support writing RO kernel text via kprobes, jump labels, etc. */ + FIX_TEXT_POKE0, + FIX_TEXT_KEXEC, + FIX_BITMAP_COUNT +}; + +extern void *parisc_vmalloc_start; +#define PCXL_DMA_MAP_SIZE (8*1024*1024) +#define VMALLOC_START ((unsigned long)parisc_vmalloc_start) +#define VMALLOC_END (KERNEL_MAP_END) + +#define __fix_to_virt(_x) (FIXMAP_START + ((_x) << PAGE_SHIFT)) + +void set_fixmap(enum fixed_addresses idx, phys_addr_t phys); +void clear_fixmap(enum fixed_addresses idx); + +#endif /*__ASSEMBLER__*/ + +#endif /*_ASM_FIXMAP_H*/ diff --git a/arch/parisc/include/asm/floppy.h b/arch/parisc/include/asm/floppy.h new file mode 100644 index 000000000..f15b69fea --- /dev/null +++ b/arch/parisc/include/asm/floppy.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Architecture specific parts of the Floppy driver + * + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * Copyright (C) 2000 Matthew Wilcox (willy a debian . org) + * Copyright (C) 2000 Dave Kennedy + */ +#ifndef __ASM_PARISC_FLOPPY_H +#define __ASM_PARISC_FLOPPY_H + +#include <linux/sizes.h> +#include <linux/vmalloc.h> + +/* + * The DMA channel used by the floppy controller cannot access data at + * addresses >= 16MB + * + * Went back to the 1MB limit, as some people had problems with the floppy + * driver otherwise. It doesn't matter much for performance anyway, as most + * floppy accesses go through the track buffer. + */ +#define _CROSS_64KB(a,s,vdma) \ + (!(vdma) && \ + ((unsigned long)(a) / SZ_64K != ((unsigned long)(a) + (s) - 1) / SZ_64K)) + +#define SW fd_routine[use_virtual_dma&1] +#define CSW fd_routine[can_use_virtual_dma & 1] + +#define fd_inb(base, reg) readb((base) + (reg)) +#define fd_outb(value, base, reg) writeb(value, (base) + (reg)) + +#define fd_request_dma() CSW._request_dma(FLOPPY_DMA,"floppy") +#define fd_free_dma() CSW._free_dma(FLOPPY_DMA) +#define fd_enable_irq() enable_irq(FLOPPY_IRQ) +#define fd_disable_irq() disable_irq(FLOPPY_IRQ) +#define fd_free_irq() free_irq(FLOPPY_IRQ, NULL) +#define fd_get_dma_residue() SW._get_dma_residue(FLOPPY_DMA) +#define fd_dma_mem_alloc(size) SW._dma_mem_alloc(size) +#define fd_dma_setup(addr, size, mode, io) SW._dma_setup(addr, size, mode, io) + +#define FLOPPY_CAN_FALLBACK_ON_NODMA + +static int virtual_dma_count=0; +static int virtual_dma_residue=0; +static char *virtual_dma_addr=0; +static int virtual_dma_mode=0; +static int doing_pdma=0; + +static void floppy_hardint(int irq, void *dev_id, struct pt_regs * regs) +{ + register unsigned char st; + +#undef TRACE_FLPY_INT + +#ifdef TRACE_FLPY_INT + static int calls=0; + static int bytes=0; + static int dma_wait=0; +#endif + if (!doing_pdma) { + floppy_interrupt(irq, dev_id, regs); + return; + } + +#ifdef TRACE_FLPY_INT + if(!calls) + bytes = virtual_dma_count; +#endif + + { + register int lcount; + register char *lptr = virtual_dma_addr; + + for (lcount = virtual_dma_count; lcount; lcount--) { + st = fd_inb(virtual_dma_port, FD_STATUS); + st &= STATUS_DMA | STATUS_READY; + if (st != (STATUS_DMA | STATUS_READY)) + break; + if (virtual_dma_mode) { + fd_outb(*lptr, virtual_dma_port, FD_DATA); + } else { + *lptr = fd_inb(virtual_dma_port, FD_DATA); + } + lptr++; + } + virtual_dma_count = lcount; + virtual_dma_addr = lptr; + st = fd_inb(virtual_dma_port, FD_STATUS); + } + +#ifdef TRACE_FLPY_INT + calls++; +#endif + if (st == STATUS_DMA) + return; + if (!(st & STATUS_DMA)) { + virtual_dma_residue += virtual_dma_count; + virtual_dma_count = 0; +#ifdef TRACE_FLPY_INT + printk("count=%x, residue=%x calls=%d bytes=%d dma_wait=%d\n", + virtual_dma_count, virtual_dma_residue, calls, bytes, + dma_wait); + calls = 0; + dma_wait=0; +#endif + doing_pdma = 0; + floppy_interrupt(irq, dev_id, regs); + return; + } +#ifdef TRACE_FLPY_INT + if (!virtual_dma_count) + dma_wait++; +#endif +} + +static void fd_disable_dma(void) +{ + if(! (can_use_virtual_dma & 1)) + disable_dma(FLOPPY_DMA); + doing_pdma = 0; + virtual_dma_residue += virtual_dma_count; + virtual_dma_count=0; +} + +static int vdma_request_dma(unsigned int dmanr, const char * device_id) +{ + return 0; +} + +static void vdma_nop(unsigned int dummy) +{ +} + + +static int vdma_get_dma_residue(unsigned int dummy) +{ + return virtual_dma_count + virtual_dma_residue; +} + + +static int fd_request_irq(void) +{ + if(can_use_virtual_dma) + return request_irq(FLOPPY_IRQ, floppy_hardint, + 0, "floppy", NULL); + else + return request_irq(FLOPPY_IRQ, floppy_interrupt, + 0, "floppy", NULL); +} + +static unsigned long dma_mem_alloc(unsigned long size) +{ + return __get_dma_pages(GFP_KERNEL, get_order(size)); +} + + +static unsigned long vdma_mem_alloc(unsigned long size) +{ + return (unsigned long) vmalloc(size); + +} + +#define nodma_mem_alloc(size) vdma_mem_alloc(size) + +static void _fd_dma_mem_free(unsigned long addr, unsigned long size) +{ + if((unsigned int) addr >= (unsigned int) high_memory) + return vfree((void *)addr); + else + free_pages(addr, get_order(size)); +} + +#define fd_dma_mem_free(addr, size) _fd_dma_mem_free(addr, size) + +static void _fd_chose_dma_mode(char *addr, unsigned long size) +{ + if(can_use_virtual_dma == 2) { + if((unsigned int) addr >= (unsigned int) high_memory || + virt_to_phys(addr) >= 0x1000000 || + _CROSS_64KB(addr, size, 0)) + use_virtual_dma = 1; + else + use_virtual_dma = 0; + } else { + use_virtual_dma = can_use_virtual_dma & 1; + } +} + +#define fd_chose_dma_mode(addr, size) _fd_chose_dma_mode(addr, size) + + +static int vdma_dma_setup(char *addr, unsigned long size, int mode, int io) +{ + doing_pdma = 1; + virtual_dma_port = io; + virtual_dma_mode = (mode == DMA_MODE_WRITE); + virtual_dma_addr = addr; + virtual_dma_count = size; + virtual_dma_residue = 0; + return 0; +} + +static int hard_dma_setup(char *addr, unsigned long size, int mode, int io) +{ +#ifdef FLOPPY_SANITY_CHECK + if (_CROSS_64KB(addr, size, use_virtual_dma & 1)) { + printk("DMA crossing 64-K boundary %p-%p\n", addr, addr+size); + return -1; + } +#endif + /* actual, physical DMA */ + doing_pdma = 0; + clear_dma_ff(FLOPPY_DMA); + set_dma_mode(FLOPPY_DMA,mode); + set_dma_addr(FLOPPY_DMA,virt_to_phys(addr)); + set_dma_count(FLOPPY_DMA,size); + enable_dma(FLOPPY_DMA); + return 0; +} + +static struct fd_routine_l { + int (*_request_dma)(unsigned int dmanr, const char * device_id); + void (*_free_dma)(unsigned int dmanr); + int (*_get_dma_residue)(unsigned int dummy); + unsigned long (*_dma_mem_alloc) (unsigned long size); + int (*_dma_setup)(char *addr, unsigned long size, int mode, int io); +} fd_routine[] = { + { + request_dma, + free_dma, + get_dma_residue, + dma_mem_alloc, + hard_dma_setup + }, + { + vdma_request_dma, + vdma_nop, + vdma_get_dma_residue, + vdma_mem_alloc, + vdma_dma_setup + } +}; + + +static int FDC1 = 0x3f0; /* Lies. Floppy controller is memory mapped, not io mapped */ +static int FDC2 = -1; + +#define FLOPPY0_TYPE 0 +#define FLOPPY1_TYPE 0 + +#define N_FDC 1 +#define N_DRIVE 8 + +#define EXTRA_FLOPPY_PARAMS + +#endif /* __ASM_PARISC_FLOPPY_H */ diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h new file mode 100644 index 000000000..8b89d2b64 --- /dev/null +++ b/arch/parisc/include/asm/ftrace.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_FTRACE_H +#define _ASM_PARISC_FTRACE_H + +#ifndef __ASSEMBLER__ +extern void mcount(void); + +#define MCOUNT_ADDR ((unsigned long)mcount) +#define MCOUNT_INSN_SIZE 4 +#define CC_USING_NOP_MCOUNT +#define ARCH_SUPPORTS_FTRACE_OPS 1 +extern unsigned long sys_call_table[]; + +extern unsigned long return_address(unsigned int); +struct ftrace_regs; +extern void ftrace_function_trampoline(unsigned long parent, + unsigned long self_addr, unsigned long org_sp_gr3, + struct ftrace_regs *fregs); + +#ifdef CONFIG_DYNAMIC_FTRACE +extern void ftrace_caller(void); + +struct dyn_arch_ftrace { +}; + +unsigned long ftrace_call_adjust(unsigned long addr); + +#endif + +#define ftrace_return_address(n) return_address(n) + +#endif /* __ASSEMBLER__ */ + +#endif /* _ASM_PARISC_FTRACE_H */ diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h new file mode 100644 index 000000000..3222206cb --- /dev/null +++ b/arch/parisc/include/asm/futex.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_FUTEX_H +#define _ASM_PARISC_FUTEX_H + +#include <linux/futex.h> +#include <linux/uaccess.h> +#include <asm/atomic.h> +#include <asm/errno.h> + +/* The following has to match the LWS code in syscall.S. We have + * 256 four-word locks. We use bits 20-27 of the futex virtual + * address for the hash index. + */ + +static inline unsigned long _futex_hash_index(unsigned long ua) +{ + return (ua >> 2) & 0x3fc; +} + +static inline void +_futex_spin_lock_irqsave(arch_spinlock_t *s, unsigned long *flags) +{ + local_irq_save(*flags); + arch_spin_lock(s); +} + +static inline void +_futex_spin_unlock_irqrestore(arch_spinlock_t *s, unsigned long *flags) +{ + arch_spin_unlock(s); + local_irq_restore(*flags); +} + +static inline int +arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) +{ + extern u32 lws_lock_start[]; + unsigned long ua = (unsigned long)uaddr; + arch_spinlock_t *s; + unsigned long flags; + int oldval, ret; + u32 tmp; + + s = (arch_spinlock_t *)&lws_lock_start[_futex_hash_index(ua)]; + _futex_spin_lock_irqsave(s, &flags); + + /* Return -EFAULT if we encounter a page fault or COW break */ + if (unlikely(get_user(oldval, uaddr) != 0)) { + ret = -EFAULT; + goto out_pagefault_enable; + } + + ret = 0; + tmp = oldval; + + switch (op) { + case FUTEX_OP_SET: + tmp = oparg; + break; + case FUTEX_OP_ADD: + tmp += oparg; + break; + case FUTEX_OP_OR: + tmp |= oparg; + break; + case FUTEX_OP_ANDN: + tmp &= ~oparg; + break; + case FUTEX_OP_XOR: + tmp ^= oparg; + break; + default: + ret = -ENOSYS; + goto out_pagefault_enable; + } + + if (unlikely(put_user(tmp, uaddr) != 0)) + ret = -EFAULT; + +out_pagefault_enable: + _futex_spin_unlock_irqrestore(s, &flags); + + if (!ret) + *oval = oldval; + + return ret; +} + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + extern u32 lws_lock_start[]; + unsigned long ua = (unsigned long)uaddr; + arch_spinlock_t *s; + u32 val; + unsigned long flags; + + if (!access_ok(uaddr, sizeof(u32))) + return -EFAULT; + + /* HPPA has no cmpxchg in hardware and therefore the + * best we can do here is use an array of locks. The + * lock selected is based on a hash of the virtual + * address of the futex. This should scale to a couple + * of CPUs. + */ + + s = (arch_spinlock_t *)&lws_lock_start[_futex_hash_index(ua)]; + _futex_spin_lock_irqsave(s, &flags); + if (unlikely(get_user(val, uaddr) != 0)) { + _futex_spin_unlock_irqrestore(s, &flags); + return -EFAULT; + } + + if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) { + _futex_spin_unlock_irqrestore(s, &flags); + return -EFAULT; + } + + *uval = val; + _futex_spin_unlock_irqrestore(s, &flags); + + return 0; +} + +#endif /*_ASM_PARISC_FUTEX_H*/ diff --git a/arch/parisc/include/asm/grfioctl.h b/arch/parisc/include/asm/grfioctl.h new file mode 100644 index 000000000..597201530 --- /dev/null +++ b/arch/parisc/include/asm/grfioctl.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Architecture specific parts of HP's STI (framebuffer) driver. + * Structures are HP-UX compatible for XFree86 usage. + * + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * Copyright (C) 2001 Helge Deller (deller a parisc-linux org) + */ + +#ifndef __ASM_PARISC_GRFIOCTL_H +#define __ASM_PARISC_GRFIOCTL_H + +/* upper 32 bits of graphics id (HP/UX identifier) */ + +#define GRFGATOR 8 +#define S9000_ID_S300 9 +#define GRFBOBCAT 9 +#define GRFCATSEYE 9 +#define S9000_ID_98720 10 +#define GRFRBOX 10 +#define S9000_ID_98550 11 +#define GRFFIREEYE 11 +#define S9000_ID_A1096A 12 +#define GRFHYPERION 12 +#define S9000_ID_FRI 13 +#define S9000_ID_98730 14 +#define GRFDAVINCI 14 +#define S9000_ID_98705 0x26C08070 /* Tigershark */ +#define S9000_ID_98736 0x26D148AB +#define S9000_ID_A1659A 0x26D1482A /* CRX 8 plane color (=ELK) */ +#define S9000_ID_ELK S9000_ID_A1659A +#define S9000_ID_A1439A 0x26D148EE /* CRX24 = CRX+ (24-plane color) */ +#define S9000_ID_A1924A 0x26D1488C /* GRX gray-scale */ +#define S9000_ID_ELM S9000_ID_A1924A +#define S9000_ID_98765 0x27480DEF +#define S9000_ID_ELK_768 0x27482101 +#define S9000_ID_STINGER 0x27A4A402 +#define S9000_ID_TIMBER 0x27F12392 /* Bushmaster (710) Graphics */ +#define S9000_ID_TOMCAT 0x27FCCB6D /* dual-headed ELK (Dual CRX) */ +#define S9000_ID_ARTIST 0x2B4DED6D /* Artist (Gecko/712 & 715) onboard Graphics */ +#define S9000_ID_HCRX 0x2BCB015A /* Hyperdrive/Hyperbowl (A4071A) Graphics */ +#define CRX24_OVERLAY_PLANES 0x920825AA /* Overlay planes on CRX24 */ + +#define CRT_ID_ELK_1024 S9000_ID_ELK_768 /* Elk 1024x768 CRX */ +#define CRT_ID_ELK_1280 S9000_ID_A1659A /* Elk 1280x1024 CRX */ +#define CRT_ID_ELK_1024DB 0x27849CA5 /* Elk 1024x768 double buffer */ +#define CRT_ID_ELK_GS S9000_ID_A1924A /* Elk 1280x1024 GreyScale */ +#define CRT_ID_CRX24 S9000_ID_A1439A /* Piranha */ +#define CRT_ID_VISUALIZE_EG 0x2D08C0A7 /* Graffiti, A4450A (built-in B132+/B160L) */ +#define CRT_ID_THUNDER 0x2F23E5FC /* Thunder 1 VISUALIZE 48*/ +#define CRT_ID_THUNDER2 0x2F8D570E /* Thunder 2 VISUALIZE 48 XP*/ +#define CRT_ID_HCRX S9000_ID_HCRX /* Hyperdrive HCRX */ +#define CRT_ID_CRX48Z S9000_ID_STINGER /* Stinger */ +#define CRT_ID_DUAL_CRX S9000_ID_TOMCAT /* Tomcat */ +#define CRT_ID_PVRX S9000_ID_98705 /* Tigershark */ +#define CRT_ID_TIMBER S9000_ID_TIMBER /* Timber (710 builtin) */ +#define CRT_ID_TVRX S9000_ID_98765 /* TVRX (gto/falcon) */ +#define CRT_ID_ARTIST S9000_ID_ARTIST /* Artist */ +#define CRT_ID_SUMMIT 0x2FC1066B /* Summit FX2, FX4, FX6 ... */ +#define CRT_ID_LEGO 0x35ACDA30 /* Lego FX5, FX10 ... */ +#define CRT_ID_PINNACLE 0x35ACDA16 /* Pinnacle FXe */ + +#endif /* __ASM_PARISC_GRFIOCTL_H */ diff --git a/arch/parisc/include/asm/hardirq.h b/arch/parisc/include/asm/hardirq.h new file mode 100644 index 000000000..1e4fbd0fd --- /dev/null +++ b/arch/parisc/include/asm/hardirq.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* hardirq.h: PA-RISC hard IRQ support. + * + * Copyright (C) 2001 Matthew Wilcox <matthew@wil.cx> + * Copyright (C) 2013 Helge Deller <deller@gmx.de> + */ + +#ifndef _PARISC_HARDIRQ_H +#define _PARISC_HARDIRQ_H + +#include <linux/cache.h> +#include <linux/threads.h> +#include <linux/irq.h> + +typedef struct { + unsigned int __softirq_pending; + unsigned int kernel_stack_usage; + unsigned int irq_stack_usage; +#ifdef CONFIG_SMP + unsigned int irq_resched_count; + unsigned int irq_call_count; +#endif + unsigned int irq_unaligned_count; + unsigned int irq_fpassist_count; + unsigned int irq_tlb_count; +} ____cacheline_aligned irq_cpustat_t; + +DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); + +#define __ARCH_IRQ_STAT +#define inc_irq_stat(member) this_cpu_inc(irq_stat.member) +#define __inc_irq_stat(member) __this_cpu_inc(irq_stat.member) +#define ack_bad_irq(irq) WARN(1, "unexpected IRQ trap at vector %02x\n", irq) + +#endif /* _PARISC_HARDIRQ_H */ diff --git a/arch/parisc/include/asm/hardware.h b/arch/parisc/include/asm/hardware.h new file mode 100644 index 000000000..a797c8753 --- /dev/null +++ b/arch/parisc/include/asm/hardware.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_HARDWARE_H +#define _PARISC_HARDWARE_H + +#include <linux/device-id/parisc.h> + +#define HWTYPE_ANY_ID PA_HWTYPE_ANY_ID +#define HVERSION_ANY_ID PA_HVERSION_ANY_ID +#define HVERSION_REV_ANY_ID PA_HVERSION_REV_ANY_ID +#define SVERSION_ANY_ID PA_SVERSION_ANY_ID + +struct hp_hardware { + unsigned int hw_type:8; /* HPHW_xxx */ + unsigned int hversion:12; + unsigned int sversion:12; + unsigned char opt; + unsigned char name[59]; /* The hardware description */ +} __packed; + +struct parisc_device; + +enum cpu_type { + pcx = 0, /* pa7000 pa 1.0 */ + pcxs = 1, /* pa7000 pa 1.1a */ + pcxt = 2, /* pa7100 pa 1.1b */ + pcxt_ = 3, /* pa7200 (t') pa 1.1c */ + pcxl = 4, /* pa7100lc pa 1.1d */ + pcxl2 = 5, /* pa7300lc pa 1.1e */ + pcxu = 6, /* pa8000 pa 2.0 */ + pcxu_ = 7, /* pa8200 (u+) pa 2.0 */ + pcxw = 8, /* pa8500 pa 2.0 */ + pcxw_ = 9, /* pa8600 (w+) pa 2.0 */ + pcxw2 = 10, /* pa8700 pa 2.0 */ + mako = 11, /* pa8800 pa 2.0 */ + mako2 = 12 /* pa8900 pa 2.0 */ +}; + +extern const char * const cpu_name_version[][2]; /* mapping from enum cpu_type to strings */ + +struct parisc_driver; + +struct io_module { + volatile uint32_t nothing; /* reg 0 */ + volatile uint32_t io_eim; + volatile uint32_t io_dc_adata; + volatile uint32_t io_ii_cdata; + volatile uint32_t io_dma_link; /* reg 4 */ + volatile uint32_t io_dma_command; + volatile uint32_t io_dma_address; + volatile uint32_t io_dma_count; + volatile uint32_t io_flex; /* reg 8 */ + volatile uint32_t io_spa_address; + volatile uint32_t reserved1[2]; + volatile uint32_t io_command; /* reg 12 */ + volatile uint32_t io_status; + volatile uint32_t io_control; + volatile uint32_t io_data; + volatile uint32_t reserved2; /* reg 16 */ + volatile uint32_t chain_addr; + volatile uint32_t sub_mask_clr; + volatile uint32_t reserved3[13]; + volatile uint32_t undefined[480]; + volatile uint32_t unpriv[512]; +}; + +struct bc_module { + volatile uint32_t unused1[12]; + volatile uint32_t io_command; + volatile uint32_t io_status; + volatile uint32_t io_control; + volatile uint32_t unused2[1]; + volatile uint32_t io_err_resp; + volatile uint32_t io_err_info; + volatile uint32_t io_err_req; + volatile uint32_t unused3[11]; + volatile uint32_t io_io_low; + volatile uint32_t io_io_high; +}; + +#define HPHW_NPROC 0 +#define HPHW_MEMORY 1 +#define HPHW_B_DMA 2 +#define HPHW_OBSOLETE 3 +#define HPHW_A_DMA 4 +#define HPHW_A_DIRECT 5 +#define HPHW_OTHER 6 +#define HPHW_BCPORT 7 +#define HPHW_CIO 8 +#define HPHW_CONSOLE 9 +#define HPHW_FIO 10 +#define HPHW_BA 11 +#define HPHW_IOA 12 +#define HPHW_BRIDGE 13 +#define HPHW_FABRIC 14 +#define HPHW_MC 15 +#define HPHW_FAULTY 31 + +/* hardware.c: */ +extern const char *parisc_hardware_description(struct parisc_device_id *id); +extern enum cpu_type parisc_get_cpu_type(unsigned long hversion); + +struct pci_dev; +struct hardware_path; + +/* drivers.c: */ +extern struct parisc_device *alloc_pa_dev(unsigned long hpa, + struct hardware_path *path); +extern int register_parisc_device(struct parisc_device *dev); +extern int register_parisc_driver(struct parisc_driver *driver); +extern int count_parisc_driver(struct parisc_driver *driver); +extern int unregister_parisc_driver(struct parisc_driver *driver); +extern void walk_central_bus(void); +extern const struct parisc_device *find_pa_parent_type(const struct parisc_device *, int); +extern void print_parisc_devices(void); +extern char *print_pa_hwpath(struct parisc_device *dev, char *path); +extern char *print_pci_hwpath(struct pci_dev *dev, char *path); +extern void get_pci_node_path(struct pci_dev *dev, struct hardware_path *path); +extern void init_parisc_bus(void); +extern struct device *hwpath_to_device(struct hardware_path *modpath); +extern void device_to_hwpath(struct device *dev, struct hardware_path *path); +extern int machine_has_merced_bus(void); + +/* inventory.c: */ +extern void do_memory_inventory(void); +extern void do_device_inventory(void); + +#endif /* _PARISC_HARDWARE_H */ diff --git a/arch/parisc/include/asm/hash.h b/arch/parisc/include/asm/hash.h new file mode 100644 index 000000000..525950ed8 --- /dev/null +++ b/arch/parisc/include/asm/hash.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_HASH_H +#define _ASM_HASH_H + +/* + * HP-PA only implements integer multiply in the FPU. However, for + * integer multiplies by constant, it has a number of shift-and-add + * (but no shift-and-subtract, sigh!) instructions that a compiler + * can synthesize a code sequence with. + * + * Unfortunately, GCC isn't very efficient at using them. For example + * it uses three instructions for "x *= 21" when only two are needed. + * But we can find a sequence manually. + */ + +#define HAVE_ARCH__HASH_32 1 + +/* + * This is a multiply by GOLDEN_RATIO_32 = 0x61C88647 optimized for the + * PA7100 pairing rules. This is an in-order 2-way superscalar processor. + * Only one instruction in a pair may be a shift (by more than 3 bits), + * but other than that, simple ALU ops (including shift-and-add by up + * to 3 bits) may be paired arbitrarily. + * + * PA8xxx processors also dual-issue ALU instructions, although with + * fewer constraints, so this schedule is good for them, too. + * + * This 6-step sequence was found by Yevgen Voronenko's implementation + * of the Hcub algorithm at http://spiral.ece.cmu.edu/mcm/gen.html. + */ +static inline u32 __attribute_const__ __hash_32(u32 x) +{ + u32 a, b, c; + + /* + * Phase 1: Compute a = (x << 19) + x, + * b = (x << 9) + a, c = (x << 23) + b. + */ + a = x << 19; /* Two shifts can't be paired */ + b = x << 9; a += x; + c = x << 23; b += a; + c += b; + /* Phase 2: Return (b<<11) + (c<<6) + (a<<3) - c */ + b <<= 11; + a += c << 3; b -= c; + return (a << 3) + b; +} + +#if BITS_PER_LONG == 64 + +#define HAVE_ARCH_HASH_64 1 + +/* + * Finding a good shift-and-add chain for GOLDEN_RATIO_64 is tricky, + * because available software for the purpose chokes on constants this + * large. (It's mostly designed for compiling FIR filter coefficients + * into FPGAs.) + * + * However, Jason Thong pointed out a work-around. The Hcub software + * (http://spiral.ece.cmu.edu/mcm/gen.html) is designed for *multiple* + * constant multiplication, and is good at finding shift-and-add chains + * which share common terms. + * + * Looking at 0x0x61C8864680B583EB in binary: + * 0110000111001000100001100100011010000000101101011000001111101011 + * \______________/ \__________/ \_______/ \________/ + * \____________________________/ \____________________/ + * you can see the non-zero bits are divided into several well-separated + * blocks. Hcub can find algorithms for those terms separately, which + * can then be shifted and added together. + * + * Dividing the input into 2, 3 or 4 blocks, Hcub can find solutions + * with 10, 9 or 8 adds, respectively, making a total of 11 for the + * whole number. + * + * Using just two large blocks, 0xC3910C8D << 31 in the high bits, + * and 0xB583EB in the low bits, produces as good an algorithm as any, + * and with one more small shift than alternatives. + * + * The high bits are a larger number and more work to compute, as well + * as needing one extra cycle to shift left 31 bits before the final + * addition, so they are the critical path for scheduling. The low bits + * can fit into the scheduling slots left over. + */ + + +/* + * This _ASSIGN(dst, src) macro performs "dst = src", but prevents GCC + * from inferring anything about the value assigned to "dest". + * + * This prevents it from mis-optimizing certain sequences. + * In particular, gcc is annoyingly eager to combine consecutive shifts. + * Given "x <<= 19; y += x; z += x << 1;", GCC will turn this into + * "y += x << 19; z += x << 20;" even though the latter sequence needs + * an additional instruction and temporary register. + * + * Because no actual assembly code is generated, this construct is + * usefully portable across all GCC platforms, and so can be test-compiled + * on non-PA systems. + * + * In two places, additional unused input dependencies are added. This + * forces GCC's scheduling so it does not rearrange instructions too much. + * Because the PA-8xxx is out of order, I'm not sure how much this matters, + * but why make it more difficult for the processor than necessary? + */ +#define _ASSIGN(dst, src, ...) asm("" : "=r" (dst) : "0" (src), ##__VA_ARGS__) + +/* + * Multiply by GOLDEN_RATIO_64 = 0x0x61C8864680B583EB using a heavily + * optimized shift-and-add sequence. + * + * Without the final shift, the multiply proper is 19 instructions, + * 10 cycles and uses only 4 temporaries. Whew! + * + * You are not expected to understand this. + */ +static __always_inline u32 __attribute_const__ +hash_64(u64 a, unsigned int bits) +{ + u64 b, c, d; + + /* + * Encourage GCC to move a dynamic shift to %sar early, + * thereby freeing up an additional temporary register. + */ + if (!__builtin_constant_p(bits)) + asm("" : "=q" (bits) : "0" (64 - bits)); + else + bits = 64 - bits; + + _ASSIGN(b, a*5); c = a << 13; + b = (b << 2) + a; _ASSIGN(d, a << 17); + a = b + (a << 1); c += d; + d = a << 10; _ASSIGN(a, a << 19); + d = a - d; _ASSIGN(a, a << 4, "X" (d)); + c += b; a += b; + d -= c; c += a << 1; + a += c << 3; _ASSIGN(b, b << (7+31), "X" (c), "X" (d)); + a <<= 31; b += d; + a += b; + return a >> bits; +} +#undef _ASSIGN /* We're a widely-used header file, so don't litter! */ + +#endif /* BITS_PER_LONG == 64 */ + +#endif /* _ASM_HASH_H */ diff --git a/arch/parisc/include/asm/hugetlb.h b/arch/parisc/include/asm/hugetlb.h new file mode 100644 index 000000000..21e9ace17 --- /dev/null +++ b/arch/parisc/include/asm/hugetlb.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC64_HUGETLB_H +#define _ASM_PARISC64_HUGETLB_H + +#include <asm/page.h> + +#define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte, unsigned long sz); + +#define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR +pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long sz); + +#define __HAVE_ARCH_HUGE_PTEP_CLEAR_FLUSH +static inline pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ + return *ptep; +} + +#define __HAVE_ARCH_HUGE_PTEP_SET_WRPROTECT +void huge_ptep_set_wrprotect(struct mm_struct *mm, + unsigned long addr, pte_t *ptep); + +#define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS +int huge_ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + pte_t pte, int dirty); + +#include <asm-generic/hugetlb.h> + +#endif /* _ASM_PARISC64_HUGETLB_H */ diff --git a/arch/parisc/include/asm/io.h b/arch/parisc/include/asm/io.h new file mode 100644 index 000000000..f01ad3ad6 --- /dev/null +++ b/arch/parisc/include/asm/io.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_IO_H +#define _ASM_IO_H + +#include <linux/types.h> +#include <linux/pgtable.h> + +#define virt_to_phys(a) ((unsigned long)__pa(a)) +#define phys_to_virt(a) __va(a) + +static inline unsigned long isa_bus_to_virt(unsigned long addr) { + BUG(); + return 0; +} + +static inline unsigned long isa_virt_to_bus(void *addr) { + BUG(); + return 0; +} + +/* + * Memory mapped I/O + * + * readX()/writeX() do byteswapping and take an ioremapped address + * __raw_readX()/__raw_writeX() don't byteswap and take an ioremapped address. + * gsc_*() don't byteswap and operate on physical addresses; + * eg dev->hpa or 0xfee00000. + */ + +static inline unsigned char gsc_readb(unsigned long addr) +{ + long flags; + unsigned char ret; + + __asm__ __volatile__( + " rsm %3,%0\n" + " ldbx 0(%2),%1\n" + " mtsm %0\n" + : "=&r" (flags), "=r" (ret) : "r" (addr), "i" (PSW_SM_D) ); + + return ret; +} + +static inline unsigned short gsc_readw(unsigned long addr) +{ + long flags; + unsigned short ret; + + __asm__ __volatile__( + " rsm %3,%0\n" + " ldhx 0(%2),%1\n" + " mtsm %0\n" + : "=&r" (flags), "=r" (ret) : "r" (addr), "i" (PSW_SM_D) ); + + return ret; +} + +static inline unsigned int gsc_readl(unsigned long addr) +{ + u32 ret; + + __asm__ __volatile__( + " ldwax 0(%1),%0\n" + : "=r" (ret) : "r" (addr) ); + + return ret; +} + +static inline unsigned long long gsc_readq(unsigned long addr) +{ + unsigned long long ret; + +#ifdef CONFIG_64BIT + __asm__ __volatile__( + " ldda 0(%1),%0\n" + : "=r" (ret) : "r" (addr) ); +#else + /* two reads may have side effects.. */ + ret = ((u64) gsc_readl(addr)) << 32; + ret |= gsc_readl(addr+4); +#endif + return ret; +} + +static inline void gsc_writeb(unsigned char val, unsigned long addr) +{ + long flags; + __asm__ __volatile__( + " rsm %3,%0\n" + " stbs %1,0(%2)\n" + " mtsm %0\n" + : "=&r" (flags) : "r" (val), "r" (addr), "i" (PSW_SM_D) ); +} + +static inline void gsc_writew(unsigned short val, unsigned long addr) +{ + long flags; + __asm__ __volatile__( + " rsm %3,%0\n" + " sths %1,0(%2)\n" + " mtsm %0\n" + : "=&r" (flags) : "r" (val), "r" (addr), "i" (PSW_SM_D) ); +} + +static inline void gsc_writel(unsigned int val, unsigned long addr) +{ + __asm__ __volatile__( + " stwas %0,0(%1)\n" + : : "r" (val), "r" (addr) ); +} + +static inline void gsc_writeq(unsigned long long val, unsigned long addr) +{ +#ifdef CONFIG_64BIT + __asm__ __volatile__( + " stda %0,0(%1)\n" + : : "r" (val), "r" (addr) ); +#else + /* two writes may have side effects.. */ + gsc_writel(val >> 32, addr); + gsc_writel(val, addr+4); +#endif +} + +/* + * The standard PCI ioremap interfaces + */ +#define ioremap_prot ioremap_prot + +#define _PAGE_IOREMAP (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | \ + _PAGE_ACCESSED | _PAGE_NO_CACHE) + +#define ioremap_wc(addr, size) \ + ioremap_prot((addr), (size), __pgprot(_PAGE_IOREMAP)) + +#define pci_iounmap pci_iounmap + +/* Port-space IO */ + +#define inb_p inb +#define inw_p inw +#define inl_p inl +#define outb_p outb +#define outw_p outw +#define outl_p outl + +extern unsigned char eisa_in8(unsigned short port); +extern unsigned short eisa_in16(unsigned short port); +extern unsigned int eisa_in32(unsigned short port); +extern void eisa_out8(unsigned char data, unsigned short port); +extern void eisa_out16(unsigned short data, unsigned short port); +extern void eisa_out32(unsigned int data, unsigned short port); + +#if defined(CONFIG_PCI) +extern unsigned char inb(int addr); +extern unsigned short inw(int addr); +extern unsigned int inl(int addr); +extern void outb(unsigned char b, int addr); +extern void outw(unsigned short b, int addr); +extern void outl(unsigned int b, int addr); +#define inb inb +#define inw inw +#define inl inl +#define outb outb +#define outw outw +#define outl outl +#elif defined(CONFIG_EISA) +#define inb eisa_in8 +#define inw eisa_in16 +#define inl eisa_in32 +#define outb eisa_out8 +#define outw eisa_out16 +#define outl eisa_out32 +#else +static inline char inb(unsigned long addr) +{ + BUG(); + return -1; +} + +static inline short inw(unsigned long addr) +{ + BUG(); + return -1; +} + +static inline int inl(unsigned long addr) +{ + BUG(); + return -1; +} +#define inb inb +#define inw inw +#define inl inl +#define outb(x, y) ({(void)(x); (void)(y); BUG(); 0;}) +#define outw(x, y) ({(void)(x); (void)(y); BUG(); 0;}) +#define outl(x, y) ({(void)(x); (void)(y); BUG(); 0;}) +#endif + +/* + * String versions of in/out ops: + */ +extern void insb (unsigned long port, void *dst, unsigned long count); +extern void insw (unsigned long port, void *dst, unsigned long count); +extern void insl (unsigned long port, void *dst, unsigned long count); +extern void outsb (unsigned long port, const void *src, unsigned long count); +extern void outsw (unsigned long port, const void *src, unsigned long count); +extern void outsl (unsigned long port, const void *src, unsigned long count); +#define insb insb +#define insw insw +#define insl insl +#define outsb outsb +#define outsw outsw +#define outsl outsl + +/* IO Port space is : BBiiii where BB is HBA number. */ +#define IO_SPACE_LIMIT 0x00ffffff + +/* PA machines have an MM I/O space from 0xf0000000-0xffffffff in 32 + * bit mode and from 0xfffffffff0000000-0xfffffffffffffff in 64 bit + * mode (essentially just sign extending. This macro takes in a 32 + * bit I/O address (still with the leading f) and outputs the correct + * value for either 32 or 64 bit mode */ +#define F_EXTEND(x) ((unsigned long)((x) | (0xffffffff00000000ULL))) + +#ifdef CONFIG_64BIT +extern u64 ioread64(const void __iomem *addr); +extern u64 ioread64be(const void __iomem *addr); +#define ioread64 ioread64 +#define ioread64be ioread64be + +extern void iowrite64(u64 val, void __iomem *addr); +extern void iowrite64be(u64 val, void __iomem *addr); +#define iowrite64 iowrite64 +#define iowrite64be iowrite64be +#endif + +extern void __iomem *ioport_map(unsigned long port, unsigned int nr); +extern void ioport_unmap(void __iomem *); +#define ioport_map ioport_map +#define ioport_unmap ioport_unmap + +extern unsigned int ioread8(const void __iomem *); +extern unsigned int ioread16(const void __iomem *); +extern unsigned int ioread16be(const void __iomem *); +extern unsigned int ioread32(const void __iomem *); +extern unsigned int ioread32be(const void __iomem *); +#define ioread8 ioread8 +#define ioread16 ioread16 +#define ioread32 ioread32 +#define ioread16be ioread16be +#define ioread32be ioread32be + +extern void iowrite8(u8, void __iomem *); +extern void iowrite16(u16, void __iomem *); +extern void iowrite16be(u16, void __iomem *); +extern void iowrite32(u32, void __iomem *); +extern void iowrite32be(u32, void __iomem *); +#define iowrite8 iowrite8 +#define iowrite16 iowrite16 +#define iowrite32 iowrite32 +#define iowrite16be iowrite16be +#define iowrite32be iowrite32be + +extern void ioread8_rep(const void __iomem *port, void *buf, unsigned long count); +extern void ioread16_rep(const void __iomem *port, void *buf, unsigned long count); +extern void ioread32_rep(const void __iomem *port, void *buf, unsigned long count); +#define ioread8_rep ioread8_rep +#define ioread16_rep ioread16_rep +#define ioread32_rep ioread32_rep + +extern void iowrite8_rep(void __iomem *port, const void *buf, unsigned long count); +extern void iowrite16_rep(void __iomem *port, const void *buf, unsigned long count); +extern void iowrite32_rep(void __iomem *port, const void *buf, unsigned long count); +#define iowrite8_rep iowrite8_rep +#define iowrite16_rep iowrite16_rep +#define iowrite32_rep iowrite32_rep + +extern int devmem_is_allowed(unsigned long pfn); + +#include <asm-generic/io.h> + +#endif diff --git a/arch/parisc/include/asm/irq.h b/arch/parisc/include/asm/irq.h new file mode 100644 index 000000000..378f63c40 --- /dev/null +++ b/arch/parisc/include/asm/irq.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * include/asm-parisc/irq.h + * + * Copyright 2005 Matthew Wilcox <matthew@wil.cx> + */ + +#ifndef _ASM_PARISC_IRQ_H +#define _ASM_PARISC_IRQ_H + +#include <linux/cpumask.h> +#include <asm/types.h> + +#define NO_IRQ (-1) + +#ifdef CONFIG_GSC +#define GSC_IRQ_BASE 16 +#define GSC_IRQ_MAX 63 +#define CPU_IRQ_BASE 64 +#else +#define CPU_IRQ_BASE 16 +#endif + +#define TIMER_IRQ (CPU_IRQ_BASE + 0) +#define IPI_IRQ (CPU_IRQ_BASE + 1) +#define CPU_IRQ_MAX (CPU_IRQ_BASE + (BITS_PER_LONG - 1)) + +#define NR_IRQS (CPU_IRQ_MAX + 1) + +static __inline__ int irq_canonicalize(int irq) +{ + return (irq == 2) ? 9 : irq; +} + +struct irq_chip; +struct irq_data; + +void cpu_ack_irq(struct irq_data *d); +void cpu_eoi_irq(struct irq_data *d); + +extern int txn_alloc_irq(unsigned int nbits); +extern int txn_claim_irq(int); +extern unsigned int txn_alloc_data(unsigned int); +extern unsigned long txn_alloc_addr(unsigned int); +extern unsigned long txn_affinity_addr(unsigned int irq, int cpu); + +extern int cpu_claim_irq(unsigned int irq, struct irq_chip *, void *); +extern int cpu_check_affinity(struct irq_data *d, const struct cpumask *dest); + +#endif /* _ASM_PARISC_IRQ_H */ diff --git a/arch/parisc/include/asm/irqflags.h b/arch/parisc/include/asm/irqflags.h new file mode 100644 index 000000000..00fd87724 --- /dev/null +++ b/arch/parisc/include/asm/irqflags.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_IRQFLAGS_H +#define __PARISC_IRQFLAGS_H + +#include <linux/types.h> +#include <asm/psw.h> + +static inline unsigned long arch_local_save_flags(void) +{ + unsigned long flags; + asm volatile("ssm 0, %0" : "=r" (flags) : : "memory"); + return flags; +} + +static inline void arch_local_irq_disable(void) +{ + asm volatile("rsm %0,%%r0\n" : : "i" (PSW_I) : "memory"); +} + +static inline void arch_local_irq_enable(void) +{ + asm volatile("ssm %0,%%r0\n" : : "i" (PSW_I) : "memory"); +} + +static inline unsigned long arch_local_irq_save(void) +{ + unsigned long flags; + asm volatile("rsm %1,%0" : "=r" (flags) : "i" (PSW_I) : "memory"); + return flags; +} + +static inline void arch_local_irq_restore(unsigned long flags) +{ + /* warn if IRQs are on although they should be off */ + if (IS_ENABLED(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK)) + if (arch_local_save_flags() & PSW_I) + asm volatile("break 6,6\n"); /* SPINLOCK_BREAK_INSN */ + + asm volatile("mtsm %0" : : "r" (flags) : "memory"); +} + +static inline bool arch_irqs_disabled_flags(unsigned long flags) +{ + return (flags & PSW_I) == 0; +} + +static inline bool arch_irqs_disabled(void) +{ + return arch_irqs_disabled_flags(arch_local_save_flags()); +} + +#endif /* __PARISC_IRQFLAGS_H */ diff --git a/arch/parisc/include/asm/jump_label.h b/arch/parisc/include/asm/jump_label.h new file mode 100644 index 000000000..f325ae3c6 --- /dev/null +++ b/arch/parisc/include/asm/jump_label.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_JUMP_LABEL_H +#define _ASM_PARISC_JUMP_LABEL_H + +#ifndef __ASSEMBLER__ + +#include <linux/types.h> +#include <linux/stringify.h> +#include <asm/assembly.h> + +#define JUMP_LABEL_NOP_SIZE 4 + +static __always_inline bool arch_static_branch(struct static_key *key, bool branch) +{ + asm goto("1:\n\t" + "nop\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".align %1\n\t" + ".word 1b - ., %l[l_yes] - .\n\t" + __stringify(ASM_ULONG_INSN) " %c0 - .\n\t" + ".popsection\n\t" + : : "i" (&((char *)key)[branch]), "i" (sizeof(long)) + : : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) +{ + asm goto("1:\n\t" + "b,n %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".align %1\n\t" + ".word 1b - ., %l[l_yes] - .\n\t" + __stringify(ASM_ULONG_INSN) " %c0 - .\n\t" + ".popsection\n\t" + : : "i" (&((char *)key)[branch]), "i" (sizeof(long)) + : : l_yes); + + return false; +l_yes: + return true; +} + +#endif /* __ASSEMBLER__ */ +#endif diff --git a/arch/parisc/include/asm/kbdleds.h b/arch/parisc/include/asm/kbdleds.h new file mode 100644 index 000000000..50fcce810 --- /dev/null +++ b/arch/parisc/include/asm/kbdleds.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_KBDLEDS_H +#define _ASM_PARISC_KBDLEDS_H + +/* + * On HIL keyboards of PARISC machines there is no NumLock key and + * everyone expects the keypad to be used for numbers. That's why + * we can safely turn on the NUMLOCK bit. + */ + +static inline int kbd_defleds(void) +{ +#if defined(CONFIG_KEYBOARD_HIL) || defined(CONFIG_KEYBOARD_HIL_OLD) + return 1 << VC_NUMLOCK; +#else + return 0; +#endif +} + +#endif /* _ASM_PARISC_KBDLEDS_H */ diff --git a/arch/parisc/include/asm/kexec.h b/arch/parisc/include/asm/kexec.h new file mode 100644 index 000000000..bf31e2d50 --- /dev/null +++ b/arch/parisc/include/asm/kexec.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_KEXEC_H +#define _ASM_PARISC_KEXEC_H + +/* Maximum physical address we can use pages from */ +#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) +/* Maximum address we can reach in physical address mode */ +#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) +/* Maximum address we can use for the control code buffer */ +#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL) + +#define KEXEC_CONTROL_PAGE_SIZE 4096 + +#define KEXEC_ARCH KEXEC_ARCH_PARISC +#define ARCH_HAS_KIMAGE_ARCH + +#ifndef __ASSEMBLER__ + +struct kimage_arch { + unsigned long initrd_start; + unsigned long initrd_end; + unsigned long cmdline; +}; + +static inline void crash_setup_regs(struct pt_regs *newregs, + struct pt_regs *oldregs) +{ + /* Dummy implementation for now */ +} + +#endif /* __ASSEMBLER__ */ + +#endif /* _ASM_PARISC_KEXEC_H */ diff --git a/arch/parisc/include/asm/kfence.h b/arch/parisc/include/asm/kfence.h new file mode 100644 index 000000000..6259e5ac1 --- /dev/null +++ b/arch/parisc/include/asm/kfence.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PA-RISC KFENCE support. + * + * Copyright (C) 2021, Helge Deller <deller@gmx.de> + */ + +#ifndef _ASM_PARISC_KFENCE_H +#define _ASM_PARISC_KFENCE_H + +#include <linux/kfence.h> + +#include <asm/pgtable.h> +#include <asm/tlbflush.h> + +static inline bool arch_kfence_init_pool(void) +{ + return true; +} + +/* Protect the given page and flush TLB. */ +static inline bool kfence_protect_page(unsigned long addr, bool protect) +{ + pte_t *pte = virt_to_kpte(addr); + + if (WARN_ON(!pte)) + return false; + + /* + * We need to avoid IPIs, as we may get KFENCE allocations or faults + * with interrupts disabled. + */ + + if (protect) + set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT)); + else + set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT)); + + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); + + return true; +} + +#endif /* _ASM_PARISC_KFENCE_H */ diff --git a/arch/parisc/include/asm/kgdb.h b/arch/parisc/include/asm/kgdb.h new file mode 100644 index 000000000..9ece98bc6 --- /dev/null +++ b/arch/parisc/include/asm/kgdb.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PA-RISC KGDB support + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + * + */ + +#ifndef __PARISC_KGDB_H__ +#define __PARISC_KGDB_H__ + +#define BREAK_INSTR_SIZE 4 +#define PARISC_KGDB_COMPILED_BREAK_INSN 0x3ffc01f +#define PARISC_KGDB_BREAK_INSN 0x3ffa01f + + +#define NUMREGBYTES sizeof(struct parisc_gdb_regs) +#define BUFMAX 4096 + +#define KGDB_MAX_BREAKPOINTS 40 + +#define CACHE_FLUSH_IS_SAFE 1 + +#ifndef __ASSEMBLER__ + +static inline void arch_kgdb_breakpoint(void) +{ + asm(".word %0" : : "i"(PARISC_KGDB_COMPILED_BREAK_INSN) : "memory"); +} + +struct parisc_gdb_regs { + unsigned long gpr[32]; + unsigned long sar; + unsigned long iaoq_f; + unsigned long iasq_f; + unsigned long iaoq_b; + unsigned long iasq_b; + unsigned long eiem; + unsigned long iir; + unsigned long isr; + unsigned long ior; + unsigned long ipsw; + unsigned long __unused0; + unsigned long sr4; + unsigned long sr0; + unsigned long sr1; + unsigned long sr2; + unsigned long sr3; + unsigned long sr5; + unsigned long sr6; + unsigned long sr7; + unsigned long cr0; + unsigned long pid1; + unsigned long pid2; + unsigned long scrccr; + unsigned long pid3; + unsigned long pid4; + unsigned long cr24; + unsigned long cr25; + unsigned long cr26; + unsigned long cr27; + unsigned long cr28; + unsigned long cr29; + unsigned long cr30; + + u64 fr[32]; +}; + +#endif +#endif diff --git a/arch/parisc/include/asm/kprobes.h b/arch/parisc/include/asm/kprobes.h new file mode 100644 index 000000000..0f42f5c8e --- /dev/null +++ b/arch/parisc/include/asm/kprobes.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * arch/parisc/include/asm/kprobes.h + * + * PA-RISC kprobes implementation + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + */ + +#ifndef _PARISC_KPROBES_H +#define _PARISC_KPROBES_H + +#include <asm-generic/kprobes.h> + +#ifdef CONFIG_KPROBES + +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/notifier.h> + +#define PARISC_KPROBES_BREAK_INSN 0x3ff801f +#define PARISC_KPROBES_BREAK_INSN2 0x3ff801e +#define __ARCH_WANT_KPROBES_INSN_SLOT +#define MAX_INSN_SIZE 2 + +typedef u32 kprobe_opcode_t; +struct kprobe; + +void arch_remove_kprobe(struct kprobe *p); + +#define flush_insn_slot(p) \ + flush_icache_range((unsigned long)&(p)->ainsn.insn[0], \ + (unsigned long)&(p)->ainsn.insn[0] + \ + MAX_INSN_SIZE*sizeof(kprobe_opcode_t)) + +#define kretprobe_blacklist_size 0 + +struct arch_specific_insn { + kprobe_opcode_t *insn; +}; + +struct prev_kprobe { + struct kprobe *kp; + unsigned long status; +}; + +struct kprobe_ctlblk { + unsigned int kprobe_status; + struct prev_kprobe prev_kprobe; + unsigned long iaoq[2]; +}; + +int __kprobes parisc_kprobe_break_handler(struct pt_regs *regs); +int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs); +static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + return 0; +} + +#endif /* CONFIG_KPROBES */ +#endif /* _PARISC_KPROBES_H */ diff --git a/arch/parisc/include/asm/ldcw.h b/arch/parisc/include/asm/ldcw.h new file mode 100644 index 000000000..47ebc4c91 --- /dev/null +++ b/arch/parisc/include/asm/ldcw.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_LDCW_H +#define __PARISC_LDCW_H + +/* Because kmalloc only guarantees 8-byte alignment for kmalloc'd data, + and GCC only guarantees 8-byte alignment for stack locals, we can't + be assured of 16-byte alignment for atomic lock data even if we + specify "__attribute ((aligned(16)))" in the type declaration. So, + we use a struct containing an array of four ints for the atomic lock + type and dynamically select the 16-byte aligned int from the array + for the semaphore. */ + +/* From: "Jim Hull" <jim.hull of hp.com> + I've attached a summary of the change, but basically, for PA 2.0, as + long as the ",CO" (coherent operation) completer is implemented, then the + 16-byte alignment requirement for ldcw and ldcd is relaxed, and instead + they only require "natural" alignment (4-byte for ldcw, 8-byte for + ldcd). + + Although the cache control hint is accepted by all PA 2.0 processors, + it is only implemented on PA8800/PA8900 CPUs. Prior PA8X00 CPUs still + require 16-byte alignment. If the address is unaligned, the operation + of the instruction is undefined. The ldcw instruction does not generate + unaligned data reference traps so misaligned accesses are not detected. + This hid the problem for years. So, restore the 16-byte alignment dropped + by Kyle McMartin in "Remove __ldcw_align for PA-RISC 2.0 processors". */ + +#define __PA_LDCW_ALIGNMENT 16 +#define __ldcw_align(a) ({ \ + unsigned long __ret = (unsigned long) &(a)->lock[0]; \ + __ret = (__ret + __PA_LDCW_ALIGNMENT - 1) \ + & ~(__PA_LDCW_ALIGNMENT - 1); \ + (volatile unsigned int *) __ret; \ +}) + +#ifdef CONFIG_PA20 +#define __LDCW "ldcw,co" +#else +#define __LDCW "ldcw" +#endif + +/* LDCW, the only atomic read-write operation PA-RISC has. *sigh*. + We don't explicitly expose that "*a" may be written as reload + fails to find a register in class R1_REGS when "a" needs to be + reloaded when generating 64-bit PIC code. Instead, we clobber + memory to indicate to the compiler that the assembly code reads + or writes to items other than those listed in the input and output + operands. This may pessimize the code somewhat but __ldcw is + usually used within code blocks surrounded by memory barriers. */ +#define __ldcw(a) ({ \ + unsigned __ret; \ + __asm__ __volatile__(__LDCW " 0(%1),%0" \ + : "=r" (__ret) : "r" (a) : "memory"); \ + __ret; \ +}) + +#ifdef CONFIG_SMP +# define __lock_aligned __section(".data..lock_aligned") __aligned(16) +#endif + +#endif /* __PARISC_LDCW_H */ diff --git a/arch/parisc/include/asm/led.h b/arch/parisc/include/asm/led.h new file mode 100644 index 000000000..0aea47eff --- /dev/null +++ b/arch/parisc/include/asm/led.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef LED_H +#define LED_H + +#define LED7 0x80 /* top (or furthest right) LED */ +#define LED6 0x40 +#define LED5 0x20 +#define LED4 0x10 +#define LED3 0x08 +#define LED2 0x04 +#define LED1 0x02 +#define LED0 0x01 /* bottom (or furthest left) LED */ + +#define LED_LAN_RCV LED0 /* for LAN receive activity */ +#define LED_LAN_TX LED1 /* for LAN transmit activity */ +#define LED_DISK_IO LED2 /* for disk activity */ +#define LED_HEARTBEAT LED3 /* heartbeat */ + +/* values for pdc_chassis_lcd_info_ret_block.model: */ +#define DISPLAY_MODEL_LCD 0 /* KittyHawk LED or LCD */ +#define DISPLAY_MODEL_NONE 1 /* no LED or LCD */ +#define DISPLAY_MODEL_LASI 2 /* LASI style 8 bit LED */ +#define DISPLAY_MODEL_OLD_ASP 0x7F /* faked: ASP style 8 x 1 bit LED (only very old ASP versions) */ + +#define LED_CMD_REG_NONE 0 /* NULL == no addr for the cmd register */ + +/* register_led_driver() */ +int register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg); + +#ifdef CONFIG_CHASSIS_LCD_LED +/* writes a string to the LCD display (if possible on this h/w) */ +void lcd_print(const char *str); +#else +#define lcd_print(str) do { } while (0) +#endif + +#endif /* LED_H */ diff --git a/arch/parisc/include/asm/linkage.h b/arch/parisc/include/asm/linkage.h new file mode 100644 index 000000000..d4cad492b --- /dev/null +++ b/arch/parisc/include/asm/linkage.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_PARISC_LINKAGE_H +#define __ASM_PARISC_LINKAGE_H + +#include <asm/dwarf.h> + +#ifndef __ALIGN +#define __ALIGN .align 4 +#define __ALIGN_STR ".align 4" +#endif + +/* + * In parisc assembly a semicolon marks a comment while a + * exclamation mark is used to separate independent lines. + */ +#define ASM_NL ! + +#ifdef __ASSEMBLER__ + +#define ENTRY(name) \ + ALIGN !\ +name: ASM_NL\ + .export name + +#define ENTRY_CFI(name, ...) \ + ENTRY(name) ASM_NL\ + .proc ASM_NL\ + .callinfo __VA_ARGS__ ASM_NL\ + .entry ASM_NL\ + CFI_STARTPROC + +#define ENDPROC_CFI(name) \ + CFI_ENDPROC ASM_NL\ + .exit ASM_NL\ + .procend ASM_NL\ + ENDPROC(name) + +#endif /* __ASSEMBLER__ */ + +#endif /* __ASM_PARISC_LINKAGE_H */ diff --git a/arch/parisc/include/asm/mman.h b/arch/parisc/include/asm/mman.h new file mode 100644 index 000000000..663f587dc --- /dev/null +++ b/arch/parisc/include/asm/mman.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MMAN_H__ +#define __ASM_MMAN_H__ + +#include <linux/fs.h> +#include <uapi/asm/mman.h> + +/* PARISC cannot allow mdwe as it needs writable stacks */ +static inline bool arch_memory_deny_write_exec_supported(void) +{ + return false; +} +#define arch_memory_deny_write_exec_supported arch_memory_deny_write_exec_supported + +static inline unsigned long arch_calc_vm_flag_bits(struct file *file, unsigned long flags) +{ + /* + * The stack on parisc grows upwards, so if userspace requests memory + * for a stack, mark it with VM_GROWSUP so that the stack expansion in + * the fault handler will work. + */ + if (flags & MAP_STACK) + return VM_GROWSUP; + + return 0; +} +#define arch_calc_vm_flag_bits(file, flags) arch_calc_vm_flag_bits(file, flags) + +#endif /* __ASM_MMAN_H__ */ diff --git a/arch/parisc/include/asm/mmu.h b/arch/parisc/include/asm/mmu.h new file mode 100644 index 000000000..44fd062b6 --- /dev/null +++ b/arch/parisc/include/asm/mmu.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_MMU_H_ +#define _PARISC_MMU_H_ + +typedef struct { + unsigned long space_id; + unsigned long vdso_base; +} mm_context_t; + +#endif /* _PARISC_MMU_H_ */ diff --git a/arch/parisc/include/asm/mmu_context.h b/arch/parisc/include/asm/mmu_context.h new file mode 100644 index 000000000..c9187fe83 --- /dev/null +++ b/arch/parisc/include/asm/mmu_context.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_MMU_CONTEXT_H +#define __PARISC_MMU_CONTEXT_H + +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/atomic.h> +#include <linux/spinlock.h> +#include <asm-generic/mm_hooks.h> + +/* on PA-RISC, we actually have enough contexts to justify an allocator + * for them. prumpf */ + +extern unsigned long alloc_sid(void); +extern void free_sid(unsigned long); + +#define init_new_context init_new_context +static inline int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + BUG_ON(atomic_read(&mm->mm_users) != 1); + + mm->context.space_id = alloc_sid(); + return 0; +} + +#define destroy_context destroy_context +static inline void +destroy_context(struct mm_struct *mm) +{ + free_sid(mm->context.space_id); + mm->context.space_id = 0; +} + +static inline unsigned long __space_to_prot(mm_context_t context) +{ +#if SPACEID_SHIFT == 0 + return context.space_id << 1; +#else + return context.space_id >> (SPACEID_SHIFT - 1); +#endif +} + +static inline void load_context(mm_context_t context) +{ + mtsp(context.space_id, SR_USER); + mtctl(__space_to_prot(context), 8); +} + +static inline void switch_mm_irqs_off(struct mm_struct *prev, + struct mm_struct *next, struct task_struct *tsk) +{ + if (prev != next) { +#ifdef CONFIG_TLB_PTLOCK + /* put physical address of page_table_lock in cr28 (tr4) + for TLB faults */ + spinlock_t *pgd_lock = &next->page_table_lock; + mtctl(__pa(__ldcw_align(&pgd_lock->rlock.raw_lock)), 28); +#endif + mtctl(__pa(next->pgd), 25); + load_context(next->context); + } +} + +static inline void switch_mm(struct mm_struct *prev, + struct mm_struct *next, struct task_struct *tsk) +{ + unsigned long flags; + + if (prev == next) + return; + + local_irq_save(flags); + switch_mm_irqs_off(prev, next, tsk); + local_irq_restore(flags); +} +#define switch_mm_irqs_off switch_mm_irqs_off + +#define activate_mm activate_mm +static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) +{ + /* + * Activate_mm is our one chance to allocate a space id + * for a new mm created in the exec path. There's also + * some lazy tlb stuff, which is currently dead code, but + * we only allocate a space id if one hasn't been allocated + * already, so we should be OK. + */ + + BUG_ON(next == &init_mm); /* Should never happen */ + + if (next->context.space_id == 0) + next->context.space_id = alloc_sid(); + + switch_mm(prev,next,current); +} + +#include <asm-generic/mmu_context.h> + +#endif diff --git a/arch/parisc/include/asm/mmzone.h b/arch/parisc/include/asm/mmzone.h new file mode 100644 index 000000000..8d390406d --- /dev/null +++ b/arch/parisc/include/asm/mmzone.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_MMZONE_H +#define _PARISC_MMZONE_H + +#define MAX_PHYSMEM_RANGES 4 /* Fix the size for now (current known max is 3) */ + +#endif /* _PARISC_MMZONE_H */ diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h new file mode 100644 index 000000000..c8c131a74 --- /dev/null +++ b/arch/parisc/include/asm/module.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_MODULE_H +#define _ASM_PARISC_MODULE_H + +#include <asm-generic/module.h> + +/* + * This file contains the parisc architecture specific module code. + */ + +struct unwind_table; + +struct mod_arch_specific +{ + unsigned long got_offset, got_count, got_max; + unsigned long fdesc_offset, fdesc_count, fdesc_max; + struct { + unsigned long stub_offset; + unsigned int stub_entries; + } *section; + int unwind_section; + struct unwind_table *unwind; +}; + +#endif /* _ASM_PARISC_MODULE_H */ diff --git a/arch/parisc/include/asm/page.h b/arch/parisc/include/asm/page.h new file mode 100644 index 000000000..3630b36d0 --- /dev/null +++ b/arch/parisc/include/asm/page.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_PAGE_H +#define _PARISC_PAGE_H + +#include <linux/const.h> + +#include <vdso/page.h> + +#define HAVE_ARCH_HUGETLB_UNMAPPED_AREA + +#ifndef __ASSEMBLER__ + +#include <asm/types.h> +#include <asm/cache.h> + +#define clear_page(page) clear_page_asm((void *)(page)) +#define copy_page(to, from) copy_page_asm((void *)(to), (void *)(from)) + +struct page; +struct vm_area_struct; + +void clear_page_asm(void *page); +void copy_page_asm(void *to, void *from); +void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr, + struct vm_area_struct *vma); +#define __HAVE_ARCH_COPY_USER_HIGHPAGE + +/* + * These are used to make use of C type-checking.. + */ +#define STRICT_MM_TYPECHECKS +#ifdef STRICT_MM_TYPECHECKS +typedef struct { unsigned long pte; } pte_t; /* either 32 or 64bit */ + +/* NOTE: even on 64 bits, these entries are __u32 because we allocate + * the pmd and pgd in ZONE_DMA (i.e. under 4GB) */ +typedef struct { __u32 pgd; } pgd_t; +typedef struct { unsigned long pgprot; } pgprot_t; + +#if CONFIG_PGTABLE_LEVELS == 3 +typedef struct { __u32 pmd; } pmd_t; +#define __pmd(x) ((pmd_t) { (x) } ) +/* pXd_val() do not work as lvalues, so make sure we don't use them as such. */ +#define pmd_val(x) ((x).pmd + 0) +#endif + +#define pte_val(x) ((x).pte) +#define pgd_val(x) ((x).pgd + 0) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) } ) +#define __pgd(x) ((pgd_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) + +#else +/* + * .. while these make it easier on the compiler + */ +typedef unsigned long pte_t; + +#if CONFIG_PGTABLE_LEVELS == 3 +typedef __u32 pmd_t; +#define pmd_val(x) (x) +#define __pmd(x) (x) +#endif + +typedef __u32 pgd_t; +typedef unsigned long pgprot_t; + +#define pte_val(x) (x) +#define pgd_val(x) (x) +#define pgprot_val(x) (x) + +#define __pte(x) (x) +#define __pgd(x) (x) +#define __pgprot(x) (x) + +#endif /* STRICT_MM_TYPECHECKS */ + +#define set_pmd(pmdptr, pmdval) (*(pmdptr) = (pmdval)) +#if CONFIG_PGTABLE_LEVELS == 3 +#define set_pud(pudptr, pudval) (*(pudptr) = (pudval)) +#endif + +typedef struct page *pgtable_t; + +typedef struct __physmem_range { + unsigned long start_pfn; + unsigned long pages; /* PAGE_SIZE pages */ +} physmem_range_t; + +extern physmem_range_t pmem_ranges[]; +extern int npmem_ranges; + +#endif /* !__ASSEMBLER__ */ + +/* WARNING: The definitions below must match exactly to sizeof(pte_t) + * etc + */ +#ifdef CONFIG_64BIT +#define BITS_PER_PTE_ENTRY 3 +#define BITS_PER_PMD_ENTRY 2 +#define BITS_PER_PGD_ENTRY 2 +#else +#define BITS_PER_PTE_ENTRY 2 +#define BITS_PER_PMD_ENTRY 2 +#define BITS_PER_PGD_ENTRY 2 +#endif +#define PGD_ENTRY_SIZE (1UL << BITS_PER_PGD_ENTRY) +#define PMD_ENTRY_SIZE (1UL << BITS_PER_PMD_ENTRY) +#define PTE_ENTRY_SIZE (1UL << BITS_PER_PTE_ENTRY) + +#define LINUX_GATEWAY_SPACE 0 + +/* This governs the relationship between virtual and physical addresses. + * If you alter it, make sure to take care of our various fixed mapping + * segments in fixmap.h */ +#ifdef CONFIG_64BIT +#define __PAGE_OFFSET_DEFAULT (0x40000000) /* 1GB */ +#else +#define __PAGE_OFFSET_DEFAULT (0x10000000) /* 256MB */ +#endif + +#if defined(BOOTLOADER) +#define __PAGE_OFFSET (0) /* bootloader uses physical addresses */ +#else +#define __PAGE_OFFSET __PAGE_OFFSET_DEFAULT +#endif /* BOOTLOADER */ + +#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) + +/* The size of the gateway page (we leave lots of room for expansion) */ +#define GATEWAY_PAGE_SIZE 0x4000 + +/* The start of the actual kernel binary---used in vmlinux.lds.S + * Leave some space after __PAGE_OFFSET for detecting kernel null + * ptr derefs */ +#define KERNEL_BINARY_TEXT_START (__PAGE_OFFSET + 0x100000) + +/* These macros don't work for 64-bit C code -- don't allow in C at all */ +#ifdef __ASSEMBLER__ +# define PA(x) ((x)-__PAGE_OFFSET) +# define VA(x) ((x)+__PAGE_OFFSET) +#endif +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) +#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) + +#ifdef CONFIG_HUGETLB_PAGE +#define HPAGE_SHIFT PMD_SHIFT /* fixed for transparent huge pages */ +#define HPAGE_SIZE ((1UL) << HPAGE_SHIFT) +#define HPAGE_MASK (~(HPAGE_SIZE - 1)) +#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) + +#if defined(CONFIG_64BIT) && defined(CONFIG_PARISC_PAGE_SIZE_4KB) +# define REAL_HPAGE_SHIFT 20 /* 20 = 1MB */ +# define _HUGE_PAGE_SIZE_ENCODING_DEFAULT _PAGE_SIZE_ENCODING_1M +#elif !defined(CONFIG_64BIT) && defined(CONFIG_PARISC_PAGE_SIZE_4KB) +# define REAL_HPAGE_SHIFT 22 /* 22 = 4MB */ +# define _HUGE_PAGE_SIZE_ENCODING_DEFAULT _PAGE_SIZE_ENCODING_4M +#else +# define REAL_HPAGE_SHIFT 24 /* 24 = 16MB */ +# define _HUGE_PAGE_SIZE_ENCODING_DEFAULT _PAGE_SIZE_ENCODING_16M +#endif +#endif /* CONFIG_HUGETLB_PAGE */ + +#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) + +#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) + +#include <asm-generic/memory_model.h> +#include <asm-generic/getorder.h> +#include <asm/pdc.h> + +#define PAGE0 ((struct zeropage *)absolute_pointer(__PAGE_OFFSET)) + +/* DEFINITION OF THE ZERO-PAGE (PAG0) */ +/* based on work by Jason Eckhardt (jason@equator.com) */ + +#endif /* _PARISC_PAGE_H */ diff --git a/arch/parisc/include/asm/parisc-device.h b/arch/parisc/include/asm/parisc-device.h new file mode 100644 index 000000000..4731420e5 --- /dev/null +++ b/arch/parisc/include/asm/parisc-device.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_PARISC_DEVICE_H_ +#define _ASM_PARISC_PARISC_DEVICE_H_ + +#include <linux/device.h> +#include <linux/device-id/parisc.h> + +struct parisc_device { + struct resource hpa; /* Hard Physical Address */ + struct parisc_device_id id; + struct parisc_driver *driver; /* Driver for this device */ + char name[80]; /* The hardware description */ + int irq; + int aux_irq; /* Some devices have a second IRQ */ + + char hw_path; /* The module number on this bus */ + unsigned int num_addrs; /* some devices have additional address ranges. */ + unsigned long *addr; /* which will be stored here */ + +#ifdef CONFIG_64BIT + /* parms for pdc_pat_cell_module() call */ + unsigned long pcell_loc; /* Physical Cell location */ + unsigned long mod_index; /* PAT specific - Misc Module info */ + + /* generic info returned from pdc_pat_cell_module() */ + unsigned long mod_info; /* PAT specific - Misc Module info */ + unsigned long pmod_loc; /* physical Module location */ + unsigned long mod0; +#endif + u64 dma_mask; /* DMA mask for I/O */ + struct device dev; +}; + +struct parisc_driver { + struct parisc_driver *next; + char *name; + const struct parisc_device_id *id_table; + int (*probe)(struct parisc_device *dev); /* New device discovered */ + void (*remove)(struct parisc_device *dev); + struct device_driver drv; +}; + + +#define to_parisc_device(d) container_of(d, struct parisc_device, dev) +#define to_parisc_driver(d) container_of_const(d, struct parisc_driver, drv) +#define parisc_parent(d) to_parisc_device(d->dev.parent) + +static inline const char *parisc_pathname(struct parisc_device *d) +{ + return dev_name(&d->dev); +} + +static inline void +parisc_set_drvdata(struct parisc_device *d, void *p) +{ + dev_set_drvdata(&d->dev, p); +} + +static inline void * +parisc_get_drvdata(struct parisc_device *d) +{ + return dev_get_drvdata(&d->dev); +} + +extern const struct bus_type parisc_bus_type; + +int iosapic_serial_irq(struct parisc_device *dev); + +#endif /*_ASM_PARISC_PARISC_DEVICE_H_*/ diff --git a/arch/parisc/include/asm/parport.h b/arch/parisc/include/asm/parport.h new file mode 100644 index 000000000..2c8e2321c --- /dev/null +++ b/arch/parisc/include/asm/parport.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * + * parport.h: ia32-compatible parport initialisation + * + * This file should only be included by drivers/parport/parport_pc.c. + */ +#ifndef _ASM_PARPORT_H +#define _ASM_PARPORT_H 1 + + +static int parport_pc_find_nonpci_ports (int autoirq, int autodma) +{ + /* nothing ! */ + return 0; +} + + +#endif /* !(_ASM_PARPORT_H) */ diff --git a/arch/parisc/include/asm/pci.h b/arch/parisc/include/asm/pci.h new file mode 100644 index 000000000..127ed5021 --- /dev/null +++ b/arch/parisc/include/asm/pci.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_PARISC_PCI_H +#define __ASM_PARISC_PCI_H + +#include <linux/scatterlist.h> + + + +/* +** HP PCI platforms generally support multiple bus adapters. +** (workstations 1-~4, servers 2-~32) +** +** Newer platforms number the busses across PCI bus adapters *sparsely*. +** E.g. 0, 8, 16, ... +** +** Under a PCI bus, most HP platforms support PPBs up to two or three +** levels deep. See "Bit3" product line. +*/ +#define PCI_MAX_BUSSES 256 + + +/* To be used as: mdelay(pci_post_reset_delay); + * + * post_reset is the time the kernel should stall to prevent anyone from + * accessing the PCI bus once #RESET is de-asserted. + * PCI spec somewhere says 1 second but with multi-PCI bus systems, + * this makes the boot time much longer than necessary. + * 20ms seems to work for all the HP PCI implementations to date. + */ +#define pci_post_reset_delay 50 + + +/* +** pci_hba_data (aka H2P_OBJECT in HP/UX) +** +** This is the "common" or "base" data structure which HBA drivers +** (eg Dino or LBA) are required to place at the top of their own +** platform_data structure. I've heard this called "C inheritance" too. +** +** Data needed by pcibios layer belongs here. +*/ +struct pci_hba_data { + void __iomem *base_addr; /* aka Host Physical Address */ + const struct parisc_device *dev; /* device from PA bus walk */ + struct pci_bus *hba_bus; /* primary PCI bus below HBA */ + int hba_num; /* I/O port space access "key" */ + struct resource bus_num; /* PCI bus numbers */ + struct resource io_space; /* PIOP */ + struct resource lmmio_space; /* bus addresses < 4Gb */ + struct resource elmmio_space; /* additional bus addresses < 4Gb */ + struct resource gmmio_space; /* bus addresses > 4Gb */ + + /* NOTE: Dino code assumes it can use *all* of the lmmio_space, + * elmmio_space and gmmio_space as a contiguous array of + * resources. This #define represents the array size */ + #define DINO_MAX_LMMIO_RESOURCES 3 + + unsigned long lmmio_space_offset; /* CPU view - PCI view */ + struct ioc *iommu; /* IOMMU this device is under */ + /* REVISIT - spinlock to protect resources? */ + + #define HBA_NAME_SIZE 16 + char io_name[HBA_NAME_SIZE]; + char lmmio_name[HBA_NAME_SIZE]; + char elmmio_name[HBA_NAME_SIZE]; + char gmmio_name[HBA_NAME_SIZE]; +}; + +/* +** We support 2^16 I/O ports per HBA. These are set up in the form +** 0xbbxxxx, where bb is the bus number and xxxx is the I/O port +** space address. +*/ +#define HBA_PORT_SPACE_BITS 16 + +#define HBA_PORT_BASE(h) ((h) << HBA_PORT_SPACE_BITS) +#define HBA_PORT_SPACE_SIZE (1UL << HBA_PORT_SPACE_BITS) + +#define PCI_PORT_HBA(a) ((a) >> HBA_PORT_SPACE_BITS) +#define PCI_PORT_ADDR(a) ((a) & (HBA_PORT_SPACE_SIZE - 1)) + +#ifdef CONFIG_64BIT +#define PCI_F_EXTEND 0xffffffff00000000UL +#else /* !CONFIG_64BIT */ +#define PCI_F_EXTEND 0UL +#endif /* !CONFIG_64BIT */ + +/* +** Most PCI devices (eg Tulip, NCR720) also export the same registers +** to both MMIO and I/O port space. Due to poor performance of I/O Port +** access under HP PCI bus adapters, strongly recommend the use of MMIO +** address space. +** +** While I'm at it more PA programming notes: +** +** 1) MMIO stores (writes) are posted operations. This means the processor +** gets an "ACK" before the write actually gets to the device. A read +** to the same device (or typically the bus adapter above it) will +** force in-flight write transaction(s) out to the targeted device +** before the read can complete. +** +** 2) The Programmed I/O (PIO) data may not always be strongly ordered with +** respect to DMA on all platforms. Ie PIO data can reach the processor +** before in-flight DMA reaches memory. Since most SMP PA platforms +** are I/O coherent, it generally doesn't matter...but sometimes +** it does. +** +** I've helped device driver writers debug both types of problems. +*/ +struct pci_port_ops { + u8 (*inb) (struct pci_hba_data *hba, u16 port); + u16 (*inw) (struct pci_hba_data *hba, u16 port); + u32 (*inl) (struct pci_hba_data *hba, u16 port); + void (*outb) (struct pci_hba_data *hba, u16 port, u8 data); + void (*outw) (struct pci_hba_data *hba, u16 port, u16 data); + void (*outl) (struct pci_hba_data *hba, u16 port, u32 data); +}; + + +struct pci_bios_ops { + void (*init)(void); + void (*fixup_bus)(struct pci_bus *bus); +}; + +/* +** Stuff declared in arch/parisc/kernel/pci.c +*/ +extern struct pci_port_ops *pci_port; +extern struct pci_bios_ops *pci_bios; + +#ifdef CONFIG_PCI +extern void pcibios_register_hba(struct pci_hba_data *); +#else +static inline void pcibios_register_hba(struct pci_hba_data *x) +{ +} +#endif +extern void pcibios_init_bridge(struct pci_dev *); + +/* + * pcibios_assign_all_busses() is used in drivers/pci/pci.c:pci_do_scan_bus() + * 0 == check if bridge is numbered before re-numbering. + * 1 == pci_do_scan_bus() should automatically number all PCI-PCI bridges. + * + * We *should* set this to zero for "legacy" platforms and one + * for PAT platforms. + * + * But legacy platforms also need to renumber the busses below a Host + * Bus controller. Adding a 4-port Tulip card on the first PCI root + * bus of a C200 resulted in the secondary bus being numbered as 1. + * The second PCI host bus controller's root bus had already been + * assigned bus number 1 by firmware and sysfs complained. + * + * Firmware isn't doing anything wrong here since each controller + * is its own PCI domain. It's simpler and easier for us to renumber + * the busses rather than treat each Dino as a separate PCI domain. + * Eventually, we may want to introduce PCI domains for Superdome or + * rp7420/8420 boxes and then revisit this issue. + */ +#define pcibios_assign_all_busses() (1) + +#define PCIBIOS_MIN_IO 0x10 +#define PCIBIOS_MIN_MEM 0x1000 /* NBPG - but pci/setup-res.c dies */ + +#define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE + +#endif /* __ASM_PARISC_PCI_H */ diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h new file mode 100644 index 000000000..6080a1516 --- /dev/null +++ b/arch/parisc/include/asm/pdc.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_PDC_H +#define _PARISC_PDC_H + +#include <uapi/asm/pdc.h> + +#if !defined(__ASSEMBLER__) + +extern int parisc_narrow_firmware; + +extern int pdc_type; +extern unsigned long parisc_cell_num; /* cell number the CPU runs on (PAT) */ +extern unsigned long parisc_cell_loc; /* cell location of CPU (PAT) */ +extern unsigned long parisc_pat_pdc_cap; /* PDC capabilities (PAT) */ + +/* Values for pdc_type */ +#define PDC_TYPE_ILLEGAL -1 +#define PDC_TYPE_PAT 0 /* 64-bit PAT-PDC */ +#define PDC_TYPE_SYSTEM_MAP 1 /* 32-bit, but supports PDC_SYSTEM_MAP */ +#define PDC_TYPE_SNAKE 2 /* Doesn't support SYSTEM_MAP */ + +void setup_pdc(void); /* in inventory.c */ + +/* wrapper-functions from pdc.c */ + +int pdc_add_valid(unsigned long address); +int pdc_instr(unsigned int *instr); +int pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_info, unsigned long len); +int pdc_chassis_disp(unsigned long disp); +int pdc_chassis_warn(unsigned long *warn); +int pdc_coproc_cfg(struct pdc_coproc_cfg *pdc_coproc_info); +int pdc_coproc_cfg_unlocked(struct pdc_coproc_cfg *pdc_coproc_info); +int pdc_iodc_read(unsigned long *actcnt, unsigned long hpa, unsigned int index, + void *iodc_data, unsigned int iodc_data_size); +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 pdc_system_map_find_addrs(struct pdc_system_map_addr_info *pdc_addr_info, + long mod_index, long addr_index); +int pdc_model_info(struct pdc_model *model); +int pdc_model_sysmodel(unsigned int os_id, char *name); +int pdc_model_cpuid(unsigned long *cpu_id); +int pdc_model_versions(unsigned long *versions, int id); +int pdc_model_capabilities(unsigned long *capabilities); +int pdc_model_platform_info(char *orig_prod_num, char *current_prod_num, char *serial_no); +int pdc_cache_info(struct pdc_cache_info *cache); +int pdc_spaceid_bits(unsigned long *space_bits); +int pdc_btlb_info(struct pdc_btlb_info *btlb); +int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned long len, + unsigned long entry_info, unsigned long slot); +int pdc_btlb_purge_all(void); +int pdc_mem_map_hpa(struct pdc_memory_map *r_addr, struct pdc_module_path *mod_path); +int pdc_pim_toc11(struct pdc_toc_pim_11 *ret); +int pdc_pim_toc20(struct pdc_toc_pim_20 *ret); +int pdc_lan_station_id(char *lan_addr, unsigned long net_hpa); + +int pdc_stable_read(unsigned long staddr, void *memaddr, unsigned long count); +int pdc_stable_write(unsigned long staddr, void *memaddr, unsigned long count); +int pdc_stable_get_size(unsigned long *size); +int pdc_stable_verify_contents(void); +int pdc_stable_initialize(void); + +int pdc_pci_irt_size(unsigned long *num_entries, unsigned long hpa); +int pdc_pci_irt(unsigned long num_entries, unsigned long hpa, void *tbl); + +int pdc_get_initiator(struct hardware_path *, struct pdc_initiator *); +int pdc_tod_read(struct pdc_tod *tod); +int pdc_tod_set(unsigned long sec, unsigned long usec); + +void pdc_pdt_init(void); /* in pdt.c */ +int pdc_mem_pdt_info(struct pdc_mem_retinfo *rinfo); +int pdc_mem_pdt_read_entries(struct pdc_mem_read_pdt *rpdt_read, + unsigned long *pdt_entries_ptr); +#ifdef CONFIG_64BIT +int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr, + struct pdc_memory_table *tbl, unsigned long entries); +#endif + +void set_firmware_width(void); +void set_firmware_width_unlocked(void); +int pdc_do_firm_test_reset(unsigned long ftc_bitmap); +int pdc_do_reset(void); +int pdc_soft_power_info(unsigned long *power_reg); +int pdc_soft_power_button(int sw_control); +int pdc_soft_power_button_panic(int sw_control); +void pdc_io_reset(void); +void pdc_io_reset_devices(void); +int pdc_iodc_getc(void); +int pdc_iodc_print(const unsigned char *str, unsigned count); + +void pdc_emergency_unlock(void); +int pdc_sti_call(unsigned long func, unsigned long flags, + unsigned long inptr, unsigned long outputr, + unsigned long glob_cfg, int do_call64); + +int __pdc_cpu_rendezvous(void); +void pdc_cpu_rendezvous_lock(void); +void pdc_cpu_rendezvous_unlock(void); + +static inline char * os_id_to_string(u16 os_id) { + switch(os_id) { + case OS_ID_NONE: return "No OS"; + case OS_ID_HPUX: return "HP-UX"; + case OS_ID_MPEXL: return "MPE-iX"; + case OS_ID_OSF: return "OSF"; + case OS_ID_HPRT: return "HP-RT"; + case OS_ID_NOVEL: return "Novell Netware"; + case OS_ID_LINUX: return "Linux"; + default: return "Unknown"; + } +} + +#endif /* !defined(__ASSEMBLER__) */ +#endif /* _PARISC_PDC_H */ diff --git a/arch/parisc/include/asm/pdc_chassis.h b/arch/parisc/include/asm/pdc_chassis.h new file mode 100644 index 000000000..d6d82f53d --- /dev/null +++ b/arch/parisc/include/asm/pdc_chassis.h @@ -0,0 +1,367 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * include/asm-parisc/pdc_chassis.h + * + * Copyright (C) 2002 Laurent Canet <canetl@esiee.fr> + * Copyright (C) 2002 Thibaut Varene <varenet@parisc-linux.org> + * + * TODO: - handle processor number on SMP systems (Reporting Entity ID) + * - handle message ID + * - handle timestamps + */ + + +#ifndef _PARISC_PDC_CHASSIS_H +#define _PARISC_PDC_CHASSIS_H + +/* + * ---------- + * Prototypes + * ---------- + */ + +int pdc_chassis_send_status(int message); +void parisc_pdc_chassis_init(void); + + +/* + * ----------------- + * Direct call names + * ----------------- + * They setup everything for you, the Log message and the corresponding LED state + */ + +#define PDC_CHASSIS_DIRECT_BSTART 0 +#define PDC_CHASSIS_DIRECT_BCOMPLETE 1 +#define PDC_CHASSIS_DIRECT_SHUTDOWN 2 +#define PDC_CHASSIS_DIRECT_PANIC 3 +#define PDC_CHASSIS_DIRECT_HPMC 4 +#define PDC_CHASSIS_DIRECT_LPMC 5 +#define PDC_CHASSIS_DIRECT_DUMP 6 /* not yet implemented */ +#define PDC_CHASSIS_DIRECT_OOPS 7 /* not yet implemented */ + + +/* + * ------------ + * LEDs control + * ------------ + * Set the three LEDs -- Run, Attn, and Fault. + */ + +/* Old PDC LED control */ +#define PDC_CHASSIS_DISP_DATA(v) ((unsigned long)(v) << 17) + +/* + * Available PDC PAT LED states + */ + +#define PDC_CHASSIS_LED_RUN_OFF (0ULL << 4) +#define PDC_CHASSIS_LED_RUN_FLASH (1ULL << 4) +#define PDC_CHASSIS_LED_RUN_ON (2ULL << 4) +#define PDC_CHASSIS_LED_RUN_NC (3ULL << 4) +#define PDC_CHASSIS_LED_ATTN_OFF (0ULL << 6) +#define PDC_CHASSIS_LED_ATTN_FLASH (1ULL << 6) +#define PDC_CHASSIS_LED_ATTN_NC (3ULL << 6) /* ATTN ON is invalid */ +#define PDC_CHASSIS_LED_FAULT_OFF (0ULL << 8) +#define PDC_CHASSIS_LED_FAULT_FLASH (1ULL << 8) +#define PDC_CHASSIS_LED_FAULT_ON (2ULL << 8) +#define PDC_CHASSIS_LED_FAULT_NC (3ULL << 8) +#define PDC_CHASSIS_LED_VALID (1ULL << 10) + +/* + * Valid PDC PAT LED states combinations + */ + +/* System running normally */ +#define PDC_CHASSIS_LSTATE_RUN_NORMAL (PDC_CHASSIS_LED_RUN_ON | \ + PDC_CHASSIS_LED_ATTN_OFF | \ + PDC_CHASSIS_LED_FAULT_OFF | \ + PDC_CHASSIS_LED_VALID ) +/* System crashed and rebooted itself successfully */ +#define PDC_CHASSIS_LSTATE_RUN_CRASHREC (PDC_CHASSIS_LED_RUN_ON | \ + PDC_CHASSIS_LED_ATTN_OFF | \ + PDC_CHASSIS_LED_FAULT_FLASH | \ + PDC_CHASSIS_LED_VALID ) +/* There was a system interruption that did not take the system down */ +#define PDC_CHASSIS_LSTATE_RUN_SYSINT (PDC_CHASSIS_LED_RUN_ON | \ + PDC_CHASSIS_LED_ATTN_FLASH | \ + PDC_CHASSIS_LED_FAULT_OFF | \ + PDC_CHASSIS_LED_VALID ) +/* System running and unexpected reboot or non-critical error detected */ +#define PDC_CHASSIS_LSTATE_RUN_NCRIT (PDC_CHASSIS_LED_RUN_ON | \ + PDC_CHASSIS_LED_ATTN_FLASH | \ + PDC_CHASSIS_LED_FAULT_FLASH | \ + PDC_CHASSIS_LED_VALID ) +/* Executing non-OS code */ +#define PDC_CHASSIS_LSTATE_NONOS (PDC_CHASSIS_LED_RUN_FLASH | \ + PDC_CHASSIS_LED_ATTN_OFF | \ + PDC_CHASSIS_LED_FAULT_OFF | \ + PDC_CHASSIS_LED_VALID ) +/* Boot failed - Executing non-OS code */ +#define PDC_CHASSIS_LSTATE_NONOS_BFAIL (PDC_CHASSIS_LED_RUN_FLASH | \ + PDC_CHASSIS_LED_ATTN_OFF | \ + PDC_CHASSIS_LED_FAULT_ON | \ + PDC_CHASSIS_LED_VALID ) +/* Unexpected reboot occurred - Executing non-OS code */ +#define PDC_CHASSIS_LSTATE_NONOS_UNEXP (PDC_CHASSIS_LED_RUN_FLASH | \ + PDC_CHASSIS_LED_ATTN_OFF | \ + PDC_CHASSIS_LED_FAULT_FLASH | \ + PDC_CHASSIS_LED_VALID ) +/* Executing non-OS code - Non-critical error detected */ +#define PDC_CHASSIS_LSTATE_NONOS_NCRIT (PDC_CHASSIS_LED_RUN_FLASH | \ + PDC_CHASSIS_LED_ATTN_FLASH | \ + PDC_CHASSIS_LED_FAULT_OFF | \ + PDC_CHASSIS_LED_VALID ) +/* Boot failed - Executing non-OS code - Non-critical error detected */ +#define PDC_CHASSIS_LSTATE_BFAIL_NCRIT (PDC_CHASSIS_LED_RUN_FLASH | \ + PDC_CHASSIS_LED_ATTN_FLASH | \ + PDC_CHASSIS_LED_FAULT_ON | \ + PDC_CHASSIS_LED_VALID ) +/* Unexpected reboot/recovering - Executing non-OS code - Non-critical error detected */ +#define PDC_CHASSIS_LSTATE_UNEXP_NCRIT (PDC_CHASSIS_LED_RUN_FLASH | \ + PDC_CHASSIS_LED_ATTN_FLASH | \ + PDC_CHASSIS_LED_FAULT_FLASH | \ + PDC_CHASSIS_LED_VALID ) +/* Cannot execute PDC */ +#define PDC_CHASSIS_LSTATE_CANNOT_PDC (PDC_CHASSIS_LED_RUN_OFF | \ + PDC_CHASSIS_LED_ATTN_OFF | \ + PDC_CHASSIS_LED_FAULT_OFF | \ + PDC_CHASSIS_LED_VALID ) +/* Boot failed - OS not up - PDC has detected a failure that prevents boot */ +#define PDC_CHASSIS_LSTATE_FATAL_BFAIL (PDC_CHASSIS_LED_RUN_OFF | \ + PDC_CHASSIS_LED_ATTN_OFF | \ + PDC_CHASSIS_LED_FAULT_ON | \ + PDC_CHASSIS_LED_VALID ) +/* No code running - Non-critical error detected (double fault situation) */ +#define PDC_CHASSIS_LSTATE_NOCODE_NCRIT (PDC_CHASSIS_LED_RUN_OFF | \ + PDC_CHASSIS_LED_ATTN_FLASH | \ + PDC_CHASSIS_LED_FAULT_OFF | \ + PDC_CHASSIS_LED_VALID ) +/* Boot failed - OS not up - Fatal failure detected - Non-critical error detected */ +#define PDC_CHASSIS_LSTATE_FATAL_NCRIT (PDC_CHASSIS_LED_RUN_OFF | \ + PDC_CHASSIS_LED_ATTN_FLASH | \ + PDC_CHASSIS_LED_FAULT_ON | \ + PDC_CHASSIS_LED_VALID ) +/* All other states are invalid */ + + +/* + * -------------- + * PDC Log events + * -------------- + * Here follows bits needed to fill up the log event sent to PDC_CHASSIS + * The log message contains: Alert level, Source, Source detail, + * Source ID, Problem detail, Caller activity, Activity status, + * Caller subactivity, Reporting entity type, Reporting entity ID, + * Data type, Unique message ID and EOM. + */ + +/* Alert level */ +#define PDC_CHASSIS_ALERT_FORWARD (0ULL << 36) /* no failure detected */ +#define PDC_CHASSIS_ALERT_SERPROC (1ULL << 36) /* service proc - no failure */ +#define PDC_CHASSIS_ALERT_NURGENT (2ULL << 36) /* non-urgent operator attn */ +#define PDC_CHASSIS_ALERT_BLOCKED (3ULL << 36) /* system blocked */ +#define PDC_CHASSIS_ALERT_CONF_CHG (4ULL << 36) /* unexpected configuration change */ +#define PDC_CHASSIS_ALERT_ENV_PB (5ULL << 36) /* boot possible, environmental pb */ +#define PDC_CHASSIS_ALERT_PENDING (6ULL << 36) /* boot possible, pending failure */ +#define PDC_CHASSIS_ALERT_PERF_IMP (8ULL << 36) /* boot possible, performance impaired */ +#define PDC_CHASSIS_ALERT_FUNC_IMP (10ULL << 36) /* boot possible, functionality impaired */ +#define PDC_CHASSIS_ALERT_SOFT_FAIL (12ULL << 36) /* software failure */ +#define PDC_CHASSIS_ALERT_HANG (13ULL << 36) /* system hang */ +#define PDC_CHASSIS_ALERT_ENV_FATAL (14ULL << 36) /* fatal power or environmental pb */ +#define PDC_CHASSIS_ALERT_HW_FATAL (15ULL << 36) /* fatal hardware problem */ + +/* Source */ +#define PDC_CHASSIS_SRC_NONE (0ULL << 28) /* unknown, no source stated */ +#define PDC_CHASSIS_SRC_PROC (1ULL << 28) /* processor */ +/* For later use ? */ +#define PDC_CHASSIS_SRC_PROC_CACHE (2ULL << 28) /* processor cache*/ +#define PDC_CHASSIS_SRC_PDH (3ULL << 28) /* processor dependent hardware */ +#define PDC_CHASSIS_SRC_PWR (4ULL << 28) /* power */ +#define PDC_CHASSIS_SRC_FAB (5ULL << 28) /* fabric connector */ +#define PDC_CHASSIS_SRC_PLATi (6ULL << 28) /* platform */ +#define PDC_CHASSIS_SRC_MEM (7ULL << 28) /* memory */ +#define PDC_CHASSIS_SRC_IO (8ULL << 28) /* I/O */ +#define PDC_CHASSIS_SRC_CELL (9ULL << 28) /* cell */ +#define PDC_CHASSIS_SRC_PD (10ULL << 28) /* protected domain */ + +/* Source detail field */ +#define PDC_CHASSIS_SRC_D_PROC (1ULL << 24) /* processor general */ + +/* Source ID - platform dependent */ +#define PDC_CHASSIS_SRC_ID_UNSPEC (0ULL << 16) + +/* Problem detail - problem source dependent */ +#define PDC_CHASSIS_PB_D_PROC_NONE (0ULL << 32) /* no problem detail */ +#define PDC_CHASSIS_PB_D_PROC_TIMEOUT (4ULL << 32) /* timeout */ + +/* Caller activity */ +#define PDC_CHASSIS_CALL_ACT_HPUX_BL (7ULL << 12) /* Boot Loader */ +#define PDC_CHASSIS_CALL_ACT_HPUX_PD (8ULL << 12) /* SAL_PD activities */ +#define PDC_CHASSIS_CALL_ACT_HPUX_EVENT (9ULL << 12) /* SAL_EVENTS activities */ +#define PDC_CHASSIS_CALL_ACT_HPUX_IO (10ULL << 12) /* SAL_IO activities */ +#define PDC_CHASSIS_CALL_ACT_HPUX_PANIC (11ULL << 12) /* System panic */ +#define PDC_CHASSIS_CALL_ACT_HPUX_INIT (12ULL << 12) /* System initialization */ +#define PDC_CHASSIS_CALL_ACT_HPUX_SHUT (13ULL << 12) /* System shutdown */ +#define PDC_CHASSIS_CALL_ACT_HPUX_WARN (14ULL << 12) /* System warning */ +#define PDC_CHASSIS_CALL_ACT_HPUX_DU (15ULL << 12) /* Display_Activity() update */ + +/* Activity status - implementation dependent */ +#define PDC_CHASSIS_ACT_STATUS_UNSPEC (0ULL << 0) + +/* Caller subactivity - implementation dependent */ +/* FIXME: other subactivities ? */ +#define PDC_CHASSIS_CALL_SACT_UNSPEC (0ULL << 4) /* implementation dependent */ + +/* Reporting entity type */ +#define PDC_CHASSIS_RET_GENERICOS (12ULL << 52) /* generic OSes */ +#define PDC_CHASSIS_RET_IA64_NT (13ULL << 52) /* IA-64 NT */ +#define PDC_CHASSIS_RET_HPUX (14ULL << 52) /* HP-UX */ +#define PDC_CHASSIS_RET_DIAG (15ULL << 52) /* offline diagnostics & utilities */ + +/* Reporting entity ID */ +#define PDC_CHASSIS_REID_UNSPEC (0ULL << 44) + +/* Data type */ +#define PDC_CHASSIS_DT_NONE (0ULL << 59) /* data field unused */ +/* For later use ? Do we need these ? */ +#define PDC_CHASSIS_DT_PHYS_ADDR (1ULL << 59) /* physical address */ +#define PDC_CHASSIS_DT_DATA_EXPECT (2ULL << 59) /* expected data */ +#define PDC_CHASSIS_DT_ACTUAL (3ULL << 59) /* actual data */ +#define PDC_CHASSIS_DT_PHYS_LOC (4ULL << 59) /* physical location */ +#define PDC_CHASSIS_DT_PHYS_LOC_EXT (5ULL << 59) /* physical location extension */ +#define PDC_CHASSIS_DT_TAG (6ULL << 59) /* tag */ +#define PDC_CHASSIS_DT_SYNDROME (7ULL << 59) /* syndrome */ +#define PDC_CHASSIS_DT_CODE_ADDR (8ULL << 59) /* code address */ +#define PDC_CHASSIS_DT_ASCII_MSG (9ULL << 59) /* ascii message */ +#define PDC_CHASSIS_DT_POST (10ULL << 59) /* POST code */ +#define PDC_CHASSIS_DT_TIMESTAMP (11ULL << 59) /* timestamp */ +#define PDC_CHASSIS_DT_DEV_STAT (12ULL << 59) /* device status */ +#define PDC_CHASSIS_DT_DEV_TYPE (13ULL << 59) /* device type */ +#define PDC_CHASSIS_DT_PB_DET (14ULL << 59) /* problem detail */ +#define PDC_CHASSIS_DT_ACT_LEV (15ULL << 59) /* activity level/timeout */ +#define PDC_CHASSIS_DT_SER_NUM (16ULL << 59) /* serial number */ +#define PDC_CHASSIS_DT_REV_NUM (17ULL << 59) /* revision number */ +#define PDC_CHASSIS_DT_INTERRUPT (18ULL << 59) /* interruption information */ +#define PDC_CHASSIS_DT_TEST_NUM (19ULL << 59) /* test number */ +#define PDC_CHASSIS_DT_STATE_CHG (20ULL << 59) /* major changes in system state */ +#define PDC_CHASSIS_DT_PROC_DEALLOC (21ULL << 59) /* processor deallocate */ +#define PDC_CHASSIS_DT_RESET (30ULL << 59) /* reset type and cause */ +#define PDC_CHASSIS_DT_PA_LEGACY (31ULL << 59) /* legacy PA hex chassis code */ + +/* System states - part of major changes in system state data field */ +#define PDC_CHASSIS_SYSTATE_BSTART (0ULL << 0) /* boot start */ +#define PDC_CHASSIS_SYSTATE_BCOMP (1ULL << 0) /* boot complete */ +#define PDC_CHASSIS_SYSTATE_CHANGE (2ULL << 0) /* major change */ +#define PDC_CHASSIS_SYSTATE_LED (3ULL << 0) /* LED change */ +#define PDC_CHASSIS_SYSTATE_PANIC (9ULL << 0) /* OS Panic */ +#define PDC_CHASSIS_SYSTATE_DUMP (10ULL << 0) /* memory dump */ +#define PDC_CHASSIS_SYSTATE_HPMC (11ULL << 0) /* processing HPMC */ +#define PDC_CHASSIS_SYSTATE_HALT (15ULL << 0) /* system halted */ + +/* Message ID */ +#define PDC_CHASSIS_MSG_ID (0ULL << 40) /* we do not handle msg IDs atm */ + +/* EOM - separates log entries */ +#define PDC_CHASSIS_EOM_CLEAR (0ULL << 43) +#define PDC_CHASSIS_EOM_SET (1ULL << 43) + +/* + * Preformated well known messages + */ + +/* Boot started */ +#define PDC_CHASSIS_PMSG_BSTART (PDC_CHASSIS_ALERT_SERPROC | \ + PDC_CHASSIS_SRC_PROC | \ + PDC_CHASSIS_SRC_D_PROC | \ + PDC_CHASSIS_SRC_ID_UNSPEC | \ + PDC_CHASSIS_PB_D_PROC_NONE | \ + PDC_CHASSIS_CALL_ACT_HPUX_INIT | \ + PDC_CHASSIS_ACT_STATUS_UNSPEC | \ + PDC_CHASSIS_CALL_SACT_UNSPEC | \ + PDC_CHASSIS_RET_HPUX | \ + PDC_CHASSIS_REID_UNSPEC | \ + PDC_CHASSIS_DT_STATE_CHG | \ + PDC_CHASSIS_SYSTATE_BSTART | \ + PDC_CHASSIS_MSG_ID | \ + PDC_CHASSIS_EOM_SET ) + +/* Boot complete */ +#define PDC_CHASSIS_PMSG_BCOMPLETE (PDC_CHASSIS_ALERT_SERPROC | \ + PDC_CHASSIS_SRC_PROC | \ + PDC_CHASSIS_SRC_D_PROC | \ + PDC_CHASSIS_SRC_ID_UNSPEC | \ + PDC_CHASSIS_PB_D_PROC_NONE | \ + PDC_CHASSIS_CALL_ACT_HPUX_INIT | \ + PDC_CHASSIS_ACT_STATUS_UNSPEC | \ + PDC_CHASSIS_CALL_SACT_UNSPEC | \ + PDC_CHASSIS_RET_HPUX | \ + PDC_CHASSIS_REID_UNSPEC | \ + PDC_CHASSIS_DT_STATE_CHG | \ + PDC_CHASSIS_SYSTATE_BCOMP | \ + PDC_CHASSIS_MSG_ID | \ + PDC_CHASSIS_EOM_SET ) + +/* Shutdown */ +#define PDC_CHASSIS_PMSG_SHUTDOWN (PDC_CHASSIS_ALERT_SERPROC | \ + PDC_CHASSIS_SRC_PROC | \ + PDC_CHASSIS_SRC_D_PROC | \ + PDC_CHASSIS_SRC_ID_UNSPEC | \ + PDC_CHASSIS_PB_D_PROC_NONE | \ + PDC_CHASSIS_CALL_ACT_HPUX_SHUT | \ + PDC_CHASSIS_ACT_STATUS_UNSPEC | \ + PDC_CHASSIS_CALL_SACT_UNSPEC | \ + PDC_CHASSIS_RET_HPUX | \ + PDC_CHASSIS_REID_UNSPEC | \ + PDC_CHASSIS_DT_STATE_CHG | \ + PDC_CHASSIS_SYSTATE_HALT | \ + PDC_CHASSIS_MSG_ID | \ + PDC_CHASSIS_EOM_SET ) + +/* Panic */ +#define PDC_CHASSIS_PMSG_PANIC (PDC_CHASSIS_ALERT_SOFT_FAIL | \ + PDC_CHASSIS_SRC_PROC | \ + PDC_CHASSIS_SRC_D_PROC | \ + PDC_CHASSIS_SRC_ID_UNSPEC | \ + PDC_CHASSIS_PB_D_PROC_NONE | \ + PDC_CHASSIS_CALL_ACT_HPUX_PANIC| \ + PDC_CHASSIS_ACT_STATUS_UNSPEC | \ + PDC_CHASSIS_CALL_SACT_UNSPEC | \ + PDC_CHASSIS_RET_HPUX | \ + PDC_CHASSIS_REID_UNSPEC | \ + PDC_CHASSIS_DT_STATE_CHG | \ + PDC_CHASSIS_SYSTATE_PANIC | \ + PDC_CHASSIS_MSG_ID | \ + PDC_CHASSIS_EOM_SET ) + +// FIXME: extrapolated data +/* HPMC */ +#define PDC_CHASSIS_PMSG_HPMC (PDC_CHASSIS_ALERT_CONF_CHG /*?*/ | \ + PDC_CHASSIS_SRC_PROC | \ + PDC_CHASSIS_SRC_D_PROC | \ + PDC_CHASSIS_SRC_ID_UNSPEC | \ + PDC_CHASSIS_PB_D_PROC_NONE | \ + PDC_CHASSIS_CALL_ACT_HPUX_WARN | \ + PDC_CHASSIS_RET_HPUX | \ + PDC_CHASSIS_DT_STATE_CHG | \ + PDC_CHASSIS_SYSTATE_HPMC | \ + PDC_CHASSIS_MSG_ID | \ + PDC_CHASSIS_EOM_SET ) + +/* LPMC */ +#define PDC_CHASSIS_PMSG_LPMC (PDC_CHASSIS_ALERT_BLOCKED /*?*/| \ + PDC_CHASSIS_SRC_PROC | \ + PDC_CHASSIS_SRC_D_PROC | \ + PDC_CHASSIS_SRC_ID_UNSPEC | \ + PDC_CHASSIS_PB_D_PROC_NONE | \ + PDC_CHASSIS_CALL_ACT_HPUX_WARN | \ + PDC_CHASSIS_ACT_STATUS_UNSPEC | \ + PDC_CHASSIS_CALL_SACT_UNSPEC | \ + PDC_CHASSIS_RET_HPUX | \ + PDC_CHASSIS_REID_UNSPEC | \ + PDC_CHASSIS_DT_STATE_CHG | \ + PDC_CHASSIS_SYSTATE_CHANGE | \ + PDC_CHASSIS_MSG_ID | \ + PDC_CHASSIS_EOM_SET ) + +#endif /* _PARISC_PDC_CHASSIS_H */ diff --git a/arch/parisc/include/asm/pdcpat.h b/arch/parisc/include/asm/pdcpat.h new file mode 100644 index 000000000..ec97eefa4 --- /dev/null +++ b/arch/parisc/include/asm/pdcpat.h @@ -0,0 +1,397 @@ +#ifndef __PARISC_PATPDC_H +#define __PARISC_PATPDC_H + +/* + * 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 2000 (c) Hewlett Packard (Paul Bame <bame()spam.parisc-linux.org>) + * Copyright 2000,2004 (c) Grant Grundler <grundler()nahspam.parisc-linux.org> + */ + + +#define PDC_PAT_CELL 64L /* Interface for gaining and + * manipulatin g cell state within PD */ +#define PDC_PAT_CELL_GET_NUMBER 0L /* Return Cell number */ +#define PDC_PAT_CELL_GET_INFO 1L /* Returns info about Cell */ +#define PDC_PAT_CELL_MODULE 2L /* Returns info about Module */ +#define PDC_PAT_CELL_SET_ATTENTION 9L /* Set Cell Attention indicator */ +#define PDC_PAT_CELL_NUMBER_TO_LOC 10L /* Cell Number -> Location */ +#define PDC_PAT_CELL_WALK_FABRIC 11L /* Walk the Fabric */ +#define PDC_PAT_CELL_GET_RDT_SIZE 12L /* Return Route Distance Table Sizes */ +#define PDC_PAT_CELL_GET_RDT 13L /* Return Route Distance Tables */ +#define PDC_PAT_CELL_GET_LOCAL_PDH_SZ 14L /* Read Local PDH Buffer Size */ +#define PDC_PAT_CELL_SET_LOCAL_PDH 15L /* Write Local PDH Buffer */ +#define PDC_PAT_CELL_GET_REMOTE_PDH_SZ 16L /* Return Remote PDH Buffer Size */ +#define PDC_PAT_CELL_GET_REMOTE_PDH 17L /* Read Remote PDH Buffer */ +#define PDC_PAT_CELL_GET_DBG_INFO 128L /* Return DBG Buffer Info */ +#define PDC_PAT_CELL_CHANGE_ALIAS 129L /* Change Non-Equivalent Alias Chacking */ + + +/* +** Arg to PDC_PAT_CELL_MODULE memaddr[4] +** +** Addresses on the Merced Bus != all Runway Bus addresses. +** This is intended for programming SBA/LBA chips range registers. +*/ +#define IO_VIEW 0UL +#define PA_VIEW 1UL + +/* PDC_PAT_CELL_MODULE entity type values */ +#define PAT_ENTITY_CA 0 /* central agent */ +#define PAT_ENTITY_PROC 1 /* processor */ +#define PAT_ENTITY_MEM 2 /* memory controller */ +#define PAT_ENTITY_SBA 3 /* system bus adapter */ +#define PAT_ENTITY_LBA 4 /* local bus adapter */ +#define PAT_ENTITY_PBC 5 /* processor bus converter */ +#define PAT_ENTITY_XBC 6 /* crossbar fabric connect */ +#define PAT_ENTITY_RC 7 /* fabric interconnect */ + +/* PDC_PAT_CELL_MODULE address range type values */ +#define PAT_PBNUM 0 /* PCI Bus Number */ +#define PAT_LMMIO 1 /* < 4G MMIO Space */ +#define PAT_GMMIO 2 /* > 4G MMIO Space */ +#define PAT_NPIOP 3 /* Non Postable I/O Port Space */ +#define PAT_PIOP 4 /* Postable I/O Port Space */ +#define PAT_AHPA 5 /* Addional HPA Space */ +#define PAT_UFO 6 /* HPA Space (UFO for Mariposa) */ +#define PAT_GNIP 7 /* GNI Reserved Space */ + + + +/* PDC PAT CHASSIS LOG -- Platform logging & forward progress functions */ + +#define PDC_PAT_CHASSIS_LOG 65L +#define PDC_PAT_CHASSIS_WRITE_LOG 0L /* Write Log Entry */ +#define PDC_PAT_CHASSIS_READ_LOG 1L /* Read Log Entry */ + + +/* PDC PAT COMPLEX */ + +#define PDC_PAT_COMPLEX 66L + +/* PDC PAT CPU -- CPU configuration within the protection domain */ + +#define PDC_PAT_CPU 67L +#define PDC_PAT_CPU_INFO 0L /* Return CPU config info */ +#define PDC_PAT_CPU_DELETE 1L /* Delete CPU */ +#define PDC_PAT_CPU_ADD 2L /* Add CPU */ +#define PDC_PAT_CPU_GET_NUMBER 3L /* Return CPU Number */ +#define PDC_PAT_CPU_GET_HPA 4L /* Return CPU HPA */ +#define PDC_PAT_CPU_STOP 5L /* Stop CPU */ +#define PDC_PAT_CPU_RENDEZVOUS 6L /* Rendezvous CPU */ +#define PDC_PAT_CPU_GET_CLOCK_INFO 7L /* Return CPU Clock info */ +#define PDC_PAT_CPU_GET_RENDEZVOUS_STATE 8L /* Return Rendezvous State */ +#define PDC_PAT_CPU_GET_PDC_ENTRYPOINT 11L /* Return PDC Entry point */ +#define PDC_PAT_CPU_PLUNGE_FABRIC 128L /* Plunge Fabric */ +#define PDC_PAT_CPU_UPDATE_CACHE_CLEANSING 129L /* Manipulate Cache + * Cleansing Mode */ +/* PDC PAT EVENT -- Platform Events */ + +#define PDC_PAT_EVENT 68L +#define PDC_PAT_EVENT_GET_CAPS 0L /* Get Capabilities */ +#define PDC_PAT_EVENT_SET_MODE 1L /* Set Notification Mode */ +#define PDC_PAT_EVENT_SCAN 2L /* Scan Event */ +#define PDC_PAT_EVENT_HANDLE 3L /* Handle Event */ +#define PDC_PAT_EVENT_GET_NB_CALL 4L /* Get Non-Blocking call Args */ + +/* PDC PAT HPMC -- Cause processor to go into spin loop, and wait + * for wake up from Monarch Processor. + */ + +#define PDC_PAT_HPMC 70L +#define PDC_PAT_HPMC_RENDEZ_CPU 0L /* go into spin loop */ +#define PDC_PAT_HPMC_SET_PARAMS 1L /* Allows OS to specify intr which PDC + * will use to interrupt OS during + * machine check rendezvous */ + +/* parameters for PDC_PAT_HPMC_SET_PARAMS: */ +#define HPMC_SET_PARAMS_INTR 1L /* Rendezvous Interrupt */ +#define HPMC_SET_PARAMS_WAKE 2L /* Wake up processor */ + + +/* PDC PAT IO -- On-line services for I/O modules */ + +#define PDC_PAT_IO 71L +#define PDC_PAT_IO_GET_SLOT_STATUS 5L /* Get Slot Status Info*/ +#define PDC_PAT_IO_GET_LOC_FROM_HARDWARE 6L /* Get Physical Location from */ + /* Hardware Path */ +#define PDC_PAT_IO_GET_HARDWARE_FROM_LOC 7L /* Get Hardware Path from + * Physical Location */ +#define PDC_PAT_IO_GET_PCI_CONFIG_FROM_HW 11L /* Get PCI Configuration + * Address from Hardware Path */ +#define PDC_PAT_IO_GET_HW_FROM_PCI_CONFIG 12L /* Get Hardware Path + * from PCI Configuration Address */ +#define PDC_PAT_IO_READ_HOST_BRIDGE_INFO 13L /* Read Host Bridge State Info */ +#define PDC_PAT_IO_CLEAR_HOST_BRIDGE_INFO 14L /* Clear Host Bridge State Info*/ +#define PDC_PAT_IO_GET_PCI_ROUTING_TABLE_SIZE 15L /* Get PCI INT Routing Table + * Size */ +#define PDC_PAT_IO_GET_PCI_ROUTING_TABLE 16L /* Get PCI INT Routing Table */ +#define PDC_PAT_IO_GET_HINT_TABLE_SIZE 17L /* Get Hint Table Size */ +#define PDC_PAT_IO_GET_HINT_TABLE 18L /* Get Hint Table */ +#define PDC_PAT_IO_PCI_CONFIG_READ 19L /* PCI Config Read */ +#define PDC_PAT_IO_PCI_CONFIG_WRITE 20L /* PCI Config Write */ +#define PDC_PAT_IO_GET_NUM_IO_SLOTS 21L /* Get Number of I/O Bay Slots in + * Cabinet */ +#define PDC_PAT_IO_GET_LOC_IO_SLOTS 22L /* Get Physical Location of I/O */ + /* Bay Slots in Cabinet */ +#define PDC_PAT_IO_BAY_STATUS_INFO 28L /* Get I/O Bay Slot Status Info */ +#define PDC_PAT_IO_GET_PROC_VIEW 29L /* Get Processor view of IO address */ +#define PDC_PAT_IO_PROG_SBA_DIR_RANGE 30L /* Program directed range */ + + +/* PDC PAT MEM -- Manage memory page deallocation */ + +#define PDC_PAT_MEM 72L +#define PDC_PAT_MEM_PD_INFO 0L /* Return PDT info for PD */ +#define PDC_PAT_MEM_PD_CLEAR 1L /* Clear PDT for PD */ +#define PDC_PAT_MEM_PD_READ 2L /* Read PDT entries for PD */ +#define PDC_PAT_MEM_PD_RESET 3L /* Reset clear bit for PD */ +#define PDC_PAT_MEM_CELL_INFO 5L /* Return PDT info For Cell */ +#define PDC_PAT_MEM_CELL_CLEAR 6L /* Clear PDT For Cell */ +#define PDC_PAT_MEM_CELL_READ 7L /* Read PDT entries For Cell */ +#define PDC_PAT_MEM_CELL_RESET 8L /* Reset clear bit For Cell */ +#define PDC_PAT_MEM_SETGM 9L /* Set Good Memory value */ +#define PDC_PAT_MEM_ADD_PAGE 10L /* ADDs a page to the cell */ +#define PDC_PAT_MEM_ADDRESS 11L /* Get Physical Location From */ + /* Memory Address */ +#define PDC_PAT_MEM_GET_TXT_SIZE 12L /* Get Formatted Text Size */ +#define PDC_PAT_MEM_GET_PD_TXT 13L /* Get PD Formatted Text */ +#define PDC_PAT_MEM_GET_CELL_TXT 14L /* Get Cell Formatted Text */ +#define PDC_PAT_MEM_RD_STATE_INFO 15L /* Read Mem Module State Info*/ +#define PDC_PAT_MEM_CLR_STATE_INFO 16L /*Clear Mem Module State Info*/ +#define PDC_PAT_MEM_CLEAN_RANGE 128L /*Clean Mem in specific range*/ +#define PDC_PAT_MEM_GET_TBL_SIZE 131L /* Get Memory Table Size */ +#define PDC_PAT_MEM_GET_TBL 132L /* Get Memory Table */ + + +/* PDC PAT NVOLATILE -- Access Non-Volatile Memory */ + +#define PDC_PAT_NVOLATILE 73L +#define PDC_PAT_NVOLATILE_READ 0L /* Read Non-Volatile Memory */ +#define PDC_PAT_NVOLATILE_WRITE 1L /* Write Non-Volatile Memory */ +#define PDC_PAT_NVOLATILE_GET_SIZE 2L /* Return size of NVM */ +#define PDC_PAT_NVOLATILE_VERIFY 3L /* Verify contents of NVM */ +#define PDC_PAT_NVOLATILE_INIT 4L /* Initialize NVM */ + +/* PDC PAT PD */ +#define PDC_PAT_PD 74L /* Protection Domain Info */ +#define PDC_PAT_PD_GET_ADDR_MAP 0L /* Get Address Map */ +#define PDC_PAT_PD_GET_PDC_INTERF_REV 1L /* Get PDC Interface Revisions */ +#define PDC_PAT_PD_GET_PLATFORM_COUNTER 10L /* Get 64-bit free running counter */ + +#define PDC_PAT_CAPABILITY_BIT_PDC_SERIALIZE (1UL << 0) +#define PDC_PAT_CAPABILITY_BIT_PDC_POLLING (1UL << 1) +#define PDC_PAT_CAPABILITY_BIT_PDC_NBC (1UL << 2) /* non-blocking calls */ +#define PDC_PAT_CAPABILITY_BIT_PDC_UFO (1UL << 3) +#define PDC_PAT_CAPABILITY_BIT_PDC_IODC_32 (1UL << 4) +#define PDC_PAT_CAPABILITY_BIT_PDC_IODC_64 (1UL << 5) +#define PDC_PAT_CAPABILITY_BIT_PDC_HPMC_RENDEZ (1UL << 6) +#define PDC_PAT_CAPABILITY_BIT_SIMULTANEOUS_PTLB (1UL << 7) + +/* PDC_PAT_PD_GET_ADDR_MAP entry types */ +#define PAT_MEMORY_DESCRIPTOR 1 + +/* PDC_PAT_PD_GET_ADDR_MAP memory types */ +#define PAT_MEMTYPE_MEMORY 0 +#define PAT_MEMTYPE_FIRMWARE 4 + +/* PDC_PAT_PD_GET_ADDR_MAP memory usage */ +#define PAT_MEMUSE_GENERAL 0 +#define PAT_MEMUSE_GI 128 +#define PAT_MEMUSE_GNI 129 + +/* PDC PAT REGISTER TOC */ +#define PDC_PAT_REGISTER_TOC 75L +#define PDC_PAT_TOC_REGISTER_VECTOR 0L /* Register TOC Vector */ +#define PDC_PAT_TOC_READ_VECTOR 1L /* Read TOC Vector */ + +/* PDC PAT SYSTEM_INFO */ +#define PDC_PAT_SYSTEM_INFO 76L +/* PDC_PAT_SYSTEM_INFO uses the same options as PDC_SYSTEM_INFO function. */ + +#ifndef __ASSEMBLER__ +#include <linux/types.h> + +#ifdef CONFIG_64BIT +#define is_pdc_pat() (PDC_TYPE_PAT == pdc_type) +extern int pdc_pat_get_irt_size(unsigned long *num_entries, unsigned long cell_num); +extern int pdc_pat_get_irt(void *r_addr, unsigned long cell_num); +#else /* ! CONFIG_64BIT */ +/* No PAT support for 32-bit kernels...sorry */ +#define is_pdc_pat() (0) +#define pdc_pat_get_irt_size(num_entries, cell_numn) PDC_BAD_PROC +#define pdc_pat_get_irt(r_addr, cell_num) PDC_BAD_PROC +#endif /* ! CONFIG_64BIT */ + + +struct pdc_pat_cell_num { + unsigned long cell_num; + unsigned long cell_loc; +}; + +struct pdc_pat_cpu_num { + unsigned long cpu_num; + unsigned long cpu_loc; +}; + +struct pdc_pat_mem_retinfo { /* PDC_PAT_MEM/PDC_PAT_MEM_PD_INFO (return info) */ + unsigned int ke; /* bit 0: memory inside good memory? */ + unsigned int current_pdt_entries:16; + unsigned int max_pdt_entries:16; + unsigned long Cs_bitmap; + unsigned long Ic_bitmap; + unsigned long good_mem; + unsigned long first_dbe_loc; /* first location of double bit error */ + unsigned long clear_time; /* last PDT clear time (since Jan 1970) */ +}; + +struct pdc_pat_mem_cell_pdt_retinfo { /* PDC_PAT_MEM/PDC_PAT_MEM_CELL_INFO */ + u64 reserved:32; + u64 cs:1; /* clear status: cleared since the last call? */ + u64 current_pdt_entries:15; + u64 ic:1; /* interleaving had to be changed ? */ + u64 max_pdt_entries:15; + unsigned long good_mem; + unsigned long first_dbe_loc; /* first location of double bit error */ + unsigned long clear_time; /* last PDT clear time (since Jan 1970) */ +}; + + +struct pdc_pat_mem_read_pd_retinfo { /* PDC_PAT_MEM/PDC_PAT_MEM_PD_READ */ + unsigned long actual_count_bytes; + unsigned long pdt_entries; +}; + +struct pdc_pat_mem_phys_mem_location { /* PDC_PAT_MEM/PDC_PAT_MEM_ADDRESS */ + u64 cabinet:8; + u64 ign1:8; + u64 ign2:8; + u64 cell_slot:8; + u64 ign3:8; + u64 dimm_slot:8; /* DIMM slot, e.g. 0x1A, 0x2B, show user hex value! */ + u64 ign4:8; + u64 source:4; /* for mem: always 0x07 */ + u64 source_detail:4; /* for mem: always 0x04 (SIMM or DIMM) */ +}; + +struct pdc_pat_pd_addr_map_entry { + unsigned char entry_type; /* 1 = Memory Descriptor Entry Type */ + unsigned char reserve1[5]; + unsigned char memory_type; + unsigned char memory_usage; + unsigned long paddr; + unsigned int pages; /* Length in 4K pages */ + unsigned int reserve2; + unsigned long cell_map; +}; + +/******************************************************************** +* PDC_PAT_CELL[Return Cell Module] memaddr[0] conf_base_addr +* ---------------------------------------------------------- +* Bit 0 to 51 - conf_base_addr +* Bit 52 to 62 - reserved +* Bit 63 - endianess bit +********************************************************************/ +#define PAT_GET_CBA(value) ((value) & 0xfffffffffffff000UL) + +/******************************************************************** +* PDC_PAT_CELL[Return Cell Module] memaddr[1] mod_info +* ---------------------------------------------------- +* Bit 0 to 7 - entity type +* 0 = central agent, 1 = processor, +* 2 = memory controller, 3 = system bus adapter, +* 4 = local bus adapter, 5 = processor bus converter, +* 6 = crossbar fabric connect, 7 = fabric interconnect, +* 8 to 254 reserved, 255 = unknown. +* Bit 8 to 15 - DVI +* Bit 16 to 23 - IOC functions +* Bit 24 to 39 - reserved +* Bit 40 to 63 - mod_pages +* number of 4K pages a module occupies starting at conf_base_addr +********************************************************************/ +#define PAT_GET_ENTITY(value) (((value) >> 56) & 0xffUL) +#define PAT_GET_DVI(value) (((value) >> 48) & 0xffUL) +#define PAT_GET_IOC(value) (((value) >> 40) & 0xffUL) +#define PAT_GET_MOD_PAGES(value) ((value) & 0xffffffUL) + + +/* +** PDC_PAT_CELL_GET_INFO return block +*/ +typedef struct pdc_pat_cell_info_rtn_block { + unsigned long pdc_rev; + unsigned long capabilities; /* see PDC_PAT_CAPABILITY_BIT_* */ + unsigned long reserved0[2]; + unsigned long cell_info; /* 0x20 */ + unsigned long cell_phys_location; + unsigned long cpu_info; + unsigned long cpu_speed; + unsigned long io_chassis_phys_location; + unsigned long cell_io_information; + unsigned long reserved1[2]; + unsigned long io_slot_info_size; /* 0x60 */ + struct { + unsigned long header, info0, info1; + unsigned long phys_loc, hw_path; + } io_slot[16]; + unsigned long cell_mem_size; /* 0x2e8 */ + unsigned long cell_dimm_info_size; + unsigned long dimm_info[16]; + unsigned long fabric_info_size; /* 0x3f8 */ + struct { /* 0x380 */ + unsigned long fabric_info_xbc_port; + unsigned long rc_attached_to_xbc; + } xbc[8*4]; +} pdc_pat_cell_info_rtn_block_t; + + +/* FIXME: mod[508] should really be a union of the various mod components */ +struct pdc_pat_cell_mod_maddr_block { /* PDC_PAT_CELL_MODULE */ + unsigned long cba; /* func 0 cfg space address */ + unsigned long mod_info; /* module information */ + unsigned long mod_location; /* physical location of the module */ + struct hardware_path mod_path; /* module path (device path - layers) */ + unsigned long mod[508]; /* PAT cell module components */ +} __attribute__((aligned(8))) ; + +typedef struct pdc_pat_cell_mod_maddr_block pdc_pat_cell_mod_maddr_block_t; + +extern int pdc_pat_get_PDC_entrypoint(unsigned long *pdc_entry); +extern int pdc_pat_chassis_send_log(unsigned long status, unsigned long data); +extern int pdc_pat_cell_get_number(struct pdc_pat_cell_num *cell_info); +extern int pdc_pat_cell_info(struct pdc_pat_cell_info_rtn_block *info, + unsigned long *actcnt, unsigned long offset, + unsigned long cell_number); +extern int pdc_pat_cell_module(unsigned long *actcnt, unsigned long ploc, + unsigned long mod, unsigned long view_type, void *mem_addr); +extern int pdc_pat_cell_num_to_loc(void *, unsigned long); + +extern int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, unsigned long hpa); + +extern int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr, + unsigned long count, unsigned long offset); +extern int pdc_pat_pd_get_pdc_revisions(unsigned long *legacy_rev, + unsigned long *pat_rev, unsigned long *pdc_cap); +extern int pdc_pat_pd_get_platform_counter(uint64_t **addr, + unsigned long *freq, unsigned long *uniq); + +extern int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *val); +extern int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val); + +extern int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo); +extern int pdc_pat_mem_pdt_cell_info(struct pdc_pat_mem_cell_pdt_retinfo *rinfo, + unsigned long cell); +extern int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, + unsigned long *pdt_entries_ptr, unsigned long max_entries); +extern int pdc_pat_mem_read_pd_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, + unsigned long *pdt_entries_ptr, unsigned long count, + unsigned long offset); +extern int pdc_pat_mem_get_dimm_phys_location( + struct pdc_pat_mem_phys_mem_location *pret, + unsigned long phys_addr); + +#endif /* __ASSEMBLER__ */ + +#endif /* ! __PARISC_PATPDC_H */ diff --git a/arch/parisc/include/asm/perf.h b/arch/parisc/include/asm/perf.h new file mode 100644 index 000000000..2a5a60aff --- /dev/null +++ b/arch/parisc/include/asm/perf.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PERF_H_ +#define _ASM_PERF_H_ + +/* ioctls */ +#define PA_PERF_ON _IO('p', 1) +#define PA_PERF_OFF _IOR('p', 2, unsigned int) +#define PA_PERF_VERSION _IOR('p', 3, int) + +#define PA_PERF_DEV "perf" +#define PA_PERF_MINOR 146 + +/* Interface types */ +#define UNKNOWN_INTF 255 +#define ONYX_INTF 0 +#define CUDA_INTF 1 + +/* Common Onyx and Cuda images */ +#define CPI 0 +#define BUSUTIL 1 +#define TLBMISS 2 +#define TLBHANDMISS 3 +#define PTKN 4 +#define PNTKN 5 +#define IMISS 6 +#define DMISS 7 +#define DMISS_ACCESS 8 +#define BIG_CPI 9 +#define BIG_LS 10 +#define BR_ABORT 11 +#define ISNT 12 +#define QUADRANT 13 +#define RW_PDFET 14 +#define RW_WDFET 15 +#define SHLIB_CPI 16 + +/* Cuda only Images */ +#define FLOPS 17 +#define CACHEMISS 18 +#define BRANCHES 19 +#define CRSTACK 20 +#define I_CACHE_SPEC 21 +#define MAX_CUDA_IMAGES 22 + +/* Onyx only Images */ +#define ADDR_INV_ABORT_ALU 17 +#define BRAD_STALL 18 +#define CNTL_IN_PIPEL 19 +#define DSNT_XFH 20 +#define FET_SIG1 21 +#define FET_SIG2 22 +#define G7_1 23 +#define G7_2 24 +#define G7_3 25 +#define G7_4 26 +#define MPB_LABORT 27 +#define PANIC 28 +#define RARE_INST 29 +#define RW_DFET 30 +#define RW_IFET 31 +#define RW_SDFET 32 +#define SPEC_IFET 33 +#define ST_COND0 34 +#define ST_COND1 35 +#define ST_COND2 36 +#define ST_COND3 37 +#define ST_COND4 38 +#define ST_UNPRED0 39 +#define ST_UNPRED1 40 +#define UNPRED 41 +#define GO_STORE 42 +#define SHLIB_CALL 43 +#define MAX_ONYX_IMAGES 44 + +#endif diff --git a/arch/parisc/include/asm/perf_event.h b/arch/parisc/include/asm/perf_event.h new file mode 100644 index 000000000..8a2925029 --- /dev/null +++ b/arch/parisc/include/asm/perf_event.h @@ -0,0 +1,12 @@ +#ifndef __ASM_PARISC_PERF_EVENT_H +#define __ASM_PARISC_PERF_EVENT_H + +#include <asm/psw.h> + +#define perf_arch_fetch_caller_regs(regs, __ip) { \ + (regs)->gr[0] = KERNEL_PSW; \ + (regs)->iaoq[0] = (__ip); \ + asm volatile("copy %%sp, %0\n":"=r"((regs)->gr[30])); \ +} + +#endif /* __ASM_PARISC_PERF_EVENT_H */ diff --git a/arch/parisc/include/asm/pgalloc.h b/arch/parisc/include/asm/pgalloc.h new file mode 100644 index 000000000..3b84ee93e --- /dev/null +++ b/arch/parisc/include/asm/pgalloc.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PGALLOC_H +#define _ASM_PGALLOC_H + +#include <linux/gfp.h> +#include <linux/mm.h> +#include <linux/threads.h> +#include <asm/processor.h> +#include <asm/fixmap.h> + +#include <asm/cache.h> + +#define __HAVE_ARCH_PMD_ALLOC_ONE +#include <asm-generic/pgalloc.h> + +/* Allocate the top level pgd (page directory) */ +static inline pgd_t *pgd_alloc(struct mm_struct *mm) +{ + return __pgd_alloc(mm, PGD_TABLE_ORDER); +} + +#if CONFIG_PGTABLE_LEVELS == 3 + +/* Three Level Page Table Support for pmd's */ + +static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) +{ + set_pud(pud, __pud((PxD_FLAG_PRESENT | PxD_FLAG_VALID) + + (__u32)(__pa((unsigned long)pmd) >> PxD_VALUE_SHIFT))); +} + +static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) +{ + struct ptdesc *ptdesc; + gfp_t gfp = GFP_PGTABLE_USER; + + if (mm == &init_mm) + gfp = GFP_PGTABLE_KERNEL; + ptdesc = pagetable_alloc(gfp, PMD_TABLE_ORDER); + if (!ptdesc) + return NULL; + if (!pagetable_pmd_ctor(mm, ptdesc)) { + pagetable_free(ptdesc); + return NULL; + } + return ptdesc_address(ptdesc); +} +#endif + +static inline void +pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) +{ + set_pmd(pmd, __pmd((PxD_FLAG_PRESENT | PxD_FLAG_VALID) + + (__u32)(__pa((unsigned long)pte) >> PxD_VALUE_SHIFT))); +} + +#define pmd_populate(mm, pmd, pte_page) \ + pmd_populate_kernel(mm, pmd, page_address(pte_page)) + +#endif diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h new file mode 100644 index 000000000..467b8547a --- /dev/null +++ b/arch/parisc/include/asm/pgtable.h @@ -0,0 +1,500 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_PGTABLE_H +#define _PARISC_PGTABLE_H + +#include <asm/page.h> + +#if CONFIG_PGTABLE_LEVELS == 3 +#include <asm-generic/pgtable-nopud.h> +#elif CONFIG_PGTABLE_LEVELS == 2 +#include <asm-generic/pgtable-nopmd.h> +#endif + +#include <asm/fixmap.h> + +#ifndef __ASSEMBLER__ +/* + * we simulate an x86-style page table for the linux mm code + */ + +#include <linux/bitops.h> +#include <linux/spinlock.h> +#include <linux/mm_types.h> +#include <asm/processor.h> +#include <asm/cache.h> + +/* This is for the serialization of PxTLB broadcasts. At least on the N class + * systems, only one PxTLB inter processor broadcast can be active at any one + * time on the Merced bus. */ +extern spinlock_t pa_tlb_flush_lock; +#if defined(CONFIG_64BIT) && defined(CONFIG_SMP) +extern int pa_serialize_tlb_flushes; +#else +#define pa_serialize_tlb_flushes (0) +#endif + +#define purge_tlb_start(flags) do { \ + if (pa_serialize_tlb_flushes) \ + spin_lock_irqsave(&pa_tlb_flush_lock, flags); \ + else \ + local_irq_save(flags); \ + } while (0) +#define purge_tlb_end(flags) do { \ + if (pa_serialize_tlb_flushes) \ + spin_unlock_irqrestore(&pa_tlb_flush_lock, flags); \ + else \ + local_irq_restore(flags); \ + } while (0) + +/* Purge data and instruction TLB entries. The TLB purge instructions + * are slow on SMP machines since the purge must be broadcast to all CPUs. + */ + +static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr) +{ + unsigned long flags; + + purge_tlb_start(flags); + mtsp(mm->context.space_id, SR_TEMP1); + pdtlb(SR_TEMP1, addr); + pitlb(SR_TEMP1, addr); + purge_tlb_end(flags); +} + +extern void __update_cache(pte_t pte); + +/* Certain architectures need to do special things when PTEs + * within a page table are directly modified. Thus, the following + * hook is made available. + */ +#define set_pte(pteptr, pteval) \ + do { \ + *(pteptr) = (pteval); \ + mb(); \ + } while(0) + +#endif /* !__ASSEMBLER__ */ + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#if CONFIG_PGTABLE_LEVELS == 3 +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, (unsigned long)pmd_val(e)) +#endif +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, (unsigned long)pgd_val(e)) + +/* This is the size of the initially mapped kernel memory */ +#if defined(CONFIG_64BIT) || defined(CONFIG_KALLSYMS) +#define KERNEL_INITIAL_ORDER 26 /* 1<<26 = 64MB */ +#else +#define KERNEL_INITIAL_ORDER 25 /* 1<<25 = 32MB */ +#endif +#define KERNEL_INITIAL_SIZE (1 << KERNEL_INITIAL_ORDER) + +#if CONFIG_PGTABLE_LEVELS == 3 +#define PMD_TABLE_ORDER 1 +#define PGD_TABLE_ORDER 0 +#else +#define PGD_TABLE_ORDER 1 +#endif + +/* Definitions for 3rd level (we use PLD here for Page Lower directory + * because PTE_SHIFT is used lower down to mean shift that has to be + * done to get usable bits out of the PTE) */ +#define PLD_SHIFT PAGE_SHIFT +#define PLD_SIZE PAGE_SIZE +#define BITS_PER_PTE (PAGE_SHIFT - BITS_PER_PTE_ENTRY) +#define PTRS_PER_PTE (1UL << BITS_PER_PTE) + +/* Definitions for 2nd level */ +#if CONFIG_PGTABLE_LEVELS == 3 +#define PMD_SHIFT (PLD_SHIFT + BITS_PER_PTE) +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PMD_MASK (~(PMD_SIZE-1)) +#define BITS_PER_PMD (PAGE_SHIFT + PMD_TABLE_ORDER - BITS_PER_PMD_ENTRY) +#define PTRS_PER_PMD (1UL << BITS_PER_PMD) +#else +#define BITS_PER_PMD 0 +#endif + +/* Definitions for 1st level */ +#define PGDIR_SHIFT (PLD_SHIFT + BITS_PER_PTE + BITS_PER_PMD) +#if (PGDIR_SHIFT + PAGE_SHIFT + PGD_TABLE_ORDER - BITS_PER_PGD_ENTRY) > BITS_PER_LONG +#define BITS_PER_PGD (BITS_PER_LONG - PGDIR_SHIFT) +#else +#define BITS_PER_PGD (PAGE_SHIFT + PGD_TABLE_ORDER - BITS_PER_PGD_ENTRY) +#endif +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) +#define PTRS_PER_PGD (1UL << BITS_PER_PGD) +#define USER_PTRS_PER_PGD PTRS_PER_PGD + +#ifdef CONFIG_64BIT +#define MAX_ADDRBITS (PGDIR_SHIFT + BITS_PER_PGD) +#define MAX_ADDRESS (1UL << MAX_ADDRBITS) +#define SPACEID_SHIFT (MAX_ADDRBITS - 32) +#else +#define MAX_ADDRBITS (BITS_PER_LONG) +#define MAX_ADDRESS (1ULL << MAX_ADDRBITS) +#define SPACEID_SHIFT 0 +#endif + +/* This calculates the number of initial pages we need for the initial + * page tables */ +#if (KERNEL_INITIAL_ORDER) >= (PLD_SHIFT + BITS_PER_PTE) +# define PT_INITIAL (1 << (KERNEL_INITIAL_ORDER - PLD_SHIFT - BITS_PER_PTE)) +#else +# define PT_INITIAL (1) /* all initial PTEs fit into one page */ +#endif + +/* + * pgd entries used up by user/kernel: + */ + +/* NB: The tlb miss handlers make certain assumptions about the order */ +/* of the following bits, so be careful (One example, bits 25-31 */ +/* are moved together in one instruction). */ + +#define _PAGE_READ_BIT 31 /* (0x001) read access allowed */ +#define _PAGE_WRITE_BIT 30 /* (0x002) write access allowed */ +#define _PAGE_EXEC_BIT 29 /* (0x004) execute access allowed */ +#define _PAGE_GATEWAY_BIT 28 /* (0x008) privilege promotion allowed */ +#define _PAGE_DMB_BIT 27 /* (0x010) Data Memory Break enable (B bit) */ +#define _PAGE_DIRTY_BIT 26 /* (0x020) Page Dirty (D bit) */ +#define _PAGE_REFTRAP_BIT 25 /* (0x040) Page Ref. Trap enable (T bit) */ +#define _PAGE_NO_CACHE_BIT 24 /* (0x080) Uncached Page (U bit) */ +#define _PAGE_ACCESSED_BIT 23 /* (0x100) Software: Page Accessed */ +#define _PAGE_PRESENT_BIT 22 /* (0x200) Software: translation valid */ +#define _PAGE_HPAGE_BIT 21 /* (0x400) Software: Huge Page */ +#define _PAGE_USER_BIT 20 /* (0x800) Software: User accessible page */ +#ifdef CONFIG_HUGETLB_PAGE +#define _PAGE_SPECIAL_BIT _PAGE_DMB_BIT /* DMB feature is currently unused */ +#else +#define _PAGE_SPECIAL_BIT _PAGE_HPAGE_BIT /* use unused HUGE PAGE bit */ +#endif + +/* N.B. The bits are defined in terms of a 32 bit word above, so the */ +/* following macro is ok for both 32 and 64 bit. */ + +#define xlate_pabit(x) (31 - x) + +/* this defines the shift to the usable bits in the PTE it is set so + * that the valid bits _PAGE_PRESENT_BIT and _PAGE_USER_BIT are set + * to zero */ +#define PTE_SHIFT xlate_pabit(_PAGE_USER_BIT) + +/* PFN_PTE_SHIFT defines the shift of a PTE value to access the PFN field */ +#define PFN_PTE_SHIFT 12 + +#define _PAGE_READ (1 << xlate_pabit(_PAGE_READ_BIT)) +#define _PAGE_WRITE (1 << xlate_pabit(_PAGE_WRITE_BIT)) +#define _PAGE_RW (_PAGE_READ | _PAGE_WRITE) +#define _PAGE_EXEC (1 << xlate_pabit(_PAGE_EXEC_BIT)) +#define _PAGE_GATEWAY (1 << xlate_pabit(_PAGE_GATEWAY_BIT)) +#define _PAGE_DMB (1 << xlate_pabit(_PAGE_DMB_BIT)) +#define _PAGE_DIRTY (1 << xlate_pabit(_PAGE_DIRTY_BIT)) +#define _PAGE_REFTRAP (1 << xlate_pabit(_PAGE_REFTRAP_BIT)) +#define _PAGE_NO_CACHE (1 << xlate_pabit(_PAGE_NO_CACHE_BIT)) +#define _PAGE_ACCESSED (1 << xlate_pabit(_PAGE_ACCESSED_BIT)) +#define _PAGE_PRESENT (1 << xlate_pabit(_PAGE_PRESENT_BIT)) +#define _PAGE_HUGE (1 << xlate_pabit(_PAGE_HPAGE_BIT)) +#define _PAGE_USER (1 << xlate_pabit(_PAGE_USER_BIT)) +#define _PAGE_SPECIAL (1 << xlate_pabit(_PAGE_SPECIAL_BIT)) + +#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | _PAGE_DIRTY | _PAGE_ACCESSED) +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_SPECIAL) +#define _PAGE_KERNEL_RO (_PAGE_PRESENT | _PAGE_READ | _PAGE_DIRTY | _PAGE_ACCESSED) +#define _PAGE_KERNEL_EXEC (_PAGE_KERNEL_RO | _PAGE_EXEC) +#define _PAGE_KERNEL_RWX (_PAGE_KERNEL_EXEC | _PAGE_WRITE) +#define _PAGE_KERNEL (_PAGE_KERNEL_RO | _PAGE_WRITE) + +/* We borrow bit 23 to store the exclusive marker in swap PTEs. */ +#define _PAGE_SWP_EXCLUSIVE _PAGE_ACCESSED + +/* The pgd/pmd contains a ptr (in phys addr space); since all pgds/pmds + * are page-aligned, we don't care about the PAGE_OFFSET bits, except + * for a few meta-information bits, so we shift the address to be + * able to effectively address 40/42/44-bits of physical address space + * depending on 4k/16k/64k PAGE_SIZE */ +#define _PxD_PRESENT_BIT 31 +#define _PxD_VALID_BIT 30 + +#define PxD_FLAG_PRESENT (1 << xlate_pabit(_PxD_PRESENT_BIT)) +#define PxD_FLAG_VALID (1 << xlate_pabit(_PxD_VALID_BIT)) +#define PxD_FLAG_MASK (0xf) +#define PxD_FLAG_SHIFT (4) +#define PxD_VALUE_SHIFT (PFN_PTE_SHIFT-PxD_FLAG_SHIFT) + +#ifndef __ASSEMBLER__ + +#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_USER) +#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_WRITE) +/* Others seem to make this executable, I don't know if that's correct + or not. The stack is mapped this way though so this is necessary + in the short term - dhd@linuxcare.com, 2000-08-08 */ +#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ) +#define PAGE_WRITEONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_WRITE) +#define PAGE_EXECREAD __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_EXEC) +#define PAGE_COPY PAGE_EXECREAD +#define PAGE_RWX __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_WRITE | _PAGE_EXEC) +#define PAGE_KERNEL __pgprot(_PAGE_KERNEL) +#define PAGE_KERNEL_EXEC __pgprot(_PAGE_KERNEL_EXEC) +#define PAGE_KERNEL_RWX __pgprot(_PAGE_KERNEL_RWX) +#define PAGE_KERNEL_RO __pgprot(_PAGE_KERNEL_RO) +#define PAGE_KERNEL_UNC __pgprot(_PAGE_KERNEL | _PAGE_NO_CACHE) +#define PAGE_GATEWAY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_GATEWAY| _PAGE_READ) + + +/* + * We could have an execute only page using "gateway - promote to priv + * level 3", but that is kind of silly. So, the way things are defined + * now, we must always have read permission for pages with execute + * permission. For the fun of it we'll go ahead and support write only + * pages. + */ + + /*xwr*/ + +extern pgd_t swapper_pg_dir[]; /* declared in init_task.c */ + +/* initial page tables for 0-8MB for kernel */ + +extern pte_t pg0[]; + +#define pte_none(x) (pte_val(x) == 0) +#define pte_present(x) (pte_val(x) & _PAGE_PRESENT) +#define pte_user(x) (pte_val(x) & _PAGE_USER) +#define pte_clear(mm, addr, xp) set_pte_at((mm), (addr), (xp), __pte(0)) + +#define pmd_flag(x) (pmd_val(x) & PxD_FLAG_MASK) +#define pmd_address(x) ((unsigned long)(pmd_val(x) &~ PxD_FLAG_MASK) << PxD_VALUE_SHIFT) +#define pud_flag(x) (pud_val(x) & PxD_FLAG_MASK) +#define pud_address(x) ((unsigned long)(pud_val(x) &~ PxD_FLAG_MASK) << PxD_VALUE_SHIFT) +#define pgd_flag(x) (pgd_val(x) & PxD_FLAG_MASK) +#define pgd_address(x) ((unsigned long)(pgd_val(x) &~ PxD_FLAG_MASK) << PxD_VALUE_SHIFT) + +#define pmd_none(x) (!pmd_val(x)) +#define pmd_bad(x) (!(pmd_flag(x) & PxD_FLAG_VALID)) +#define pmd_present(x) (pmd_flag(x) & PxD_FLAG_PRESENT) +static inline void pmd_clear(pmd_t *pmd) { + set_pmd(pmd, __pmd(0)); +} + + + +#if CONFIG_PGTABLE_LEVELS == 3 +#define pud_pgtable(pud) ((pmd_t *) __va(pud_address(pud))) +#define pud_page(pud) virt_to_page((void *)pud_pgtable(pud)) + +/* For 64 bit we have three level tables */ + +#define pud_none(x) (!pud_val(x)) +#define pud_bad(x) (!(pud_flag(x) & PxD_FLAG_VALID)) +#define pud_present(x) (pud_flag(x) & PxD_FLAG_PRESENT) +static inline void pud_clear(pud_t *pud) { + set_pud(pud, __pud(0)); +} +#endif + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; } +static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; } +static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; } + +static inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; } +static inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; } +static inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_WRITE; return pte; } +static inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; } +static inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_ACCESSED; return pte; } +static inline pte_t pte_mkwrite_novma(pte_t pte) { pte_val(pte) |= _PAGE_WRITE; return pte; } +static inline pte_t pte_mkspecial(pte_t pte) { pte_val(pte) |= _PAGE_SPECIAL; return pte; } + +/* + * Huge pte definitions. + */ +#ifdef CONFIG_HUGETLB_PAGE +#define pte_huge(pte) (pte_val(pte) & _PAGE_HUGE) +#define pte_mkhuge(pte) (__pte(pte_val(pte) | \ + (parisc_requires_coherency() ? 0 : _PAGE_HUGE))) +#else +#define pte_huge(pte) (0) +#define pte_mkhuge(pte) (pte) +#endif + + +#define __mk_pte(addr,pgprot) \ +({ \ + pte_t __pte; \ + \ + pte_val(__pte) = ((((addr)>>PAGE_SHIFT)<<PFN_PTE_SHIFT) + pgprot_val(pgprot)); \ + \ + __pte; \ +}) + +static inline pte_t pfn_pte(unsigned long pfn, pgprot_t pgprot) +{ + pte_t pte; + pte_val(pte) = (pfn << PFN_PTE_SHIFT) | pgprot_val(pgprot); + return pte; +} + +static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; } + +/* Permanent address of a page. On parisc we don't have highmem. */ + +#define pte_pfn(x) (pte_val(x) >> PFN_PTE_SHIFT) + +#define pte_page(pte) (pfn_to_page(pte_pfn(pte))) + +static inline unsigned long pmd_page_vaddr(pmd_t pmd) +{ + return ((unsigned long) __va(pmd_address(pmd))); +} + +#define pmd_pfn(pmd) (pmd_address(pmd) >> PAGE_SHIFT) +#define __pmd_page(pmd) ((unsigned long) __va(pmd_address(pmd))) +#define pmd_page(pmd) virt_to_page((void *)__pmd_page(pmd)) + +/* Find an entry in the second-level page table.. */ + +extern void paging_init (void); + +static inline void set_ptes(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte, unsigned int nr) +{ + if (pte_present(pte) && pte_user(pte)) + __update_cache(pte); + for (;;) { + *ptep = pte; + purge_tlb_entries(mm, addr); + if (--nr == 0) + break; + ptep++; + pte_val(pte) += 1 << PFN_PTE_SHIFT; + addr += PAGE_SIZE; + } +} +#define set_ptes set_ptes +#define set_pte_at(mm, addr, ptep, pte) set_ptes(mm, addr, ptep, pte, 1) + +/* Used for deferring calls to flush_dcache_page() */ + +#define PG_dcache_dirty PG_arch_1 + +#define update_mmu_cache_range(vmf, vma, addr, ptep, nr) __update_cache(*ptep) +#define update_mmu_cache(vma, addr, ptep) __update_cache(*ptep) + +/* + * Encode/decode swap entries and swap PTEs. Swap PTEs are all PTEs that + * are !pte_none() && !pte_present(). + * + * Format of swap PTEs (32bit): + * + * 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * <---------------- offset -----------------> P E <ofs> < type -> + * + * E is the exclusive marker that is not stored in swap entries. + * _PAGE_PRESENT (P) must be 0. + * + * For the 64bit version, the offset is extended by 32bit. + */ +#define __swp_type(x) ((x).val & 0x1f) +#define __swp_offset(x) ( (((x).val >> 5) & 0x7) | \ + (((x).val >> 10) << 3) ) +#define __swp_entry(type, offset) ((swp_entry_t) { \ + ((type) & 0x1f) | \ + ((offset & 0x7) << 5) | \ + ((offset >> 3) << 10) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) + +static inline bool pte_swp_exclusive(pte_t pte) +{ + return pte_val(pte) & _PAGE_SWP_EXCLUSIVE; +} + +static inline pte_t pte_swp_mkexclusive(pte_t pte) +{ + pte_val(pte) |= _PAGE_SWP_EXCLUSIVE; + return pte; +} + +static inline pte_t pte_swp_clear_exclusive(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_SWP_EXCLUSIVE; + return pte; +} + +static inline pte_t ptep_get(pte_t *ptep) +{ + return READ_ONCE(*ptep); +} +#define ptep_get ptep_get + +static inline bool ptep_test_and_clear_young(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ + pte_t pte; + + pte = ptep_get(ptep); + if (!pte_young(pte)) { + return false; + } + set_pte_at(vma->vm_mm, addr, ptep, pte_mkold(pte)); + return true; +} + +bool ptep_clear_flush_young(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep); +pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep); + +struct mm_struct; +static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + set_pte_at(mm, addr, ptep, pte_wrprotect(*ptep)); +} + +#define pte_same(A,B) (pte_val(A) == pte_val(B)) + +#endif /* !__ASSEMBLER__ */ + + +/* TLB page size encoding - see table 3-1 in parisc20.pdf */ +#define _PAGE_SIZE_ENCODING_4K 0 +#define _PAGE_SIZE_ENCODING_16K 1 +#define _PAGE_SIZE_ENCODING_64K 2 +#define _PAGE_SIZE_ENCODING_256K 3 +#define _PAGE_SIZE_ENCODING_1M 4 +#define _PAGE_SIZE_ENCODING_4M 5 +#define _PAGE_SIZE_ENCODING_16M 6 +#define _PAGE_SIZE_ENCODING_64M 7 + +#if defined(CONFIG_PARISC_PAGE_SIZE_4KB) +# define _PAGE_SIZE_ENCODING_DEFAULT _PAGE_SIZE_ENCODING_4K +#elif defined(CONFIG_PARISC_PAGE_SIZE_16KB) +# define _PAGE_SIZE_ENCODING_DEFAULT _PAGE_SIZE_ENCODING_16K +#elif defined(CONFIG_PARISC_PAGE_SIZE_64KB) +# define _PAGE_SIZE_ENCODING_DEFAULT _PAGE_SIZE_ENCODING_64K +#endif + + +#define pgprot_noncached(prot) __pgprot(pgprot_val(prot) | _PAGE_NO_CACHE) + +/* We provide our own get_unmapped_area to provide cache coherency */ + +#define HAVE_ARCH_UNMAPPED_AREA +#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN + +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG +#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH +#define __HAVE_ARCH_PTEP_CLEAR_FLUSH +#define __HAVE_ARCH_PTEP_SET_WRPROTECT +#define __HAVE_ARCH_PTE_SAME + +#endif /* _PARISC_PGTABLE_H */ diff --git a/arch/parisc/include/asm/prefetch.h b/arch/parisc/include/asm/prefetch.h new file mode 100644 index 000000000..748eefb27 --- /dev/null +++ b/arch/parisc/include/asm/prefetch.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * include/asm-parisc/prefetch.h + * + * PA 2.0 defines data prefetch instructions on page 6-11 of the Kane book. + * In addition, many implementations do hardware prefetching of both + * instructions and data. + * + * PA7300LC (page 14-4 of the ERS) also implements prefetching by a load + * to gr0 but not in a way that Linux can use. If the load would cause an + * interruption (eg due to prefetching 0), it is suppressed on PA2.0 + * processors, but not on 7300LC. + * + */ + +#ifndef __ASM_PARISC_PREFETCH_H +#define __ASM_PARISC_PREFETCH_H + +#ifndef __ASSEMBLER__ +#ifdef CONFIG_PREFETCH + +#define ARCH_HAS_PREFETCH +static inline void prefetch(const void *addr) +{ + __asm__( +#ifndef CONFIG_PA20 + /* Need to avoid prefetch of NULL on PA7300LC */ + " extrw,u,= %0,31,32,%%r0\n" +#endif + " ldw 0(%0), %%r0" : : "r" (addr)); +} + +/* LDD is a PA2.0 addition. */ +#ifdef CONFIG_PA20 +#define ARCH_HAS_PREFETCHW +static inline void prefetchw(const void *addr) +{ + __asm__("ldd 0(%0), %%r0" : : "r" (addr)); +} +#endif /* CONFIG_PA20 */ + +#endif /* CONFIG_PREFETCH */ +#endif /* __ASSEMBLER__ */ + +#endif /* __ASM_PARISC_PROCESSOR_H */ diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h new file mode 100644 index 000000000..dd0b5e199 --- /dev/null +++ b/arch/parisc/include/asm/processor.h @@ -0,0 +1,330 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * include/asm-parisc/processor.h + * + * Copyright (C) 1994 Linus Torvalds + * Copyright (C) 2001 Grant Grundler + */ + +#ifndef __ASM_PARISC_PROCESSOR_H +#define __ASM_PARISC_PROCESSOR_H + +#ifndef __ASSEMBLER__ +#include <linux/threads.h> +#include <linux/irqreturn.h> + +#include <asm/assembly.h> +#include <asm/prefetch.h> +#include <asm/hardware.h> +#include <asm/pdc.h> +#include <asm/ptrace.h> +#include <asm/types.h> +#include <asm/percpu.h> +#endif /* __ASSEMBLER__ */ + +#define HAVE_ARCH_PICK_MMAP_LAYOUT + +#define TASK_SIZE_OF(tsk) ((tsk)->thread.task_size) +#define TASK_SIZE TASK_SIZE_OF(current) +#define TASK_UNMAPPED_BASE (current->thread.map_base) + +#define DEFAULT_TASK_SIZE32 (0xFFF00000UL) +#define DEFAULT_MAP_BASE32 (0x40000000UL) + +#ifdef CONFIG_64BIT +#define DEFAULT_TASK_SIZE (MAX_ADDRESS-0xf000000) +#define DEFAULT_MAP_BASE (0x200000000UL) +#else +#define DEFAULT_TASK_SIZE DEFAULT_TASK_SIZE32 +#define DEFAULT_MAP_BASE DEFAULT_MAP_BASE32 +#endif + +/* XXX: STACK_TOP actually should be STACK_BOTTOM for parisc. + * prumpf */ + +#define STACK_TOP TASK_SIZE +#define STACK_TOP_MAX DEFAULT_TASK_SIZE + +#ifndef __ASSEMBLER__ + +struct rlimit; +unsigned long mmap_upper_limit(const struct rlimit *rlim_stack); +unsigned long calc_max_stack_size(unsigned long stack_max); + +/* + * Data detected about CPUs at boot time which is the same for all CPU's. + * HP boxes are SMP - ie identical processors. + * + * FIXME: some CPU rev info may be processor specific... + */ +struct system_cpuinfo_parisc { + unsigned int cpu_count; + unsigned int cpu_hz; + unsigned int hversion; + unsigned int sversion; + enum cpu_type cpu_type; + + struct { + struct pdc_model model; + unsigned long versions; + unsigned long cpuid; + unsigned long capabilities; + char sys_model_name[81]; /* PDC-ROM returnes this model name */ + } pdc; + + const char *cpu_name; /* e.g. "PA7300LC (PCX-L2)" */ + const char *family_name; /* e.g. "1.1e" */ +}; + + +/* Per CPU data structure - ie varies per CPU. */ +struct cpuinfo_parisc { + unsigned long it_value; /* Interval Timer at last timer Intr */ + unsigned long irq_count; /* number of IRQ's since boot */ + unsigned long cpuid; /* aka slot_number or set to NO_PROC_ID */ + unsigned long hpa; /* Host Physical address */ + unsigned long txn_addr; /* MMIO addr of EIR or id_eid */ +#ifdef CONFIG_SMP + unsigned long pending_ipi; /* bitmap of type ipi_message_type */ +#endif + unsigned long bh_count; /* number of times bh was invoked */ + unsigned long fp_rev; + unsigned long fp_model; + unsigned long cpu_num; /* CPU number from PAT firmware */ + unsigned long cpu_loc; /* CPU location from PAT firmware */ + unsigned int state; + struct parisc_device *dev; +}; + +extern struct system_cpuinfo_parisc boot_cpu_data; +DECLARE_PER_CPU(struct cpuinfo_parisc, cpu_data); +extern int time_keeper_id; /* CPU used for timekeeping */ + +#define CPU_HVERSION ((boot_cpu_data.hversion >> 4) & 0x0FFF) + +struct thread_struct { + struct pt_regs regs; + unsigned long task_size; + unsigned long map_base; + unsigned long flags; +}; + +#define task_pt_regs(tsk) ((struct pt_regs *)&((tsk)->thread.regs)) + +/* Thread struct flags. */ +#define PARISC_UAC_NOPRINT (1UL << 0) /* see prctl and unaligned.c */ +#define PARISC_UAC_SIGBUS (1UL << 1) +#define PARISC_KERNEL_DEATH (1UL << 31) /* see die_if_kernel()... */ + +#define PARISC_UAC_SHIFT 0 +#define PARISC_UAC_MASK (PARISC_UAC_NOPRINT|PARISC_UAC_SIGBUS) + +#define SET_UNALIGN_CTL(task,value) \ + ({ \ + (task)->thread.flags = (((task)->thread.flags & ~PARISC_UAC_MASK) \ + | (((value) << PARISC_UAC_SHIFT) & \ + PARISC_UAC_MASK)); \ + 0; \ + }) + +#define GET_UNALIGN_CTL(task,addr) \ + ({ \ + put_user(((task)->thread.flags & PARISC_UAC_MASK) \ + >> PARISC_UAC_SHIFT, (int __user *) (addr)); \ + }) + +#define INIT_THREAD { \ + .regs = { .gr = { 0, }, \ + .fr = { 0, }, \ + .sr = { 0, }, \ + .iasq = { 0, }, \ + .iaoq = { 0, }, \ + .cr27 = 0, \ + }, \ + .task_size = DEFAULT_TASK_SIZE, \ + .map_base = DEFAULT_MAP_BASE, \ + .flags = 0 \ + } + +struct task_struct; +void show_trace(struct task_struct *task, unsigned long *stack); + +/* + * Start user thread in another space. + * + * Note that we set both the iaoq and r31 to the new pc. When + * the kernel initially calls execve it will return through an + * rfi path that will use the values in the iaoq. The execve + * syscall path will return through the gateway page, and + * that uses r31 to branch to. + * + * For ELF we clear r23, because the dynamic linker uses it to pass + * the address of the finalizer function. + * + * We also initialize sr3 to an illegal value (illegal for our + * implementation, not for the architecture). + */ +typedef unsigned int elf_caddr_t; + +/* The ELF abi wants things done a "wee bit" differently than + * som does. Supporting this behavior here avoids + * having our own version of create_elf_tables. + * + * Oh, and yes, that is not a typo, we are really passing argc in r25 + * and argv in r24 (rather than r26 and r25). This is because that's + * where __libc_start_main wants them. + * + * Duplicated from dl-machine.h for the benefit of readers: + * + * Our initial stack layout is rather different from everyone else's + * due to the unique PA-RISC ABI. As far as I know it looks like + * this: + + ----------------------------------- (user startup code creates this frame) + | 32 bytes of magic | + |---------------------------------| + | 32 bytes argument/sp save area | + |---------------------------------| (bprm->p) + | ELF auxiliary info | + | (up to 28 words) | + |---------------------------------| + | NULL | + |---------------------------------| + | Environment pointers | + |---------------------------------| + | NULL | + |---------------------------------| + | Argument pointers | + |---------------------------------| <- argv + | argc (1 word) | + |---------------------------------| <- bprm->exec (HACK!) + | N bytes of slack | + |---------------------------------| + | filename passed to execve | + |---------------------------------| (mm->env_end) + | env strings | + |---------------------------------| (mm->env_start, mm->arg_end) + | arg strings | + |---------------------------------| + | additional faked arg strings if | + | we're invoked via binfmt_script | + |---------------------------------| (mm->arg_start) + stack base is at TASK_SIZE - rlim_max. + +on downward growing arches, it looks like this: + stack base at TASK_SIZE + | filename passed to execve + | env strings + | arg strings + | faked arg strings + | slack + | ELF + | envps + | argvs + | argc + + * The pleasant part of this is that if we need to skip arguments we + * can just decrement argc and move argv, because the stack pointer + * is utterly unrelated to the location of the environment and + * argument vectors. + * + * Note that the S/390 people took the easy way out and hacked their + * GCC to make the stack grow downwards. + * + * Final Note: For entry from syscall, the W (wide) bit of the PSW + * is stuffed into the lowest bit of the user sp (%r30), so we fill + * it in here from the current->personality + */ + +#define USER_WIDE_MODE (!is_32bit_task()) + +#define start_thread(regs, new_pc, new_sp) do { \ + elf_addr_t *sp = (elf_addr_t *)new_sp; \ + __u32 spaceid = (__u32)current->mm->context.space_id; \ + elf_addr_t pc = (elf_addr_t)new_pc | 3; \ + elf_caddr_t *argv = (elf_caddr_t *)bprm->exec + 1; \ + \ + regs->iasq[0] = spaceid; \ + regs->iasq[1] = spaceid; \ + regs->iaoq[0] = pc; \ + regs->iaoq[1] = pc + 4; \ + regs->sr[2] = LINUX_GATEWAY_SPACE; \ + regs->sr[3] = 0xffff; \ + regs->sr[4] = spaceid; \ + regs->sr[5] = spaceid; \ + regs->sr[6] = spaceid; \ + regs->sr[7] = spaceid; \ + regs->gr[ 0] = USER_PSW | (USER_WIDE_MODE ? PSW_W : 0); \ + regs->fr[ 0] = 0LL; \ + regs->fr[ 1] = 0LL; \ + regs->fr[ 2] = 0LL; \ + regs->fr[ 3] = 0LL; \ + regs->gr[30] = (((unsigned long)sp + 63) &~ 63) | (USER_WIDE_MODE ? 1 : 0); \ + regs->gr[31] = pc; \ + \ + get_user(regs->gr[25], (argv - 1)); \ + regs->gr[24] = (long) argv; \ + regs->gr[23] = 0; \ +} while(0) + +struct mm_struct; + +extern unsigned long __get_wchan(struct task_struct *p); + +#define KSTK_EIP(tsk) ((tsk)->thread.regs.iaoq[0]) +#define KSTK_ESP(tsk) ((tsk)->thread.regs.gr[30]) + +#define cpu_relax() barrier() + +/* + * parisc_requires_coherency() is used to identify the combined VIPT/PIPT + * cached CPUs which require a guarantee of coherency (no inequivalent aliases + * with different data, whether clean or not) to operate + */ +#ifdef CONFIG_PA8X00 +extern int _parisc_requires_coherency; +#define parisc_requires_coherency() _parisc_requires_coherency +#else +#define parisc_requires_coherency() (0) +#endif + +extern int running_on_qemu; +extern int parisc_narrow_firmware; + +extern void __noreturn toc_intr(struct pt_regs *regs); +extern void toc_handler(void); +extern unsigned int toc_handler_size; +extern unsigned int toc_handler_csum; +extern void do_cpu_irq_mask(struct pt_regs *); +extern irqreturn_t timer_interrupt(int, void *); +extern irqreturn_t ipi_interrupt(int, void *); +extern void parisc_clockevent_init(void); +extern void handle_interruption(int, struct pt_regs *); + +/* called from assembly code: */ +extern void start_parisc(void); +extern void smp_callin(unsigned long); +extern void sys_rt_sigreturn(struct pt_regs *, int); +extern void do_notify_resume(struct pt_regs *, long); +extern long do_syscall_trace_enter(struct pt_regs *); +extern void do_syscall_trace_exit(struct pt_regs *); + +/* CPU startup and info */ +struct seq_file; +extern void early_trap_init(void); +extern void collect_boot_cpu_data(void); +extern void btlb_init_per_cpu(void); +extern int show_cpuinfo (struct seq_file *m, void *v); + +/* driver code in driver/parisc */ +extern void processor_init(void); +struct parisc_device; +struct resource; +extern void sba_distributed_lmmio(struct parisc_device *, struct resource *); +extern void sba_directed_lmmio(struct parisc_device *, struct resource *); +extern void lba_set_iregs(struct parisc_device *lba, u32 ibase, u32 imask); +extern void ccio_cujo20_fixup(struct parisc_device *dev, u32 iovp); + +#endif /* __ASSEMBLER__ */ + +#endif /* __ASM_PARISC_PROCESSOR_H */ diff --git a/arch/parisc/include/asm/psw.h b/arch/parisc/include/asm/psw.h new file mode 100644 index 000000000..9140e1ab7 --- /dev/null +++ b/arch/parisc/include/asm/psw.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_PSW_H +#define _PARISC_PSW_H + +#define PSW_I 0x00000001 +#define PSW_D 0x00000002 +#define PSW_P 0x00000004 +#define PSW_Q 0x00000008 + +#define PSW_R 0x00000010 +#define PSW_F 0x00000020 +#define PSW_G 0x00000040 /* PA1.x only */ +#define PSW_O 0x00000080 /* PA2.0 only */ + +/* ssm/rsm instructions number PSW_W and PSW_E differently */ +#define PSW_SM_I PSW_I /* Enable External Interrupts */ +#define PSW_SM_D PSW_D +#define PSW_SM_P PSW_P +#define PSW_SM_Q PSW_Q /* Enable Interrupt State Collection */ +#define PSW_SM_R PSW_R /* Enable Recover Counter Trap */ +#define PSW_SM_W 0x200 /* PA2.0 only : Enable Wide Mode */ + +#define PSW_SM_QUIET PSW_SM_R+PSW_SM_Q+PSW_SM_P+PSW_SM_D+PSW_SM_I + +#define PSW_CB 0x0000ff00 + +#define PSW_M 0x00010000 +#define PSW_V 0x00020000 +#define PSW_C 0x00040000 +#define PSW_B 0x00080000 + +#define PSW_X 0x00100000 +#define PSW_N 0x00200000 +#define PSW_L 0x00400000 +#define PSW_H 0x00800000 + +#define PSW_T 0x01000000 +#define PSW_S 0x02000000 +#define PSW_E 0x04000000 +#define PSW_W 0x08000000 /* PA2.0 only */ +#define PSW_W_BIT 36 /* PA2.0 only */ + +#define PSW_Z 0x40000000 /* PA1.x only */ +#define PSW_Y 0x80000000 /* PA1.x only */ + +#ifdef CONFIG_64BIT +# define PSW_HI_CB 0x000000ff /* PA2.0 only */ +#endif + +#ifdef CONFIG_64BIT +# define USER_PSW_HI_MASK PSW_HI_CB +# define WIDE_PSW PSW_W +#else +# define WIDE_PSW 0 +#endif + +/* Used when setting up for rfi */ +#define KERNEL_PSW (WIDE_PSW | PSW_C | PSW_Q | PSW_P | PSW_D) +#define REAL_MODE_PSW (WIDE_PSW | PSW_Q) +#define USER_PSW_MASK (WIDE_PSW | PSW_T | PSW_N | PSW_X | PSW_B | PSW_V | PSW_CB) +#define USER_PSW (PSW_C | PSW_Q | PSW_P | PSW_D | PSW_I) + +#ifndef __ASSEMBLER__ + +/* The program status word as bitfields. */ +struct pa_psw { + unsigned int y:1; + unsigned int z:1; + unsigned int rv:2; + unsigned int w:1; + unsigned int e:1; + unsigned int s:1; + unsigned int t:1; + + unsigned int h:1; + unsigned int l:1; + unsigned int n:1; + unsigned int x:1; + unsigned int b:1; + unsigned int c:1; + unsigned int v:1; + unsigned int m:1; + + unsigned int cb:8; + + unsigned int o:1; + unsigned int g:1; + unsigned int f:1; + unsigned int r:1; + unsigned int q:1; + unsigned int p:1; + unsigned int d:1; + unsigned int i:1; +}; + +#ifdef CONFIG_64BIT +#define pa_psw(task) ((struct pa_psw *) ((char *) (task) + TASK_PT_PSW + 4)) +#else +#define pa_psw(task) ((struct pa_psw *) ((char *) (task) + TASK_PT_PSW)) +#endif + +#endif /* !__ASSEMBLER__ */ + +#endif diff --git a/arch/parisc/include/asm/ptrace.h b/arch/parisc/include/asm/ptrace.h new file mode 100644 index 000000000..5e1f52b39 --- /dev/null +++ b/arch/parisc/include/asm/ptrace.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* written by Philipp Rumpf, Copyright (C) 1999 SuSE GmbH Nuernberg +** Copyright (C) 2000 Grant Grundler, Hewlett-Packard +*/ +#ifndef _PARISC_PTRACE_H +#define _PARISC_PTRACE_H + +#include <asm/assembly.h> +#include <uapi/asm/ptrace.h> +#include <linux/compiler.h> + +#define task_regs(task) ((struct pt_regs *) ((char *)(task) + TASK_REGS)) + +#define arch_has_single_step() 1 +#define arch_has_block_step() 1 + +/* XXX should we use iaoq[1] or iaoq[0] ? */ +#define user_mode(regs) (((regs)->iaoq[0] & 3) != PRIV_KERNEL) +#define user_space(regs) ((regs)->iasq[1] != PRIV_KERNEL) +#define instruction_pointer(regs) ((regs)->iaoq[0] & ~3) +#define user_stack_pointer(regs) ((regs)->gr[30]) +unsigned long profile_pc(struct pt_regs *); + +static inline unsigned long regs_return_value(struct pt_regs *regs) +{ + return regs->gr[28]; +} + +static inline void instruction_pointer_set(struct pt_regs *regs, + unsigned long val) +{ + regs->iaoq[0] = val; + regs->iaoq[1] = val + 4; +} + +/* Query offset/name of register from its name/offset */ +extern int regs_query_register_offset(const char *name); +extern const char *regs_query_register_name(unsigned int offset); +#define MAX_REG_OFFSET (offsetof(struct pt_regs, ipsw)) + +#define kernel_stack_pointer(regs) ((regs)->gr[30]) + +static inline unsigned long regs_get_register(struct pt_regs *regs, + unsigned int offset) +{ + if (unlikely(offset > MAX_REG_OFFSET)) + return 0; + return *(unsigned long *)((unsigned long)regs + offset); +} + +unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n); +int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr); + +#endif diff --git a/arch/parisc/include/asm/ropes.h b/arch/parisc/include/asm/ropes.h new file mode 100644 index 000000000..e2d2d7e9b --- /dev/null +++ b/arch/parisc/include/asm/ropes.h @@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_ROPES_H_ +#define _ASM_PARISC_ROPES_H_ + +#include <asm/parisc-device.h> + +#ifdef CONFIG_64BIT +/* "low end" PA8800 machines use ZX1 chipset: PAT PDC and only run 64-bit */ +#define ZX1_SUPPORT +#endif + +#ifdef CONFIG_PROC_FS +/* depends on proc fs support. But costs CPU performance */ +#undef SBA_COLLECT_STATS +#endif + +/* +** The number of pdir entries to "free" before issuing +** a read to PCOM register to flush out PCOM writes. +** Interacts with allocation granularity (ie 4 or 8 entries +** allocated and free'd/purged at a time might make this +** less interesting). +*/ +#define DELAYED_RESOURCE_CNT 16 + +#define MAX_IOC 2 /* per Ike. Pluto/Astro only have 1. */ +#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */ + +struct ioc { + void __iomem *ioc_hpa; /* I/O MMU base address */ + char *res_map; /* resource map, bit == pdir entry */ + __le64 *pdir_base; /* physical base address */ + unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */ + unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */ +#ifdef ZX1_SUPPORT + unsigned long iovp_mask; /* help convert IOVA to IOVP */ +#endif + unsigned long *res_hint; /* next avail IOVP - circular search */ + spinlock_t res_lock; + unsigned int res_bitshift; /* from the LEFT! */ + unsigned int res_size; /* size of resource map in bytes */ +#ifdef SBA_HINT_SUPPORT +/* FIXME : DMA HINTs not used */ + unsigned long hint_mask_pdir; /* bits used for DMA hints */ + unsigned int hint_shift_pdir; +#endif +#if DELAYED_RESOURCE_CNT > 0 + int saved_cnt; + struct sba_dma_pair { + dma_addr_t iova; + size_t size; + } saved[DELAYED_RESOURCE_CNT]; +#endif + +#ifdef SBA_COLLECT_STATS +#define SBA_SEARCH_SAMPLE 0x100 + unsigned long avg_search[SBA_SEARCH_SAMPLE]; + unsigned long avg_idx; /* current index into avg_search */ + unsigned long used_pages; + unsigned long msingle_calls; + unsigned long msingle_pages; + unsigned long msg_calls; + unsigned long msg_pages; + unsigned long usingle_calls; + unsigned long usingle_pages; + unsigned long usg_calls; + unsigned long usg_pages; +#endif + /* STUFF We don't need in performance path */ + unsigned int pdir_size; /* in bytes, determined by IOV Space size */ +}; + +struct sba_device { + struct sba_device *next; /* list of SBA's in system */ + struct parisc_device *dev; /* dev found in bus walk */ + const char *name; + void __iomem *sba_hpa; /* base address */ + spinlock_t sba_lock; + unsigned int flags; /* state/functionality enabled */ + unsigned int hw_rev; /* HW revision of chip */ + + struct resource chip_resv; /* MMIO reserved for chip */ + struct resource iommu_resv; /* MMIO reserved for iommu */ + + unsigned int num_ioc; /* number of on-board IOC's */ + struct ioc ioc[MAX_IOC]; +}; + +/* list of SBA's in system, see drivers/parisc/sba_iommu.c */ +extern struct sba_device *sba_list; + +#define ASTRO_RUNWAY_PORT 0x582 +#define IKE_MERCED_PORT 0x803 +#define REO_MERCED_PORT 0x804 +#define REOG_MERCED_PORT 0x805 +#define PLUTO_MCKINLEY_PORT 0x880 + +static inline int IS_ASTRO(struct parisc_device *d) { + return d->id.hversion == ASTRO_RUNWAY_PORT; +} + +static inline int IS_IKE(struct parisc_device *d) { + return d->id.hversion == IKE_MERCED_PORT; +} + +static inline int IS_PLUTO(struct parisc_device *d) { + return d->id.hversion == PLUTO_MCKINLEY_PORT; +} + +#define PLUTO_IOVA_BASE (1UL*1024*1024*1024) /* 1GB */ +#define PLUTO_IOVA_SIZE (1UL*1024*1024*1024) /* 1GB */ +#define PLUTO_GART_SIZE (PLUTO_IOVA_SIZE / 2) + +#define SBA_PDIR_VALID_BIT 0x8000000000000000ULL + +#define SBA_AGPGART_COOKIE (__force __le64) 0x0000badbadc0ffeeULL + +#define SBA_FUNC_ID 0x0000 /* function id */ +#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ + +#define SBA_FUNC_SIZE 4096 /* SBA configuration function reg set */ + +#define ASTRO_IOC_OFFSET (32 * SBA_FUNC_SIZE) +#define PLUTO_IOC_OFFSET (1 * SBA_FUNC_SIZE) +/* Ike's IOC's occupy functions 2 and 3 */ +#define IKE_IOC_OFFSET(p) ((p+2) * SBA_FUNC_SIZE) + +#define IOC_CTRL 0x8 /* IOC_CTRL offset */ +#define IOC_CTRL_TC (1 << 0) /* TOC Enable */ +#define IOC_CTRL_CE (1 << 1) /* Coalesce Enable */ +#define IOC_CTRL_DE (1 << 2) /* Dillon Enable */ +#define IOC_CTRL_RM (1 << 8) /* Real Mode */ +#define IOC_CTRL_NC (1 << 9) /* Non Coherent Mode */ +#define IOC_CTRL_D4 (1 << 11) /* Disable 4-byte coalescing */ +#define IOC_CTRL_DD (1 << 13) /* Disable distr. LMMIO range coalescing */ + +/* +** Offsets into MBIB (Function 0 on Ike and hopefully Astro) +** Firmware programs this stuff. Don't touch it. +*/ +#define LMMIO_DIRECT0_BASE 0x300 +#define LMMIO_DIRECT0_MASK 0x308 +#define LMMIO_DIRECT0_ROUTE 0x310 + +#define LMMIO_DIST_BASE 0x360 +#define LMMIO_DIST_MASK 0x368 +#define LMMIO_DIST_ROUTE 0x370 + +#define IOS_DIST_BASE 0x390 +#define IOS_DIST_MASK 0x398 +#define IOS_DIST_ROUTE 0x3A0 + +#define IOS_DIRECT_BASE 0x3C0 +#define IOS_DIRECT_MASK 0x3C8 +#define IOS_DIRECT_ROUTE 0x3D0 + +/* +** Offsets into I/O TLB (Function 2 and 3 on Ike) +*/ +#define ROPE0_CTL 0x200 /* "regbus pci0" */ +#define ROPE1_CTL 0x208 +#define ROPE2_CTL 0x210 +#define ROPE3_CTL 0x218 +#define ROPE4_CTL 0x220 +#define ROPE5_CTL 0x228 +#define ROPE6_CTL 0x230 +#define ROPE7_CTL 0x238 + +#define IOC_ROPE0_CFG 0x500 /* pluto only */ +#define IOC_ROPE_AO 0x10 /* Allow "Relaxed Ordering" */ + +#define HF_ENABLE 0x40 + +#define IOC_IBASE 0x300 /* IO TLB */ +#define IOC_IMASK 0x308 +#define IOC_PCOM 0x310 +#define IOC_TCNFG 0x318 +#define IOC_PDIR_BASE 0x320 + +/* +** IOC supports 4/8/16/64KB page sizes (see TCNFG register) +** It's safer (avoid memory corruption) to keep DMA page mappings +** equivalently sized to VM PAGE_SIZE. +** +** We really can't avoid generating a new mapping for each +** page since the Virtual Coherence Index has to be generated +** and updated for each page. +** +** PAGE_SIZE could be greater than IOVP_SIZE. But not the inverse. +*/ +#define IOVP_SIZE PAGE_SIZE +#define IOVP_SHIFT PAGE_SHIFT +#define IOVP_MASK PAGE_MASK + +#define SBA_PERF_CFG 0x708 /* Performance Counter stuff */ +#define SBA_PERF_MASK1 0x718 +#define SBA_PERF_MASK2 0x730 + +/* +** Offsets into PCI Performance Counters (functions 12 and 13) +** Controlled by PERF registers in function 2 & 3 respectively. +*/ +#define SBA_PERF_CNT1 0x200 +#define SBA_PERF_CNT2 0x208 +#define SBA_PERF_CNT3 0x210 + +/* +** lba_device: Per instance Elroy data structure +*/ +struct lba_device { + struct pci_hba_data hba; + + spinlock_t lba_lock; + void *iosapic_obj; + +#ifdef CONFIG_64BIT + void __iomem *iop_base; /* PA_VIEW - for IO port accessor funcs */ +#endif + + int flags; /* state/functionality enabled */ + int hw_rev; /* HW revision of chip */ +}; + +#define ELROY_HVERS 0x782 +#define MERCURY_HVERS 0x783 +#define QUICKSILVER_HVERS 0x784 + +static inline int IS_ELROY(struct parisc_device *d) { + return (d->id.hversion == ELROY_HVERS); +} + +static inline int IS_MERCURY(struct parisc_device *d) { + return (d->id.hversion == MERCURY_HVERS); +} + +static inline int IS_QUICKSILVER(struct parisc_device *d) { + return (d->id.hversion == QUICKSILVER_HVERS); +} + +static inline int agp_mode_mercury(void __iomem *hpa) { + u64 bus_mode; + + bus_mode = readl(hpa + 0x0620); + if (bus_mode & 1) + return 1; + + return 0; +} + +/* +** I/O SAPIC init function +** Caller knows where an I/O SAPIC is. LBA has an integrated I/O SAPIC. +** Call setup as part of per instance initialization. +** (ie *not* init_module() function unless only one is present.) +** fixup_irq is to initialize PCI IRQ line support and +** virtualize pcidev->irq value. To be called by pci_fixup_bus(). +*/ +extern void *iosapic_register(unsigned long hpa, void __iomem *vaddr); +extern int iosapic_fixup_irq(void *obj, struct pci_dev *pcidev); + +#define LBA_FUNC_ID 0x0000 /* function id */ +#define LBA_FCLASS 0x0008 /* function class, bist, header, rev... */ +#define LBA_CAPABLE 0x0030 /* capabilities register */ + +#define LBA_PCI_CFG_ADDR 0x0040 /* poke CFG address here */ +#define LBA_PCI_CFG_DATA 0x0048 /* read or write data here */ + +#define LBA_PMC_MTLT 0x0050 /* Firmware sets this - read only. */ +#define LBA_FW_SCRATCH 0x0058 /* Firmware writes the PCI bus number here. */ +#define LBA_ERROR_ADDR 0x0070 /* On error, address gets logged here */ + +#define LBA_ARB_MASK 0x0080 /* bit 0 enable arbitration. PAT/PDC enables */ +#define LBA_ARB_PRI 0x0088 /* firmware sets this. */ +#define LBA_ARB_MODE 0x0090 /* firmware sets this. */ +#define LBA_ARB_MTLT 0x0098 /* firmware sets this. */ + +#define LBA_MOD_ID 0x0100 /* Module ID. PDC_PAT_CELL reports 4 */ + +#define LBA_STAT_CTL 0x0108 /* Status & Control */ +#define LBA_BUS_RESET 0x01 /* Deassert PCI Bus Reset Signal */ +#define CLEAR_ERRLOG 0x10 /* "Clear Error Log" cmd */ +#define CLEAR_ERRLOG_ENABLE 0x20 /* "Clear Error Log" Enable */ +#define HF_ENABLE 0x40 /* enable HF mode (default is -1 mode) */ + +#define LBA_LMMIO_BASE 0x0200 /* < 4GB I/O address range */ +#define LBA_LMMIO_MASK 0x0208 + +#define LBA_GMMIO_BASE 0x0210 /* > 4GB I/O address range */ +#define LBA_GMMIO_MASK 0x0218 + +#define LBA_WLMMIO_BASE 0x0220 /* All < 4GB ranges under the same *SBA* */ +#define LBA_WLMMIO_MASK 0x0228 + +#define LBA_WGMMIO_BASE 0x0230 /* All > 4GB ranges under the same *SBA* */ +#define LBA_WGMMIO_MASK 0x0238 + +#define LBA_IOS_BASE 0x0240 /* I/O port space for this LBA */ +#define LBA_IOS_MASK 0x0248 + +#define LBA_ELMMIO_BASE 0x0250 /* Extra LMMIO range */ +#define LBA_ELMMIO_MASK 0x0258 + +#define LBA_EIOS_BASE 0x0260 /* Extra I/O port space */ +#define LBA_EIOS_MASK 0x0268 + +#define LBA_GLOBAL_MASK 0x0270 /* Mercury only: Global Address Mask */ +#define LBA_DMA_CTL 0x0278 /* firmware sets this */ + +#define LBA_IBASE 0x0300 /* SBA DMA support */ +#define LBA_IMASK 0x0308 + +/* FIXME: ignore DMA Hint stuff until we can measure performance */ +#define LBA_HINT_CFG 0x0310 +#define LBA_HINT_BASE 0x0380 /* 14 registers at every 8 bytes. */ + +#define LBA_BUS_MODE 0x0620 + +/* ERROR regs are needed for config cycle kluges */ +#define LBA_ERROR_CONFIG 0x0680 +#define LBA_SMART_MODE 0x20 +#define LBA_ERROR_STATUS 0x0688 +#define LBA_ROPE_CTL 0x06A0 + +#define LBA_IOSAPIC_BASE 0x800 /* Offset of IRQ logic */ + +#endif /*_ASM_PARISC_ROPES_H_*/ diff --git a/arch/parisc/include/asm/rt_sigframe.h b/arch/parisc/include/asm/rt_sigframe.h new file mode 100644 index 000000000..bb7fb4153 --- /dev/null +++ b/arch/parisc/include/asm/rt_sigframe.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_RT_SIGFRAME_H +#define _ASM_PARISC_RT_SIGFRAME_H + +struct rt_sigframe { + unsigned int tramp[2]; /* holds original return address */ + struct siginfo info; + struct ucontext uc; +}; + +#define SIGFRAME 128 +#define FUNCTIONCALLFRAME 96 +#define PARISC_RT_SIGFRAME_SIZE \ + (((sizeof(struct rt_sigframe) + FUNCTIONCALLFRAME) + SIGFRAME) & -SIGFRAME) + +#endif diff --git a/arch/parisc/include/asm/runway.h b/arch/parisc/include/asm/runway.h new file mode 100644 index 000000000..2837f0223 --- /dev/null +++ b/arch/parisc/include/asm/runway.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ASM_PARISC_RUNWAY_H +#define ASM_PARISC_RUNWAY_H + +#define RUNWAY_STATUS 0x10 +#define RUNWAY_DEBUG 0x40 + +#endif /* ASM_PARISC_RUNWAY_H */ diff --git a/arch/parisc/include/asm/seccomp.h b/arch/parisc/include/asm/seccomp.h new file mode 100644 index 000000000..b058b2220 --- /dev/null +++ b/arch/parisc/include/asm/seccomp.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_SECCOMP_H +#define _ASM_SECCOMP_H + +#include <asm-generic/seccomp.h> + +#ifdef CONFIG_64BIT +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PARISC64 +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "parisc64" +# ifdef CONFIG_COMPAT +# define SECCOMP_ARCH_COMPAT AUDIT_ARCH_PARISC +# define SECCOMP_ARCH_COMPAT_NR NR_syscalls +# define SECCOMP_ARCH_COMPAT_NAME "parisc" +# endif +#else /* !CONFIG_64BIT */ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PARISC +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "parisc" +#endif + +#endif /* _ASM_SECCOMP_H */ diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h new file mode 100644 index 000000000..33df42b5c --- /dev/null +++ b/arch/parisc/include/asm/sections.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_SECTIONS_H +#define _PARISC_SECTIONS_H + +#ifdef CONFIG_HAVE_FUNCTION_DESCRIPTORS +#include <asm/elf.h> +typedef Elf64_Fdesc func_desc_t; +#endif + +/* nothing to see, move along */ +#include <asm-generic/sections.h> + +extern char __alt_instructions[], __alt_instructions_end[]; + +#endif diff --git a/arch/parisc/include/asm/shmparam.h b/arch/parisc/include/asm/shmparam.h new file mode 100644 index 000000000..5a95b0f62 --- /dev/null +++ b/arch/parisc/include/asm/shmparam.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASMPARISC_SHMPARAM_H +#define _ASMPARISC_SHMPARAM_H + +/* + * PA-RISC uses virtually indexed & physically tagged (VIPT) caches + * which has strict requirements when two pages to the same physical + * address are accessed through different mappings. Read the section + * "Address Aliasing" in the arch docs for more detail: + * PA-RISC 1.1 (page 3-6): + * https://parisc.wiki.kernel.org/images-parisc/6/68/Pa11_acd.pdf + * PA-RISC 2.0 (page F-5): + * https://parisc.wiki.kernel.org/images-parisc/7/73/Parisc2.0.pdf + * + * For Linux we allow kernel and userspace to map pages on page size + * granularity (SHMLBA) but have to ensure that, if two pages are + * mapped to the same physical address, the virtual and physical + * addresses modulo SHM_COLOUR are identical. + */ +#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ +#define SHM_COLOUR 0x00400000 /* shared mappings colouring */ + +#endif /* _ASMPARISC_SHMPARAM_H */ diff --git a/arch/parisc/include/asm/signal.h b/arch/parisc/include/asm/signal.h new file mode 100644 index 000000000..85c3d7409 --- /dev/null +++ b/arch/parisc/include/asm/signal.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_SIGNAL_H +#define _ASM_PARISC_SIGNAL_H + +#include <uapi/asm/signal.h> + +# ifndef __ASSEMBLER__ + +/* Most things should be clean enough to redefine this at will, if care + is taken to make libc match. */ + +#include <asm/sigcontext.h> + +#endif /* !__ASSEMBLER__ */ +#endif /* _ASM_PARISC_SIGNAL_H */ diff --git a/arch/parisc/include/asm/smp.h b/arch/parisc/include/asm/smp.h new file mode 100644 index 000000000..0cf1c3a26 --- /dev/null +++ b/arch/parisc/include/asm/smp.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_SMP_H +#define __ASM_SMP_H + +extern int init_per_cpu(int cpuid); + +#if defined(CONFIG_SMP) + +/* Page Zero Location PDC will look for the address to branch to when we poke +** slave CPUs still in "Icache loop". +*/ +#define PDC_OS_BOOT_RENDEZVOUS 0x10 +#define PDC_OS_BOOT_RENDEZVOUS_HI 0x28 + +#ifndef __ASSEMBLER__ +#include <linux/bitops.h> +#include <linux/threads.h> /* for NR_CPUS */ +#include <linux/cpumask.h> +typedef unsigned long address_t; + + +/* + * Private routines/data + * + * physical and logical are equivalent until we support CPU hotplug. + */ +#define cpu_number_map(cpu) (cpu) +#define cpu_logical_map(cpu) (cpu) + +extern void smp_send_all_nop(void); + +extern void arch_send_call_function_single_ipi(int cpu); +extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); + +#define raw_smp_processor_id() (current_thread_info()->cpu) + +#endif /* !__ASSEMBLER__ */ + +#else /* CONFIG_SMP */ + +static inline void smp_send_all_nop(void) { return; } + +#endif + +#define NO_PROC_ID 0xFF /* No processor magic marker */ +#define ANY_PROC_ID 0xFF /* Any processor magic marker */ +int __cpu_disable(void); +void __cpu_die(unsigned int cpu); + +#endif /* __ASM_SMP_H */ diff --git a/arch/parisc/include/asm/socket.h b/arch/parisc/include/asm/socket.h new file mode 100644 index 000000000..33500c9f6 --- /dev/null +++ b/arch/parisc/include/asm/socket.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_SOCKET_H +#define _ASM_SOCKET_H + +#include <uapi/asm/socket.h> + +/* O_NONBLOCK clashed with the bits used for socket types. Therefore we + * had to define SOCK_NONBLOCK to a different value here. + */ +#define SOCK_NONBLOCK 0x40000000 + +#endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/asm/sparsemem.h b/arch/parisc/include/asm/sparsemem.h new file mode 100644 index 000000000..b5c3a7904 --- /dev/null +++ b/arch/parisc/include/asm/sparsemem.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ASM_PARISC_SPARSEMEM_H +#define ASM_PARISC_SPARSEMEM_H + +/* We have these possible memory map layouts: + * Astro: 0-3.75, 67.75-68, 4-64 + * zx1: 0-1, 257-260, 4-256 + * Stretch (N-class): 0-2, 4-32, 34-xxx + */ + +#define MAX_PHYSMEM_BITS 39 /* 512 GB */ +#define SECTION_SIZE_BITS 27 /* 128 MB */ + +#endif diff --git a/arch/parisc/include/asm/special_insns.h b/arch/parisc/include/asm/special_insns.h new file mode 100644 index 000000000..1013eeba3 --- /dev/null +++ b/arch/parisc/include/asm/special_insns.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_SPECIAL_INSNS_H +#define __PARISC_SPECIAL_INSNS_H + +#define lpa(va) ({ \ + unsigned long pa; \ + __asm__ __volatile__( \ + "copy %%r0,%0\n" \ + "8:\tlpa %%r0(%1),%0\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \ + "or %%r0,%%r0,%%r0") \ + : "=&r" (pa) \ + : "r" (va) \ + : "memory" \ + ); \ + pa; \ +}) + +#define lpa_user(va) ({ \ + unsigned long pa; \ + __asm__ __volatile__( \ + "copy %%r0,%0\n" \ + "8:\tlpa %%r0(%%sr3,%1),%0\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \ + "or %%r0,%%r0,%%r0") \ + : "=&r" (pa) \ + : "r" (va) \ + : "memory" \ + ); \ + pa; \ +}) + +/** + * prober_user() - Probe user read access + * @sr: Space regster. + * @va: Virtual address. + * + * Return: Non-zero if address is accessible. + * + * Due to the way _PAGE_READ is handled in TLB entries, we need + * a special check to determine whether a user address is accessible. + * The ldb instruction does the initial access check. If it is + * successful, the probe instruction checks user access rights. + */ +#define prober_user(sr, va) ({ \ + unsigned long read_allowed; \ + __asm__ __volatile__( \ + "copy %%r0,%0\n" \ + "8:\tldb 0(%%sr%1,%2),%%r0\n" \ + "\tproberi (%%sr%1,%2),%3,%0\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \ + "or %%r0,%%r0,%%r0") \ + : "=&r" (read_allowed) \ + : "i" (sr), "r" (va), "i" (PRIV_USER) \ + : "memory" \ + ); \ + read_allowed; \ +}) + +#define CR_EIEM 15 /* External Interrupt Enable Mask */ +#define CR_CR16 16 /* CR16 Interval Timer */ +#define CR_EIRR 23 /* External Interrupt Request Register */ + +#define mfctl(reg) ({ \ + unsigned long cr; \ + __asm__ __volatile__( \ + "mfctl %1,%0" : \ + "=r" (cr) : "i" (reg) \ + ); \ + cr; \ +}) + +#define mtctl(gr, cr) \ + __asm__ __volatile__("mtctl %0,%1" \ + : /* no outputs */ \ + : "r" (gr), "i" (cr) : "memory") + +#define get_eiem() mfctl(CR_EIEM) +#define set_eiem(val) mtctl(val, CR_EIEM) + +#define mfsp(reg) ({ \ + unsigned long cr; \ + __asm__ __volatile__( \ + "mfsp %%sr%1,%0" \ + : "=r" (cr) : "i"(reg) \ + ); \ + cr; \ +}) + +#define mtsp(val, cr) \ + { if (__builtin_constant_p(val) && ((val) == 0)) \ + __asm__ __volatile__("mtsp %%r0,%0" : : "i" (cr) : "memory"); \ + else \ + __asm__ __volatile__("mtsp %0,%1" \ + : /* no outputs */ \ + : "r" (val), "i" (cr) : "memory"); } + +#endif /* __PARISC_SPECIAL_INSNS_H */ diff --git a/arch/parisc/include/asm/spinlock.h b/arch/parisc/include/asm/spinlock.h new file mode 100644 index 000000000..0b326e522 --- /dev/null +++ b/arch/parisc/include/asm/spinlock.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_SPINLOCK_H +#define __ASM_SPINLOCK_H + +#include <asm/barrier.h> +#include <asm/ldcw.h> +#include <asm/processor.h> +#include <asm/spinlock_types.h> + +static inline void arch_spin_val_check(int lock_val) +{ + if (IS_ENABLED(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK)) + asm volatile( "andcm,= %0,%1,%%r0\n" + ".word %2\n" + : : "r" (lock_val), "r" (__ARCH_SPIN_LOCK_UNLOCKED_VAL), + "i" (SPINLOCK_BREAK_INSN)); +} + +static inline int arch_spin_is_locked(arch_spinlock_t *x) +{ + volatile unsigned int *a; + int lock_val; + + a = __ldcw_align(x); + lock_val = READ_ONCE(*a); + arch_spin_val_check(lock_val); + return (lock_val == 0); +} + +static inline void arch_spin_lock(arch_spinlock_t *x) +{ + volatile unsigned int *a; + + a = __ldcw_align(x); + do { + int lock_val_old; + + lock_val_old = __ldcw(a); + arch_spin_val_check(lock_val_old); + if (lock_val_old) + return; /* got lock */ + + /* wait until we should try to get lock again */ + while (*a == 0) + continue; + } while (1); +} + +static inline void arch_spin_unlock(arch_spinlock_t *x) +{ + volatile unsigned int *a; + + a = __ldcw_align(x); + /* Release with ordered store. */ + __asm__ __volatile__("stw,ma %0,0(%1)" + : : "r"(__ARCH_SPIN_LOCK_UNLOCKED_VAL), "r"(a) : "memory"); +} + +static inline int arch_spin_trylock(arch_spinlock_t *x) +{ + volatile unsigned int *a; + int lock_val; + + a = __ldcw_align(x); + lock_val = __ldcw(a); + arch_spin_val_check(lock_val); + return lock_val != 0; +} + +/* + * Read-write spinlocks, allowing multiple readers but only one writer. + * Unfair locking as Writers could be starved indefinitely by Reader(s) + * + * The spinlock itself is contained in @counter and access to it is + * serialized with @lock_mutex. + */ + +/* 1 - lock taken successfully */ +static inline int arch_read_trylock(arch_rwlock_t *rw) +{ + int ret = 0; + unsigned long flags; + + local_irq_save(flags); + arch_spin_lock(&(rw->lock_mutex)); + + /* + * zero means writer holds the lock exclusively, deny Reader. + * Otherwise grant lock to first/subseq reader + */ + if (rw->counter > 0) { + rw->counter--; + ret = 1; + } + + arch_spin_unlock(&(rw->lock_mutex)); + local_irq_restore(flags); + + return ret; +} + +/* 1 - lock taken successfully */ +static inline int arch_write_trylock(arch_rwlock_t *rw) +{ + int ret = 0; + unsigned long flags; + + local_irq_save(flags); + arch_spin_lock(&(rw->lock_mutex)); + + /* + * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__), + * deny writer. Otherwise if unlocked grant to writer + * Hence the claim that Linux rwlocks are unfair to writers. + * (can be starved for an indefinite time by readers). + */ + if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) { + rw->counter = 0; + ret = 1; + } + arch_spin_unlock(&(rw->lock_mutex)); + local_irq_restore(flags); + + return ret; +} + +static inline void arch_read_lock(arch_rwlock_t *rw) +{ + while (!arch_read_trylock(rw)) + cpu_relax(); +} + +static inline void arch_write_lock(arch_rwlock_t *rw) +{ + while (!arch_write_trylock(rw)) + cpu_relax(); +} + +static inline void arch_read_unlock(arch_rwlock_t *rw) +{ + unsigned long flags; + + local_irq_save(flags); + arch_spin_lock(&(rw->lock_mutex)); + rw->counter++; + arch_spin_unlock(&(rw->lock_mutex)); + local_irq_restore(flags); +} + +static inline void arch_write_unlock(arch_rwlock_t *rw) +{ + unsigned long flags; + + local_irq_save(flags); + arch_spin_lock(&(rw->lock_mutex)); + rw->counter = __ARCH_RW_LOCK_UNLOCKED__; + arch_spin_unlock(&(rw->lock_mutex)); + local_irq_restore(flags); +} + +#endif /* __ASM_SPINLOCK_H */ diff --git a/arch/parisc/include/asm/spinlock_types.h b/arch/parisc/include/asm/spinlock_types.h new file mode 100644 index 000000000..8e6889bc2 --- /dev/null +++ b/arch/parisc/include/asm/spinlock_types.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_SPINLOCK_TYPES_H +#define __ASM_SPINLOCK_TYPES_H + +#define __ARCH_SPIN_LOCK_UNLOCKED_VAL 0x1a46 + +#define SPINLOCK_BREAK_INSN 0x0000c006 /* break 6,6 */ + +#ifndef __ASSEMBLER__ + +typedef struct { + volatile unsigned int lock[4]; +# define __ARCH_SPIN_LOCK_UNLOCKED \ + { { __ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL, \ + __ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL } } +} arch_spinlock_t; + + +/* counter: + * Unlocked : 0x0100_0000 + * Read lock(s) : 0x00FF_FFFF to 0x01 (Multiple Readers decrement it) + * Write lock : 0x0, but only if prior value is "unlocked" 0x0100_0000 + */ +typedef struct { + arch_spinlock_t lock_mutex; + volatile unsigned int counter; +} arch_rwlock_t; + +#endif /* __ASSEMBLER__ */ + +#define __ARCH_RW_LOCK_UNLOCKED__ 0x01000000 +#define __ARCH_RW_LOCK_UNLOCKED { .lock_mutex = __ARCH_SPIN_LOCK_UNLOCKED, \ + .counter = __ARCH_RW_LOCK_UNLOCKED__ } + +#endif diff --git a/arch/parisc/include/asm/string.h b/arch/parisc/include/asm/string.h new file mode 100644 index 000000000..f6e1132f4 --- /dev/null +++ b/arch/parisc/include/asm/string.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PA_STRING_H_ +#define _PA_STRING_H_ + +#define __HAVE_ARCH_MEMSET +extern void * memset(void *, int, size_t); + +#define __HAVE_ARCH_MEMCPY +void * memcpy(void * dest,const void *src,size_t count); + +#endif diff --git a/arch/parisc/include/asm/superio.h b/arch/parisc/include/asm/superio.h new file mode 100644 index 000000000..5e11c11d4 --- /dev/null +++ b/arch/parisc/include/asm/superio.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_SUPERIO_H +#define _PARISC_SUPERIO_H + +#define IC_PIC1 0x20 /* PCI I/O address of master 8259 */ +#define IC_PIC2 0xA0 /* PCI I/O address of slave */ + +/* Config Space Offsets to configuration and base address registers */ +#define SIO_CR 0x5A /* Configuration Register */ +#define SIO_ACPIBAR 0x88 /* ACPI BAR */ +#define SIO_FDCBAR 0x90 /* Floppy Disk Controller BAR */ +#define SIO_SP1BAR 0x94 /* Serial 1 BAR */ +#define SIO_SP2BAR 0x98 /* Serial 2 BAR */ +#define SIO_PPBAR 0x9C /* Parallel BAR */ + +#define TRIGGER_1 0x67 /* Edge/level trigger register 1 */ +#define TRIGGER_2 0x68 /* Edge/level trigger register 2 */ + +/* Interrupt Routing Control registers */ +#define CFG_IR_SER 0x69 /* Serial 1 [0:3] and Serial 2 [4:7] */ +#define CFG_IR_PFD 0x6a /* Parallel [0:3] and Floppy [4:7] */ +#define CFG_IR_IDE 0x6b /* IDE1 [0:3] and IDE2 [4:7] */ +#define CFG_IR_INTAB 0x6c /* PCI INTA [0:3] and INT B [4:7] */ +#define CFG_IR_INTCD 0x6d /* PCI INTC [0:3] and INT D [4:7] */ +#define CFG_IR_PS2 0x6e /* PS/2 KBINT [0:3] and Mouse [4:7] */ +#define CFG_IR_FXBUS 0x6f /* FXIRQ[0] [0:3] and FXIRQ[1] [4:7] */ +#define CFG_IR_USB 0x70 /* FXIRQ[2] [0:3] and USB [4:7] */ +#define CFG_IR_ACPI 0x71 /* ACPI SCI [0:3] and reserved [4:7] */ + +#define CFG_IR_LOW CFG_IR_SER /* Lowest interrupt routing reg */ +#define CFG_IR_HIGH CFG_IR_ACPI /* Highest interrupt routing reg */ + +/* 8259 operational control words */ +#define OCW2_EOI 0x20 /* Non-specific EOI */ +#define OCW2_SEOI 0x60 /* Specific EOI */ +#define OCW3_IIR 0x0A /* Read request register */ +#define OCW3_ISR 0x0B /* Read service register */ +#define OCW3_POLL 0x0C /* Poll the PIC for an interrupt vector */ + +/* Interrupt lines. Only PIC1 is used */ +#define USB_IRQ 1 /* USB */ +#define SP1_IRQ 3 /* Serial port 1 */ +#define SP2_IRQ 4 /* Serial port 2 */ +#define PAR_IRQ 5 /* Parallel port */ +#define FDC_IRQ 6 /* Floppy controller */ +#define IDE_IRQ 7 /* IDE (pri+sec) */ + +/* ACPI registers */ +#define USB_REG_CR 0x1f /* USB Regulator Control Register */ + +#define SUPERIO_NIRQS 8 + +struct superio_device { + u32 fdc_base; + u32 sp1_base; + u32 sp2_base; + u32 pp_base; + u32 acpi_base; + int suckyio_irq_enabled; + struct pci_dev *lio_pdev; /* pci device for legacy IO (fn 1) */ + struct pci_dev *usb_pdev; /* pci device for USB (fn 2) */ +}; + +/* + * Does NS make a 87415 based plug in PCI card? If so, because of this + * macro we currently don't support it being plugged into a machine + * that contains a SuperIO chip AND has CONFIG_SUPERIO enabled. + * + * This could be fixed by checking to see if function 1 exists, and + * if it is SuperIO Legacy IO; but really now, is this combination + * going to EVER happen? + */ + +#define SUPERIO_IDE_FN 0 /* Function number of IDE controller */ +#define SUPERIO_LIO_FN 1 /* Function number of Legacy IO controller */ +#define SUPERIO_USB_FN 2 /* Function number of USB controller */ + +#define is_superio_device(x) \ + (((x)->vendor == PCI_VENDOR_ID_NS) && \ + ( ((x)->device == PCI_DEVICE_ID_NS_87415) \ + || ((x)->device == PCI_DEVICE_ID_NS_87560_LIO) \ + || ((x)->device == PCI_DEVICE_ID_NS_87560_USB) ) ) + +extern int superio_fixup_irq(struct pci_dev *pcidev); /* called by iosapic */ + +#endif /* _PARISC_SUPERIO_H */ diff --git a/arch/parisc/include/asm/switch_to.h b/arch/parisc/include/asm/switch_to.h new file mode 100644 index 000000000..f2ac9cc0d --- /dev/null +++ b/arch/parisc/include/asm/switch_to.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_SWITCH_TO_H +#define __PARISC_SWITCH_TO_H + +struct task_struct; + +extern struct task_struct *_switch_to(struct task_struct *, struct task_struct *); + +#define switch_to(prev, next, last) do { \ + (last) = _switch_to(prev, next); \ +} while(0) + +#endif /* __PARISC_SWITCH_TO_H */ diff --git a/arch/parisc/include/asm/syscall.h b/arch/parisc/include/asm/syscall.h new file mode 100644 index 000000000..c11222798 --- /dev/null +++ b/arch/parisc/include/asm/syscall.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* syscall.h */ + +#ifndef _ASM_PARISC_SYSCALL_H_ +#define _ASM_PARISC_SYSCALL_H_ + +#include <uapi/linux/audit.h> +#include <linux/compat.h> +#include <linux/err.h> +#include <asm/ptrace.h> + +#define NR_syscalls (__NR_Linux_syscalls) + +static inline long syscall_get_nr(struct task_struct *tsk, + struct pt_regs *regs) +{ + return regs->gr[20]; +} + +static inline void syscall_set_nr(struct task_struct *tsk, + struct pt_regs *regs, + int nr) +{ + regs->gr[20] = nr; +} + +static inline void syscall_get_arguments(struct task_struct *tsk, + struct pt_regs *regs, + unsigned long *args) +{ + args[5] = regs->gr[21]; + args[4] = regs->gr[22]; + args[3] = regs->gr[23]; + args[2] = regs->gr[24]; + args[1] = regs->gr[25]; + args[0] = regs->gr[26]; +} + +static inline void syscall_set_arguments(struct task_struct *tsk, + struct pt_regs *regs, + unsigned long *args) +{ + regs->gr[21] = args[5]; + regs->gr[22] = args[4]; + regs->gr[23] = args[3]; + regs->gr[24] = args[2]; + regs->gr[25] = args[1]; + regs->gr[26] = args[0]; +} + +static inline long syscall_get_error(struct task_struct *task, + struct pt_regs *regs) +{ + unsigned long error = regs->gr[28]; + return IS_ERR_VALUE(error) ? error : 0; +} + +static inline long syscall_get_return_value(struct task_struct *task, + struct pt_regs *regs) +{ + return regs->gr[28]; +} + +static inline void syscall_set_return_value(struct task_struct *task, + struct pt_regs *regs, + int error, long val) +{ + regs->gr[28] = error ? error : val; +} + +static inline void syscall_rollback(struct task_struct *task, + struct pt_regs *regs) +{ + /* do nothing */ +} + +static inline int syscall_get_arch(struct task_struct *task) +{ + int arch = AUDIT_ARCH_PARISC; +#ifdef CONFIG_64BIT + if (!__is_compat_task(task)) + arch = AUDIT_ARCH_PARISC64; +#endif + return arch; +} +#endif /*_ASM_PARISC_SYSCALL_H_*/ diff --git a/arch/parisc/include/asm/text-patching.h b/arch/parisc/include/asm/text-patching.h new file mode 100644 index 000000000..400d84c6e --- /dev/null +++ b/arch/parisc/include/asm/text-patching.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_KERNEL_PATCH_H +#define _PARISC_KERNEL_PATCH_H + +/* stop machine and patch kernel text */ +void patch_text(void *addr, unsigned int insn); +void patch_text_multiple(void *addr, u32 *insn, unsigned int len); + +/* patch kernel text with machine already stopped (e.g. in kgdb) */ +void __patch_text(void *addr, u32 insn); +void __patch_text_multiple(void *addr, u32 *insn, unsigned int len); + +#endif diff --git a/arch/parisc/include/asm/thread_info.h b/arch/parisc/include/asm/thread_info.h new file mode 100644 index 000000000..b283738bb --- /dev/null +++ b/arch/parisc/include/asm/thread_info.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_THREAD_INFO_H +#define _ASM_PARISC_THREAD_INFO_H + +#ifndef __ASSEMBLER__ +#include <asm/processor.h> +#include <asm/special_insns.h> + +struct thread_info { + unsigned long flags; /* thread_info flags (see TIF_*) */ + int preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */ +#ifdef CONFIG_SMP + unsigned int cpu; +#endif +}; + +#define INIT_THREAD_INFO(tsk) \ +{ \ + .flags = 0, \ + .preempt_count = INIT_PREEMPT_COUNT, \ +} + +#endif /* !__ASSEMBLER__ */ + +/* thread information allocation */ + +#ifdef CONFIG_IRQSTACKS +#define THREAD_SIZE_ORDER 2 /* PA-RISC requires at least 16k stack */ +#else +#define THREAD_SIZE_ORDER 3 /* PA-RISC requires at least 32k stack */ +#endif + +/* Be sure to hunt all references to this down when you change the size of + * the kernel stack */ +#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) +#define THREAD_SHIFT (PAGE_SHIFT + THREAD_SIZE_ORDER) + +/* + * thread information flags + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_SIGPENDING 1 /* signal pending */ +#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ +#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling TIF_NEED_RESCHED */ +#define TIF_32BIT 4 /* 32 bit binary */ +#define TIF_MEMDIE 5 /* is terminating due to OOM killer */ +#define TIF_NOTIFY_SIGNAL 6 /* signal notifications exist */ +#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ +#define TIF_NOTIFY_RESUME 8 /* callback before returning to user */ +#define TIF_SINGLESTEP 9 /* single stepping? */ +#define TIF_BLOCKSTEP 10 /* branch stepping? */ +#define TIF_SECCOMP 11 /* secure computing */ +#define TIF_SYSCALL_TRACEPOINT 12 /* syscall tracepoint instrumentation */ +#define TIF_NONBLOCK_WARNING 13 /* warned about wrong O_NONBLOCK usage */ + +#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) +#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) +#define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) +#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) +#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) +#define _TIF_32BIT (1 << TIF_32BIT) +#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) +#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) +#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) +#define _TIF_BLOCKSTEP (1 << TIF_BLOCKSTEP) +#define _TIF_SECCOMP (1 << TIF_SECCOMP) +#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) + +#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | \ + _TIF_NEED_RESCHED | _TIF_NOTIFY_SIGNAL) +#define _TIF_SYSCALL_TRACE_MASK (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \ + _TIF_BLOCKSTEP | _TIF_SYSCALL_AUDIT | \ + _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT) + +#ifdef CONFIG_64BIT +# ifdef CONFIG_COMPAT +# define is_32bit_task() (test_thread_flag(TIF_32BIT)) +# else +# define is_32bit_task() (0) +# endif +#else +# define is_32bit_task() (1) +#endif + +#endif /* _ASM_PARISC_THREAD_INFO_H */ diff --git a/arch/parisc/include/asm/timex.h b/arch/parisc/include/asm/timex.h new file mode 100644 index 000000000..453468ba4 --- /dev/null +++ b/arch/parisc/include/asm/timex.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/include/asm-parisc/timex.h + * + * PARISC architecture timex specifications + */ +#ifndef _ASMPARISC_TIMEX_H +#define _ASMPARISC_TIMEX_H + +#include <asm/special_insns.h> + +typedef unsigned long cycles_t; + +static inline cycles_t get_cycles(void) +{ + return mfctl(16); +} +#define get_cycles get_cycles + +#endif diff --git a/arch/parisc/include/asm/tlb.h b/arch/parisc/include/asm/tlb.h new file mode 100644 index 000000000..4501fee0a --- /dev/null +++ b/arch/parisc/include/asm/tlb.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_TLB_H +#define _PARISC_TLB_H + +#include <asm-generic/tlb.h> + +#if CONFIG_PGTABLE_LEVELS == 3 +#define __pmd_free_tlb(tlb, pmd, addr) tlb_remove_ptdesc((tlb), virt_to_ptdesc(pmd)) +#endif +#define __pte_free_tlb(tlb, pte, addr) tlb_remove_ptdesc((tlb), page_ptdesc(pte)) + +#endif diff --git a/arch/parisc/include/asm/tlbflush.h b/arch/parisc/include/asm/tlbflush.h new file mode 100644 index 000000000..5ffd7c17f --- /dev/null +++ b/arch/parisc/include/asm/tlbflush.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PARISC_TLBFLUSH_H +#define _PARISC_TLBFLUSH_H + +/* TLB flushing routines.... */ + +#include <linux/mm.h> +#include <linux/sched.h> +#include <asm/mmu_context.h> + +extern void flush_tlb_all(void); +extern void flush_tlb_all_local(void *); + +#define smp_flush_tlb_all() flush_tlb_all() + +int __flush_tlb_range(unsigned long sid, + unsigned long start, unsigned long end); + +#define flush_tlb_range(vma, start, end) \ + __flush_tlb_range((vma)->vm_mm->context.space_id, start, end) + +#define flush_tlb_kernel_range(start, end) \ + __flush_tlb_range(0, start, end) + +/* + * flush_tlb_mm() + * + * The code to switch to a new context is NOT valid for processes + * which play with the space id's. Thus, we have to preserve the + * space and just flush the entire tlb. However, the compilers, + * dynamic linker, etc, do not manipulate space id's, so there + * could be a significant performance benefit in switching contexts + * and not flushing the whole tlb. + */ + +static inline void flush_tlb_mm(struct mm_struct *mm) +{ + BUG_ON(mm == &init_mm); /* Should never happen */ + +#if 1 || defined(CONFIG_SMP) + /* Except for very small threads, flushing the whole TLB is + * faster than using __flush_tlb_range. The pdtlb and pitlb + * instructions are very slow because of the TLB broadcast. + * It might be faster to do local range flushes on all CPUs + * on PA 2.0 systems. + */ + flush_tlb_all(); +#else + /* FIXME: currently broken, causing space id and protection ids + * to go out of sync, resulting in faults on userspace accesses. + * This approach needs further investigation since running many + * small applications (e.g., GCC testsuite) is faster on HP-UX. + */ + if (mm) { + if (mm->context != 0) + free_sid(mm->context); + mm->context = alloc_sid(); + if (mm == current->active_mm) + load_context(mm->context); + } +#endif +} + +static inline void flush_tlb_page(struct vm_area_struct *vma, + unsigned long addr) +{ + purge_tlb_entries(vma->vm_mm, addr); +} +#endif diff --git a/arch/parisc/include/asm/topology.h b/arch/parisc/include/asm/topology.h new file mode 100644 index 000000000..406afb356 --- /dev/null +++ b/arch/parisc/include/asm/topology.h @@ -0,0 +1,19 @@ +#ifndef _ASM_PARISC_TOPOLOGY_H +#define _ASM_PARISC_TOPOLOGY_H + +#ifdef CONFIG_GENERIC_ARCH_TOPOLOGY + +#include <linux/cpumask.h> +#include <linux/arch_topology.h> + +#else + +static inline void init_cpu_topology(void) { } +static inline void store_cpu_topology(unsigned int cpuid) { } +static inline void reset_cpu_topology(void) { } + +#endif + +#include <asm-generic/topology.h> + +#endif /* _ASM_ARM_TOPOLOGY_H */ diff --git a/arch/parisc/include/asm/traps.h b/arch/parisc/include/asm/traps.h new file mode 100644 index 000000000..10c8fb68e --- /dev/null +++ b/arch/parisc/include/asm/traps.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_TRAPS_H +#define __ASM_TRAPS_H + +#define PARISC_ITLB_TRAP 6 /* defined by architecture. Do not change. */ + +#if !defined(__ASSEMBLER__) +struct pt_regs; + +/* traps.c */ +void parisc_terminate(char *msg, struct pt_regs *regs, + int code, unsigned long offset) __noreturn __cold; + +void die_if_kernel(char *str, struct pt_regs *regs, long err); + +/* mm/fault.c */ +unsigned long parisc_acctyp(unsigned long code, unsigned int inst); +const char *trap_name(unsigned long code); +void do_page_fault(struct pt_regs *regs, unsigned long code, + unsigned long address); +int handle_nadtlb_fault(struct pt_regs *regs); +#endif + +#endif diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h new file mode 100644 index 000000000..0d17f81c8 --- /dev/null +++ b/arch/parisc/include/asm/uaccess.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_UACCESS_H +#define __PARISC_UACCESS_H + +/* + * User space memory access functions + */ +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/extable.h> + +#include <linux/bug.h> +#include <linux/string.h> + +#define TASK_SIZE_MAX DEFAULT_TASK_SIZE +#include <asm/pgtable.h> +#include <asm-generic/access_ok.h> + +#define put_user __put_user +#define get_user __get_user + +#if !defined(CONFIG_64BIT) +#define LDD_USER(sr, val, ptr) __get_user_asm64(sr, val, ptr) +#define STD_USER(sr, x, ptr) __put_user_asm64(sr, x, ptr) +#else +#define LDD_USER(sr, val, ptr) __get_user_asm(sr, val, "ldd", ptr) +#define STD_USER(sr, x, ptr) __put_user_asm(sr, "std", x, ptr) +#endif + +#define __get_user_internal(sr, val, ptr) \ +({ \ + ASM_EXCEPTIONTABLE_VAR(__gu_err); \ + \ + switch (sizeof(*(ptr))) { \ + case 1: __get_user_asm(sr, val, "ldb", ptr); break; \ + case 2: __get_user_asm(sr, val, "ldh", ptr); break; \ + case 4: __get_user_asm(sr, val, "ldw", ptr); break; \ + case 8: LDD_USER(sr, val, ptr); break; \ + default: BUILD_BUG(); \ + } \ + \ + __gu_err; \ +}) + +#define __probe_user_internal(sr, error, ptr) \ +({ \ + __asm__("\tproberi (%%sr%1,%2),%3,%0\n" \ + "\tcmpiclr,= 1,%0,%0\n" \ + "\tldi %4,%0\n" \ + : "=r"(error) \ + : "i"(sr), "r"(ptr), "i"(PRIV_USER), \ + "i"(-EFAULT)); \ +}) + +#define __get_user(val, ptr) \ +({ \ + register long __gu_err; \ + \ + __gu_err = __get_user_internal(SR_USER, val, ptr); \ + if (likely(!__gu_err)) \ + __probe_user_internal(SR_USER, __gu_err, ptr); \ + __gu_err; \ +}) + +#define __get_user_asm(sr, val, ldx, ptr) \ +{ \ + register long __gu_val; \ + \ + __asm__("1: " ldx " 0(%%sr%2,%3),%0\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%1") \ + : "=r"(__gu_val), "+r"(__gu_err) \ + : "i"(sr), "r"(ptr)); \ + \ + (val) = (__force __typeof__(*(ptr))) __gu_val; \ +} + +#define __get_kernel_nofault(dst, src, type, err_label) \ +{ \ + type __z; \ + long __err; \ + __err = __get_user_internal(SR_KERNEL, __z, (type *)(src)); \ + if (unlikely(__err)) \ + goto err_label; \ + else \ + *(type *)(dst) = __z; \ +} + + +#if !defined(CONFIG_64BIT) + +#define __get_user_asm64(sr, val, ptr) \ +{ \ + union { \ + unsigned long long l; \ + __typeof__(*(ptr)) t; \ + } __gu_tmp; \ + \ + __asm__(" copy %%r0,%R0\n" \ + "1: ldw 0(%%sr%2,%3),%0\n" \ + "2: ldw 4(%%sr%2,%3),%R0\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%1") \ + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b, "%1") \ + : "=&r"(__gu_tmp.l), "+r"(__gu_err) \ + : "i"(sr), "r"(ptr)); \ + \ + (val) = __gu_tmp.t; \ +} + +#endif /* !defined(CONFIG_64BIT) */ + + +#define __put_user_internal(sr, x, ptr) \ +({ \ + ASM_EXCEPTIONTABLE_VAR(__pu_err); \ + \ + switch (sizeof(*(ptr))) { \ + case 1: __put_user_asm(sr, "stb", x, ptr); break; \ + case 2: __put_user_asm(sr, "sth", x, ptr); break; \ + case 4: __put_user_asm(sr, "stw", x, ptr); break; \ + case 8: STD_USER(sr, x, ptr); break; \ + default: BUILD_BUG(); \ + } \ + \ + __pu_err; \ +}) + +#define __put_user(x, ptr) \ +({ \ + __typeof__(&*(ptr)) __ptr = ptr; \ + __typeof__(*(__ptr)) __x = (__typeof__(*(__ptr)))(x); \ + __put_user_internal(SR_USER, __x, __ptr); \ +}) + +#define __put_kernel_nofault(dst, src, type, err_label) \ +{ \ + type __z = *(type *)(src); \ + long __err; \ + __err = __put_user_internal(SR_KERNEL, __z, (type *)(dst)); \ + if (unlikely(__err)) \ + goto err_label; \ +} + + + + +/* + * The "__put_user/kernel_asm()" macros tell gcc they read from memory + * instead of writing. This is because they do not write to any memory + * gcc knows about, so there are no aliasing issues. These macros must + * also be aware that fixups are executed in the context of the fault, + * and any registers used there must be listed as clobbers. + * The register holding the possible EFAULT error (ASM_EXCEPTIONTABLE_REG) + * is already listed as input and output register. + */ + +#define __put_user_asm(sr, stx, x, ptr) \ + __asm__ __volatile__ ( \ + "1: " stx " %1,0(%%sr%2,%3)\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%0") \ + : "+r"(__pu_err) \ + : "r"(x), "i"(sr), "r"(ptr)) + + +#if !defined(CONFIG_64BIT) + +#define __put_user_asm64(sr, __val, ptr) do { \ + __asm__ __volatile__ ( \ + "1: stw %1,0(%%sr%2,%3)\n" \ + "2: stw %R1,4(%%sr%2,%3)\n" \ + "9:\n" \ + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%0") \ + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b, "%0") \ + : "+r"(__pu_err) \ + : "r"(__val), "i"(sr), "r"(ptr)); \ +} while (0) + +#endif /* !defined(CONFIG_64BIT) */ + +/* + * Complex access routines -- external declarations + */ + +extern long strncpy_from_user(char *, const char __user *, long); +extern __must_check unsigned lclear_user(void __user *, unsigned long); +extern __must_check long strnlen_user(const char __user *src, long n); +/* + * Complex access routines -- macros + */ + +#define clear_user lclear_user +#define __clear_user lclear_user + +unsigned long __must_check raw_copy_to_user(void __user *dst, const void *src, + unsigned long len); +unsigned long __must_check raw_copy_from_user(void *dst, const void __user *src, + unsigned long len); +#define INLINE_COPY_USER + +#endif /* __PARISC_UACCESS_H */ diff --git a/arch/parisc/include/asm/ucontext.h b/arch/parisc/include/asm/ucontext.h new file mode 100644 index 000000000..ac7f86386 --- /dev/null +++ b/arch/parisc/include/asm/ucontext.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_UCONTEXT_H +#define _ASM_PARISC_UCONTEXT_H + +struct ucontext { + unsigned int uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif /* !_ASM_PARISC_UCONTEXT_H */ diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h new file mode 100644 index 000000000..3e46c6ea9 --- /dev/null +++ b/arch/parisc/include/asm/unistd.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PARISC_UNISTD_H_ +#define _ASM_PARISC_UNISTD_H_ + +#include <uapi/asm/unistd.h> + +#define __NR_Linux_syscalls __NR_syscalls + +#ifndef __ASSEMBLER__ + +#define SYS_ify(syscall_name) __NR_##syscall_name + +#define __IGNORE_fadvise64 /* fadvise64_64 */ + +#ifndef ASM_LINE_SEP +# define ASM_LINE_SEP ; +#endif + +/* Definition taken from glibc 2.3.3 + * sysdeps/unix/sysv/linux/hppa/sysdep.h + */ + +#ifndef DONT_USE_PIC +/* WARNING: CANNOT BE USED IN A NOP! */ +# define K_STW_ASM_PIC " copy %%r19, %%r4\n" +# define K_LDW_ASM_PIC " copy %%r4, %%r19\n" +# define K_USING_GR4 "%r4", +#else +# define K_STW_ASM_PIC " \n" +# define K_LDW_ASM_PIC " \n" +# define K_USING_GR4 +#endif + +/* GCC has to be warned that a syscall may clobber all the ABI + registers listed as "caller-saves", see page 8, Table 2 + in section 2.2.6 of the PA-RISC RUN-TIME architecture + document. However! r28 is the result and will conflict with + the clobber list so it is left out. Also the input arguments + registers r20 -> r26 will conflict with the list so they + are treated specially. Although r19 is clobbered by the syscall + we cannot say this because it would violate ABI, thus we say + r4 is clobbered and use that register to save/restore r19 + across the syscall. */ + +#define K_CALL_CLOB_REGS "%r1", "%r2", K_USING_GR4 \ + "%r20", "%r29", "%r31" + +#undef K_INLINE_SYSCALL +#define K_INLINE_SYSCALL(name, nr, args...) ({ \ + long __sys_res; \ + { \ + register unsigned long __res __asm__("r28"); \ + K_LOAD_ARGS_##nr(args) \ + /* FIXME: HACK stw/ldw r19 around syscall */ \ + __asm__ volatile( \ + K_STW_ASM_PIC \ + " ble 0x100(%%sr2, %%r0)\n" \ + " ldi %1, %%r20\n" \ + K_LDW_ASM_PIC \ + : "=r" (__res) \ + : "i" (name) K_ASM_ARGS_##nr \ + : "memory", K_CALL_CLOB_REGS K_CLOB_ARGS_##nr \ + ); \ + __sys_res = (long)__res; \ + } \ + __sys_res; \ +}) + +#define K_LOAD_ARGS_0() +#define K_LOAD_ARGS_1(r26) \ + register unsigned long __r26 __asm__("r26") = (unsigned long)(r26); \ + K_LOAD_ARGS_0() +#define K_LOAD_ARGS_2(r26,r25) \ + register unsigned long __r25 __asm__("r25") = (unsigned long)(r25); \ + K_LOAD_ARGS_1(r26) +#define K_LOAD_ARGS_3(r26,r25,r24) \ + register unsigned long __r24 __asm__("r24") = (unsigned long)(r24); \ + K_LOAD_ARGS_2(r26,r25) +#define K_LOAD_ARGS_4(r26,r25,r24,r23) \ + register unsigned long __r23 __asm__("r23") = (unsigned long)(r23); \ + K_LOAD_ARGS_3(r26,r25,r24) +#define K_LOAD_ARGS_5(r26,r25,r24,r23,r22) \ + register unsigned long __r22 __asm__("r22") = (unsigned long)(r22); \ + K_LOAD_ARGS_4(r26,r25,r24,r23) +#define K_LOAD_ARGS_6(r26,r25,r24,r23,r22,r21) \ + register unsigned long __r21 __asm__("r21") = (unsigned long)(r21); \ + K_LOAD_ARGS_5(r26,r25,r24,r23,r22) + +/* Even with zero args we use r20 for the syscall number */ +#define K_ASM_ARGS_0 +#define K_ASM_ARGS_1 K_ASM_ARGS_0, "r" (__r26) +#define K_ASM_ARGS_2 K_ASM_ARGS_1, "r" (__r25) +#define K_ASM_ARGS_3 K_ASM_ARGS_2, "r" (__r24) +#define K_ASM_ARGS_4 K_ASM_ARGS_3, "r" (__r23) +#define K_ASM_ARGS_5 K_ASM_ARGS_4, "r" (__r22) +#define K_ASM_ARGS_6 K_ASM_ARGS_5, "r" (__r21) + +/* The registers not listed as inputs but clobbered */ +#define K_CLOB_ARGS_6 +#define K_CLOB_ARGS_5 K_CLOB_ARGS_6, "%r21" +#define K_CLOB_ARGS_4 K_CLOB_ARGS_5, "%r22" +#define K_CLOB_ARGS_3 K_CLOB_ARGS_4, "%r23" +#define K_CLOB_ARGS_2 K_CLOB_ARGS_3, "%r24" +#define K_CLOB_ARGS_1 K_CLOB_ARGS_2, "%r25" +#define K_CLOB_ARGS_0 K_CLOB_ARGS_1, "%r26" + +#define syscall0(name) \ + K_INLINE_SYSCALL(name, 0) +#define syscall1(name, arg1) \ + K_INLINE_SYSCALL(name, 1, arg1) +#define syscall2(name, arg1, arg2) \ + K_INLINE_SYSCALL(name, 2, arg1, arg2) +#define syscall3(name, arg1, arg2, arg3) \ + K_INLINE_SYSCALL(name, 3, arg1, arg2, arg3) +#define syscall4(name, arg1, arg2, arg3, arg4) \ + K_INLINE_SYSCALL(name, 4, arg1, arg2, arg3, arg4) +#define syscall5(name, arg1, arg2, arg3, arg4, arg5) \ + K_INLINE_SYSCALL(name, 5, arg1, arg2, arg3, arg4, arg5) + +#define __ARCH_WANT_NEW_STAT +#define __ARCH_WANT_STAT64 +#define __ARCH_WANT_SYS_ALARM +#define __ARCH_WANT_SYS_GETHOSTNAME +#define __ARCH_WANT_SYS_PAUSE +#define __ARCH_WANT_SYS_SIGNAL +#define __ARCH_WANT_SYS_TIME32 +#define __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL +#define __ARCH_WANT_SYS_UTIME32 +#define __ARCH_WANT_SYS_WAITPID +#define __ARCH_WANT_SYS_SOCKETCALL +#define __ARCH_WANT_SYS_FADVISE64 +#define __ARCH_WANT_SYS_GETPGRP +#define __ARCH_WANT_SYS_NICE +#define __ARCH_WANT_SYS_SIGPENDING +#define __ARCH_WANT_SYS_SIGPROCMASK +#define __ARCH_WANT_SYS_FORK +#define __ARCH_WANT_SYS_VFORK +#define __ARCH_WANT_SYS_CLONE +#define __ARCH_WANT_COMPAT_SYS_SENDFILE +#define __ARCH_WANT_COMPAT_STAT + +#ifdef CONFIG_64BIT +#define __ARCH_WANT_SYS_TIME +#define __ARCH_WANT_SYS_UTIME +#endif + +#endif /* __ASSEMBLER__ */ + +#undef STR + +#endif /* _ASM_PARISC_UNISTD_H_ */ diff --git a/arch/parisc/include/asm/unwind.h b/arch/parisc/include/asm/unwind.h new file mode 100644 index 000000000..9547e5261 --- /dev/null +++ b/arch/parisc/include/asm/unwind.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _UNWIND_H_ +#define _UNWIND_H_ + +#include <linux/list.h> + +/* Max number of levels to backtrace */ +#define MAX_UNWIND_ENTRIES 30 + +/* From ABI specifications */ +struct unwind_table_entry { + unsigned int region_start; + unsigned int region_end; + unsigned int Cannot_unwind:1; /* 0 */ + unsigned int Millicode:1; /* 1 */ + unsigned int Millicode_save_sr0:1; /* 2 */ + unsigned int Region_description:2; /* 3..4 */ + unsigned int reserved1:1; /* 5 */ + unsigned int Entry_SR:1; /* 6 */ + unsigned int Entry_FR:4; /* number saved *//* 7..10 */ + unsigned int Entry_GR:5; /* number saved *//* 11..15 */ + unsigned int Args_stored:1; /* 16 */ + unsigned int Variable_Frame:1; /* 17 */ + unsigned int Separate_Package_Body:1; /* 18 */ + unsigned int Frame_Extension_Millicode:1; /* 19 */ + unsigned int Stack_Overflow_Check:1; /* 20 */ + unsigned int Two_Instruction_SP_Increment:1; /* 21 */ + unsigned int Ada_Region:1; /* 22 */ + unsigned int cxx_info:1; /* 23 */ + unsigned int cxx_try_catch:1; /* 24 */ + unsigned int sched_entry_seq:1; /* 25 */ + unsigned int reserved2:1; /* 26 */ + unsigned int Save_SP:1; /* 27 */ + unsigned int Save_RP:1; /* 28 */ + unsigned int Save_MRP_in_frame:1; /* 29 */ + unsigned int extn_ptr_defined:1; /* 30 */ + unsigned int Cleanup_defined:1; /* 31 */ + + unsigned int MPE_XL_interrupt_marker:1; /* 0 */ + unsigned int HP_UX_interrupt_marker:1; /* 1 */ + unsigned int Large_frame:1; /* 2 */ + unsigned int Pseudo_SP_Set:1; /* 3 */ + unsigned int reserved4:1; /* 4 */ + unsigned int Total_frame_size:27; /* 5..31 */ +}; + +struct unwind_table { + struct list_head list; + const char *name; + unsigned long gp; + unsigned long base_addr; + unsigned long start; + unsigned long end; + const struct unwind_table_entry *table; + unsigned long length; +}; + +struct unwind_frame_info { + struct task_struct *t; + /* Eventually we would like to be able to get at any of the registers + available; but for now we only try to get the sp and ip for each + frame */ + /* struct pt_regs regs; */ + unsigned long sp, ip, rp, r31; + unsigned long prev_sp, prev_ip; +}; + +struct unwind_table * +unwind_table_add(const char *name, unsigned long base_addr, + unsigned long gp, void *start, void *end); +void +unwind_table_remove(struct unwind_table *table); + +void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, + struct pt_regs *regs); +void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, + struct task_struct *t); +void unwind_frame_init_task(struct unwind_frame_info *info, + struct task_struct *task, struct pt_regs *regs); +int unwind_once(struct unwind_frame_info *info); +int unwind_to_user(struct unwind_frame_info *info); + +int unwind_init(void); + +#endif diff --git a/arch/parisc/include/asm/vdso.h b/arch/parisc/include/asm/vdso.h new file mode 100644 index 000000000..e5cca3c9c --- /dev/null +++ b/arch/parisc/include/asm/vdso.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PARISC_VDSO_H__ +#define __PARISC_VDSO_H__ + +#ifndef __ASSEMBLER__ + +#ifdef CONFIG_64BIT +#include <generated/vdso64-offsets.h> +#define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name)) +#endif +#if !defined(CONFIG_64BIT) || defined(CONFIG_COMPAT) +#include <generated/vdso32-offsets.h> +#define VDSO32_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso32_offset_##name)) +#else +#define VDSO32_SYMBOL(tsk, name) 0UL +#endif + +#endif /* __ASSEMBLER__ */ + +/* Default link addresses for the vDSOs */ +#define VDSO_LBASE 0 + +#define VDSO_VERSION_STRING LINUX_6.11 + +#endif /* __PARISC_VDSO_H__ */ diff --git a/arch/parisc/include/asm/video.h b/arch/parisc/include/asm/video.h new file mode 100644 index 000000000..a9d50ebd6 --- /dev/null +++ b/arch/parisc/include/asm/video.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_VIDEO_H_ +#define _ASM_VIDEO_H_ + +#include <linux/types.h> + +struct device; + +#if defined(CONFIG_STI_CORE) && defined(CONFIG_VIDEO) +bool video_is_primary_device(struct device *dev); +#define video_is_primary_device video_is_primary_device +#endif + +#include <asm-generic/video.h> + +#endif /* _ASM_VIDEO_H_ */ diff --git a/arch/parisc/include/asm/vmalloc.h b/arch/parisc/include/asm/vmalloc.h new file mode 100644 index 000000000..1088ae4e7 --- /dev/null +++ b/arch/parisc/include/asm/vmalloc.h @@ -0,0 +1,4 @@ +#ifndef _ASM_PARISC_VMALLOC_H +#define _ASM_PARISC_VMALLOC_H + +#endif /* _ASM_PARISC_VMALLOC_H */ diff --git a/arch/parisc/include/uapi/asm/Kbuild b/arch/parisc/include/uapi/asm/Kbuild new file mode 100644 index 000000000..353b70b19 --- /dev/null +++ b/arch/parisc/include/uapi/asm/Kbuild @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +generated-y += unistd_32.h +generated-y += unistd_64.h diff --git a/arch/parisc/include/uapi/asm/auxvec.h b/arch/parisc/include/uapi/asm/auxvec.h new file mode 100644 index 000000000..90d2aa699 --- /dev/null +++ b/arch/parisc/include/uapi/asm/auxvec.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_PARISC_AUXVEC_H +#define _UAPI_PARISC_AUXVEC_H + +/* The vDSO location. */ +#define AT_SYSINFO_EHDR 33 + +#endif /* _UAPI_PARISC_AUXVEC_H */ diff --git a/arch/parisc/include/uapi/asm/bitsperlong.h b/arch/parisc/include/uapi/asm/bitsperlong.h new file mode 100644 index 000000000..307e2ef1c --- /dev/null +++ b/arch/parisc/include/uapi/asm/bitsperlong.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_PARISC_BITSPERLONG_H +#define __ASM_PARISC_BITSPERLONG_H + +#if defined(__LP64__) +#define __BITS_PER_LONG 64 +#else +#define __BITS_PER_LONG 32 +#endif + +#include <asm-generic/bitsperlong.h> + +#endif /* __ASM_PARISC_BITSPERLONG_H */ diff --git a/arch/parisc/include/uapi/asm/byteorder.h b/arch/parisc/include/uapi/asm/byteorder.h new file mode 100644 index 000000000..a59d9b7e3 --- /dev/null +++ b/arch/parisc/include/uapi/asm/byteorder.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_BYTEORDER_H +#define _PARISC_BYTEORDER_H + +#include <linux/byteorder/big_endian.h> + +#endif /* _PARISC_BYTEORDER_H */ diff --git a/arch/parisc/include/uapi/asm/cachectl.h b/arch/parisc/include/uapi/asm/cachectl.h new file mode 100644 index 000000000..68d6b4554 --- /dev/null +++ b/arch/parisc/include/uapi/asm/cachectl.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_CACHECTL +#define _ASM_CACHECTL + +/* + * Options for cacheflush system call + */ +#define ICACHE (1<<0) /* flush instruction cache */ +#define DCACHE (1<<1) /* writeback and flush data cache */ +#define BCACHE (ICACHE|DCACHE) /* flush both caches */ + +#endif /* _ASM_CACHECTL */ diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h new file mode 100644 index 000000000..93194fbb0 --- /dev/null +++ b/arch/parisc/include/uapi/asm/errno.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_ERRNO_H +#define _PARISC_ERRNO_H + +#include <asm-generic/errno-base.h> + +#define ENOMSG 35 /* No message of desired type */ +#define EIDRM 36 /* Identifier removed */ +#define ECHRNG 37 /* Channel number out of range */ +#define EL2NSYNC 38 /* Level 2 not synchronized */ +#define EL3HLT 39 /* Level 3 halted */ +#define EL3RST 40 /* Level 3 reset */ +#define ELNRNG 41 /* Link number out of range */ +#define EUNATCH 42 /* Protocol driver not attached */ +#define ENOCSI 43 /* No CSI structure available */ +#define EL2HLT 44 /* Level 2 halted */ +#define EDEADLK 45 /* Resource deadlock would occur */ +#define EDEADLOCK EDEADLK +#define ENOLCK 46 /* No record locks available */ +#define EILSEQ 47 /* Illegal byte sequence */ + +#define ENONET 50 /* Machine is not on the network */ +#define ENODATA 51 /* No data available */ +#define ETIME 52 /* Timer expired */ +#define ENOSR 53 /* Out of streams resources */ +#define ENOSTR 54 /* Device not a stream */ +#define ENOPKG 55 /* Package not installed */ + +#define ENOLINK 57 /* Link has been severed */ +#define EADV 58 /* Advertise error */ +#define ESRMNT 59 /* Srmount error */ +#define ECOMM 60 /* Communication error on send */ +#define EPROTO 61 /* Protocol error */ + +#define EMULTIHOP 64 /* Multihop attempted */ + +#define EDOTDOT 66 /* RFS specific error */ +#define EBADMSG 67 /* Not a data message */ +#define EFSBADCRC EBADMSG /* Bad CRC detected */ +#define EUSERS 68 /* Too many users */ +#define EDQUOT 69 /* Quota exceeded */ +#define ESTALE 70 /* Stale file handle */ +#define EREMOTE 71 /* Object is remote */ +#define EOVERFLOW 72 /* Value too large for defined data type */ + +/* these errnos are defined by Linux but not HPUX. */ + +#define EBADE 160 /* Invalid exchange */ +#define EBADR 161 /* Invalid request descriptor */ +#define EXFULL 162 /* Exchange full */ +#define ENOANO 163 /* No anode */ +#define EBADRQC 164 /* Invalid request code */ +#define EBADSLT 165 /* Invalid slot */ +#define EBFONT 166 /* Bad font file format */ +#define ENOTUNIQ 167 /* Name not unique on network */ +#define EBADFD 168 /* File descriptor in bad state */ +#define EREMCHG 169 /* Remote address changed */ +#define ELIBACC 170 /* Can not access a needed shared library */ +#define ELIBBAD 171 /* Accessing a corrupted shared library */ +#define ELIBSCN 172 /* .lib section in a.out corrupted */ +#define ELIBMAX 173 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 174 /* Cannot exec a shared library directly */ +#define ERESTART 175 /* Interrupted system call should be restarted */ +#define ESTRPIPE 176 /* Streams pipe error */ +#define EUCLEAN 177 /* Structure needs cleaning */ +#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ +#define ENOTNAM 178 /* Not a XENIX named type file */ +#define ENAVAIL 179 /* No XENIX semaphores available */ +#define EISNAM 180 /* Is a named type file */ +#define EREMOTEIO 181 /* Remote I/O error */ +#define ENOMEDIUM 182 /* No medium found */ +#define EMEDIUMTYPE 183 /* Wrong medium type */ +#define ENOKEY 184 /* Required key not available */ +#define EKEYEXPIRED 185 /* Key has expired */ +#define EKEYREVOKED 186 /* Key has been revoked */ +#define EKEYREJECTED 187 /* Key was rejected by service */ + +/* We now return you to your regularly scheduled HPUX. */ + +#define ENOTSOCK 216 /* Socket operation on non-socket */ +#define EDESTADDRREQ 217 /* Destination address required */ +#define EMSGSIZE 218 /* Message too long */ +#define EPROTOTYPE 219 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 220 /* Protocol not available */ +#define EPROTONOSUPPORT 221 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 222 /* Socket type not supported */ +#define EOPNOTSUPP 223 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 224 /* Protocol family not supported */ +#define EAFNOSUPPORT 225 /* Address family not supported by protocol */ +#define EADDRINUSE 226 /* Address already in use */ +#define EADDRNOTAVAIL 227 /* Cannot assign requested address */ +#define ENETDOWN 228 /* Network is down */ +#define ENETUNREACH 229 /* Network is unreachable */ +#define ENETRESET 230 /* Network dropped connection because of reset */ +#define ECONNABORTED 231 /* Software caused connection abort */ +#define ECONNRESET 232 /* Connection reset by peer */ +#define ENOBUFS 233 /* No buffer space available */ +#define EISCONN 234 /* Transport endpoint is already connected */ +#define ENOTCONN 235 /* Transport endpoint is not connected */ +#define ESHUTDOWN 236 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 237 /* Too many references: cannot splice */ +#define ETIMEDOUT 238 /* Connection timed out */ +#define ECONNREFUSED 239 /* Connection refused */ +#define EREFUSED ECONNREFUSED /* for HP's NFS apparently */ +#define EHOSTDOWN 241 /* Host is down */ +#define EHOSTUNREACH 242 /* No route to host */ + +#define EALREADY 244 /* Operation already in progress */ +#define EINPROGRESS 245 /* Operation now in progress */ +#define EWOULDBLOCK EAGAIN /* Operation would block (Not HPUX compliant) */ +#define ENOTEMPTY 247 /* Directory not empty */ +#define ENAMETOOLONG 248 /* File name too long */ +#define ELOOP 249 /* Too many symbolic links encountered */ +#define ENOSYS 251 /* Function not implemented */ + +#define ECANCELLED 253 /* aio request was canceled before complete (POSIX.4 / HPUX) */ +#define ECANCELED ECANCELLED /* SuSv3 and Solaris wants one 'L' */ + +/* for robust mutexes */ +#define EOWNERDEAD 254 /* Owner died */ +#define ENOTRECOVERABLE 255 /* State not recoverable */ + +#define ERFKILL 256 /* Operation not possible due to RF-kill */ + +#define EHWPOISON 257 /* Memory page has hardware error */ + +#define EFTYPE 258 /* Wrong file type for the intended operation */ + +#endif diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h new file mode 100644 index 000000000..2e1bb18ee --- /dev/null +++ b/arch/parisc/include/uapi/asm/fcntl.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_FCNTL_H +#define _PARISC_FCNTL_H + +#define O_APPEND (1 << 3) +#define O_CREAT (1 << 8) /* not fcntl */ +#define O_EXCL (1 << 10) /* not fcntl */ +#define O_LARGEFILE (1 << 11) +#define __O_SYNC (1 << 15) +#define O_SYNC (__O_SYNC|O_DSYNC) +#define O_NONBLOCK (1 << 16) +#define O_NOCTTY (1 << 17) /* not fcntl */ +#define O_DSYNC (1 << 18) +#define O_NOATIME (1 << 20) +#define O_CLOEXEC (1 << 21) /* set close_on_exec */ + +#define O_DIRECTORY (1 << 12) /* must be a directory */ +#define O_NOFOLLOW (1 << 7) /* don't follow links */ + +#define O_PATH (1 << 22) +#define __O_TMPFILE (1 << 23) + +#define F_GETLK64 8 +#define F_SETLK64 9 +#define F_SETLKW64 10 + +#define F_GETOWN 11 /* for sockets. */ +#define F_SETOWN 12 /* for sockets. */ +#define F_SETSIG 13 /* for sockets. */ +#define F_GETSIG 14 /* for sockets. */ + +/* for posix fcntl() and lockf() */ +#define F_RDLCK 01 +#define F_WRLCK 02 +#define F_UNLCK 03 + +#include <asm-generic/fcntl.h> + +#endif diff --git a/arch/parisc/include/uapi/asm/ioctl.h b/arch/parisc/include/uapi/asm/ioctl.h new file mode 100644 index 000000000..b509bcc94 --- /dev/null +++ b/arch/parisc/include/uapi/asm/ioctl.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * Copyright (C) 1999,2003 Matthew Wilcox < willy at debian . org > + * portions from "linux/ioctl.h for Linux" by H.H. Bergman. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _ASM_PARISC_IOCTL_H +#define _ASM_PARISC_IOCTL_H + +/* ioctl command encoding: 32 bits total, command in lower 16 bits, + * size of the parameter structure in the lower 14 bits of the + * upper 16 bits. + * Encoding the size of the parameter structure in the ioctl request + * is useful for catching programs compiled with old versions + * and to avoid overwriting user space outside the user buffer area. + * The highest 2 bits are reserved for indicating the ``access mode''. + * NOTE: This limits the max parameter size to 16kB -1 ! + */ + +/* + * Direction bits. + */ +#define _IOC_NONE 0U +#define _IOC_WRITE 2U +#define _IOC_READ 1U + +#include <asm-generic/ioctl.h> + +#endif /* _ASM_PARISC_IOCTL_H */ diff --git a/arch/parisc/include/uapi/asm/ioctls.h b/arch/parisc/include/uapi/asm/ioctls.h new file mode 100644 index 000000000..74b4027a4 --- /dev/null +++ b/arch/parisc/include/uapi/asm/ioctls.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ARCH_PARISC_IOCTLS_H__ +#define __ARCH_PARISC_IOCTLS_H__ + +#include <asm/ioctl.h> + +/* 0x54 is just a magic number to make these relatively unique ('T') */ + +#define TCGETS _IOR('T', 16, struct termios) /* TCGETATTR */ +#define TCSETS _IOW('T', 17, struct termios) /* TCSETATTR */ +#define TCSETSW _IOW('T', 18, struct termios) /* TCSETATTRD */ +#define TCSETSF _IOW('T', 19, struct termios) /* TCSETATTRF */ +#define TCGETA 0x40125401 +#define TCSETA 0x80125402 +#define TCSETAW 0x80125403 +#define TCSETAF 0x80125404 +#define TCSBRK _IO('T', 5) +#define TCXONC _IO('T', 6) +#define TCFLSH _IO('T', 7) +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP _IOR('T', 30, int) +#define TIOCSPGRP _IOW('T', 29, int) +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define FIONREAD 0x541B +#define TIOCINQ FIONREAD +#define TIOCLINUX 0x541C +#define TIOCCONS 0x541D +#define TIOCGSERIAL 0x541E +#define TIOCSSERIAL 0x541F +#define TIOCPKT 0x5420 +#define FIONBIO 0x5421 +#define TIOCNOTTY 0x5422 +#define TIOCSETD 0x5423 +#define TIOCGETD 0x5424 +#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ +#define TIOCGSID _IOR('T', 20, int) /* Return the session ID of FD */ +#define TCGETS2 _IOR('T',0x2A, struct termios2) +#define TCSETS2 _IOW('T',0x2B, struct termios2) +#define TCSETSW2 _IOW('T',0x2C, struct termios2) +#define TCSETSF2 _IOW('T',0x2D, struct termios2) +#define TIOCGRS485 _IOR('T', 0x2E, struct serial_rs485) +#define TIOCSRS485 _IOWR('T', 0x2F, struct serial_rs485) +#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCGDEV _IOR('T',0x32, int) /* Get primary device node of /dev/console */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ +#define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ +#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) + +#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define FIOCLEX 0x5451 +#define FIOASYNC 0x5452 +#define TIOCSERCONFIG 0x5453 +#define TIOCSERGWILD 0x5454 +#define TIOCSERSWILD 0x5455 +#define TIOCGLCKTRMIOS 0x5456 +#define TIOCSLCKTRMIOS 0x5457 +#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define FIOQSIZE 0x5460 /* Get exact space used by quota */ + +#define TIOCSTART 0x5461 +#define TIOCSTOP 0x5462 +#define TIOCSLTC 0x5462 + +/* Used for packet mode */ +#define TIOCPKT_DATA 0 +#define TIOCPKT_FLUSHREAD 1 +#define TIOCPKT_FLUSHWRITE 2 +#define TIOCPKT_STOP 4 +#define TIOCPKT_START 8 +#define TIOCPKT_NOSTOP 16 +#define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 + +#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ + +#endif /* _ASM_PARISC_IOCTLS_H */ diff --git a/arch/parisc/include/uapi/asm/ipcbuf.h b/arch/parisc/include/uapi/asm/ipcbuf.h new file mode 100644 index 000000000..edf266204 --- /dev/null +++ b/arch/parisc/include/uapi/asm/ipcbuf.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __PARISC_IPCBUF_H__ +#define __PARISC_IPCBUF_H__ + +#include <asm/bitsperlong.h> +#include <linux/posix_types.h> + +/* + * The ipc64_perm structure for PA-RISC is almost identical to + * kern_ipc_perm as we have always had 32-bit UIDs and GIDs in the kernel. + * 'seq' has been changed from long to int so that it's the same size + * on 64-bit kernels as on 32-bit ones. + */ + +struct ipc64_perm +{ + __kernel_key_t key; + __kernel_uid_t uid; + __kernel_gid_t gid; + __kernel_uid_t cuid; + __kernel_gid_t cgid; +#if __BITS_PER_LONG != 64 + unsigned short int __pad1; +#endif + __kernel_mode_t mode; + unsigned short int __pad2; + unsigned short int seq; + unsigned int __pad3; + unsigned long long int __unused1; + unsigned long long int __unused2; +}; + +#endif /* __PARISC_IPCBUF_H__ */ diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h new file mode 100644 index 000000000..b6a709506 --- /dev/null +++ b/arch/parisc/include/uapi/asm/mman.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __PARISC_MMAN_H__ +#define __PARISC_MMAN_H__ + +#define PROT_READ 0x1 /* page can be read */ +#define PROT_WRITE 0x2 /* page can be written */ +#define PROT_EXEC 0x4 /* page can be executed */ +#define PROT_SEM 0x8 /* page may be used for atomic ops */ +#define PROT_NONE 0x0 /* page can not be accessed */ +#define PROT_GROWSDOWN 0x01000000 /* mprotect flag: extend change to start of growsdown vma */ +#define PROT_GROWSUP 0x02000000 /* mprotect flag: extend change to end of growsup vma */ + +/* 0x01 - 0x03 are defined in linux/mman.h */ +#define MAP_TYPE 0x2b /* Mask for type of mapping, includes bits 0x08 and 0x20 */ +#define MAP_FIXED 0x04 /* Interpret addr exactly */ +#define MAP_ANONYMOUS 0x10 /* don't use a file */ + +#define MAP_DENYWRITE 0x0800 /* ETXTBSY */ +#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ +#define MAP_LOCKED 0x2000 /* pages are locked */ +#define MAP_NORESERVE 0x4000 /* don't check for reservations */ +#define MAP_GROWSDOWN 0x8000 /* stack-like segment */ +#define MAP_POPULATE 0x10000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x20000 /* do not block on IO */ +#define MAP_STACK 0x40000 /* give out an address that is best suited for process/thread stacks */ +#define MAP_HUGETLB 0x80000 /* create a huge page mapping */ +#define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED which doesn't unmap underlying mapping */ +#define MAP_UNINITIALIZED 0 /* uninitialized anonymous mmap */ + +#define MS_SYNC 1 /* synchronous memory sync */ +#define MS_ASYNC 2 /* sync memory asynchronously */ +#define MS_INVALIDATE 4 /* invalidate the caches */ + +#define MCL_CURRENT 1 /* lock all current mappings */ +#define MCL_FUTURE 2 /* lock all future mappings */ +#define MCL_ONFAULT 4 /* lock all pages that are faulted in */ + +#define MLOCK_ONFAULT 0x01 /* Lock pages in range after they are faulted in, do not prefault */ + +#define MADV_NORMAL 0 /* no further special treatment */ +#define MADV_RANDOM 1 /* expect random page references */ +#define MADV_SEQUENTIAL 2 /* expect sequential page references */ +#define MADV_WILLNEED 3 /* will need these pages */ +#define MADV_DONTNEED 4 /* don't need these pages */ + +/* common/generic parameters */ +#define MADV_FREE 8 /* free pages only if memory pressure */ +#define MADV_REMOVE 9 /* remove these pages & resources */ +#define MADV_DONTFORK 10 /* don't inherit across fork */ +#define MADV_DOFORK 11 /* do inherit across fork */ + +#define MADV_MERGEABLE 12 /* KSM may merge identical pages */ +#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */ + +#define MADV_HUGEPAGE 14 /* Worth backing with hugepages */ +#define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */ + +#define MADV_DONTDUMP 16 /* Explicity exclude from the core dump, + overrides the coredump filter bits */ +#define MADV_DODUMP 17 /* Clear the MADV_NODUMP flag */ + +#define MADV_WIPEONFORK 18 /* Zero memory on fork, child only */ +#define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK */ + +#define MADV_COLD 20 /* deactivate these pages */ +#define MADV_PAGEOUT 21 /* reclaim these pages */ + +#define MADV_POPULATE_READ 22 /* populate (prefault) page tables readable */ +#define MADV_POPULATE_WRITE 23 /* populate (prefault) page tables writable */ + +#define MADV_DONTNEED_LOCKED 24 /* like DONTNEED, but drop locked pages too */ + +#define MADV_COLLAPSE 25 /* Synchronous hugepage collapse */ + +#define MADV_HWPOISON 100 /* poison a page for testing */ +#define MADV_SOFT_OFFLINE 101 /* soft offline page for testing */ + +#define MADV_GUARD_INSTALL 102 /* fatal signal on access to range */ +#define MADV_GUARD_REMOVE 103 /* unguard range */ + +/* compatibility flags */ +#define MAP_FILE 0 + +#define PKEY_DISABLE_ACCESS 0x1 +#define PKEY_DISABLE_WRITE 0x2 +#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\ + PKEY_DISABLE_WRITE) + +#endif /* __PARISC_MMAN_H__ */ diff --git a/arch/parisc/include/uapi/asm/msgbuf.h b/arch/parisc/include/uapi/asm/msgbuf.h new file mode 100644 index 000000000..3b4de5b66 --- /dev/null +++ b/arch/parisc/include/uapi/asm/msgbuf.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_MSGBUF_H +#define _PARISC_MSGBUF_H + +#include <asm/bitsperlong.h> +#include <asm/ipcbuf.h> + +/* + * The msqid64_ds structure for parisc architecture, copied from sparc. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 2 miscellaneous 32-bit values + */ + +struct msqid64_ds { + struct ipc64_perm msg_perm; +#if __BITS_PER_LONG == 64 + long msg_stime; /* last msgsnd time */ + long msg_rtime; /* last msgrcv time */ + long msg_ctime; /* last change time */ +#else + unsigned long msg_stime_high; + unsigned long msg_stime; /* last msgsnd time */ + unsigned long msg_rtime_high; + unsigned long msg_rtime; /* last msgrcv time */ + unsigned long msg_ctime_high; + unsigned long msg_ctime; /* last change time */ +#endif + unsigned long msg_cbytes; /* current number of bytes on queue */ + unsigned long msg_qnum; /* number of messages in queue */ + unsigned long msg_qbytes; /* max number of bytes on queue */ + __kernel_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_pid_t msg_lrpid; /* last receive pid */ + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* _PARISC_MSGBUF_H */ diff --git a/arch/parisc/include/uapi/asm/pdc.h b/arch/parisc/include/uapi/asm/pdc.h new file mode 100644 index 000000000..65031ddf8 --- /dev/null +++ b/arch/parisc/include/uapi/asm/pdc.h @@ -0,0 +1,746 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_PARISC_PDC_H +#define _UAPI_PARISC_PDC_H + +/* + * PDC return values ... + * All PDC calls return a subset of these errors. + */ + +#define PDC_WARN 3 /* Call completed with a warning */ +#define PDC_REQ_ERR_1 2 /* See above */ +#define PDC_REQ_ERR_0 1 /* Call would generate a requestor error */ +#define PDC_OK 0 /* Call completed successfully */ +#define PDC_BAD_PROC -1 /* Called non-existent procedure*/ +#define PDC_BAD_OPTION -2 /* Called with non-existent option */ +#define PDC_ERROR -3 /* Call could not complete without an error */ +#define PDC_NE_MOD -5 /* Module not found */ +#define PDC_NE_CELL_MOD -7 /* Cell module not found */ +#define PDC_NE_BOOTDEV -9 /* Cannot locate a console device or boot device */ +#define PDC_INVALID_ARG -10 /* Called with an invalid argument */ +#define PDC_BUS_POW_WARN -12 /* Call could not complete in allowed power budget */ +#define PDC_NOT_NARROW -17 /* Narrow mode not supported */ + +/* + * PDC entry points... + */ + +#define PDC_POW_FAIL 1 /* perform a power-fail */ +#define PDC_POW_FAIL_PREPARE 0 /* prepare for powerfail */ + +#define PDC_CHASSIS 2 /* PDC-chassis functions */ +#define PDC_CHASSIS_DISP 0 /* update chassis display */ +#define PDC_CHASSIS_WARN 1 /* return chassis warnings */ +#define PDC_CHASSIS_DISPWARN 2 /* update&return chassis status */ +#define PDC_RETURN_CHASSIS_INFO 128 /* HVERSION dependent: return chassis LED/LCD info */ + +#define PDC_PIM 3 /* Get PIM data */ +#define PDC_PIM_HPMC 0 /* Transfer HPMC data */ +#define PDC_PIM_RETURN_SIZE 1 /* Get Max buffer needed for PIM*/ +#define PDC_PIM_LPMC 2 /* Transfer HPMC data */ +#define PDC_PIM_SOFT_BOOT 3 /* Transfer Soft Boot data */ +#define PDC_PIM_TOC 4 /* Transfer TOC data */ + +#define PDC_MODEL 4 /* PDC model information call */ +#define PDC_MODEL_INFO 0 /* returns information */ +#define PDC_MODEL_BOOTID 1 /* set the BOOT_ID */ +#define PDC_MODEL_VERSIONS 2 /* returns cpu-internal versions*/ +#define PDC_MODEL_SYSMODEL 3 /* return system model info */ +#define PDC_MODEL_ENSPEC 4 /* enable specific option */ +#define PDC_MODEL_DISPEC 5 /* disable specific option */ +#define PDC_MODEL_CPU_ID 6 /* returns cpu-id (only newer machines!) */ +#define PDC_MODEL_CAPABILITIES 7 /* returns OS32/OS64-flags */ +/* Values for PDC_MODEL_CAPABILITIES non-equivalent virtual aliasing support */ +#define PDC_MODEL_OS64 (1 << 0) +#define PDC_MODEL_OS32 (1 << 1) +#define PDC_MODEL_IOPDIR_FDC (1 << 2) +#define PDC_MODEL_NVA_MASK (3 << 4) +#define PDC_MODEL_NVA_SUPPORTED (0 << 4) +#define PDC_MODEL_NVA_SLOW (1 << 4) +#define PDC_MODEL_NVA_UNSUPPORTED (3 << 4) +#define PDC_MODEL_FIRM_TEST_GET 8 /* returns boot test options */ +#define PDC_MODEL_FIRM_TEST_SET 9 /* set boot test options */ +#define PDC_MODEL_GET_PLATFORM_INFO 10 /* returns platform info */ +#define PDC_MODEL_GET_INSTALL_KERNEL 11 /* returns kernel for installation */ + +#define PA89_INSTRUCTION_SET 0x4 /* capabilities returned */ +#define PA90_INSTRUCTION_SET 0x8 + +#define PDC_CACHE 5 /* return/set cache (& TLB) info*/ +#define PDC_CACHE_INFO 0 /* returns information */ +#define PDC_CACHE_SET_COH 1 /* set coherence state */ +#define PDC_CACHE_RET_SPID 2 /* returns space-ID bits */ + +#define PDC_HPA 6 /* return HPA of processor */ +#define PDC_HPA_PROCESSOR 0 +#define PDC_HPA_MODULES 1 + +#define PDC_COPROC 7 /* Co-Processor (usually FP unit(s)) */ +#define PDC_COPROC_CFG 0 /* Co-Processor Cfg (FP unit(s) enabled?) */ + +#define PDC_IODC 8 /* talk to IODC */ +#define PDC_IODC_READ 0 /* read IODC entry point */ +/* PDC_IODC_RI_ * INDEX parameter of PDC_IODC_READ */ +#define PDC_IODC_RI_DATA_BYTES 0 /* IODC Data Bytes */ +/* 1, 2 obsolete - HVERSION dependent*/ +#define PDC_IODC_RI_INIT 3 /* Initialize module */ +#define PDC_IODC_RI_IO 4 /* Module input/output */ +#define PDC_IODC_RI_SPA 5 /* Module input/output */ +#define PDC_IODC_RI_CONFIG 6 /* Module input/output */ +/* 7 obsolete - HVERSION dependent */ +#define PDC_IODC_RI_TEST 8 /* Module input/output */ +#define PDC_IODC_RI_TLB 9 /* Module input/output */ +#define PDC_IODC_NINIT 2 /* non-destructive init */ +#define PDC_IODC_DINIT 3 /* destructive init */ +#define PDC_IODC_MEMERR 4 /* check for memory errors */ +#define PDC_IODC_INDEX_DATA 0 /* get first 16 bytes from mod IODC */ +#define PDC_IODC_BUS_ERROR -4 /* bus error return value */ +#define PDC_IODC_INVALID_INDEX -5 /* invalid index return value */ +#define PDC_IODC_COUNT -6 /* count is too small */ + +#define PDC_TOD 9 /* time-of-day clock (TOD) */ +#define PDC_TOD_READ 0 /* read TOD */ +#define PDC_TOD_WRITE 1 /* write TOD */ +#define PDC_TOD_CALIBRATE 2 /* calibrate timers */ + +#define PDC_STABLE 10 /* stable storage (sprockets) */ +#define PDC_STABLE_READ 0 +#define PDC_STABLE_WRITE 1 +#define PDC_STABLE_RETURN_SIZE 2 +#define PDC_STABLE_VERIFY_CONTENTS 3 +#define PDC_STABLE_INITIALIZE 4 + +#define PDC_NVOLATILE 11 /* often not implemented */ +#define PDC_NVOLATILE_READ 0 +#define PDC_NVOLATILE_WRITE 1 +#define PDC_NVOLATILE_RETURN_SIZE 2 +#define PDC_NVOLATILE_VERIFY_CONTENTS 3 +#define PDC_NVOLATILE_INITIALIZE 4 + +#define PDC_ADD_VALID 12 /* Memory validation PDC call */ +#define PDC_ADD_VALID_VERIFY 0 /* Make PDC_ADD_VALID verify region */ + +#define PDC_DEBUG 14 /* Obsolete */ + +#define PDC_INSTR 15 /* get instr to invoke PDCE_CHECK() */ + +#define PDC_PROC 16 /* (sprockets) */ + +#define PDC_CONFIG 17 /* (sprockets) */ +#define PDC_CONFIG_DECONFIG 0 +#define PDC_CONFIG_DRECONFIG 1 +#define PDC_CONFIG_DRETURN_CONFIG 2 + +#define PDC_BLOCK_TLB 18 /* manage hardware block-TLB */ +#define PDC_BTLB_INFO 0 /* returns parameter */ +#define PDC_BTLB_INSERT 1 /* insert BTLB entry */ +#define PDC_BTLB_PURGE 2 /* purge BTLB entries */ +#define PDC_BTLB_PURGE_ALL 3 /* purge all BTLB entries */ + +#define PDC_TLB 19 /* manage hardware TLB miss handling */ +#define PDC_TLB_INFO 0 /* returns parameter */ +#define PDC_TLB_SETUP 1 /* set up miss handling */ + +#define PDC_MEM 20 /* Manage memory */ +#define PDC_MEM_MEMINFO 0 /* Return PDT info */ +#define PDC_MEM_ADD_PAGE 1 /* Add page to PDT */ +#define PDC_MEM_CLEAR_PDT 2 /* Clear PDT */ +#define PDC_MEM_READ_PDT 3 /* Read PDT entry */ +#define PDC_MEM_RESET_CLEAR 4 /* Reset PDT clear flag */ +#define PDC_MEM_GOODMEM 5 /* Set good_mem value */ +#define PDC_MEM_TABLE 128 /* Non contig mem map (sprockets) */ +#define PDC_MEM_RETURN_ADDRESS_TABLE PDC_MEM_TABLE +#define PDC_MEM_GET_MEMORY_SYSTEM_TABLES_SIZE 131 +#define PDC_MEM_GET_MEMORY_SYSTEM_TABLES 132 +#define PDC_MEM_GET_PHYSICAL_LOCATION_FROM_MEMORY_ADDRESS 133 + +#define PDC_MEM_RET_SBE_REPLACED 5 /* PDC_MEM return values */ +#define PDC_MEM_RET_DUPLICATE_ENTRY 4 +#define PDC_MEM_RET_BUF_SIZE_SMALL 1 +#define PDC_MEM_RET_PDT_FULL -11 +#define PDC_MEM_RET_INVALID_PHYSICAL_LOCATION ~0ULL + +#define PDC_PSW 21 /* Get/Set default System Mask */ +#define PDC_PSW_MASK 0 /* Return mask */ +#define PDC_PSW_GET_DEFAULTS 1 /* Return defaults */ +#define PDC_PSW_SET_DEFAULTS 2 /* Set default */ +#define PDC_PSW_ENDIAN_BIT 1 /* set for big endian */ +#define PDC_PSW_WIDE_BIT 2 /* set for wide mode */ + +#define PDC_SYSTEM_MAP 22 /* find system modules */ +#define PDC_FIND_MODULE 0 +#define PDC_FIND_ADDRESS 1 +#define PDC_TRANSLATE_PATH 2 + +#define PDC_SOFT_POWER 23 /* soft power switch */ +#define PDC_SOFT_POWER_INFO 0 /* return info about the soft power switch */ +#define PDC_SOFT_POWER_ENABLE 1 /* enable/disable soft power switch */ + +#define PDC_ALLOC 24 /* allocate static storage for PDC & IODC */ + +#define PDC_CRASH_PREP 25 /* Prepare system for crash dump */ +#define PDC_CRASH_DUMP 0 /* Do platform specific preparations for dump */ +#define PDC_CRASH_LOG_CEC_ERROR 1 /* Dump hardware registers */ + +#define PDC_SCSI_PARMS 26 /* Get and set SCSI parameters */ +#define PDC_SCSI_GET_PARMS 0 /* Get SCSI parameters for I/O device */ +#define PDC_SCSI_SET_PARMS 1 /* Set SCSI parameters for I/O device */ + +/* HVERSION dependent */ + +/* The PDC_MEM_MAP calls */ +#define PDC_MEM_MAP 128 /* on s700: return page info */ +#define PDC_MEM_MAP_HPA 0 /* returns hpa of a module */ + +#define PDC_EEPROM 129 /* EEPROM access */ +#define PDC_EEPROM_READ_WORD 0 +#define PDC_EEPROM_WRITE_WORD 1 +#define PDC_EEPROM_READ_BYTE 2 +#define PDC_EEPROM_WRITE_BYTE 3 +#define PDC_EEPROM_EEPROM_PASSWORD -1000 + +#define PDC_NVM 130 /* NVM (non-volatile memory) access */ +#define PDC_NVM_READ_WORD 0 +#define PDC_NVM_WRITE_WORD 1 +#define PDC_NVM_READ_BYTE 2 +#define PDC_NVM_WRITE_BYTE 3 + +#define PDC_SEED_ERROR 132 /* (sprockets) */ + +#define PDC_IO 135 /* log error info, reset IO system */ +#define PDC_IO_READ_AND_CLEAR_ERRORS 0 +#define PDC_IO_RESET 1 +#define PDC_IO_RESET_DEVICES 2 +/* sets bits 6&7 (little endian) of the HcControl Register */ +#define PDC_IO_USB_SUSPEND 0xC000000000000000 +#define PDC_IO_EEPROM_IO_ERR_TABLE_FULL -5 /* return value */ +#define PDC_IO_NO_SUSPEND -6 /* return value */ + +#define PDC_BROADCAST_RESET 136 /* reset all processors */ +#define PDC_DO_RESET 0 /* option: perform a broadcast reset */ +#define PDC_DO_FIRM_TEST_RESET 1 /* Do broadcast reset with bitmap */ +#define PDC_BR_RECONFIGURATION 2 /* reset w/reconfiguration */ +#define PDC_FIRM_TEST_MAGIC 0xab9ec36fUL /* for this reboot only */ + +#define PDC_LAN_STATION_ID 138 /* Hversion dependent mechanism for */ +#define PDC_LAN_STATION_ID_READ 0 /* getting the lan station address */ + +#define PDC_LAN_STATION_ID_SIZE 6 + +#define PDC_CHECK_RANGES 139 /* (sprockets) */ + +#define PDC_NV_SECTIONS 141 /* (sprockets) */ + +#define PDC_PERFORMANCE 142 /* performance monitoring */ + +#define PDC_SYSTEM_INFO 143 /* system information */ +#define PDC_SYSINFO_RETURN_INFO_SIZE 0 +#define PDC_SYSINFO_RRETURN_SYS_INFO 1 +#define PDC_SYSINFO_RRETURN_ERRORS 2 +#define PDC_SYSINFO_RRETURN_WARNINGS 3 +#define PDC_SYSINFO_RETURN_REVISIONS 4 +#define PDC_SYSINFO_RRETURN_DIAGNOSE 5 +#define PDC_SYSINFO_RRETURN_HV_DIAGNOSE 1005 + +#define PDC_RDR 144 /* (sprockets) */ +#define PDC_RDR_READ_BUFFER 0 +#define PDC_RDR_READ_SINGLE 1 +#define PDC_RDR_WRITE_SINGLE 2 + +#define PDC_INTRIGUE 145 /* (sprockets) */ +#define PDC_INTRIGUE_WRITE_BUFFER 0 +#define PDC_INTRIGUE_GET_SCRATCH_BUFSIZE 1 +#define PDC_INTRIGUE_START_CPU_COUNTERS 2 +#define PDC_INTRIGUE_STOP_CPU_COUNTERS 3 + +#define PDC_STI 146 /* STI access */ +/* same as PDC_PCI_XXX values (see below) */ + +/* Legacy PDC definitions for same stuff */ +#define PDC_PCI_INDEX 147 +#define PDC_PCI_INTERFACE_INFO 0 +#define PDC_PCI_SLOT_INFO 1 +#define PDC_PCI_INFLIGHT_BYTES 2 +#define PDC_PCI_READ_CONFIG 3 +#define PDC_PCI_WRITE_CONFIG 4 +#define PDC_PCI_READ_PCI_IO 5 +#define PDC_PCI_WRITE_PCI_IO 6 +#define PDC_PCI_READ_CONFIG_DELAY 7 +#define PDC_PCI_UPDATE_CONFIG_DELAY 8 +#define PDC_PCI_PCI_PATH_TO_PCI_HPA 9 +#define PDC_PCI_PCI_HPA_TO_PCI_PATH 10 +#define PDC_PCI_PCI_PATH_TO_PCI_BUS 11 +#define PDC_PCI_PCI_RESERVED 12 +#define PDC_PCI_PCI_INT_ROUTE_SIZE 13 +#define PDC_PCI_GET_INT_TBL_SIZE PDC_PCI_PCI_INT_ROUTE_SIZE +#define PDC_PCI_PCI_INT_ROUTE 14 +#define PDC_PCI_GET_INT_TBL PDC_PCI_PCI_INT_ROUTE +#define PDC_PCI_READ_MON_TYPE 15 +#define PDC_PCI_WRITE_MON_TYPE 16 + +#define PDC_RELOCATE 149 /* (sprockets) */ +#define PDC_RELOCATE_GET_RELOCINFO 0 +#define PDC_RELOCATE_CHECKSUM 1 +#define PDC_RELOCATE_RELOCATE 2 + +/* Get SCSI Interface Card info: SDTR, SCSI ID, mode (SE vs LVD) */ +#define PDC_INITIATOR 163 +#define PDC_GET_INITIATOR 0 +#define PDC_SET_INITIATOR 1 +#define PDC_DELETE_INITIATOR 2 +#define PDC_RETURN_TABLE_SIZE 3 +#define PDC_RETURN_TABLE 4 + +#define PDC_LINK 165 /* (sprockets) */ +#define PDC_LINK_PCI_ENTRY_POINTS 0 /* list (Arg1) = 0 */ +#define PDC_LINK_USB_ENTRY_POINTS 1 /* list (Arg1) = 1 */ + +/* cl_class + * page 3-33 of IO-Firmware ARS + * IODC ENTRY_INIT(Search first) RET[1] + */ +#define CL_NULL 0 /* invalid */ +#define CL_RANDOM 1 /* random access (as disk) */ +#define CL_SEQU 2 /* sequential access (as tape) */ +#define CL_DUPLEX 7 /* full-duplex point-to-point (RS-232, Net) */ +#define CL_KEYBD 8 /* half-duplex console (HIL Keyboard) */ +#define CL_DISPL 9 /* half-duplex console (display) */ +#define CL_FC 10 /* FiberChannel access media */ + +/* IODC ENTRY_INIT() */ +#define ENTRY_INIT_SRCH_FRST 2 +#define ENTRY_INIT_SRCH_NEXT 3 +#define ENTRY_INIT_MOD_DEV 4 +#define ENTRY_INIT_DEV 5 +#define ENTRY_INIT_MOD 6 +#define ENTRY_INIT_MSG 9 + +/* IODC ENTRY_IO() */ +#define ENTRY_IO_BOOTIN 0 +#define ENTRY_IO_BOOTOUT 1 +#define ENTRY_IO_CIN 2 +#define ENTRY_IO_COUT 3 +#define ENTRY_IO_CLOSE 4 +#define ENTRY_IO_GETMSG 9 +#define ENTRY_IO_BBLOCK_IN 16 +#define ENTRY_IO_BBLOCK_OUT 17 + +/* IODC ENTRY_SPA() */ + +/* IODC ENTRY_CONFIG() */ + +/* IODC ENTRY_TEST() */ + +/* IODC ENTRY_TLB() */ + +/* constants for OS (NVM...) */ +#define OS_ID_NONE 0 /* Undefined OS ID */ +#define OS_ID_HPUX 1 /* HP-UX OS */ +#define OS_ID_MPEXL 2 /* MPE XL OS */ +#define OS_ID_OSF 3 /* OSF OS */ +#define OS_ID_HPRT 4 /* HP-RT OS */ +#define OS_ID_NOVEL 5 /* NOVELL OS */ +#define OS_ID_LINUX 6 /* Linux */ + + +/* constants for PDC_CHASSIS */ +#define OSTAT_OFF 0 +#define OSTAT_FLT 1 +#define OSTAT_TEST 2 +#define OSTAT_INIT 3 +#define OSTAT_SHUT 4 +#define OSTAT_WARN 5 +#define OSTAT_RUN 6 +#define OSTAT_ON 7 + +/* Page Zero constant offsets used by the HPMC handler */ +#define BOOT_CONSOLE_HPA_OFFSET 0x3c0 +#define BOOT_CONSOLE_SPA_OFFSET 0x3c4 +#define BOOT_CONSOLE_PATH_OFFSET 0x3a8 + +/* size of the pdc_result buffer for firmware.c */ +#define NUM_PDC_RESULT 32 + +#if !defined(__ASSEMBLER__) + +/* flags for hardware_path */ +#define PF_AUTOBOOT 0x80 +#define PF_AUTOSEARCH 0x40 +#define PF_TIMER 0x0F + +struct hardware_path { + unsigned char flags; /* see bit definitions below */ + signed char bc[6]; /* Bus Converter routing info to a specific */ + /* I/O adaptor (< 0 means none, > 63 resvd) */ + signed char mod; /* fixed field of specified module */ +}; + +struct pdc_module_path { /* page 1-69 */ + struct hardware_path path; + unsigned int layers[6]; /* device-specific info (ctlr #, unit # ...) */ +} __attribute__((aligned(8))); + +struct pz_device { + struct pdc_module_path dp; /* see above */ + /* struct iomod *hpa; */ + unsigned int hpa; /* HPA base address */ + /* char *spa; */ + unsigned int spa; /* SPA base address */ + /* int (*iodc_io)(struct iomod*, ...); */ + unsigned int iodc_io; /* device entry point */ + short pad; /* reserved */ + unsigned short cl_class;/* see below */ +} __attribute__((aligned(8))) ; + +struct zeropage { + /* [0x000] initialize vectors (VEC) */ + unsigned int vec_special; /* must be zero */ + /* int (*vec_pow_fail)(void);*/ + unsigned int vec_pow_fail; /* power failure handler */ + /* int (*vec_toc)(void); */ + unsigned int vec_toc; + unsigned int vec_toclen; + /* int (*vec_rendz)(void); */ + unsigned int vec_rendz; + int vec_pow_fail_flen; + int vec_pad0[3]; + unsigned int vec_toc_hi; + int vec_pad1[6]; + + /* [0x040] reserved processor dependent */ + int pad0[112]; /* in QEMU pad0[0] holds "SeaBIOS\0" */ + + /* [0x200] reserved */ + int pad1[84]; + + /* [0x350] memory configuration (MC) */ + int memc_cont; /* contiguous mem size (bytes) */ + int memc_phsize; /* physical memory size */ + int memc_adsize; /* additional mem size, bytes of SPA space used by PDC */ + unsigned int mem_pdc_hi; /* used for 64-bit */ + + /* [0x360] various parameters for the boot-CPU */ + /* unsigned int *mem_booterr[8]; */ + unsigned int mem_booterr[8]; /* ptr to boot errors */ + unsigned int mem_free; /* first location, where OS can be loaded */ + /* struct iomod *mem_hpa; */ + unsigned int mem_hpa; /* HPA of the boot-CPU */ + /* int (*mem_pdc)(int, ...); */ + unsigned int mem_pdc; /* PDC entry point */ + unsigned int mem_10msec; /* number of clock ticks in 10msec */ + + /* [0x390] initial memory module (IMM) */ + /* struct iomod *imm_hpa; */ + unsigned int imm_hpa; /* HPA of the IMM */ + int imm_soft_boot; /* 0 = was hard boot, 1 = was soft boot */ + unsigned int imm_spa_size; /* SPA size of the IMM in bytes */ + unsigned int imm_max_mem; /* bytes of mem in IMM */ + + /* [0x3A0] boot console, display device and keyboard */ + struct pz_device mem_cons; /* description of console device */ + struct pz_device mem_boot; /* description of boot device */ + struct pz_device mem_kbd; /* description of keyboard device */ + + /* [0x430] reserved */ + int pad430[116]; + + /* [0x600] processor dependent */ + unsigned int pad600[1]; + unsigned int proc_sti; /* pointer to STI ROM */ + unsigned int pad608[126]; +}; + +struct pdc_chassis_info { /* for PDC_CHASSIS_INFO */ + unsigned long actcnt; /* actual number of bytes returned */ + unsigned long maxcnt; /* maximum number of bytes that could be returned */ +}; + +struct pdc_coproc_cfg { /* for PDC_COPROC_CFG */ + unsigned long ccr_functional; + unsigned long ccr_present; + unsigned long revision; + unsigned long model; +}; + +struct pdc_model { /* for PDC_MODEL */ + unsigned long hversion; + unsigned long sversion; + unsigned long hw_id; + unsigned long boot_id; + unsigned long sw_id; + unsigned long sw_cap; + unsigned long arch_rev; + unsigned long pot_key; + unsigned long curr_key; + unsigned long width; /* default of PSW_W bit (1=enabled) */ +}; + +struct pdc_cache_cf { /* for PDC_CACHE (I/D-caches) */ + unsigned long +#ifdef __LP64__ + cc_padW:32, +#endif + cc_alias: 4, /* alias boundaries for virtual addresses */ + cc_block: 4, /* to determine most efficient stride */ + cc_line : 3, /* maximum amount written back as a result of store (multiple of 16 bytes) */ + cc_shift: 2, /* how much to shift cc_block left */ + cc_wt : 1, /* 0 = WT-Dcache, 1 = WB-Dcache */ + cc_sh : 2, /* 0 = separate I/D-cache, else shared I/D-cache */ + cc_cst : 3, /* 0 = incoherent D-cache, 1=coherent D-cache */ + cc_pad1 : 10, /* reserved */ + cc_hv : 3; /* hversion dependent */ +}; + +struct pdc_tlb_cf { /* for PDC_CACHE (I/D-TLB's) */ + unsigned long tc_pad0:12, /* reserved */ +#ifdef __LP64__ + tc_padW:32, +#endif + tc_sh : 2, /* 0 = separate I/D-TLB, else shared I/D-TLB */ + tc_hv : 1, /* HV */ + tc_page : 1, /* 0 = 2K page-size-machine, 1 = 4k page size */ + tc_cst : 3, /* 0 = incoherent operations, else coherent operations */ + tc_aid : 5, /* ITLB: width of access ids of processor (encoded!) */ + tc_sr : 8; /* ITLB: width of space-registers (encoded) */ +}; + +struct pdc_cache_info { /* main-PDC_CACHE-structure (caches & TLB's) */ + /* I-cache */ + unsigned long ic_size; /* size in bytes */ + struct pdc_cache_cf ic_conf; /* configuration */ + unsigned long ic_base; /* base-addr */ + unsigned long ic_stride; + unsigned long ic_count; + unsigned long ic_loop; + /* D-cache */ + unsigned long dc_size; /* size in bytes */ + struct pdc_cache_cf dc_conf; /* configuration */ + unsigned long dc_base; /* base-addr */ + unsigned long dc_stride; + unsigned long dc_count; + unsigned long dc_loop; + /* Instruction-TLB */ + unsigned long it_size; /* number of entries in I-TLB */ + struct pdc_tlb_cf it_conf; /* I-TLB-configuration */ + unsigned long it_sp_base; + unsigned long it_sp_stride; + unsigned long it_sp_count; + unsigned long it_off_base; + unsigned long it_off_stride; + unsigned long it_off_count; + unsigned long it_loop; + /* data-TLB */ + unsigned long dt_size; /* number of entries in D-TLB */ + struct pdc_tlb_cf dt_conf; /* D-TLB-configuration */ + unsigned long dt_sp_base; + unsigned long dt_sp_stride; + unsigned long dt_sp_count; + unsigned long dt_off_base; + unsigned long dt_off_stride; + unsigned long dt_off_count; + unsigned long dt_loop; +}; + +/* Might need adjustment to work with 64-bit firmware */ +struct pdc_iodc { /* PDC_IODC */ + unsigned char hversion_model; + unsigned char hversion; + unsigned char spa; + unsigned char type; + unsigned int sversion_rev:4; + unsigned int sversion_model:19; + unsigned int sversion_opt:8; + unsigned char rev; + unsigned char dep; + unsigned char features; + unsigned char pad1; + unsigned int checksum:16; + unsigned int length:16; + unsigned int pad[15]; +} __attribute__((aligned(8))) ; + +/* no BLTBs in pa2.0 processors */ +struct pdc_btlb_info_range { + unsigned char res00; + unsigned char num_i; + unsigned char num_d; + unsigned char num_comb; +}; + +struct pdc_btlb_info { /* PDC_BLOCK_TLB, return of PDC_BTLB_INFO */ + unsigned int min_size; /* minimum size of BTLB in pages */ + unsigned int max_size; /* maximum size of BTLB in pages */ + struct pdc_btlb_info_range fixed_range_info; + struct pdc_btlb_info_range variable_range_info; +}; + +struct pdc_mem_retinfo { /* PDC_MEM/PDC_MEM_MEMINFO (return info) */ + unsigned long pdt_size; + unsigned long pdt_entries; + unsigned long pdt_status; + unsigned long first_dbe_loc; + unsigned long good_mem; +}; + +struct pdc_mem_read_pdt { /* PDC_MEM/PDC_MEM_READ_PDT (return info) */ + unsigned long pdt_entries; +}; + +#ifdef __LP64__ +struct pdc_memory_table_raddr { /* PDC_MEM/PDC_MEM_TABLE (return info) */ + unsigned long entries_returned; + unsigned long entries_total; +}; + +struct pdc_memory_table { /* PDC_MEM/PDC_MEM_TABLE (arguments) */ + unsigned long paddr; + unsigned int pages; + unsigned int reserved; +}; +#endif /* __LP64__ */ + +struct pdc_system_map_mod_info { /* PDC_SYSTEM_MAP/FIND_MODULE */ + unsigned long mod_addr; + unsigned long mod_pgs; + unsigned long add_addrs; +}; + +struct pdc_system_map_addr_info { /* PDC_SYSTEM_MAP/FIND_ADDRESS */ + unsigned long mod_addr; + unsigned long mod_pgs; +}; + +struct pdc_relocate_info_block { /* PDC_RELOCATE_INFO */ + unsigned long pdc_size; + unsigned long pdc_alignment; + unsigned long pdc_address; +}; + +struct pdc_initiator { /* PDC_INITIATOR */ + int host_id; + int factor; + int width; + int mode; +}; + +/* Only used on some pre-PA2.0 boxes */ +struct pdc_memory_map { /* PDC_MEMORY_MAP */ + unsigned long hpa; /* mod's register set address */ + unsigned long more_pgs; /* number of additional I/O pgs */ +}; + +struct pdc_tod { + unsigned long tod_sec; + unsigned long tod_usec; +}; + +/* architected results from PDC_PIM/transfer hpmc on a PA1.1 machine */ + +struct pdc_hpmc_pim_11 { /* PDC_PIM */ + unsigned int gr[32]; + unsigned int cr[32]; + unsigned int sr[8]; + unsigned int iasq_back; + unsigned int iaoq_back; + unsigned int check_type; + unsigned int cpu_state; + unsigned int rsvd1; + unsigned int cache_check; + unsigned int tlb_check; + unsigned int bus_check; + unsigned int assists_check; + unsigned int rsvd2; + unsigned int assist_state; + unsigned int responder_addr; + unsigned int requestor_addr; + unsigned int path_info; + unsigned long long fr[32]; +}; + +/* + * architected results from PDC_PIM/transfer hpmc on a PA2.0 machine + * + * Note that PDC_PIM doesn't care whether or not wide mode was enabled + * so the results are different on PA1.1 vs. PA2.0 when in narrow mode. + * + * Note also that there are unarchitected results available, which + * are hversion dependent. Do a "ser pim 0 hpmc" after rebooting, since + * the firmware is probably the best way of printing hversion dependent + * data. + */ + +struct pdc_hpmc_pim_20 { /* PDC_PIM */ + unsigned long long gr[32]; + unsigned long long cr[32]; + unsigned long long sr[8]; + unsigned long long iasq_back; + unsigned long long iaoq_back; + unsigned int check_type; + unsigned int cpu_state; + unsigned int cache_check; + unsigned int tlb_check; + unsigned int bus_check; + unsigned int assists_check; + unsigned int assist_state; + unsigned int path_info; + unsigned long long responder_addr; + unsigned long long requestor_addr; + unsigned long long fr[32]; +}; + +struct pim_cpu_state_cf { + union { + unsigned int + iqv : 1, /* IIA queue Valid */ + iqf : 1, /* IIA queue Failure */ + ipv : 1, /* IPRs Valid */ + grv : 1, /* GRs Valid */ + crv : 1, /* CRs Valid */ + srv : 1, /* SRs Valid */ + trv : 1, /* CR24 through CR31 valid */ + pad : 24, /* reserved */ + td : 1; /* TOC did not cause any damage to the system state */ + unsigned int val; + }; +}; + +struct pdc_toc_pim_11 { + unsigned int gr[32]; + unsigned int cr[32]; + unsigned int sr[8]; + unsigned int iasq_back; + unsigned int iaoq_back; + unsigned int check_type; + struct pim_cpu_state_cf cpu_state; +}; + +struct pdc_toc_pim_20 { + unsigned long long gr[32]; + unsigned long long cr[32]; + unsigned long long sr[8]; + unsigned long long iasq_back; + unsigned long long iaoq_back; + unsigned int check_type; + struct pim_cpu_state_cf cpu_state; +}; + +/* for SpeedyBoot/firm_ctl funtionality */ +struct pdc_firm_test_get_rtn_block { /* PDC_MODEL/PDC_FIRM_TEST_GET */ + unsigned long current_tests; /* u_R_addr Raddr_ints[0] */ + unsigned long tests_supported; /* u_R_addr Raddr_ints[1] */ + unsigned long default_tests; /* u_R_addr Raddr_ints[2] */ +}; + +#define TORNADO_CPU_ID 0xB +#define PCXL_CPU_ID 0xD +#define PCXU_CPU_ID 0xE /* U and U+ for all but C-class with bug */ +#define VR_CPU_ID 0xF +#define PCXU_PLUS_CPU_ID 0x10 /* U+ only on C-class with bug */ +#define PCXW_CPU_ID 0x11 +#define PCXW_PLUS_CPU_ID 0x12 +#define PIRANHA_CPU_ID 0x13 +#define MAKO_CPU_ID 0x14 + +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* _UAPI_PARISC_PDC_H */ diff --git a/arch/parisc/include/uapi/asm/perf_regs.h b/arch/parisc/include/uapi/asm/perf_regs.h new file mode 100644 index 000000000..1ae687bb3 --- /dev/null +++ b/arch/parisc/include/uapi/asm/perf_regs.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_PARISC_PERF_REGS_H +#define _UAPI_ASM_PARISC_PERF_REGS_H + +/* see struct user_regs_struct */ +enum perf_event_parisc_regs { + PERF_REG_PARISC_R0, /* PSW is in gr[0] */ + PERF_REG_PARISC_R1, + PERF_REG_PARISC_R2, + PERF_REG_PARISC_R3, + PERF_REG_PARISC_R4, + PERF_REG_PARISC_R5, + PERF_REG_PARISC_R6, + PERF_REG_PARISC_R7, + PERF_REG_PARISC_R8, + PERF_REG_PARISC_R9, + PERF_REG_PARISC_R10, + PERF_REG_PARISC_R11, + PERF_REG_PARISC_R12, + PERF_REG_PARISC_R13, + PERF_REG_PARISC_R14, + PERF_REG_PARISC_R15, + PERF_REG_PARISC_R16, + PERF_REG_PARISC_R17, + PERF_REG_PARISC_R18, + PERF_REG_PARISC_R19, + PERF_REG_PARISC_R20, + PERF_REG_PARISC_R21, + PERF_REG_PARISC_R22, + PERF_REG_PARISC_R23, + PERF_REG_PARISC_R24, + PERF_REG_PARISC_R25, + PERF_REG_PARISC_R26, + PERF_REG_PARISC_R27, + PERF_REG_PARISC_R28, + PERF_REG_PARISC_R29, + PERF_REG_PARISC_R30, + PERF_REG_PARISC_R31, + + PERF_REG_PARISC_SR0, + PERF_REG_PARISC_SR1, + PERF_REG_PARISC_SR2, + PERF_REG_PARISC_SR3, + PERF_REG_PARISC_SR4, + PERF_REG_PARISC_SR5, + PERF_REG_PARISC_SR6, + PERF_REG_PARISC_SR7, + + PERF_REG_PARISC_IAOQ0, + PERF_REG_PARISC_IAOQ1, + PERF_REG_PARISC_IASQ0, + PERF_REG_PARISC_IASQ1, + + PERF_REG_PARISC_SAR, /* CR11 */ + PERF_REG_PARISC_IIR, /* CR19 */ + PERF_REG_PARISC_ISR, /* CR20 */ + PERF_REG_PARISC_IOR, /* CR21 */ + PERF_REG_PARISC_IPSW, /* CR22 */ + + PERF_REG_PARISC_MAX +}; + +#endif /* _UAPI_ASM_PARISC_PERF_REGS_H */ diff --git a/arch/parisc/include/uapi/asm/posix_types.h b/arch/parisc/include/uapi/asm/posix_types.h new file mode 100644 index 000000000..8dce56f5d --- /dev/null +++ b/arch/parisc/include/uapi/asm/posix_types.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ARCH_PARISC_POSIX_TYPES_H +#define __ARCH_PARISC_POSIX_TYPES_H + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. Also, we cannot + * assume GCC is being used. + */ + +#ifndef __LP64__ +typedef unsigned short __kernel_mode_t; +#define __kernel_mode_t __kernel_mode_t +#endif + +typedef unsigned short __kernel_ipc_pid_t; +#define __kernel_ipc_pid_t __kernel_ipc_pid_t + +typedef long long __kernel_off64_t; +typedef unsigned long long __kernel_ino64_t; + +#include <asm-generic/posix_types.h> + +#endif diff --git a/arch/parisc/include/uapi/asm/ptrace.h b/arch/parisc/include/uapi/asm/ptrace.h new file mode 100644 index 000000000..e72e06247 --- /dev/null +++ b/arch/parisc/include/uapi/asm/ptrace.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* written by Philipp Rumpf, Copyright (C) 1999 SuSE GmbH Nuernberg +** Copyright (C) 2000 Grant Grundler, Hewlett-Packard +*/ +#ifndef _UAPI_PARISC_PTRACE_H +#define _UAPI_PARISC_PTRACE_H + + +#include <linux/types.h> + +/* This struct defines the way the registers are stored on the + * stack during a system call. + * + * N.B. gdb/strace care about the size and offsets within this + * structure. If you change things, you may break object compatibility + * for those applications. + * + * Please do NOT use this structure for future programs, but use + * user_regs_struct (see below) instead. + * + * It can be accessed through PTRACE_PEEKUSR/PTRACE_POKEUSR only. + */ + +struct pt_regs { + unsigned long gr[32]; /* PSW is in gr[0] */ + __u64 fr[32]; + unsigned long sr[ 8]; + unsigned long iasq[2]; + unsigned long iaoq[2]; + unsigned long cr27; + unsigned long pad0; /* available for other uses */ + unsigned long orig_r28; + unsigned long ksp; + unsigned long kpc; + unsigned long sar; /* CR11 */ + unsigned long iir; /* CR19 */ + unsigned long isr; /* CR20 */ + unsigned long ior; /* CR21 */ + unsigned long ipsw; /* CR22 */ +}; + +/** + * struct user_regs_struct - User general purpose registers + * + * This is the user-visible general purpose register state structure + * which is used to define the elf_gregset_t. + * + * It can be accessed through PTRACE_GETREGSET with NT_PRSTATUS + * and through PTRACE_GETREGS. + */ +struct user_regs_struct { + unsigned long gr[32]; /* PSW is in gr[0] */ + unsigned long sr[8]; + unsigned long iaoq[2]; + unsigned long iasq[2]; + unsigned long sar; /* CR11 */ + unsigned long iir; /* CR19 */ + unsigned long isr; /* CR20 */ + unsigned long ior; /* CR21 */ + unsigned long ipsw; /* CR22 */ + unsigned long cr0; + unsigned long cr24, cr25, cr26, cr27, cr28, cr29, cr30, cr31; + unsigned long cr8, cr9, cr12, cr13, cr10, cr15; + unsigned long _pad[80-64]; /* pad to ELF_NGREG (80) */ +}; + +/** + * struct user_fp_struct - User floating point registers + * + * This is the user-visible floating point register state structure. + * It uses the same layout and size as elf_fpregset_t. + * + * It can be accessed through PTRACE_GETREGSET with NT_PRFPREG + * and through PTRACE_GETFPREGS. + */ +struct user_fp_struct { + __u64 fr[32]; +}; + + +/* + * The numbers chosen here are somewhat arbitrary but absolutely MUST + * not overlap with any of the number assigned in <linux/ptrace.h>. + * + * These ones are taken from IA-64 on the assumption that theirs are + * the most correct (and we also want to support PTRACE_SINGLEBLOCK + * since we have taken branch traps too) + */ +#define PTRACE_SINGLEBLOCK 12 /* resume execution until next branch */ + +#define PTRACE_GETREGS 18 +#define PTRACE_SETREGS 19 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 + +#endif /* _UAPI_PARISC_PTRACE_H */ diff --git a/arch/parisc/include/uapi/asm/sembuf.h b/arch/parisc/include/uapi/asm/sembuf.h new file mode 100644 index 000000000..e2ca4301c --- /dev/null +++ b/arch/parisc/include/uapi/asm/sembuf.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_SEMBUF_H +#define _PARISC_SEMBUF_H + +#include <asm/bitsperlong.h> +#include <asm/ipcbuf.h> + +/* + * The semid64_ds structure for parisc architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 2 miscellaneous 32-bit values + */ + +struct semid64_ds { + struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ +#if __BITS_PER_LONG == 64 + long sem_otime; /* last semop time */ + long sem_ctime; /* last change time */ +#else + unsigned long sem_otime_high; + unsigned long sem_otime; /* last semop time */ + unsigned long sem_ctime_high; + unsigned long sem_ctime; /* last change time */ +#endif + unsigned long sem_nsems; /* no. of semaphores in array */ + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* _PARISC_SEMBUF_H */ diff --git a/arch/parisc/include/uapi/asm/setup.h b/arch/parisc/include/uapi/asm/setup.h new file mode 100644 index 000000000..78b2f4ec7 --- /dev/null +++ b/arch/parisc/include/uapi/asm/setup.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_SETUP_H +#define _PARISC_SETUP_H + +#define COMMAND_LINE_SIZE 1024 + +#endif /* _PARISC_SETUP_H */ diff --git a/arch/parisc/include/uapi/asm/shmbuf.h b/arch/parisc/include/uapi/asm/shmbuf.h new file mode 100644 index 000000000..532da742f --- /dev/null +++ b/arch/parisc/include/uapi/asm/shmbuf.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_SHMBUF_H +#define _PARISC_SHMBUF_H + +#include <asm/bitsperlong.h> +#include <asm/ipcbuf.h> +#include <asm/posix_types.h> + +/* + * The shmid64_ds structure for parisc architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 2 miscellaneous 32-bit values + */ + +struct shmid64_ds { + struct ipc64_perm shm_perm; /* operation perms */ +#if __BITS_PER_LONG == 64 + long shm_atime; /* last attach time */ + long shm_dtime; /* last detach time */ + long shm_ctime; /* last change time */ +#else + unsigned long shm_atime_high; + unsigned long shm_atime; /* last attach time */ + unsigned long shm_dtime_high; + unsigned long shm_dtime; /* last detach time */ + unsigned long shm_ctime_high; + unsigned long shm_ctime; /* last change time */ + unsigned int __pad4; +#endif + __kernel_size_t shm_segsz; /* size of segment (bytes) */ + __kernel_pid_t shm_cpid; /* pid of creator */ + __kernel_pid_t shm_lpid; /* pid of last operator */ + unsigned long shm_nattch; /* no. of current attaches */ + unsigned long __unused1; + unsigned long __unused2; +}; + +struct shminfo64 { + unsigned long shmmax; + unsigned long shmmin; + unsigned long shmmni; + unsigned long shmseg; + unsigned long shmall; + unsigned long __unused1; + unsigned long __unused2; + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _PARISC_SHMBUF_H */ diff --git a/arch/parisc/include/uapi/asm/sigcontext.h b/arch/parisc/include/uapi/asm/sigcontext.h new file mode 100644 index 000000000..be404bb0f --- /dev/null +++ b/arch/parisc/include/uapi/asm/sigcontext.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASMPARISC_SIGCONTEXT_H +#define _ASMPARISC_SIGCONTEXT_H + +#define PARISC_SC_FLAG_ONSTACK 1<<0 +#define PARISC_SC_FLAG_IN_SYSCALL 1<<1 + +/* We will add more stuff here as it becomes necessary, until we know + it works. */ +struct sigcontext { + unsigned long sc_flags; + + unsigned long sc_gr[32]; /* PSW in sc_gr[0] */ + unsigned long long sc_fr[32]; /* FIXME, do we need other state info? */ + unsigned long sc_iasq[2]; + unsigned long sc_iaoq[2]; + unsigned long sc_sar; /* cr11 */ +}; + + +#endif diff --git a/arch/parisc/include/uapi/asm/signal.h b/arch/parisc/include/uapi/asm/signal.h new file mode 100644 index 000000000..d99accf37 --- /dev/null +++ b/arch/parisc/include/uapi/asm/signal.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_PARISC_SIGNAL_H +#define _UAPI_ASM_PARISC_SIGNAL_H + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGSTKFLT 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGBUS 10 +#define SIGSEGV 11 +#define SIGXCPU 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGUSR1 16 +#define SIGUSR2 17 +#define SIGCHLD 18 +#define SIGPWR 19 +#define SIGVTALRM 20 +#define SIGPROF 21 +#define SIGIO 22 +#define SIGPOLL SIGIO +#define SIGWINCH 23 +#define SIGSTOP 24 +#define SIGTSTP 25 +#define SIGCONT 26 +#define SIGTTIN 27 +#define SIGTTOU 28 +#define SIGURG 29 +#define SIGXFSZ 30 +#define SIGUNUSED 31 +#define SIGSYS 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +#define SA_ONSTACK 0x00000001 +#define SA_RESETHAND 0x00000004 +#define SA_NOCLDSTOP 0x00000008 +#define SA_SIGINFO 0x00000010 +#define SA_NODEFER 0x00000020 +#define SA_RESTART 0x00000040 +#define SA_NOCLDWAIT 0x00000080 + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#include <asm-generic/signal-defs.h> + +#define _NSIG 64 +#define _NSIG_BPW (sizeof(unsigned long) * 8) +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +# ifndef __ASSEMBLER__ + +# include <linux/types.h> + +typedef unsigned long old_sigset_t; /* at least 32 bits */ + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +/* Avoid too many header ordering problems. */ +struct siginfo; + +typedef struct sigaltstack { + void __user *ss_sp; + int ss_flags; + __kernel_size_t ss_size; +} stack_t; + +#endif /* !__ASSEMBLER__ */ +#endif /* _UAPI_ASM_PARISC_SIGNAL_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h new file mode 100644 index 000000000..f3a3815c7 --- /dev/null +++ b/arch/parisc/include/uapi/asm/socket.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_SOCKET_H +#define _UAPI_ASM_SOCKET_H + +#include <linux/posix_types.h> +#include <asm/sockios.h> + +/* For setsockopt(2) */ +#define SOL_SOCKET 0xffff + +#define SO_DEBUG 0x0001 +#define SO_REUSEADDR 0x0004 +#define SO_KEEPALIVE 0x0008 +#define SO_DONTROUTE 0x0010 +#define SO_BROADCAST 0x0020 +#define SO_LINGER 0x0080 +#define SO_OOBINLINE 0x0100 +#define SO_REUSEPORT 0x0200 +#define SO_SNDBUF 0x1001 +#define SO_RCVBUF 0x1002 +#define SO_SNDBUFFORCE 0x100a +#define SO_RCVBUFFORCE 0x100b +#define SO_SNDLOWAT 0x1003 +#define SO_RCVLOWAT 0x1004 +#define SO_SNDTIMEO_OLD 0x1005 +#define SO_RCVTIMEO_OLD 0x1006 +#define SO_ERROR 0x1007 +#define SO_TYPE 0x1008 +#define SO_PROTOCOL 0x1028 +#define SO_DOMAIN 0x1029 +#define SO_PEERNAME 0x2000 + +#define SO_NO_CHECK 0x400b +#define SO_PRIORITY 0x400c +#define SO_BSDCOMPAT 0x400e +#define SO_PASSCRED 0x4010 +#define SO_PEERCRED 0x4011 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define SO_SECURITY_AUTHENTICATION 0x4016 +#define SO_SECURITY_ENCRYPTION_TRANSPORT 0x4017 +#define SO_SECURITY_ENCRYPTION_NETWORK 0x4018 + +#define SO_BINDTODEVICE 0x4019 + +/* Socket filtering */ +#define SO_ATTACH_FILTER 0x401a +#define SO_DETACH_FILTER 0x401b +#define SO_GET_FILTER SO_ATTACH_FILTER + +#define SO_ACCEPTCONN 0x401c + +#define SO_PEERSEC 0x401d +#define SO_PASSSEC 0x401e + +#define SO_MARK 0x401f + +#define SO_RXQ_OVFL 0x4021 + +#define SO_WIFI_STATUS 0x4022 +#define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 0x4023 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 0x4024 + +#define SO_LOCK_FILTER 0x4025 + +#define SO_SELECT_ERR_QUEUE 0x4026 + +#define SO_BUSY_POLL 0x4027 + +#define SO_MAX_PACING_RATE 0x4028 + +#define SO_BPF_EXTENSIONS 0x4029 + +#define SO_INCOMING_CPU 0x402A + +#define SO_ATTACH_BPF 0x402B +#define SO_DETACH_BPF SO_DETACH_FILTER + +#define SO_ATTACH_REUSEPORT_CBPF 0x402C +#define SO_ATTACH_REUSEPORT_EBPF 0x402D + +#define SO_CNX_ADVICE 0x402E + +#define SCM_TIMESTAMPING_OPT_STATS 0x402F + +#define SO_MEMINFO 0x4030 + +#define SO_INCOMING_NAPI_ID 0x4031 + +#define SO_COOKIE 0x4032 + +#define SCM_TIMESTAMPING_PKTINFO 0x4033 + +#define SO_PEERGROUPS 0x4034 + +#define SO_ZEROCOPY 0x4035 + +#define SO_TXTIME 0x4036 +#define SCM_TXTIME SO_TXTIME + +#define SO_BINDTOIFINDEX 0x4037 + +#define SO_TIMESTAMP_OLD 0x4012 +#define SO_TIMESTAMPNS_OLD 0x4013 +#define SO_TIMESTAMPING_OLD 0x4020 + +#define SO_TIMESTAMP_NEW 0x4038 +#define SO_TIMESTAMPNS_NEW 0x4039 +#define SO_TIMESTAMPING_NEW 0x403A + +#define SO_RCVTIMEO_NEW 0x4040 +#define SO_SNDTIMEO_NEW 0x4041 + +#define SO_DETACH_REUSEPORT_BPF 0x4042 + +#define SO_PREFER_BUSY_POLL 0x4043 +#define SO_BUSY_POLL_BUDGET 0x4044 + +#define SO_NETNS_COOKIE 0x4045 + +#define SO_BUF_LOCK 0x4046 + +#define SO_RESERVE_MEM 0x4047 + +#define SO_TXREHASH 0x4048 + +#define SO_RCVMARK 0x4049 + +#define SO_PASSPIDFD 0x404A +#define SO_PEERPIDFD 0x404B + +#define SCM_TS_OPT_ID 0x404C + +#define SO_RCVPRIORITY 0x404D + +#define SO_DEVMEM_LINEAR 0x404E +#define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR +#define SO_DEVMEM_DMABUF 0x404F +#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF +#define SO_DEVMEM_DONTNEED 0x4050 + +#define SO_PASSRIGHTS 0x4051 + +#define SO_INQ 0x4052 +#define SCM_INQ SO_INQ + +#define SO_RIGHTS_NOTRUNC 0x4053 + +#if !defined(__KERNEL__) + +#if __BITS_PER_LONG == 64 +#define SO_TIMESTAMP SO_TIMESTAMP_OLD +#define SO_TIMESTAMPNS SO_TIMESTAMPNS_OLD +#define SO_TIMESTAMPING SO_TIMESTAMPING_OLD +#define SO_RCVTIMEO SO_RCVTIMEO_OLD +#define SO_SNDTIMEO SO_SNDTIMEO_OLD +#else +#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW) +#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW) +#define SO_TIMESTAMPING (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPING_OLD : SO_TIMESTAMPING_NEW) + +#define SO_RCVTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_RCVTIMEO_OLD : SO_RCVTIMEO_NEW) +#define SO_SNDTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_SNDTIMEO_OLD : SO_SNDTIMEO_NEW) +#endif + +#define SCM_TIMESTAMP SO_TIMESTAMP +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS +#define SCM_TIMESTAMPING SO_TIMESTAMPING + +#endif + +#endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/stat.h b/arch/parisc/include/uapi/asm/stat.h new file mode 100644 index 000000000..b5bbf6704 --- /dev/null +++ b/arch/parisc/include/uapi/asm/stat.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_STAT_H +#define _PARISC_STAT_H + +#include <linux/types.h> + +struct stat { + unsigned int st_dev; /* dev_t is 32 bits on parisc */ + unsigned int st_ino; /* 32 bits */ + unsigned short st_mode; /* 16 bits */ + unsigned short st_nlink; /* 16 bits */ + unsigned short st_reserved1; /* old st_uid */ + unsigned short st_reserved2; /* old st_gid */ + unsigned int st_rdev; + signed int st_size; + signed int st_atime; + unsigned int st_atime_nsec; + signed int st_mtime; + unsigned int st_mtime_nsec; + signed int st_ctime; + unsigned int st_ctime_nsec; + int st_blksize; + int st_blocks; + unsigned int __unused1; /* ACL stuff */ + unsigned int __unused2; /* network */ + unsigned int __unused3; /* network */ + unsigned int __unused4; /* cnodes */ + unsigned short __unused5; /* netsite */ + short st_fstype; + unsigned int st_realdev; + unsigned short st_basemode; + unsigned short st_spareshort; + unsigned int st_uid; + unsigned int st_gid; + unsigned int st_spare4[3]; +}; + +#define STAT_HAVE_NSEC + +/* This is the struct that 32-bit userspace applications are expecting. + * How 64-bit apps are going to be compiled, I have no idea. But at least + * this way, we don't have a wrapper in the kernel. + */ +struct stat64 { + unsigned long long st_dev; + unsigned int __pad1; + + unsigned int __st_ino; /* Not actually filled in */ + unsigned int st_mode; + unsigned int st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned long long st_rdev; + unsigned int __pad2; + signed long long st_size; + signed int st_blksize; + + signed long long st_blocks; + signed int st_atime; + unsigned int st_atime_nsec; + signed int st_mtime; + unsigned int st_mtime_nsec; + signed int st_ctime; + unsigned int st_ctime_nsec; + unsigned long long st_ino; +}; + +#endif diff --git a/arch/parisc/include/uapi/asm/statfs.h b/arch/parisc/include/uapi/asm/statfs.h new file mode 100644 index 000000000..e5de020c2 --- /dev/null +++ b/arch/parisc/include/uapi/asm/statfs.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _PARISC_STATFS_H +#define _PARISC_STATFS_H + +#define __statfs_word long +#include <asm-generic/statfs.h> + +#endif diff --git a/arch/parisc/include/uapi/asm/termbits.h b/arch/parisc/include/uapi/asm/termbits.h new file mode 100644 index 000000000..3a8938d26 --- /dev/null +++ b/arch/parisc/include/uapi/asm/termbits.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ARCH_PARISC_TERMBITS_H__ +#define __ARCH_PARISC_TERMBITS_H__ + +#include <asm-generic/termbits-common.h> + +typedef unsigned int tcflag_t; + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ +}; + +struct termios2 { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +struct ktermios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IUCLC 0x0200 +#define IXON 0x0400 +#define IXOFF 0x1000 +#define IMAXBEL 0x4000 +#define IUTF8 0x8000 + +/* c_oflag bits */ +#define OLCUC 0x00002 +#define ONLCR 0x00004 +#define NLDLY 0x00100 +#define NL0 0x00000 +#define NL1 0x00100 +#define CRDLY 0x00600 +#define CR0 0x00000 +#define CR1 0x00200 +#define CR2 0x00400 +#define CR3 0x00600 +#define TABDLY 0x01800 +#define TAB0 0x00000 +#define TAB1 0x00800 +#define TAB2 0x01000 +#define TAB3 0x01800 +#define XTABS 0x01800 +#define BSDLY 0x02000 +#define BS0 0x00000 +#define BS1 0x02000 +#define VTDLY 0x04000 +#define VT0 0x00000 +#define VT1 0x04000 +#define FFDLY 0x08000 +#define FF0 0x00000 +#define FF1 0x08000 + +/* c_cflag bit meaning */ +#define CBAUD 0x0000100f +#define CSIZE 0x00000030 +#define CS5 0x00000000 +#define CS6 0x00000010 +#define CS7 0x00000020 +#define CS8 0x00000030 +#define CSTOPB 0x00000040 +#define CREAD 0x00000080 +#define PARENB 0x00000100 +#define PARODD 0x00000200 +#define HUPCL 0x00000400 +#define CLOCAL 0x00000800 +#define CBAUDEX 0x00001000 +#define BOTHER 0x00001000 +#define B57600 0x00001001 +#define B115200 0x00001002 +#define B230400 0x00001003 +#define B460800 0x00001004 +#define B500000 0x00001005 +#define B576000 0x00001006 +#define B921600 0x00001007 +#define B1000000 0x00001008 +#define B1152000 0x00001009 +#define B1500000 0x0000100a +#define B2000000 0x0000100b +#define B2500000 0x0000100c +#define B3000000 0x0000100d +#define B3500000 0x0000100e +#define B4000000 0x0000100f +#define CIBAUD 0x100f0000 /* input baud rate */ + +/* c_lflag bits */ +#define ISIG 0x00001 +#define ICANON 0x00002 +#define XCASE 0x00004 +#define ECHO 0x00008 +#define ECHOE 0x00010 +#define ECHOK 0x00020 +#define ECHONL 0x00040 +#define NOFLSH 0x00080 +#define TOSTOP 0x00100 +#define ECHOCTL 0x00200 +#define ECHOPRT 0x00400 +#define ECHOKE 0x00800 +#define FLUSHO 0x01000 +#define PENDIN 0x04000 +#define IEXTEN 0x08000 +#define EXTPROC 0x10000 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#endif diff --git a/arch/parisc/include/uapi/asm/unistd.h b/arch/parisc/include/uapi/asm/unistd.h new file mode 100644 index 000000000..98dc95365 --- /dev/null +++ b/arch/parisc/include/uapi/asm/unistd.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_PARISC_UNISTD_H_ +#define _UAPI_ASM_PARISC_UNISTD_H_ + +#ifdef __LP64__ +#include <asm/unistd_64.h> +#else +#include <asm/unistd_32.h> +#endif + +#define LINUX_GATEWAY_ADDR 0x100 + +#endif /* _UAPI_ASM_PARISC_UNISTD_H_ */ diff --git a/arch/parisc/install.sh b/arch/parisc/install.sh new file mode 100755 index 000000000..664c2d77f --- /dev/null +++ b/arch/parisc/install.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# +# 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) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) + +set -e + +if [ "$(basename $2)" = "vmlinuz" ]; then +# Compressed install + echo "Installing compressed kernel" + base=vmlinuz +else +# Normal install + echo "Installing normal kernel" + base=vmlinux +fi + +if [ -f $4/$base-$1 ]; then + mv $4/$base-$1 $4/$base-$1.old +fi +cat $2 > $4/$base-$1 + +# Install system map file +if [ -f $4/System.map-$1 ]; then + mv $4/System.map-$1 $4/System.map-$1.old +fi +cp $3 $4/System.map-$1 + 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..49f937c2a --- /dev/null +++ b/arch/parisc/kernel/Makefile @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for arch/parisc/kernel +# + +always-$(KBUILD_BUILTIN) := vmlinux.lds + +obj-y := head.o cache.o pacache.o setup.o pdt.o traps.o time.o irq.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 + +CFLAGS_REMOVE_sys_parisc.o = -Wmissing-prototypes -Wmissing-declarations +CFLAGS_REMOVE_sys_parisc32.o = -Wmissing-prototypes -Wmissing-declarations + +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_PERF_EVENTS) += perf_event.o perf_regs.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 +ifdef CONFIG_64BIT +obj-y += vdso64/ +obj-$(CONFIG_COMPAT) += vdso32/ +else +obj-y += vdso32/ +endif 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..3de4b5933 --- /dev/null +++ b/arch/parisc/kernel/asm-offsets.c @@ -0,0 +1,290 @@ +// 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> + */ +#define COMPILE_OFFSETS + +#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); + DEFINE(TIF_32BIT_PA_BIT, 31-TIF_32BIT); + + 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); +#elif !defined(CONFIG_64BIT) + DEFINE(HUGEPAGE_SIZE, 4*1024*1024); +#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..375cd73b5 --- /dev/null +++ b/arch/parisc/kernel/audit.c @@ -0,0 +1,83 @@ +// 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) +{ + 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: +#ifdef CONFIG_COMPAT + if (abi == AUDIT_ARCH_PARISC) + return AUDITSC_COMPAT; +#endif + 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..98200b6b5 --- /dev/null +++ b/arch/parisc/kernel/cache.c @@ -0,0 +1,979 @@ +/* + * 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 <linux/syscalls.h> +#include <linux/vmalloc.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> +#include <asm/cachectl.h> + +#define PTR_PAGE_ALIGN_DOWN(addr) PTR_ALIGN_DOWN(addr, PAGE_SIZE) + +/* + * When nonzero, use _PAGE_ACCESSED bit to try to reduce the number + * of page flushes done flush_cache_page_if_present. There are some + * pros and cons in using this option. It may increase the risk of + * random segmentation faults. + */ +#define CONFIG_FLUSH_PAGE_ACCESSED 0 + +int split_tlb __ro_after_init; +int dcache_stride __ro_after_init; +int icache_stride __ro_after_init; +EXPORT_SYMBOL(dcache_stride); + +/* Internal implementation in arch/parisc/kernel/pacache.S */ +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); +void flush_data_cache_local(void *); /* flushes local data-cache only */ +void flush_instruction_cache_local(void); /* flushes local code-cache only */ + +static void flush_kernel_dcache_page_addr(const void *addr); + +/* 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 +struct pdc_btlb_info btlb_info; +#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 folio *folio; + unsigned int nr; + + /* 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; + + folio = page_folio(pfn_to_page(pfn)); + pfn = folio_pfn(folio); + nr = folio_nr_pages(folio); + if (folio_flush_mapping(folio) && + test_bit(PG_dcache_dirty, &folio->flags.f)) { + while (nr--) + flush_kernel_dcache_page_addr(pfn_va(pfn + nr)); + clear_bit(PG_dcache_dirty, &folio->flags.f); + } else if (parisc_requires_coherency()) + while (nr--) + flush_kernel_dcache_page_addr(pfn_va(pfn + nr)); +} + +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 + + /* stride needs to be non-zero, otherwise cache flushes will not work */ + WARN_ON(cache_info.dc_size && dcache_stride == 0); + WARN_ON(cache_info.ic_size && icache_stride == 0); + + 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; + + /* + * 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(vma, vmaddr); + + 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_kernel_dcache_page_addr(const void *addr) +{ + unsigned long vaddr = (unsigned long)addr; + unsigned long flags; + + /* Purge TLB entry to remove translation on all CPUs */ + purge_tlb_start(flags); + pdtlb(SR_KERNEL, addr); + purge_tlb_end(flags); + + /* Use tmpalias flush to prevent data cache move-in */ + preempt_disable(); + flush_dcache_page_asm(__pa(vaddr), vaddr); + preempt_enable(); +} + +static void flush_kernel_icache_page_addr(const void *addr) +{ + unsigned long vaddr = (unsigned long)addr; + unsigned long flags; + + /* Purge TLB entry to remove translation on all CPUs */ + purge_tlb_start(flags); + pdtlb(SR_KERNEL, addr); + purge_tlb_end(flags); + + /* Use tmpalias flush to prevent instruction cache move-in */ + preempt_disable(); + flush_icache_page_asm(__pa(vaddr), vaddr); + preempt_enable(); +} + +void kunmap_flush_on_unmap(const void *addr) +{ + flush_kernel_dcache_page_addr(addr); +} +EXPORT_SYMBOL(kunmap_flush_on_unmap); + +void flush_icache_pages(struct vm_area_struct *vma, struct page *page, + unsigned int nr) +{ + void *kaddr = page_address(page); + + for (;;) { + flush_kernel_dcache_page_addr(kaddr); + flush_kernel_icache_page_addr(kaddr); + if (--nr == 0) + break; + kaddr += PAGE_SIZE; + } +} + +/* + * Walk page directory for MM to find PTEP pointer for address ADDR. + */ +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_cache_flush(pte_t pte) +{ + return (pte_val(pte) & (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_NO_CACHE)) + == (_PAGE_PRESENT | _PAGE_ACCESSED); +} + +/* + * Return user physical address. Returns 0 if page is not present. + */ +static inline unsigned long get_upa(struct mm_struct *mm, unsigned long addr) +{ + unsigned long flags, space, pgd, prot, pa; +#ifdef CONFIG_TLB_PTLOCK + unsigned long pgd_lock; +#endif + + /* Save context */ + local_irq_save(flags); + prot = mfctl(8); + space = mfsp(SR_USER); + pgd = mfctl(25); +#ifdef CONFIG_TLB_PTLOCK + pgd_lock = mfctl(28); +#endif + + /* Set context for lpa_user */ + switch_mm_irqs_off(NULL, mm, NULL); + pa = lpa_user(addr); + + /* Restore previous context */ +#ifdef CONFIG_TLB_PTLOCK + mtctl(pgd_lock, 28); +#endif + mtctl(pgd, 25); + mtsp(space, SR_USER); + mtctl(prot, 8); + local_irq_restore(flags); + + return pa; +} + +void flush_dcache_folio(struct folio *folio) +{ + struct address_space *mapping = folio_flush_mapping(folio); + struct vm_area_struct *vma; + unsigned long addr, old_addr = 0; + void *kaddr; + unsigned long count = 0; + unsigned long i, nr, flags; + pgoff_t pgoff; + + if (mapping && !mapping_mapped(mapping)) { + set_bit(PG_dcache_dirty, &folio->flags.f); + return; + } + + nr = folio_nr_pages(folio); + kaddr = folio_address(folio); + for (i = 0; i < nr; i++) + flush_kernel_dcache_page_addr(kaddr + i * PAGE_SIZE); + + if (!mapping) + return; + + pgoff = folio->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); + mapping_rmap_tree_foreach(vma, mapping, pgoff, pgoff + nr - 1) { + unsigned long offset = pgoff - vma->vm_pgoff; + unsigned long pfn = folio_pfn(folio); + + addr = vma->vm_start; + nr = folio_nr_pages(folio); + if (offset > -nr) { + pfn -= offset; + nr += offset; + } else { + addr += offset * PAGE_SIZE; + } + if (addr + nr * PAGE_SIZE > vma->vm_end) + nr = (vma->vm_end - addr) / PAGE_SIZE; + + if (old_addr == 0 || (old_addr & (SHM_COLOUR - 1)) + != (addr & (SHM_COLOUR - 1))) { + for (i = 0; i < nr; i++) + __flush_cache_page(vma, + addr + i * PAGE_SIZE, + (pfn + i) * PAGE_SIZE); + /* + * Software is allowed to have any number + * of private mappings to a page. + */ + if (!(vma->vm_flags & VM_SHARED)) + continue; + if (old_addr) + pr_err("INEQUIVALENT ALIASES 0x%lx and 0x%lx in file %pD\n", + old_addr, addr, vma->vm_file); + if (nr == folio_nr_pages(folio)) + old_addr = addr; + } + WARN_ON(++count == 4096); + } + flush_dcache_mmap_unlock_irqrestore(mapping, flags); +} +EXPORT_SYMBOL(flush_dcache_folio); + +/* 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: + parisc_tlb_flush_threshold = max(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); + +static void flush_cache_page_if_present(struct vm_area_struct *vma, + unsigned long vmaddr) +{ +#if CONFIG_FLUSH_PAGE_ACCESSED + bool needs_flush = false; + pte_t *ptep, pte; + + ptep = get_ptep(vma->vm_mm, vmaddr); + if (ptep) { + pte = ptep_get(ptep); + needs_flush = pte_needs_cache_flush(pte); + pte_unmap(ptep); + } + if (needs_flush) + __flush_cache_page(vma, vmaddr, PFN_PHYS(pte_pfn(pte))); +#else + struct mm_struct *mm = vma->vm_mm; + unsigned long physaddr = get_upa(mm, vmaddr); + + if (physaddr) + __flush_cache_page(vma, vmaddr, PAGE_ALIGN_DOWN(physaddr)); +#endif +} + +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(vma, vaddr, PFN_PHYS(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(vma, user_vaddr, PFN_PHYS(page_to_pfn(page))); + memcpy(dst, src, len); + flush_kernel_dcache_page_addr(PTR_PAGE_ALIGN_DOWN(dst)); +} + +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(vma, user_vaddr, PFN_PHYS(page_to_pfn(page))); + memcpy(dst, src, len); + flush_kernel_dcache_page_addr(PTR_PAGE_ALIGN_DOWN(src)); +} + +/* __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; + + for (addr = start; addr < end; addr += PAGE_SIZE) + flush_cache_page_if_present(vma, addr); +} + +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); + if (vma->vm_flags & VM_EXEC) + flush_cache_all(); + else + flush_data_cache(); + return; + } + + flush_cache_pages(vma, start & PAGE_MASK, end); +} + +void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn) +{ + __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; + + __flush_cache_page(vma, vmaddr, PFN_PHYS(page_to_pfn(page))); +} + +bool ptep_clear_flush_young(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ + pte_t pte = ptep_get(ptep); + + if (!pte_young(pte)) + return false; + set_pte(ptep, pte_mkold(pte)); +#if CONFIG_FLUSH_PAGE_ACCESSED + __flush_cache_page(vma, addr, PFN_PHYS(pte_pfn(pte))); +#endif + return true; +} + +/* + * After a PTE is cleared, we have no way to flush the cache for + * the physical page. On PA8800 and PA8900 processors, these lines + * can cause random cache corruption. Thus, we must flush the cache + * as well as the TLB when clearing a PTE that's valid. + */ +pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep) +{ + struct mm_struct *mm = (vma)->vm_mm; + pte_t pte = ptep_get_and_clear(mm, addr, ptep); + unsigned long pfn = pte_pfn(pte); + + if (pfn_valid(pfn)) + __flush_cache_page(vma, addr, PFN_PHYS(pfn)); + else if (pte_accessible(mm, pte)) + flush_tlb_page(vma, addr); + + return pte; +} + +/* + * The physical address for pages in the ioremap case can be obtained + * from the vm_struct struct. I wasn't able to successfully handle the + * vmalloc and vmap cases. We have an array of struct page pointers in + * the uninitialized vmalloc case but the flush failed using page_to_pfn. + */ +void flush_cache_vmap(unsigned long start, unsigned long end) +{ + unsigned long addr, physaddr; + struct vm_struct *vm; + + /* Prevent cache move-in */ + flush_tlb_kernel_range(start, end); + + if (end - start >= parisc_cache_flush_threshold) { + flush_cache_all(); + return; + } + + if (WARN_ON_ONCE(!is_vmalloc_addr((void *)start))) { + flush_cache_all(); + return; + } + + vm = find_vm_area((void *)start); + if (!vm) { + flush_cache_all(); + return; + } + + /* The physical addresses of IOREMAP regions are contiguous */ + if (vm->flags & VM_IOREMAP) { + physaddr = vm->phys_addr; + for (addr = start; addr < end; addr += PAGE_SIZE) { + preempt_disable(); + flush_dcache_page_asm(physaddr, start); + flush_icache_page_asm(physaddr, start); + preempt_enable(); + physaddr += PAGE_SIZE; + } + return; + } + + flush_cache_all(); +} +EXPORT_SYMBOL(flush_cache_vmap); + +/* + * The vm_struct has been retired and the page table is set up. The + * last page in the range is a guard page. Its physical address can't + * be determined using lpa, so there is no way to flush the range + * using flush_dcache_page_asm. + */ +void flush_cache_vunmap(unsigned long start, unsigned long end) +{ + /* Prevent cache move-in */ + flush_tlb_kernel_range(start, end); + flush_data_cache(); +} +EXPORT_SYMBOL(flush_cache_vunmap); + +/* + * On systems with PA8800/PA8900 processors, there is no way to flush + * a vmap range other than using the architected loop to flush the + * entire cache. The page directory is not set up, so we can't use + * fdc, etc. FDCE/FICE don't work to flush a portion of the cache. + * L2 is physically indexed but FDCE/FICE instructions in virtual + * mode output their virtual address on the core bus, not their + * real address. As a result, the L2 cache index formed from the + * virtual address will most likely not be the same as the L2 index + * formed from the real address. + */ +void flush_kernel_vmap_range(void *vaddr, int size) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size; + + flush_tlb_kernel_range(start, end); + + if (!static_branch_likely(&parisc_has_dcache)) + return; + + /* If interrupts are disabled, we can only do local flush */ + if (WARN_ON(IS_ENABLED(CONFIG_SMP) && arch_irqs_disabled())) { + flush_data_cache_local(NULL); + return; + } + + flush_data_cache(); +} +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(); + + flush_tlb_kernel_range(start, end); + + if (!static_branch_likely(&parisc_has_dcache)) + return; + + /* If interrupts are disabled, we can only do local flush */ + if (WARN_ON(IS_ENABLED(CONFIG_SMP) && arch_irqs_disabled())) { + flush_data_cache_local(NULL); + return; + } + + flush_data_cache(); +} +EXPORT_SYMBOL(invalidate_kernel_vmap_range); + + +SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes, + unsigned int, cache) +{ + unsigned long start, end; + ASM_EXCEPTIONTABLE_VAR(error); + + if (bytes == 0) + return 0; + if (!access_ok((void __user *) addr, bytes)) + return -EFAULT; + + end = addr + bytes; + + if (cache & DCACHE) { + start = addr; + __asm__ __volatile__ ( +#ifdef CONFIG_64BIT + "1: cmpb,*<<,n %0,%2,1b\n" +#else + "1: cmpb,<<,n %0,%2,1b\n" +#endif + " fdc,m %3(%4,%0)\n" + "2: sync\n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b, "%1") + : "+r" (start), "+r" (error) + : "r" (end), "r" (dcache_stride), "i" (SR_USER)); + } + + if (cache & ICACHE && error == 0) { + start = addr; + __asm__ __volatile__ ( +#ifdef CONFIG_64BIT + "1: cmpb,*<<,n %0,%2,1b\n" +#else + "1: cmpb,<<,n %0,%2,1b\n" +#endif + " fic,m %3(%4,%0)\n" + "2: sync\n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b, "%1") + : "+r" (start), "+r" (error) + : "r" (end), "r" (icache_stride), "i" (SR_USER)); + } + + return error; +} diff --git a/arch/parisc/kernel/compat_audit.c b/arch/parisc/kernel/compat_audit.c new file mode 100644 index 000000000..3ac53f1ab --- /dev/null +++ b/arch/parisc/kernel/compat_audit.c @@ -0,0 +1,28 @@ +// 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 +}; diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c new file mode 100644 index 000000000..b52ad704e --- /dev/null +++ b/arch/parisc/kernel/drivers.c @@ -0,0 +1,1108 @@ +// 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-2023 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; + +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(const 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 + * @padev: 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); +} + +static struct parisc_device * __init create_tree_node(char id, + struct device *parent) +{ + struct parisc_device *dev = kzalloc_obj(*dev); + 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)) { + put_device(&dev->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, const struct device_driver *drv) +{ + return match_device(to_parisc_driver(drv), to_parisc_device(dev)); +} + +static ssize_t make_modalias(const 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(const 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); + +const 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. + * @dev: the target device + * @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"); + + root = root_device_register("parisc"); + if (IS_ERR(root)) + panic("Could not register PA-RISC root device\n"); +} + +static __init void qemu_header(void) +{ + int num; + unsigned long *p; + char name_mpe[80]; + + 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", + boot_cpu_data.pdc.sys_model_name); + strcpy(name_mpe, boot_cpu_data.pdc.sys_model_name); + pdc_model_sysmodel(OS_ID_MPEXL, name_mpe); + pr_info("#define PARISC_MODEL_MPE \"%s\"\n\n", name_mpe); + + #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, 0x%lx\n\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); + #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; + + memset(&iodc_data, 0, sizeof(iodc_data)); + 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"); + + /* Prevent hung task messages when printing on serial console */ + cond_resched(); + + pr_info("#define HPA_%08lx_DESCRIPTION \"%s\"\n", + hpa, parisc_hardware_description(&dev->id)); + + mod_index = 0; + do { + /* initialize device path for old machines */ + memset(&mod_path, 0xff, sizeof(mod_path)); + get_node_path(dev->dev.parent, &mod_path.path); + mod_path.path.mod = dev->hw_path; + memset(&pdc_mod_info, 0, sizeof(pdc_mod_info)); + 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%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x }, ", + (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%02x }\n", (unsigned char)mod_path.path.mod); + 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("};\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..e04c5d806 --- /dev/null +++ b/arch/parisc/kernel/entry.S @@ -0,0 +1,2354 @@ +/* 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 <asm/spinlock_types.h> + +#include <linux/linkage.h> +#include <linux/pgtable.h> + +#ifdef CONFIG_64BIT + .level 2.0w +#else + .level 2.0 +#endif + +/* + * We need seven instructions after a TLB insert for it to take effect. + * The PA8800/PA8900 processors are an exception and need 12 instructions. + * The RFI changes both IAOQ_Back and IAOQ_Front, so it counts as one. + */ +#ifdef CONFIG_64BIT +#define NUM_PIPELINE_INSNS 12 +#else +#define NUM_PIPELINE_INSNS 7 +#endif + + /* Insert num nops */ + .macro insert_nops num + .rept \num + nop + .endr + .endm + + /* 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 \tmp1,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 if for user space. We use an ordered + store to ensure all prior accesses are performed prior to + releasing the lock. Note stw may not be executed, so we + provide one extra nop when CONFIG_TLB_PTLOCK is defined. */ + .macro ptl_unlock spc,tmp,tmp2 +#ifdef CONFIG_TLB_PTLOCK +98: get_ptl \tmp + ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, \tmp2 + or,COND(=) %r0,\spc,%r0 + stw,ma \tmp2,0(\tmp) +99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP) + insert_nops NUM_PIPELINE_INSNS - 4 +#else + insert_nops NUM_PIPELINE_INSNS - 1 +#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 */ + +#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 + + /* 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 */ + 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). While the kernel can't directly write + * user pages which have _PAGE_WRITE zero, it can read pages + * which have _PAGE_READ zero (PL <= PL1). Thus, the kernel + * exception fault handler doesn't trigger when reading pages + * that aren't user read accessible */ + 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 defined(CONFIG_64BIT) +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. + */ + bb,COND(>=),n %r8,PSW_W_BIT,intr_save2 + LDREG PT_IASQ0(%r29), %r16 + LDREG PT_IAOQ0(%r29), %r17 + /* adjust iasq0/iaoq0 */ + space_adjust %r16,%r17,%r1 + STREG %r16, PT_IASQ0(%r29) + STREG %r17, PT_IAOQ0(%r29) + + LDREG PT_IASQ1(%r29), %r16 + LDREG PT_IAOQ1(%r29), %r17 + /* adjust iasq1/iaoq1 */ + space_adjust %r16,%r17,%r1 + STREG %r16, PT_IASQ1(%r29) + STREG %r17, PT_IAOQ1(%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_unlock spc,t0,t1 + rfir + nop + +dtlb_check_alias_20w: + do_alias spc,t0,t1,va,pte,prot,dtlb_fault,20 + + idtlbt pte,prot + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + rfir + nop + +nadtlb_check_alias_20w: + do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,20 + + idtlbt pte,prot + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + rfir + nop + +dtlb_check_alias_11: + do_alias spc,t0,t1,va,pte,prot,dtlb_fault,11 + + idtlba pte,(va) + idtlbp prot,(va) + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + rfir + nop + +nadtlb_check_alias_11: + do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,11 + + idtlba pte,(va) + idtlbp prot,(va) + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + rfir + nop + +dtlb_check_alias_20: + do_alias spc,t0,t1,va,pte,prot,dtlb_fault,20 + + idtlbt pte,prot + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + rfir + nop + +nadtlb_check_alias_20: + do_alias spc,t0,t1,va,pte,prot,nadtlb_emulate,20 + + idtlbt pte,prot + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + 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_unlock spc,t0,t1 + rfir + nop + +naitlb_check_alias_20w: + do_alias spc,t0,t1,va,pte,prot,naitlb_fault,20 + + iitlbt pte,prot + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + 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_unlock spc,t0,t1 + 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) + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + 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_unlock spc,t0,t1 + rfir + nop + +naitlb_check_alias_20: + do_alias spc,t0,t1,va,pte,prot,naitlb_fault,20 + + iitlbt pte,prot + + insert_nops NUM_PIPELINE_INSNS - 1 + 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_unlock spc,t0,t1 + 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_unlock spc,t0,t1 + 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_unlock spc,t0,t1 + 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 */ + +#ifdef CONFIG_64BIT + extru,<> %r19,TIF_32BIT_PA_BIT,1,%r0 + depi -1,4,1,%r20 /* W bit */ +#endif + 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) + + /* 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..c35a34809 --- /dev/null +++ b/arch/parisc/kernel/firmware.c @@ -0,0 +1,1957 @@ +// 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 + +static unsigned long pdc_result[NUM_PDC_RESULT] __aligned(8); +static unsigned long pdc_result2[NUM_PDC_RESULT] __aligned(8); + +#ifdef CONFIG_64BIT +#define WIDE_FIRMWARE PDC_MODEL_OS64 +#define NARROW_FIRMWARE PDC_MODEL_OS32 + +/* Firmware needs to be initially set to narrow to determine the + * actual firmware width. */ +int parisc_narrow_firmware __ro_after_init = NARROW_FIRMWARE; +#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. + * @addr: 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); + if (ret < 0) + return; + 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 != NARROW_FIRMWARE) + 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. + * @chassis_info: The memory buffer address. + * @led_info: The size of 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. + * @state: state of the machine + * @data: value for that state + * + * 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 + * @disp: value to show on display + */ +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_rendezvous - Stop currently executing CPU and do not return. + */ +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) __acquires(&pdc_lock) +{ + spin_lock(&pdc_lock); +} + +/** + * pdc_cpu_rendezvous_unlock - Unlock PDC after reaching rendezvous state + */ +void pdc_cpu_rendezvous_unlock(void) __releases(&pdc_lock) +{ + spin_unlock(&pdc_lock); +} + +/** + * pdc_pat_get_PDC_entrypoint - Get PDC entry point for current CPU + * @pdc_entry: pointer to where the PDC entry point should be stored + */ +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 + * @warn: The warning value to be shown + */ +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); + memcpy(pdc_result2, mod_path, sizeof(*mod_path)); + 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. + * @os_id: The operating system ID asked for (an OS_ID_* value) + * @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(unsigned int os_id, 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, __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. + * @versions: 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; +} + +/** + * 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; + + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + + 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; +} + +int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned long len, + unsigned long entry_info, unsigned long slot) +{ + int retval; + unsigned long flags; + + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INSERT, (unsigned long) (vpage >> 32), + (unsigned long) vpage, physpage, len, entry_info, slot); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; +} + +int pdc_btlb_purge_all(void) +{ + int retval; + unsigned long flags; + + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_PURGE_ALL); + spin_unlock_irqrestore(&pdc_lock, flags); + 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; + + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + + 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; +} + +/** + * 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 + * @cfg_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 + * @cfg_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 do_call64) +{ + int retval = 0; + unsigned long irqflags; + + spin_lock_irqsave(&pdc_lock, irqflags); + if (IS_ENABLED(CONFIG_64BIT) && do_call64) { +#ifdef CONFIG_64BIT + retval = real64_call(func, flags, inptr, outputr, glob_cfg); +#else + WARN_ON(1); +#endif + } else { + 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. + * @actual_len: 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_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_pd_get_platform_counter - Retrieve address of free-running 64-bit counter. + * @addr: The address of the 64-bit counter. + * @freq: The frequency of the counter, or -1 if unknown. + * @unique: Although monotonic growing, may it return the same number twice? + * + */ +int pdc_pat_pd_get_platform_counter(uint64_t **addr, + unsigned long *freq, unsigned long *unique) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_PAT_PD, PDC_PAT_PD_GET_PLATFORM_COUNTER, + __pa(pdc_result)); + if (retval == PDC_OK) { + *addr = (uint64_t *)pdc_result[0]; + *freq = pdc_result[1]; + *unique = 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. + * @val: 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_pdt_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..10fd5b3e6 --- /dev/null +++ b/arch/parisc/kernel/ftrace.c @@ -0,0 +1,254 @@ +// 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/text-patching.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; + +asmlinkage 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 +} + +#if defined(CONFIG_DYNAMIC_FTRACE) && defined(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_disable(&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; + + if (unlikely(kprobe_ftrace_disabled)) + return; + + 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..e84d7d6e1 --- /dev/null +++ b/arch/parisc/kernel/head.S @@ -0,0 +1,449 @@ +/* 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 + .import __end,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: + .align 4 + +$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(_end),%r1 + SHRREG %r1,PAGE_SHIFT,%r1 /* %r1 is PFN count for _end symbol */ + cmpb,<<,n %r11,%r1,1f + copy %r1,%r11 /* %r1 PFN count smaller than %r11 */ +1: 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 + +#define MEM_PDC_LO 0x388 +#define MEM_PDC_HI 0x35C +#ifdef CONFIG_64BIT + /* Get PDCE_PROC for monarch CPU. */ + 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 + +#ifndef CONFIG_64BIT + /* clear all BTLBs */ + ldi PDC_BLOCK_TLB,%arg0 + load32 PA(stext_pdc_btlb_ret), %rp + ldw MEM_PDC_LO(%r0),%r3 + bv (%r3) + ldi PDC_BTLB_PURGE_ALL,%arg1 +stext_pdc_btlb_ret: +#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..103f58dac --- /dev/null +++ b/arch/parisc/kernel/inventory.c @@ -0,0 +1,687 @@ +// 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_obj(*pa_pdc_cell); + 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; + } + +#ifdef DEBUG_PAT + pr_debug("PAT INDEX: %lu: cba 0x%lx, " + "mod_info 0x%lx, mod_location 0x%lx, " + "mod: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx " + "0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + mod_index + 1, pa_pdc_cell->cba, + pa_pdc_cell->mod_info, pa_pdc_cell->mod_location, + pa_pdc_cell->mod[0], pa_pdc_cell->mod[1], pa_pdc_cell->mod[2], + pa_pdc_cell->mod[3], pa_pdc_cell->mod[4], pa_pdc_cell->mod[5], + pa_pdc_cell->mod[6], pa_pdc_cell->mod[7], pa_pdc_cell->mod[8], + pa_pdc_cell->mod[9], pa_pdc_cell->mod[10], pa_pdc_cell->mod[11]); +#endif + + 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_objs(*dev->addr, num_addrs); + 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..dff66be65 --- /dev/null +++ b/arch/parisc/kernel/irq.c @@ -0,0 +1,579 @@ +// 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 + +#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() */ +asmlinkage 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_rcu(); + + 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_rcu(); + 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..ea51f15bf --- /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/text-patching.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..db57345a9 --- /dev/null +++ b/arch/parisc/kernel/kexec.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/kexec.h> +#include <linux/delay.h> +#include <linux/reboot.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..3fc82130b --- /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); + + kexec_dprintk("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; + + kexec_dprintk("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; + + kexec_dprintk("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..fee81f877 --- /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/text-patching.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..9255adba6 --- /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/text-patching.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..4e5d991b2 --- /dev/null +++ b/arch/parisc/kernel/module.c @@ -0,0 +1,961 @@ +// 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->mem[MOD_INIT_TEXT].base; + * else + * val -= (uint32_t)me->mem[MOD_TEXT].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/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 + +#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)); +} + +#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; + struct module_memory *mod_mem; + + 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; + } + + mod_mem = &me->mem[MOD_TEXT]; + /* align things a bit */ + mod_mem->size = ALIGN(mod_mem->size, 16); + me->arch.got_offset = mod_mem->size; + mod_mem->size += gots * sizeof(struct got_entry); + + mod_mem->size = ALIGN(mod_mem->size, 16); + me->arch.fdesc_offset = mod_mem->size; + mod_mem->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->mem[MOD_TEXT].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->mem[MOD_TEXT].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->mem[MOD_TEXT].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 (within_module(val, me)) { + /* 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 (within_module(val + addend, me)) { + *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->mem[MOD_TEXT].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 __maybe_unused = -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->mem[MOD_TEXT].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/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..509146a52 --- /dev/null +++ b/arch/parisc/kernel/parisc_ksyms.c @@ -0,0 +1,128 @@ +// 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/libgcc.h> + +#include <linux/string.h> +EXPORT_SYMBOL(memset); + +#include <linux/atomic.h> +EXPORT_SYMBOL(__xchg8); +EXPORT_SYMBOL(__xchg32); +EXPORT_SYMBOL(__cmpxchg_u8); +EXPORT_SYMBOL(__cmpxchg_u16); +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> + +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); + +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..35dd764b8 --- /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/text-patching.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..bf9f192c8 --- /dev/null +++ b/arch/parisc/kernel/pci-dma.c @@ -0,0 +1,464 @@ +// 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; + +unsigned long pcxl_dma_start __ro_after_init; /* pcxl dma mapping area start */ +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_kernel(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); \ +} + +static 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("bus/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; + + 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..b8007c740 --- /dev/null +++ b/arch/parisc/kernel/pci.c @@ -0,0 +1,271 @@ +/* + * 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/align.h> +#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, + const struct resource *empty_res, + resource_size_t size, + resource_size_t alignment) +{ + struct pci_dev *dev = data; + resource_size_t 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; + if (align > alignment) + start = ALIGN(start, align); + else + start = pci_align_resource(dev, res, empty_res, size, alignment); + + 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..d477d0177 --- /dev/null +++ b/arch/parisc/kernel/pdc_chassis.c @@ -0,0 +1,290 @@ +// 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> +#include <asm/led.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 + * + * 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. + * @this: unused + * @event: unused + * @ptr: unused + * + * 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, +}; + + +/** + * pdc_chassis_reboot_event() - Called by the reboot handler. + * @this: unused + * @event: unused + * @ptr: unused + * + * 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 + * @message: Type of message, one of PDC_CHASSIS_DIRECT_* values. + * + * 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) */ + + /* if system has LCD display, update current string */ + if (retval != -1 && IS_ENABLED(CONFIG_CHASSIS_LCD_LED)) + lcd_print(NULL); + +#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..b70b67adb --- /dev/null +++ b/arch/parisc/kernel/pdt.c @@ -0,0 +1,363 @@ +// 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/proc_fs.h> +#include <linux/initrd.h> +#include <linux/pgtable.h> +#include <linux/mm.h> + +#include <asm/pdc.h> +#include <asm/pdcpat.h> +#include <asm/sections.h> +#include <asm/pgtable.h> + +enum pdt_access_type { + PDT_NONE, + PDT_PDC, + PDT_PAT_NEW, + PDT_PAT_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 + +#ifdef CONFIG_PROC_FS +/* 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); +} +#endif + +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(addr >> PAGE_SHIFT); + } +} + + +/* + * 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"); + + return PTR_ERR_OR_ZERO(kpdtd_task); +} + +late_initcall(pdt_initcall); diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c new file mode 100644 index 000000000..5e10f98ce --- /dev/null +++ b/arch/parisc/kernel/perf.c @@ -0,0 +1,837 @@ +// 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); +static 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 __maybe_unused; + 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 = { + .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 = { + .minor = MISC_DYNAMIC_MINOR, + .name = PA_PERF_DEV, + .fops = &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_event.c b/arch/parisc/kernel/perf_event.c new file mode 100644 index 000000000..f90b83886 --- /dev/null +++ b/arch/parisc/kernel/perf_event.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Performance event support for parisc + * + * Copyright (C) 2025 by Helge Deller <deller@gmx.de> + */ + +#include <linux/kernel.h> +#include <linux/perf_event.h> +#include <asm/unwind.h> + +void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, + struct pt_regs *regs) +{ + + struct unwind_frame_info info; + + unwind_frame_init_task(&info, current, NULL); + while (1) { + if (unwind_once(&info) < 0 || info.ip == 0) + break; + + if (!__kernel_text_address(info.ip) || + perf_callchain_store(entry, info.ip)) + return; + } +} 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/perf_regs.c b/arch/parisc/kernel/perf_regs.c new file mode 100644 index 000000000..10a1a5f06 --- /dev/null +++ b/arch/parisc/kernel/perf_regs.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Copyright (C) 2025 by Helge Deller <deller@gmx.de> */ + +#include <linux/perf_event.h> +#include <linux/perf_regs.h> +#include <asm/ptrace.h> + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + switch (idx) { + case PERF_REG_PARISC_R0 ... PERF_REG_PARISC_R31: + return regs->gr[idx - PERF_REG_PARISC_R0]; + case PERF_REG_PARISC_SR0 ... PERF_REG_PARISC_SR7: + return regs->sr[idx - PERF_REG_PARISC_SR0]; + case PERF_REG_PARISC_IASQ0 ... PERF_REG_PARISC_IASQ1: + return regs->iasq[idx - PERF_REG_PARISC_IASQ0]; + case PERF_REG_PARISC_IAOQ0 ... PERF_REG_PARISC_IAOQ1: + return regs->iasq[idx - PERF_REG_PARISC_IAOQ0]; + case PERF_REG_PARISC_SAR: /* CR11 */ + return regs->sar; + case PERF_REG_PARISC_IIR: /* CR19 */ + return regs->iir; + case PERF_REG_PARISC_ISR: /* CR20 */ + return regs->isr; + case PERF_REG_PARISC_IOR: /* CR21 */ + return regs->ior; + case PERF_REG_PARISC_IPSW: /* CR22 */ + return regs->ipsw; + } + WARN_ON_ONCE((u32)idx >= PERF_REG_PARISC_MAX); + return 0; +} + +#define REG_RESERVED (~((1ULL << PERF_REG_PARISC_MAX) - 1)) + +int perf_reg_validate(u64 mask) +{ + if (!mask || mask & REG_RESERVED) + return -EINVAL; + + return 0; +} + +u64 perf_reg_abi(struct task_struct *task) +{ + if (!IS_ENABLED(CONFIG_64BIT)) + return PERF_SAMPLE_REGS_ABI_32; + + if (test_tsk_thread_flag(task, TIF_32BIT)) + return PERF_SAMPLE_REGS_ABI_32; + + return PERF_SAMPLE_REGS_ABI_64; +} + +void perf_get_regs_user(struct perf_regs *regs_user, + struct pt_regs *regs) +{ + regs_user->regs = task_pt_regs(current); + regs_user->abi = perf_reg_abi(current); +} diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c new file mode 100644 index 000000000..703644e5b --- /dev/null +++ b/arch/parisc/kernel/process.c @@ -0,0 +1,283 @@ +// 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); + + /* prevent interrupts during reboot */ + set_eiem(0); + + /* "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) ; + +} + +/* + * This routine is called from sys_reboot to actually turn off the + * machine + */ +void machine_power_off(void) +{ + /* 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 __noreturn arch_cpu_idle_dead(void) +{ +#ifdef CONFIG_HOTPLUG_CPU + idle_task_exit(); + + local_irq_disable(); + + /* Tell the core that this CPU is now safe to dispose of. */ + cpuhp_ap_report_dead(); + + /* 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) +{ + /* 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) +{ + u64 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; +} diff --git a/arch/parisc/kernel/processor.c b/arch/parisc/kernel/processor.c new file mode 100644 index 000000000..d09be22ba --- /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-2026 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/smp.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_percpu_prof - 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_obj(*pa_pdc_cell); + 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; + + 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) { + pr_info("model 0x%04lx 0x%04lx 0x%04lx 0x%04lx 0x%04lx " + "0x%04lx 0x%04lx 0x%04lx 0x%04lx 0x%04lx\n", + p[0], p[1], p[2], p[3], p[4], + p[5], p[6], p[7], p[8], p[9]); + + 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) { + pr_info("vers 0x%04lx\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) { + pr_info("CPUID vers %ld rev %ld (0x%04lx)\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(OS_ID_HPUX, boot_cpu_data.pdc.sys_model_name) == PDC_OK) + pr_info("HP-UX model name: %s\n", + boot_cpu_data.pdc.sys_model_name); + + serial_no[0] = 0; + if (pdc_model_sysmodel(OS_ID_MPEXL, serial_no) == PDC_OK && + serial_no[0]) + pr_info("MPE/iX model name: %s\n", serial_no); + + 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); + + btlb_init_per_cpu(); + + 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 */ + strscpy(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%04x\n" + "sversion\t: 0x%04x\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) +{ + reset_cpu_topology(); + 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..1d9e21070 --- /dev/null +++ b/arch/parisc/kernel/ptrace.c @@ -0,0 +1,791 @@ +// 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)) { + bool permit = ptrace_report_syscall_permit_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 (!permit) { + /* + * 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 (!seccomp_permit_syscall()) + 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); + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + ELF_NFPREG * sizeof(reg), -1); + return 0; +} + +#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); + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + ELF_NGREG * sizeof(reg), -1); + return 0; +} + +static const struct user_regset native_regsets[] = { + [REGSET_GENERAL] = { + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, + .size = sizeof(long), .align = sizeof(long), + .regset_get = gpr_get, .set = gpr_set + }, + [REGSET_FP] = { + USER_REGSET_NOTE_TYPE(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); + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + ELF_NGREG * sizeof(reg), -1); + return 0; +} + +/* + * These are the regset flavors matching the 32bit native set. + */ +static const struct user_regset compat_regsets[] = { + [REGSET_GENERAL] = { + USER_REGSET_NOTE_TYPE(PRSTATUS), .n = ELF_NGREG, + .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), + .regset_get = gpr32_get, .set = gpr32_set + }, + [REGSET_FP] = { + USER_REGSET_NOTE_TYPE(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..509d18b8e --- /dev/null +++ b/arch/parisc/kernel/real2.S @@ -0,0 +1,292 @@ +/* + * + * 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> + + .export real_stack + .export real64_stack + __PAGE_ALIGNED_BSS +real_stack: +real64_stack: + .block 8192 + +#define N_SAVED_REGS 9 + .section .bss +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..d3e17a7a8 --- /dev/null +++ b/arch/parisc/kernel/setup.c @@ -0,0 +1,311 @@ +// 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/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]; + +static void __init setup_cmdline(char **cmdline_p) +{ + extern unsigned int boot_args[]; + char *p; + + *cmdline_p = command_line; + + /* boot_args[0] is free-mem start, boot_args[1] is ptr to command line */ + if (boot_args[0] < 64) + return; /* return if called from hpux boot loader */ + + /* Collect stuff passed in from the boot loader */ + 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 + /* did palo pass us a ramdisk? */ + if (boot_args[2] != 0) { + 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); +} + +#ifdef CONFIG_PA11 +static 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: + default: + break; + } +} +#endif + +void __init setup_arch(char **cmdline_p) +{ + 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"); + +#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_PA11 + dma_ops_init(); +#endif + + clear_sched_clock_stable(); +} + +/* + * Display CPU info for all CPUs. + */ +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 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; +} + +static int __init parisc_init(void) +{ + u32 osid = (OS_ID_LINUX << 16); + + 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(); + return 0; +} +arch_initcall(parisc_init); + +void __init start_parisc(void) +{ + int ret, cpunum; + struct pdc_coproc_cfg coproc_cfg; + + /* + * Check if initial kernel page mapping is sufficient. + * Print warning if not, because we may access kernel functions and + * variables which can't be reached yet through the initial mappings. + * Note that the panic() and printk() functions are not functional + * yet, so we need to use direct iodc() firmware calls instead. + */ + const char warn1[] = "CRITICAL: Kernel may crash because " + "KERNEL_INITIAL_ORDER is too small.\n"; + if (__pa((unsigned long) &_end) >= KERNEL_INITIAL_SIZE) + pdc_iodc_print(warn1, sizeof(warn1) - 1); + + /* 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..64a62006b --- /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; +} + +asmlinkage 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_COMPAT + 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_COMPAT + 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_COMPAT + 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, long 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, + long in_syscall) +{ + struct rt_sigframe __user *frame; + unsigned long rp, usp; + unsigned long haddr, sigframe_size; + unsigned long start; + int err = 0; +#ifdef CONFIG_COMPAT + struct compat_rt_sigframe __user * compat_frame; +#endif + + usp = (regs->gr[30] & ~(0x01UL)); + sigframe_size = PARISC_RT_SIGFRAME_SIZE; +#ifdef CONFIG_COMPAT + 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_COMPAT + + 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_COMPAT + 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, long 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 = (u32 __user *) ((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(); +} + +asmlinkage 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..36be17e32 --- /dev/null +++ b/arch/parisc/kernel/smp.c @@ -0,0 +1,507 @@ +// 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 +arch_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) +{ + /* 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 */ + parisc_clockevent_init(); +} + + +/* + * 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) + *per_cpu_ptr(desc->kstat_irqs, cpuid) = (struct irqstat) { }; + } +#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) +{ + pr_info("SMP: bootstrap CPU ID is 0\n"); +} + + + +/* +** 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(); +} + +void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu) +{ + 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..1a676a9bf --- /dev/null +++ b/arch/parisc/kernel/sys_parisc.c @@ -0,0 +1,409 @@ +// 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; +} + + +#ifdef CONFIG_COMPAT +#define STACK_SIZE_DEFAULT (USER_WIDE_MODE \ + ? (1 << 30) /* 1 GB */ \ + : (CONFIG_STACK_MAX_DEFAULT_SIZE_MB*1024*1024)) +#else +#define STACK_SIZE_DEFAULT (1 << 30) +#endif + +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(). + */ +unsigned long mmap_upper_limit(const 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 = { + .length = len + }; + + 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.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.low_limit = mm->mmap_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, + vm_flags_t vm_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, vm_flags_t vm_flags) +{ + return arch_get_unmapped_area_common(filp, + addr, len, pgoff, flags, DOWN); +} + +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, FTRUNCATE_LFS); +} + +/* 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, FTRUNCATE_LFS); +} +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..826c8e51b --- /dev/null +++ b/arch/parisc/kernel/sys_parisc32.c @@ -0,0 +1,25 @@ +// 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; +} diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S new file mode 100644 index 000000000..e11c88c34 --- /dev/null +++ b/arch/parisc/kernel/syscall.S @@ -0,0 +1,1375 @@ +/* + * 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 <asm/spinlock_types.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 + + /* raise exception if spinlock content is not zero or + * __ARCH_SPIN_LOCK_UNLOCKED_VAL */ + .macro spinlock_check spin_val,tmpreg +#ifdef CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK + ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, \tmpreg + andcm,= \spin_val, \tmpreg, %r0 + .word SPINLOCK_BREAK_INSN +#endif + .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_COMPAT + ldil L%sys_call_table, %r1 + or,ev %r2,%r2,%r2 + ldil L%sys_call_table64, %r1 + ldo R%sys_call_table(%r1), %r19 + or,ev %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_COMPAT + 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,ev %r2,%r2,%r2 + ldil L%sys_call_table64, %r1 + ldo R%sys_call_table(%r1), %r19 + or,ev %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 + ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, %r21 + stw,ma %r21, 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 + ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, %r21 + stw,ma %r21, 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 + proberi (%r26), PRIV_USER, %r28 + comb,=,n %r28, %r0, lws_fault /* backwards, likely not taken */ + nop +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 + spinlock_check %r28, %r21 + 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 + proberi (%r28), PRIV_USER, %r1 + comb,=,n %r1, %r0, lws_fault /* backwards, likely not taken */ + nop +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 + spinlock_check %r28, %r21 + 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 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop + copy %r23, %r20 + depi_safe 0, 31, 2, %r20 + b atomic_xchg_start +2: stbys,e %r0, 0(%r20) + + /* 16-bit exchange */ +3: ldh 0(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop + copy %r23, %r20 + depi_safe 0, 31, 2, %r20 + b atomic_xchg_start +4: stbys,e %r0, 0(%r20) + + /* 32-bit exchange */ +5: ldw 0(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop + b atomic_xchg_start +6: stbys,e %r0, 0(%r23) + nop + nop + + /* 64-bit exchange */ +#ifdef CONFIG_64BIT +7: ldd 0(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop +8: stdby,e %r0, 0(%r23) +#else +7: ldw 0(%r24), %r20 +8: ldw 4(%r24), %r20 + proberi (%r24), PRIV_USER, %r20 + comb,=,n %r20, %r0, lws_fault /* backwards, likely not taken */ + nop + 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 + spinlock_check %r28, %r21 + 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 + spinlock_check %r28, %r21 + 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_COMPAT +#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 +#if defined(CONFIG_COMPAT) || !defined(CONFIG_64BIT) +#include <asm/syscall_table_32.h> /* 32-bit syscalls */ +#endif +END(sys_call_table) + +#ifdef CONFIG_64BIT +#undef __SYSCALL_WITH_COMPAT +#define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, native) + .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 __ARCH_SPIN_LOCK_UNLOCKED_VAL + .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..88adc4016 --- /dev/null +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -0,0 +1,473 @@ +# 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 compat_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 compat_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 32 _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_ni_syscall +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 compat_sys_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 +356 common cacheflush sys_cacheflush +# 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 +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal +463 common setxattrat sys_setxattrat +464 common getxattrat sys_getxattrat +465 common listxattrat sys_listxattrat +466 common removexattrat sys_removexattrat +467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr +470 common listns sys_listns +471 common rseq_slice_yield sys_rseq_slice_yield +472 common fchroot sys_fchroot diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c new file mode 100644 index 000000000..71c9d5426 --- /dev/null +++ b/arch/parisc/kernel/time.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common time service routines for parisc machines. + * based on arch/loongarch/kernel/time.c + * + * Copyright (C) 2024 Helge Deller <deller@gmx.de> + */ +#include <linux/clockchips.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/sched_clock.h> +#include <linux/spinlock.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> +#include <asm/processor.h> +#include <asm/pdcpat.h> + +static u64 cr16_clock_freq; +static unsigned long clocktick; + +int time_keeper_id; /* CPU used for timekeeping */ + +static DEFINE_PER_CPU(struct clock_event_device, parisc_clockevent_device); + +static void parisc_event_handler(struct clock_event_device *dev) +{ +} + +static int parisc_timer_next_event(unsigned long delta, struct clock_event_device *evt) +{ + unsigned long new_cr16; + + new_cr16 = mfctl(16) + delta; + mtctl(new_cr16, 16); + + return 0; +} + +irqreturn_t timer_interrupt(int irq, void *data) +{ + struct clock_event_device *cd; + int cpu = smp_processor_id(); + + cd = &per_cpu(parisc_clockevent_device, cpu); + + if (clockevent_state_periodic(cd)) + parisc_timer_next_event(clocktick, cd); + + if (clockevent_state_periodic(cd) || clockevent_state_oneshot(cd)) + cd->event_handler(cd); + + return IRQ_HANDLED; +} + +static int parisc_set_state_oneshot(struct clock_event_device *evt) +{ + parisc_timer_next_event(clocktick, evt); + + return 0; +} + +static int parisc_set_state_periodic(struct clock_event_device *evt) +{ + parisc_timer_next_event(clocktick, evt); + + return 0; +} + +static int parisc_set_state_shutdown(struct clock_event_device *evt) +{ + return 0; +} + +void parisc_clockevent_init(void) +{ + unsigned int cpu = smp_processor_id(); + unsigned long min_delta = 0x600; /* XXX */ + unsigned long max_delta = (1UL << (BITS_PER_LONG - 1)); + struct clock_event_device *cd; + + cd = &per_cpu(parisc_clockevent_device, cpu); + + cd->name = "cr16_clockevent"; + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_PERCPU; + + cd->irq = TIMER_IRQ; + cd->rating = 320; + cd->cpumask = cpumask_of(cpu); + cd->set_state_oneshot = parisc_set_state_oneshot; + cd->set_state_oneshot_stopped = parisc_set_state_shutdown; + cd->set_state_periodic = parisc_set_state_periodic; + cd->set_state_shutdown = parisc_set_state_shutdown; + cd->set_next_event = parisc_timer_next_event; + cd->event_handler = parisc_event_handler; + + clockevents_config_and_register(cd, cr16_clock_freq, min_delta, max_delta); +} + +static void parisc_find_64bit_counter(void) +{ +#ifdef CONFIG_64BIT + uint64_t *pclock; + unsigned long freq, unique; + int ret; + + ret = pdc_pat_pd_get_platform_counter(&pclock, &freq, &unique); + if (ret == PDC_OK) + pr_info("64-bit counter found at %px, freq: %lu, unique: %lu\n", + pclock, freq, unique); + else + pr_info("64-bit counter not found.\n"); +#endif +} + +unsigned long notrace 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); + +#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(); +} + +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 | + CLOCK_SOURCE_VALID_FOR_HRES, +}; + +/* + * timer interrupt and sched_clock() initialization + */ + +void __init time_init(void) +{ + cr16_clock_freq = 100 * PAGE0->mem_10msec; /* Hz */ + clocktick = cr16_clock_freq / HZ; + + /* register as sched_clock source */ + sched_clock_register(read_cr16_sched_clock, BITS_PER_LONG, cr16_clock_freq); + + parisc_clockevent_init(); + + /* check for free-running 64-bit platform counter */ + parisc_find_64bit_counter(); + + /* register at clocksource framework */ + clocksource_register_hz(&clocksource_cr16, cr16_clock_freq); +} 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..4c7c5df80 --- /dev/null +++ b/arch/parisc/kernel/traps.c @@ -0,0 +1,863 @@ +// 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 <linux/perf_event.h> + +#include <asm/assembly.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/traps.h> +#include <linux/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 "unaligned.h" + +#if defined(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK) +#include <asm/spinlock.h> +#endif + +#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 + +#ifdef CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK + if ((iir == SPINLOCK_BREAK_INSN) && !user_mode(regs)) { + die_if_kernel("Spinlock was trashed", regs, 1); + } +#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); +} + +static 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 | PRIV_USER; + 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(); + default_trap(code, 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); + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); + 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); +} + + +static void __init initialize_ivt(const void *iva) +{ + extern const u32 os_hpmc[]; + + int i; + u32 check = 0; + u32 *ivap; + 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); + + 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..fb64d9ce0 --- /dev/null +++ b/arch/parisc/kernel/unaligned.c @@ -0,0 +1,683 @@ +// 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 <linux/sysctl.h> +#include <linux/unaligned.h> +#include <linux/perf_event.h> +#include <asm/hardirq.h> +#include <asm/traps.h> +#include "unaligned.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 "0x%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; +int no_unaligned_warning __read_mostly; + +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, "%1") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1") + : "+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, "%1") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1") + : "+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; + unsigned long shift, temp1; + __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 %2,60,3,%3\n" /* shift=(ofs&7)*8 */ +" mtsp %5, %%sr1\n" +" depd %%r0,63,3,%2\n" +"1: ldd 0(%%sr1,%2),%0\n" +"2: ldd 8(%%sr1,%2),%4\n" +" subi 64,%3,%3\n" +" mtsar %3\n" +" shrpd %0,%4,%%sar,%0\n" +"3: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%1") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1") + : "+r" (val), "+r" (ret), "+r" (saddr), "=&r" (shift), "=&r" (temp1) + : "r" (regs->isr) ); +#else + __asm__ __volatile__ ( +" zdep %2,29,2,%3\n" /* shift=(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, "%1") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 4b, "%1") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 4b, "%1") + : "+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, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%0") + : "+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, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%0") + : "+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, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 5b, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 5b, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 5b, "%0") + : "+r" (ret) + : "r" (val), "r" (regs->ior), "r" (regs->isr) + : "r19", "r20", "r21", "r22", "r1" ); +#else + { + __asm__ __volatile__ ( +" mtsp %3, %%sr1\n" +" zdep %R1, 29, 2, %%r19\n" +" dep %%r0, 31, 2, %2\n" +" mtsar %%r19\n" +" zvdepi -2, 32, %%r19\n" +"1: ldw 0(%%sr1,%2),%%r20\n" +"2: ldw 8(%%sr1,%2),%%r21\n" +" vshd %1, %R1, %%r1\n" +" vshd %%r0, %1, %1\n" +" vshd %R1, %%r0, %R1\n" +" and %%r20, %%r19, %%r20\n" +" andcm %%r21, %%r19, %%r21\n" +" or %1, %%r20, %1\n" +" or %R1, %%r21, %R1\n" +"3: stw %1,0(%%sr1,%2)\n" +"4: stw %%r1,4(%%sr1,%2)\n" +"5: stw %R1,8(%%sr1,%2)\n" +"6: \n" + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 6b, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 6b, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 6b, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 6b, "%0") + ASM_EXCEPTIONTABLE_ENTRY_EFAULT(5b, 6b, "%0") + : "+r" (ret) + : "r" (val), "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); + perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, regs->ior); + + /* 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; + } else { + static DEFINE_RATELIMIT_STATE(kernel_ratelimit, 5 * HZ, 5); + if (!(current->thread.flags & PARISC_UAC_NOPRINT) && + !no_unaligned_warning && + __ratelimit(&kernel_ratelimit)) + pr_warn("Kernel: unaligned access to " RFMT " in %pS " + "(iir " RFMT ")\n", + regs->ior, (void *)regs->iaoq[0], regs->iir); + } + + /* 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/unaligned.h b/arch/parisc/kernel/unaligned.h new file mode 100644 index 000000000..c1aa4b12e --- /dev/null +++ b/arch/parisc/kernel/unaligned.h @@ -0,0 +1,3 @@ +struct pt_regs; +void handle_unaligned(struct pt_regs *regs); +int check_unaligned(struct pt_regs *regs); diff --git a/arch/parisc/kernel/unwind.c b/arch/parisc/kernel/unwind.c new file mode 100644 index 000000000..32103a270 --- /dev/null +++ b/arch/parisc/kernel/unwind.c @@ -0,0 +1,496 @@ +// 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> +#include <asm/ftrace.h> + +/* #define DEBUG 1 */ +#ifdef DEBUG +#define dbg(x...) pr_debug(x) +#else +#define dbg(x...) do { } while (0) +#endif + +#define KERNEL_START (KERNEL_BINARY_TEXT_START) + +#define ALIGNMENT_OK(ptr, type) (((ptr) & (sizeof(type) - 1)) == 0) + +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_obj(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 __maybe_unused, stop __maybe_unused; + 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 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 == (unsigned long)&ret_from_kernel_thread || + pc == (unsigned long)&syscall_exit) { + info->prev_sp = info->prev_ip = 0; + return 1; + } + + if (pc == (unsigned long)&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 == (unsigned long)&_switch_to_ret) { + info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE; + if (ALIGNMENT_OK(info->prev_sp, long)) + info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET); + else + info->prev_ip = info->prev_sp = 0; + return 1; + } + +#ifdef CONFIG_IRQSTACKS + if (pc == (unsigned long)&_call_on_stack && ALIGNMENT_OK(info->sp, long)) { + 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 && ALIGNMENT_OK(info->prev_sp, long)) + info->rp = *(unsigned long *)(info->prev_sp - rpoffset); + else + info->rp = 0; + 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_obj(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..54c185744 --- /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 -= get_random_u32_below(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 = kzalloc_objs(struct page *, pages + 1); + 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..4ee8d17da --- /dev/null +++ b/arch/parisc/kernel/vdso32/Makefile @@ -0,0 +1,66 @@ +# Include the generic Makefile to check the built vdso. +include $(srctree)/lib/vdso/Makefile.include + +KCOV_INSTRUMENT := n + +# Disable gcov profiling, ubsan and kasan for VDSO code +GCOV_PROFILE := n +UBSAN_SANITIZE := n +KASAN_SANITIZE := n +KCSAN_SANITIZE := n + +obj-vdso32 = note.o sigtramp.o restart_syscall.o +obj-cvdso32 = vdso32_generic.o + +# Build rules + +targets := $(obj-vdso32) $(obj-cvdso32) vdso32.so +obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32)) +obj-cvdso32 := $(addprefix $(obj)/, $(obj-cvdso32)) + +VDSO_CFLAGS_REMOVE := -pg $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_vdso32_generic.o = $(VDSO_CFLAGS_REMOVE) + +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 +targets += 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: $(obj)/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 -o $@ $< + +# Generate VDSO offsets using helper script +gen-vdsosym := $(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..4273baa26 --- /dev/null +++ b/arch/parisc/kernel/vdso32/vdso32.lds.S @@ -0,0 +1,114 @@ +/* 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; + __vdso_gettimeofday; + __vdso_clock_gettime; + __vdso_clock_gettime64; + local: *; + }; +} diff --git a/arch/parisc/kernel/vdso32/vdso32_generic.c b/arch/parisc/kernel/vdso32/vdso32_generic.c new file mode 100644 index 000000000..8d5bd59e8 --- /dev/null +++ b/arch/parisc/kernel/vdso32/vdso32_generic.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "asm/unistd.h" +#include <linux/types.h> +#include <uapi/asm/unistd_32.h> + +struct timezone; +struct old_timespec32; +struct __kernel_timespec; +struct __kernel_old_timeval; + +/* forward declarations */ +int __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz); +int __vdso_clock_gettime(clockid_t clock, struct old_timespec32 *ts); +int __vdso_clock_gettime64(clockid_t clock, struct __kernel_timespec *ts); + + +int __vdso_gettimeofday(struct __kernel_old_timeval *tv, + struct timezone *tz) +{ + return syscall2(__NR_gettimeofday, (long)tv, (long)tz); +} + +int __vdso_clock_gettime(clockid_t clock, struct old_timespec32 *ts) +{ + return syscall2(__NR_clock_gettime, (long)clock, (long)ts); +} + +int __vdso_clock_gettime64(clockid_t clock, struct __kernel_timespec *ts) +{ + return syscall2(__NR_clock_gettime64, (long)clock, (long)ts); +} 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..c63f40691 --- /dev/null +++ b/arch/parisc/kernel/vdso64/Makefile @@ -0,0 +1,65 @@ +# Include the generic Makefile to check the built vdso. +include $(srctree)/lib/vdso/Makefile.include + +KCOV_INSTRUMENT := n + +# Disable gcov profiling, ubsan and kasan for VDSO code +GCOV_PROFILE := n +UBSAN_SANITIZE := n +KASAN_SANITIZE := n +KCSAN_SANITIZE := n + +obj-vdso64 = note.o sigtramp.o restart_syscall.o +obj-cvdso64 = vdso64_generic.o + +# Build rules + +targets := $(obj-vdso64) $(obj-cvdso64) vdso64.so +obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64)) +obj-cvdso64 := $(addprefix $(obj)/, $(obj-cvdso64)) + +VDSO_CFLAGS_REMOVE := -pg $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_vdso64_generic.o = $(VDSO_CFLAGS_REMOVE) + +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 +targets += 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: $(obj)/vdso64.lds $(obj-vdso64) $(obj-cvdso64) $(VDSO_LIBGCC) FORCE + $(call if_changed,vdso64ld) + +# assembly rules for the .S files +$(obj-vdso64): %.o: %.S FORCE + $(call if_changed_dep,vdso64as) +$(obj-cvdso64): %.o: %.c FORCE + $(call if_changed_dep,vdso64cc) + +# 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 $@ $< +quiet_cmd_vdso64cc = VDSO64C $@ + cmd_vdso64cc = $(CC) $(c_flags) -c -o $@ $< + +# Generate VDSO offsets using helper script +gen-vdsosym := $(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..10f25e4e1 --- /dev/null +++ b/arch/parisc/kernel/vdso64/vdso64.lds.S @@ -0,0 +1,111 @@ +/* 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; + __vdso_gettimeofday; + __vdso_clock_gettime; + local: *; + }; +} diff --git a/arch/parisc/kernel/vdso64/vdso64_generic.c b/arch/parisc/kernel/vdso64/vdso64_generic.c new file mode 100644 index 000000000..fc6836a00 --- /dev/null +++ b/arch/parisc/kernel/vdso64/vdso64_generic.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "asm/unistd.h" +#include <linux/types.h> + +struct timezone; +struct __kernel_timespec; +struct __kernel_old_timeval; + +/* forward declarations */ +int __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz); +int __vdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts); + + +int __vdso_gettimeofday(struct __kernel_old_timeval *tv, + struct timezone *tz) +{ + return syscall2(__NR_gettimeofday, (long)tv, (long)tz); +} + +int __vdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts) +{ + return syscall2(__NR_clock_gettime, (long)clock, (long)ts); +} 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..0ca93d6d7 --- /dev/null +++ b/arch/parisc/kernel/vmlinux.lds.S @@ -0,0 +1,187 @@ +/* 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 + 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(PAGE_SIZE) + + /* 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 */ + . = ALIGN(PAGE_SIZE); + _edata = .; + + /* BSS */ + BSS_SECTION(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE) + + . = ALIGN(HUGEPAGE_SIZE); + _end = . ; + + STABS_DEBUG + MODINFO + 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 + } +} diff --git a/arch/parisc/lib/Makefile b/arch/parisc/lib/Makefile new file mode 100644 index 000000000..d5975d1fb --- /dev/null +++ b/arch/parisc/lib/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for parisc-specific library files +# + +lib-y := lusercopy.o bitops.o io.o memset.o memcpy.o \ + ucmpdi2.o delay.o + +obj-y := iomap.o diff --git a/arch/parisc/lib/bitops.c b/arch/parisc/lib/bitops.c new file mode 100644 index 000000000..9df810050 --- /dev/null +++ b/arch/parisc/lib/bitops.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * bitops.c: atomic operations which got too long to be inlined all over + * the place. + * + * Copyright 1999 Philipp Rumpf (prumpf@tux.org) + * Copyright 2000 Grant Grundler (grundler@cup.hp.com) + */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/atomic.h> + +#ifdef CONFIG_SMP +arch_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned = { + [0 ... (ATOMIC_HASH_SIZE-1)] = __ARCH_SPIN_LOCK_UNLOCKED +}; +#endif + +#ifdef CONFIG_64BIT +unsigned long notrace __xchg64(unsigned long x, volatile unsigned long *ptr) +{ + unsigned long temp, flags; + + _atomic_spin_lock_irqsave(ptr, flags); + temp = *ptr; + *ptr = x; + _atomic_spin_unlock_irqrestore(ptr, flags); + return temp; +} +#endif + +unsigned long notrace __xchg32(int x, volatile int *ptr) +{ + unsigned long flags; + long temp; + + _atomic_spin_lock_irqsave(ptr, flags); + temp = (long) *ptr; /* XXX - sign extension wanted? */ + *ptr = x; + _atomic_spin_unlock_irqrestore(ptr, flags); + return (unsigned long)temp; +} + + +unsigned long notrace __xchg8(char x, volatile char *ptr) +{ + unsigned long flags; + long temp; + + _atomic_spin_lock_irqsave(ptr, flags); + temp = (long) *ptr; /* XXX - sign extension wanted? */ + *ptr = x; + _atomic_spin_unlock_irqrestore(ptr, flags); + return (unsigned long)temp; +} + + +#define CMPXCHG(T) \ + T notrace __cmpxchg_##T(volatile T *ptr, T old, T new) \ + { \ + unsigned long flags; \ + T prev; \ + \ + _atomic_spin_lock_irqsave(ptr, flags); \ + if ((prev = *ptr) == old) \ + *ptr = new; \ + _atomic_spin_unlock_irqrestore(ptr, flags); \ + return prev; \ + } + +CMPXCHG(u64) +CMPXCHG(u32) +CMPXCHG(u16) +CMPXCHG(u8) diff --git a/arch/parisc/lib/delay.c b/arch/parisc/lib/delay.c new file mode 100644 index 000000000..66e506520 --- /dev/null +++ b/arch/parisc/lib/delay.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Precise Delay Loops for parisc + * + * based on code by: + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + * Copyright (C) 2008 Jiri Hladky <hladky _dot_ jiri _at_ gmail _dot_ com> + * + * parisc implementation: + * Copyright (C) 2013 Helge Deller <deller@gmx.de> + */ + + +#include <linux/module.h> +#include <linux/preempt.h> +#include <linux/init.h> + +#include <asm/delay.h> +#include <asm/special_insns.h> /* for mfctl() */ +#include <asm/processor.h> /* for boot_cpu_data */ + +/* CR16 based delay: */ +static void __cr16_delay(unsigned long __loops) +{ + /* + * Note: Due to unsigned math, cr16 rollovers shouldn't be + * a problem here. However, on 32 bit, we need to make sure + * we don't pass in too big a value. The current default + * value of MAX_UDELAY_MS should help prevent this. + */ + u32 bclock, now, loops = __loops; + int cpu; + + preempt_disable(); + cpu = smp_processor_id(); + bclock = mfctl(16); + for (;;) { + now = mfctl(16); + if ((now - bclock) >= loops) + break; + + /* Allow RT tasks to run */ + preempt_enable(); + asm volatile(" nop\n"); + barrier(); + preempt_disable(); + + /* + * It is possible that we moved to another CPU, and + * since CR16's are per-cpu we need to calculate + * that. The delay must guarantee that we wait "at + * least" the amount of time. Being moved to another + * CPU could make the wait longer but we just need to + * make sure we waited long enough. Rebalance the + * counter for this CPU. + */ + if (unlikely(cpu != smp_processor_id())) { + loops -= (now - bclock); + cpu = smp_processor_id(); + bclock = mfctl(16); + } + } + preempt_enable(); +} + + +void __udelay(unsigned long usecs) +{ + __cr16_delay(usecs * ((unsigned long)boot_cpu_data.cpu_hz / 1000000UL)); +} +EXPORT_SYMBOL(__udelay); diff --git a/arch/parisc/lib/io.c b/arch/parisc/lib/io.c new file mode 100644 index 000000000..3c7e617f5 --- /dev/null +++ b/arch/parisc/lib/io.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * arch/parisc/lib/io.c + * + * Copyright (c) Matthew Wilcox 2001 for Hewlett-Packard + * Copyright (c) Randolph Chung 2001 <tausq@debian.org> + * + * IO accessing functions which shouldn't be inlined because they're too big + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> + +/* + * Read COUNT 8-bit bytes from port PORT into memory starting at + * SRC. + */ +void insb (unsigned long port, void *dst, unsigned long count) +{ + unsigned char *p; + + p = (unsigned char *)dst; + + while (((unsigned long)p) & 0x3) { + if (!count) + return; + count--; + *p = inb(port); + p++; + } + + while (count >= 4) { + unsigned int w; + count -= 4; + w = inb(port) << 24; + w |= inb(port) << 16; + w |= inb(port) << 8; + w |= inb(port); + *(unsigned int *) p = w; + p += 4; + } + + while (count) { + --count; + *p = inb(port); + p++; + } +} + + +/* + * Read COUNT 16-bit words from port PORT into memory starting at + * SRC. SRC must be at least short aligned. This is used by the + * IDE driver to read disk sectors. Performance is important, but + * the interfaces seems to be slow: just using the inlined version + * of the inw() breaks things. + */ +void insw (unsigned long port, void *dst, unsigned long count) +{ + unsigned int l = 0, l2; + unsigned char *p; + + p = (unsigned char *)dst; + + if (!count) + return; + + switch (((unsigned long)p) & 0x3) + { + case 0x00: /* Buffer 32-bit aligned */ + while (count>=2) { + + count -= 2; + l = cpu_to_le16(inw(port)) << 16; + l |= cpu_to_le16(inw(port)); + *(unsigned int *)p = l; + p += 4; + } + if (count) { + *(unsigned short *)p = cpu_to_le16(inw(port)); + } + break; + + case 0x02: /* Buffer 16-bit aligned */ + *(unsigned short *)p = cpu_to_le16(inw(port)); + p += 2; + count--; + while (count>=2) { + + count -= 2; + l = cpu_to_le16(inw(port)) << 16; + l |= cpu_to_le16(inw(port)); + *(unsigned int *)p = l; + p += 4; + } + if (count) { + *(unsigned short *)p = cpu_to_le16(inw(port)); + } + break; + + case 0x01: /* Buffer 8-bit aligned */ + case 0x03: + /* I don't bother with 32bit transfers + * in this case, 16bit will have to do -- DE */ + --count; + + l = cpu_to_le16(inw(port)); + *p = l >> 8; + p++; + while (count--) + { + l2 = cpu_to_le16(inw(port)); + *(unsigned short *)p = (l & 0xff) << 8 | (l2 >> 8); + p += 2; + l = l2; + } + *p = l & 0xff; + break; + } +} + + + +/* + * Read COUNT 32-bit words from port PORT into memory starting at + * SRC. Now works with any alignment in SRC. Performance is important, + * but the interfaces seems to be slow: just using the inlined version + * of the inl() breaks things. + */ +void insl (unsigned long port, void *dst, unsigned long count) +{ + unsigned int l = 0, l2; + unsigned char *p; + + p = (unsigned char *)dst; + + if (!count) + return; + + switch (((unsigned long) dst) & 0x3) + { + case 0x00: /* Buffer 32-bit aligned */ + while (count--) + { + *(unsigned int *)p = cpu_to_le32(inl(port)); + p += 4; + } + break; + + case 0x02: /* Buffer 16-bit aligned */ + --count; + + l = cpu_to_le32(inl(port)); + *(unsigned short *)p = l >> 16; + p += 2; + + while (count--) + { + l2 = cpu_to_le32(inl(port)); + *(unsigned int *)p = (l & 0xffff) << 16 | (l2 >> 16); + p += 4; + l = l2; + } + *(unsigned short *)p = l & 0xffff; + break; + case 0x01: /* Buffer 8-bit aligned */ + --count; + + l = cpu_to_le32(inl(port)); + *(unsigned char *)p = l >> 24; + p++; + *(unsigned short *)p = (l >> 8) & 0xffff; + p += 2; + while (count--) + { + l2 = cpu_to_le32(inl(port)); + *(unsigned int *)p = (l & 0xff) << 24 | (l2 >> 8); + p += 4; + l = l2; + } + *p = l & 0xff; + break; + case 0x03: /* Buffer 8-bit aligned */ + --count; + + l = cpu_to_le32(inl(port)); + *p = l >> 24; + p++; + while (count--) + { + l2 = cpu_to_le32(inl(port)); + *(unsigned int *)p = (l & 0xffffff) << 8 | l2 >> 24; + p += 4; + l = l2; + } + *(unsigned short *)p = (l >> 8) & 0xffff; + p += 2; + *p = l & 0xff; + break; + } +} + + +/* + * Like insb but in the opposite direction. + * Don't worry as much about doing aligned memory transfers: + * doing byte reads the "slow" way isn't nearly as slow as + * doing byte writes the slow way (no r-m-w cycle). + */ +void outsb(unsigned long port, const void * src, unsigned long count) +{ + const unsigned char *p; + + p = (const unsigned char *)src; + while (count) { + count--; + outb(*p, port); + p++; + } +} + +/* + * Like insw but in the opposite direction. This is used by the IDE + * driver to write disk sectors. Performance is important, but the + * interfaces seems to be slow: just using the inlined version of the + * outw() breaks things. + */ +void outsw (unsigned long port, const void *src, unsigned long count) +{ + unsigned int l = 0, l2; + const unsigned char *p; + + p = (const unsigned char *)src; + + if (!count) + return; + + switch (((unsigned long)p) & 0x3) + { + case 0x00: /* Buffer 32-bit aligned */ + while (count>=2) { + count -= 2; + l = *(unsigned int *)p; + p += 4; + outw(le16_to_cpu(l >> 16), port); + outw(le16_to_cpu(l & 0xffff), port); + } + if (count) { + outw(le16_to_cpu(*(unsigned short*)p), port); + } + break; + + case 0x02: /* Buffer 16-bit aligned */ + + outw(le16_to_cpu(*(unsigned short*)p), port); + p += 2; + count--; + + while (count>=2) { + count -= 2; + l = *(unsigned int *)p; + p += 4; + outw(le16_to_cpu(l >> 16), port); + outw(le16_to_cpu(l & 0xffff), port); + } + if (count) { + outw(le16_to_cpu(*(unsigned short *)p), port); + } + break; + + case 0x01: /* Buffer 8-bit aligned */ + /* I don't bother with 32bit transfers + * in this case, 16bit will have to do -- DE */ + + l = *p << 8; + p++; + count--; + while (count) + { + count--; + l2 = *(unsigned short *)p; + p += 2; + outw(le16_to_cpu(l | l2 >> 8), port); + l = l2 << 8; + } + l2 = *(unsigned char *)p; + outw (le16_to_cpu(l | l2>>8), port); + break; + + } +} + + +/* + * Like insl but in the opposite direction. This is used by the IDE + * driver to write disk sectors. Works with any alignment in SRC. + * Performance is important, but the interfaces seems to be slow: + * just using the inlined version of the outl() breaks things. + */ +void outsl (unsigned long port, const void *src, unsigned long count) +{ + unsigned int l = 0, l2; + const unsigned char *p; + + p = (const unsigned char *)src; + + if (!count) + return; + + switch (((unsigned long)p) & 0x3) + { + case 0x00: /* Buffer 32-bit aligned */ + while (count--) + { + outl(le32_to_cpu(*(unsigned int *)p), port); + p += 4; + } + break; + + case 0x02: /* Buffer 16-bit aligned */ + --count; + + l = *(unsigned short *)p; + p += 2; + + while (count--) + { + l2 = *(unsigned int *)p; + p += 4; + outl (le32_to_cpu(l << 16 | l2 >> 16), port); + l = l2; + } + l2 = *(unsigned short *)p; + outl (le32_to_cpu(l << 16 | l2), port); + break; + case 0x01: /* Buffer 8-bit aligned */ + --count; + + l = *p << 24; + p++; + l |= *(unsigned short *)p << 8; + p += 2; + + while (count--) + { + l2 = *(unsigned int *)p; + p += 4; + outl (le32_to_cpu(l | l2 >> 24), port); + l = l2 << 8; + } + l2 = *p; + outl (le32_to_cpu(l | l2), port); + break; + case 0x03: /* Buffer 8-bit aligned */ + --count; + + l = *p << 24; + p++; + + while (count--) + { + l2 = *(unsigned int *)p; + p += 4; + outl (le32_to_cpu(l | l2 >> 8), port); + l = l2 << 24; + } + l2 = *(unsigned short *)p << 16; + p += 2; + l2 |= *p; + outl (le32_to_cpu(l | l2), port); + break; + } +} + +EXPORT_SYMBOL(insb); +EXPORT_SYMBOL(insw); +EXPORT_SYMBOL(insl); +EXPORT_SYMBOL(outsb); +EXPORT_SYMBOL(outsw); +EXPORT_SYMBOL(outsl); diff --git a/arch/parisc/lib/iomap.c b/arch/parisc/lib/iomap.c new file mode 100644 index 000000000..915c0c4da --- /dev/null +++ b/arch/parisc/lib/iomap.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * iomap.c - Implement iomap interface for PA-RISC + * Copyright (c) 2004 Matthew Wilcox + */ + +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/export.h> +#include <asm/io.h> + +/* + * The iomap space on 32-bit PA-RISC is intended to look like this: + * 00000000-7fffffff virtual mapped IO + * 80000000-8fffffff ISA/EISA port space that can't be virtually mapped + * 90000000-9fffffff Dino port space + * a0000000-afffffff Astro port space + * b0000000-bfffffff PAT port space + * c0000000-cfffffff non-swapped memory IO + * f0000000-ffffffff legacy IO memory pointers + * + * For the moment, here's what it looks like: + * 80000000-8fffffff All ISA/EISA port space + * f0000000-ffffffff legacy IO memory pointers + * + * On 64-bit, everything is extended, so: + * 8000000000000000-8fffffffffffffff All ISA/EISA port space + * f000000000000000-ffffffffffffffff legacy IO memory pointers + */ + +/* + * Technically, this should be 'if (VMALLOC_START < addr < VMALLOC_END), + * but that's slow and we know it'll be within the first 2GB. + */ +#ifdef CONFIG_64BIT +#define INDIRECT_ADDR(addr) (((unsigned long)(addr) & 1UL<<63) != 0) +#define ADDR_TO_REGION(addr) (((unsigned long)addr >> 60) & 7) +#define IOPORT_MAP_BASE (8UL << 60) +#else +#define INDIRECT_ADDR(addr) (((unsigned long)(addr) & 1UL<<31) != 0) +#define ADDR_TO_REGION(addr) (((unsigned long)addr >> 28) & 7) +#define IOPORT_MAP_BASE (8UL << 28) +#endif + +struct iomap_ops { + unsigned int (*read8)(const void __iomem *); + unsigned int (*read16)(const void __iomem *); + unsigned int (*read16be)(const void __iomem *); + unsigned int (*read32)(const void __iomem *); + unsigned int (*read32be)(const void __iomem *); +#ifdef CONFIG_64BIT + u64 (*read64)(const void __iomem *); + u64 (*read64be)(const void __iomem *); +#endif + void (*write8)(u8, void __iomem *); + void (*write16)(u16, void __iomem *); + void (*write16be)(u16, void __iomem *); + void (*write32)(u32, void __iomem *); + void (*write32be)(u32, void __iomem *); +#ifdef CONFIG_64BIT + void (*write64)(u64, void __iomem *); + void (*write64be)(u64, void __iomem *); +#endif + void (*read8r)(const void __iomem *, void *, unsigned long); + void (*read16r)(const void __iomem *, void *, unsigned long); + void (*read32r)(const void __iomem *, void *, unsigned long); + void (*write8r)(void __iomem *, const void *, unsigned long); + void (*write16r)(void __iomem *, const void *, unsigned long); + void (*write32r)(void __iomem *, const void *, unsigned long); +}; + +/* Generic ioport ops. To be replaced later by specific dino/elroy/wax code */ + +#define ADDR2PORT(addr) ((unsigned long __force)(addr) & 0xffffff) + +static unsigned int ioport_read8(const void __iomem *addr) +{ + return inb(ADDR2PORT(addr)); +} + +static unsigned int ioport_read16(const void __iomem *addr) +{ + return inw(ADDR2PORT(addr)); +} + +static unsigned int ioport_read32(const void __iomem *addr) +{ + return inl(ADDR2PORT(addr)); +} + +static void ioport_write8(u8 datum, void __iomem *addr) +{ + outb(datum, ADDR2PORT(addr)); +} + +static void ioport_write16(u16 datum, void __iomem *addr) +{ + outw(datum, ADDR2PORT(addr)); +} + +static void ioport_write32(u32 datum, void __iomem *addr) +{ + outl(datum, ADDR2PORT(addr)); +} + +static void ioport_read8r(const void __iomem *addr, void *dst, unsigned long count) +{ + insb(ADDR2PORT(addr), dst, count); +} + +static void ioport_read16r(const void __iomem *addr, void *dst, unsigned long count) +{ + insw(ADDR2PORT(addr), dst, count); +} + +static void ioport_read32r(const void __iomem *addr, void *dst, unsigned long count) +{ + insl(ADDR2PORT(addr), dst, count); +} + +static void ioport_write8r(void __iomem *addr, const void *s, unsigned long n) +{ + outsb(ADDR2PORT(addr), s, n); +} + +static void ioport_write16r(void __iomem *addr, const void *s, unsigned long n) +{ + outsw(ADDR2PORT(addr), s, n); +} + +static void ioport_write32r(void __iomem *addr, const void *s, unsigned long n) +{ + outsl(ADDR2PORT(addr), s, n); +} + +static const struct iomap_ops ioport_ops = { + .read8 = ioport_read8, + .read16 = ioport_read16, + .read16be = ioport_read16, + .read32 = ioport_read32, + .read32be = ioport_read32, + .write8 = ioport_write8, + .write16 = ioport_write16, + .write16be = ioport_write16, + .write32 = ioport_write32, + .write32be = ioport_write32, + .read8r = ioport_read8r, + .read16r = ioport_read16r, + .read32r = ioport_read32r, + .write8r = ioport_write8r, + .write16r = ioport_write16r, + .write32r = ioport_write32r, +}; + +/* Legacy I/O memory ops */ + +static unsigned int iomem_read8(const void __iomem *addr) +{ + return readb(addr); +} + +static unsigned int iomem_read16(const void __iomem *addr) +{ + return readw(addr); +} + +static unsigned int iomem_read16be(const void __iomem *addr) +{ + return __raw_readw(addr); +} + +static unsigned int iomem_read32(const void __iomem *addr) +{ + return readl(addr); +} + +static unsigned int iomem_read32be(const void __iomem *addr) +{ + return __raw_readl(addr); +} + +#ifdef CONFIG_64BIT +static u64 iomem_read64(const void __iomem *addr) +{ + return readq(addr); +} + +static u64 iomem_read64be(const void __iomem *addr) +{ + return __raw_readq(addr); +} +#endif + +static void iomem_write8(u8 datum, void __iomem *addr) +{ + writeb(datum, addr); +} + +static void iomem_write16(u16 datum, void __iomem *addr) +{ + writew(datum, addr); +} + +static void iomem_write16be(u16 datum, void __iomem *addr) +{ + __raw_writew(datum, addr); +} + +static void iomem_write32(u32 datum, void __iomem *addr) +{ + writel(datum, addr); +} + +static void iomem_write32be(u32 datum, void __iomem *addr) +{ + __raw_writel(datum, addr); +} + +#ifdef CONFIG_64BIT +static void iomem_write64(u64 datum, void __iomem *addr) +{ + writeq(datum, addr); +} + +static void iomem_write64be(u64 datum, void __iomem *addr) +{ + __raw_writeq(datum, addr); +} +#endif + +static void iomem_read8r(const void __iomem *addr, void *dst, unsigned long count) +{ + while (count--) { + *(u8 *)dst = __raw_readb(addr); + dst++; + } +} + +static void iomem_read16r(const void __iomem *addr, void *dst, unsigned long count) +{ + while (count--) { + *(u16 *)dst = __raw_readw(addr); + dst += 2; + } +} + +static void iomem_read32r(const void __iomem *addr, void *dst, unsigned long count) +{ + while (count--) { + *(u32 *)dst = __raw_readl(addr); + dst += 4; + } +} + +static void iomem_write8r(void __iomem *addr, const void *s, unsigned long n) +{ + while (n--) { + __raw_writeb(*(u8 *)s, addr); + s++; + } +} + +static void iomem_write16r(void __iomem *addr, const void *s, unsigned long n) +{ + while (n--) { + __raw_writew(*(u16 *)s, addr); + s += 2; + } +} + +static void iomem_write32r(void __iomem *addr, const void *s, unsigned long n) +{ + while (n--) { + __raw_writel(*(u32 *)s, addr); + s += 4; + } +} + +static const struct iomap_ops iomem_ops = { + .read8 = iomem_read8, + .read16 = iomem_read16, + .read16be = iomem_read16be, + .read32 = iomem_read32, + .read32be = iomem_read32be, +#ifdef CONFIG_64BIT + .read64 = iomem_read64, + .read64be = iomem_read64be, +#endif + .write8 = iomem_write8, + .write16 = iomem_write16, + .write16be = iomem_write16be, + .write32 = iomem_write32, + .write32be = iomem_write32be, +#ifdef CONFIG_64BIT + .write64 = iomem_write64, + .write64be = iomem_write64be, +#endif + .read8r = iomem_read8r, + .read16r = iomem_read16r, + .read32r = iomem_read32r, + .write8r = iomem_write8r, + .write16r = iomem_write16r, + .write32r = iomem_write32r, +}; + +static const struct iomap_ops *iomap_ops[8] = { + [0] = &ioport_ops, + [7] = &iomem_ops +}; + + +unsigned int ioread8(const void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) + return iomap_ops[ADDR_TO_REGION(addr)]->read8(addr); + return *((u8 *)addr); +} + +unsigned int ioread16(const void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) + return iomap_ops[ADDR_TO_REGION(addr)]->read16(addr); + return le16_to_cpup((u16 *)addr); +} + +unsigned int ioread16be(const void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) + return iomap_ops[ADDR_TO_REGION(addr)]->read16be(addr); + return *((u16 *)addr); +} + +unsigned int ioread32(const void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) + return iomap_ops[ADDR_TO_REGION(addr)]->read32(addr); + return le32_to_cpup((u32 *)addr); +} + +unsigned int ioread32be(const void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) + return iomap_ops[ADDR_TO_REGION(addr)]->read32be(addr); + return *((u32 *)addr); +} + +#ifdef CONFIG_64BIT +u64 ioread64(const void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) + return iomap_ops[ADDR_TO_REGION(addr)]->read64(addr); + return le64_to_cpup((u64 *)addr); +} + +u64 ioread64be(const void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) + return iomap_ops[ADDR_TO_REGION(addr)]->read64be(addr); + return *((u64 *)addr); +} +#endif + +void iowrite8(u8 datum, void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write8(datum, addr); + } else { + *((u8 *)addr) = datum; + } +} + +void iowrite16(u16 datum, void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write16(datum, addr); + } else { + *((u16 *)addr) = cpu_to_le16(datum); + } +} + +void iowrite16be(u16 datum, void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write16be(datum, addr); + } else { + *((u16 *)addr) = datum; + } +} + +void iowrite32(u32 datum, void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write32(datum, addr); + } else { + *((u32 *)addr) = cpu_to_le32(datum); + } +} + +void iowrite32be(u32 datum, void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write32be(datum, addr); + } else { + *((u32 *)addr) = datum; + } +} + +#ifdef CONFIG_64BIT +void iowrite64(u64 datum, void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write64(datum, addr); + } else { + *((u64 *)addr) = cpu_to_le64(datum); + } +} + +void iowrite64be(u64 datum, void __iomem *addr) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write64be(datum, addr); + } else { + *((u64 *)addr) = datum; + } +} +#endif + +/* Repeating interfaces */ + +void ioread8_rep(const void __iomem *addr, void *dst, unsigned long count) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->read8r(addr, dst, count); + } else { + while (count--) { + *(u8 *)dst = *(u8 *)addr; + dst++; + } + } +} + +void ioread16_rep(const void __iomem *addr, void *dst, unsigned long count) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->read16r(addr, dst, count); + } else { + while (count--) { + *(u16 *)dst = *(u16 *)addr; + dst += 2; + } + } +} + +void ioread32_rep(const void __iomem *addr, void *dst, unsigned long count) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->read32r(addr, dst, count); + } else { + while (count--) { + *(u32 *)dst = *(u32 *)addr; + dst += 4; + } + } +} + +void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write8r(addr, src, count); + } else { + while (count--) { + *(u8 *)addr = *(u8 *)src; + src++; + } + } +} + +void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write16r(addr, src, count); + } else { + while (count--) { + *(u16 *)addr = *(u16 *)src; + src += 2; + } + } +} + +void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) +{ + if (unlikely(INDIRECT_ADDR(addr))) { + iomap_ops[ADDR_TO_REGION(addr)]->write32r(addr, src, count); + } else { + while (count--) { + *(u32 *)addr = *(u32 *)src; + src += 4; + } + } +} + +/* Mapping interfaces */ + +void __iomem *ioport_map(unsigned long port, unsigned int nr) +{ + return (void __iomem *)(IOPORT_MAP_BASE | port); +} + +void ioport_unmap(void __iomem *addr) +{ + if (!INDIRECT_ADDR(addr)) { + iounmap(addr); + } +} + +#ifdef CONFIG_PCI +void pci_iounmap(struct pci_dev *dev, void __iomem * addr) +{ + if (!INDIRECT_ADDR(addr)) { + iounmap(addr); + } +} +EXPORT_SYMBOL(pci_iounmap); +#endif + +EXPORT_SYMBOL(ioread8); +EXPORT_SYMBOL(ioread16); +EXPORT_SYMBOL(ioread16be); +EXPORT_SYMBOL(ioread32); +EXPORT_SYMBOL(ioread32be); +#ifdef CONFIG_64BIT +EXPORT_SYMBOL(ioread64); +EXPORT_SYMBOL(ioread64be); +#endif +EXPORT_SYMBOL(iowrite8); +EXPORT_SYMBOL(iowrite16); +EXPORT_SYMBOL(iowrite16be); +EXPORT_SYMBOL(iowrite32); +EXPORT_SYMBOL(iowrite32be); +#ifdef CONFIG_64BIT +EXPORT_SYMBOL(iowrite64); +EXPORT_SYMBOL(iowrite64be); +#endif +EXPORT_SYMBOL(ioread8_rep); +EXPORT_SYMBOL(ioread16_rep); +EXPORT_SYMBOL(ioread32_rep); +EXPORT_SYMBOL(iowrite8_rep); +EXPORT_SYMBOL(iowrite16_rep); +EXPORT_SYMBOL(iowrite32_rep); +EXPORT_SYMBOL(ioport_map); +EXPORT_SYMBOL(ioport_unmap); diff --git a/arch/parisc/lib/lusercopy.S b/arch/parisc/lib/lusercopy.S new file mode 100644 index 000000000..b428d29e4 --- /dev/null +++ b/arch/parisc/lib/lusercopy.S @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * User Space Access Routines + * + * Copyright (C) 2000-2002 Hewlett-Packard (John Marvin) + * Copyright (C) 2000 Richard Hirst <rhirst with parisc-linux.org> + * Copyright (C) 2001 Matthieu Delahaye <delahaym at esiee.fr> + * Copyright (C) 2003 Randolph Chung <tausq with parisc-linux.org> + * Copyright (C) 2017 Helge Deller <deller@gmx.de> + * Copyright (C) 2017 John David Anglin <dave.anglin@bell.net> + */ + +/* + * These routines still have plenty of room for optimization + * (word & doubleword load/store, dual issue, store hints, etc.). + */ + +/* + * The following routines assume that space register 3 (sr3) contains + * the space id associated with the current users address space. + */ + + + .text + +#include <asm/assembly.h> +#include <asm/errno.h> +#include <linux/linkage.h> + + /* + * unsigned long lclear_user(void *to, unsigned long n) + * + * Returns 0 for success. + * otherwise, returns number of bytes not transferred. + */ + +ENTRY_CFI(lclear_user) + comib,=,n 0,%r25,$lclu_done +$lclu_loop: + addib,<> -1,%r25,$lclu_loop +1: stbs,ma %r0,1(%sr3,%r26) + +$lclu_done: + bv %r0(%r2) + copy %r25,%r28 + +2: b $lclu_done + ldo 1(%r25),%r25 + + ASM_EXCEPTIONTABLE_ENTRY(1b,2b) +ENDPROC_CFI(lclear_user) + + +/* + * unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len) + * + * Inputs: + * - sr1 already contains space of source region + * - sr2 already contains space of destination region + * + * Returns: + * - number of bytes that could not be copied. + * On success, this will be zero. + * + * This code is based on a C-implementation of a copy routine written by + * Randolph Chung, which in turn was derived from the glibc. + * + * Several strategies are tried to try to get the best performance for various + * conditions. In the optimal case, we copy by loops that copy 32- or 16-bytes + * at a time using general registers. Unaligned copies are handled either by + * aligning the destination and then using shift-and-write method, or in a few + * cases by falling back to a byte-at-a-time copy. + * + * Testing with various alignments and buffer sizes shows that this code is + * often >10x faster than a simple byte-at-a-time copy, even for strangely + * aligned operands. It is interesting to note that the glibc version of memcpy + * (written in C) is actually quite fast already. This routine is able to beat + * it by 30-40% for aligned copies because of the loop unrolling, but in some + * cases the glibc version is still slightly faster. This lends more + * credibility that gcc can generate very good code as long as we are careful. + * + * Possible optimizations: + * - add cache prefetching + * - try not to use the post-increment address modifiers; they may create + * additional interlocks. Assumption is that those were only efficient on old + * machines (pre PA8000 processors) + */ + + dst = arg0 + src = arg1 + len = arg2 + end = arg3 + t1 = r19 + t2 = r20 + t3 = r21 + t4 = r22 + srcspc = sr1 + dstspc = sr2 + + t0 = r1 + a1 = t1 + a2 = t2 + a3 = t3 + a0 = t4 + + save_src = ret0 + save_dst = ret1 + save_len = r31 + +ENTRY_CFI(pa_memcpy) + /* Last destination address */ + add dst,len,end + + /* short copy with less than 16 bytes? */ + cmpib,COND(>>=),n 15,len,.Lbyte_loop + + /* same alignment? */ + xor src,dst,t0 + extru t0,31,2,t1 + cmpib,<>,n 0,t1,.Lunaligned_copy + +#ifdef CONFIG_64BIT + /* only do 64-bit copies if we can get aligned. */ + extru t0,31,3,t1 + cmpib,<>,n 0,t1,.Lalign_loop32 + + /* loop until we are 64-bit aligned */ +.Lalign_loop64: + extru dst,31,3,t1 + cmpib,=,n 0,t1,.Lcopy_loop_16_start +20: ldb,ma 1(srcspc,src),t1 +21: stb,ma t1,1(dstspc,dst) + b .Lalign_loop64 + ldo -1(len),len + + ASM_EXCEPTIONTABLE_ENTRY(20b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(21b,.Lcopy_done) + +.Lcopy_loop_16_start: + ldi 31,t0 +.Lcopy_loop_16: + cmpb,COND(>>=),n t0,len,.Lword_loop + +10: ldd 0(srcspc,src),t1 +11: ldd 8(srcspc,src),t2 + ldo 16(src),src +12: std,ma t1,8(dstspc,dst) +13: std,ma t2,8(dstspc,dst) +14: ldd 0(srcspc,src),t1 +15: ldd 8(srcspc,src),t2 + ldo 16(src),src +16: std,ma t1,8(dstspc,dst) +17: std,ma t2,8(dstspc,dst) + + ASM_EXCEPTIONTABLE_ENTRY(10b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(11b,.Lcopy16_fault) + ASM_EXCEPTIONTABLE_ENTRY(12b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(13b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(14b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(15b,.Lcopy16_fault) + ASM_EXCEPTIONTABLE_ENTRY(16b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(17b,.Lcopy_done) + + b .Lcopy_loop_16 + ldo -32(len),len + +.Lword_loop: + cmpib,COND(>>=),n 3,len,.Lbyte_loop +20: ldw,ma 4(srcspc,src),t1 +21: stw,ma t1,4(dstspc,dst) + b .Lword_loop + ldo -4(len),len + + ASM_EXCEPTIONTABLE_ENTRY(20b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(21b,.Lcopy_done) + +#endif /* CONFIG_64BIT */ + + /* loop until we are 32-bit aligned */ +.Lalign_loop32: + extru dst,31,2,t1 + cmpib,=,n 0,t1,.Lcopy_loop_8 +20: ldb,ma 1(srcspc,src),t1 +21: stb,ma t1,1(dstspc,dst) + b .Lalign_loop32 + ldo -1(len),len + + ASM_EXCEPTIONTABLE_ENTRY(20b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(21b,.Lcopy_done) + + +.Lcopy_loop_8: + cmpib,COND(>>=),n 15,len,.Lbyte_loop + +10: ldw 0(srcspc,src),t1 +11: ldw 4(srcspc,src),t2 +12: stw,ma t1,4(dstspc,dst) +13: stw,ma t2,4(dstspc,dst) +14: ldw 8(srcspc,src),t1 +15: ldw 12(srcspc,src),t2 + ldo 16(src),src +16: stw,ma t1,4(dstspc,dst) +17: stw,ma t2,4(dstspc,dst) + + ASM_EXCEPTIONTABLE_ENTRY(10b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(11b,.Lcopy8_fault) + ASM_EXCEPTIONTABLE_ENTRY(12b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(13b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(14b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(15b,.Lcopy8_fault) + ASM_EXCEPTIONTABLE_ENTRY(16b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(17b,.Lcopy_done) + + b .Lcopy_loop_8 + ldo -16(len),len + +.Lbyte_loop: + cmpclr,COND(<>) len,%r0,%r0 + b,n .Lcopy_done +20: ldb 0(srcspc,src),t1 + ldo 1(src),src +21: stb,ma t1,1(dstspc,dst) + b .Lbyte_loop + ldo -1(len),len + + ASM_EXCEPTIONTABLE_ENTRY(20b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(21b,.Lcopy_done) + +.Lcopy_done: + bv %r0(%r2) + sub end,dst,ret0 + + + /* src and dst are not aligned the same way. */ + /* need to go the hard way */ +.Lunaligned_copy: + /* align until dst is 32bit-word-aligned */ + extru dst,31,2,t1 + cmpib,=,n 0,t1,.Lcopy_dstaligned +20: ldb 0(srcspc,src),t1 + ldo 1(src),src +21: stb,ma t1,1(dstspc,dst) + b .Lunaligned_copy + ldo -1(len),len + + ASM_EXCEPTIONTABLE_ENTRY(20b,.Lcopy_done) + ASM_EXCEPTIONTABLE_ENTRY(21b,.Lcopy_done) + +.Lcopy_dstaligned: + + /* store src, dst and len in safe place */ + copy src,save_src + copy dst,save_dst + copy len,save_len + + /* len now needs give number of words to copy */ + SHRREG len,2,len + + /* + * Copy from a not-aligned src to an aligned dst using shifts. + * Handles 4 words per loop. + */ + + depw,z src,28,2,t0 + subi 32,t0,t0 + mtsar t0 + extru len,31,2,t0 + cmpib,= 2,t0,.Lcase2 + /* Make src aligned by rounding it down. */ + depi 0,31,2,src + + cmpiclr,<> 3,t0,%r0 + b,n .Lcase3 + cmpiclr,<> 1,t0,%r0 + b,n .Lcase1 +.Lcase0: + cmpb,COND(=) %r0,len,.Lcda_finish + nop + +1: ldw,ma 4(srcspc,src), a3 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) +1: ldw,ma 4(srcspc,src), a0 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + b,n .Ldo3 +.Lcase1: +1: ldw,ma 4(srcspc,src), a2 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) +1: ldw,ma 4(srcspc,src), a3 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + ldo -1(len),len + cmpb,COND(=),n %r0,len,.Ldo0 +.Ldo4: +1: ldw,ma 4(srcspc,src), a0 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + shrpw a2, a3, %sar, t0 +1: stw,ma t0, 4(dstspc,dst) + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcopy_done) +.Ldo3: +1: ldw,ma 4(srcspc,src), a1 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + shrpw a3, a0, %sar, t0 +1: stw,ma t0, 4(dstspc,dst) + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcopy_done) +.Ldo2: +1: ldw,ma 4(srcspc,src), a2 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + shrpw a0, a1, %sar, t0 +1: stw,ma t0, 4(dstspc,dst) + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcopy_done) +.Ldo1: +1: ldw,ma 4(srcspc,src), a3 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + shrpw a1, a2, %sar, t0 +1: stw,ma t0, 4(dstspc,dst) + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcopy_done) + ldo -4(len),len + cmpb,COND(<>) %r0,len,.Ldo4 + nop +.Ldo0: + shrpw a2, a3, %sar, t0 +1: stw,ma t0, 4(dstspc,dst) + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcopy_done) + +.Lcda_rdfault: +.Lcda_finish: + /* calculate new src, dst and len and jump to byte-copy loop */ + sub dst,save_dst,t0 + add save_src,t0,src + b .Lbyte_loop + sub save_len,t0,len + +.Lcase3: +1: ldw,ma 4(srcspc,src), a0 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) +1: ldw,ma 4(srcspc,src), a1 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + b .Ldo2 + ldo 1(len),len +.Lcase2: +1: ldw,ma 4(srcspc,src), a1 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) +1: ldw,ma 4(srcspc,src), a2 + ASM_EXCEPTIONTABLE_ENTRY(1b,.Lcda_rdfault) + b .Ldo1 + ldo 2(len),len + + + /* fault exception fixup handlers: */ +#ifdef CONFIG_64BIT +.Lcopy16_fault: + b .Lcopy_done +10: std,ma t1,8(dstspc,dst) + ASM_EXCEPTIONTABLE_ENTRY(10b,.Lcopy_done) +#endif + +.Lcopy8_fault: + b .Lcopy_done +10: stw,ma t1,4(dstspc,dst) + ASM_EXCEPTIONTABLE_ENTRY(10b,.Lcopy_done) +ENDPROC_CFI(pa_memcpy) + + .end diff --git a/arch/parisc/lib/memcpy.c b/arch/parisc/lib/memcpy.c new file mode 100644 index 000000000..03165c82d --- /dev/null +++ b/arch/parisc/lib/memcpy.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Optimized memory copy routines. + * + * Copyright (C) 2004 Randolph Chung <tausq@debian.org> + * Copyright (C) 2013-2017 Helge Deller <deller@gmx.de> + * + * Portions derived from the GNU C Library + * Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. + */ + +#include <linux/module.h> +#include <linux/compiler.h> +#include <linux/uaccess.h> +#include <linux/mm.h> + +#define get_user_space() mfsp(SR_USER) +#define get_kernel_space() SR_KERNEL + +/* Returns 0 for success, otherwise, returns number of bytes not transferred. */ +extern unsigned long pa_memcpy(void *dst, const void *src, + unsigned long len); + +unsigned long raw_copy_to_user(void __user *dst, const void *src, + unsigned long len) +{ + mtsp(get_kernel_space(), SR_TEMP1); + mtsp(get_user_space(), SR_TEMP2); + return pa_memcpy((void __force *)dst, src, len); +} +EXPORT_SYMBOL(raw_copy_to_user); + +unsigned long raw_copy_from_user(void *dst, const void __user *src, + unsigned long len) +{ + unsigned long start = (unsigned long) src; + unsigned long end = start + len; + unsigned long newlen = len; + + mtsp(get_user_space(), SR_TEMP1); + mtsp(get_kernel_space(), SR_TEMP2); + + /* Check region is user accessible */ + while (start < end) { + if (!prober_user(SR_TEMP1, start)) { + newlen = (start - (unsigned long) src); + break; + } + start += PAGE_SIZE; + /* align to page boundry which may have different permission */ + start = PAGE_ALIGN_DOWN(start); + } + return len - newlen + pa_memcpy(dst, (void __force *)src, newlen); +} +EXPORT_SYMBOL(raw_copy_from_user); + +void * memcpy(void * dst,const void *src, size_t count) +{ + mtsp(get_kernel_space(), SR_TEMP1); + mtsp(get_kernel_space(), SR_TEMP2); + pa_memcpy(dst, src, count); + return dst; +} + +EXPORT_SYMBOL(memcpy); + +bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size) +{ + if ((unsigned long)unsafe_src < PAGE_SIZE) + return false; + /* check for I/O space F_EXTEND(0xfff00000) access as well? */ + return true; +} diff --git a/arch/parisc/lib/memset.c b/arch/parisc/lib/memset.c new file mode 100644 index 000000000..133e48098 --- /dev/null +++ b/arch/parisc/lib/memset.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include <linux/types.h> +#include <asm/string.h> + +#define OPSIZ (BITS_PER_LONG/8) +typedef unsigned long op_t; + +void * +memset (void *dstpp, int sc, size_t len) +{ + unsigned int c = sc; + long int dstp = (long int) dstpp; + + if (len >= 8) + { + size_t xlen; + op_t cccc; + + cccc = (unsigned char) c; + cccc |= cccc << 8; + cccc |= cccc << 16; + if (OPSIZ > 4) + /* Do the shift in two steps to avoid warning if long has 32 bits. */ + cccc |= (cccc << 16) << 16; + + /* There are at least some bytes to set. + No need to test for LEN == 0 in this alignment loop. */ + while (dstp % OPSIZ != 0) + { + ((unsigned char *) dstp)[0] = c; + dstp += 1; + len -= 1; + } + + /* Write 8 `op_t' per iteration until less than 8 `op_t' remain. */ + xlen = len / (OPSIZ * 8); + while (xlen > 0) + { + ((op_t *) dstp)[0] = cccc; + ((op_t *) dstp)[1] = cccc; + ((op_t *) dstp)[2] = cccc; + ((op_t *) dstp)[3] = cccc; + ((op_t *) dstp)[4] = cccc; + ((op_t *) dstp)[5] = cccc; + ((op_t *) dstp)[6] = cccc; + ((op_t *) dstp)[7] = cccc; + dstp += 8 * OPSIZ; + xlen -= 1; + } + len %= OPSIZ * 8; + + /* Write 1 `op_t' per iteration until less than OPSIZ bytes remain. */ + xlen = len / OPSIZ; + while (xlen > 0) + { + ((op_t *) dstp)[0] = cccc; + dstp += OPSIZ; + xlen -= 1; + } + len %= OPSIZ; + } + + /* Write the last few bytes. */ + while (len > 0) + { + ((unsigned char *) dstp)[0] = c; + dstp += 1; + len -= 1; + } + + return dstpp; +} diff --git a/arch/parisc/lib/ucmpdi2.c b/arch/parisc/lib/ucmpdi2.c new file mode 100644 index 000000000..9d8b4dbae --- /dev/null +++ b/arch/parisc/lib/ucmpdi2.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/module.h> +#include <linux/libgcc.h> + +union ull_union { + unsigned long long ull; + struct { + unsigned int high; + unsigned int low; + } ui; +}; + +word_type __ucmpdi2(unsigned long long a, unsigned long long b) +{ + union ull_union au = {.ull = a}; + union ull_union bu = {.ull = b}; + + if (au.ui.high < bu.ui.high) + return 0; + else if (au.ui.high > bu.ui.high) + return 2; + if (au.ui.low < bu.ui.low) + return 0; + else if (au.ui.low > bu.ui.low) + return 2; + return 1; +} diff --git a/arch/parisc/math-emu/Makefile b/arch/parisc/math-emu/Makefile new file mode 100644 index 000000000..7b64740e1 --- /dev/null +++ b/arch/parisc/math-emu/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux/parisc floating point code +# + +# See arch/parisc/math-emu/README +ccflags-y := -Wno-parentheses -Wno-implicit-function-declaration \ + -Wno-uninitialized -Wno-strict-prototypes -Wno-return-type \ + -Wno-implicit-int -Wno-missing-prototypes -Wno-missing-declarations \ + -Wno-old-style-definition -Wno-unused-but-set-variable + +obj-y := frnd.o driver.o decode_exc.o fpudispatch.o denormal.o \ + dfmpy.o sfmpy.o sfsqrt.o dfsqrt.o dfadd.o fmpyfadd.o \ + sfadd.o dfsub.o sfsub.o fcnvfxt.o fcnvff.o fcnvxf.o \ + fcnvfx.o fcnvuf.o fcnvfu.o fcnvfut.o dfdiv.o sfdiv.o \ + dfrem.o sfrem.o dfcmp.o sfcmp.o + +# Math emulation code beyond the FRND is required for 712/80i and +# other very old or stripped-down PA-RISC CPUs -- not currently supported + +obj-$(CONFIG_MATH_EMULATION) += unimplemented-math-emulation.o +CFLAGS_REMOVE_fpudispatch.o = -Wimplicit-fallthrough diff --git a/arch/parisc/math-emu/README b/arch/parisc/math-emu/README new file mode 100644 index 000000000..1a0124ef0 --- /dev/null +++ b/arch/parisc/math-emu/README @@ -0,0 +1,11 @@ +All files except driver.c are snapshots from the HP-UX kernel. They've +been modified as little as possible. Even though they don't fit the +Linux coding style, please leave them in their funny format just in case +someone in the future, with access to HP-UX source code, is generous +enough to update our copies with later changes from HP-UX -- it'll +make their 'diff' job easier if our code is relatively unmodified. + +Required Disclaimer: Hewlett-Packard makes no implied or expressed +warranties about this code nor any promises to maintain or test it +in any way. This copy of this snapshot is no longer the property +of Hewlett-Packard. diff --git a/arch/parisc/math-emu/cnv_float.h b/arch/parisc/math-emu/cnv_float.h new file mode 100644 index 000000000..ef783a383 --- /dev/null +++ b/arch/parisc/math-emu/cnv_float.h @@ -0,0 +1,363 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ + +#ifdef __NO_PA_HDRS + PA header file -- do not include this header file for non-PA builds. +#endif + +/* + * Some more constants + */ +#define SGL_FX_MAX_EXP 30 +#define DBL_FX_MAX_EXP 62 +#define QUAD_FX_MAX_EXP 126 + +#define Dintp1(object) (object) +#define Dintp2(object) (object) + +#define Duintp1(object) (object) +#define Duintp2(object) (object) + +#define Qintp0(object) (object) +#define Qintp1(object) (object) +#define Qintp2(object) (object) +#define Qintp3(object) (object) + + +/* + * These macros will be used specifically by the convert instructions. + * + * + * Single format macros + */ + +#define Sgl_to_dbl_exponent(src_exponent,dest) \ + Deposit_dexponent(dest,src_exponent+(DBL_BIAS-SGL_BIAS)) + +#define Sgl_to_dbl_mantissa(src_mantissa,destA,destB) \ + Deposit_dmantissap1(destA,src_mantissa>>3); \ + Dmantissap2(destB) = src_mantissa << 29 + +#define Sgl_isinexact_to_fix(sgl_value,exponent) \ + ((exponent < (SGL_P - 1)) ? \ + (Sall(sgl_value) << (SGL_EXP_LENGTH + 1 + exponent)) : FALSE) + +#define Int_isinexact_to_sgl(int_value) ((int_value << 33 - SGL_EXP_LENGTH) != 0) + +#define Sgl_roundnearest_from_int(int_value,sgl_value) \ + if (int_value & 1<<(SGL_EXP_LENGTH - 2)) /* round bit */ \ + if (((int_value << 34 - SGL_EXP_LENGTH) != 0) || Slow(sgl_value)) \ + Sall(sgl_value)++ + +#define Dint_isinexact_to_sgl(dint_valueA,dint_valueB) \ + (((Dintp1(dint_valueA) << 33 - SGL_EXP_LENGTH) != 0) || Dintp2(dint_valueB)) + +#define Sgl_roundnearest_from_dint(dint_valueA,dint_valueB,sgl_value) \ + if (Dintp1(dint_valueA) & 1<<(SGL_EXP_LENGTH - 2)) \ + if (((Dintp1(dint_valueA) << 34 - SGL_EXP_LENGTH) != 0) || \ + Dintp2(dint_valueB) || Slow(sgl_value)) Sall(sgl_value)++ + +#define Dint_isinexact_to_dbl(dint_value) \ + (Dintp2(dint_value) << 33 - DBL_EXP_LENGTH) + +#define Dbl_roundnearest_from_dint(dint_opndB,dbl_opndA,dbl_opndB) \ + if (Dintp2(dint_opndB) & 1<<(DBL_EXP_LENGTH - 2)) \ + if ((Dintp2(dint_opndB) << 34 - DBL_EXP_LENGTH) || Dlowp2(dbl_opndB)) \ + if ((++Dallp2(dbl_opndB))==0) Dallp1(dbl_opndA)++ + +#define Sgl_isone_roundbit(sgl_value,exponent) \ + ((Sall(sgl_value) << (SGL_EXP_LENGTH + 1 + exponent)) >> 31) + +#define Sgl_isone_stickybit(sgl_value,exponent) \ + (exponent < (SGL_P - 2) ? \ + Sall(sgl_value) << (SGL_EXP_LENGTH + 2 + exponent) : FALSE) + + +/* + * Double format macros + */ + +#define Dbl_to_sgl_exponent(src_exponent,dest) \ + dest = src_exponent + (SGL_BIAS - DBL_BIAS) + +#define Dbl_to_sgl_mantissa(srcA,srcB,dest,inexact,guard,sticky,odd) \ + Shiftdouble(Dmantissap1(srcA),Dmantissap2(srcB),29,dest); \ + guard = Dbit3p2(srcB); \ + sticky = Dallp2(srcB)<<4; \ + inexact = guard | sticky; \ + odd = Dbit2p2(srcB) + +#define Dbl_to_sgl_denormalized(srcA,srcB,exp,dest,inexact,guard,sticky,odd,tiny) \ + Deposit_dexponent(srcA,1); \ + tiny = TRUE; \ + if (exp >= -2) { \ + if (exp == 0) { \ + inexact = Dallp2(srcB) << 3; \ + guard = inexact >> 31; \ + sticky = inexact << 1; \ + Shiftdouble(Dmantissap1(srcA),Dmantissap2(srcB),29,dest); \ + odd = dest << 31; \ + if (inexact) { \ + switch(Rounding_mode()) { \ + case ROUNDPLUS: \ + if (Dbl_iszero_sign(srcA)) { \ + dest++; \ + if (Sgl_isone_hidden(dest)) \ + tiny = FALSE; \ + dest--; \ + } \ + break; \ + case ROUNDMINUS: \ + if (Dbl_isone_sign(srcA)) { \ + dest++; \ + if (Sgl_isone_hidden(dest)) \ + tiny = FALSE; \ + dest--; \ + } \ + break; \ + case ROUNDNEAREST: \ + if (guard && (sticky || odd)) { \ + dest++; \ + if (Sgl_isone_hidden(dest)) \ + tiny = FALSE; \ + dest--; \ + } \ + break; \ + } \ + } \ + /* shift right by one to get correct result */ \ + guard = odd; \ + sticky = inexact; \ + inexact |= guard; \ + dest >>= 1; \ + Deposit_dsign(srcA,0); \ + Shiftdouble(Dallp1(srcA),Dallp2(srcB),30,dest); \ + odd = dest << 31; \ + } \ + else { \ + inexact = Dallp2(srcB) << (2 + exp); \ + guard = inexact >> 31; \ + sticky = inexact << 1; \ + Deposit_dsign(srcA,0); \ + if (exp == -2) dest = Dallp1(srcA); \ + else Variable_shift_double(Dallp1(srcA),Dallp2(srcB),30-exp,dest); \ + odd = dest << 31; \ + } \ + } \ + else { \ + Deposit_dsign(srcA,0); \ + if (exp > (1 - SGL_P)) { \ + dest = Dallp1(srcA) >> (- 2 - exp); \ + inexact = Dallp1(srcA) << (34 + exp); \ + guard = inexact >> 31; \ + sticky = (inexact << 1) | Dallp2(srcB); \ + inexact |= Dallp2(srcB); \ + odd = dest << 31; \ + } \ + else { \ + dest = 0; \ + inexact = Dallp1(srcA) | Dallp2(srcB); \ + if (exp == (1 - SGL_P)) { \ + guard = Dhidden(srcA); \ + sticky = Dmantissap1(srcA) | Dallp2(srcB); \ + } \ + else { \ + guard = 0; \ + sticky = inexact; \ + } \ + odd = 0; \ + } \ + } \ + exp = 0 + +#define Dbl_isinexact_to_fix(dbl_valueA,dbl_valueB,exponent) \ + (exponent < (DBL_P-33) ? \ + Dallp2(dbl_valueB) || Dallp1(dbl_valueA) << (DBL_EXP_LENGTH+1+exponent) : \ + (exponent < (DBL_P-1) ? Dallp2(dbl_valueB) << (exponent + (33-DBL_P)) : \ + FALSE)) + +#define Dbl_isoverflow_to_int(exponent,dbl_valueA,dbl_valueB) \ + ((exponent > SGL_FX_MAX_EXP + 1) || Dsign(dbl_valueA)==0 || \ + Dmantissap1(dbl_valueA)!=0 || (Dallp2(dbl_valueB)>>21)!=0 ) + +#define Dbl_isone_roundbit(dbl_valueA,dbl_valueB,exponent) \ + ((exponent < (DBL_P - 33) ? \ + Dallp1(dbl_valueA) >> ((30 - DBL_EXP_LENGTH) - exponent) : \ + Dallp2(dbl_valueB) >> ((DBL_P - 2) - exponent)) & 1) + +#define Dbl_isone_stickybit(dbl_valueA,dbl_valueB,exponent) \ + (exponent < (DBL_P-34) ? \ + (Dallp2(dbl_valueB) || Dallp1(dbl_valueA)<<(DBL_EXP_LENGTH+2+exponent)) : \ + (exponent<(DBL_P-2) ? (Dallp2(dbl_valueB) << (exponent + (34-DBL_P))) : \ + FALSE)) + + +/* Int macros */ + +#define Int_from_sgl_mantissa(sgl_value,exponent) \ + Sall(sgl_value) = \ + (unsigned)(Sall(sgl_value) << SGL_EXP_LENGTH)>>(31 - exponent) + +#define Int_from_dbl_mantissa(dbl_valueA,dbl_valueB,exponent) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),22,Dallp1(dbl_valueA)); \ + if (exponent < 31) Dallp1(dbl_valueA) >>= 30 - exponent; \ + else Dallp1(dbl_valueA) <<= 1 + +#define Int_negate(int_value) int_value = -int_value + + +/* Dint macros */ + +#define Dint_from_sgl_mantissa(sgl_value,exponent,dresultA,dresultB) \ + {Sall(sgl_value) <<= SGL_EXP_LENGTH; /* left-justify */ \ + if (exponent <= 31) { \ + Dintp1(dresultA) = 0; \ + Dintp2(dresultB) = (unsigned)Sall(sgl_value) >> (31 - exponent); \ + } \ + else { \ + Dintp1(dresultA) = Sall(sgl_value) >> (63 - exponent); \ + Dintp2(dresultB) = Sall(sgl_value) << (exponent - 31); \ + }} + + +#define Dint_from_dbl_mantissa(dbl_valueA,dbl_valueB,exponent,destA,destB) \ + {if (exponent < 32) { \ + Dintp1(destA) = 0; \ + if (exponent <= 20) \ + Dintp2(destB) = Dallp1(dbl_valueA) >> 20-exponent; \ + else Variable_shift_double(Dallp1(dbl_valueA),Dallp2(dbl_valueB), \ + 52-exponent,Dintp2(destB)); \ + } \ + else { \ + if (exponent <= 52) { \ + Dintp1(destA) = Dallp1(dbl_valueA) >> 52-exponent; \ + if (exponent == 52) Dintp2(destB) = Dallp2(dbl_valueB); \ + else Variable_shift_double(Dallp1(dbl_valueA),Dallp2(dbl_valueB), \ + 52-exponent,Dintp2(destB)); \ + } \ + else { \ + Variable_shift_double(Dallp1(dbl_valueA),Dallp2(dbl_valueB), \ + 84-exponent,Dintp1(destA)); \ + Dintp2(destB) = Dallp2(dbl_valueB) << exponent-52; \ + } \ + }} + +#define Dint_setzero(dresultA,dresultB) \ + Dintp1(dresultA) = 0; \ + Dintp2(dresultB) = 0 + +#define Dint_setone_sign(dresultA,dresultB) \ + Dintp1(dresultA) = ~Dintp1(dresultA); \ + if ((Dintp2(dresultB) = -Dintp2(dresultB)) == 0) Dintp1(dresultA)++ + +#define Dint_set_minint(dresultA,dresultB) \ + Dintp1(dresultA) = (unsigned int)1<<31; \ + Dintp2(dresultB) = 0 + +#define Dint_isone_lowp2(dresultB) (Dintp2(dresultB) & 01) + +#define Dint_increment(dresultA,dresultB) \ + if ((++Dintp2(dresultB))==0) Dintp1(dresultA)++ + +#define Dint_decrement(dresultA,dresultB) \ + if ((Dintp2(dresultB)--)==0) Dintp1(dresultA)-- + +#define Dint_negate(dresultA,dresultB) \ + Dintp1(dresultA) = ~Dintp1(dresultA); \ + if ((Dintp2(dresultB) = -Dintp2(dresultB))==0) Dintp1(dresultA)++ + +#define Dint_copyfromptr(src,destA,destB) \ + Dintp1(destA) = src->wd0; \ + Dintp2(destB) = src->wd1 +#define Dint_copytoptr(srcA,srcB,dest) \ + dest->wd0 = Dintp1(srcA); \ + dest->wd1 = Dintp2(srcB) + + +/* other macros */ + +#define Find_ms_one_bit(value, position) \ + { \ + int var; \ + for (var=8; var >=1; var >>= 1) { \ + if (value >> 32 - position) \ + position -= var; \ + else position += var; \ + } \ + if ((value >> 32 - position) == 0) \ + position--; \ + else position -= 2; \ + } + + +/* + * Unsigned int macros + */ +#define Duint_copyfromptr(src,destA,destB) \ + Dint_copyfromptr(src,destA,destB) +#define Duint_copytoptr(srcA,srcB,dest) \ + Dint_copytoptr(srcA,srcB,dest) + +#define Suint_isinexact_to_sgl(int_value) \ + (int_value << 32 - SGL_EXP_LENGTH) + +#define Sgl_roundnearest_from_suint(suint_value,sgl_value) \ + if (suint_value & 1<<(SGL_EXP_LENGTH - 1)) /* round bit */ \ + if ((suint_value << 33 - SGL_EXP_LENGTH) || Slow(sgl_value)) \ + Sall(sgl_value)++ + +#define Duint_isinexact_to_sgl(duint_valueA,duint_valueB) \ + ((Duintp1(duint_valueA) << 32 - SGL_EXP_LENGTH) || Duintp2(duint_valueB)) + +#define Sgl_roundnearest_from_duint(duint_valueA,duint_valueB,sgl_value) \ + if (Duintp1(duint_valueA) & 1<<(SGL_EXP_LENGTH - 1)) \ + if ((Duintp1(duint_valueA) << 33 - SGL_EXP_LENGTH) || \ + Duintp2(duint_valueB) || Slow(sgl_value)) Sall(sgl_value)++ + +#define Duint_isinexact_to_dbl(duint_value) \ + (Duintp2(duint_value) << 32 - DBL_EXP_LENGTH) + +#define Dbl_roundnearest_from_duint(duint_opndB,dbl_opndA,dbl_opndB) \ + if (Duintp2(duint_opndB) & 1<<(DBL_EXP_LENGTH - 1)) \ + if ((Duintp2(duint_opndB) << 33 - DBL_EXP_LENGTH) || Dlowp2(dbl_opndB)) \ + if ((++Dallp2(dbl_opndB))==0) Dallp1(dbl_opndA)++ + +#define Suint_from_sgl_mantissa(src,exponent,result) \ + Sall(result) = (unsigned)(Sall(src) << SGL_EXP_LENGTH)>>(31 - exponent) + +#define Sgl_isinexact_to_unsigned(sgl_value,exponent) \ + Sgl_isinexact_to_fix(sgl_value,exponent) + +#define Duint_from_sgl_mantissa(sgl_value,exponent,dresultA,dresultB) \ + {unsigned int val = Sall(sgl_value) << SGL_EXP_LENGTH; \ + if (exponent <= 31) { \ + Dintp1(dresultA) = 0; \ + Dintp2(dresultB) = val >> (31 - exponent); \ + } \ + else { \ + Dintp1(dresultA) = val >> (63 - exponent); \ + Dintp2(dresultB) = exponent <= 62 ? val << (exponent - 31) : 0; \ + } \ + } + +#define Duint_setzero(dresultA,dresultB) \ + Dint_setzero(dresultA,dresultB) + +#define Duint_increment(dresultA,dresultB) Dint_increment(dresultA,dresultB) + +#define Duint_isone_lowp2(dresultB) Dint_isone_lowp2(dresultB) + +#define Suint_from_dbl_mantissa(srcA,srcB,exponent,dest) \ + Shiftdouble(Dallp1(srcA),Dallp2(srcB),21,dest); \ + dest = (unsigned)dest >> 31 - exponent + +#define Dbl_isinexact_to_unsigned(dbl_valueA,dbl_valueB,exponent) \ + Dbl_isinexact_to_fix(dbl_valueA,dbl_valueB,exponent) + +#define Duint_from_dbl_mantissa(dbl_valueA,dbl_valueB,exponent,destA,destB) \ + Dint_from_dbl_mantissa(dbl_valueA,dbl_valueB,exponent,destA,destB) diff --git a/arch/parisc/math-emu/dbl_float.h b/arch/parisc/math-emu/dbl_float.h new file mode 100644 index 000000000..e57fee8e1 --- /dev/null +++ b/arch/parisc/math-emu/dbl_float.h @@ -0,0 +1,834 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +#ifdef __NO_PA_HDRS + PA header file -- do not include this header file for non-PA builds. +#endif + +/* 32-bit word grabbing functions */ +#define Dbl_firstword(value) Dallp1(value) +#define Dbl_secondword(value) Dallp2(value) +#define Dbl_thirdword(value) dummy_location +#define Dbl_fourthword(value) dummy_location + +#define Dbl_sign(object) Dsign(object) +#define Dbl_exponent(object) Dexponent(object) +#define Dbl_signexponent(object) Dsignexponent(object) +#define Dbl_mantissap1(object) Dmantissap1(object) +#define Dbl_mantissap2(object) Dmantissap2(object) +#define Dbl_exponentmantissap1(object) Dexponentmantissap1(object) +#define Dbl_allp1(object) Dallp1(object) +#define Dbl_allp2(object) Dallp2(object) + +/* dbl_and_signs ANDs the sign bits of each argument and puts the result + * into the first argument. dbl_or_signs ors those same sign bits */ +#define Dbl_and_signs( src1dst, src2) \ + Dallp1(src1dst) = (Dallp1(src2)|~((unsigned int)1<<31)) & Dallp1(src1dst) +#define Dbl_or_signs( src1dst, src2) \ + Dallp1(src1dst) = (Dallp1(src2)&((unsigned int)1<<31)) | Dallp1(src1dst) + +/* The hidden bit is always the low bit of the exponent */ +#define Dbl_clear_exponent_set_hidden(srcdst) Deposit_dexponent(srcdst,1) +#define Dbl_clear_signexponent_set_hidden(srcdst) \ + Deposit_dsignexponent(srcdst,1) +#define Dbl_clear_sign(srcdst) Dallp1(srcdst) &= ~((unsigned int)1<<31) +#define Dbl_clear_signexponent(srcdst) \ + Dallp1(srcdst) &= Dmantissap1((unsigned int)-1) + +/* Exponent field for doubles has already been cleared and may be + * included in the shift. Here we need to generate two double width + * variable shifts. The insignificant bits can be ignored. + * MTSAR f(varamount) + * VSHD srcdst.high,srcdst.low => srcdst.low + * VSHD 0,srcdst.high => srcdst.high + * This is very difficult to model with C expressions since the shift amount + * could exceed 32. */ +/* varamount must be less than 64 */ +#define Dbl_rightshift(srcdstA, srcdstB, varamount) \ + {if((varamount) >= 32) { \ + Dallp2(srcdstB) = Dallp1(srcdstA) >> (varamount-32); \ + Dallp1(srcdstA)=0; \ + } \ + else if(varamount > 0) { \ + Variable_shift_double(Dallp1(srcdstA), Dallp2(srcdstB), \ + (varamount), Dallp2(srcdstB)); \ + Dallp1(srcdstA) >>= varamount; \ + } } +/* varamount must be less than 64 */ +#define Dbl_rightshift_exponentmantissa(srcdstA, srcdstB, varamount) \ + {if((varamount) >= 32) { \ + Dallp2(srcdstB) = Dexponentmantissap1(srcdstA) >> (varamount-32); \ + Dallp1(srcdstA) &= ((unsigned int)1<<31); /* clear expmant field */ \ + } \ + else if(varamount > 0) { \ + Variable_shift_double(Dexponentmantissap1(srcdstA), Dallp2(srcdstB), \ + (varamount), Dallp2(srcdstB)); \ + Deposit_dexponentmantissap1(srcdstA, \ + (Dexponentmantissap1(srcdstA)>>varamount)); \ + } } +/* varamount must be less than 64 */ +#define Dbl_leftshift(srcdstA, srcdstB, varamount) \ + {if((varamount) >= 32) { \ + Dallp1(srcdstA) = Dallp2(srcdstB) << (varamount-32); \ + Dallp2(srcdstB)=0; \ + } \ + else { \ + if ((varamount) > 0) { \ + Dallp1(srcdstA) = (Dallp1(srcdstA) << (varamount)) | \ + (Dallp2(srcdstB) >> (32-(varamount))); \ + Dallp2(srcdstB) <<= varamount; \ + } \ + } } +#define Dbl_leftshiftby1_withextent(lefta,leftb,right,resulta,resultb) \ + Shiftdouble(Dallp1(lefta), Dallp2(leftb), 31, Dallp1(resulta)); \ + Shiftdouble(Dallp2(leftb), Extall(right), 31, Dallp2(resultb)) + +#define Dbl_rightshiftby1_withextent(leftb,right,dst) \ + Extall(dst) = (Dallp2(leftb) << 31) | ((unsigned int)Extall(right) >> 1) | \ + Extlow(right) + +#define Dbl_arithrightshiftby1(srcdstA,srcdstB) \ + Shiftdouble(Dallp1(srcdstA),Dallp2(srcdstB),1,Dallp2(srcdstB));\ + Dallp1(srcdstA) = (int)Dallp1(srcdstA) >> 1 + +/* Sign extend the sign bit with an integer destination */ +#define Dbl_signextendedsign(value) Dsignedsign(value) + +#define Dbl_isone_hidden(dbl_value) (Is_dhidden(dbl_value)!=0) +/* Singles and doubles may include the sign and exponent fields. The + * hidden bit and the hidden overflow must be included. */ +#define Dbl_increment(dbl_valueA,dbl_valueB) \ + if( (Dallp2(dbl_valueB) += 1) == 0 ) Dallp1(dbl_valueA) += 1 +#define Dbl_increment_mantissa(dbl_valueA,dbl_valueB) \ + if( (Dmantissap2(dbl_valueB) += 1) == 0 ) \ + Deposit_dmantissap1(dbl_valueA,dbl_valueA+1) +#define Dbl_decrement(dbl_valueA,dbl_valueB) \ + if( Dallp2(dbl_valueB) == 0 ) Dallp1(dbl_valueA) -= 1; \ + Dallp2(dbl_valueB) -= 1 + +#define Dbl_isone_sign(dbl_value) (Is_dsign(dbl_value)!=0) +#define Dbl_isone_hiddenoverflow(dbl_value) (Is_dhiddenoverflow(dbl_value)!=0) +#define Dbl_isone_lowmantissap1(dbl_valueA) (Is_dlowp1(dbl_valueA)!=0) +#define Dbl_isone_lowmantissap2(dbl_valueB) (Is_dlowp2(dbl_valueB)!=0) +#define Dbl_isone_signaling(dbl_value) (Is_dsignaling(dbl_value)!=0) +#define Dbl_is_signalingnan(dbl_value) (Dsignalingnan(dbl_value)==0xfff) +#define Dbl_isnotzero(dbl_valueA,dbl_valueB) \ + (Dallp1(dbl_valueA) || Dallp2(dbl_valueB)) +#define Dbl_isnotzero_hiddenhigh7mantissa(dbl_value) \ + (Dhiddenhigh7mantissa(dbl_value)!=0) +#define Dbl_isnotzero_exponent(dbl_value) (Dexponent(dbl_value)!=0) +#define Dbl_isnotzero_mantissa(dbl_valueA,dbl_valueB) \ + (Dmantissap1(dbl_valueA) || Dmantissap2(dbl_valueB)) +#define Dbl_isnotzero_mantissap1(dbl_valueA) (Dmantissap1(dbl_valueA)!=0) +#define Dbl_isnotzero_mantissap2(dbl_valueB) (Dmantissap2(dbl_valueB)!=0) +#define Dbl_isnotzero_exponentmantissa(dbl_valueA,dbl_valueB) \ + (Dexponentmantissap1(dbl_valueA) || Dmantissap2(dbl_valueB)) +#define Dbl_isnotzero_low4p2(dbl_value) (Dlow4p2(dbl_value)!=0) +#define Dbl_iszero(dbl_valueA,dbl_valueB) (Dallp1(dbl_valueA)==0 && \ + Dallp2(dbl_valueB)==0) +#define Dbl_iszero_allp1(dbl_value) (Dallp1(dbl_value)==0) +#define Dbl_iszero_allp2(dbl_value) (Dallp2(dbl_value)==0) +#define Dbl_iszero_hidden(dbl_value) (Is_dhidden(dbl_value)==0) +#define Dbl_iszero_hiddenoverflow(dbl_value) (Is_dhiddenoverflow(dbl_value)==0) +#define Dbl_iszero_hiddenhigh3mantissa(dbl_value) \ + (Dhiddenhigh3mantissa(dbl_value)==0) +#define Dbl_iszero_hiddenhigh7mantissa(dbl_value) \ + (Dhiddenhigh7mantissa(dbl_value)==0) +#define Dbl_iszero_sign(dbl_value) (Is_dsign(dbl_value)==0) +#define Dbl_iszero_exponent(dbl_value) (Dexponent(dbl_value)==0) +#define Dbl_iszero_mantissa(dbl_valueA,dbl_valueB) \ + (Dmantissap1(dbl_valueA)==0 && Dmantissap2(dbl_valueB)==0) +#define Dbl_iszero_exponentmantissa(dbl_valueA,dbl_valueB) \ + (Dexponentmantissap1(dbl_valueA)==0 && Dmantissap2(dbl_valueB)==0) +#define Dbl_isinfinity_exponent(dbl_value) \ + (Dexponent(dbl_value)==DBL_INFINITY_EXPONENT) +#define Dbl_isnotinfinity_exponent(dbl_value) \ + (Dexponent(dbl_value)!=DBL_INFINITY_EXPONENT) +#define Dbl_isinfinity(dbl_valueA,dbl_valueB) \ + (Dexponent(dbl_valueA)==DBL_INFINITY_EXPONENT && \ + Dmantissap1(dbl_valueA)==0 && Dmantissap2(dbl_valueB)==0) +#define Dbl_isnan(dbl_valueA,dbl_valueB) \ + (Dexponent(dbl_valueA)==DBL_INFINITY_EXPONENT && \ + (Dmantissap1(dbl_valueA)!=0 || Dmantissap2(dbl_valueB)!=0)) +#define Dbl_isnotnan(dbl_valueA,dbl_valueB) \ + (Dexponent(dbl_valueA)!=DBL_INFINITY_EXPONENT || \ + (Dmantissap1(dbl_valueA)==0 && Dmantissap2(dbl_valueB)==0)) + +#define Dbl_islessthan(dbl_op1a,dbl_op1b,dbl_op2a,dbl_op2b) \ + (Dallp1(dbl_op1a) < Dallp1(dbl_op2a) || \ + (Dallp1(dbl_op1a) == Dallp1(dbl_op2a) && \ + Dallp2(dbl_op1b) < Dallp2(dbl_op2b))) +#define Dbl_isgreaterthan(dbl_op1a,dbl_op1b,dbl_op2a,dbl_op2b) \ + (Dallp1(dbl_op1a) > Dallp1(dbl_op2a) || \ + (Dallp1(dbl_op1a) == Dallp1(dbl_op2a) && \ + Dallp2(dbl_op1b) > Dallp2(dbl_op2b))) +#define Dbl_isnotlessthan(dbl_op1a,dbl_op1b,dbl_op2a,dbl_op2b) \ + (Dallp1(dbl_op1a) > Dallp1(dbl_op2a) || \ + (Dallp1(dbl_op1a) == Dallp1(dbl_op2a) && \ + Dallp2(dbl_op1b) >= Dallp2(dbl_op2b))) +#define Dbl_isnotgreaterthan(dbl_op1a,dbl_op1b,dbl_op2a,dbl_op2b) \ + (Dallp1(dbl_op1a) < Dallp1(dbl_op2a) || \ + (Dallp1(dbl_op1a) == Dallp1(dbl_op2a) && \ + Dallp2(dbl_op1b) <= Dallp2(dbl_op2b))) +#define Dbl_isequal(dbl_op1a,dbl_op1b,dbl_op2a,dbl_op2b) \ + ((Dallp1(dbl_op1a) == Dallp1(dbl_op2a)) && \ + (Dallp2(dbl_op1b) == Dallp2(dbl_op2b))) + +#define Dbl_leftshiftby8(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),24,Dallp1(dbl_valueA)); \ + Dallp2(dbl_valueB) <<= 8 +#define Dbl_leftshiftby7(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),25,Dallp1(dbl_valueA)); \ + Dallp2(dbl_valueB) <<= 7 +#define Dbl_leftshiftby4(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),28,Dallp1(dbl_valueA)); \ + Dallp2(dbl_valueB) <<= 4 +#define Dbl_leftshiftby3(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),29,Dallp1(dbl_valueA)); \ + Dallp2(dbl_valueB) <<= 3 +#define Dbl_leftshiftby2(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),30,Dallp1(dbl_valueA)); \ + Dallp2(dbl_valueB) <<= 2 +#define Dbl_leftshiftby1(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),31,Dallp1(dbl_valueA)); \ + Dallp2(dbl_valueB) <<= 1 + +#define Dbl_rightshiftby8(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),8,Dallp2(dbl_valueB)); \ + Dallp1(dbl_valueA) >>= 8 +#define Dbl_rightshiftby4(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),4,Dallp2(dbl_valueB)); \ + Dallp1(dbl_valueA) >>= 4 +#define Dbl_rightshiftby2(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),2,Dallp2(dbl_valueB)); \ + Dallp1(dbl_valueA) >>= 2 +#define Dbl_rightshiftby1(dbl_valueA,dbl_valueB) \ + Shiftdouble(Dallp1(dbl_valueA),Dallp2(dbl_valueB),1,Dallp2(dbl_valueB)); \ + Dallp1(dbl_valueA) >>= 1 + +/* This magnitude comparison uses the signless first words and + * the regular part2 words. The comparison is graphically: + * + * 1st greater? ------------- + * | + * 1st less?-----------------+--------- + * | | + * 2nd greater or equal----->| | + * False True + */ +#define Dbl_ismagnitudeless(leftB,rightB,signlessleft,signlessright) \ + ((signlessleft <= signlessright) && \ + ( (signlessleft < signlessright) || (Dallp2(leftB)<Dallp2(rightB)) )) + +#define Dbl_copytoint_exponentmantissap1(src,dest) \ + dest = Dexponentmantissap1(src) + +/* A quiet NaN has the high mantissa bit clear and at least on other (in this + * case the adjacent bit) bit set. */ +#define Dbl_set_quiet(dbl_value) Deposit_dhigh2mantissa(dbl_value,1) +#define Dbl_set_exponent(dbl_value, exp) Deposit_dexponent(dbl_value,exp) + +#define Dbl_set_mantissa(desta,destb,valuea,valueb) \ + Deposit_dmantissap1(desta,valuea); \ + Dmantissap2(destb) = Dmantissap2(valueb) +#define Dbl_set_mantissap1(desta,valuea) \ + Deposit_dmantissap1(desta,valuea) +#define Dbl_set_mantissap2(destb,valueb) \ + Dmantissap2(destb) = Dmantissap2(valueb) + +#define Dbl_set_exponentmantissa(desta,destb,valuea,valueb) \ + Deposit_dexponentmantissap1(desta,valuea); \ + Dmantissap2(destb) = Dmantissap2(valueb) +#define Dbl_set_exponentmantissap1(dest,value) \ + Deposit_dexponentmantissap1(dest,value) + +#define Dbl_copyfromptr(src,desta,destb) \ + Dallp1(desta) = src->wd0; \ + Dallp2(destb) = src->wd1 +#define Dbl_copytoptr(srca,srcb,dest) \ + dest->wd0 = Dallp1(srca); \ + dest->wd1 = Dallp2(srcb) + +/* An infinity is represented with the max exponent and a zero mantissa */ +#define Dbl_setinfinity_exponent(dbl_value) \ + Deposit_dexponent(dbl_value,DBL_INFINITY_EXPONENT) +#define Dbl_setinfinity_exponentmantissa(dbl_valueA,dbl_valueB) \ + Deposit_dexponentmantissap1(dbl_valueA, \ + (DBL_INFINITY_EXPONENT << (32-(1+DBL_EXP_LENGTH)))); \ + Dmantissap2(dbl_valueB) = 0 +#define Dbl_setinfinitypositive(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) \ + = (DBL_INFINITY_EXPONENT << (32-(1+DBL_EXP_LENGTH))); \ + Dmantissap2(dbl_valueB) = 0 +#define Dbl_setinfinitynegative(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) = ((unsigned int)1<<31) | \ + (DBL_INFINITY_EXPONENT << (32-(1+DBL_EXP_LENGTH))); \ + Dmantissap2(dbl_valueB) = 0 +#define Dbl_setinfinity(dbl_valueA,dbl_valueB,sign) \ + Dallp1(dbl_valueA) = ((unsigned int)sign << 31) | \ + (DBL_INFINITY_EXPONENT << (32-(1+DBL_EXP_LENGTH))); \ + Dmantissap2(dbl_valueB) = 0 + +#define Dbl_sethigh4bits(dbl_value, extsign) Deposit_dhigh4p1(dbl_value,extsign) +#define Dbl_set_sign(dbl_value,sign) Deposit_dsign(dbl_value,sign) +#define Dbl_invert_sign(dbl_value) Deposit_dsign(dbl_value,~Dsign(dbl_value)) +#define Dbl_setone_sign(dbl_value) Deposit_dsign(dbl_value,1) +#define Dbl_setone_lowmantissap2(dbl_value) Deposit_dlowp2(dbl_value,1) +#define Dbl_setzero_sign(dbl_value) Dallp1(dbl_value) &= 0x7fffffff +#define Dbl_setzero_exponent(dbl_value) \ + Dallp1(dbl_value) &= 0x800fffff +#define Dbl_setzero_mantissa(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) &= 0xfff00000; \ + Dallp2(dbl_valueB) = 0 +#define Dbl_setzero_mantissap1(dbl_value) Dallp1(dbl_value) &= 0xfff00000 +#define Dbl_setzero_mantissap2(dbl_value) Dallp2(dbl_value) = 0 +#define Dbl_setzero_exponentmantissa(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) &= 0x80000000; \ + Dallp2(dbl_valueB) = 0 +#define Dbl_setzero_exponentmantissap1(dbl_valueA) \ + Dallp1(dbl_valueA) &= 0x80000000 +#define Dbl_setzero(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) = 0; Dallp2(dbl_valueB) = 0 +#define Dbl_setzerop1(dbl_value) Dallp1(dbl_value) = 0 +#define Dbl_setzerop2(dbl_value) Dallp2(dbl_value) = 0 +#define Dbl_setnegativezero(dbl_value) \ + Dallp1(dbl_value) = (unsigned int)1 << 31; Dallp2(dbl_value) = 0 +#define Dbl_setnegativezerop1(dbl_value) Dallp1(dbl_value) = (unsigned int)1<<31 + +/* Use the following macro for both overflow & underflow conditions */ +#define ovfl - +#define unfl + +#define Dbl_setwrapped_exponent(dbl_value,exponent,op) \ + Deposit_dexponent(dbl_value,(exponent op DBL_WRAP)) + +#define Dbl_setlargestpositive(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) = ((DBL_EMAX+DBL_BIAS) << (32-(1+DBL_EXP_LENGTH))) \ + | ((1<<(32-(1+DBL_EXP_LENGTH))) - 1 ); \ + Dallp2(dbl_valueB) = 0xFFFFFFFF +#define Dbl_setlargestnegative(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) = ((DBL_EMAX+DBL_BIAS) << (32-(1+DBL_EXP_LENGTH))) \ + | ((1<<(32-(1+DBL_EXP_LENGTH))) - 1 ) \ + | ((unsigned int)1<<31); \ + Dallp2(dbl_valueB) = 0xFFFFFFFF +#define Dbl_setlargest_exponentmantissa(dbl_valueA,dbl_valueB) \ + Deposit_dexponentmantissap1(dbl_valueA, \ + (((DBL_EMAX+DBL_BIAS) << (32-(1+DBL_EXP_LENGTH))) \ + | ((1<<(32-(1+DBL_EXP_LENGTH))) - 1 ))); \ + Dallp2(dbl_valueB) = 0xFFFFFFFF + +#define Dbl_setnegativeinfinity(dbl_valueA,dbl_valueB) \ + Dallp1(dbl_valueA) = ((1<<DBL_EXP_LENGTH) | DBL_INFINITY_EXPONENT) \ + << (32-(1+DBL_EXP_LENGTH)) ; \ + Dallp2(dbl_valueB) = 0 +#define Dbl_setlargest(dbl_valueA,dbl_valueB,sign) \ + Dallp1(dbl_valueA) = ((unsigned int)sign << 31) | \ + ((DBL_EMAX+DBL_BIAS) << (32-(1+DBL_EXP_LENGTH))) | \ + ((1 << (32-(1+DBL_EXP_LENGTH))) - 1 ); \ + Dallp2(dbl_valueB) = 0xFFFFFFFF + + +/* The high bit is always zero so arithmetic or logical shifts will work. */ +#define Dbl_right_align(srcdstA,srcdstB,shift,extent) \ + if( shift >= 32 ) \ + { \ + /* Big shift requires examining the portion shift off \ + the end to properly set inexact. */ \ + if(shift < 64) \ + { \ + if(shift > 32) \ + { \ + Variable_shift_double(Dallp1(srcdstA),Dallp2(srcdstB), \ + shift-32, Extall(extent)); \ + if(Dallp2(srcdstB) << 64 - (shift)) Ext_setone_low(extent); \ + } \ + else Extall(extent) = Dallp2(srcdstB); \ + Dallp2(srcdstB) = Dallp1(srcdstA) >> (shift - 32); \ + } \ + else \ + { \ + Extall(extent) = Dallp1(srcdstA); \ + if(Dallp2(srcdstB)) Ext_setone_low(extent); \ + Dallp2(srcdstB) = 0; \ + } \ + Dallp1(srcdstA) = 0; \ + } \ + else \ + { \ + /* Small alignment is simpler. Extension is easily set. */ \ + if (shift > 0) \ + { \ + Extall(extent) = Dallp2(srcdstB) << 32 - (shift); \ + Variable_shift_double(Dallp1(srcdstA),Dallp2(srcdstB),shift, \ + Dallp2(srcdstB)); \ + Dallp1(srcdstA) >>= shift; \ + } \ + else Extall(extent) = 0; \ + } + +/* + * Here we need to shift the result right to correct for an overshift + * (due to the exponent becoming negative) during normalization. + */ +#define Dbl_fix_overshift(srcdstA,srcdstB,shift,extent) \ + Extall(extent) = Dallp2(srcdstB) << 32 - (shift); \ + Dallp2(srcdstB) = (Dallp1(srcdstA) << 32 - (shift)) | \ + (Dallp2(srcdstB) >> (shift)); \ + Dallp1(srcdstA) = Dallp1(srcdstA) >> shift + +#define Dbl_hiddenhigh3mantissa(dbl_value) Dhiddenhigh3mantissa(dbl_value) +#define Dbl_hidden(dbl_value) Dhidden(dbl_value) +#define Dbl_lowmantissap2(dbl_value) Dlowp2(dbl_value) + +/* The left argument is never smaller than the right argument */ +#define Dbl_subtract(lefta,leftb,righta,rightb,resulta,resultb) \ + if( Dallp2(rightb) > Dallp2(leftb) ) Dallp1(lefta)--; \ + Dallp2(resultb) = Dallp2(leftb) - Dallp2(rightb); \ + Dallp1(resulta) = Dallp1(lefta) - Dallp1(righta) + +/* Subtract right augmented with extension from left augmented with zeros and + * store into result and extension. */ +#define Dbl_subtract_withextension(lefta,leftb,righta,rightb,extent,resulta,resultb) \ + Dbl_subtract(lefta,leftb,righta,rightb,resulta,resultb); \ + if( (Extall(extent) = 0-Extall(extent)) ) \ + { \ + if((Dallp2(resultb)--) == 0) Dallp1(resulta)--; \ + } + +#define Dbl_addition(lefta,leftb,righta,rightb,resulta,resultb) \ + /* If the sum of the low words is less than either source, then \ + * an overflow into the next word occurred. */ \ + Dallp1(resulta) = Dallp1(lefta) + Dallp1(righta); \ + if((Dallp2(resultb) = Dallp2(leftb) + Dallp2(rightb)) < Dallp2(rightb)) \ + Dallp1(resulta)++ + +#define Dbl_xortointp1(left,right,result) \ + result = Dallp1(left) XOR Dallp1(right) + +#define Dbl_xorfromintp1(left,right,result) \ + Dallp1(result) = left XOR Dallp1(right) + +#define Dbl_swap_lower(left,right) \ + Dallp2(left) = Dallp2(left) XOR Dallp2(right); \ + Dallp2(right) = Dallp2(left) XOR Dallp2(right); \ + Dallp2(left) = Dallp2(left) XOR Dallp2(right) + +/* Need to Initialize */ +#define Dbl_makequietnan(desta,destb) \ + Dallp1(desta) = ((DBL_EMAX+DBL_BIAS)+1)<< (32-(1+DBL_EXP_LENGTH)) \ + | (1<<(32-(1+DBL_EXP_LENGTH+2))); \ + Dallp2(destb) = 0 +#define Dbl_makesignalingnan(desta,destb) \ + Dallp1(desta) = ((DBL_EMAX+DBL_BIAS)+1)<< (32-(1+DBL_EXP_LENGTH)) \ + | (1<<(32-(1+DBL_EXP_LENGTH+1))); \ + Dallp2(destb) = 0 + +#define Dbl_normalize(dbl_opndA,dbl_opndB,exponent) \ + while(Dbl_iszero_hiddenhigh7mantissa(dbl_opndA)) { \ + Dbl_leftshiftby8(dbl_opndA,dbl_opndB); \ + exponent -= 8; \ + } \ + if(Dbl_iszero_hiddenhigh3mantissa(dbl_opndA)) { \ + Dbl_leftshiftby4(dbl_opndA,dbl_opndB); \ + exponent -= 4; \ + } \ + while(Dbl_iszero_hidden(dbl_opndA)) { \ + Dbl_leftshiftby1(dbl_opndA,dbl_opndB); \ + exponent -= 1; \ + } + +#define Twoword_add(src1dstA,src1dstB,src2A,src2B) \ + /* \ + * want this macro to generate: \ + * ADD src1dstB,src2B,src1dstB; \ + * ADDC src1dstA,src2A,src1dstA; \ + */ \ + if ((src1dstB) + (src2B) < (src1dstB)) Dallp1(src1dstA)++; \ + Dallp1(src1dstA) += (src2A); \ + Dallp2(src1dstB) += (src2B) + +#define Twoword_subtract(src1dstA,src1dstB,src2A,src2B) \ + /* \ + * want this macro to generate: \ + * SUB src1dstB,src2B,src1dstB; \ + * SUBB src1dstA,src2A,src1dstA; \ + */ \ + if ((src1dstB) < (src2B)) Dallp1(src1dstA)--; \ + Dallp1(src1dstA) -= (src2A); \ + Dallp2(src1dstB) -= (src2B) + +#define Dbl_setoverflow(resultA,resultB) \ + /* set result to infinity or largest number */ \ + switch (Rounding_mode()) { \ + case ROUNDPLUS: \ + if (Dbl_isone_sign(resultA)) { \ + Dbl_setlargestnegative(resultA,resultB); \ + } \ + else { \ + Dbl_setinfinitypositive(resultA,resultB); \ + } \ + break; \ + case ROUNDMINUS: \ + if (Dbl_iszero_sign(resultA)) { \ + Dbl_setlargestpositive(resultA,resultB); \ + } \ + else { \ + Dbl_setinfinitynegative(resultA,resultB); \ + } \ + break; \ + case ROUNDNEAREST: \ + Dbl_setinfinity_exponentmantissa(resultA,resultB); \ + break; \ + case ROUNDZERO: \ + Dbl_setlargest_exponentmantissa(resultA,resultB); \ + } + +#define Dbl_denormalize(opndp1,opndp2,exponent,guard,sticky,inexact) \ + Dbl_clear_signexponent_set_hidden(opndp1); \ + if (exponent >= (1-DBL_P)) { \ + if (exponent >= -31) { \ + guard = (Dallp2(opndp2) >> -exponent) & 1; \ + if (exponent < 0) sticky |= Dallp2(opndp2) << (32+exponent); \ + if (exponent > -31) { \ + Variable_shift_double(opndp1,opndp2,1-exponent,opndp2); \ + Dallp1(opndp1) >>= 1-exponent; \ + } \ + else { \ + Dallp2(opndp2) = Dallp1(opndp1); \ + Dbl_setzerop1(opndp1); \ + } \ + } \ + else { \ + guard = (Dallp1(opndp1) >> -32-exponent) & 1; \ + if (exponent == -32) sticky |= Dallp2(opndp2); \ + else sticky |= (Dallp2(opndp2) | Dallp1(opndp1) << 64+exponent); \ + Dallp2(opndp2) = Dallp1(opndp1) >> -31-exponent; \ + Dbl_setzerop1(opndp1); \ + } \ + inexact = guard | sticky; \ + } \ + else { \ + guard = 0; \ + sticky |= (Dallp1(opndp1) | Dallp2(opndp2)); \ + Dbl_setzero(opndp1,opndp2); \ + inexact = sticky; \ + } + +/* + * The fused multiply add instructions requires a double extended format, + * with 106 bits of mantissa. + */ +#define DBLEXT_THRESHOLD 106 + +#define Dblext_setzero(valA,valB,valC,valD) \ + Dextallp1(valA) = 0; Dextallp2(valB) = 0; \ + Dextallp3(valC) = 0; Dextallp4(valD) = 0 + + +#define Dblext_isnotzero_mantissap3(valC) (Dextallp3(valC)!=0) +#define Dblext_isnotzero_mantissap4(valD) (Dextallp3(valD)!=0) +#define Dblext_isone_lowp2(val) (Dextlowp2(val)!=0) +#define Dblext_isone_highp3(val) (Dexthighp3(val)!=0) +#define Dblext_isnotzero_low31p3(val) (Dextlow31p3(val)!=0) +#define Dblext_iszero(valA,valB,valC,valD) (Dextallp1(valA)==0 && \ + Dextallp2(valB)==0 && Dextallp3(valC)==0 && Dextallp4(valD)==0) + +#define Dblext_copy(srca,srcb,srcc,srcd,desta,destb,destc,destd) \ + Dextallp1(desta) = Dextallp4(srca); \ + Dextallp2(destb) = Dextallp4(srcb); \ + Dextallp3(destc) = Dextallp4(srcc); \ + Dextallp4(destd) = Dextallp4(srcd) + +#define Dblext_swap_lower(leftp2,leftp3,leftp4,rightp2,rightp3,rightp4) \ + Dextallp2(leftp2) = Dextallp2(leftp2) XOR Dextallp2(rightp2); \ + Dextallp2(rightp2) = Dextallp2(leftp2) XOR Dextallp2(rightp2); \ + Dextallp2(leftp2) = Dextallp2(leftp2) XOR Dextallp2(rightp2); \ + Dextallp3(leftp3) = Dextallp3(leftp3) XOR Dextallp3(rightp3); \ + Dextallp3(rightp3) = Dextallp3(leftp3) XOR Dextallp3(rightp3); \ + Dextallp3(leftp3) = Dextallp3(leftp3) XOR Dextallp3(rightp3); \ + Dextallp4(leftp4) = Dextallp4(leftp4) XOR Dextallp4(rightp4); \ + Dextallp4(rightp4) = Dextallp4(leftp4) XOR Dextallp4(rightp4); \ + Dextallp4(leftp4) = Dextallp4(leftp4) XOR Dextallp4(rightp4) + +#define Dblext_setone_lowmantissap4(dbl_value) Deposit_dextlowp4(dbl_value,1) + +/* The high bit is always zero so arithmetic or logical shifts will work. */ +#define Dblext_right_align(srcdstA,srcdstB,srcdstC,srcdstD,shift) \ + {int shiftamt, sticky; \ + shiftamt = shift % 32; \ + sticky = 0; \ + switch (shift/32) { \ + case 0: if (shiftamt > 0) { \ + sticky = Dextallp4(srcdstD) << 32 - (shiftamt); \ + Variable_shift_double(Dextallp3(srcdstC), \ + Dextallp4(srcdstD),shiftamt,Dextallp4(srcdstD)); \ + Variable_shift_double(Dextallp2(srcdstB), \ + Dextallp3(srcdstC),shiftamt,Dextallp3(srcdstC)); \ + Variable_shift_double(Dextallp1(srcdstA), \ + Dextallp2(srcdstB),shiftamt,Dextallp2(srcdstB)); \ + Dextallp1(srcdstA) >>= shiftamt; \ + } \ + break; \ + case 1: if (shiftamt > 0) { \ + sticky = (Dextallp3(srcdstC) << 31 - shiftamt) | \ + Dextallp4(srcdstD); \ + Variable_shift_double(Dextallp2(srcdstB), \ + Dextallp3(srcdstC),shiftamt,Dextallp4(srcdstD)); \ + Variable_shift_double(Dextallp1(srcdstA), \ + Dextallp2(srcdstB),shiftamt,Dextallp3(srcdstC)); \ + } \ + else { \ + sticky = Dextallp4(srcdstD); \ + Dextallp4(srcdstD) = Dextallp3(srcdstC); \ + Dextallp3(srcdstC) = Dextallp2(srcdstB); \ + } \ + Dextallp2(srcdstB) = Dextallp1(srcdstA) >> shiftamt; \ + Dextallp1(srcdstA) = 0; \ + break; \ + case 2: if (shiftamt > 0) { \ + sticky = (Dextallp2(srcdstB) << 31 - shiftamt) | \ + Dextallp3(srcdstC) | Dextallp4(srcdstD); \ + Variable_shift_double(Dextallp1(srcdstA), \ + Dextallp2(srcdstB),shiftamt,Dextallp4(srcdstD)); \ + } \ + else { \ + sticky = Dextallp3(srcdstC) | Dextallp4(srcdstD); \ + Dextallp4(srcdstD) = Dextallp2(srcdstB); \ + } \ + Dextallp3(srcdstC) = Dextallp1(srcdstA) >> shiftamt; \ + Dextallp1(srcdstA) = Dextallp2(srcdstB) = 0; \ + break; \ + case 3: if (shiftamt > 0) { \ + sticky = (Dextallp1(srcdstA) << 31 - shiftamt) | \ + Dextallp2(srcdstB) | Dextallp3(srcdstC) | \ + Dextallp4(srcdstD); \ + } \ + else { \ + sticky = Dextallp2(srcdstB) | Dextallp3(srcdstC) | \ + Dextallp4(srcdstD); \ + } \ + Dextallp4(srcdstD) = Dextallp1(srcdstA) >> shiftamt; \ + Dextallp1(srcdstA) = Dextallp2(srcdstB) = 0; \ + Dextallp3(srcdstC) = 0; \ + break; \ + } \ + if (sticky) Dblext_setone_lowmantissap4(srcdstD); \ + } + +/* The left argument is never smaller than the right argument */ +#define Dblext_subtract(lefta,leftb,leftc,leftd,righta,rightb,rightc,rightd,resulta,resultb,resultc,resultd) \ + if( Dextallp4(rightd) > Dextallp4(leftd) ) \ + if( (Dextallp3(leftc)--) == 0) \ + if( (Dextallp2(leftb)--) == 0) Dextallp1(lefta)--; \ + Dextallp4(resultd) = Dextallp4(leftd) - Dextallp4(rightd); \ + if( Dextallp3(rightc) > Dextallp3(leftc) ) \ + if( (Dextallp2(leftb)--) == 0) Dextallp1(lefta)--; \ + Dextallp3(resultc) = Dextallp3(leftc) - Dextallp3(rightc); \ + if( Dextallp2(rightb) > Dextallp2(leftb) ) Dextallp1(lefta)--; \ + Dextallp2(resultb) = Dextallp2(leftb) - Dextallp2(rightb); \ + Dextallp1(resulta) = Dextallp1(lefta) - Dextallp1(righta) + +#define Dblext_addition(lefta,leftb,leftc,leftd,righta,rightb,rightc,rightd,resulta,resultb,resultc,resultd) \ + /* If the sum of the low words is less than either source, then \ + * an overflow into the next word occurred. */ \ + if ((Dextallp4(resultd) = Dextallp4(leftd)+Dextallp4(rightd)) < \ + Dextallp4(rightd)) \ + if((Dextallp3(resultc) = Dextallp3(leftc)+Dextallp3(rightc)+1) <= \ + Dextallp3(rightc)) \ + if((Dextallp2(resultb) = Dextallp2(leftb)+Dextallp2(rightb)+1) \ + <= Dextallp2(rightb)) \ + Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta)+1; \ + else Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta); \ + else \ + if ((Dextallp2(resultb) = Dextallp2(leftb)+Dextallp2(rightb)) < \ + Dextallp2(rightb)) \ + Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta)+1; \ + else Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta); \ + else \ + if ((Dextallp3(resultc) = Dextallp3(leftc)+Dextallp3(rightc)) < \ + Dextallp3(rightc)) \ + if ((Dextallp2(resultb) = Dextallp2(leftb)+Dextallp2(rightb)+1) \ + <= Dextallp2(rightb)) \ + Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta)+1; \ + else Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta); \ + else \ + if ((Dextallp2(resultb) = Dextallp2(leftb)+Dextallp2(rightb)) < \ + Dextallp2(rightb)) \ + Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta)+1; \ + else Dextallp1(resulta) = Dextallp1(lefta)+Dextallp1(righta) + + +#define Dblext_arithrightshiftby1(srcdstA,srcdstB,srcdstC,srcdstD) \ + Shiftdouble(Dextallp3(srcdstC),Dextallp4(srcdstD),1,Dextallp4(srcdstD)); \ + Shiftdouble(Dextallp2(srcdstB),Dextallp3(srcdstC),1,Dextallp3(srcdstC)); \ + Shiftdouble(Dextallp1(srcdstA),Dextallp2(srcdstB),1,Dextallp2(srcdstB)); \ + Dextallp1(srcdstA) = (int)Dextallp1(srcdstA) >> 1 + +#define Dblext_leftshiftby8(valA,valB,valC,valD) \ + Shiftdouble(Dextallp1(valA),Dextallp2(valB),24,Dextallp1(valA)); \ + Shiftdouble(Dextallp2(valB),Dextallp3(valC),24,Dextallp2(valB)); \ + Shiftdouble(Dextallp3(valC),Dextallp4(valD),24,Dextallp3(valC)); \ + Dextallp4(valD) <<= 8 +#define Dblext_leftshiftby4(valA,valB,valC,valD) \ + Shiftdouble(Dextallp1(valA),Dextallp2(valB),28,Dextallp1(valA)); \ + Shiftdouble(Dextallp2(valB),Dextallp3(valC),28,Dextallp2(valB)); \ + Shiftdouble(Dextallp3(valC),Dextallp4(valD),28,Dextallp3(valC)); \ + Dextallp4(valD) <<= 4 +#define Dblext_leftshiftby3(valA,valB,valC,valD) \ + Shiftdouble(Dextallp1(valA),Dextallp2(valB),29,Dextallp1(valA)); \ + Shiftdouble(Dextallp2(valB),Dextallp3(valC),29,Dextallp2(valB)); \ + Shiftdouble(Dextallp3(valC),Dextallp4(valD),29,Dextallp3(valC)); \ + Dextallp4(valD) <<= 3 +#define Dblext_leftshiftby2(valA,valB,valC,valD) \ + Shiftdouble(Dextallp1(valA),Dextallp2(valB),30,Dextallp1(valA)); \ + Shiftdouble(Dextallp2(valB),Dextallp3(valC),30,Dextallp2(valB)); \ + Shiftdouble(Dextallp3(valC),Dextallp4(valD),30,Dextallp3(valC)); \ + Dextallp4(valD) <<= 2 +#define Dblext_leftshiftby1(valA,valB,valC,valD) \ + Shiftdouble(Dextallp1(valA),Dextallp2(valB),31,Dextallp1(valA)); \ + Shiftdouble(Dextallp2(valB),Dextallp3(valC),31,Dextallp2(valB)); \ + Shiftdouble(Dextallp3(valC),Dextallp4(valD),31,Dextallp3(valC)); \ + Dextallp4(valD) <<= 1 + +#define Dblext_rightshiftby4(valueA,valueB,valueC,valueD) \ + Shiftdouble(Dextallp3(valueC),Dextallp4(valueD),4,Dextallp4(valueD)); \ + Shiftdouble(Dextallp2(valueB),Dextallp3(valueC),4,Dextallp3(valueC)); \ + Shiftdouble(Dextallp1(valueA),Dextallp2(valueB),4,Dextallp2(valueB)); \ + Dextallp1(valueA) >>= 4 +#define Dblext_rightshiftby1(valueA,valueB,valueC,valueD) \ + Shiftdouble(Dextallp3(valueC),Dextallp4(valueD),1,Dextallp4(valueD)); \ + Shiftdouble(Dextallp2(valueB),Dextallp3(valueC),1,Dextallp3(valueC)); \ + Shiftdouble(Dextallp1(valueA),Dextallp2(valueB),1,Dextallp2(valueB)); \ + Dextallp1(valueA) >>= 1 + +#define Dblext_xortointp1(left,right,result) Dbl_xortointp1(left,right,result) + +#define Dblext_xorfromintp1(left,right,result) \ + Dbl_xorfromintp1(left,right,result) + +#define Dblext_copytoint_exponentmantissap1(src,dest) \ + Dbl_copytoint_exponentmantissap1(src,dest) + +#define Dblext_ismagnitudeless(leftB,rightB,signlessleft,signlessright) \ + Dbl_ismagnitudeless(leftB,rightB,signlessleft,signlessright) + +#define Dbl_copyto_dblext(src1,src2,dest1,dest2,dest3,dest4) \ + Dextallp1(dest1) = Dallp1(src1); Dextallp2(dest2) = Dallp2(src2); \ + Dextallp3(dest3) = 0; Dextallp4(dest4) = 0 + +#define Dblext_set_sign(dbl_value,sign) Dbl_set_sign(dbl_value,sign) +#define Dblext_clear_signexponent_set_hidden(srcdst) \ + Dbl_clear_signexponent_set_hidden(srcdst) +#define Dblext_clear_signexponent(srcdst) Dbl_clear_signexponent(srcdst) +#define Dblext_clear_sign(srcdst) Dbl_clear_sign(srcdst) +#define Dblext_isone_hidden(dbl_value) Dbl_isone_hidden(dbl_value) + +/* + * The Fourword_add() macro assumes that integers are 4 bytes in size. + * It will break if this is not the case. + */ + +#define Fourword_add(src1dstA,src1dstB,src1dstC,src1dstD,src2A,src2B,src2C,src2D) \ + /* \ + * want this macro to generate: \ + * ADD src1dstD,src2D,src1dstD; \ + * ADDC src1dstC,src2C,src1dstC; \ + * ADDC src1dstB,src2B,src1dstB; \ + * ADDC src1dstA,src2A,src1dstA; \ + */ \ + if ((unsigned int)(src1dstD += (src2D)) < (unsigned int)(src2D)) { \ + if ((unsigned int)(src1dstC += (src2C) + 1) <= \ + (unsigned int)(src2C)) { \ + if ((unsigned int)(src1dstB += (src2B) + 1) <= \ + (unsigned int)(src2B)) src1dstA++; \ + } \ + else if ((unsigned int)(src1dstB += (src2B)) < \ + (unsigned int)(src2B)) src1dstA++; \ + } \ + else { \ + if ((unsigned int)(src1dstC += (src2C)) < \ + (unsigned int)(src2C)) { \ + if ((unsigned int)(src1dstB += (src2B) + 1) <= \ + (unsigned int)(src2B)) src1dstA++; \ + } \ + else if ((unsigned int)(src1dstB += (src2B)) < \ + (unsigned int)(src2B)) src1dstA++; \ + } \ + src1dstA += (src2A) + +#define Dblext_denormalize(opndp1,opndp2,opndp3,opndp4,exponent,is_tiny) \ + {int shiftamt, sticky; \ + is_tiny = TRUE; \ + if (exponent == 0 && (Dextallp3(opndp3) || Dextallp4(opndp4))) { \ + switch (Rounding_mode()) { \ + case ROUNDPLUS: \ + if (Dbl_iszero_sign(opndp1)) { \ + Dbl_increment(opndp1,opndp2); \ + if (Dbl_isone_hiddenoverflow(opndp1)) \ + is_tiny = FALSE; \ + Dbl_decrement(opndp1,opndp2); \ + } \ + break; \ + case ROUNDMINUS: \ + if (Dbl_isone_sign(opndp1)) { \ + Dbl_increment(opndp1,opndp2); \ + if (Dbl_isone_hiddenoverflow(opndp1)) \ + is_tiny = FALSE; \ + Dbl_decrement(opndp1,opndp2); \ + } \ + break; \ + case ROUNDNEAREST: \ + if (Dblext_isone_highp3(opndp3) && \ + (Dblext_isone_lowp2(opndp2) || \ + Dblext_isnotzero_low31p3(opndp3))) { \ + Dbl_increment(opndp1,opndp2); \ + if (Dbl_isone_hiddenoverflow(opndp1)) \ + is_tiny = FALSE; \ + Dbl_decrement(opndp1,opndp2); \ + } \ + break; \ + } \ + } \ + Dblext_clear_signexponent_set_hidden(opndp1); \ + if (exponent >= (1-QUAD_P)) { \ + shiftamt = (1-exponent) % 32; \ + switch((1-exponent)/32) { \ + case 0: sticky = Dextallp4(opndp4) << 32-(shiftamt); \ + Variableshiftdouble(opndp3,opndp4,shiftamt,opndp4); \ + Variableshiftdouble(opndp2,opndp3,shiftamt,opndp3); \ + Variableshiftdouble(opndp1,opndp2,shiftamt,opndp2); \ + Dextallp1(opndp1) >>= shiftamt; \ + break; \ + case 1: sticky = (Dextallp3(opndp3) << 32-(shiftamt)) | \ + Dextallp4(opndp4); \ + Variableshiftdouble(opndp2,opndp3,shiftamt,opndp4); \ + Variableshiftdouble(opndp1,opndp2,shiftamt,opndp3); \ + Dextallp2(opndp2) = Dextallp1(opndp1) >> shiftamt; \ + Dextallp1(opndp1) = 0; \ + break; \ + case 2: sticky = (Dextallp2(opndp2) << 32-(shiftamt)) | \ + Dextallp3(opndp3) | Dextallp4(opndp4); \ + Variableshiftdouble(opndp1,opndp2,shiftamt,opndp4); \ + Dextallp3(opndp3) = Dextallp1(opndp1) >> shiftamt; \ + Dextallp1(opndp1) = Dextallp2(opndp2) = 0; \ + break; \ + case 3: sticky = (Dextallp1(opndp1) << 32-(shiftamt)) | \ + Dextallp2(opndp2) | Dextallp3(opndp3) | \ + Dextallp4(opndp4); \ + Dextallp4(opndp4) = Dextallp1(opndp1) >> shiftamt; \ + Dextallp1(opndp1) = Dextallp2(opndp2) = 0; \ + Dextallp3(opndp3) = 0; \ + break; \ + } \ + } \ + else { \ + sticky = Dextallp1(opndp1) | Dextallp2(opndp2) | \ + Dextallp3(opndp3) | Dextallp4(opndp4); \ + Dblext_setzero(opndp1,opndp2,opndp3,opndp4); \ + } \ + if (sticky) Dblext_setone_lowmantissap4(opndp4); \ + exponent = 0; \ + } diff --git a/arch/parisc/math-emu/decode_exc.c b/arch/parisc/math-emu/decode_exc.c new file mode 100644 index 000000000..d41ddb343 --- /dev/null +++ b/arch/parisc/math-emu/decode_exc.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/fp/decode_exc.c $ Revision: $ + * + * Purpose: + * <<please update with a synopsis of the functionality provided by this file>> + * + * External Interfaces: + * <<the following list was autogenerated, please review>> + * decode_fpu(Fpu_register, trap_counts) + * + * Internal Interfaces: + * <<please update>> + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + +#include <linux/kernel.h> +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" +/* #include "types.h" */ +#include <asm/signal.h> +#include <asm/siginfo.h> +/* #include <machine/sys/mdep_private.h> */ + +#undef Fpustatus_register +#define Fpustatus_register Fpu_register[0] + +/* General definitions */ +#define DOESTRAP 1 +#define NOTRAP 0 +#define SIGNALCODE(signal, code) ((signal) << 24 | (code)) +#define copropbit 1<<31-2 /* bit position 2 */ +#define opclass 9 /* bits 21 & 22 */ +#define fmtbits 11 /* bits 19 & 20 */ +#define df 13 /* bits 17 & 18 */ +#define twobits 3 /* mask low-order 2 bits */ +#define fivebits 31 /* mask low-order 5 bits */ +#define MAX_EXCP_REG 7 /* number of excpeption registers to check */ + +/* Exception register definitions */ +#define Excp_type(index) Exceptiontype(Fpu_register[index]) +#define Excp_instr(index) Instructionfield(Fpu_register[index]) +#define Clear_excp_register(index) Allexception(Fpu_register[index]) = 0 +#define Excp_format() \ + (current_ir >> ((current_ir>>opclass & twobits) == 1 ? df : fmtbits) & twobits) + +/* Miscellaneous definitions */ +#define Fpu_sgl(index) Fpu_register[index*2] + +#define Fpu_dblp1(index) Fpu_register[index*2] +#define Fpu_dblp2(index) Fpu_register[(index*2)+1] + +#define Fpu_quadp1(index) Fpu_register[index*2] +#define Fpu_quadp2(index) Fpu_register[(index*2)+1] +#define Fpu_quadp3(index) Fpu_register[(index*2)+2] +#define Fpu_quadp4(index) Fpu_register[(index*2)+3] + +/* Single precision floating-point definitions */ +#ifndef Sgl_decrement +# define Sgl_decrement(sgl_value) Sall(sgl_value)-- +#endif + +/* Double precision floating-point definitions */ +#ifndef Dbl_decrement +# define Dbl_decrement(dbl_valuep1,dbl_valuep2) \ + if ((Dallp2(dbl_valuep2)--) == 0) Dallp1(dbl_valuep1)-- +#endif + + +#define update_trap_counts(Fpu_register, aflags, bflags, trap_counts) { \ + aflags=(Fpu_register[0])>>27; /* assumes zero fill. 32 bit */ \ + Fpu_register[0] |= bflags; \ +} + +u_int +decode_fpu(unsigned int Fpu_register[], unsigned int trap_counts[]) +{ + unsigned int current_ir, excp; + int target, exception_index = 1; + boolean inexact; + unsigned int aflags; + unsigned int bflags; + unsigned int excptype; + + + /* Keep stats on how many floating point exceptions (based on type) + * that happen. Want to keep this overhead low, but still provide + * some information to the customer. All exits from this routine + * need to restore Fpu_register[0] + */ + + bflags=(Fpu_register[0] & 0xf8000000); + Fpu_register[0] &= 0x07ffffff; + + /* exception_index is used to index the exception register queue. It + * always points at the last register that contains a valid exception. A + * zero value implies no exceptions (also the initialized value). Setting + * the T-bit resets the exception_index to zero. + */ + + /* + * Check for reserved-op exception. A reserved-op exception does not + * set any exception registers nor does it set the T-bit. If the T-bit + * is not set then a reserved-op exception occurred. + * + * At some point, we may want to report reserved op exceptions as + * illegal instructions. + */ + + if (!Is_tbit_set()) { + update_trap_counts(Fpu_register, aflags, bflags, trap_counts); + return SIGNALCODE(SIGILL, ILL_COPROC); + } + + /* + * Is a coprocessor op. + * + * Now we need to determine what type of exception occurred. + */ + for (exception_index=1; exception_index<=MAX_EXCP_REG; exception_index++) { + current_ir = Excp_instr(exception_index); + /* + * On PA89: there are 5 different unimplemented exception + * codes: 0x1, 0x9, 0xb, 0x3, and 0x23. PA-RISC 2.0 adds + * another, 0x2b. Only these have the low order bit set. + */ + excptype = Excp_type(exception_index); + if (excptype & UNIMPLEMENTEDEXCEPTION) { + /* + * Clear T-bit and exception register so that + * we can tell if a trap really occurs while + * emulating the instruction. + */ + Clear_tbit(); + Clear_excp_register(exception_index); + /* + * Now emulate this instruction. If a trap occurs, + * fpudispatch will return a non-zero number + */ + excp = fpudispatch(current_ir,excptype,0,Fpu_register); + /* accumulate the status flags, don't lose them as in hpux */ + if (excp) { + /* + * We now need to make sure that the T-bit and the + * exception register contain the correct values + * before continuing. + */ + /* + * Set t-bit since it might still be needed for a + * subsequent real trap (I don't understand fully -PB) + */ + Set_tbit(); + /* some of the following code uses + * Excp_type(exception_index) so fix that up */ + Set_exceptiontype_and_instr_field(excp,current_ir, + Fpu_register[exception_index]); + if (excp == UNIMPLEMENTEDEXCEPTION) { + /* + * it is really unimplemented, so restore the + * TIMEX extended unimplemented exception code + */ + excp = excptype; + update_trap_counts(Fpu_register, aflags, bflags, + trap_counts); + return SIGNALCODE(SIGILL, ILL_COPROC); + } + /* some of the following code uses excptype, so + * fix that up too */ + excptype = excp; + } + /* handle exceptions other than the real UNIMPLIMENTED the + * same way as if the hardware had caused them */ + if (excp == NOEXCEPTION) + /* For now use 'break', should technically be 'continue' */ + break; + } + + /* + * In PA89, the underflow exception has been extended to encode + * additional information. The exception looks like pp01x0, + * where x is 1 if inexact and pp represent the inexact bit (I) + * and the round away bit (RA) + */ + if (excptype & UNDERFLOWEXCEPTION) { + /* check for underflow trap enabled */ + if (Is_underflowtrap_enabled()) { + update_trap_counts(Fpu_register, aflags, bflags, + trap_counts); + return SIGNALCODE(SIGFPE, FPE_FLTUND); + } else { + /* + * Isn't a real trap; we need to + * return the default value. + */ + target = current_ir & fivebits; +#ifndef lint + if (Ibit(Fpu_register[exception_index])) inexact = TRUE; + else inexact = FALSE; +#endif + switch (Excp_format()) { + case SGL: + /* + * If ra (round-away) is set, will + * want to undo the rounding done + * by the hardware. + */ + if (Rabit(Fpu_register[exception_index])) + Sgl_decrement(Fpu_sgl(target)); + + /* now denormalize */ + sgl_denormalize(&Fpu_sgl(target),&inexact,Rounding_mode()); + break; + case DBL: + /* + * If ra (round-away) is set, will + * want to undo the rounding done + * by the hardware. + */ + if (Rabit(Fpu_register[exception_index])) + Dbl_decrement(Fpu_dblp1(target),Fpu_dblp2(target)); + + /* now denormalize */ + dbl_denormalize(&Fpu_dblp1(target),&Fpu_dblp2(target), + &inexact,Rounding_mode()); + break; + } + if (inexact) Set_underflowflag(); + /* + * Underflow can generate an inexact + * exception. If inexact trap is enabled, + * want to do an inexact trap, otherwise + * set inexact flag. + */ + if (inexact && Is_inexacttrap_enabled()) { + /* + * Set exception field of exception register + * to inexact, parm field to zero. + * Underflow bit should be cleared. + */ + Set_exceptiontype(Fpu_register[exception_index], + INEXACTEXCEPTION); + Set_parmfield(Fpu_register[exception_index],0); + update_trap_counts(Fpu_register, aflags, bflags, + trap_counts); + return SIGNALCODE(SIGFPE, FPE_FLTRES); + } + else { + /* + * Exception register needs to be cleared. + * Inexact flag needs to be set if inexact. + */ + Clear_excp_register(exception_index); + if (inexact) Set_inexactflag(); + } + } + continue; + } + switch(Excp_type(exception_index)) { + case OVERFLOWEXCEPTION: + case OVERFLOWEXCEPTION | INEXACTEXCEPTION: + /* check for overflow trap enabled */ + update_trap_counts(Fpu_register, aflags, bflags, + trap_counts); + if (Is_overflowtrap_enabled()) { + update_trap_counts(Fpu_register, aflags, bflags, + trap_counts); + return SIGNALCODE(SIGFPE, FPE_FLTOVF); + } else { + /* + * Isn't a real trap; we need to + * return the default value. + */ + target = current_ir & fivebits; + switch (Excp_format()) { + case SGL: + Sgl_setoverflow(Fpu_sgl(target)); + break; + case DBL: + Dbl_setoverflow(Fpu_dblp1(target),Fpu_dblp2(target)); + break; + } + Set_overflowflag(); + /* + * Overflow always generates an inexact + * exception. If inexact trap is enabled, + * want to do an inexact trap, otherwise + * set inexact flag. + */ + if (Is_inexacttrap_enabled()) { + /* + * Set exception field of exception + * register to inexact. Overflow + * bit should be cleared. + */ + Set_exceptiontype(Fpu_register[exception_index], + INEXACTEXCEPTION); + update_trap_counts(Fpu_register, aflags, bflags, + trap_counts); + return SIGNALCODE(SIGFPE, FPE_FLTRES); + } + else { + /* + * Exception register needs to be cleared. + * Inexact flag needs to be set. + */ + Clear_excp_register(exception_index); + Set_inexactflag(); + } + } + break; + case INVALIDEXCEPTION: + case OPC_2E_INVALIDEXCEPTION: + update_trap_counts(Fpu_register, aflags, bflags, trap_counts); + return SIGNALCODE(SIGFPE, FPE_FLTINV); + case DIVISIONBYZEROEXCEPTION: + update_trap_counts(Fpu_register, aflags, bflags, trap_counts); + Clear_excp_register(exception_index); + return SIGNALCODE(SIGFPE, FPE_FLTDIV); + case INEXACTEXCEPTION: + update_trap_counts(Fpu_register, aflags, bflags, trap_counts); + return SIGNALCODE(SIGFPE, FPE_FLTRES); + default: + update_trap_counts(Fpu_register, aflags, bflags, trap_counts); + printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__, + __LINE__, Excp_type(exception_index)); + return SIGNALCODE(SIGILL, ILL_COPROC); + case NOEXCEPTION: /* no exception */ + /* + * Clear exception register in case + * other fields are non-zero. + */ + Clear_excp_register(exception_index); + break; + } + } + /* + * No real exceptions occurred. + */ + Clear_tbit(); + update_trap_counts(Fpu_register, aflags, bflags, trap_counts); + return(NOTRAP); +} diff --git a/arch/parisc/math-emu/denormal.c b/arch/parisc/math-emu/denormal.c new file mode 100644 index 000000000..7f1a60d59 --- /dev/null +++ b/arch/parisc/math-emu/denormal.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/fp/denormal.c $ Revision: $ + * + * Purpose: + * <<please update with a synopsis of the functionality provided by this file>> + * + * External Interfaces: + * <<the following list was autogenerated, please review>> + * dbl_denormalize(dbl_opndp1,dbl_opndp2,inexactflag,rmode) + * sgl_denormalize(sgl_opnd,inexactflag,rmode) + * + * Internal Interfaces: + * <<please update>> + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "hppa.h" +#include <linux/kernel.h> +/* #include <machine/sys/mdep_private.h> */ + +#undef Fpustatus_register +#define Fpustatus_register Fpu_register[0] + +void +sgl_denormalize(unsigned int *sgl_opnd, boolean *inexactflag, int rmode) +{ + unsigned int opnd; + int sign, exponent; + boolean guardbit = FALSE, stickybit, inexact; + + opnd = *sgl_opnd; + stickybit = *inexactflag; + exponent = Sgl_exponent(opnd) - SGL_WRAP; + sign = Sgl_sign(opnd); + Sgl_denormalize(opnd,exponent,guardbit,stickybit,inexact); + if (inexact) { + switch (rmode) { + case ROUNDPLUS: + if (sign == 0) { + Sgl_increment(opnd); + } + break; + case ROUNDMINUS: + if (sign != 0) { + Sgl_increment(opnd); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Sgl_isone_lowmantissa(opnd))) { + Sgl_increment(opnd); + } + break; + } + } + Sgl_set_sign(opnd,sign); + *sgl_opnd = opnd; + *inexactflag = inexact; + return; +} + +void +dbl_denormalize(unsigned int *dbl_opndp1, + unsigned int * dbl_opndp2, + boolean *inexactflag, + int rmode) +{ + unsigned int opndp1, opndp2; + int sign, exponent; + boolean guardbit = FALSE, stickybit, inexact; + + opndp1 = *dbl_opndp1; + opndp2 = *dbl_opndp2; + stickybit = *inexactflag; + exponent = Dbl_exponent(opndp1) - DBL_WRAP; + sign = Dbl_sign(opndp1); + Dbl_denormalize(opndp1,opndp2,exponent,guardbit,stickybit,inexact); + if (inexact) { + switch (rmode) { + case ROUNDPLUS: + if (sign == 0) { + Dbl_increment(opndp1,opndp2); + } + break; + case ROUNDMINUS: + if (sign != 0) { + Dbl_increment(opndp1,opndp2); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Dbl_isone_lowmantissap2(opndp2))) { + Dbl_increment(opndp1,opndp2); + } + break; + } + } + Dbl_set_sign(opndp1,sign); + *dbl_opndp1 = opndp1; + *dbl_opndp2 = opndp2; + *inexactflag = inexact; + return; +} diff --git a/arch/parisc/math-emu/dfadd.c b/arch/parisc/math-emu/dfadd.c new file mode 100644 index 000000000..00e561d4a --- /dev/null +++ b/arch/parisc/math-emu/dfadd.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/dfadd.c $Revision: 1.1 $ + * + * Purpose: + * Double_add: add two double precision values. + * + * External Interfaces: + * dbl_fadd(leftptr, rightptr, dstptr, status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "dbl_float.h" + +/* + * Double_add: add two double precision values. + */ +dbl_fadd( + dbl_floating_point *leftptr, + dbl_floating_point *rightptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int signless_upper_left, signless_upper_right, save; + register unsigned int leftp1, leftp2, rightp1, rightp2, extent; + register unsigned int resultp1 = 0, resultp2 = 0; + + register int result_exponent, right_exponent, diff_exponent; + register int sign_save, jumpsize; + register boolean inexact = FALSE; + register boolean underflowtrap; + + /* Create local copies of the numbers */ + Dbl_copyfromptr(leftptr,leftp1,leftp2); + Dbl_copyfromptr(rightptr,rightp1,rightp2); + + /* A zero "save" helps discover equal operands (for later), * + * and is used in swapping operands (if needed). */ + Dbl_xortointp1(leftp1,rightp1,/*to*/save); + + /* + * check first operand for NaN's or infinity + */ + if ((result_exponent = Dbl_exponent(leftp1)) == DBL_INFINITY_EXPONENT) + { + if (Dbl_iszero_mantissa(leftp1,leftp2)) + { + if (Dbl_isnotnan(rightp1,rightp2)) + { + if (Dbl_isinfinity(rightp1,rightp2) && save!=0) + { + /* + * invalid since operands are opposite signed infinity's + */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * return infinity + */ + Dbl_copytoptr(leftp1,leftp2,dstptr); + return(NOEXCEPTION); + } + } + else + { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(leftp1)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(leftp1); + } + /* + * is second operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(rightp1)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(rightp1); + Dbl_copytoptr(rightp1,rightp2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(leftp1,leftp2,dstptr); + return(NOEXCEPTION); + } + } /* End left NaN or Infinity processing */ + /* + * check second operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(rightp1)) + { + if (Dbl_iszero_mantissa(rightp1,rightp2)) + { + /* return infinity */ + Dbl_copytoptr(rightp1,rightp2,dstptr); + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(rightp1)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(rightp1); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(rightp1,rightp2,dstptr); + return(NOEXCEPTION); + } /* End right NaN or Infinity processing */ + + /* Invariant: Must be dealing with finite numbers */ + + /* Compare operands by removing the sign */ + Dbl_copytoint_exponentmantissap1(leftp1,signless_upper_left); + Dbl_copytoint_exponentmantissap1(rightp1,signless_upper_right); + + /* sign difference selects add or sub operation. */ + if(Dbl_ismagnitudeless(leftp2,rightp2,signless_upper_left,signless_upper_right)) + { + /* Set the left operand to the larger one by XOR swap * + * First finish the first word using "save" */ + Dbl_xorfromintp1(save,rightp1,/*to*/rightp1); + Dbl_xorfromintp1(save,leftp1,/*to*/leftp1); + Dbl_swap_lower(leftp2,rightp2); + result_exponent = Dbl_exponent(leftp1); + } + /* Invariant: left is not smaller than right. */ + + if((right_exponent = Dbl_exponent(rightp1)) == 0) + { + /* Denormalized operands. First look for zeroes */ + if(Dbl_iszero_mantissa(rightp1,rightp2)) + { + /* right is zero */ + if(Dbl_iszero_exponentmantissa(leftp1,leftp2)) + { + /* Both operands are zeros */ + if(Is_rounding_mode(ROUNDMINUS)) + { + Dbl_or_signs(leftp1,/*with*/rightp1); + } + else + { + Dbl_and_signs(leftp1,/*with*/rightp1); + } + } + else + { + /* Left is not a zero and must be the result. Trapped + * underflows are signaled if left is denormalized. Result + * is always exact. */ + if( (result_exponent == 0) && Is_underflowtrap_enabled() ) + { + /* need to normalize results mantissa */ + sign_save = Dbl_signextendedsign(leftp1); + Dbl_leftshiftby1(leftp1,leftp2); + Dbl_normalize(leftp1,leftp2,result_exponent); + Dbl_set_sign(leftp1,/*using*/sign_save); + Dbl_setwrapped_exponent(leftp1,result_exponent,unfl); + Dbl_copytoptr(leftp1,leftp2,dstptr); + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + } + Dbl_copytoptr(leftp1,leftp2,dstptr); + return(NOEXCEPTION); + } + + /* Neither are zeroes */ + Dbl_clear_sign(rightp1); /* Exponent is already cleared */ + if(result_exponent == 0 ) + { + /* Both operands are denormalized. The result must be exact + * and is simply calculated. A sum could become normalized and a + * difference could cancel to a true zero. */ + if( (/*signed*/int) save < 0 ) + { + Dbl_subtract(leftp1,leftp2,/*minus*/rightp1,rightp2, + /*into*/resultp1,resultp2); + if(Dbl_iszero_mantissa(resultp1,resultp2)) + { + if(Is_rounding_mode(ROUNDMINUS)) + { + Dbl_setone_sign(resultp1); + } + else + { + Dbl_setzero_sign(resultp1); + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else + { + Dbl_addition(leftp1,leftp2,rightp1,rightp2, + /*into*/resultp1,resultp2); + if(Dbl_isone_hidden(resultp1)) + { + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + if(Is_underflowtrap_enabled()) + { + /* need to normalize result */ + sign_save = Dbl_signextendedsign(resultp1); + Dbl_leftshiftby1(resultp1,resultp2); + Dbl_normalize(resultp1,resultp2,result_exponent); + Dbl_set_sign(resultp1,/*using*/sign_save); + Dbl_setwrapped_exponent(resultp1,result_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + right_exponent = 1; /* Set exponent to reflect different bias + * with denormalized numbers. */ + } + else + { + Dbl_clear_signexponent_set_hidden(rightp1); + } + Dbl_clear_exponent_set_hidden(leftp1); + diff_exponent = result_exponent - right_exponent; + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for this + * infrequent case. + */ + if(diff_exponent > DBL_THRESHOLD) + { + diff_exponent = DBL_THRESHOLD; + } + + /* Align right operand by shifting to right */ + Dbl_right_align(/*operand*/rightp1,rightp2,/*shifted by*/diff_exponent, + /*and lower to*/extent); + + /* Treat sum and difference of the operands separately. */ + if( (/*signed*/int) save < 0 ) + { + /* + * Difference of the two operands. Their can be no overflow. A + * borrow can occur out of the hidden bit and force a post + * normalization phase. + */ + Dbl_subtract_withextension(leftp1,leftp2,/*minus*/rightp1,rightp2, + /*with*/extent,/*into*/resultp1,resultp2); + if(Dbl_iszero_hidden(resultp1)) + { + /* Handle normalization */ + /* A straight forward algorithm would now shift the result + * and extension left until the hidden bit becomes one. Not + * all of the extension bits need participate in the shift. + * Only the two most significant bits (round and guard) are + * needed. If only a single shift is needed then the guard + * bit becomes a significant low order bit and the extension + * must participate in the rounding. If more than a single + * shift is needed, then all bits to the right of the guard + * bit are zeros, and the guard bit may or may not be zero. */ + sign_save = Dbl_signextendedsign(resultp1); + Dbl_leftshiftby1_withextent(resultp1,resultp2,extent,resultp1,resultp2); + + /* Need to check for a zero result. The sign and exponent + * fields have already been zeroed. The more efficient test + * of the full object can be used. + */ + if(Dbl_iszero(resultp1,resultp2)) + /* Must have been "x-x" or "x+(-x)". */ + { + if(Is_rounding_mode(ROUNDMINUS)) Dbl_setone_sign(resultp1); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + result_exponent--; + /* Look to see if normalization is finished. */ + if(Dbl_isone_hidden(resultp1)) + { + if(result_exponent==0) + { + /* Denormalized, exponent should be zero. Left operand * + * was normalized, so extent (guard, round) was zero */ + goto underflow; + } + else + { + /* No further normalization is needed. */ + Dbl_set_sign(resultp1,/*using*/sign_save); + Ext_leftshiftby1(extent); + goto round; + } + } + + /* Check for denormalized, exponent should be zero. Left * + * operand was normalized, so extent (guard, round) was zero */ + if(!(underflowtrap = Is_underflowtrap_enabled()) && + result_exponent==0) goto underflow; + + /* Shift extension to complete one bit of normalization and + * update exponent. */ + Ext_leftshiftby1(extent); + + /* Discover first one bit to determine shift amount. Use a + * modified binary search. We have already shifted the result + * one position right and still not found a one so the remainder + * of the extension must be zero and simplifies rounding. */ + /* Scan bytes */ + while(Dbl_iszero_hiddenhigh7mantissa(resultp1)) + { + Dbl_leftshiftby8(resultp1,resultp2); + if((result_exponent -= 8) <= 0 && !underflowtrap) + goto underflow; + } + /* Now narrow it down to the nibble */ + if(Dbl_iszero_hiddenhigh3mantissa(resultp1)) + { + /* The lower nibble contains the normalizing one */ + Dbl_leftshiftby4(resultp1,resultp2); + if((result_exponent -= 4) <= 0 && !underflowtrap) + goto underflow; + } + /* Select case were first bit is set (already normalized) + * otherwise select the proper shift. */ + if((jumpsize = Dbl_hiddenhigh3mantissa(resultp1)) > 7) + { + /* Already normalized */ + if(result_exponent <= 0) goto underflow; + Dbl_set_sign(resultp1,/*using*/sign_save); + Dbl_set_exponent(resultp1,/*using*/result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Dbl_sethigh4bits(resultp1,/*using*/sign_save); + switch(jumpsize) + { + case 1: + { + Dbl_leftshiftby3(resultp1,resultp2); + result_exponent -= 3; + break; + } + case 2: + case 3: + { + Dbl_leftshiftby2(resultp1,resultp2); + result_exponent -= 2; + break; + } + case 4: + case 5: + case 6: + case 7: + { + Dbl_leftshiftby1(resultp1,resultp2); + result_exponent -= 1; + break; + } + } + if(result_exponent > 0) + { + Dbl_set_exponent(resultp1,/*using*/result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); /* Sign bit is already set */ + } + /* Fixup potential underflows */ + underflow: + if(Is_underflowtrap_enabled()) + { + Dbl_set_sign(resultp1,sign_save); + Dbl_setwrapped_exponent(resultp1,result_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + /* + * Since we cannot get an inexact denormalized result, + * we can now return. + */ + Dbl_fix_overshift(resultp1,resultp2,(1-result_exponent),extent); + Dbl_clear_signexponent(resultp1); + Dbl_set_sign(resultp1,sign_save); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } /* end if(hidden...)... */ + /* Fall through and round */ + } /* end if(save < 0)... */ + else + { + /* Add magnitudes */ + Dbl_addition(leftp1,leftp2,rightp1,rightp2,/*to*/resultp1,resultp2); + if(Dbl_isone_hiddenoverflow(resultp1)) + { + /* Prenormalization required. */ + Dbl_rightshiftby1_withextent(resultp2,extent,extent); + Dbl_arithrightshiftby1(resultp1,resultp2); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...add magnitudes... */ + + /* Round the result. If the extension is all zeros,then the result is + * exact. Otherwise round in the correct direction. No underflow is + * possible. If a postnormalization is necessary, then the mantissa is + * all zeros so no shift is needed. */ + round: + if(Ext_isnotzero(extent)) + { + inexact = TRUE; + switch(Rounding_mode()) + { + case ROUNDNEAREST: /* The default. */ + if(Ext_isone_sign(extent)) + { + /* at least 1/2 ulp */ + if(Ext_isnotzero_lower(extent) || + Dbl_isone_lowmantissap2(resultp2)) + { + /* either exactly half way and odd or more than 1/2ulp */ + Dbl_increment(resultp1,resultp2); + } + } + break; + + case ROUNDPLUS: + if(Dbl_iszero_sign(resultp1)) + { + /* Round up positive results */ + Dbl_increment(resultp1,resultp2); + } + break; + + case ROUNDMINUS: + if(Dbl_isone_sign(resultp1)) + { + /* Round down negative results */ + Dbl_increment(resultp1,resultp2); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if(Dbl_isone_hiddenoverflow(resultp1)) result_exponent++; + } + if(result_exponent == DBL_INFINITY_EXPONENT) + { + /* Overflow */ + if(Is_overflowtrap_enabled()) + { + Dbl_setwrapped_exponent(resultp1,result_exponent,ovfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + else + { + inexact = TRUE; + Set_overflowflag(); + Dbl_setoverflow(resultp1,resultp2); + } + } + else Dbl_set_exponent(resultp1,result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if(inexact) + if(Is_inexacttrap_enabled()) + return(INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/dfcmp.c b/arch/parisc/math-emu/dfcmp.c new file mode 100644 index 000000000..ae4b49744 --- /dev/null +++ b/arch/parisc/math-emu/dfcmp.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/dfcmp.c $Revision: 1.1 $ + * + * Purpose: + * dbl_cmp: compare two values + * + * External Interfaces: + * dbl_fcmp(leftptr, rightptr, cond, status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + + +#include "float.h" +#include "dbl_float.h" + +/* + * dbl_cmp: compare two values + */ +int +dbl_fcmp (dbl_floating_point * leftptr, dbl_floating_point * rightptr, + unsigned int cond, unsigned int *status) + + /* The predicate to be tested */ + + { + register unsigned int leftp1, leftp2, rightp1, rightp2; + register int xorresult; + + /* Create local copies of the numbers */ + Dbl_copyfromptr(leftptr,leftp1,leftp2); + Dbl_copyfromptr(rightptr,rightp1,rightp2); + /* + * Test for NaN + */ + if( (Dbl_exponent(leftp1) == DBL_INFINITY_EXPONENT) + || (Dbl_exponent(rightp1) == DBL_INFINITY_EXPONENT) ) + { + /* Check if a NaN is involved. Signal an invalid exception when + * comparing a signaling NaN or when comparing quiet NaNs and the + * low bit of the condition is set */ + if( ((Dbl_exponent(leftp1) == DBL_INFINITY_EXPONENT) + && Dbl_isnotzero_mantissa(leftp1,leftp2) + && (Exception(cond) || Dbl_isone_signaling(leftp1))) + || + ((Dbl_exponent(rightp1) == DBL_INFINITY_EXPONENT) + && Dbl_isnotzero_mantissa(rightp1,rightp2) + && (Exception(cond) || Dbl_isone_signaling(rightp1))) ) + { + if( Is_invalidtrap_enabled() ) { + Set_status_cbit(Unordered(cond)); + return(INVALIDEXCEPTION); + } + else Set_invalidflag(); + Set_status_cbit(Unordered(cond)); + return(NOEXCEPTION); + } + /* All the exceptional conditions are handled, now special case + NaN compares */ + else if( ((Dbl_exponent(leftp1) == DBL_INFINITY_EXPONENT) + && Dbl_isnotzero_mantissa(leftp1,leftp2)) + || + ((Dbl_exponent(rightp1) == DBL_INFINITY_EXPONENT) + && Dbl_isnotzero_mantissa(rightp1,rightp2)) ) + { + /* NaNs always compare unordered. */ + Set_status_cbit(Unordered(cond)); + return(NOEXCEPTION); + } + /* infinities will drop down to the normal compare mechanisms */ + } + /* First compare for unequal signs => less or greater or + * special equal case */ + Dbl_xortointp1(leftp1,rightp1,xorresult); + if( xorresult < 0 ) + { + /* left negative => less, left positive => greater. + * equal is possible if both operands are zeros. */ + if( Dbl_iszero_exponentmantissa(leftp1,leftp2) + && Dbl_iszero_exponentmantissa(rightp1,rightp2) ) + { + Set_status_cbit(Equal(cond)); + } + else if( Dbl_isone_sign(leftp1) ) + { + Set_status_cbit(Lessthan(cond)); + } + else + { + Set_status_cbit(Greaterthan(cond)); + } + } + /* Signs are the same. Treat negative numbers separately + * from the positives because of the reversed sense. */ + else if(Dbl_isequal(leftp1,leftp2,rightp1,rightp2)) + { + Set_status_cbit(Equal(cond)); + } + else if( Dbl_iszero_sign(leftp1) ) + { + /* Positive compare */ + if( Dbl_allp1(leftp1) < Dbl_allp1(rightp1) ) + { + Set_status_cbit(Lessthan(cond)); + } + else if( Dbl_allp1(leftp1) > Dbl_allp1(rightp1) ) + { + Set_status_cbit(Greaterthan(cond)); + } + else + { + /* Equal first parts. Now we must use unsigned compares to + * resolve the two possibilities. */ + if( Dbl_allp2(leftp2) < Dbl_allp2(rightp2) ) + { + Set_status_cbit(Lessthan(cond)); + } + else + { + Set_status_cbit(Greaterthan(cond)); + } + } + } + else + { + /* Negative compare. Signed or unsigned compares + * both work the same. That distinction is only + * important when the sign bits differ. */ + if( Dbl_allp1(leftp1) > Dbl_allp1(rightp1) ) + { + Set_status_cbit(Lessthan(cond)); + } + else if( Dbl_allp1(leftp1) < Dbl_allp1(rightp1) ) + { + Set_status_cbit(Greaterthan(cond)); + } + else + { + /* Equal first parts. Now we must use unsigned compares to + * resolve the two possibilities. */ + if( Dbl_allp2(leftp2) > Dbl_allp2(rightp2) ) + { + Set_status_cbit(Lessthan(cond)); + } + else + { + Set_status_cbit(Greaterthan(cond)); + } + } + } + return(NOEXCEPTION); + } diff --git a/arch/parisc/math-emu/dfdiv.c b/arch/parisc/math-emu/dfdiv.c new file mode 100644 index 000000000..239150dbe --- /dev/null +++ b/arch/parisc/math-emu/dfdiv.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/dfdiv.c $Revision: 1.1 $ + * + * Purpose: + * Double Precision Floating-point Divide + * + * External Interfaces: + * dbl_fdiv(srcptr1,srcptr2,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "dbl_float.h" + +/* + * Double Precision Floating-point Divide + */ + +int +dbl_fdiv (dbl_floating_point * srcptr1, dbl_floating_point * srcptr2, + dbl_floating_point * dstptr, unsigned int *status) +{ + register unsigned int opnd1p1, opnd1p2, opnd2p1, opnd2p2; + register unsigned int opnd3p1, opnd3p2, resultp1, resultp2; + register int dest_exponent, count; + register boolean inexact = FALSE, guardbit = FALSE, stickybit = FALSE; + boolean is_tiny; + + Dbl_copyfromptr(srcptr1,opnd1p1,opnd1p2); + Dbl_copyfromptr(srcptr2,opnd2p1,opnd2p2); + /* + * set sign bit of result + */ + if (Dbl_sign(opnd1p1) ^ Dbl_sign(opnd2p1)) + Dbl_setnegativezerop1(resultp1); + else Dbl_setzerop1(resultp1); + /* + * check first operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd1p1)) { + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + if (Dbl_isnotnan(opnd2p1,opnd2p2)) { + if (Dbl_isinfinity(opnd2p1,opnd2p2)) { + /* + * invalid since both operands + * are infinity + */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * return infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd1p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd1p1); + } + /* + * is second operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd1p1,opnd1p2,dstptr); + return(NOEXCEPTION); + } + } + /* + * check second operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd2p1)) { + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + /* + * return zero + */ + Dbl_setzero_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * check for division by zero + */ + if (Dbl_iszero_exponentmantissa(opnd2p1,opnd2p2)) { + if (Dbl_iszero_exponentmantissa(opnd1p1,opnd1p2)) { + /* invalid since both operands are zero */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + if (Is_divisionbyzerotrap_enabled()) + return(DIVISIONBYZEROEXCEPTION); + Set_divisionbyzeroflag(); + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate exponent + */ + dest_exponent = Dbl_exponent(opnd1p1) - Dbl_exponent(opnd2p1) + DBL_BIAS; + + /* + * Generate mantissa + */ + if (Dbl_isnotzero_exponent(opnd1p1)) { + /* set hidden bit */ + Dbl_clear_signexponent_set_hidden(opnd1p1); + } + else { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + Dbl_setzero_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* is denormalized, want to normalize */ + Dbl_clear_signexponent(opnd1p1); + Dbl_leftshiftby1(opnd1p1,opnd1p2); + Dbl_normalize(opnd1p1,opnd1p2,dest_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Dbl_isnotzero_exponent(opnd2p1)) { + Dbl_clear_signexponent_set_hidden(opnd2p1); + } + else { + /* is denormalized; want to normalize */ + Dbl_clear_signexponent(opnd2p1); + Dbl_leftshiftby1(opnd2p1,opnd2p2); + while (Dbl_iszero_hiddenhigh7mantissa(opnd2p1)) { + dest_exponent+=8; + Dbl_leftshiftby8(opnd2p1,opnd2p2); + } + if (Dbl_iszero_hiddenhigh3mantissa(opnd2p1)) { + dest_exponent+=4; + Dbl_leftshiftby4(opnd2p1,opnd2p2); + } + while (Dbl_iszero_hidden(opnd2p1)) { + dest_exponent++; + Dbl_leftshiftby1(opnd2p1,opnd2p2); + } + } + + /* Divide the source mantissas */ + + /* + * A non-restoring divide algorithm is used. + */ + Twoword_subtract(opnd1p1,opnd1p2,opnd2p1,opnd2p2); + Dbl_setzero(opnd3p1,opnd3p2); + for (count=1; count <= DBL_P && (opnd1p1 || opnd1p2); count++) { + Dbl_leftshiftby1(opnd1p1,opnd1p2); + Dbl_leftshiftby1(opnd3p1,opnd3p2); + if (Dbl_iszero_sign(opnd1p1)) { + Dbl_setone_lowmantissap2(opnd3p2); + Twoword_subtract(opnd1p1,opnd1p2,opnd2p1,opnd2p2); + } + else { + Twoword_add(opnd1p1, opnd1p2, opnd2p1, opnd2p2); + } + } + if (count <= DBL_P) { + Dbl_leftshiftby1(opnd3p1,opnd3p2); + Dbl_setone_lowmantissap2(opnd3p2); + Dbl_leftshift(opnd3p1,opnd3p2,(DBL_P-count)); + if (Dbl_iszero_hidden(opnd3p1)) { + Dbl_leftshiftby1(opnd3p1,opnd3p2); + dest_exponent--; + } + } + else { + if (Dbl_iszero_hidden(opnd3p1)) { + /* need to get one more bit of result */ + Dbl_leftshiftby1(opnd1p1,opnd1p2); + Dbl_leftshiftby1(opnd3p1,opnd3p2); + if (Dbl_iszero_sign(opnd1p1)) { + Dbl_setone_lowmantissap2(opnd3p2); + Twoword_subtract(opnd1p1,opnd1p2,opnd2p1,opnd2p2); + } + else { + Twoword_add(opnd1p1,opnd1p2,opnd2p1,opnd2p2); + } + dest_exponent--; + } + if (Dbl_iszero_sign(opnd1p1)) guardbit = TRUE; + stickybit = Dbl_allp1(opnd1p1) || Dbl_allp2(opnd1p2); + } + inexact = guardbit | stickybit; + + /* + * round result + */ + if (inexact && (dest_exponent > 0 || Is_underflowtrap_enabled())) { + Dbl_clear_signexponent(opnd3p1); + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) + Dbl_increment(opnd3p1,opnd3p2); + break; + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) + Dbl_increment(opnd3p1,opnd3p2); + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Dbl_isone_lowmantissap2(opnd3p2))) { + Dbl_increment(opnd3p1,opnd3p2); + } + } + if (Dbl_isone_hidden(opnd3p1)) dest_exponent++; + } + Dbl_set_mantissa(resultp1,resultp2,opnd3p1,opnd3p2); + + /* + * Test for overflow + */ + if (dest_exponent >= DBL_INFINITY_EXPONENT) { + /* trap if OVERFLOWTRAP enabled */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,dest_exponent,ovfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + Set_overflowflag(); + /* set result to infinity or largest number */ + Dbl_setoverflow(resultp1,resultp2); + inexact = TRUE; + } + /* + * Test for underflow + */ + else if (dest_exponent <= 0) { + /* trap if UNDERFLOWTRAP enabled */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,dest_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return(UNDERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(UNDERFLOWEXCEPTION); + } + + /* Determine if should set underflow flag */ + is_tiny = TRUE; + if (dest_exponent == 0 && inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + if (Dbl_isone_hiddenoverflow(opnd3p1)) + is_tiny = FALSE; + Dbl_decrement(opnd3p1,opnd3p2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + if (Dbl_isone_hiddenoverflow(opnd3p1)) + is_tiny = FALSE; + Dbl_decrement(opnd3p1,opnd3p2); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Dbl_isone_lowmantissap2(opnd3p2))) { + Dbl_increment(opnd3p1,opnd3p2); + if (Dbl_isone_hiddenoverflow(opnd3p1)) + is_tiny = FALSE; + Dbl_decrement(opnd3p1,opnd3p2); + } + break; + } + } + + /* + * denormalize result or set to signed zero + */ + stickybit = inexact; + Dbl_denormalize(opnd3p1,opnd3p2,dest_exponent,guardbit, + stickybit,inexact); + + /* return rounded number */ + if (inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Dbl_isone_lowmantissap2(opnd3p2))) { + Dbl_increment(opnd3p1,opnd3p2); + } + break; + } + if (is_tiny) Set_underflowflag(); + } + Dbl_set_exponentmantissa(resultp1,resultp2,opnd3p1,opnd3p2); + } + else Dbl_set_exponent(resultp1,dest_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/dfmpy.c b/arch/parisc/math-emu/dfmpy.c new file mode 100644 index 000000000..87e0ce849 --- /dev/null +++ b/arch/parisc/math-emu/dfmpy.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/dfmpy.c $Revision: 1.1 $ + * + * Purpose: + * Double Precision Floating-point Multiply + * + * External Interfaces: + * dbl_fmpy(srcptr1,srcptr2,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "dbl_float.h" + +/* + * Double Precision Floating-point Multiply + */ + +int +dbl_fmpy( + dbl_floating_point *srcptr1, + dbl_floating_point *srcptr2, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int opnd1p1, opnd1p2, opnd2p1, opnd2p2; + register unsigned int opnd3p1, opnd3p2, resultp1, resultp2; + register int dest_exponent, count; + register boolean inexact = FALSE, guardbit = FALSE, stickybit = FALSE; + boolean is_tiny; + + Dbl_copyfromptr(srcptr1,opnd1p1,opnd1p2); + Dbl_copyfromptr(srcptr2,opnd2p1,opnd2p2); + + /* + * set sign bit of result + */ + if (Dbl_sign(opnd1p1) ^ Dbl_sign(opnd2p1)) + Dbl_setnegativezerop1(resultp1); + else Dbl_setzerop1(resultp1); + /* + * check first operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd1p1)) { + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + if (Dbl_isnotnan(opnd2p1,opnd2p2)) { + if (Dbl_iszero_exponentmantissa(opnd2p1,opnd2p2)) { + /* + * invalid since operands are infinity + * and zero + */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * return infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd1p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd1p1); + } + /* + * is second operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd1p1,opnd1p2,dstptr); + return(NOEXCEPTION); + } + } + /* + * check second operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd2p1)) { + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + if (Dbl_iszero_exponentmantissa(opnd1p1,opnd1p2)) { + /* invalid since operands are zero & infinity */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(opnd2p1,opnd2p2); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * return infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate exponent + */ + dest_exponent = Dbl_exponent(opnd1p1) + Dbl_exponent(opnd2p1) -DBL_BIAS; + + /* + * Generate mantissa + */ + if (Dbl_isnotzero_exponent(opnd1p1)) { + /* set hidden bit */ + Dbl_clear_signexponent_set_hidden(opnd1p1); + } + else { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + Dbl_setzero_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* is denormalized, adjust exponent */ + Dbl_clear_signexponent(opnd1p1); + Dbl_leftshiftby1(opnd1p1,opnd1p2); + Dbl_normalize(opnd1p1,opnd1p2,dest_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Dbl_isnotzero_exponent(opnd2p1)) { + Dbl_clear_signexponent_set_hidden(opnd2p1); + } + else { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + Dbl_setzero_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* is denormalized; want to normalize */ + Dbl_clear_signexponent(opnd2p1); + Dbl_leftshiftby1(opnd2p1,opnd2p2); + Dbl_normalize(opnd2p1,opnd2p2,dest_exponent); + } + + /* Multiply two source mantissas together */ + + /* make room for guard bits */ + Dbl_leftshiftby7(opnd2p1,opnd2p2); + Dbl_setzero(opnd3p1,opnd3p2); + /* + * Four bits at a time are inspected in each loop, and a + * simple shift and add multiply algorithm is used. + */ + for (count=1;count<=DBL_P;count+=4) { + stickybit |= Dlow4p2(opnd3p2); + Dbl_rightshiftby4(opnd3p1,opnd3p2); + if (Dbit28p2(opnd1p2)) { + /* Twoword_add should be an ADDC followed by an ADD. */ + Twoword_add(opnd3p1, opnd3p2, opnd2p1<<3 | opnd2p2>>29, + opnd2p2<<3); + } + if (Dbit29p2(opnd1p2)) { + Twoword_add(opnd3p1, opnd3p2, opnd2p1<<2 | opnd2p2>>30, + opnd2p2<<2); + } + if (Dbit30p2(opnd1p2)) { + Twoword_add(opnd3p1, opnd3p2, opnd2p1<<1 | opnd2p2>>31, + opnd2p2<<1); + } + if (Dbit31p2(opnd1p2)) { + Twoword_add(opnd3p1, opnd3p2, opnd2p1, opnd2p2); + } + Dbl_rightshiftby4(opnd1p1,opnd1p2); + } + if (Dbit3p1(opnd3p1)==0) { + Dbl_leftshiftby1(opnd3p1,opnd3p2); + } + else { + /* result mantissa >= 2. */ + dest_exponent++; + } + /* check for denormalized result */ + while (Dbit3p1(opnd3p1)==0) { + Dbl_leftshiftby1(opnd3p1,opnd3p2); + dest_exponent--; + } + /* + * check for guard, sticky and inexact bits + */ + stickybit |= Dallp2(opnd3p2) << 25; + guardbit = (Dallp2(opnd3p2) << 24) >> 31; + inexact = guardbit | stickybit; + + /* align result mantissa */ + Dbl_rightshiftby8(opnd3p1,opnd3p2); + + /* + * round result + */ + if (inexact && (dest_exponent>0 || Is_underflowtrap_enabled())) { + Dbl_clear_signexponent(opnd3p1); + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) + Dbl_increment(opnd3p1,opnd3p2); + break; + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) + Dbl_increment(opnd3p1,opnd3p2); + break; + case ROUNDNEAREST: + if (guardbit) { + if (stickybit || Dbl_isone_lowmantissap2(opnd3p2)) + Dbl_increment(opnd3p1,opnd3p2); + } + } + if (Dbl_isone_hidden(opnd3p1)) dest_exponent++; + } + Dbl_set_mantissa(resultp1,resultp2,opnd3p1,opnd3p2); + + /* + * Test for overflow + */ + if (dest_exponent >= DBL_INFINITY_EXPONENT) { + /* trap if OVERFLOWTRAP enabled */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,dest_exponent,ovfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return (OVERFLOWEXCEPTION); + } + inexact = TRUE; + Set_overflowflag(); + /* set result to infinity or largest number */ + Dbl_setoverflow(resultp1,resultp2); + } + /* + * Test for underflow + */ + else if (dest_exponent <= 0) { + /* trap if UNDERFLOWTRAP enabled */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,dest_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (UNDERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return (UNDERFLOWEXCEPTION); + } + + /* Determine if should set underflow flag */ + is_tiny = TRUE; + if (dest_exponent == 0 && inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + if (Dbl_isone_hiddenoverflow(opnd3p1)) + is_tiny = FALSE; + Dbl_decrement(opnd3p1,opnd3p2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + if (Dbl_isone_hiddenoverflow(opnd3p1)) + is_tiny = FALSE; + Dbl_decrement(opnd3p1,opnd3p2); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Dbl_isone_lowmantissap2(opnd3p2))) { + Dbl_increment(opnd3p1,opnd3p2); + if (Dbl_isone_hiddenoverflow(opnd3p1)) + is_tiny = FALSE; + Dbl_decrement(opnd3p1,opnd3p2); + } + break; + } + } + + /* + * denormalize result or set to signed zero + */ + stickybit = inexact; + Dbl_denormalize(opnd3p1,opnd3p2,dest_exponent,guardbit, + stickybit,inexact); + + /* return zero or smallest number */ + if (inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) { + Dbl_increment(opnd3p1,opnd3p2); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Dbl_isone_lowmantissap2(opnd3p2))) { + Dbl_increment(opnd3p1,opnd3p2); + } + break; + } + if (is_tiny) Set_underflowflag(); + } + Dbl_set_exponentmantissa(resultp1,resultp2,opnd3p1,opnd3p2); + } + else Dbl_set_exponent(resultp1,dest_exponent); + /* check for inexact */ + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/dfrem.c b/arch/parisc/math-emu/dfrem.c new file mode 100644 index 000000000..9243a5954 --- /dev/null +++ b/arch/parisc/math-emu/dfrem.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/dfrem.c $Revision: 1.1 $ + * + * Purpose: + * Double Precision Floating-point Remainder + * + * External Interfaces: + * dbl_frem(srcptr1,srcptr2,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + + +#include "float.h" +#include "dbl_float.h" + +/* + * Double Precision Floating-point Remainder + */ + +int +dbl_frem (dbl_floating_point * srcptr1, dbl_floating_point * srcptr2, + dbl_floating_point * dstptr, unsigned int *status) +{ + register unsigned int opnd1p1, opnd1p2, opnd2p1, opnd2p2; + register unsigned int resultp1, resultp2; + register int opnd1_exponent, opnd2_exponent, dest_exponent, stepcount; + register boolean roundup = FALSE; + + Dbl_copyfromptr(srcptr1,opnd1p1,opnd1p2); + Dbl_copyfromptr(srcptr2,opnd2p1,opnd2p2); + /* + * check first operand for NaN's or infinity + */ + if ((opnd1_exponent = Dbl_exponent(opnd1p1)) == DBL_INFINITY_EXPONENT) { + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + if (Dbl_isnotnan(opnd2p1,opnd2p2)) { + /* invalid since first operand is infinity */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd1p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd1p1); + } + /* + * is second operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd1p1,opnd1p2,dstptr); + return(NOEXCEPTION); + } + } + /* + * check second operand for NaN's or infinity + */ + if ((opnd2_exponent = Dbl_exponent(opnd2p1)) == DBL_INFINITY_EXPONENT) { + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + /* + * return first operand + */ + Dbl_copytoptr(opnd1p1,opnd1p2,dstptr); + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * check second operand for zero + */ + if (Dbl_iszero_exponentmantissa(opnd2p1,opnd2p2)) { + /* invalid since second operand is zero */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + + /* + * get sign of result + */ + resultp1 = opnd1p1; + + /* + * check for denormalized operands + */ + if (opnd1_exponent == 0) { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + Dbl_copytoptr(opnd1p1,opnd1p2,dstptr); + return(NOEXCEPTION); + } + /* normalize, then continue */ + opnd1_exponent = 1; + Dbl_normalize(opnd1p1,opnd1p2,opnd1_exponent); + } + else { + Dbl_clear_signexponent_set_hidden(opnd1p1); + } + if (opnd2_exponent == 0) { + /* normalize, then continue */ + opnd2_exponent = 1; + Dbl_normalize(opnd2p1,opnd2p2,opnd2_exponent); + } + else { + Dbl_clear_signexponent_set_hidden(opnd2p1); + } + + /* find result exponent and divide step loop count */ + dest_exponent = opnd2_exponent - 1; + stepcount = opnd1_exponent - opnd2_exponent; + + /* + * check for opnd1/opnd2 < 1 + */ + if (stepcount < 0) { + /* + * check for opnd1/opnd2 > 1/2 + * + * In this case n will round to 1, so + * r = opnd1 - opnd2 + */ + if (stepcount == -1 && + Dbl_isgreaterthan(opnd1p1,opnd1p2,opnd2p1,opnd2p2)) { + /* set sign */ + Dbl_allp1(resultp1) = ~Dbl_allp1(resultp1); + /* align opnd2 with opnd1 */ + Dbl_leftshiftby1(opnd2p1,opnd2p2); + Dbl_subtract(opnd2p1,opnd2p2,opnd1p1,opnd1p2, + opnd2p1,opnd2p2); + /* now normalize */ + while (Dbl_iszero_hidden(opnd2p1)) { + Dbl_leftshiftby1(opnd2p1,opnd2p2); + dest_exponent--; + } + Dbl_set_exponentmantissa(resultp1,resultp2,opnd2p1,opnd2p2); + goto testforunderflow; + } + /* + * opnd1/opnd2 <= 1/2 + * + * In this case n will round to zero, so + * r = opnd1 + */ + Dbl_set_exponentmantissa(resultp1,resultp2,opnd1p1,opnd1p2); + dest_exponent = opnd1_exponent; + goto testforunderflow; + } + + /* + * Generate result + * + * Do iterative subtract until remainder is less than operand 2. + */ + while (stepcount-- > 0 && (Dbl_allp1(opnd1p1) || Dbl_allp2(opnd1p2))) { + if (Dbl_isnotlessthan(opnd1p1,opnd1p2,opnd2p1,opnd2p2)) { + Dbl_subtract(opnd1p1,opnd1p2,opnd2p1,opnd2p2,opnd1p1,opnd1p2); + } + Dbl_leftshiftby1(opnd1p1,opnd1p2); + } + /* + * Do last subtract, then determine which way to round if remainder + * is exactly 1/2 of opnd2 + */ + if (Dbl_isnotlessthan(opnd1p1,opnd1p2,opnd2p1,opnd2p2)) { + Dbl_subtract(opnd1p1,opnd1p2,opnd2p1,opnd2p2,opnd1p1,opnd1p2); + roundup = TRUE; + } + if (stepcount > 0 || Dbl_iszero(opnd1p1,opnd1p2)) { + /* division is exact, remainder is zero */ + Dbl_setzero_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + + /* + * Check for cases where opnd1/opnd2 < n + * + * In this case the result's sign will be opposite that of + * opnd1. The mantissa also needs some correction. + */ + Dbl_leftshiftby1(opnd1p1,opnd1p2); + if (Dbl_isgreaterthan(opnd1p1,opnd1p2,opnd2p1,opnd2p2)) { + Dbl_invert_sign(resultp1); + Dbl_leftshiftby1(opnd2p1,opnd2p2); + Dbl_subtract(opnd2p1,opnd2p2,opnd1p1,opnd1p2,opnd1p1,opnd1p2); + } + /* check for remainder being exactly 1/2 of opnd2 */ + else if (Dbl_isequal(opnd1p1,opnd1p2,opnd2p1,opnd2p2) && roundup) { + Dbl_invert_sign(resultp1); + } + + /* normalize result's mantissa */ + while (Dbl_iszero_hidden(opnd1p1)) { + dest_exponent--; + Dbl_leftshiftby1(opnd1p1,opnd1p2); + } + Dbl_set_exponentmantissa(resultp1,resultp2,opnd1p1,opnd1p2); + + /* + * Test for underflow + */ + testforunderflow: + if (dest_exponent <= 0) { + /* trap if UNDERFLOWTRAP enabled */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,dest_exponent,unfl); + /* frem is always exact */ + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(UNDERFLOWEXCEPTION); + } + /* + * denormalize result or set to signed zero + */ + if (dest_exponent >= (1 - DBL_P)) { + Dbl_rightshift_exponentmantissa(resultp1,resultp2, + 1-dest_exponent); + } + else { + Dbl_setzero_exponentmantissa(resultp1,resultp2); + } + } + else Dbl_set_exponent(resultp1,dest_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/dfsqrt.c b/arch/parisc/math-emu/dfsqrt.c new file mode 100644 index 000000000..e3a3a19b9 --- /dev/null +++ b/arch/parisc/math-emu/dfsqrt.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/dfsqrt.c $Revision: 1.1 $ + * + * Purpose: + * Double Floating-point Square Root + * + * External Interfaces: + * dbl_fsqrt(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "dbl_float.h" + +/* + * Double Floating-point Square Root + */ + +/*ARGSUSED*/ +unsigned int +dbl_fsqrt( + dbl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int srcp1, srcp2, resultp1, resultp2; + register unsigned int newbitp1, newbitp2, sump1, sump2; + register int src_exponent; + register boolean guardbit = FALSE, even_exponent; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + /* + * check source operand for NaN or infinity + */ + if ((src_exponent = Dbl_exponent(srcp1)) == DBL_INFINITY_EXPONENT) { + /* + * is signaling NaN? + */ + if (Dbl_isone_signaling(srcp1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(srcp1); + } + /* + * Return quiet NaN or positive infinity. + * Fall through to negative test if negative infinity. + */ + if (Dbl_iszero_sign(srcp1) || + Dbl_isnotzero_mantissa(srcp1,srcp2)) { + Dbl_copytoptr(srcp1,srcp2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check for zero source operand + */ + if (Dbl_iszero_exponentmantissa(srcp1,srcp2)) { + Dbl_copytoptr(srcp1,srcp2,dstptr); + return(NOEXCEPTION); + } + + /* + * check for negative source operand + */ + if (Dbl_isone_sign(srcp1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_makequietnan(srcp1,srcp2); + Dbl_copytoptr(srcp1,srcp2,dstptr); + return(NOEXCEPTION); + } + + /* + * Generate result + */ + if (src_exponent > 0) { + even_exponent = Dbl_hidden(srcp1); + Dbl_clear_signexponent_set_hidden(srcp1); + } + else { + /* normalize operand */ + Dbl_clear_signexponent(srcp1); + src_exponent++; + Dbl_normalize(srcp1,srcp2,src_exponent); + even_exponent = src_exponent & 1; + } + if (even_exponent) { + /* exponent is even */ + /* Add comment here. Explain why odd exponent needs correction */ + Dbl_leftshiftby1(srcp1,srcp2); + } + /* + * Add comment here. Explain following algorithm. + * + * Trust me, it works. + * + */ + Dbl_setzero(resultp1,resultp2); + Dbl_allp1(newbitp1) = 1 << (DBL_P - 32); + Dbl_setzero_mantissap2(newbitp2); + while (Dbl_isnotzero(newbitp1,newbitp2) && Dbl_isnotzero(srcp1,srcp2)) { + Dbl_addition(resultp1,resultp2,newbitp1,newbitp2,sump1,sump2); + if(Dbl_isnotgreaterthan(sump1,sump2,srcp1,srcp2)) { + Dbl_leftshiftby1(newbitp1,newbitp2); + /* update result */ + Dbl_addition(resultp1,resultp2,newbitp1,newbitp2, + resultp1,resultp2); + Dbl_subtract(srcp1,srcp2,sump1,sump2,srcp1,srcp2); + Dbl_rightshiftby2(newbitp1,newbitp2); + } + else { + Dbl_rightshiftby1(newbitp1,newbitp2); + } + Dbl_leftshiftby1(srcp1,srcp2); + } + /* correct exponent for pre-shift */ + if (even_exponent) { + Dbl_rightshiftby1(resultp1,resultp2); + } + + /* check for inexact */ + if (Dbl_isnotzero(srcp1,srcp2)) { + if (!even_exponent && Dbl_islessthan(resultp1,resultp2,srcp1,srcp2)) { + Dbl_increment(resultp1,resultp2); + } + guardbit = Dbl_lowmantissap2(resultp2); + Dbl_rightshiftby1(resultp1,resultp2); + + /* now round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + Dbl_increment(resultp1,resultp2); + break; + case ROUNDNEAREST: + /* stickybit is always true, so guardbit + * is enough to determine rounding */ + if (guardbit) { + Dbl_increment(resultp1,resultp2); + } + break; + } + /* increment result exponent by 1 if mantissa overflowed */ + if (Dbl_isone_hiddenoverflow(resultp1)) src_exponent+=2; + + if (Is_inexacttrap_enabled()) { + Dbl_set_exponent(resultp1, + ((src_exponent-DBL_BIAS)>>1)+DBL_BIAS); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + else { + Dbl_rightshiftby1(resultp1,resultp2); + } + Dbl_set_exponent(resultp1,((src_exponent-DBL_BIAS)>>1)+DBL_BIAS); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/dfsub.c b/arch/parisc/math-emu/dfsub.c new file mode 100644 index 000000000..4f0378228 --- /dev/null +++ b/arch/parisc/math-emu/dfsub.c @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/dfsub.c $Revision: 1.1 $ + * + * Purpose: + * Double_subtract: subtract two double precision values. + * + * External Interfaces: + * dbl_fsub(leftptr, rightptr, dstptr, status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "dbl_float.h" + +/* + * Double_subtract: subtract two double precision values. + */ +int +dbl_fsub( + dbl_floating_point *leftptr, + dbl_floating_point *rightptr, + dbl_floating_point *dstptr, + unsigned int *status) + { + register unsigned int signless_upper_left, signless_upper_right, save; + register unsigned int leftp1, leftp2, rightp1, rightp2, extent; + register unsigned int resultp1 = 0, resultp2 = 0; + + register int result_exponent, right_exponent, diff_exponent; + register int sign_save, jumpsize; + register boolean inexact = FALSE, underflowtrap; + + /* Create local copies of the numbers */ + Dbl_copyfromptr(leftptr,leftp1,leftp2); + Dbl_copyfromptr(rightptr,rightp1,rightp2); + + /* A zero "save" helps discover equal operands (for later), * + * and is used in swapping operands (if needed). */ + Dbl_xortointp1(leftp1,rightp1,/*to*/save); + + /* + * check first operand for NaN's or infinity + */ + if ((result_exponent = Dbl_exponent(leftp1)) == DBL_INFINITY_EXPONENT) + { + if (Dbl_iszero_mantissa(leftp1,leftp2)) + { + if (Dbl_isnotnan(rightp1,rightp2)) + { + if (Dbl_isinfinity(rightp1,rightp2) && save==0) + { + /* + * invalid since operands are same signed infinity's + */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * return infinity + */ + Dbl_copytoptr(leftp1,leftp2,dstptr); + return(NOEXCEPTION); + } + } + else + { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(leftp1)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(leftp1); + } + /* + * is second operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(rightp1)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(rightp1); + Dbl_copytoptr(rightp1,rightp2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(leftp1,leftp2,dstptr); + return(NOEXCEPTION); + } + } /* End left NaN or Infinity processing */ + /* + * check second operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(rightp1)) + { + if (Dbl_iszero_mantissa(rightp1,rightp2)) + { + /* return infinity */ + Dbl_invert_sign(rightp1); + Dbl_copytoptr(rightp1,rightp2,dstptr); + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(rightp1)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(rightp1); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(rightp1,rightp2,dstptr); + return(NOEXCEPTION); + } /* End right NaN or Infinity processing */ + + /* Invariant: Must be dealing with finite numbers */ + + /* Compare operands by removing the sign */ + Dbl_copytoint_exponentmantissap1(leftp1,signless_upper_left); + Dbl_copytoint_exponentmantissap1(rightp1,signless_upper_right); + + /* sign difference selects add or sub operation. */ + if(Dbl_ismagnitudeless(leftp2,rightp2,signless_upper_left,signless_upper_right)) + { + /* Set the left operand to the larger one by XOR swap * + * First finish the first word using "save" */ + Dbl_xorfromintp1(save,rightp1,/*to*/rightp1); + Dbl_xorfromintp1(save,leftp1,/*to*/leftp1); + Dbl_swap_lower(leftp2,rightp2); + result_exponent = Dbl_exponent(leftp1); + Dbl_invert_sign(leftp1); + } + /* Invariant: left is not smaller than right. */ + + if((right_exponent = Dbl_exponent(rightp1)) == 0) + { + /* Denormalized operands. First look for zeroes */ + if(Dbl_iszero_mantissa(rightp1,rightp2)) + { + /* right is zero */ + if(Dbl_iszero_exponentmantissa(leftp1,leftp2)) + { + /* Both operands are zeros */ + Dbl_invert_sign(rightp1); + if(Is_rounding_mode(ROUNDMINUS)) + { + Dbl_or_signs(leftp1,/*with*/rightp1); + } + else + { + Dbl_and_signs(leftp1,/*with*/rightp1); + } + } + else + { + /* Left is not a zero and must be the result. Trapped + * underflows are signaled if left is denormalized. Result + * is always exact. */ + if( (result_exponent == 0) && Is_underflowtrap_enabled() ) + { + /* need to normalize results mantissa */ + sign_save = Dbl_signextendedsign(leftp1); + Dbl_leftshiftby1(leftp1,leftp2); + Dbl_normalize(leftp1,leftp2,result_exponent); + Dbl_set_sign(leftp1,/*using*/sign_save); + Dbl_setwrapped_exponent(leftp1,result_exponent,unfl); + Dbl_copytoptr(leftp1,leftp2,dstptr); + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + } + Dbl_copytoptr(leftp1,leftp2,dstptr); + return(NOEXCEPTION); + } + + /* Neither are zeroes */ + Dbl_clear_sign(rightp1); /* Exponent is already cleared */ + if(result_exponent == 0 ) + { + /* Both operands are denormalized. The result must be exact + * and is simply calculated. A sum could become normalized and a + * difference could cancel to a true zero. */ + if( (/*signed*/int) save >= 0 ) + { + Dbl_subtract(leftp1,leftp2,/*minus*/rightp1,rightp2, + /*into*/resultp1,resultp2); + if(Dbl_iszero_mantissa(resultp1,resultp2)) + { + if(Is_rounding_mode(ROUNDMINUS)) + { + Dbl_setone_sign(resultp1); + } + else + { + Dbl_setzero_sign(resultp1); + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else + { + Dbl_addition(leftp1,leftp2,rightp1,rightp2, + /*into*/resultp1,resultp2); + if(Dbl_isone_hidden(resultp1)) + { + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + if(Is_underflowtrap_enabled()) + { + /* need to normalize result */ + sign_save = Dbl_signextendedsign(resultp1); + Dbl_leftshiftby1(resultp1,resultp2); + Dbl_normalize(resultp1,resultp2,result_exponent); + Dbl_set_sign(resultp1,/*using*/sign_save); + Dbl_setwrapped_exponent(resultp1,result_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + right_exponent = 1; /* Set exponent to reflect different bias + * with denormalized numbers. */ + } + else + { + Dbl_clear_signexponent_set_hidden(rightp1); + } + Dbl_clear_exponent_set_hidden(leftp1); + diff_exponent = result_exponent - right_exponent; + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for this + * infrequent case. + */ + if(diff_exponent > DBL_THRESHOLD) + { + diff_exponent = DBL_THRESHOLD; + } + + /* Align right operand by shifting to right */ + Dbl_right_align(/*operand*/rightp1,rightp2,/*shifted by*/diff_exponent, + /*and lower to*/extent); + + /* Treat sum and difference of the operands separately. */ + if( (/*signed*/int) save >= 0 ) + { + /* + * Difference of the two operands. Their can be no overflow. A + * borrow can occur out of the hidden bit and force a post + * normalization phase. + */ + Dbl_subtract_withextension(leftp1,leftp2,/*minus*/rightp1,rightp2, + /*with*/extent,/*into*/resultp1,resultp2); + if(Dbl_iszero_hidden(resultp1)) + { + /* Handle normalization */ + /* A straight forward algorithm would now shift the result + * and extension left until the hidden bit becomes one. Not + * all of the extension bits need participate in the shift. + * Only the two most significant bits (round and guard) are + * needed. If only a single shift is needed then the guard + * bit becomes a significant low order bit and the extension + * must participate in the rounding. If more than a single + * shift is needed, then all bits to the right of the guard + * bit are zeros, and the guard bit may or may not be zero. */ + sign_save = Dbl_signextendedsign(resultp1); + Dbl_leftshiftby1_withextent(resultp1,resultp2,extent,resultp1,resultp2); + + /* Need to check for a zero result. The sign and exponent + * fields have already been zeroed. The more efficient test + * of the full object can be used. + */ + if(Dbl_iszero(resultp1,resultp2)) + /* Must have been "x-x" or "x+(-x)". */ + { + if(Is_rounding_mode(ROUNDMINUS)) Dbl_setone_sign(resultp1); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + result_exponent--; + /* Look to see if normalization is finished. */ + if(Dbl_isone_hidden(resultp1)) + { + if(result_exponent==0) + { + /* Denormalized, exponent should be zero. Left operand * + * was normalized, so extent (guard, round) was zero */ + goto underflow; + } + else + { + /* No further normalization is needed. */ + Dbl_set_sign(resultp1,/*using*/sign_save); + Ext_leftshiftby1(extent); + goto round; + } + } + + /* Check for denormalized, exponent should be zero. Left * + * operand was normalized, so extent (guard, round) was zero */ + if(!(underflowtrap = Is_underflowtrap_enabled()) && + result_exponent==0) goto underflow; + + /* Shift extension to complete one bit of normalization and + * update exponent. */ + Ext_leftshiftby1(extent); + + /* Discover first one bit to determine shift amount. Use a + * modified binary search. We have already shifted the result + * one position right and still not found a one so the remainder + * of the extension must be zero and simplifies rounding. */ + /* Scan bytes */ + while(Dbl_iszero_hiddenhigh7mantissa(resultp1)) + { + Dbl_leftshiftby8(resultp1,resultp2); + if((result_exponent -= 8) <= 0 && !underflowtrap) + goto underflow; + } + /* Now narrow it down to the nibble */ + if(Dbl_iszero_hiddenhigh3mantissa(resultp1)) + { + /* The lower nibble contains the normalizing one */ + Dbl_leftshiftby4(resultp1,resultp2); + if((result_exponent -= 4) <= 0 && !underflowtrap) + goto underflow; + } + /* Select case were first bit is set (already normalized) + * otherwise select the proper shift. */ + if((jumpsize = Dbl_hiddenhigh3mantissa(resultp1)) > 7) + { + /* Already normalized */ + if(result_exponent <= 0) goto underflow; + Dbl_set_sign(resultp1,/*using*/sign_save); + Dbl_set_exponent(resultp1,/*using*/result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Dbl_sethigh4bits(resultp1,/*using*/sign_save); + switch(jumpsize) + { + case 1: + { + Dbl_leftshiftby3(resultp1,resultp2); + result_exponent -= 3; + break; + } + case 2: + case 3: + { + Dbl_leftshiftby2(resultp1,resultp2); + result_exponent -= 2; + break; + } + case 4: + case 5: + case 6: + case 7: + { + Dbl_leftshiftby1(resultp1,resultp2); + result_exponent -= 1; + break; + } + } + if(result_exponent > 0) + { + Dbl_set_exponent(resultp1,/*using*/result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); /* Sign bit is already set */ + } + /* Fixup potential underflows */ + underflow: + if(Is_underflowtrap_enabled()) + { + Dbl_set_sign(resultp1,sign_save); + Dbl_setwrapped_exponent(resultp1,result_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + /* + * Since we cannot get an inexact denormalized result, + * we can now return. + */ + Dbl_fix_overshift(resultp1,resultp2,(1-result_exponent),extent); + Dbl_clear_signexponent(resultp1); + Dbl_set_sign(resultp1,sign_save); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } /* end if(hidden...)... */ + /* Fall through and round */ + } /* end if(save >= 0)... */ + else + { + /* Subtract magnitudes */ + Dbl_addition(leftp1,leftp2,rightp1,rightp2,/*to*/resultp1,resultp2); + if(Dbl_isone_hiddenoverflow(resultp1)) + { + /* Prenormalization required. */ + Dbl_rightshiftby1_withextent(resultp2,extent,extent); + Dbl_arithrightshiftby1(resultp1,resultp2); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...subtract magnitudes... */ + + /* Round the result. If the extension is all zeros,then the result is + * exact. Otherwise round in the correct direction. No underflow is + * possible. If a postnormalization is necessary, then the mantissa is + * all zeros so no shift is needed. */ + round: + if(Ext_isnotzero(extent)) + { + inexact = TRUE; + switch(Rounding_mode()) + { + case ROUNDNEAREST: /* The default. */ + if(Ext_isone_sign(extent)) + { + /* at least 1/2 ulp */ + if(Ext_isnotzero_lower(extent) || + Dbl_isone_lowmantissap2(resultp2)) + { + /* either exactly half way and odd or more than 1/2ulp */ + Dbl_increment(resultp1,resultp2); + } + } + break; + + case ROUNDPLUS: + if(Dbl_iszero_sign(resultp1)) + { + /* Round up positive results */ + Dbl_increment(resultp1,resultp2); + } + break; + + case ROUNDMINUS: + if(Dbl_isone_sign(resultp1)) + { + /* Round down negative results */ + Dbl_increment(resultp1,resultp2); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if(Dbl_isone_hiddenoverflow(resultp1)) result_exponent++; + } + if(result_exponent == DBL_INFINITY_EXPONENT) + { + /* Overflow */ + if(Is_overflowtrap_enabled()) + { + Dbl_setwrapped_exponent(resultp1,result_exponent,ovfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + else + { + inexact = TRUE; + Set_overflowflag(); + Dbl_setoverflow(resultp1,resultp2); + } + } + else Dbl_set_exponent(resultp1,result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if(inexact) + if(Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); + } diff --git a/arch/parisc/math-emu/driver.c b/arch/parisc/math-emu/driver.c new file mode 100644 index 000000000..71829cb7b --- /dev/null +++ b/arch/parisc/math-emu/driver.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * linux/arch/math-emu/driver.c.c + * + * decodes and dispatches unimplemented FPU instructions + * + * Copyright (C) 1999, 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2001 Hewlett-Packard <bame@debian.org> + */ + +#include <linux/sched/signal.h> + +#include "float.h" +#include "math-emu.h" + + +#define fptpos 31 +#define fpr1pos 10 +#define extru(r,pos,len) (((r) >> (31-(pos))) & (( 1 << (len)) - 1)) + +#define FPUDEBUG 0 + +/* Macros for grabbing bits of the instruction format from the 'ei' + field above. */ +/* Major opcode 0c and 0e */ +#define FP0CE_UID(i) (((i) >> 6) & 3) +#define FP0CE_CLASS(i) (((i) >> 9) & 3) +#define FP0CE_SUBOP(i) (((i) >> 13) & 7) +#define FP0CE_SUBOP1(i) (((i) >> 15) & 7) /* Class 1 subopcode */ +#define FP0C_FORMAT(i) (((i) >> 11) & 3) +#define FP0E_FORMAT(i) (((i) >> 11) & 1) + +/* Major opcode 0c, uid 2 (performance monitoring) */ +#define FPPM_SUBOP(i) (((i) >> 9) & 0x1f) + +/* Major opcode 2e (fused operations). */ +#define FP2E_SUBOP(i) (((i) >> 5) & 1) +#define FP2E_FORMAT(i) (((i) >> 11) & 1) + +/* Major opcode 26 (FMPYSUB) */ +/* Major opcode 06 (FMPYADD) */ +#define FPx6_FORMAT(i) ((i) & 0x1f) + +/* Flags and enable bits of the status word. */ +#define FPSW_FLAGS(w) ((w) >> 27) +#define FPSW_ENABLE(w) ((w) & 0x1f) +#define FPSW_V (1<<4) +#define FPSW_Z (1<<3) +#define FPSW_O (1<<2) +#define FPSW_U (1<<1) +#define FPSW_I (1<<0) + +/* Handle a floating point exception. Return zero if the faulting + instruction can be completed successfully. */ +int +handle_fpe(struct pt_regs *regs) +{ + extern void printbinary(unsigned long x, int nbits); + unsigned int orig_sw, sw; + int signalcode; + /* need an intermediate copy of float regs because FPU emulation + * code expects an artificial last entry which contains zero + * + * also, the passed in fr registers contain one word that defines + * the fpu type. the fpu type information is constructed + * inside the emulation code + */ + __u64 frcopy[36]; + + memcpy(frcopy, regs->fr, sizeof regs->fr); + frcopy[32] = 0; + + memcpy(&orig_sw, frcopy, sizeof(orig_sw)); + + if (FPUDEBUG) { + printk(KERN_DEBUG "FP VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI ->\n "); + printbinary(orig_sw, 32); + printk(KERN_DEBUG "\n"); + } + + signalcode = decode_fpu(frcopy, 0x666); + + /* Status word = FR0L. */ + memcpy(&sw, frcopy, sizeof(sw)); + if (FPUDEBUG) { + printk(KERN_DEBUG "VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI decode_fpu returns %d|0x%x\n", + signalcode >> 24, signalcode & 0xffffff); + printbinary(sw, 32); + printk(KERN_DEBUG "\n"); + } + + memcpy(regs->fr, frcopy, sizeof regs->fr); + if (signalcode != 0) { + int sig = signalcode >> 24; + + if (sig == SIGFPE) { + /* + * Clear floating point trap bit to avoid trapping + * again on the first floating-point instruction in + * the userspace signal handler. + */ + regs->fr[0] &= ~(1ULL << 38); + } + force_sig_fault(sig, signalcode & 0xffffff, + (void __user *) regs->iaoq[0]); + return -1; + } + + return signalcode ? -1 : 0; +} diff --git a/arch/parisc/math-emu/fcnvff.c b/arch/parisc/math-emu/fcnvff.c new file mode 100644 index 000000000..61e489704 --- /dev/null +++ b/arch/parisc/math-emu/fcnvff.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fcnvff.c $Revision: 1.1 $ + * + * Purpose: + * Single Floating-point to Double Floating-point + * Double Floating-point to Single Floating-point + * + * External Interfaces: + * dbl_to_sgl_fcnvff(srcptr,_nullptr,dstptr,status) + * sgl_to_dbl_fcnvff(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/* + * Single Floating-point to Double Floating-point + */ +/*ARGSUSED*/ +int +sgl_to_dbl_fcnvff( + sgl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int src, resultp1, resultp2; + register int src_exponent; + + src = *srcptr; + src_exponent = Sgl_exponent(src); + Dbl_allp1(resultp1) = Sgl_all(src); /* set sign of result */ + /* + * Test for NaN or infinity + */ + if (src_exponent == SGL_INFINITY_EXPONENT) { + /* + * determine if NaN or infinity + */ + if (Sgl_iszero_mantissa(src)) { + /* + * is infinity; want to return double infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(src)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + else { + Set_invalidflag(); + Sgl_set_quiet(src); + } + } + /* + * NaN is quiet, return as double NaN + */ + Dbl_setinfinity_exponent(resultp1); + Sgl_to_dbl_mantissa(src,resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + /* + * Test for zero or denormalized + */ + if (src_exponent == 0) { + /* + * determine if zero or denormalized + */ + if (Sgl_isnotzero_mantissa(src)) { + /* + * is denormalized; want to normalize + */ + Sgl_clear_signexponent(src); + Sgl_leftshiftby1(src); + Sgl_normalize(src,src_exponent); + Sgl_to_dbl_exponent(src_exponent,resultp1); + Sgl_to_dbl_mantissa(src,resultp1,resultp2); + } + else { + Dbl_setzero_exponentmantissa(resultp1,resultp2); + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * No special cases, just complete the conversion + */ + Sgl_to_dbl_exponent(src_exponent, resultp1); + Sgl_to_dbl_mantissa(Sgl_mantissa(src), resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Single Floating-point + */ +/*ARGSUSED*/ +int +dbl_to_sgl_fcnvff( + dbl_floating_point *srcptr, + unsigned int *_nullptr, + sgl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int srcp1, srcp2, result; + register int src_exponent, dest_exponent, dest_mantissa; + register boolean inexact = FALSE, guardbit = FALSE, stickybit = FALSE; + register boolean lsb_odd = FALSE; + boolean is_tiny = FALSE; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1); + Sgl_all(result) = Dbl_allp1(srcp1); /* set sign of result */ + /* + * Test for NaN or infinity + */ + if (src_exponent == DBL_INFINITY_EXPONENT) { + /* + * determine if NaN or infinity + */ + if (Dbl_iszero_mantissa(srcp1,srcp2)) { + /* + * is infinity; want to return single infinity + */ + Sgl_setinfinity_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(srcp1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + else { + Set_invalidflag(); + /* make NaN quiet */ + Dbl_set_quiet(srcp1); + } + } + /* + * NaN is quiet, return as single NaN + */ + Sgl_setinfinity_exponent(result); + Sgl_set_mantissa(result,Dallp1(srcp1)<<3 | Dallp2(srcp2)>>29); + if (Sgl_iszero_mantissa(result)) Sgl_set_quiet(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate result + */ + Dbl_to_sgl_exponent(src_exponent,dest_exponent); + if (dest_exponent > 0) { + Dbl_to_sgl_mantissa(srcp1,srcp2,dest_mantissa,inexact,guardbit, + stickybit,lsb_odd); + } + else { + if (Dbl_iszero_exponentmantissa(srcp1,srcp2)){ + Sgl_setzero_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + if (Is_underflowtrap_enabled()) { + Dbl_to_sgl_mantissa(srcp1,srcp2,dest_mantissa,inexact, + guardbit,stickybit,lsb_odd); + } + else { + /* compute result, determine inexact info, + * and set Underflowflag if appropriate + */ + Dbl_to_sgl_denormalized(srcp1,srcp2,dest_exponent, + dest_mantissa,inexact,guardbit,stickybit,lsb_odd, + is_tiny); + } + } + /* + * Now round result if not exact + */ + if (inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) dest_mantissa++; + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) dest_mantissa++; + break; + case ROUNDNEAREST: + if (guardbit) { + if (stickybit || lsb_odd) dest_mantissa++; + } + } + } + Sgl_set_exponentmantissa(result,dest_mantissa); + + /* + * check for mantissa overflow after rounding + */ + if ((dest_exponent>0 || Is_underflowtrap_enabled()) && + Sgl_isone_hidden(result)) dest_exponent++; + + /* + * Test for overflow + */ + if (dest_exponent >= SGL_INFINITY_EXPONENT) { + /* trap if OVERFLOWTRAP enabled */ + if (Is_overflowtrap_enabled()) { + /* + * Check for gross overflow + */ + if (dest_exponent >= SGL_INFINITY_EXPONENT+SGL_WRAP) + return(UNIMPLEMENTEDEXCEPTION); + + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(result,dest_exponent,ovfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION|INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + Set_overflowflag(); + inexact = TRUE; + /* set result to infinity or largest number */ + Sgl_setoverflow(result); + } + /* + * Test for underflow + */ + else if (dest_exponent <= 0) { + /* trap if UNDERFLOWTRAP enabled */ + if (Is_underflowtrap_enabled()) { + /* + * Check for gross underflow + */ + if (dest_exponent <= -(SGL_WRAP)) + return(UNIMPLEMENTEDEXCEPTION); + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(result,dest_exponent,unfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(UNDERFLOWEXCEPTION|INEXACTEXCEPTION); + else Set_inexactflag(); + return(UNDERFLOWEXCEPTION); + } + /* + * result is denormalized or signed zero + */ + if (inexact && is_tiny) Set_underflowflag(); + + } + else Sgl_set_exponent(result,dest_exponent); + *dstptr = result; + /* + * Trap if inexact trap is enabled + */ + if (inexact) + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/fcnvfu.c b/arch/parisc/math-emu/fcnvfu.c new file mode 100644 index 000000000..c31790cee --- /dev/null +++ b/arch/parisc/math-emu/fcnvfu.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fcnvfu.c $Revision: 1.1 $ + * + * Purpose: + * Floating-point to Unsigned Fixed-point Converts + * + * External Interfaces: + * dbl_to_dbl_fcnvfu(srcptr,_nullptr,dstptr,status) + * dbl_to_sgl_fcnvfu(srcptr,_nullptr,dstptr,status) + * sgl_to_dbl_fcnvfu(srcptr,_nullptr,dstptr,status) + * sgl_to_sgl_fcnvfu(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/************************************************************************ + * Floating-point to Unsigned Fixed-point Converts * + ************************************************************************/ + +/* + * Single Floating-point to Single Unsigned Fixed + */ +/*ARGSUSED*/ +int +sgl_to_sgl_fcnvfu( + sgl_floating_point *srcptr, + unsigned int *_nullptr, + unsigned int *dstptr, + unsigned int *status) +{ + register unsigned int src, result; + register int src_exponent; + register boolean inexact = FALSE; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP + 1) { + if (Sgl_isone_sign(src)) { + result = 0; + } else { + result = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Sgl_isone_sign(src)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + Sgl_clear_signexponent_set_hidden(src); + Suint_from_sgl_mantissa(src,src_exponent,result); + + /* check for inexact */ + if (Sgl_isinexact_to_unsigned(src,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + result++; + break; + case ROUNDMINUS: /* never negative */ + break; + case ROUNDNEAREST: + if (Sgl_isone_roundbit(src,src_exponent) && + (Sgl_isone_stickybit(src,src_exponent) || + (result & 1))) { + result++; + } + break; + } + } + } else { + result = 0; + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) { + result++; + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + break; + case ROUNDNEAREST: + if (src_exponent == -1 && + Sgl_isnotzero_mantissa(src)) { + if (Sgl_isone_sign(src)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + else result++; + } + break; + } + } + } + *dstptr = result; + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + +/* + * Single Floating-point to Double Unsigned Fixed + */ +/*ARGSUSED*/ +int +sgl_to_dbl_fcnvfu( + sgl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_unsigned *dstptr, + unsigned int *status) +{ + register int src_exponent; + register unsigned int src, resultp1, resultp2; + register boolean inexact = FALSE; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP + 1) { + if (Sgl_isone_sign(src)) { + resultp1 = resultp2 = 0; + } else { + resultp1 = resultp2 = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Sgl_isone_sign(src)) { + resultp1 = resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Sgl_clear_signexponent_set_hidden(src); + Duint_from_sgl_mantissa(src,src_exponent,resultp1,resultp2); + + /* check for inexact */ + if (Sgl_isinexact_to_unsigned(src,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + Duint_increment(resultp1,resultp2); + break; + case ROUNDMINUS: /* never negative */ + break; + case ROUNDNEAREST: + if (Sgl_isone_roundbit(src,src_exponent) && + (Sgl_isone_stickybit(src,src_exponent) || + Duint_isone_lowp2(resultp2))) { + Duint_increment(resultp1,resultp2); + } + break; + } + } + } else { + Duint_setzero(resultp1,resultp2); + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) { + Duint_increment(resultp1,resultp2); + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) { + resultp1 = resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + break; + case ROUNDNEAREST: + if (src_exponent == -1 && + Sgl_isnotzero_mantissa(src)) { + if (Sgl_isone_sign(src)) { + resultp1 = 0; + resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + else Duint_increment(resultp1,resultp2); + } + } + } + } + Duint_copytoptr(resultp1,resultp2,dstptr); + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Single Unsigned Fixed + */ +/*ARGSUSED*/ +int +dbl_to_sgl_fcnvfu (dbl_floating_point * srcptr, unsigned int *_nullptr, + unsigned int *dstptr, unsigned int *status) +{ + register unsigned int srcp1, srcp2, result; + register int src_exponent; + register boolean inexact = FALSE; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP + 1) { + if (Dbl_isone_sign(srcp1)) { + result = 0; + } else { + result = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Dbl_isone_sign(srcp1)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + Dbl_clear_signexponent_set_hidden(srcp1); + Suint_from_dbl_mantissa(srcp1,srcp2,src_exponent,result); + + /* check for inexact */ + if (Dbl_isinexact_to_unsigned(srcp1,srcp2,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + result++; + break; + case ROUNDMINUS: /* never negative */ + break; + case ROUNDNEAREST: + if(Dbl_isone_roundbit(srcp1,srcp2,src_exponent) && + (Dbl_isone_stickybit(srcp1,srcp2,src_exponent)|| + result&1)) + result++; + break; + } + /* check for overflow */ + if (result == 0) { + result = 0xffffffff; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + } + } else { + result = 0; + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) result++; + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + break; + case ROUNDNEAREST: + if (src_exponent == -1 && + Dbl_isnotzero_mantissa(srcp1,srcp2)) + if (Dbl_isone_sign(srcp1)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + else result++; + } + } + } + *dstptr = result; + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Double Unsigned Fixed + */ +/*ARGSUSED*/ +int +dbl_to_dbl_fcnvfu (dbl_floating_point * srcptr, unsigned int *_nullptr, + dbl_unsigned * dstptr, unsigned int *status) +{ + register int src_exponent; + register unsigned int srcp1, srcp2, resultp1, resultp2; + register boolean inexact = FALSE; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP + 1) { + if (Dbl_isone_sign(srcp1)) { + resultp1 = resultp2 = 0; + } else { + resultp1 = resultp2 = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Dbl_isone_sign(srcp1)) { + resultp1 = resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Dbl_clear_signexponent_set_hidden(srcp1); + Duint_from_dbl_mantissa(srcp1,srcp2,src_exponent,resultp1, + resultp2); + + /* check for inexact */ + if (Dbl_isinexact_to_unsigned(srcp1,srcp2,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + Duint_increment(resultp1,resultp2); + break; + case ROUNDMINUS: /* never negative */ + break; + case ROUNDNEAREST: + if(Dbl_isone_roundbit(srcp1,srcp2,src_exponent)) + if(Dbl_isone_stickybit(srcp1,srcp2,src_exponent) || + Duint_isone_lowp2(resultp2)) + Duint_increment(resultp1,resultp2); + } + } + } else { + Duint_setzero(resultp1,resultp2); + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) { + Duint_increment(resultp1,resultp2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) { + resultp1 = resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + break; + case ROUNDNEAREST: + if (src_exponent == -1 && + Dbl_isnotzero_mantissa(srcp1,srcp2)) + if (Dbl_iszero_sign(srcp1)) { + Duint_increment(resultp1,resultp2); + } else { + resultp1 = 0; + resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + inexact = FALSE; + } + } + } + } + Duint_copytoptr(resultp1,resultp2,dstptr); + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + diff --git a/arch/parisc/math-emu/fcnvfut.c b/arch/parisc/math-emu/fcnvfut.c new file mode 100644 index 000000000..2cf1daf3b --- /dev/null +++ b/arch/parisc/math-emu/fcnvfut.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fcnvfut.c $Revision: 1.1 $ + * + * Purpose: + * Floating-point to Unsigned Fixed-point Converts with Truncation + * + * External Interfaces: + * dbl_to_dbl_fcnvfut(srcptr,_nullptr,dstptr,status) + * dbl_to_sgl_fcnvfut(srcptr,_nullptr,dstptr,status) + * sgl_to_dbl_fcnvfut(srcptr,_nullptr,dstptr,status) + * sgl_to_sgl_fcnvfut(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/************************************************************************ + * Floating-point to Unsigned Fixed-point Converts with Truncation * + ************************************************************************/ + +/* + * Convert single floating-point to single fixed-point format + * with truncated result + */ +/*ARGSUSED*/ +int +sgl_to_sgl_fcnvfut (sgl_floating_point * srcptr, unsigned int *_nullptr, + unsigned int *dstptr, unsigned int *status) +{ + register unsigned int src, result; + register int src_exponent; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP + 1) { + if (Sgl_isone_sign(src)) { + result = 0; + } else { + result = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Sgl_isone_sign(src)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + Sgl_clear_signexponent_set_hidden(src); + Suint_from_sgl_mantissa(src,src_exponent,result); + *dstptr = result; + + /* check for inexact */ + if (Sgl_isinexact_to_unsigned(src,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + *dstptr = 0; + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} + +/* + * Single Floating-point to Double Unsigned Fixed + */ +/*ARGSUSED*/ +int +sgl_to_dbl_fcnvfut (sgl_floating_point * srcptr, unsigned int *_nullptr, + dbl_unsigned * dstptr, unsigned int *status) +{ + register int src_exponent; + register unsigned int src, resultp1, resultp2; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP + 1) { + if (Sgl_isone_sign(src)) { + resultp1 = resultp2 = 0; + } else { + resultp1 = resultp2 = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Sgl_isone_sign(src)) { + resultp1 = resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Sgl_clear_signexponent_set_hidden(src); + Duint_from_sgl_mantissa(src,src_exponent,resultp1,resultp2); + Duint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Sgl_isinexact_to_unsigned(src,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + Duint_setzero(resultp1,resultp2); + Duint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Single Unsigned Fixed + */ +/*ARGSUSED*/ +int +dbl_to_sgl_fcnvfut (dbl_floating_point * srcptr, unsigned int *_nullptr, + unsigned int *dstptr, unsigned int *status) +{ + register unsigned int srcp1, srcp2, result; + register int src_exponent; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP + 1) { + if (Dbl_isone_sign(srcp1)) { + result = 0; + } else { + result = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Dbl_isone_sign(srcp1)) { + result = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + Dbl_clear_signexponent_set_hidden(srcp1); + Suint_from_dbl_mantissa(srcp1,srcp2,src_exponent,result); + *dstptr = result; + + /* check for inexact */ + if (Dbl_isinexact_to_unsigned(srcp1,srcp2,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + *dstptr = 0; + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Double Unsigned Fixed + */ +/*ARGSUSED*/ +int +dbl_to_dbl_fcnvfut (dbl_floating_point * srcptr, unsigned int *_nullptr, + dbl_unsigned * dstptr, unsigned int *status) +{ + register int src_exponent; + register unsigned int srcp1, srcp2, resultp1, resultp2; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP + 1) { + if (Dbl_isone_sign(srcp1)) { + resultp1 = resultp2 = 0; + } else { + resultp1 = resultp2 = 0xffffffff; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + /* + * Check sign. + * If negative, trap unimplemented. + */ + if (Dbl_isone_sign(srcp1)) { + resultp1 = resultp2 = 0; + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Duint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Dbl_clear_signexponent_set_hidden(srcp1); + Duint_from_dbl_mantissa(srcp1,srcp2,src_exponent, + resultp1,resultp2); + Duint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Dbl_isinexact_to_unsigned(srcp1,srcp2,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + Duint_setzero(resultp1,resultp2); + Duint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/fcnvfx.c b/arch/parisc/math-emu/fcnvfx.c new file mode 100644 index 000000000..99bd61479 --- /dev/null +++ b/arch/parisc/math-emu/fcnvfx.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fcnvfx.c $Revision: 1.1 $ + * + * Purpose: + * Single Floating-point to Single Fixed-point + * Single Floating-point to Double Fixed-point + * Double Floating-point to Single Fixed-point + * Double Floating-point to Double Fixed-point + * + * External Interfaces: + * dbl_to_dbl_fcnvfx(srcptr,_nullptr,dstptr,status) + * dbl_to_sgl_fcnvfx(srcptr,_nullptr,dstptr,status) + * sgl_to_dbl_fcnvfx(srcptr,_nullptr,dstptr,status) + * sgl_to_sgl_fcnvfx(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/* + * Single Floating-point to Single Fixed-point + */ +/*ARGSUSED*/ +int +sgl_to_sgl_fcnvfx( + sgl_floating_point *srcptr, + sgl_floating_point *_nullptr, + int *dstptr, + sgl_floating_point *status) +{ + register unsigned int src, temp; + register int src_exponent, result; + register boolean inexact = FALSE; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP) { + /* check for MININT */ + if ((src_exponent > SGL_FX_MAX_EXP + 1) || + Sgl_isnotzero_mantissa(src) || Sgl_iszero_sign(src)) { + if (Sgl_iszero_sign(src)) result = 0x7fffffff; + else result = 0x80000000; + + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + } + /* + * Generate result + */ + if (src_exponent >= 0) { + temp = src; + Sgl_clear_signexponent_set_hidden(temp); + Int_from_sgl_mantissa(temp,src_exponent); + if (Sgl_isone_sign(src)) result = -Sgl_all(temp); + else result = Sgl_all(temp); + + /* check for inexact */ + if (Sgl_isinexact_to_fix(src,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) result++; + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) result--; + break; + case ROUNDNEAREST: + if (Sgl_isone_roundbit(src,src_exponent)) { + if (Sgl_isone_stickybit(src,src_exponent) + || (Sgl_isone_lowmantissa(temp))) + if (Sgl_iszero_sign(src)) result++; + else result--; + } + } + } + } + else { + result = 0; + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) result++; + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) result--; + break; + case ROUNDNEAREST: + if (src_exponent == -1) + if (Sgl_isnotzero_mantissa(src)) + if (Sgl_iszero_sign(src)) result++; + else result--; + } + } + } + *dstptr = result; + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + +/* + * Single Floating-point to Double Fixed-point + */ +/*ARGSUSED*/ +int +sgl_to_dbl_fcnvfx( + sgl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_integer *dstptr, + unsigned int *status) +{ + register int src_exponent, resultp1; + register unsigned int src, temp, resultp2; + register boolean inexact = FALSE; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP) { + /* check for MININT */ + if ((src_exponent > DBL_FX_MAX_EXP + 1) || + Sgl_isnotzero_mantissa(src) || Sgl_iszero_sign(src)) { + if (Sgl_iszero_sign(src)) { + resultp1 = 0x7fffffff; + resultp2 = 0xffffffff; + } + else { + resultp1 = 0x80000000; + resultp2 = 0; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Dint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Dint_set_minint(resultp1,resultp2); + Dint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + temp = src; + Sgl_clear_signexponent_set_hidden(temp); + Dint_from_sgl_mantissa(temp,src_exponent,resultp1,resultp2); + if (Sgl_isone_sign(src)) { + Dint_setone_sign(resultp1,resultp2); + } + + /* check for inexact */ + if (Sgl_isinexact_to_fix(src,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) { + Dint_increment(resultp1,resultp2); + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) { + Dint_decrement(resultp1,resultp2); + } + break; + case ROUNDNEAREST: + if (Sgl_isone_roundbit(src,src_exponent)) + if (Sgl_isone_stickybit(src,src_exponent) || + (Dint_isone_lowp2(resultp2))) + if (Sgl_iszero_sign(src)) { + Dint_increment(resultp1,resultp2); + } + else { + Dint_decrement(resultp1,resultp2); + } + } + } + } + else { + Dint_setzero(resultp1,resultp2); + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) { + Dint_increment(resultp1,resultp2); + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) { + Dint_decrement(resultp1,resultp2); + } + break; + case ROUNDNEAREST: + if (src_exponent == -1) + if (Sgl_isnotzero_mantissa(src)) + if (Sgl_iszero_sign(src)) { + Dint_increment(resultp1,resultp2); + } + else { + Dint_decrement(resultp1,resultp2); + } + } + } + } + Dint_copytoptr(resultp1,resultp2,dstptr); + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Single Fixed-point + */ +/*ARGSUSED*/ +int +dbl_to_sgl_fcnvfx( + dbl_floating_point *srcptr, + unsigned int *_nullptr, + int *dstptr, + unsigned int *status) +{ + register unsigned int srcp1,srcp2, tempp1,tempp2; + register int src_exponent, result; + register boolean inexact = FALSE; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP) { + /* check for MININT */ + if (Dbl_isoverflow_to_int(src_exponent,srcp1,srcp2)) { + if (Dbl_iszero_sign(srcp1)) result = 0x7fffffff; + else result = 0x80000000; + + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + } + /* + * Generate result + */ + if (src_exponent >= 0) { + tempp1 = srcp1; + tempp2 = srcp2; + Dbl_clear_signexponent_set_hidden(tempp1); + Int_from_dbl_mantissa(tempp1,tempp2,src_exponent); + if (Dbl_isone_sign(srcp1) && (src_exponent <= SGL_FX_MAX_EXP)) + result = -Dbl_allp1(tempp1); + else result = Dbl_allp1(tempp1); + + /* check for inexact */ + if (Dbl_isinexact_to_fix(srcp1,srcp2,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) result++; + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) result--; + break; + case ROUNDNEAREST: + if (Dbl_isone_roundbit(srcp1,srcp2,src_exponent)) + if (Dbl_isone_stickybit(srcp1,srcp2,src_exponent) || + (Dbl_isone_lowmantissap1(tempp1))) + if (Dbl_iszero_sign(srcp1)) result++; + else result--; + } + /* check for overflow */ + if ((Dbl_iszero_sign(srcp1) && result < 0) || + (Dbl_isone_sign(srcp1) && result > 0)) { + + if (Dbl_iszero_sign(srcp1)) result = 0x7fffffff; + else result = 0x80000000; + + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + } + } + else { + result = 0; + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) result++; + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) result--; + break; + case ROUNDNEAREST: + if (src_exponent == -1) + if (Dbl_isnotzero_mantissa(srcp1,srcp2)) + if (Dbl_iszero_sign(srcp1)) result++; + else result--; + } + } + } + *dstptr = result; + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Double Fixed-point + */ +/*ARGSUSED*/ +int +dbl_to_dbl_fcnvfx( + dbl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_integer *dstptr, + unsigned int *status) +{ + register int src_exponent, resultp1; + register unsigned int srcp1, srcp2, tempp1, tempp2, resultp2; + register boolean inexact = FALSE; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP) { + /* check for MININT */ + if ((src_exponent > DBL_FX_MAX_EXP + 1) || + Dbl_isnotzero_mantissa(srcp1,srcp2) || Dbl_iszero_sign(srcp1)) { + if (Dbl_iszero_sign(srcp1)) { + resultp1 = 0x7fffffff; + resultp2 = 0xffffffff; + } + else { + resultp1 = 0x80000000; + resultp2 = 0; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Dint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * Generate result + */ + if (src_exponent >= 0) { + tempp1 = srcp1; + tempp2 = srcp2; + Dbl_clear_signexponent_set_hidden(tempp1); + Dint_from_dbl_mantissa(tempp1,tempp2,src_exponent,resultp1, + resultp2); + if (Dbl_isone_sign(srcp1)) { + Dint_setone_sign(resultp1,resultp2); + } + + /* check for inexact */ + if (Dbl_isinexact_to_fix(srcp1,srcp2,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) { + Dint_increment(resultp1,resultp2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) { + Dint_decrement(resultp1,resultp2); + } + break; + case ROUNDNEAREST: + if (Dbl_isone_roundbit(srcp1,srcp2,src_exponent)) + if (Dbl_isone_stickybit(srcp1,srcp2,src_exponent) || + (Dint_isone_lowp2(resultp2))) + if (Dbl_iszero_sign(srcp1)) { + Dint_increment(resultp1,resultp2); + } + else { + Dint_decrement(resultp1,resultp2); + } + } + } + } + else { + Dint_setzero(resultp1,resultp2); + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) { + Dint_increment(resultp1,resultp2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) { + Dint_decrement(resultp1,resultp2); + } + break; + case ROUNDNEAREST: + if (src_exponent == -1) + if (Dbl_isnotzero_mantissa(srcp1,srcp2)) + if (Dbl_iszero_sign(srcp1)) { + Dint_increment(resultp1,resultp2); + } + else { + Dint_decrement(resultp1,resultp2); + } + } + } + } + Dint_copytoptr(resultp1,resultp2,dstptr); + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/fcnvfxt.c b/arch/parisc/math-emu/fcnvfxt.c new file mode 100644 index 000000000..3b7cc6225 --- /dev/null +++ b/arch/parisc/math-emu/fcnvfxt.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fcnvfxt.c $Revision: 1.1 $ + * + * Purpose: + * Single Floating-point to Single Fixed-point /w truncated result + * Single Floating-point to Double Fixed-point /w truncated result + * Double Floating-point to Single Fixed-point /w truncated result + * Double Floating-point to Double Fixed-point /w truncated result + * + * External Interfaces: + * dbl_to_dbl_fcnvfxt(srcptr,_nullptr,dstptr,status) + * dbl_to_sgl_fcnvfxt(srcptr,_nullptr,dstptr,status) + * sgl_to_dbl_fcnvfxt(srcptr,_nullptr,dstptr,status) + * sgl_to_sgl_fcnvfxt(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/* + * Convert single floating-point to single fixed-point format + * with truncated result + */ +/*ARGSUSED*/ +int +sgl_to_sgl_fcnvfxt( + sgl_floating_point *srcptr, + unsigned int *_nullptr, + int *dstptr, + unsigned int *status) +{ + register unsigned int src, temp; + register int src_exponent, result; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP) { + /* check for MININT */ + if ((src_exponent > SGL_FX_MAX_EXP + 1) || + Sgl_isnotzero_mantissa(src) || Sgl_iszero_sign(src)) { + if (Sgl_iszero_sign(src)) result = 0x7fffffff; + else result = 0x80000000; + + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + } + /* + * Generate result + */ + if (src_exponent >= 0) { + temp = src; + Sgl_clear_signexponent_set_hidden(temp); + Int_from_sgl_mantissa(temp,src_exponent); + if (Sgl_isone_sign(src)) result = -Sgl_all(temp); + else result = Sgl_all(temp); + *dstptr = result; + + /* check for inexact */ + if (Sgl_isinexact_to_fix(src,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + *dstptr = 0; + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} + +/* + * Single Floating-point to Double Fixed-point + */ +/*ARGSUSED*/ +int +sgl_to_dbl_fcnvfxt( + sgl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_integer *dstptr, + unsigned int *status) +{ + register int src_exponent, resultp1; + register unsigned int src, temp, resultp2; + + src = *srcptr; + src_exponent = Sgl_exponent(src) - SGL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP) { + /* check for MININT */ + if ((src_exponent > DBL_FX_MAX_EXP + 1) || + Sgl_isnotzero_mantissa(src) || Sgl_iszero_sign(src)) { + if (Sgl_iszero_sign(src)) { + resultp1 = 0x7fffffff; + resultp2 = 0xffffffff; + } + else { + resultp1 = 0x80000000; + resultp2 = 0; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Dint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + Dint_set_minint(resultp1,resultp2); + Dint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + temp = src; + Sgl_clear_signexponent_set_hidden(temp); + Dint_from_sgl_mantissa(temp,src_exponent,resultp1,resultp2); + if (Sgl_isone_sign(src)) { + Dint_setone_sign(resultp1,resultp2); + } + Dint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Sgl_isinexact_to_fix(src,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + Dint_setzero(resultp1,resultp2); + Dint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Single Fixed-point + */ +/*ARGSUSED*/ +int +dbl_to_sgl_fcnvfxt( + dbl_floating_point *srcptr, + unsigned int *_nullptr, + int *dstptr, + unsigned int *status) +{ + register unsigned int srcp1, srcp2, tempp1, tempp2; + register int src_exponent, result; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > SGL_FX_MAX_EXP) { + /* check for MININT */ + if (Dbl_isoverflow_to_int(src_exponent,srcp1,srcp2)) { + if (Dbl_iszero_sign(srcp1)) result = 0x7fffffff; + else result = 0x80000000; + + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + *dstptr = result; + return(NOEXCEPTION); + } + } + /* + * Generate result + */ + if (src_exponent >= 0) { + tempp1 = srcp1; + tempp2 = srcp2; + Dbl_clear_signexponent_set_hidden(tempp1); + Int_from_dbl_mantissa(tempp1,tempp2,src_exponent); + if (Dbl_isone_sign(srcp1) && (src_exponent <= SGL_FX_MAX_EXP)) + result = -Dbl_allp1(tempp1); + else result = Dbl_allp1(tempp1); + *dstptr = result; + + /* check for inexact */ + if (Dbl_isinexact_to_fix(srcp1,srcp2,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + *dstptr = 0; + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point to Double Fixed-point + */ +/*ARGSUSED*/ +int +dbl_to_dbl_fcnvfxt( + dbl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_integer *dstptr, + unsigned int *status) +{ + register int src_exponent, resultp1; + register unsigned int srcp1, srcp2, tempp1, tempp2, resultp2; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + src_exponent = Dbl_exponent(srcp1) - DBL_BIAS; + + /* + * Test for overflow + */ + if (src_exponent > DBL_FX_MAX_EXP) { + /* check for MININT */ + if ((src_exponent > DBL_FX_MAX_EXP + 1) || + Dbl_isnotzero_mantissa(srcp1,srcp2) || Dbl_iszero_sign(srcp1)) { + if (Dbl_iszero_sign(srcp1)) { + resultp1 = 0x7fffffff; + resultp2 = 0xffffffff; + } + else { + resultp1 = 0x80000000; + resultp2 = 0; + } + if (Is_invalidtrap_enabled()) { + return(INVALIDEXCEPTION); + } + Set_invalidflag(); + Dint_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + /* + * Generate result + */ + if (src_exponent >= 0) { + tempp1 = srcp1; + tempp2 = srcp2; + Dbl_clear_signexponent_set_hidden(tempp1); + Dint_from_dbl_mantissa(tempp1,tempp2,src_exponent, + resultp1,resultp2); + if (Dbl_isone_sign(srcp1)) { + Dint_setone_sign(resultp1,resultp2); + } + Dint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Dbl_isinexact_to_fix(srcp1,srcp2,src_exponent)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + else { + Dint_setzero(resultp1,resultp2); + Dint_copytoptr(resultp1,resultp2,dstptr); + + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/fcnvuf.c b/arch/parisc/math-emu/fcnvuf.c new file mode 100644 index 000000000..c166feb57 --- /dev/null +++ b/arch/parisc/math-emu/fcnvuf.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fcnvuf.c $Revision: 1.1 $ + * + * Purpose: + * Fixed point to Floating-point Converts + * + * External Interfaces: + * dbl_to_dbl_fcnvuf(srcptr,_nullptr,dstptr,status) + * dbl_to_sgl_fcnvuf(srcptr,_nullptr,dstptr,status) + * sgl_to_dbl_fcnvuf(srcptr,_nullptr,dstptr,status) + * sgl_to_sgl_fcnvuf(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/************************************************************************ + * Fixed point to Floating-point Converts * + ************************************************************************/ + +/* + * Convert Single Unsigned Fixed to Single Floating-point format + */ + +int +sgl_to_sgl_fcnvuf( + unsigned int *srcptr, + unsigned int *_nullptr, + sgl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int src, result = 0; + register int dst_exponent; + + src = *srcptr; + + /* Check for zero */ + if (src == 0) { + Sgl_setzero(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(src,dst_exponent); + /* left justify source, with msb at bit position 0 */ + src <<= dst_exponent+1; + Sgl_set_mantissa(result, src >> SGL_EXP_LENGTH); + Sgl_set_exponent(result, 30+SGL_BIAS - dst_exponent); + + /* check for inexact */ + if (Suint_isinexact_to_sgl(src)) { + switch (Rounding_mode()) { + case ROUNDPLUS: + Sgl_increment(result); + break; + case ROUNDMINUS: /* never negative */ + break; + case ROUNDNEAREST: + Sgl_roundnearest_from_suint(src,result); + break; + } + if (Is_inexacttrap_enabled()) { + *dstptr = result; + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + *dstptr = result; + return(NOEXCEPTION); +} + +/* + * Single Unsigned Fixed to Double Floating-point + */ + +int +sgl_to_dbl_fcnvuf( + unsigned int *srcptr, + unsigned int *_nullptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register int dst_exponent; + register unsigned int src, resultp1 = 0, resultp2 = 0; + + src = *srcptr; + + /* Check for zero */ + if (src == 0) { + Dbl_setzero(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(src,dst_exponent); + /* left justify source, with msb at bit position 0 */ + src <<= dst_exponent+1; + Dbl_set_mantissap1(resultp1, src >> DBL_EXP_LENGTH); + Dbl_set_mantissap2(resultp2, src << (32-DBL_EXP_LENGTH)); + Dbl_set_exponent(resultp1, (30+DBL_BIAS) - dst_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); +} + +/* + * Double Unsigned Fixed to Single Floating-point + */ + +int +dbl_to_sgl_fcnvuf( + dbl_unsigned *srcptr, + unsigned int *_nullptr, + sgl_floating_point *dstptr, + unsigned int *status) +{ + int dst_exponent; + unsigned int srcp1, srcp2, result = 0; + + Duint_copyfromptr(srcptr,srcp1,srcp2); + + /* Check for zero */ + if (srcp1 == 0 && srcp2 == 0) { + Sgl_setzero(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + if (srcp1 == 0) { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(srcp2,dst_exponent); + /* left justify source, with msb at bit position 0 */ + srcp1 = srcp2 << dst_exponent+1; + srcp2 = 0; + /* + * since msb set is in second word, need to + * adjust bit position count + */ + dst_exponent += 32; + } + else { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + * + */ + Find_ms_one_bit(srcp1,dst_exponent); + /* left justify source, with msb at bit position 0 */ + if (dst_exponent >= 0) { + Variable_shift_double(srcp1,srcp2,(31-dst_exponent), + srcp1); + srcp2 <<= dst_exponent+1; + } + } + Sgl_set_mantissa(result, srcp1 >> SGL_EXP_LENGTH); + Sgl_set_exponent(result, (62+SGL_BIAS) - dst_exponent); + + /* check for inexact */ + if (Duint_isinexact_to_sgl(srcp1,srcp2)) { + switch (Rounding_mode()) { + case ROUNDPLUS: + Sgl_increment(result); + break; + case ROUNDMINUS: /* never negative */ + break; + case ROUNDNEAREST: + Sgl_roundnearest_from_duint(srcp1,srcp2,result); + break; + } + if (Is_inexacttrap_enabled()) { + *dstptr = result; + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + *dstptr = result; + return(NOEXCEPTION); +} + +/* + * Double Unsigned Fixed to Double Floating-point + */ + +int +dbl_to_dbl_fcnvuf( + dbl_unsigned *srcptr, + unsigned int *_nullptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register int dst_exponent; + register unsigned int srcp1, srcp2, resultp1 = 0, resultp2 = 0; + + Duint_copyfromptr(srcptr,srcp1,srcp2); + + /* Check for zero */ + if (srcp1 == 0 && srcp2 ==0) { + Dbl_setzero(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + if (srcp1 == 0) { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(srcp2,dst_exponent); + /* left justify source, with msb at bit position 0 */ + srcp1 = srcp2 << dst_exponent+1; + srcp2 = 0; + /* + * since msb set is in second word, need to + * adjust bit position count + */ + dst_exponent += 32; + } + else { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(srcp1,dst_exponent); + /* left justify source, with msb at bit position 0 */ + if (dst_exponent >= 0) { + Variable_shift_double(srcp1,srcp2,(31-dst_exponent), + srcp1); + srcp2 <<= dst_exponent+1; + } + } + Dbl_set_mantissap1(resultp1, srcp1 >> DBL_EXP_LENGTH); + Shiftdouble(srcp1,srcp2,DBL_EXP_LENGTH,resultp2); + Dbl_set_exponent(resultp1, (62+DBL_BIAS) - dst_exponent); + + /* check for inexact */ + if (Duint_isinexact_to_dbl(srcp2)) { + switch (Rounding_mode()) { + case ROUNDPLUS: + Dbl_increment(resultp1,resultp2); + break; + case ROUNDMINUS: /* never negative */ + break; + case ROUNDNEAREST: + Dbl_roundnearest_from_duint(srcp2,resultp1, + resultp2); + break; + } + if (Is_inexacttrap_enabled()) { + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); +} + diff --git a/arch/parisc/math-emu/fcnvxf.c b/arch/parisc/math-emu/fcnvxf.c new file mode 100644 index 000000000..11bc1e8a1 --- /dev/null +++ b/arch/parisc/math-emu/fcnvxf.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fcnvxf.c $Revision: 1.1 $ + * + * Purpose: + * Single Fixed-point to Single Floating-point + * Single Fixed-point to Double Floating-point + * Double Fixed-point to Single Floating-point + * Double Fixed-point to Double Floating-point + * + * External Interfaces: + * dbl_to_dbl_fcnvxf(srcptr,_nullptr,dstptr,status) + * dbl_to_sgl_fcnvxf(srcptr,_nullptr,dstptr,status) + * sgl_to_dbl_fcnvxf(srcptr,_nullptr,dstptr,status) + * sgl_to_sgl_fcnvxf(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/* + * Convert single fixed-point to single floating-point format + */ + +int +sgl_to_sgl_fcnvxf( + int *srcptr, + unsigned int *_nullptr, + sgl_floating_point *dstptr, + unsigned int *status) +{ + register int src, dst_exponent; + register unsigned int result = 0; + + src = *srcptr; + /* + * set sign bit of result and get magnitude of source + */ + if (src < 0) { + Sgl_setone_sign(result); + Int_negate(src); + } + else { + Sgl_setzero_sign(result); + /* Check for zero */ + if (src == 0) { + Sgl_setzero(result); + *dstptr = result; + return(NOEXCEPTION); + } + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(src,dst_exponent); + /* left justify source, with msb at bit position 1 */ + if (dst_exponent >= 0) src <<= dst_exponent; + else src = 1 << 30; + Sgl_set_mantissa(result, src >> (SGL_EXP_LENGTH-1)); + Sgl_set_exponent(result, 30+SGL_BIAS - dst_exponent); + + /* check for inexact */ + if (Int_isinexact_to_sgl(src)) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) + Sgl_increment(result); + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) + Sgl_increment(result); + break; + case ROUNDNEAREST: + Sgl_roundnearest_from_int(src,result); + } + if (Is_inexacttrap_enabled()) { + *dstptr = result; + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + *dstptr = result; + return(NOEXCEPTION); +} + +/* + * Single Fixed-point to Double Floating-point + */ + +int +sgl_to_dbl_fcnvxf( + int *srcptr, + unsigned int *_nullptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register int src, dst_exponent; + register unsigned int resultp1 = 0, resultp2 = 0; + + src = *srcptr; + /* + * set sign bit of result and get magnitude of source + */ + if (src < 0) { + Dbl_setone_sign(resultp1); + Int_negate(src); + } + else { + Dbl_setzero_sign(resultp1); + /* Check for zero */ + if (src == 0) { + Dbl_setzero(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(src,dst_exponent); + /* left justify source, with msb at bit position 1 */ + if (dst_exponent >= 0) src <<= dst_exponent; + else src = 1 << 30; + Dbl_set_mantissap1(resultp1, src >> DBL_EXP_LENGTH - 1); + Dbl_set_mantissap2(resultp2, src << (33-DBL_EXP_LENGTH)); + Dbl_set_exponent(resultp1, (30+DBL_BIAS) - dst_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); +} + +/* + * Double Fixed-point to Single Floating-point + */ + +int +dbl_to_sgl_fcnvxf( + dbl_integer *srcptr, + unsigned int *_nullptr, + sgl_floating_point *dstptr, + unsigned int *status) +{ + int dst_exponent, srcp1; + unsigned int result = 0, srcp2; + + Dint_copyfromptr(srcptr,srcp1,srcp2); + /* + * set sign bit of result and get magnitude of source + */ + if (srcp1 < 0) { + Sgl_setone_sign(result); + Dint_negate(srcp1,srcp2); + } + else { + Sgl_setzero_sign(result); + /* Check for zero */ + if (srcp1 == 0 && srcp2 == 0) { + Sgl_setzero(result); + *dstptr = result; + return(NOEXCEPTION); + } + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + if (srcp1 == 0) { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(srcp2,dst_exponent); + /* left justify source, with msb at bit position 1 */ + if (dst_exponent >= 0) { + srcp1 = srcp2 << dst_exponent; + srcp2 = 0; + } + else { + srcp1 = srcp2 >> 1; + srcp2 <<= 31; + } + /* + * since msb set is in second word, need to + * adjust bit position count + */ + dst_exponent += 32; + } + else { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + * + */ + Find_ms_one_bit(srcp1,dst_exponent); + /* left justify source, with msb at bit position 1 */ + if (dst_exponent > 0) { + Variable_shift_double(srcp1,srcp2,(32-dst_exponent), + srcp1); + srcp2 <<= dst_exponent; + } + /* + * If dst_exponent = 0, we don't need to shift anything. + * If dst_exponent = -1, src = - 2**63 so we won't need to + * shift srcp2. + */ + else srcp1 >>= -(dst_exponent); + } + Sgl_set_mantissa(result, srcp1 >> SGL_EXP_LENGTH - 1); + Sgl_set_exponent(result, (62+SGL_BIAS) - dst_exponent); + + /* check for inexact */ + if (Dint_isinexact_to_sgl(srcp1,srcp2)) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) + Sgl_increment(result); + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) + Sgl_increment(result); + break; + case ROUNDNEAREST: + Sgl_roundnearest_from_dint(srcp1,srcp2,result); + } + if (Is_inexacttrap_enabled()) { + *dstptr = result; + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + *dstptr = result; + return(NOEXCEPTION); +} + +/* + * Double Fixed-point to Double Floating-point + */ + +int +dbl_to_dbl_fcnvxf( + dbl_integer *srcptr, + unsigned int *_nullptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register int srcp1, dst_exponent; + register unsigned int srcp2, resultp1 = 0, resultp2 = 0; + + Dint_copyfromptr(srcptr,srcp1,srcp2); + /* + * set sign bit of result and get magnitude of source + */ + if (srcp1 < 0) { + Dbl_setone_sign(resultp1); + Dint_negate(srcp1,srcp2); + } + else { + Dbl_setzero_sign(resultp1); + /* Check for zero */ + if (srcp1 == 0 && srcp2 ==0) { + Dbl_setzero(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + /* + * Generate exponent and normalized mantissa + */ + dst_exponent = 16; /* initialize for normalization */ + if (srcp1 == 0) { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(srcp2,dst_exponent); + /* left justify source, with msb at bit position 1 */ + if (dst_exponent >= 0) { + srcp1 = srcp2 << dst_exponent; + srcp2 = 0; + } + else { + srcp1 = srcp2 >> 1; + srcp2 <<= 31; + } + /* + * since msb set is in second word, need to + * adjust bit position count + */ + dst_exponent += 32; + } + else { + /* + * Check word for most significant bit set. Returns + * a value in dst_exponent indicating the bit position, + * between -1 and 30. + */ + Find_ms_one_bit(srcp1,dst_exponent); + /* left justify source, with msb at bit position 1 */ + if (dst_exponent > 0) { + Variable_shift_double(srcp1,srcp2,(32-dst_exponent), + srcp1); + srcp2 <<= dst_exponent; + } + /* + * If dst_exponent = 0, we don't need to shift anything. + * If dst_exponent = -1, src = - 2**63 so we won't need to + * shift srcp2. + */ + else srcp1 >>= -(dst_exponent); + } + Dbl_set_mantissap1(resultp1, srcp1 >> (DBL_EXP_LENGTH-1)); + Shiftdouble(srcp1,srcp2,DBL_EXP_LENGTH-1,resultp2); + Dbl_set_exponent(resultp1, (62+DBL_BIAS) - dst_exponent); + + /* check for inexact */ + if (Dint_isinexact_to_dbl(srcp2)) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) { + Dbl_increment(resultp1,resultp2); + } + break; + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) { + Dbl_increment(resultp1,resultp2); + } + break; + case ROUNDNEAREST: + Dbl_roundnearest_from_dint(srcp2,resultp1, + resultp2); + } + if (Is_inexacttrap_enabled()) { + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/float.h b/arch/parisc/math-emu/float.h new file mode 100644 index 000000000..531bbda5e --- /dev/null +++ b/arch/parisc/math-emu/float.h @@ -0,0 +1,568 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/float.h $Revision: 1.1 $ + * + * Purpose: + * <<please update with a synopis of the functionality provided by this file>> + * + * BE header: no + * + * Shipped: yes + * /usr/conf/pa/spmath/float.h + * + * END_DESC +*/ + +#ifdef __NO_PA_HDRS + PA header file -- do not include this header file for non-PA builds. +#endif + +#include "fpbits.h" +#include "hppa.h" +/* + * Want to pick up the FPU capability flags, not the PDC structures. + * 'LOCORE' isn't really true in this case, but we don't want the C structures + * so it suits our purposes + */ +#define LOCORE +#include "fpu.h" + +/* + * Declare the basic structures for the 3 different + * floating-point precisions. + * + * Single number + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |s| exp | mantissa | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#define Sall(object) (object) +#define Ssign(object) Bitfield_extract( 0, 1,object) +#define Ssignedsign(object) Bitfield_signed_extract( 0, 1,object) +#define Sexponent(object) Bitfield_extract( 1, 8,object) +#define Smantissa(object) Bitfield_mask( 9, 23,object) +#define Ssignaling(object) Bitfield_extract( 9, 1,object) +#define Ssignalingnan(object) Bitfield_extract( 1, 9,object) +#define Shigh2mantissa(object) Bitfield_extract( 9, 2,object) +#define Sexponentmantissa(object) Bitfield_mask( 1, 31,object) +#define Ssignexponent(object) Bitfield_extract( 0, 9,object) +#define Shidden(object) Bitfield_extract( 8, 1,object) +#define Shiddenoverflow(object) Bitfield_extract( 7, 1,object) +#define Shiddenhigh7mantissa(object) Bitfield_extract( 8, 8,object) +#define Shiddenhigh3mantissa(object) Bitfield_extract( 8, 4,object) +#define Slow(object) Bitfield_mask( 31, 1,object) +#define Slow4(object) Bitfield_mask( 28, 4,object) +#define Slow31(object) Bitfield_mask( 1, 31,object) +#define Shigh31(object) Bitfield_extract( 0, 31,object) +#define Ssignedhigh31(object) Bitfield_signed_extract( 0, 31,object) +#define Shigh4(object) Bitfield_extract( 0, 4,object) +#define Sbit24(object) Bitfield_extract( 24, 1,object) +#define Sbit28(object) Bitfield_extract( 28, 1,object) +#define Sbit29(object) Bitfield_extract( 29, 1,object) +#define Sbit30(object) Bitfield_extract( 30, 1,object) +#define Sbit31(object) Bitfield_mask( 31, 1,object) + +#define Deposit_ssign(object,value) Bitfield_deposit(value,0,1,object) +#define Deposit_sexponent(object,value) Bitfield_deposit(value,1,8,object) +#define Deposit_smantissa(object,value) Bitfield_deposit(value,9,23,object) +#define Deposit_shigh2mantissa(object,value) Bitfield_deposit(value,9,2,object) +#define Deposit_sexponentmantissa(object,value) \ + Bitfield_deposit(value,1,31,object) +#define Deposit_ssignexponent(object,value) Bitfield_deposit(value,0,9,object) +#define Deposit_slow(object,value) Bitfield_deposit(value,31,1,object) +#define Deposit_shigh4(object,value) Bitfield_deposit(value,0,4,object) + +#define Is_ssign(object) Bitfield_mask( 0, 1,object) +#define Is_ssignaling(object) Bitfield_mask( 9, 1,object) +#define Is_shidden(object) Bitfield_mask( 8, 1,object) +#define Is_shiddenoverflow(object) Bitfield_mask( 7, 1,object) +#define Is_slow(object) Bitfield_mask( 31, 1,object) +#define Is_sbit24(object) Bitfield_mask( 24, 1,object) +#define Is_sbit28(object) Bitfield_mask( 28, 1,object) +#define Is_sbit29(object) Bitfield_mask( 29, 1,object) +#define Is_sbit30(object) Bitfield_mask( 30, 1,object) +#define Is_sbit31(object) Bitfield_mask( 31, 1,object) + +/* + * Double number. + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |s| exponent | mantissa part 1 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | mantissa part 2 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#define Dallp1(object) (object) +#define Dsign(object) Bitfield_extract( 0, 1,object) +#define Dsignedsign(object) Bitfield_signed_extract( 0, 1,object) +#define Dexponent(object) Bitfield_extract( 1, 11,object) +#define Dmantissap1(object) Bitfield_mask( 12, 20,object) +#define Dsignaling(object) Bitfield_extract( 12, 1,object) +#define Dsignalingnan(object) Bitfield_extract( 1, 12,object) +#define Dhigh2mantissa(object) Bitfield_extract( 12, 2,object) +#define Dexponentmantissap1(object) Bitfield_mask( 1, 31,object) +#define Dsignexponent(object) Bitfield_extract( 0, 12,object) +#define Dhidden(object) Bitfield_extract( 11, 1,object) +#define Dhiddenoverflow(object) Bitfield_extract( 10, 1,object) +#define Dhiddenhigh7mantissa(object) Bitfield_extract( 11, 8,object) +#define Dhiddenhigh3mantissa(object) Bitfield_extract( 11, 4,object) +#define Dlowp1(object) Bitfield_mask( 31, 1,object) +#define Dlow31p1(object) Bitfield_mask( 1, 31,object) +#define Dhighp1(object) Bitfield_extract( 0, 1,object) +#define Dhigh4p1(object) Bitfield_extract( 0, 4,object) +#define Dhigh31p1(object) Bitfield_extract( 0, 31,object) +#define Dsignedhigh31p1(object) Bitfield_signed_extract( 0, 31,object) +#define Dbit3p1(object) Bitfield_extract( 3, 1,object) + +#define Deposit_dsign(object,value) Bitfield_deposit(value,0,1,object) +#define Deposit_dexponent(object,value) Bitfield_deposit(value,1,11,object) +#define Deposit_dmantissap1(object,value) Bitfield_deposit(value,12,20,object) +#define Deposit_dhigh2mantissa(object,value) Bitfield_deposit(value,12,2,object) +#define Deposit_dexponentmantissap1(object,value) \ + Bitfield_deposit(value,1,31,object) +#define Deposit_dsignexponent(object,value) Bitfield_deposit(value,0,12,object) +#define Deposit_dlowp1(object,value) Bitfield_deposit(value,31,1,object) +#define Deposit_dhigh4p1(object,value) Bitfield_deposit(value,0,4,object) + +#define Is_dsign(object) Bitfield_mask( 0, 1,object) +#define Is_dsignaling(object) Bitfield_mask( 12, 1,object) +#define Is_dhidden(object) Bitfield_mask( 11, 1,object) +#define Is_dhiddenoverflow(object) Bitfield_mask( 10, 1,object) +#define Is_dlowp1(object) Bitfield_mask( 31, 1,object) +#define Is_dhighp1(object) Bitfield_mask( 0, 1,object) +#define Is_dbit3p1(object) Bitfield_mask( 3, 1,object) + +#define Dallp2(object) (object) +#define Dmantissap2(object) (object) +#define Dlowp2(object) Bitfield_mask( 31, 1,object) +#define Dlow4p2(object) Bitfield_mask( 28, 4,object) +#define Dlow31p2(object) Bitfield_mask( 1, 31,object) +#define Dhighp2(object) Bitfield_extract( 0, 1,object) +#define Dhigh31p2(object) Bitfield_extract( 0, 31,object) +#define Dbit2p2(object) Bitfield_extract( 2, 1,object) +#define Dbit3p2(object) Bitfield_extract( 3, 1,object) +#define Dbit21p2(object) Bitfield_extract( 21, 1,object) +#define Dbit28p2(object) Bitfield_extract( 28, 1,object) +#define Dbit29p2(object) Bitfield_extract( 29, 1,object) +#define Dbit30p2(object) Bitfield_extract( 30, 1,object) +#define Dbit31p2(object) Bitfield_mask( 31, 1,object) + +#define Deposit_dlowp2(object,value) Bitfield_deposit(value,31,1,object) + +#define Is_dlowp2(object) Bitfield_mask( 31, 1,object) +#define Is_dhighp2(object) Bitfield_mask( 0, 1,object) +#define Is_dbit2p2(object) Bitfield_mask( 2, 1,object) +#define Is_dbit3p2(object) Bitfield_mask( 3, 1,object) +#define Is_dbit21p2(object) Bitfield_mask( 21, 1,object) +#define Is_dbit28p2(object) Bitfield_mask( 28, 1,object) +#define Is_dbit29p2(object) Bitfield_mask( 29, 1,object) +#define Is_dbit30p2(object) Bitfield_mask( 30, 1,object) +#define Is_dbit31p2(object) Bitfield_mask( 31, 1,object) + +/* + * Quad number. + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |s| exponent | mantissa part 1 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | mantissa part 2 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | mantissa part 3 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | mantissa part 4 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +typedef struct + { + union + { + struct { unsigned qallp1; } u_qallp1; +/* Not needed for now... + Bitfield_extract( 0, 1,u_qsign,qsign) + Bitfield_signed_extract( 0, 1,u_qsignedsign,qsignedsign) + Bitfield_extract( 1, 15,u_qexponent,qexponent) + Bitfield_extract(16, 16,u_qmantissap1,qmantissap1) + Bitfield_extract(16, 1,u_qsignaling,qsignaling) + Bitfield_extract(1, 16,u_qsignalingnan,qsignalingnan) + Bitfield_extract(16, 2,u_qhigh2mantissa,qhigh2mantissa) + Bitfield_extract( 1, 31,u_qexponentmantissap1,qexponentmantissap1) + Bitfield_extract( 0, 16,u_qsignexponent,qsignexponent) + Bitfield_extract(15, 1,u_qhidden,qhidden) + Bitfield_extract(14, 1,u_qhiddenoverflow,qhiddenoverflow) + Bitfield_extract(15, 8,u_qhiddenhigh7mantissa,qhiddenhigh7mantissa) + Bitfield_extract(15, 4,u_qhiddenhigh3mantissa,qhiddenhigh3mantissa) + Bitfield_extract(31, 1,u_qlowp1,qlowp1) + Bitfield_extract( 1, 31,u_qlow31p1,qlow31p1) + Bitfield_extract( 0, 1,u_qhighp1,qhighp1) + Bitfield_extract( 0, 4,u_qhigh4p1,qhigh4p1) + Bitfield_extract( 0, 31,u_qhigh31p1,qhigh31p1) + */ + } quad_u1; + union + { + struct { unsigned qallp2; } u_qallp2; + /* Not needed for now... + Bitfield_extract(31, 1,u_qlowp2,qlowp2) + Bitfield_extract( 1, 31,u_qlow31p2,qlow31p2) + Bitfield_extract( 0, 1,u_qhighp2,qhighp2) + Bitfield_extract( 0, 31,u_qhigh31p2,qhigh31p2) + */ + } quad_u2; + union + { + struct { unsigned qallp3; } u_qallp3; + /* Not needed for now... + Bitfield_extract(31, 1,u_qlowp3,qlowp3) + Bitfield_extract( 1, 31,u_qlow31p3,qlow31p3) + Bitfield_extract( 0, 1,u_qhighp3,qhighp3) + Bitfield_extract( 0, 31,u_qhigh31p3,qhigh31p3) + */ + } quad_u3; + union + { + struct { unsigned qallp4; } u_qallp4; + /* Not need for now... + Bitfield_extract(31, 1,u_qlowp4,qlowp4) + Bitfield_extract( 1, 31,u_qlow31p4,qlow31p4) + Bitfield_extract( 0, 1,u_qhighp4,qhighp4) + Bitfield_extract( 0, 31,u_qhigh31p4,qhigh31p4) + */ + } quad_u4; + } quad_floating_point; + +/* Extension - An additional structure to hold the guard, round and + * sticky bits during computations. + */ +#define Extall(object) (object) +#define Extsign(object) Bitfield_extract( 0, 1,object) +#define Exthigh31(object) Bitfield_extract( 0, 31,object) +#define Extlow31(object) Bitfield_extract( 1, 31,object) +#define Extlow(object) Bitfield_extract( 31, 1,object) + +/* + * Single extended - The upper word is just like single precision, + * but one additional word of mantissa is needed. + */ +#define Sextallp1(object) (object) +#define Sextallp2(object) (object) +#define Sextlowp1(object) Bitfield_extract( 31, 1,object) +#define Sexthighp2(object) Bitfield_extract( 0, 1,object) +#define Sextlow31p2(object) Bitfield_extract( 1, 31,object) +#define Sexthiddenoverflow(object) Bitfield_extract( 4, 1,object) +#define Is_sexthiddenoverflow(object) Bitfield_mask( 4, 1,object) + +/* + * Double extended - The upper two words are just like double precision, + * but two additional words of mantissa are needed. + */ +#define Dextallp1(object) (object) +#define Dextallp2(object) (object) +#define Dextallp3(object) (object) +#define Dextallp4(object) (object) +#define Dextlowp2(object) Bitfield_extract( 31, 1,object) +#define Dexthighp3(object) Bitfield_extract( 0, 1,object) +#define Dextlow31p3(object) Bitfield_extract( 1, 31,object) +#define Dexthiddenoverflow(object) Bitfield_extract( 10, 1,object) +#define Is_dexthiddenoverflow(object) Bitfield_mask( 10, 1,object) +#define Deposit_dextlowp4(object,value) Bitfield_deposit(value,31,1,object) + +/* + * Declare the basic structures for the 3 different + * fixed-point precisions. + * + * Single number + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |s| integer | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +typedef int sgl_integer; + +/* + * Double number. + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |s| high integer | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | low integer | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +struct dint { + int wd0; + unsigned int wd1; +}; + +struct dblwd { + unsigned int wd0; + unsigned int wd1; +}; + +/* + * Quad number. + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |s| integer part1 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | integer part 2 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | integer part 3 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | integer part 4 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ + +struct quadwd { + int wd0; + unsigned int wd1; + unsigned int wd2; + unsigned int wd3; +}; + +typedef struct quadwd quad_integer; + + +/* useful typedefs */ +typedef unsigned int sgl_floating_point; +typedef struct dblwd dbl_floating_point; +typedef struct dint dbl_integer; +typedef struct dblwd dbl_unsigned; + +/* + * Define the different precisions' parameters. + */ +#define SGL_BITLENGTH 32 +#define SGL_EMAX 127 +#define SGL_EMIN (-126) +#define SGL_BIAS 127 +#define SGL_WRAP 192 +#define SGL_INFINITY_EXPONENT (SGL_EMAX+SGL_BIAS+1) +#define SGL_THRESHOLD 32 +#define SGL_EXP_LENGTH 8 +#define SGL_P 24 + +#define DBL_BITLENGTH 64 +#define DBL_EMAX 1023 +#define DBL_EMIN (-1022) +#define DBL_BIAS 1023 +#define DBL_WRAP 1536 +#define DBL_INFINITY_EXPONENT (DBL_EMAX+DBL_BIAS+1) +#define DBL_THRESHOLD 64 +#define DBL_EXP_LENGTH 11 +#define DBL_P 53 + +#define QUAD_BITLENGTH 128 +#define QUAD_EMAX 16383 +#define QUAD_EMIN (-16382) +#define QUAD_BIAS 16383 +#define QUAD_WRAP 24576 +#define QUAD_INFINITY_EXPONENT (QUAD_EMAX+QUAD_BIAS+1) +#define QUAD_P 113 + +/* Boolean Values etc. */ +#define FALSE 0 +#define TRUE (!FALSE) +#define NOT ! +#define XOR ^ + +/* other constants */ +#undef NULL +#define NULL 0 +#define NIL 0 +#define SGL 0 +#define DBL 1 +#define BADFMT 2 +#define QUAD 3 + + +/* Types */ +typedef int boolean; +typedef int FORMAT; +typedef int VOID; + + +/* Declare status register equivalent to FPUs architecture. + * + * 0 1 2 3 4 5 6 7 8 910 1 2 3 4 5 6 7 8 920 1 2 3 4 5 6 7 8 930 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |V|Z|O|U|I|C| rsv | model | version |RM |rsv|T|r|V|Z|O|U|I| + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#define Cbit(object) Bitfield_extract( 5, 1,object) +#define Tbit(object) Bitfield_extract( 25, 1,object) +#define Roundingmode(object) Bitfield_extract( 21, 2,object) +#define Invalidtrap(object) Bitfield_extract( 27, 1,object) +#define Divisionbyzerotrap(object) Bitfield_extract( 28, 1,object) +#define Overflowtrap(object) Bitfield_extract( 29, 1,object) +#define Underflowtrap(object) Bitfield_extract( 30, 1,object) +#define Inexacttrap(object) Bitfield_extract( 31, 1,object) +#define Invalidflag(object) Bitfield_extract( 0, 1,object) +#define Divisionbyzeroflag(object) Bitfield_extract( 1, 1,object) +#define Overflowflag(object) Bitfield_extract( 2, 1,object) +#define Underflowflag(object) Bitfield_extract( 3, 1,object) +#define Inexactflag(object) Bitfield_extract( 4, 1,object) +#define Allflags(object) Bitfield_extract( 0, 5,object) + +/* Definitions relevant to the status register */ + +/* Rounding Modes */ +#define ROUNDNEAREST 0 +#define ROUNDZERO 1 +#define ROUNDPLUS 2 +#define ROUNDMINUS 3 + +/* Exceptions */ +#define NOEXCEPTION 0x0 +#define INVALIDEXCEPTION 0x20 +#define DIVISIONBYZEROEXCEPTION 0x10 +#define OVERFLOWEXCEPTION 0x08 +#define UNDERFLOWEXCEPTION 0x04 +#define INEXACTEXCEPTION 0x02 +#define UNIMPLEMENTEDEXCEPTION 0x01 + +/* New exceptions for the 2E Opcode */ +#define OPC_2E_INVALIDEXCEPTION 0x30 +#define OPC_2E_OVERFLOWEXCEPTION 0x18 +#define OPC_2E_UNDERFLOWEXCEPTION 0x0c +#define OPC_2E_INEXACTEXCEPTION 0x12 + +/* Declare exception registers equivalent to FPUs architecture + * + * 0 1 2 3 4 5 6 7 8 910 1 2 3 4 5 6 7 8 920 1 2 3 4 5 6 7 8 930 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |excepttype | r1 | r2/ext | operation |parm |n| t/cond | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#define Allexception(object) (object) +#define Exceptiontype(object) Bitfield_extract( 0, 6,object) +#define Instructionfield(object) Bitfield_mask( 6,26,object) +#define Parmfield(object) Bitfield_extract( 23, 3,object) +#define Rabit(object) Bitfield_extract( 24, 1,object) +#define Ibit(object) Bitfield_extract( 25, 1,object) + +#define Set_exceptiontype(object,value) Bitfield_deposit(value, 0, 6,object) +#define Set_parmfield(object,value) Bitfield_deposit(value, 23, 3,object) +#define Set_exceptiontype_and_instr_field(exception,instruction,object) \ + object = exception << 26 | instruction + +/* Declare the condition field + * + * 0 1 2 3 4 5 6 7 8 910 1 2 3 4 5 6 7 8 920 1 2 3 4 5 6 7 8 930 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | |G|L|E|U|X| + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#define Greaterthanbit(object) Bitfield_extract( 27, 1,object) +#define Lessthanbit(object) Bitfield_extract( 28, 1,object) +#define Equalbit(object) Bitfield_extract( 29, 1,object) +#define Unorderedbit(object) Bitfield_extract( 30, 1,object) +#define Exceptionbit(object) Bitfield_extract( 31, 1,object) + +/* An alias name for the status register */ +#define Fpustatus_register (*status) + +/************************************************** + * Status register referencing and manipulation. * + **************************************************/ + +/* Rounding mode */ +#define Rounding_mode() Roundingmode(Fpustatus_register) +#define Is_rounding_mode(rmode) \ + (Roundingmode(Fpustatus_register) == rmode) +#define Set_rounding_mode(value) \ + Bitfield_deposit(value,21,2,Fpustatus_register) + +/* Boolean testing of the trap enable bits */ +#define Is_invalidtrap_enabled() Invalidtrap(Fpustatus_register) +#define Is_divisionbyzerotrap_enabled() Divisionbyzerotrap(Fpustatus_register) +#define Is_overflowtrap_enabled() Overflowtrap(Fpustatus_register) +#define Is_underflowtrap_enabled() Underflowtrap(Fpustatus_register) +#define Is_inexacttrap_enabled() Inexacttrap(Fpustatus_register) + +/* Set the indicated flags in the status register */ +#define Set_invalidflag() Bitfield_deposit(1,0,1,Fpustatus_register) +#define Set_divisionbyzeroflag() Bitfield_deposit(1,1,1,Fpustatus_register) +#define Set_overflowflag() Bitfield_deposit(1,2,1,Fpustatus_register) +#define Set_underflowflag() Bitfield_deposit(1,3,1,Fpustatus_register) +#define Set_inexactflag() Bitfield_deposit(1,4,1,Fpustatus_register) + +#define Clear_all_flags() Bitfield_deposit(0,0,5,Fpustatus_register) + +/* Manipulate the trap and condition code bits (tbit and cbit) */ +#define Set_tbit() Bitfield_deposit(1,25,1,Fpustatus_register) +#define Clear_tbit() Bitfield_deposit(0,25,1,Fpustatus_register) +#define Is_tbit_set() Tbit(Fpustatus_register) +#define Is_cbit_set() Cbit(Fpustatus_register) + +#define Set_status_cbit(value) \ + Bitfield_deposit(value,5,1,Fpustatus_register) + +/******************************* + * Condition field referencing * + *******************************/ +#define Unordered(cond) Unorderedbit(cond) +#define Equal(cond) Equalbit(cond) +#define Lessthan(cond) Lessthanbit(cond) +#define Greaterthan(cond) Greaterthanbit(cond) +#define Exception(cond) Exceptionbit(cond) + + +/* Defines for the extension */ +#define Ext_isone_sign(extent) (Extsign(extent)) +#define Ext_isnotzero(extent) \ + (Extall(extent)) +#define Ext_isnotzero_lower(extent) \ + (Extlow31(extent)) +#define Ext_leftshiftby1(extent) \ + Extall(extent) <<= 1 +#define Ext_negate(extent) \ + (int )Extall(extent) = 0 - (int )Extall(extent) +#define Ext_setone_low(extent) Bitfield_deposit(1,31,1,extent) +#define Ext_setzero(extent) Extall(extent) = 0 + +typedef int operation; + +/* error messages */ + +#define NONE 0 +#define UNDEFFPINST 1 + +/* Function definitions: opcode, opclass */ +#define FTEST (1<<2) | 0 +#define FCPY (2<<2) | 0 +#define FABS (3<<2) | 0 +#define FSQRT (4<<2) | 0 +#define FRND (5<<2) | 0 + +#define FCNVFF (0<<2) | 1 +#define FCNVXF (1<<2) | 1 +#define FCNVFX (2<<2) | 1 +#define FCNVFXT (3<<2) | 1 + +#define FCMP (0<<2) | 2 + +#define FADD (0<<2) | 3 +#define FSUB (1<<2) | 3 +#define FMPY (2<<2) | 3 +#define FDIV (3<<2) | 3 +#define FREM (4<<2) | 3 + diff --git a/arch/parisc/math-emu/fmpyfadd.c b/arch/parisc/math-emu/fmpyfadd.c new file mode 100644 index 000000000..029f9bbfe --- /dev/null +++ b/arch/parisc/math-emu/fmpyfadd.c @@ -0,0 +1,2642 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/fmpyfadd.c $Revision: 1.1 $ + * + * Purpose: + * Double Floating-point Multiply Fused Add + * Double Floating-point Multiply Negate Fused Add + * Single Floating-point Multiply Fused Add + * Single Floating-point Multiply Negate Fused Add + * + * External Interfaces: + * dbl_fmpyfadd(src1ptr,src2ptr,src3ptr,status,dstptr) + * dbl_fmpynfadd(src1ptr,src2ptr,src3ptr,status,dstptr) + * sgl_fmpyfadd(src1ptr,src2ptr,src3ptr,status,dstptr) + * sgl_fmpynfadd(src1ptr,src2ptr,src3ptr,status,dstptr) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" + + +/* + * Double Floating-point Multiply Fused Add + */ + +int +dbl_fmpyfadd( + dbl_floating_point *src1ptr, + dbl_floating_point *src2ptr, + dbl_floating_point *src3ptr, + unsigned int *status, + dbl_floating_point *dstptr) +{ + unsigned int opnd1p1, opnd1p2, opnd2p1, opnd2p2, opnd3p1, opnd3p2; + register unsigned int tmpresp1, tmpresp2, tmpresp3, tmpresp4; + unsigned int rightp1, rightp2, rightp3, rightp4; + unsigned int resultp1, resultp2 = 0, resultp3 = 0, resultp4 = 0; + register int mpy_exponent, add_exponent, count; + boolean inexact = FALSE, is_tiny = FALSE; + + unsigned int signlessleft1, signlessright1, save; + register int result_exponent, diff_exponent; + int sign_save, jumpsize; + + Dbl_copyfromptr(src1ptr,opnd1p1,opnd1p2); + Dbl_copyfromptr(src2ptr,opnd2p1,opnd2p2); + Dbl_copyfromptr(src3ptr,opnd3p1,opnd3p2); + + /* + * set sign bit of result of multiply + */ + if (Dbl_sign(opnd1p1) ^ Dbl_sign(opnd2p1)) + Dbl_setnegativezerop1(resultp1); + else Dbl_setzerop1(resultp1); + + /* + * Generate multiply exponent + */ + mpy_exponent = Dbl_exponent(opnd1p1) + Dbl_exponent(opnd2p1) - DBL_BIAS; + + /* + * check first operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd1p1)) { + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + if (Dbl_isnotnan(opnd2p1,opnd2p2) && + Dbl_isnotnan(opnd3p1,opnd3p2)) { + if (Dbl_iszero_exponentmantissa(opnd2p1,opnd2p2)) { + /* + * invalid since operands are infinity + * and zero + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Dbl_isinfinity(opnd3p1,opnd3p2) && + (Dbl_sign(resultp1) ^ Dbl_sign(opnd3p1))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd1p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd1p1); + } + /* + * is second operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * is third operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd3p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd3p1); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd1p1,opnd1p2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check second operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd2p1)) { + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + if (Dbl_isnotnan(opnd3p1,opnd3p2)) { + if (Dbl_iszero_exponentmantissa(opnd1p1,opnd1p2)) { + /* + * invalid since multiply operands are + * zero & infinity + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(opnd2p1,opnd2p2); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Dbl_isinfinity(opnd3p1,opnd3p2) && + (Dbl_sign(resultp1) ^ Dbl_sign(opnd3p1))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + } + /* + * is third operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd3p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd3p1); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check third operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd3p1)) { + if (Dbl_iszero_mantissa(opnd3p1,opnd3p2)) { + /* return infinity */ + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd3p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd3p1); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * Generate multiply mantissa + */ + if (Dbl_isnotzero_exponent(opnd1p1)) { + /* set hidden bit */ + Dbl_clear_signexponent_set_hidden(opnd1p1); + } + else { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Dbl_iszero_exponentmantissa(opnd3p1,opnd3p2)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Dbl_or_signs(opnd3p1,resultp1); + } else { + Dbl_and_signs(opnd3p1,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Dbl_iszero_exponent(opnd3p1) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Dbl_signextendedsign(opnd3p1); + result_exponent = 0; + Dbl_leftshiftby1(opnd3p1,opnd3p2); + Dbl_normalize(opnd3p1,opnd3p2,result_exponent); + Dbl_set_sign(opnd3p1,/*using*/sign_save); + Dbl_setwrapped_exponent(opnd3p1,result_exponent, + unfl); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* is denormalized, adjust exponent */ + Dbl_clear_signexponent(opnd1p1); + Dbl_leftshiftby1(opnd1p1,opnd1p2); + Dbl_normalize(opnd1p1,opnd1p2,mpy_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Dbl_isnotzero_exponent(opnd2p1)) { + Dbl_clear_signexponent_set_hidden(opnd2p1); + } + else { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Dbl_iszero_exponentmantissa(opnd3p1,opnd3p2)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Dbl_or_signs(opnd3p1,resultp1); + } else { + Dbl_and_signs(opnd3p1,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Dbl_iszero_exponent(opnd3p1) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Dbl_signextendedsign(opnd3p1); + result_exponent = 0; + Dbl_leftshiftby1(opnd3p1,opnd3p2); + Dbl_normalize(opnd3p1,opnd3p2,result_exponent); + Dbl_set_sign(opnd3p1,/*using*/sign_save); + Dbl_setwrapped_exponent(opnd3p1,result_exponent, + unfl); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* is denormalized; want to normalize */ + Dbl_clear_signexponent(opnd2p1); + Dbl_leftshiftby1(opnd2p1,opnd2p2); + Dbl_normalize(opnd2p1,opnd2p2,mpy_exponent); + } + + /* Multiply the first two source mantissas together */ + + /* + * The intermediate result will be kept in tmpres, + * which needs enough room for 106 bits of mantissa, + * so lets call it a Double extended. + */ + Dblext_setzero(tmpresp1,tmpresp2,tmpresp3,tmpresp4); + + /* + * Four bits at a time are inspected in each loop, and a + * simple shift and add multiply algorithm is used. + */ + for (count = DBL_P-1; count >= 0; count -= 4) { + Dblext_rightshiftby4(tmpresp1,tmpresp2,tmpresp3,tmpresp4); + if (Dbit28p2(opnd1p2)) { + /* Fourword_add should be an ADD followed by 3 ADDC's */ + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1<<3 | opnd2p2>>29, opnd2p2<<3, 0, 0); + } + if (Dbit29p2(opnd1p2)) { + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1<<2 | opnd2p2>>30, opnd2p2<<2, 0, 0); + } + if (Dbit30p2(opnd1p2)) { + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1<<1 | opnd2p2>>31, opnd2p2<<1, 0, 0); + } + if (Dbit31p2(opnd1p2)) { + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1, opnd2p2, 0, 0); + } + Dbl_rightshiftby4(opnd1p1,opnd1p2); + } + if (Is_dexthiddenoverflow(tmpresp1)) { + /* result mantissa >= 2 (mantissa overflow) */ + mpy_exponent++; + Dblext_rightshiftby1(tmpresp1,tmpresp2,tmpresp3,tmpresp4); + } + + /* + * Restore the sign of the mpy result which was saved in resultp1. + * The exponent will continue to be kept in mpy_exponent. + */ + Dblext_set_sign(tmpresp1,Dbl_sign(resultp1)); + + /* + * No rounding is required, since the result of the multiply + * is exact in the extended format. + */ + + /* + * Now we are ready to perform the add portion of the operation. + * + * The exponents need to be kept as integers for now, since the + * multiply result might not fit into the exponent field. We + * can't overflow or underflow because of this yet, since the + * add could bring the final result back into range. + */ + add_exponent = Dbl_exponent(opnd3p1); + + /* + * Check for denormalized or zero add operand. + */ + if (add_exponent == 0) { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd3p1,opnd3p2)) { + /* right is zero */ + /* Left can't be zero and must be result. + * + * The final result is now in tmpres and mpy_exponent, + * and needs to be rounded and squeezed back into + * double precision format from double extended. + */ + result_exponent = mpy_exponent; + Dblext_copy(tmpresp1,tmpresp2,tmpresp3,tmpresp4, + resultp1,resultp2,resultp3,resultp4); + sign_save = Dbl_signextendedsign(resultp1);/*save sign*/ + goto round; + } + + /* + * Neither are zeroes. + * Adjust exponent and normalize add operand. + */ + sign_save = Dbl_signextendedsign(opnd3p1); /* save sign */ + Dbl_clear_signexponent(opnd3p1); + Dbl_leftshiftby1(opnd3p1,opnd3p2); + Dbl_normalize(opnd3p1,opnd3p2,add_exponent); + Dbl_set_sign(opnd3p1,sign_save); /* restore sign */ + } else { + Dbl_clear_exponent_set_hidden(opnd3p1); + } + /* + * Copy opnd3 to the double extended variable called right. + */ + Dbl_copyto_dblext(opnd3p1,opnd3p2,rightp1,rightp2,rightp3,rightp4); + + /* + * A zero "save" helps discover equal operands (for later), + * and is used in swapping operands (if needed). + */ + Dblext_xortointp1(tmpresp1,rightp1,/*to*/save); + + /* + * Compare magnitude of operands. + */ + Dblext_copytoint_exponentmantissap1(tmpresp1,signlessleft1); + Dblext_copytoint_exponentmantissap1(rightp1,signlessright1); + if (mpy_exponent < add_exponent || mpy_exponent == add_exponent && + Dblext_ismagnitudeless(tmpresp2,rightp2,signlessleft1,signlessright1)){ + /* + * Set the left operand to the larger one by XOR swap. + * First finish the first word "save". + */ + Dblext_xorfromintp1(save,rightp1,/*to*/rightp1); + Dblext_xorfromintp1(save,tmpresp1,/*to*/tmpresp1); + Dblext_swap_lower(tmpresp2,tmpresp3,tmpresp4, + rightp2,rightp3,rightp4); + /* also setup exponents used in rest of routine */ + diff_exponent = add_exponent - mpy_exponent; + result_exponent = add_exponent; + } else { + /* also setup exponents used in rest of routine */ + diff_exponent = mpy_exponent - add_exponent; + result_exponent = mpy_exponent; + } + /* Invariant: left is not smaller than right. */ + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for + * this infrequent case. + */ + if (diff_exponent > DBLEXT_THRESHOLD) { + diff_exponent = DBLEXT_THRESHOLD; + } + + /* Align right operand by shifting it to the right */ + Dblext_clear_sign(rightp1); + Dblext_right_align(rightp1,rightp2,rightp3,rightp4, + /*shifted by*/diff_exponent); + + /* Treat sum and difference of the operands separately. */ + if ((int)save < 0) { + /* + * Difference of the two operands. Overflow can occur if the + * multiply overflowed. A borrow can occur out of the hidden + * bit and force a post normalization phase. + */ + Dblext_subtract(tmpresp1,tmpresp2,tmpresp3,tmpresp4, + rightp1,rightp2,rightp3,rightp4, + resultp1,resultp2,resultp3,resultp4); + sign_save = Dbl_signextendedsign(resultp1); + if (Dbl_iszero_hidden(resultp1)) { + /* Handle normalization */ + /* A straightforward algorithm would now shift the + * result and extension left until the hidden bit + * becomes one. Not all of the extension bits need + * participate in the shift. Only the two most + * significant bits (round and guard) are needed. + * If only a single shift is needed then the guard + * bit becomes a significant low order bit and the + * extension must participate in the rounding. + * If more than a single shift is needed, then all + * bits to the right of the guard bit are zeros, + * and the guard bit may or may not be zero. */ + Dblext_leftshiftby1(resultp1,resultp2,resultp3, + resultp4); + + /* Need to check for a zero result. The sign and + * exponent fields have already been zeroed. The more + * efficient test of the full object can be used. + */ + if(Dblext_iszero(resultp1,resultp2,resultp3,resultp4)){ + /* Must have been "x-x" or "x+(-x)". */ + if (Is_rounding_mode(ROUNDMINUS)) + Dbl_setone_sign(resultp1); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + result_exponent--; + + /* Look to see if normalization is finished. */ + if (Dbl_isone_hidden(resultp1)) { + /* No further normalization is needed */ + goto round; + } + + /* Discover first one bit to determine shift amount. + * Use a modified binary search. We have already + * shifted the result one position right and still + * not found a one so the remainder of the extension + * must be zero and simplifies rounding. */ + /* Scan bytes */ + while (Dbl_iszero_hiddenhigh7mantissa(resultp1)) { + Dblext_leftshiftby8(resultp1,resultp2,resultp3,resultp4); + result_exponent -= 8; + } + /* Now narrow it down to the nibble */ + if (Dbl_iszero_hiddenhigh3mantissa(resultp1)) { + /* The lower nibble contains the + * normalizing one */ + Dblext_leftshiftby4(resultp1,resultp2,resultp3,resultp4); + result_exponent -= 4; + } + /* Select case where first bit is set (already + * normalized) otherwise select the proper shift. */ + jumpsize = Dbl_hiddenhigh3mantissa(resultp1); + if (jumpsize <= 7) switch(jumpsize) { + case 1: + Dblext_leftshiftby3(resultp1,resultp2,resultp3, + resultp4); + result_exponent -= 3; + break; + case 2: + case 3: + Dblext_leftshiftby2(resultp1,resultp2,resultp3, + resultp4); + result_exponent -= 2; + break; + case 4: + case 5: + case 6: + case 7: + Dblext_leftshiftby1(resultp1,resultp2,resultp3, + resultp4); + result_exponent -= 1; + break; + } + } /* end if (hidden...)... */ + /* Fall through and round */ + } /* end if (save < 0)... */ + else { + /* Add magnitudes */ + Dblext_addition(tmpresp1,tmpresp2,tmpresp3,tmpresp4, + rightp1,rightp2,rightp3,rightp4, + /*to*/resultp1,resultp2,resultp3,resultp4); + sign_save = Dbl_signextendedsign(resultp1); + if (Dbl_isone_hiddenoverflow(resultp1)) { + /* Prenormalization required. */ + Dblext_arithrightshiftby1(resultp1,resultp2,resultp3, + resultp4); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...add magnitudes... */ + + /* Round the result. If the extension and lower two words are + * all zeros, then the result is exact. Otherwise round in the + * correct direction. Underflow is possible. If a postnormalization + * is necessary, then the mantissa is all zeros so no shift is needed. + */ + round: + if (result_exponent <= 0 && !Is_underflowtrap_enabled()) { + Dblext_denormalize(resultp1,resultp2,resultp3,resultp4, + result_exponent,is_tiny); + } + Dbl_set_sign(resultp1,/*using*/sign_save); + if (Dblext_isnotzero_mantissap3(resultp3) || + Dblext_isnotzero_mantissap4(resultp4)) { + inexact = TRUE; + switch(Rounding_mode()) { + case ROUNDNEAREST: /* The default. */ + if (Dblext_isone_highp3(resultp3)) { + /* at least 1/2 ulp */ + if (Dblext_isnotzero_low31p3(resultp3) || + Dblext_isnotzero_mantissap4(resultp4) || + Dblext_isone_lowp2(resultp2)) { + /* either exactly half way and odd or + * more than 1/2ulp */ + Dbl_increment(resultp1,resultp2); + } + } + break; + + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) { + /* Round up positive results */ + Dbl_increment(resultp1,resultp2); + } + break; + + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) { + /* Round down negative results */ + Dbl_increment(resultp1,resultp2); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if (Dbl_isone_hiddenoverflow(resultp1)) result_exponent++; + } + if (result_exponent >= DBL_INFINITY_EXPONENT) { + /* trap if OVERFLOWTRAP enabled */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,result_exponent,ovfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_OVERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return (OPC_2E_OVERFLOWEXCEPTION); + } + inexact = TRUE; + Set_overflowflag(); + /* set result to infinity or largest number */ + Dbl_setoverflow(resultp1,resultp2); + + } else if (result_exponent <= 0) { /* underflow case */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,result_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_UNDERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(OPC_2E_UNDERFLOWEXCEPTION); + } + else if (inexact && is_tiny) Set_underflowflag(); + } + else Dbl_set_exponent(resultp1,result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) return(OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); +} + +/* + * Double Floating-point Multiply Negate Fused Add + */ + +dbl_fmpynfadd(src1ptr,src2ptr,src3ptr,status,dstptr) + +dbl_floating_point *src1ptr, *src2ptr, *src3ptr, *dstptr; +unsigned int *status; +{ + unsigned int opnd1p1, opnd1p2, opnd2p1, opnd2p2, opnd3p1, opnd3p2; + register unsigned int tmpresp1, tmpresp2, tmpresp3, tmpresp4; + unsigned int rightp1, rightp2, rightp3, rightp4; + unsigned int resultp1, resultp2 = 0, resultp3 = 0, resultp4 = 0; + register int mpy_exponent, add_exponent, count; + boolean inexact = FALSE, is_tiny = FALSE; + + unsigned int signlessleft1, signlessright1, save; + register int result_exponent, diff_exponent; + int sign_save, jumpsize; + + Dbl_copyfromptr(src1ptr,opnd1p1,opnd1p2); + Dbl_copyfromptr(src2ptr,opnd2p1,opnd2p2); + Dbl_copyfromptr(src3ptr,opnd3p1,opnd3p2); + + /* + * set sign bit of result of multiply + */ + if (Dbl_sign(opnd1p1) ^ Dbl_sign(opnd2p1)) + Dbl_setzerop1(resultp1); + else + Dbl_setnegativezerop1(resultp1); + + /* + * Generate multiply exponent + */ + mpy_exponent = Dbl_exponent(opnd1p1) + Dbl_exponent(opnd2p1) - DBL_BIAS; + + /* + * check first operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd1p1)) { + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + if (Dbl_isnotnan(opnd2p1,opnd2p2) && + Dbl_isnotnan(opnd3p1,opnd3p2)) { + if (Dbl_iszero_exponentmantissa(opnd2p1,opnd2p2)) { + /* + * invalid since operands are infinity + * and zero + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Dbl_isinfinity(opnd3p1,opnd3p2) && + (Dbl_sign(resultp1) ^ Dbl_sign(opnd3p1))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd1p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd1p1); + } + /* + * is second operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + /* + * is third operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd3p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd3p1); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd1p1,opnd1p2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check second operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd2p1)) { + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + if (Dbl_isnotnan(opnd3p1,opnd3p2)) { + if (Dbl_iszero_exponentmantissa(opnd1p1,opnd1p2)) { + /* + * invalid since multiply operands are + * zero & infinity + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(opnd2p1,opnd2p2); + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Dbl_isinfinity(opnd3p1,opnd3p2) && + (Dbl_sign(resultp1) ^ Dbl_sign(opnd3p1))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Dbl_makequietnan(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Dbl_setinfinity_exponentmantissa(resultp1,resultp2); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd2p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd2p1); + } + /* + * is third operand a signaling NaN? + */ + else if (Dbl_is_signalingnan(opnd3p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd3p1); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd2p1,opnd2p2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check third operand for NaN's or infinity + */ + if (Dbl_isinfinity_exponent(opnd3p1)) { + if (Dbl_iszero_mantissa(opnd3p1,opnd3p2)) { + /* return infinity */ + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } else { + /* + * is NaN; signaling or quiet? + */ + if (Dbl_isone_signaling(opnd3p1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(opnd3p1); + } + /* + * return quiet NaN + */ + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * Generate multiply mantissa + */ + if (Dbl_isnotzero_exponent(opnd1p1)) { + /* set hidden bit */ + Dbl_clear_signexponent_set_hidden(opnd1p1); + } + else { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd1p1,opnd1p2)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Dbl_iszero_exponentmantissa(opnd3p1,opnd3p2)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Dbl_or_signs(opnd3p1,resultp1); + } else { + Dbl_and_signs(opnd3p1,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Dbl_iszero_exponent(opnd3p1) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Dbl_signextendedsign(opnd3p1); + result_exponent = 0; + Dbl_leftshiftby1(opnd3p1,opnd3p2); + Dbl_normalize(opnd3p1,opnd3p2,result_exponent); + Dbl_set_sign(opnd3p1,/*using*/sign_save); + Dbl_setwrapped_exponent(opnd3p1,result_exponent, + unfl); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* is denormalized, adjust exponent */ + Dbl_clear_signexponent(opnd1p1); + Dbl_leftshiftby1(opnd1p1,opnd1p2); + Dbl_normalize(opnd1p1,opnd1p2,mpy_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Dbl_isnotzero_exponent(opnd2p1)) { + Dbl_clear_signexponent_set_hidden(opnd2p1); + } + else { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd2p1,opnd2p2)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Dbl_iszero_exponentmantissa(opnd3p1,opnd3p2)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Dbl_or_signs(opnd3p1,resultp1); + } else { + Dbl_and_signs(opnd3p1,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Dbl_iszero_exponent(opnd3p1) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Dbl_signextendedsign(opnd3p1); + result_exponent = 0; + Dbl_leftshiftby1(opnd3p1,opnd3p2); + Dbl_normalize(opnd3p1,opnd3p2,result_exponent); + Dbl_set_sign(opnd3p1,/*using*/sign_save); + Dbl_setwrapped_exponent(opnd3p1,result_exponent, + unfl); + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Dbl_copytoptr(opnd3p1,opnd3p2,dstptr); + return(NOEXCEPTION); + } + /* is denormalized; want to normalize */ + Dbl_clear_signexponent(opnd2p1); + Dbl_leftshiftby1(opnd2p1,opnd2p2); + Dbl_normalize(opnd2p1,opnd2p2,mpy_exponent); + } + + /* Multiply the first two source mantissas together */ + + /* + * The intermediate result will be kept in tmpres, + * which needs enough room for 106 bits of mantissa, + * so lets call it a Double extended. + */ + Dblext_setzero(tmpresp1,tmpresp2,tmpresp3,tmpresp4); + + /* + * Four bits at a time are inspected in each loop, and a + * simple shift and add multiply algorithm is used. + */ + for (count = DBL_P-1; count >= 0; count -= 4) { + Dblext_rightshiftby4(tmpresp1,tmpresp2,tmpresp3,tmpresp4); + if (Dbit28p2(opnd1p2)) { + /* Fourword_add should be an ADD followed by 3 ADDC's */ + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1<<3 | opnd2p2>>29, opnd2p2<<3, 0, 0); + } + if (Dbit29p2(opnd1p2)) { + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1<<2 | opnd2p2>>30, opnd2p2<<2, 0, 0); + } + if (Dbit30p2(opnd1p2)) { + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1<<1 | opnd2p2>>31, opnd2p2<<1, 0, 0); + } + if (Dbit31p2(opnd1p2)) { + Fourword_add(tmpresp1, tmpresp2, tmpresp3, tmpresp4, + opnd2p1, opnd2p2, 0, 0); + } + Dbl_rightshiftby4(opnd1p1,opnd1p2); + } + if (Is_dexthiddenoverflow(tmpresp1)) { + /* result mantissa >= 2 (mantissa overflow) */ + mpy_exponent++; + Dblext_rightshiftby1(tmpresp1,tmpresp2,tmpresp3,tmpresp4); + } + + /* + * Restore the sign of the mpy result which was saved in resultp1. + * The exponent will continue to be kept in mpy_exponent. + */ + Dblext_set_sign(tmpresp1,Dbl_sign(resultp1)); + + /* + * No rounding is required, since the result of the multiply + * is exact in the extended format. + */ + + /* + * Now we are ready to perform the add portion of the operation. + * + * The exponents need to be kept as integers for now, since the + * multiply result might not fit into the exponent field. We + * can't overflow or underflow because of this yet, since the + * add could bring the final result back into range. + */ + add_exponent = Dbl_exponent(opnd3p1); + + /* + * Check for denormalized or zero add operand. + */ + if (add_exponent == 0) { + /* check for zero */ + if (Dbl_iszero_mantissa(opnd3p1,opnd3p2)) { + /* right is zero */ + /* Left can't be zero and must be result. + * + * The final result is now in tmpres and mpy_exponent, + * and needs to be rounded and squeezed back into + * double precision format from double extended. + */ + result_exponent = mpy_exponent; + Dblext_copy(tmpresp1,tmpresp2,tmpresp3,tmpresp4, + resultp1,resultp2,resultp3,resultp4); + sign_save = Dbl_signextendedsign(resultp1);/*save sign*/ + goto round; + } + + /* + * Neither are zeroes. + * Adjust exponent and normalize add operand. + */ + sign_save = Dbl_signextendedsign(opnd3p1); /* save sign */ + Dbl_clear_signexponent(opnd3p1); + Dbl_leftshiftby1(opnd3p1,opnd3p2); + Dbl_normalize(opnd3p1,opnd3p2,add_exponent); + Dbl_set_sign(opnd3p1,sign_save); /* restore sign */ + } else { + Dbl_clear_exponent_set_hidden(opnd3p1); + } + /* + * Copy opnd3 to the double extended variable called right. + */ + Dbl_copyto_dblext(opnd3p1,opnd3p2,rightp1,rightp2,rightp3,rightp4); + + /* + * A zero "save" helps discover equal operands (for later), + * and is used in swapping operands (if needed). + */ + Dblext_xortointp1(tmpresp1,rightp1,/*to*/save); + + /* + * Compare magnitude of operands. + */ + Dblext_copytoint_exponentmantissap1(tmpresp1,signlessleft1); + Dblext_copytoint_exponentmantissap1(rightp1,signlessright1); + if (mpy_exponent < add_exponent || mpy_exponent == add_exponent && + Dblext_ismagnitudeless(tmpresp2,rightp2,signlessleft1,signlessright1)){ + /* + * Set the left operand to the larger one by XOR swap. + * First finish the first word "save". + */ + Dblext_xorfromintp1(save,rightp1,/*to*/rightp1); + Dblext_xorfromintp1(save,tmpresp1,/*to*/tmpresp1); + Dblext_swap_lower(tmpresp2,tmpresp3,tmpresp4, + rightp2,rightp3,rightp4); + /* also setup exponents used in rest of routine */ + diff_exponent = add_exponent - mpy_exponent; + result_exponent = add_exponent; + } else { + /* also setup exponents used in rest of routine */ + diff_exponent = mpy_exponent - add_exponent; + result_exponent = mpy_exponent; + } + /* Invariant: left is not smaller than right. */ + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for + * this infrequent case. + */ + if (diff_exponent > DBLEXT_THRESHOLD) { + diff_exponent = DBLEXT_THRESHOLD; + } + + /* Align right operand by shifting it to the right */ + Dblext_clear_sign(rightp1); + Dblext_right_align(rightp1,rightp2,rightp3,rightp4, + /*shifted by*/diff_exponent); + + /* Treat sum and difference of the operands separately. */ + if ((int)save < 0) { + /* + * Difference of the two operands. Overflow can occur if the + * multiply overflowed. A borrow can occur out of the hidden + * bit and force a post normalization phase. + */ + Dblext_subtract(tmpresp1,tmpresp2,tmpresp3,tmpresp4, + rightp1,rightp2,rightp3,rightp4, + resultp1,resultp2,resultp3,resultp4); + sign_save = Dbl_signextendedsign(resultp1); + if (Dbl_iszero_hidden(resultp1)) { + /* Handle normalization */ + /* A straightforward algorithm would now shift the + * result and extension left until the hidden bit + * becomes one. Not all of the extension bits need + * participate in the shift. Only the two most + * significant bits (round and guard) are needed. + * If only a single shift is needed then the guard + * bit becomes a significant low order bit and the + * extension must participate in the rounding. + * If more than a single shift is needed, then all + * bits to the right of the guard bit are zeros, + * and the guard bit may or may not be zero. */ + Dblext_leftshiftby1(resultp1,resultp2,resultp3, + resultp4); + + /* Need to check for a zero result. The sign and + * exponent fields have already been zeroed. The more + * efficient test of the full object can be used. + */ + if (Dblext_iszero(resultp1,resultp2,resultp3,resultp4)) { + /* Must have been "x-x" or "x+(-x)". */ + if (Is_rounding_mode(ROUNDMINUS)) + Dbl_setone_sign(resultp1); + Dbl_copytoptr(resultp1,resultp2,dstptr); + return(NOEXCEPTION); + } + result_exponent--; + + /* Look to see if normalization is finished. */ + if (Dbl_isone_hidden(resultp1)) { + /* No further normalization is needed */ + goto round; + } + + /* Discover first one bit to determine shift amount. + * Use a modified binary search. We have already + * shifted the result one position right and still + * not found a one so the remainder of the extension + * must be zero and simplifies rounding. */ + /* Scan bytes */ + while (Dbl_iszero_hiddenhigh7mantissa(resultp1)) { + Dblext_leftshiftby8(resultp1,resultp2,resultp3,resultp4); + result_exponent -= 8; + } + /* Now narrow it down to the nibble */ + if (Dbl_iszero_hiddenhigh3mantissa(resultp1)) { + /* The lower nibble contains the + * normalizing one */ + Dblext_leftshiftby4(resultp1,resultp2,resultp3,resultp4); + result_exponent -= 4; + } + /* Select case where first bit is set (already + * normalized) otherwise select the proper shift. */ + jumpsize = Dbl_hiddenhigh3mantissa(resultp1); + if (jumpsize <= 7) switch(jumpsize) { + case 1: + Dblext_leftshiftby3(resultp1,resultp2,resultp3, + resultp4); + result_exponent -= 3; + break; + case 2: + case 3: + Dblext_leftshiftby2(resultp1,resultp2,resultp3, + resultp4); + result_exponent -= 2; + break; + case 4: + case 5: + case 6: + case 7: + Dblext_leftshiftby1(resultp1,resultp2,resultp3, + resultp4); + result_exponent -= 1; + break; + } + } /* end if (hidden...)... */ + /* Fall through and round */ + } /* end if (save < 0)... */ + else { + /* Add magnitudes */ + Dblext_addition(tmpresp1,tmpresp2,tmpresp3,tmpresp4, + rightp1,rightp2,rightp3,rightp4, + /*to*/resultp1,resultp2,resultp3,resultp4); + sign_save = Dbl_signextendedsign(resultp1); + if (Dbl_isone_hiddenoverflow(resultp1)) { + /* Prenormalization required. */ + Dblext_arithrightshiftby1(resultp1,resultp2,resultp3, + resultp4); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...add magnitudes... */ + + /* Round the result. If the extension and lower two words are + * all zeros, then the result is exact. Otherwise round in the + * correct direction. Underflow is possible. If a postnormalization + * is necessary, then the mantissa is all zeros so no shift is needed. + */ + round: + if (result_exponent <= 0 && !Is_underflowtrap_enabled()) { + Dblext_denormalize(resultp1,resultp2,resultp3,resultp4, + result_exponent,is_tiny); + } + Dbl_set_sign(resultp1,/*using*/sign_save); + if (Dblext_isnotzero_mantissap3(resultp3) || + Dblext_isnotzero_mantissap4(resultp4)) { + inexact = TRUE; + switch(Rounding_mode()) { + case ROUNDNEAREST: /* The default. */ + if (Dblext_isone_highp3(resultp3)) { + /* at least 1/2 ulp */ + if (Dblext_isnotzero_low31p3(resultp3) || + Dblext_isnotzero_mantissap4(resultp4) || + Dblext_isone_lowp2(resultp2)) { + /* either exactly half way and odd or + * more than 1/2ulp */ + Dbl_increment(resultp1,resultp2); + } + } + break; + + case ROUNDPLUS: + if (Dbl_iszero_sign(resultp1)) { + /* Round up positive results */ + Dbl_increment(resultp1,resultp2); + } + break; + + case ROUNDMINUS: + if (Dbl_isone_sign(resultp1)) { + /* Round down negative results */ + Dbl_increment(resultp1,resultp2); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if (Dbl_isone_hiddenoverflow(resultp1)) result_exponent++; + } + if (result_exponent >= DBL_INFINITY_EXPONENT) { + /* Overflow */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,result_exponent,ovfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_OVERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return (OPC_2E_OVERFLOWEXCEPTION); + } + inexact = TRUE; + Set_overflowflag(); + Dbl_setoverflow(resultp1,resultp2); + } else if (result_exponent <= 0) { /* underflow case */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Dbl_setwrapped_exponent(resultp1,result_exponent,unfl); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_UNDERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(OPC_2E_UNDERFLOWEXCEPTION); + } + else if (inexact && is_tiny) Set_underflowflag(); + } + else Dbl_set_exponent(resultp1,result_exponent); + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) return(OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); +} + +/* + * Single Floating-point Multiply Fused Add + */ + +sgl_fmpyfadd(src1ptr,src2ptr,src3ptr,status,dstptr) + +sgl_floating_point *src1ptr, *src2ptr, *src3ptr, *dstptr; +unsigned int *status; +{ + unsigned int opnd1, opnd2, opnd3; + register unsigned int tmpresp1, tmpresp2; + unsigned int rightp1, rightp2; + unsigned int resultp1, resultp2 = 0; + register int mpy_exponent, add_exponent, count; + boolean inexact = FALSE, is_tiny = FALSE; + + unsigned int signlessleft1, signlessright1, save; + register int result_exponent, diff_exponent; + int sign_save, jumpsize; + + Sgl_copyfromptr(src1ptr,opnd1); + Sgl_copyfromptr(src2ptr,opnd2); + Sgl_copyfromptr(src3ptr,opnd3); + + /* + * set sign bit of result of multiply + */ + if (Sgl_sign(opnd1) ^ Sgl_sign(opnd2)) + Sgl_setnegativezero(resultp1); + else Sgl_setzero(resultp1); + + /* + * Generate multiply exponent + */ + mpy_exponent = Sgl_exponent(opnd1) + Sgl_exponent(opnd2) - SGL_BIAS; + + /* + * check first operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd1)) { + if (Sgl_iszero_mantissa(opnd1)) { + if (Sgl_isnotnan(opnd2) && Sgl_isnotnan(opnd3)) { + if (Sgl_iszero_exponentmantissa(opnd2)) { + /* + * invalid since operands are infinity + * and zero + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Sgl_isinfinity(opnd3) && + (Sgl_sign(resultp1) ^ Sgl_sign(opnd3))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Sgl_setinfinity_exponentmantissa(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd1); + } + /* + * is second operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + Sgl_copytoptr(opnd2,dstptr); + return(NOEXCEPTION); + } + /* + * is third operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd3)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd3); + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Sgl_copytoptr(opnd1,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check second operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd2)) { + if (Sgl_iszero_mantissa(opnd2)) { + if (Sgl_isnotnan(opnd3)) { + if (Sgl_iszero_exponentmantissa(opnd1)) { + /* + * invalid since multiply operands are + * zero & infinity + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(opnd2); + Sgl_copytoptr(opnd2,dstptr); + return(NOEXCEPTION); + } + + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Sgl_isinfinity(opnd3) && + (Sgl_sign(resultp1) ^ Sgl_sign(opnd3))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Sgl_setinfinity_exponentmantissa(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + } + /* + * is third operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd3)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd3); + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Sgl_copytoptr(opnd2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check third operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd3)) { + if (Sgl_iszero_mantissa(opnd3)) { + /* return infinity */ + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd3)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd3); + } + /* + * return quiet NaN + */ + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + } + + /* + * Generate multiply mantissa + */ + if (Sgl_isnotzero_exponent(opnd1)) { + /* set hidden bit */ + Sgl_clear_signexponent_set_hidden(opnd1); + } + else { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd1)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Sgl_iszero_exponentmantissa(opnd3)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Sgl_or_signs(opnd3,resultp1); + } else { + Sgl_and_signs(opnd3,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Sgl_iszero_exponent(opnd3) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Sgl_signextendedsign(opnd3); + result_exponent = 0; + Sgl_leftshiftby1(opnd3); + Sgl_normalize(opnd3,result_exponent); + Sgl_set_sign(opnd3,/*using*/sign_save); + Sgl_setwrapped_exponent(opnd3,result_exponent, + unfl); + Sgl_copytoptr(opnd3,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* is denormalized, adjust exponent */ + Sgl_clear_signexponent(opnd1); + Sgl_leftshiftby1(opnd1); + Sgl_normalize(opnd1,mpy_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Sgl_isnotzero_exponent(opnd2)) { + Sgl_clear_signexponent_set_hidden(opnd2); + } + else { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd2)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Sgl_iszero_exponentmantissa(opnd3)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Sgl_or_signs(opnd3,resultp1); + } else { + Sgl_and_signs(opnd3,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Sgl_iszero_exponent(opnd3) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Sgl_signextendedsign(opnd3); + result_exponent = 0; + Sgl_leftshiftby1(opnd3); + Sgl_normalize(opnd3,result_exponent); + Sgl_set_sign(opnd3,/*using*/sign_save); + Sgl_setwrapped_exponent(opnd3,result_exponent, + unfl); + Sgl_copytoptr(opnd3,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* is denormalized; want to normalize */ + Sgl_clear_signexponent(opnd2); + Sgl_leftshiftby1(opnd2); + Sgl_normalize(opnd2,mpy_exponent); + } + + /* Multiply the first two source mantissas together */ + + /* + * The intermediate result will be kept in tmpres, + * which needs enough room for 106 bits of mantissa, + * so lets call it a Double extended. + */ + Sglext_setzero(tmpresp1,tmpresp2); + + /* + * Four bits at a time are inspected in each loop, and a + * simple shift and add multiply algorithm is used. + */ + for (count = SGL_P-1; count >= 0; count -= 4) { + Sglext_rightshiftby4(tmpresp1,tmpresp2); + if (Sbit28(opnd1)) { + /* Twoword_add should be an ADD followed by 2 ADDC's */ + Twoword_add(tmpresp1, tmpresp2, opnd2<<3, 0); + } + if (Sbit29(opnd1)) { + Twoword_add(tmpresp1, tmpresp2, opnd2<<2, 0); + } + if (Sbit30(opnd1)) { + Twoword_add(tmpresp1, tmpresp2, opnd2<<1, 0); + } + if (Sbit31(opnd1)) { + Twoword_add(tmpresp1, tmpresp2, opnd2, 0); + } + Sgl_rightshiftby4(opnd1); + } + if (Is_sexthiddenoverflow(tmpresp1)) { + /* result mantissa >= 2 (mantissa overflow) */ + mpy_exponent++; + Sglext_rightshiftby4(tmpresp1,tmpresp2); + } else { + Sglext_rightshiftby3(tmpresp1,tmpresp2); + } + + /* + * Restore the sign of the mpy result which was saved in resultp1. + * The exponent will continue to be kept in mpy_exponent. + */ + Sglext_set_sign(tmpresp1,Sgl_sign(resultp1)); + + /* + * No rounding is required, since the result of the multiply + * is exact in the extended format. + */ + + /* + * Now we are ready to perform the add portion of the operation. + * + * The exponents need to be kept as integers for now, since the + * multiply result might not fit into the exponent field. We + * can't overflow or underflow because of this yet, since the + * add could bring the final result back into range. + */ + add_exponent = Sgl_exponent(opnd3); + + /* + * Check for denormalized or zero add operand. + */ + if (add_exponent == 0) { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd3)) { + /* right is zero */ + /* Left can't be zero and must be result. + * + * The final result is now in tmpres and mpy_exponent, + * and needs to be rounded and squeezed back into + * double precision format from double extended. + */ + result_exponent = mpy_exponent; + Sglext_copy(tmpresp1,tmpresp2,resultp1,resultp2); + sign_save = Sgl_signextendedsign(resultp1);/*save sign*/ + goto round; + } + + /* + * Neither are zeroes. + * Adjust exponent and normalize add operand. + */ + sign_save = Sgl_signextendedsign(opnd3); /* save sign */ + Sgl_clear_signexponent(opnd3); + Sgl_leftshiftby1(opnd3); + Sgl_normalize(opnd3,add_exponent); + Sgl_set_sign(opnd3,sign_save); /* restore sign */ + } else { + Sgl_clear_exponent_set_hidden(opnd3); + } + /* + * Copy opnd3 to the double extended variable called right. + */ + Sgl_copyto_sglext(opnd3,rightp1,rightp2); + + /* + * A zero "save" helps discover equal operands (for later), + * and is used in swapping operands (if needed). + */ + Sglext_xortointp1(tmpresp1,rightp1,/*to*/save); + + /* + * Compare magnitude of operands. + */ + Sglext_copytoint_exponentmantissa(tmpresp1,signlessleft1); + Sglext_copytoint_exponentmantissa(rightp1,signlessright1); + if (mpy_exponent < add_exponent || mpy_exponent == add_exponent && + Sglext_ismagnitudeless(signlessleft1,signlessright1)) { + /* + * Set the left operand to the larger one by XOR swap. + * First finish the first word "save". + */ + Sglext_xorfromintp1(save,rightp1,/*to*/rightp1); + Sglext_xorfromintp1(save,tmpresp1,/*to*/tmpresp1); + Sglext_swap_lower(tmpresp2,rightp2); + /* also setup exponents used in rest of routine */ + diff_exponent = add_exponent - mpy_exponent; + result_exponent = add_exponent; + } else { + /* also setup exponents used in rest of routine */ + diff_exponent = mpy_exponent - add_exponent; + result_exponent = mpy_exponent; + } + /* Invariant: left is not smaller than right. */ + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for + * this infrequent case. + */ + if (diff_exponent > SGLEXT_THRESHOLD) { + diff_exponent = SGLEXT_THRESHOLD; + } + + /* Align right operand by shifting it to the right */ + Sglext_clear_sign(rightp1); + Sglext_right_align(rightp1,rightp2,/*shifted by*/diff_exponent); + + /* Treat sum and difference of the operands separately. */ + if ((int)save < 0) { + /* + * Difference of the two operands. Overflow can occur if the + * multiply overflowed. A borrow can occur out of the hidden + * bit and force a post normalization phase. + */ + Sglext_subtract(tmpresp1,tmpresp2, rightp1,rightp2, + resultp1,resultp2); + sign_save = Sgl_signextendedsign(resultp1); + if (Sgl_iszero_hidden(resultp1)) { + /* Handle normalization */ + /* A straightforward algorithm would now shift the + * result and extension left until the hidden bit + * becomes one. Not all of the extension bits need + * participate in the shift. Only the two most + * significant bits (round and guard) are needed. + * If only a single shift is needed then the guard + * bit becomes a significant low order bit and the + * extension must participate in the rounding. + * If more than a single shift is needed, then all + * bits to the right of the guard bit are zeros, + * and the guard bit may or may not be zero. */ + Sglext_leftshiftby1(resultp1,resultp2); + + /* Need to check for a zero result. The sign and + * exponent fields have already been zeroed. The more + * efficient test of the full object can be used. + */ + if (Sglext_iszero(resultp1,resultp2)) { + /* Must have been "x-x" or "x+(-x)". */ + if (Is_rounding_mode(ROUNDMINUS)) + Sgl_setone_sign(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + result_exponent--; + + /* Look to see if normalization is finished. */ + if (Sgl_isone_hidden(resultp1)) { + /* No further normalization is needed */ + goto round; + } + + /* Discover first one bit to determine shift amount. + * Use a modified binary search. We have already + * shifted the result one position right and still + * not found a one so the remainder of the extension + * must be zero and simplifies rounding. */ + /* Scan bytes */ + while (Sgl_iszero_hiddenhigh7mantissa(resultp1)) { + Sglext_leftshiftby8(resultp1,resultp2); + result_exponent -= 8; + } + /* Now narrow it down to the nibble */ + if (Sgl_iszero_hiddenhigh3mantissa(resultp1)) { + /* The lower nibble contains the + * normalizing one */ + Sglext_leftshiftby4(resultp1,resultp2); + result_exponent -= 4; + } + /* Select case where first bit is set (already + * normalized) otherwise select the proper shift. */ + jumpsize = Sgl_hiddenhigh3mantissa(resultp1); + if (jumpsize <= 7) switch(jumpsize) { + case 1: + Sglext_leftshiftby3(resultp1,resultp2); + result_exponent -= 3; + break; + case 2: + case 3: + Sglext_leftshiftby2(resultp1,resultp2); + result_exponent -= 2; + break; + case 4: + case 5: + case 6: + case 7: + Sglext_leftshiftby1(resultp1,resultp2); + result_exponent -= 1; + break; + } + } /* end if (hidden...)... */ + /* Fall through and round */ + } /* end if (save < 0)... */ + else { + /* Add magnitudes */ + Sglext_addition(tmpresp1,tmpresp2, + rightp1,rightp2, /*to*/resultp1,resultp2); + sign_save = Sgl_signextendedsign(resultp1); + if (Sgl_isone_hiddenoverflow(resultp1)) { + /* Prenormalization required. */ + Sglext_arithrightshiftby1(resultp1,resultp2); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...add magnitudes... */ + + /* Round the result. If the extension and lower two words are + * all zeros, then the result is exact. Otherwise round in the + * correct direction. Underflow is possible. If a postnormalization + * is necessary, then the mantissa is all zeros so no shift is needed. + */ + round: + if (result_exponent <= 0 && !Is_underflowtrap_enabled()) { + Sglext_denormalize(resultp1,resultp2,result_exponent,is_tiny); + } + Sgl_set_sign(resultp1,/*using*/sign_save); + if (Sglext_isnotzero_mantissap2(resultp2)) { + inexact = TRUE; + switch(Rounding_mode()) { + case ROUNDNEAREST: /* The default. */ + if (Sglext_isone_highp2(resultp2)) { + /* at least 1/2 ulp */ + if (Sglext_isnotzero_low31p2(resultp2) || + Sglext_isone_lowp1(resultp1)) { + /* either exactly half way and odd or + * more than 1/2ulp */ + Sgl_increment(resultp1); + } + } + break; + + case ROUNDPLUS: + if (Sgl_iszero_sign(resultp1)) { + /* Round up positive results */ + Sgl_increment(resultp1); + } + break; + + case ROUNDMINUS: + if (Sgl_isone_sign(resultp1)) { + /* Round down negative results */ + Sgl_increment(resultp1); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if (Sgl_isone_hiddenoverflow(resultp1)) result_exponent++; + } + if (result_exponent >= SGL_INFINITY_EXPONENT) { + /* Overflow */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(resultp1,result_exponent,ovfl); + Sgl_copytoptr(resultp1,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_OVERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return (OPC_2E_OVERFLOWEXCEPTION); + } + inexact = TRUE; + Set_overflowflag(); + Sgl_setoverflow(resultp1); + } else if (result_exponent <= 0) { /* underflow case */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(resultp1,result_exponent,unfl); + Sgl_copytoptr(resultp1,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_UNDERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(OPC_2E_UNDERFLOWEXCEPTION); + } + else if (inexact && is_tiny) Set_underflowflag(); + } + else Sgl_set_exponent(resultp1,result_exponent); + Sgl_copytoptr(resultp1,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) return(OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); +} + +/* + * Single Floating-point Multiply Negate Fused Add + */ + +sgl_fmpynfadd(src1ptr,src2ptr,src3ptr,status,dstptr) + +sgl_floating_point *src1ptr, *src2ptr, *src3ptr, *dstptr; +unsigned int *status; +{ + unsigned int opnd1, opnd2, opnd3; + register unsigned int tmpresp1, tmpresp2; + unsigned int rightp1, rightp2; + unsigned int resultp1, resultp2 = 0; + register int mpy_exponent, add_exponent, count; + boolean inexact = FALSE, is_tiny = FALSE; + + unsigned int signlessleft1, signlessright1, save; + register int result_exponent, diff_exponent; + int sign_save, jumpsize; + + Sgl_copyfromptr(src1ptr,opnd1); + Sgl_copyfromptr(src2ptr,opnd2); + Sgl_copyfromptr(src3ptr,opnd3); + + /* + * set sign bit of result of multiply + */ + if (Sgl_sign(opnd1) ^ Sgl_sign(opnd2)) + Sgl_setzero(resultp1); + else + Sgl_setnegativezero(resultp1); + + /* + * Generate multiply exponent + */ + mpy_exponent = Sgl_exponent(opnd1) + Sgl_exponent(opnd2) - SGL_BIAS; + + /* + * check first operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd1)) { + if (Sgl_iszero_mantissa(opnd1)) { + if (Sgl_isnotnan(opnd2) && Sgl_isnotnan(opnd3)) { + if (Sgl_iszero_exponentmantissa(opnd2)) { + /* + * invalid since operands are infinity + * and zero + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Sgl_isinfinity(opnd3) && + (Sgl_sign(resultp1) ^ Sgl_sign(opnd3))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Sgl_setinfinity_exponentmantissa(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd1); + } + /* + * is second operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + Sgl_copytoptr(opnd2,dstptr); + return(NOEXCEPTION); + } + /* + * is third operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd3)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd3); + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Sgl_copytoptr(opnd1,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check second operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd2)) { + if (Sgl_iszero_mantissa(opnd2)) { + if (Sgl_isnotnan(opnd3)) { + if (Sgl_iszero_exponentmantissa(opnd1)) { + /* + * invalid since multiply operands are + * zero & infinity + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(opnd2); + Sgl_copytoptr(opnd2,dstptr); + return(NOEXCEPTION); + } + + /* + * Check third operand for infinity with a + * sign opposite of the multiply result + */ + if (Sgl_isinfinity(opnd3) && + (Sgl_sign(resultp1) ^ Sgl_sign(opnd3))) { + /* + * invalid since attempting a magnitude + * subtraction of infinities + */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + + /* + * return infinity + */ + Sgl_setinfinity_exponentmantissa(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + } + /* + * is third operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd3)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd3); + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + Sgl_copytoptr(opnd2,dstptr); + return(NOEXCEPTION); + } + } + + /* + * check third operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd3)) { + if (Sgl_iszero_mantissa(opnd3)) { + /* return infinity */ + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd3)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(OPC_2E_INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd3); + } + /* + * return quiet NaN + */ + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + } + + /* + * Generate multiply mantissa + */ + if (Sgl_isnotzero_exponent(opnd1)) { + /* set hidden bit */ + Sgl_clear_signexponent_set_hidden(opnd1); + } + else { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd1)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Sgl_iszero_exponentmantissa(opnd3)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Sgl_or_signs(opnd3,resultp1); + } else { + Sgl_and_signs(opnd3,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Sgl_iszero_exponent(opnd3) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Sgl_signextendedsign(opnd3); + result_exponent = 0; + Sgl_leftshiftby1(opnd3); + Sgl_normalize(opnd3,result_exponent); + Sgl_set_sign(opnd3,/*using*/sign_save); + Sgl_setwrapped_exponent(opnd3,result_exponent, + unfl); + Sgl_copytoptr(opnd3,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* is denormalized, adjust exponent */ + Sgl_clear_signexponent(opnd1); + Sgl_leftshiftby1(opnd1); + Sgl_normalize(opnd1,mpy_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Sgl_isnotzero_exponent(opnd2)) { + Sgl_clear_signexponent_set_hidden(opnd2); + } + else { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd2)) { + /* + * Perform the add opnd3 with zero here. + */ + if (Sgl_iszero_exponentmantissa(opnd3)) { + if (Is_rounding_mode(ROUNDMINUS)) { + Sgl_or_signs(opnd3,resultp1); + } else { + Sgl_and_signs(opnd3,resultp1); + } + } + /* + * Now let's check for trapped underflow case. + */ + else if (Sgl_iszero_exponent(opnd3) && + Is_underflowtrap_enabled()) { + /* need to normalize results mantissa */ + sign_save = Sgl_signextendedsign(opnd3); + result_exponent = 0; + Sgl_leftshiftby1(opnd3); + Sgl_normalize(opnd3,result_exponent); + Sgl_set_sign(opnd3,/*using*/sign_save); + Sgl_setwrapped_exponent(opnd3,result_exponent, + unfl); + Sgl_copytoptr(opnd3,dstptr); + /* inexact = FALSE */ + return(OPC_2E_UNDERFLOWEXCEPTION); + } + Sgl_copytoptr(opnd3,dstptr); + return(NOEXCEPTION); + } + /* is denormalized; want to normalize */ + Sgl_clear_signexponent(opnd2); + Sgl_leftshiftby1(opnd2); + Sgl_normalize(opnd2,mpy_exponent); + } + + /* Multiply the first two source mantissas together */ + + /* + * The intermediate result will be kept in tmpres, + * which needs enough room for 106 bits of mantissa, + * so lets call it a Double extended. + */ + Sglext_setzero(tmpresp1,tmpresp2); + + /* + * Four bits at a time are inspected in each loop, and a + * simple shift and add multiply algorithm is used. + */ + for (count = SGL_P-1; count >= 0; count -= 4) { + Sglext_rightshiftby4(tmpresp1,tmpresp2); + if (Sbit28(opnd1)) { + /* Twoword_add should be an ADD followed by 2 ADDC's */ + Twoword_add(tmpresp1, tmpresp2, opnd2<<3, 0); + } + if (Sbit29(opnd1)) { + Twoword_add(tmpresp1, tmpresp2, opnd2<<2, 0); + } + if (Sbit30(opnd1)) { + Twoword_add(tmpresp1, tmpresp2, opnd2<<1, 0); + } + if (Sbit31(opnd1)) { + Twoword_add(tmpresp1, tmpresp2, opnd2, 0); + } + Sgl_rightshiftby4(opnd1); + } + if (Is_sexthiddenoverflow(tmpresp1)) { + /* result mantissa >= 2 (mantissa overflow) */ + mpy_exponent++; + Sglext_rightshiftby4(tmpresp1,tmpresp2); + } else { + Sglext_rightshiftby3(tmpresp1,tmpresp2); + } + + /* + * Restore the sign of the mpy result which was saved in resultp1. + * The exponent will continue to be kept in mpy_exponent. + */ + Sglext_set_sign(tmpresp1,Sgl_sign(resultp1)); + + /* + * No rounding is required, since the result of the multiply + * is exact in the extended format. + */ + + /* + * Now we are ready to perform the add portion of the operation. + * + * The exponents need to be kept as integers for now, since the + * multiply result might not fit into the exponent field. We + * can't overflow or underflow because of this yet, since the + * add could bring the final result back into range. + */ + add_exponent = Sgl_exponent(opnd3); + + /* + * Check for denormalized or zero add operand. + */ + if (add_exponent == 0) { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd3)) { + /* right is zero */ + /* Left can't be zero and must be result. + * + * The final result is now in tmpres and mpy_exponent, + * and needs to be rounded and squeezed back into + * double precision format from double extended. + */ + result_exponent = mpy_exponent; + Sglext_copy(tmpresp1,tmpresp2,resultp1,resultp2); + sign_save = Sgl_signextendedsign(resultp1);/*save sign*/ + goto round; + } + + /* + * Neither are zeroes. + * Adjust exponent and normalize add operand. + */ + sign_save = Sgl_signextendedsign(opnd3); /* save sign */ + Sgl_clear_signexponent(opnd3); + Sgl_leftshiftby1(opnd3); + Sgl_normalize(opnd3,add_exponent); + Sgl_set_sign(opnd3,sign_save); /* restore sign */ + } else { + Sgl_clear_exponent_set_hidden(opnd3); + } + /* + * Copy opnd3 to the double extended variable called right. + */ + Sgl_copyto_sglext(opnd3,rightp1,rightp2); + + /* + * A zero "save" helps discover equal operands (for later), + * and is used in swapping operands (if needed). + */ + Sglext_xortointp1(tmpresp1,rightp1,/*to*/save); + + /* + * Compare magnitude of operands. + */ + Sglext_copytoint_exponentmantissa(tmpresp1,signlessleft1); + Sglext_copytoint_exponentmantissa(rightp1,signlessright1); + if (mpy_exponent < add_exponent || mpy_exponent == add_exponent && + Sglext_ismagnitudeless(signlessleft1,signlessright1)) { + /* + * Set the left operand to the larger one by XOR swap. + * First finish the first word "save". + */ + Sglext_xorfromintp1(save,rightp1,/*to*/rightp1); + Sglext_xorfromintp1(save,tmpresp1,/*to*/tmpresp1); + Sglext_swap_lower(tmpresp2,rightp2); + /* also setup exponents used in rest of routine */ + diff_exponent = add_exponent - mpy_exponent; + result_exponent = add_exponent; + } else { + /* also setup exponents used in rest of routine */ + diff_exponent = mpy_exponent - add_exponent; + result_exponent = mpy_exponent; + } + /* Invariant: left is not smaller than right. */ + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for + * this infrequent case. + */ + if (diff_exponent > SGLEXT_THRESHOLD) { + diff_exponent = SGLEXT_THRESHOLD; + } + + /* Align right operand by shifting it to the right */ + Sglext_clear_sign(rightp1); + Sglext_right_align(rightp1,rightp2,/*shifted by*/diff_exponent); + + /* Treat sum and difference of the operands separately. */ + if ((int)save < 0) { + /* + * Difference of the two operands. Overflow can occur if the + * multiply overflowed. A borrow can occur out of the hidden + * bit and force a post normalization phase. + */ + Sglext_subtract(tmpresp1,tmpresp2, rightp1,rightp2, + resultp1,resultp2); + sign_save = Sgl_signextendedsign(resultp1); + if (Sgl_iszero_hidden(resultp1)) { + /* Handle normalization */ + /* A straightforward algorithm would now shift the + * result and extension left until the hidden bit + * becomes one. Not all of the extension bits need + * participate in the shift. Only the two most + * significant bits (round and guard) are needed. + * If only a single shift is needed then the guard + * bit becomes a significant low order bit and the + * extension must participate in the rounding. + * If more than a single shift is needed, then all + * bits to the right of the guard bit are zeros, + * and the guard bit may or may not be zero. */ + Sglext_leftshiftby1(resultp1,resultp2); + + /* Need to check for a zero result. The sign and + * exponent fields have already been zeroed. The more + * efficient test of the full object can be used. + */ + if (Sglext_iszero(resultp1,resultp2)) { + /* Must have been "x-x" or "x+(-x)". */ + if (Is_rounding_mode(ROUNDMINUS)) + Sgl_setone_sign(resultp1); + Sgl_copytoptr(resultp1,dstptr); + return(NOEXCEPTION); + } + result_exponent--; + + /* Look to see if normalization is finished. */ + if (Sgl_isone_hidden(resultp1)) { + /* No further normalization is needed */ + goto round; + } + + /* Discover first one bit to determine shift amount. + * Use a modified binary search. We have already + * shifted the result one position right and still + * not found a one so the remainder of the extension + * must be zero and simplifies rounding. */ + /* Scan bytes */ + while (Sgl_iszero_hiddenhigh7mantissa(resultp1)) { + Sglext_leftshiftby8(resultp1,resultp2); + result_exponent -= 8; + } + /* Now narrow it down to the nibble */ + if (Sgl_iszero_hiddenhigh3mantissa(resultp1)) { + /* The lower nibble contains the + * normalizing one */ + Sglext_leftshiftby4(resultp1,resultp2); + result_exponent -= 4; + } + /* Select case where first bit is set (already + * normalized) otherwise select the proper shift. */ + jumpsize = Sgl_hiddenhigh3mantissa(resultp1); + if (jumpsize <= 7) switch(jumpsize) { + case 1: + Sglext_leftshiftby3(resultp1,resultp2); + result_exponent -= 3; + break; + case 2: + case 3: + Sglext_leftshiftby2(resultp1,resultp2); + result_exponent -= 2; + break; + case 4: + case 5: + case 6: + case 7: + Sglext_leftshiftby1(resultp1,resultp2); + result_exponent -= 1; + break; + } + } /* end if (hidden...)... */ + /* Fall through and round */ + } /* end if (save < 0)... */ + else { + /* Add magnitudes */ + Sglext_addition(tmpresp1,tmpresp2, + rightp1,rightp2, /*to*/resultp1,resultp2); + sign_save = Sgl_signextendedsign(resultp1); + if (Sgl_isone_hiddenoverflow(resultp1)) { + /* Prenormalization required. */ + Sglext_arithrightshiftby1(resultp1,resultp2); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...add magnitudes... */ + + /* Round the result. If the extension and lower two words are + * all zeros, then the result is exact. Otherwise round in the + * correct direction. Underflow is possible. If a postnormalization + * is necessary, then the mantissa is all zeros so no shift is needed. + */ + round: + if (result_exponent <= 0 && !Is_underflowtrap_enabled()) { + Sglext_denormalize(resultp1,resultp2,result_exponent,is_tiny); + } + Sgl_set_sign(resultp1,/*using*/sign_save); + if (Sglext_isnotzero_mantissap2(resultp2)) { + inexact = TRUE; + switch(Rounding_mode()) { + case ROUNDNEAREST: /* The default. */ + if (Sglext_isone_highp2(resultp2)) { + /* at least 1/2 ulp */ + if (Sglext_isnotzero_low31p2(resultp2) || + Sglext_isone_lowp1(resultp1)) { + /* either exactly half way and odd or + * more than 1/2ulp */ + Sgl_increment(resultp1); + } + } + break; + + case ROUNDPLUS: + if (Sgl_iszero_sign(resultp1)) { + /* Round up positive results */ + Sgl_increment(resultp1); + } + break; + + case ROUNDMINUS: + if (Sgl_isone_sign(resultp1)) { + /* Round down negative results */ + Sgl_increment(resultp1); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if (Sgl_isone_hiddenoverflow(resultp1)) result_exponent++; + } + if (result_exponent >= SGL_INFINITY_EXPONENT) { + /* Overflow */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(resultp1,result_exponent,ovfl); + Sgl_copytoptr(resultp1,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_OVERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return (OPC_2E_OVERFLOWEXCEPTION); + } + inexact = TRUE; + Set_overflowflag(); + Sgl_setoverflow(resultp1); + } else if (result_exponent <= 0) { /* underflow case */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(resultp1,result_exponent,unfl); + Sgl_copytoptr(resultp1,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) + return (OPC_2E_UNDERFLOWEXCEPTION | + OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(OPC_2E_UNDERFLOWEXCEPTION); + } + else if (inexact && is_tiny) Set_underflowflag(); + } + else Sgl_set_exponent(resultp1,result_exponent); + Sgl_copytoptr(resultp1,dstptr); + if (inexact) + if (Is_inexacttrap_enabled()) return(OPC_2E_INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); +} + diff --git a/arch/parisc/math-emu/fpbits.h b/arch/parisc/math-emu/fpbits.h new file mode 100644 index 000000000..b46bddb9a --- /dev/null +++ b/arch/parisc/math-emu/fpbits.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ + +#ifdef __NO_PA_HDRS + PA header file -- do not include this header file for non-PA builds. +#endif + + +/* + * These macros are designed to be portable to all machines that have + * a wordsize greater than or equal to 32 bits that support the portable + * C compiler and the standard C preprocessor. Wordsize (default 32) + * and bitfield assignment (default left-to-right, unlike VAX, PDP-11) + * should be predefined using the constants HOSTWDSZ and BITFRL and + * the C compiler "-D" flag (e.g., -DHOSTWDSZ=36 -DBITFLR for the DEC-20). + * Note that the macro arguments assume that the integer being referenced + * is a 32-bit integer (right-justified on the 20) and that bit 0 is the + * most significant bit. + */ + +#ifndef HOSTWDSZ +#define HOSTWDSZ 32 +#endif + + +/*########################### Macros ######################################*/ + +/*------------------------------------------------------------------------- + * NewDeclareBitField_Reference - Declare a structure similar to the simulator + * function "DeclBitfR" except its use is restricted to occur within a larger + * enclosing structure or union definition. This declaration is an unnamed + * structure with the argument, name, as the member name and the argument, + * uname, as the element name. + *----------------------------------------------------------------------- */ +#define Bitfield_extract(start, length, object) \ + ((object) >> (HOSTWDSZ - (start) - (length)) & \ + ((unsigned)-1 >> (HOSTWDSZ - (length)))) + +#define Bitfield_signed_extract(start, length, object) \ + ((int)((object) << start) >> (HOSTWDSZ - (length))) + +#define Bitfield_mask(start, len, object) \ + ((object) & (((unsigned)-1 >> (HOSTWDSZ-len)) << (HOSTWDSZ-start-len))) + +#define Bitfield_deposit(value,start,len,object) object = \ + ((object) & ~(((unsigned)-1 >> (HOSTWDSZ-len)) << (HOSTWDSZ-start-len))) | \ + (((value) & ((unsigned)-1 >> (HOSTWDSZ-len))) << (HOSTWDSZ-start-len)) diff --git a/arch/parisc/math-emu/fpu.h b/arch/parisc/math-emu/fpu.h new file mode 100644 index 000000000..dec951d40 --- /dev/null +++ b/arch/parisc/math-emu/fpu.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ + +#ifndef _MACHINE_FPU_INCLUDED /* allows multiple inclusion */ +#define _MACHINE_FPU_INCLUDED + +#define PA83_FPU_FLAG 0x00000001 +#define PA89_FPU_FLAG 0x00000002 +#define PA2_0_FPU_FLAG 0x00000010 + +#define TIMEX_EXTEN_FLAG 0x00000004 + +#define ROLEX_EXTEN_FLAG 0x00000008 +#define COPR_FP 0x00000080 /* Floating point -- Coprocessor 0 */ +#define SFU_MPY_DIVIDE 0x00008000 /* Multiply/Divide __ SFU 0 */ + +#define EM_FPU_TYPE_OFFSET 272 + +/* version of EMULATION software for COPR,0,0 instruction */ +#define EMULATION_VERSION 4 + +/* + * The only way to differentiate between TIMEX and ROLEX (or PCX-S and PCX-T) + * is through the potential type field from the PDC_MODEL call. + * The following flags are used to assist this differentiation. + */ + +#define ROLEX_POTENTIAL_KEY_FLAGS PDC_MODEL_CPU_KEY_WORD_TO_IO +#define TIMEX_POTENTIAL_KEY_FLAGS (PDC_MODEL_CPU_KEY_QUAD_STORE | \ + PDC_MODEL_CPU_KEY_RECIP_SQRT) + +#endif /* ! _MACHINE_FPU_INCLUDED */ diff --git a/arch/parisc/math-emu/fpudispatch.c b/arch/parisc/math-emu/fpudispatch.c new file mode 100644 index 000000000..01ed13322 --- /dev/null +++ b/arch/parisc/math-emu/fpudispatch.c @@ -0,0 +1,1480 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/fp/fpudispatch.c $Revision: 1.1 $ + * + * Purpose: + * <<please update with a synopsis of the functionality provided by this file>> + * + * External Interfaces: + * <<the following list was autogenerated, please review>> + * emfpudispatch(ir, dummy1, dummy2, fpregs) + * fpudispatch(ir, excp_code, holder, fpregs) + * + * Internal Interfaces: + * <<the following list was autogenerated, please review>> + * static u_int decode_06(u_int, u_int *) + * static u_int decode_0c(u_int, u_int, u_int, u_int *) + * static u_int decode_0e(u_int, u_int, u_int, u_int *) + * static u_int decode_26(u_int, u_int *) + * static u_int decode_2e(u_int, u_int *) + * static void update_status_cbit(u_int *, u_int, u_int, u_int) + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + +#define FPUDEBUG 0 + +#include "float.h" +#include <linux/bug.h> +#include <linux/kernel.h> +#include <asm/processor.h> +/* #include <sys/debug.h> */ +/* #include <machine/sys/mdep_private.h> */ + +#define COPR_INST 0x30000000 + +/* + * definition of extru macro. If pos and len are constants, the compiler + * will generate an extru instruction when optimized + */ +#define extru(r,pos,len) (((r) >> (31-(pos))) & (( 1 << (len)) - 1)) +/* definitions of bit field locations in the instruction */ +#define fpmajorpos 5 +#define fpr1pos 10 +#define fpr2pos 15 +#define fptpos 31 +#define fpsubpos 18 +#define fpclass1subpos 16 +#define fpclasspos 22 +#define fpfmtpos 20 +#define fpdfpos 18 +#define fpnulpos 26 +/* + * the following are the extra bits for the 0E major op + */ +#define fpxr1pos 24 +#define fpxr2pos 19 +#define fpxtpos 25 +#define fpxpos 23 +#define fp0efmtpos 20 +/* + * the following are for the multi-ops + */ +#define fprm1pos 10 +#define fprm2pos 15 +#define fptmpos 31 +#define fprapos 25 +#define fptapos 20 +#define fpmultifmt 26 +/* + * the following are for the fused FP instructions + */ + /* fprm1pos 10 */ + /* fprm2pos 15 */ +#define fpraupos 18 +#define fpxrm2pos 19 + /* fpfmtpos 20 */ +#define fpralpos 23 +#define fpxrm1pos 24 + /* fpxtpos 25 */ +#define fpfusedsubop 26 + /* fptpos 31 */ + +/* + * offset to constant zero in the FP emulation registers + */ +#define fpzeroreg (32*sizeof(double)/sizeof(u_int)) + +/* + * extract the major opcode from the instruction + */ +#define get_major(op) extru(op,fpmajorpos,6) +/* + * extract the two bit class field from the FP instruction. The class is at bit + * positions 21-22 + */ +#define get_class(op) extru(op,fpclasspos,2) +/* + * extract the 3 bit subop field. For all but class 1 instructions, it is + * located at bit positions 16-18 + */ +#define get_subop(op) extru(op,fpsubpos,3) +/* + * extract the 2 or 3 bit subop field from class 1 instructions. It is located + * at bit positions 15-16 (PA1.1) or 14-16 (PA2.0) + */ +#define get_subop1_PA1_1(op) extru(op,fpclass1subpos,2) /* PA89 (1.1) fmt */ +#define get_subop1_PA2_0(op) extru(op,fpclass1subpos,3) /* PA 2.0 fmt */ + +/* definitions of unimplemented exceptions */ +#define MAJOR_0C_EXCP 0x09 +#define MAJOR_0E_EXCP 0x0b +#define MAJOR_06_EXCP 0x03 +#define MAJOR_26_EXCP 0x23 +#define MAJOR_2E_EXCP 0x2b +#define PA83_UNIMP_EXCP 0x01 + +/* + * Special Defines for TIMEX specific code + */ + +#define FPU_TYPE_FLAG_POS (EM_FPU_TYPE_OFFSET>>2) +#define TIMEX_ROLEX_FPU_MASK (TIMEX_EXTEN_FLAG|ROLEX_EXTEN_FLAG) + +/* + * Static function definitions + */ +#define _PROTOTYPES +#if defined(_PROTOTYPES) || defined(_lint) +static u_int decode_0c(u_int, u_int, u_int, u_int *); +static u_int decode_0e(u_int, u_int, u_int, u_int *); +static u_int decode_06(u_int, u_int *); +static u_int decode_26(u_int, u_int *); +static u_int decode_2e(u_int, u_int *); +static void update_status_cbit(u_int *, u_int, u_int, u_int); +#else /* !_PROTOTYPES&&!_lint */ +static u_int decode_0c(); +static u_int decode_0e(); +static u_int decode_06(); +static u_int decode_26(); +static u_int decode_2e(); +static void update_status_cbit(); +#endif /* _PROTOTYPES&&!_lint */ + +#define VASSERT(x) + +static void parisc_linux_get_fpu_type(u_int fpregs[]) +{ + /* on pa-linux the fpu type is not filled in by the + * caller; it is constructed here + */ + if (boot_cpu_data.cpu_type == pcxs) + fpregs[FPU_TYPE_FLAG_POS] = TIMEX_EXTEN_FLAG; + else if (boot_cpu_data.cpu_type == pcxt || + boot_cpu_data.cpu_type == pcxt_) + fpregs[FPU_TYPE_FLAG_POS] = ROLEX_EXTEN_FLAG; + else if (boot_cpu_data.cpu_type >= pcxu) + fpregs[FPU_TYPE_FLAG_POS] = PA2_0_FPU_FLAG; +} + +/* + * this routine will decode the excepting floating point instruction and + * call the appropriate emulation routine. + * It is called by decode_fpu with the following parameters: + * fpudispatch(current_ir, unimplemented_code, 0, &Fpu_register) + * where current_ir is the instruction to be emulated, + * unimplemented_code is the exception_code that the hardware generated + * and &Fpu_register is the address of emulated FP reg 0. + */ +u_int +fpudispatch(u_int ir, u_int excp_code, u_int holder, u_int fpregs[]) +{ + u_int class, subop; + u_int fpu_type_flags; + + /* All FP emulation code assumes that ints are 4-bytes in length */ + VASSERT(sizeof(int) == 4); + + parisc_linux_get_fpu_type(fpregs); + + fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; /* get fpu type flags */ + + class = get_class(ir); + if (class == 1) { + if (fpu_type_flags & PA2_0_FPU_FLAG) + subop = get_subop1_PA2_0(ir); + else + subop = get_subop1_PA1_1(ir); + } + else + subop = get_subop(ir); + + if (FPUDEBUG) printk("class %d subop %d\n", class, subop); + + switch (excp_code) { + case MAJOR_0C_EXCP: + case PA83_UNIMP_EXCP: + return(decode_0c(ir,class,subop,fpregs)); + case MAJOR_0E_EXCP: + return(decode_0e(ir,class,subop,fpregs)); + case MAJOR_06_EXCP: + return(decode_06(ir,fpregs)); + case MAJOR_26_EXCP: + return(decode_26(ir,fpregs)); + case MAJOR_2E_EXCP: + return(decode_2e(ir,fpregs)); + default: + /* "crashme Night Gallery painting nr 2. (asm_crash.s). + * This was fixed for multi-user kernels, but + * workstation kernels had a panic here. This allowed + * any arbitrary user to panic the kernel by executing + * setting the FP exception registers to strange values + * and generating an emulation trap. The emulation and + * exception code must never be able to panic the + * kernel. + */ + return(UNIMPLEMENTEDEXCEPTION); + } +} + +/* + * this routine is called by $emulation_trap to emulate a coprocessor + * instruction if one doesn't exist + */ +u_int +emfpudispatch(u_int ir, u_int dummy1, u_int dummy2, u_int fpregs[]) +{ + u_int class, subop, major; + u_int fpu_type_flags; + + /* All FP emulation code assumes that ints are 4-bytes in length */ + VASSERT(sizeof(int) == 4); + + fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; /* get fpu type flags */ + + major = get_major(ir); + class = get_class(ir); + if (class == 1) { + if (fpu_type_flags & PA2_0_FPU_FLAG) + subop = get_subop1_PA2_0(ir); + else + subop = get_subop1_PA1_1(ir); + } + else + subop = get_subop(ir); + switch (major) { + case 0x0C: + return(decode_0c(ir,class,subop,fpregs)); + case 0x0E: + return(decode_0e(ir,class,subop,fpregs)); + case 0x06: + return(decode_06(ir,fpregs)); + case 0x26: + return(decode_26(ir,fpregs)); + case 0x2E: + return(decode_2e(ir,fpregs)); + default: + return(PA83_UNIMP_EXCP); + } +} + + +static u_int +decode_0c(u_int ir, u_int class, u_int subop, u_int fpregs[]) +{ + u_int r1,r2,t; /* operand register offsets */ + u_int fmt; /* also sf for class 1 conversions */ + u_int df; /* for class 1 conversions */ + u_int *status; + u_int retval, local_status; + u_int fpu_type_flags; + + if (ir == COPR_INST) { + fpregs[0] = EMULATION_VERSION << 11; + return(NOEXCEPTION); + } + status = &fpregs[0]; /* fp status register */ + local_status = fpregs[0]; /* and local copy */ + r1 = extru(ir,fpr1pos,5) * sizeof(double)/sizeof(u_int); + if (r1 == 0) /* map fr0 source to constant zero */ + r1 = fpzeroreg; + t = extru(ir,fptpos,5) * sizeof(double)/sizeof(u_int); + if (t == 0 && class != 2) /* don't allow fr0 as a dest */ + return(MAJOR_0C_EXCP); + fmt = extru(ir,fpfmtpos,2); /* get fmt completer */ + + switch (class) { + case 0: + switch (subop) { + case 0: /* COPR 0,0 emulated above*/ + case 1: + return(MAJOR_0C_EXCP); + case 2: /* FCPY */ + switch (fmt) { + case 2: /* illegal */ + return(MAJOR_0C_EXCP); + case 3: /* quad */ + t &= ~3; /* force to even reg #s */ + r1 &= ~3; + fpregs[t+3] = fpregs[r1+3]; + fpregs[t+2] = fpregs[r1+2]; + fallthrough; + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + fpregs[t] = fpregs[r1]; + return(NOEXCEPTION); + } + BUG(); + case 3: /* FABS */ + switch (fmt) { + case 2: /* illegal */ + return(MAJOR_0C_EXCP); + case 3: /* quad */ + t &= ~3; /* force to even reg #s */ + r1 &= ~3; + fpregs[t+3] = fpregs[r1+3]; + fpregs[t+2] = fpregs[r1+2]; + fallthrough; + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + /* copy and clear sign bit */ + fpregs[t] = fpregs[r1] & 0x7fffffff; + return(NOEXCEPTION); + } + BUG(); + case 6: /* FNEG */ + switch (fmt) { + case 2: /* illegal */ + return(MAJOR_0C_EXCP); + case 3: /* quad */ + t &= ~3; /* force to even reg #s */ + r1 &= ~3; + fpregs[t+3] = fpregs[r1+3]; + fpregs[t+2] = fpregs[r1+2]; + fallthrough; + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + /* copy and invert sign bit */ + fpregs[t] = fpregs[r1] ^ 0x80000000; + return(NOEXCEPTION); + } + BUG(); + case 7: /* FNEGABS */ + switch (fmt) { + case 2: /* illegal */ + return(MAJOR_0C_EXCP); + case 3: /* quad */ + t &= ~3; /* force to even reg #s */ + r1 &= ~3; + fpregs[t+3] = fpregs[r1+3]; + fpregs[t+2] = fpregs[r1+2]; + fallthrough; + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + /* copy and set sign bit */ + fpregs[t] = fpregs[r1] | 0x80000000; + return(NOEXCEPTION); + } + BUG(); + case 4: /* FSQRT */ + switch (fmt) { + case 0: + return(sgl_fsqrt(&fpregs[r1],0, + &fpregs[t],status)); + case 1: + return(dbl_fsqrt(&fpregs[r1],0, + &fpregs[t],status)); + case 2: + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + BUG(); + case 5: /* FRND */ + switch (fmt) { + case 0: + return(sgl_frnd(&fpregs[r1],0, + &fpregs[t],status)); + case 1: + return(dbl_frnd(&fpregs[r1],0, + &fpregs[t],status)); + case 2: + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + } /* end of switch (subop) */ + BUG(); + case 1: /* class 1 */ + df = extru(ir,fpdfpos,2); /* get dest format */ + if ((df & 2) || (fmt & 2)) { + /* + * fmt's 2 and 3 are illegal of not implemented + * quad conversions + */ + return(MAJOR_0C_EXCP); + } + /* + * encode source and dest formats into 2 bits. + * high bit is source, low bit is dest. + * bit = 1 --> double precision + */ + fmt = (fmt << 1) | df; + switch (subop) { + case 0: /* FCNVFF */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(MAJOR_0C_EXCP); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvff(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvff(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(MAJOR_0C_EXCP); + } + BUG(); + case 1: /* FCNVXF */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 2: /* FCNVFX */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 3: /* FCNVFXT */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 5: /* FCNVUF (PA2.0 only) */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 6: /* FCNVFU (PA2.0 only) */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 7: /* FCNVFUT (PA2.0 only) */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 4: /* undefined */ + return(MAJOR_0C_EXCP); + } /* end of switch subop */ + BUG(); + case 2: /* class 2 */ + fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; + r2 = extru(ir, fpr2pos, 5) * sizeof(double)/sizeof(u_int); + if (r2 == 0) + r2 = fpzeroreg; + if (fpu_type_flags & PA2_0_FPU_FLAG) { + /* FTEST if nullify bit set, otherwise FCMP */ + if (extru(ir, fpnulpos, 1)) { /* FTEST */ + switch (fmt) { + case 0: + /* + * arg0 is not used + * second param is the t field used for + * ftest,acc and ftest,rej + * third param is the subop (y-field) + */ + BUG(); + /* Unsupported + * return(ftest(0L,extru(ir,fptpos,5), + * &fpregs[0],subop)); + */ + case 1: + case 2: + case 3: + return(MAJOR_0C_EXCP); + } + } else { /* FCMP */ + switch (fmt) { + case 0: + retval = sgl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + case 1: + retval = dbl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + case 2: /* illegal */ + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + } + } /* end of if for PA2.0 */ + else { /* PA1.0 & PA1.1 */ + switch (subop) { + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return(MAJOR_0C_EXCP); + case 0: /* FCMP */ + switch (fmt) { + case 0: + retval = sgl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + case 1: + retval = dbl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + case 2: /* illegal */ + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + BUG(); + case 1: /* FTEST */ + switch (fmt) { + case 0: + /* + * arg0 is not used + * second param is the t field used for + * ftest,acc and ftest,rej + * third param is the subop (y-field) + */ + BUG(); + /* unsupported + * return(ftest(0L,extru(ir,fptpos,5), + * &fpregs[0],subop)); + */ + case 1: + case 2: + case 3: + return(MAJOR_0C_EXCP); + } + BUG(); + } /* end of switch subop */ + } /* end of else for PA1.0 & PA1.1 */ + BUG(); + case 3: /* class 3 */ + r2 = extru(ir,fpr2pos,5) * sizeof(double)/sizeof(u_int); + if (r2 == 0) + r2 = fpzeroreg; + switch (subop) { + case 5: + case 6: + case 7: + return(MAJOR_0C_EXCP); + + case 0: /* FADD */ + switch (fmt) { + case 0: + return(sgl_fadd(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_fadd(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 2: /* illegal */ + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + BUG(); + case 1: /* FSUB */ + switch (fmt) { + case 0: + return(sgl_fsub(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_fsub(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 2: /* illegal */ + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + BUG(); + case 2: /* FMPY */ + switch (fmt) { + case 0: + return(sgl_fmpy(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_fmpy(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 2: /* illegal */ + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + BUG(); + case 3: /* FDIV */ + switch (fmt) { + case 0: + return(sgl_fdiv(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_fdiv(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 2: /* illegal */ + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + BUG(); + case 4: /* FREM */ + switch (fmt) { + case 0: + return(sgl_frem(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_frem(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 2: /* illegal */ + case 3: /* quad not implemented */ + return(MAJOR_0C_EXCP); + } + BUG(); + } /* end of class 3 switch */ + } /* end of switch(class) */ + + /* If we get here, something is really wrong! */ + return(MAJOR_0C_EXCP); +} + +static u_int +decode_0e(ir,class,subop,fpregs) +u_int ir,class,subop; +u_int fpregs[]; +{ + u_int r1,r2,t; /* operand register offsets */ + u_int fmt; /* also sf for class 1 conversions */ + u_int df; /* dest format for class 1 conversions */ + u_int *status; + u_int retval, local_status; + u_int fpu_type_flags; + + status = &fpregs[0]; + local_status = fpregs[0]; + r1 = ((extru(ir,fpr1pos,5)<<1)|(extru(ir,fpxr1pos,1))); + if (r1 == 0) + r1 = fpzeroreg; + t = ((extru(ir,fptpos,5)<<1)|(extru(ir,fpxtpos,1))); + if (t == 0 && class != 2) + return(MAJOR_0E_EXCP); + if (class < 2) /* class 0 or 1 has 2 bit fmt */ + fmt = extru(ir,fpfmtpos,2); + else /* class 2 and 3 have 1 bit fmt */ + fmt = extru(ir,fp0efmtpos,1); + /* + * An undefined combination, double precision accessing the + * right half of a FPR, can get us into trouble. + * Let's just force proper alignment on it. + */ + if (fmt == DBL) { + r1 &= ~1; + if (class != 1) + t &= ~1; + } + + switch (class) { + case 0: + switch (subop) { + case 0: /* unimplemented */ + case 1: + return(MAJOR_0E_EXCP); + case 2: /* FCPY */ + switch (fmt) { + case 2: + case 3: + return(MAJOR_0E_EXCP); + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + fpregs[t] = fpregs[r1]; + return(NOEXCEPTION); + } + BUG(); + case 3: /* FABS */ + switch (fmt) { + case 2: + case 3: + return(MAJOR_0E_EXCP); + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + fpregs[t] = fpregs[r1] & 0x7fffffff; + return(NOEXCEPTION); + } + BUG(); + case 6: /* FNEG */ + switch (fmt) { + case 2: + case 3: + return(MAJOR_0E_EXCP); + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + fpregs[t] = fpregs[r1] ^ 0x80000000; + return(NOEXCEPTION); + } + BUG(); + case 7: /* FNEGABS */ + switch (fmt) { + case 2: + case 3: + return(MAJOR_0E_EXCP); + case 1: /* double */ + fpregs[t+1] = fpregs[r1+1]; + fallthrough; + case 0: /* single */ + fpregs[t] = fpregs[r1] | 0x80000000; + return(NOEXCEPTION); + } + BUG(); + case 4: /* FSQRT */ + switch (fmt) { + case 0: + return(sgl_fsqrt(&fpregs[r1],0, + &fpregs[t], status)); + case 1: + return(dbl_fsqrt(&fpregs[r1],0, + &fpregs[t], status)); + case 2: + case 3: + return(MAJOR_0E_EXCP); + } + BUG(); + case 5: /* FRMD */ + switch (fmt) { + case 0: + return(sgl_frnd(&fpregs[r1],0, + &fpregs[t], status)); + case 1: + return(dbl_frnd(&fpregs[r1],0, + &fpregs[t], status)); + case 2: + case 3: + return(MAJOR_0E_EXCP); + } + } /* end of switch (subop */ + BUG(); + case 1: /* class 1 */ + df = extru(ir,fpdfpos,2); /* get dest format */ + /* + * Fix Crashme problem (writing to 31R in double precision) + * here too. + */ + if (df == DBL) { + t &= ~1; + } + if ((df & 2) || (fmt & 2)) + return(MAJOR_0E_EXCP); + + fmt = (fmt << 1) | df; + switch (subop) { + case 0: /* FCNVFF */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(MAJOR_0E_EXCP); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvff(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvff(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(MAJOR_0E_EXCP); + } + BUG(); + case 1: /* FCNVXF */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvxf(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 2: /* FCNVFX */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfx(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 3: /* FCNVFXT */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfxt(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 5: /* FCNVUF (PA2.0 only) */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvuf(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 6: /* FCNVFU (PA2.0 only) */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfu(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 7: /* FCNVFUT (PA2.0 only) */ + switch(fmt) { + case 0: /* sgl/sgl */ + return(sgl_to_sgl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + case 1: /* sgl/dbl */ + return(sgl_to_dbl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + case 2: /* dbl/sgl */ + return(dbl_to_sgl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + case 3: /* dbl/dbl */ + return(dbl_to_dbl_fcnvfut(&fpregs[r1],0, + &fpregs[t],status)); + } + BUG(); + case 4: /* undefined */ + return(MAJOR_0C_EXCP); + } /* end of switch subop */ + BUG(); + case 2: /* class 2 */ + /* + * Be careful out there. + * Crashme can generate cases where FR31R is specified + * as the source or target of a double precision operation. + * Since we just pass the address of the floating-point + * register to the emulation routines, this can cause + * corruption of fpzeroreg. + */ + if (fmt == DBL) + r2 = (extru(ir,fpr2pos,5)<<1); + else + r2 = ((extru(ir,fpr2pos,5)<<1)|(extru(ir,fpxr2pos,1))); + fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; + if (r2 == 0) + r2 = fpzeroreg; + if (fpu_type_flags & PA2_0_FPU_FLAG) { + /* FTEST if nullify bit set, otherwise FCMP */ + if (extru(ir, fpnulpos, 1)) { /* FTEST */ + /* not legal */ + return(MAJOR_0E_EXCP); + } else { /* FCMP */ + switch (fmt) { + /* + * fmt is only 1 bit long + */ + case 0: + retval = sgl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + case 1: + retval = dbl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + } + } + } /* end of if for PA2.0 */ + else { /* PA1.0 & PA1.1 */ + switch (subop) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return(MAJOR_0E_EXCP); + case 0: /* FCMP */ + switch (fmt) { + /* + * fmt is only 1 bit long + */ + case 0: + retval = sgl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + case 1: + retval = dbl_fcmp(&fpregs[r1], + &fpregs[r2],extru(ir,fptpos,5), + &local_status); + update_status_cbit(status,local_status, + fpu_type_flags, subop); + return(retval); + } + } /* end of switch subop */ + } /* end of else for PA1.0 & PA1.1 */ + BUG(); + case 3: /* class 3 */ + /* + * Be careful out there. + * Crashme can generate cases where FR31R is specified + * as the source or target of a double precision operation. + * Since we just pass the address of the floating-point + * register to the emulation routines, this can cause + * corruption of fpzeroreg. + */ + if (fmt == DBL) + r2 = (extru(ir,fpr2pos,5)<<1); + else + r2 = ((extru(ir,fpr2pos,5)<<1)|(extru(ir,fpxr2pos,1))); + if (r2 == 0) + r2 = fpzeroreg; + switch (subop) { + case 5: + case 6: + case 7: + return(MAJOR_0E_EXCP); + + /* + * Note that fmt is only 1 bit for class 3 */ + case 0: /* FADD */ + switch (fmt) { + case 0: + return(sgl_fadd(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_fadd(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + } + BUG(); + case 1: /* FSUB */ + switch (fmt) { + case 0: + return(sgl_fsub(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_fsub(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + } + BUG(); + case 2: /* FMPY or XMPYU */ + /* + * check for integer multiply (x bit set) + */ + if (extru(ir,fpxpos,1)) { + /* + * emulate XMPYU + */ + switch (fmt) { + case 0: + /* + * bad instruction if t specifies + * the right half of a register + */ + if (t & 1) + return(MAJOR_0E_EXCP); + BUG(); + /* unsupported + * impyu(&fpregs[r1],&fpregs[r2], + * &fpregs[t]); + */ + return(NOEXCEPTION); + case 1: + return(MAJOR_0E_EXCP); + } + } + else { /* FMPY */ + switch (fmt) { + case 0: + return(sgl_fmpy(&fpregs[r1], + &fpregs[r2],&fpregs[t],status)); + case 1: + return(dbl_fmpy(&fpregs[r1], + &fpregs[r2],&fpregs[t],status)); + } + } + BUG(); + case 3: /* FDIV */ + switch (fmt) { + case 0: + return(sgl_fdiv(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_fdiv(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + } + BUG(); + case 4: /* FREM */ + switch (fmt) { + case 0: + return(sgl_frem(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + case 1: + return(dbl_frem(&fpregs[r1],&fpregs[r2], + &fpregs[t],status)); + } + } /* end of class 3 switch */ + } /* end of switch(class) */ + + /* If we get here, something is really wrong! */ + return(MAJOR_0E_EXCP); +} + + +/* + * routine to decode the 06 (FMPYADD and FMPYCFXT) instruction + */ +static u_int +decode_06(ir,fpregs) +u_int ir; +u_int fpregs[]; +{ + u_int rm1, rm2, tm, ra, ta; /* operands */ + u_int fmt; + u_int error = 0; + u_int status; + u_int fpu_type_flags; + union { + double dbl; + float flt; + struct { u_int i1; u_int i2; } ints; + } mtmp, atmp; + + + status = fpregs[0]; /* use a local copy of status reg */ + fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; /* get fpu type flags */ + fmt = extru(ir, fpmultifmt, 1); /* get sgl/dbl flag */ + if (fmt == 0) { /* DBL */ + rm1 = extru(ir, fprm1pos, 5) * sizeof(double)/sizeof(u_int); + if (rm1 == 0) + rm1 = fpzeroreg; + rm2 = extru(ir, fprm2pos, 5) * sizeof(double)/sizeof(u_int); + if (rm2 == 0) + rm2 = fpzeroreg; + tm = extru(ir, fptmpos, 5) * sizeof(double)/sizeof(u_int); + if (tm == 0) + return(MAJOR_06_EXCP); + ra = extru(ir, fprapos, 5) * sizeof(double)/sizeof(u_int); + ta = extru(ir, fptapos, 5) * sizeof(double)/sizeof(u_int); + if (ta == 0) + return(MAJOR_06_EXCP); + + if (fpu_type_flags & TIMEX_ROLEX_FPU_MASK) { + + if (ra == 0) { + /* special case FMPYCFXT, see sgl case below */ + if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2], + &mtmp.ints.i1,&status)) + error = 1; + if (dbl_to_sgl_fcnvfxt(&fpregs[ta], + &atmp.ints.i1,&atmp.ints.i1,&status)) + error = 1; + } + else { + + if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, + &status)) + error = 1; + if (dbl_fadd(&fpregs[ta], &fpregs[ra], &atmp.ints.i1, + &status)) + error = 1; + } + } + + else + + { + if (ra == 0) + ra = fpzeroreg; + + if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, + &status)) + error = 1; + if (dbl_fadd(&fpregs[ta], &fpregs[ra], &atmp.ints.i1, + &status)) + error = 1; + + } + + if (error) + return(MAJOR_06_EXCP); + else { + /* copy results */ + fpregs[tm] = mtmp.ints.i1; + fpregs[tm+1] = mtmp.ints.i2; + fpregs[ta] = atmp.ints.i1; + fpregs[ta+1] = atmp.ints.i2; + fpregs[0] = status; + return(NOEXCEPTION); + } + } + else { /* SGL */ + /* + * calculate offsets for single precision numbers + * See table 6-14 in PA-89 architecture for mapping + */ + rm1 = (extru(ir,fprm1pos,4) | 0x10 ) << 1; /* get offset */ + rm1 |= extru(ir,fprm1pos-4,1); /* add right word offset */ + + rm2 = (extru(ir,fprm2pos,4) | 0x10 ) << 1; /* get offset */ + rm2 |= extru(ir,fprm2pos-4,1); /* add right word offset */ + + tm = (extru(ir,fptmpos,4) | 0x10 ) << 1; /* get offset */ + tm |= extru(ir,fptmpos-4,1); /* add right word offset */ + + ra = (extru(ir,fprapos,4) | 0x10 ) << 1; /* get offset */ + ra |= extru(ir,fprapos-4,1); /* add right word offset */ + + ta = (extru(ir,fptapos,4) | 0x10 ) << 1; /* get offset */ + ta |= extru(ir,fptapos-4,1); /* add right word offset */ + + if (ra == 0x20 &&(fpu_type_flags & TIMEX_ROLEX_FPU_MASK)) { + /* special case FMPYCFXT (really 0) + * This instruction is only present on the Timex and + * Rolex fpu's in so if it is the special case and + * one of these fpu's we run the FMPYCFXT instruction + */ + if (sgl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, + &status)) + error = 1; + if (sgl_to_sgl_fcnvfxt(&fpregs[ta],&atmp.ints.i1, + &atmp.ints.i1,&status)) + error = 1; + } + else { + if (sgl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, + &status)) + error = 1; + if (sgl_fadd(&fpregs[ta], &fpregs[ra], &atmp.ints.i1, + &status)) + error = 1; + } + if (error) + return(MAJOR_06_EXCP); + else { + /* copy results */ + fpregs[tm] = mtmp.ints.i1; + fpregs[ta] = atmp.ints.i1; + fpregs[0] = status; + return(NOEXCEPTION); + } + } +} + +/* + * routine to decode the 26 (FMPYSUB) instruction + */ +static u_int +decode_26(ir,fpregs) +u_int ir; +u_int fpregs[]; +{ + u_int rm1, rm2, tm, ra, ta; /* operands */ + u_int fmt; + u_int error = 0; + u_int status; + union { + double dbl; + float flt; + struct { u_int i1; u_int i2; } ints; + } mtmp, atmp; + + + status = fpregs[0]; + fmt = extru(ir, fpmultifmt, 1); /* get sgl/dbl flag */ + if (fmt == 0) { /* DBL */ + rm1 = extru(ir, fprm1pos, 5) * sizeof(double)/sizeof(u_int); + if (rm1 == 0) + rm1 = fpzeroreg; + rm2 = extru(ir, fprm2pos, 5) * sizeof(double)/sizeof(u_int); + if (rm2 == 0) + rm2 = fpzeroreg; + tm = extru(ir, fptmpos, 5) * sizeof(double)/sizeof(u_int); + if (tm == 0) + return(MAJOR_26_EXCP); + ra = extru(ir, fprapos, 5) * sizeof(double)/sizeof(u_int); + if (ra == 0) + return(MAJOR_26_EXCP); + ta = extru(ir, fptapos, 5) * sizeof(double)/sizeof(u_int); + if (ta == 0) + return(MAJOR_26_EXCP); + + if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1,&status)) + error = 1; + if (dbl_fsub(&fpregs[ta], &fpregs[ra], &atmp.ints.i1,&status)) + error = 1; + if (error) + return(MAJOR_26_EXCP); + else { + /* copy results */ + fpregs[tm] = mtmp.ints.i1; + fpregs[tm+1] = mtmp.ints.i2; + fpregs[ta] = atmp.ints.i1; + fpregs[ta+1] = atmp.ints.i2; + fpregs[0] = status; + return(NOEXCEPTION); + } + } + else { /* SGL */ + /* + * calculate offsets for single precision numbers + * See table 6-14 in PA-89 architecture for mapping + */ + rm1 = (extru(ir,fprm1pos,4) | 0x10 ) << 1; /* get offset */ + rm1 |= extru(ir,fprm1pos-4,1); /* add right word offset */ + + rm2 = (extru(ir,fprm2pos,4) | 0x10 ) << 1; /* get offset */ + rm2 |= extru(ir,fprm2pos-4,1); /* add right word offset */ + + tm = (extru(ir,fptmpos,4) | 0x10 ) << 1; /* get offset */ + tm |= extru(ir,fptmpos-4,1); /* add right word offset */ + + ra = (extru(ir,fprapos,4) | 0x10 ) << 1; /* get offset */ + ra |= extru(ir,fprapos-4,1); /* add right word offset */ + + ta = (extru(ir,fptapos,4) | 0x10 ) << 1; /* get offset */ + ta |= extru(ir,fptapos-4,1); /* add right word offset */ + + if (sgl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1,&status)) + error = 1; + if (sgl_fsub(&fpregs[ta], &fpregs[ra], &atmp.ints.i1,&status)) + error = 1; + if (error) + return(MAJOR_26_EXCP); + else { + /* copy results */ + fpregs[tm] = mtmp.ints.i1; + fpregs[ta] = atmp.ints.i1; + fpregs[0] = status; + return(NOEXCEPTION); + } + } + +} + +/* + * routine to decode the 2E (FMPYFADD,FMPYNFADD) instructions + */ +static u_int +decode_2e(ir,fpregs) +u_int ir; +u_int fpregs[]; +{ + u_int rm1, rm2, ra, t; /* operands */ + u_int fmt; + + fmt = extru(ir,fpfmtpos,1); /* get fmt completer */ + if (fmt == DBL) { /* DBL */ + rm1 = extru(ir,fprm1pos,5) * sizeof(double)/sizeof(u_int); + if (rm1 == 0) + rm1 = fpzeroreg; + rm2 = extru(ir,fprm2pos,5) * sizeof(double)/sizeof(u_int); + if (rm2 == 0) + rm2 = fpzeroreg; + ra = ((extru(ir,fpraupos,3)<<2)|(extru(ir,fpralpos,3)>>1)) * + sizeof(double)/sizeof(u_int); + if (ra == 0) + ra = fpzeroreg; + t = extru(ir,fptpos,5) * sizeof(double)/sizeof(u_int); + if (t == 0) + return(MAJOR_2E_EXCP); + + if (extru(ir,fpfusedsubop,1)) { /* fmpyfadd or fmpynfadd? */ + return(dbl_fmpynfadd(&fpregs[rm1], &fpregs[rm2], + &fpregs[ra], &fpregs[0], &fpregs[t])); + } else { + return(dbl_fmpyfadd(&fpregs[rm1], &fpregs[rm2], + &fpregs[ra], &fpregs[0], &fpregs[t])); + } + } /* end DBL */ + else { /* SGL */ + rm1 = (extru(ir,fprm1pos,5)<<1)|(extru(ir,fpxrm1pos,1)); + if (rm1 == 0) + rm1 = fpzeroreg; + rm2 = (extru(ir,fprm2pos,5)<<1)|(extru(ir,fpxrm2pos,1)); + if (rm2 == 0) + rm2 = fpzeroreg; + ra = (extru(ir,fpraupos,3)<<3)|extru(ir,fpralpos,3); + if (ra == 0) + ra = fpzeroreg; + t = ((extru(ir,fptpos,5)<<1)|(extru(ir,fpxtpos,1))); + if (t == 0) + return(MAJOR_2E_EXCP); + + if (extru(ir,fpfusedsubop,1)) { /* fmpyfadd or fmpynfadd? */ + return(sgl_fmpynfadd(&fpregs[rm1], &fpregs[rm2], + &fpregs[ra], &fpregs[0], &fpregs[t])); + } else { + return(sgl_fmpyfadd(&fpregs[rm1], &fpregs[rm2], + &fpregs[ra], &fpregs[0], &fpregs[t])); + } + } /* end SGL */ +} + +/* + * update_status_cbit + * + * This routine returns the correct FP status register value in + * *status, based on the C-bit & V-bit returned by the FCMP + * emulation routine in new_status. The architecture type + * (PA83, PA89 or PA2.0) is available in fpu_type. The y_field + * and the architecture type are used to determine what flavor + * of FCMP is being emulated. + */ +static void +update_status_cbit(status, new_status, fpu_type, y_field) +u_int *status, new_status; +u_int fpu_type; +u_int y_field; +{ + /* + * For PA89 FPU's which implement the Compare Queue and + * for PA2.0 FPU's, update the Compare Queue if the y-field = 0, + * otherwise update the specified bit in the Compare Array. + * Note that the y-field will always be 0 for non-PA2.0 FPU's. + */ + if ((fpu_type & TIMEX_EXTEN_FLAG) || + (fpu_type & ROLEX_EXTEN_FLAG) || + (fpu_type & PA2_0_FPU_FLAG)) { + if (y_field == 0) { + *status = ((*status & 0x04000000) >> 5) | /* old Cbit */ + ((*status & 0x003ff000) >> 1) | /* old CQ */ + (new_status & 0xffc007ff); /* all other bits*/ + } else { + *status = (*status & 0x04000000) | /* old Cbit */ + ((new_status & 0x04000000) >> (y_field+4)) | + (new_status & ~0x04000000 & /* other bits */ + ~(0x04000000 >> (y_field+4))); + } + } + /* if PA83, just update the C-bit */ + else { + *status = new_status; + } +} diff --git a/arch/parisc/math-emu/frnd.c b/arch/parisc/math-emu/frnd.c new file mode 100644 index 000000000..825d89650 --- /dev/null +++ b/arch/parisc/math-emu/frnd.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * Purpose: + * Single Floating-point Round to Integer + * Double Floating-point Round to Integer + * Quad Floating-point Round to Integer (returns unimplemented) + * + * External Interfaces: + * dbl_frnd(srcptr,_nullptr,dstptr,status) + * sgl_frnd(srcptr,_nullptr,dstptr,status) + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" +#include "dbl_float.h" +#include "cnv_float.h" + +/* + * Single Floating-point Round to Integer + */ + +/*ARGSUSED*/ +int +sgl_frnd(sgl_floating_point *srcptr, + unsigned int *_nullptr, + sgl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int src, result; + register int src_exponent; + register boolean inexact = FALSE; + + src = *srcptr; + /* + * check source operand for NaN or infinity + */ + if ((src_exponent = Sgl_exponent(src)) == SGL_INFINITY_EXPONENT) { + /* + * is signaling NaN? + */ + if (Sgl_isone_signaling(src)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(src); + } + /* + * return quiet NaN or infinity + */ + *dstptr = src; + return(NOEXCEPTION); + } + /* + * Need to round? + */ + if ((src_exponent -= SGL_BIAS) >= SGL_P - 1) { + *dstptr = src; + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + Sgl_clear_exponent_set_hidden(src); + result = src; + Sgl_rightshift(result,(SGL_P-1) - (src_exponent)); + /* check for inexact */ + if (Sgl_isinexact_to_fix(src,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) Sgl_increment(result); + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) Sgl_increment(result); + break; + case ROUNDNEAREST: + if (Sgl_isone_roundbit(src,src_exponent)) + if (Sgl_isone_stickybit(src,src_exponent) + || (Sgl_isone_lowmantissa(result))) + Sgl_increment(result); + } + } + Sgl_leftshift(result,(SGL_P-1) - (src_exponent)); + if (Sgl_isone_hiddenoverflow(result)) + Sgl_set_exponent(result,src_exponent + (SGL_BIAS+1)); + else Sgl_set_exponent(result,src_exponent + SGL_BIAS); + } + else { + result = src; /* set sign */ + Sgl_setzero_exponentmantissa(result); + /* check for inexact */ + if (Sgl_isnotzero_exponentmantissa(src)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(src)) + Sgl_set_exponent(result,SGL_BIAS); + break; + case ROUNDMINUS: + if (Sgl_isone_sign(src)) + Sgl_set_exponent(result,SGL_BIAS); + break; + case ROUNDNEAREST: + if (src_exponent == -1) + if (Sgl_isnotzero_mantissa(src)) + Sgl_set_exponent(result,SGL_BIAS); + } + } + } + *dstptr = result; + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} + +/* + * Double Floating-point Round to Integer + */ + +/*ARGSUSED*/ +int +dbl_frnd( + dbl_floating_point *srcptr, + unsigned int *_nullptr, + dbl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int srcp1, srcp2, resultp1, resultp2; + register int src_exponent; + register boolean inexact = FALSE; + + Dbl_copyfromptr(srcptr,srcp1,srcp2); + /* + * check source operand for NaN or infinity + */ + if ((src_exponent = Dbl_exponent(srcp1)) == DBL_INFINITY_EXPONENT) { + /* + * is signaling NaN? + */ + if (Dbl_isone_signaling(srcp1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Dbl_set_quiet(srcp1); + } + /* + * return quiet NaN or infinity + */ + Dbl_copytoptr(srcp1,srcp2,dstptr); + return(NOEXCEPTION); + } + /* + * Need to round? + */ + if ((src_exponent -= DBL_BIAS) >= DBL_P - 1) { + Dbl_copytoptr(srcp1,srcp2,dstptr); + return(NOEXCEPTION); + } + /* + * Generate result + */ + if (src_exponent >= 0) { + Dbl_clear_exponent_set_hidden(srcp1); + resultp1 = srcp1; + resultp2 = srcp2; + Dbl_rightshift(resultp1,resultp2,(DBL_P-1) - (src_exponent)); + /* check for inexact */ + if (Dbl_isinexact_to_fix(srcp1,srcp2,src_exponent)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) + Dbl_increment(resultp1,resultp2); + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) + Dbl_increment(resultp1,resultp2); + break; + case ROUNDNEAREST: + if (Dbl_isone_roundbit(srcp1,srcp2,src_exponent)) + if (Dbl_isone_stickybit(srcp1,srcp2,src_exponent) + || (Dbl_isone_lowmantissap2(resultp2))) + Dbl_increment(resultp1,resultp2); + } + } + Dbl_leftshift(resultp1,resultp2,(DBL_P-1) - (src_exponent)); + if (Dbl_isone_hiddenoverflow(resultp1)) + Dbl_set_exponent(resultp1,src_exponent + (DBL_BIAS+1)); + else Dbl_set_exponent(resultp1,src_exponent + DBL_BIAS); + } + else { + resultp1 = srcp1; /* set sign */ + Dbl_setzero_exponentmantissa(resultp1,resultp2); + /* check for inexact */ + if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) { + inexact = TRUE; + /* round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Dbl_iszero_sign(srcp1)) + Dbl_set_exponent(resultp1,DBL_BIAS); + break; + case ROUNDMINUS: + if (Dbl_isone_sign(srcp1)) + Dbl_set_exponent(resultp1,DBL_BIAS); + break; + case ROUNDNEAREST: + if (src_exponent == -1) + if (Dbl_isnotzero_mantissa(srcp1,srcp2)) + Dbl_set_exponent(resultp1,DBL_BIAS); + } + } + } + Dbl_copytoptr(resultp1,resultp2,dstptr); + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/hppa.h b/arch/parisc/math-emu/hppa.h new file mode 100644 index 000000000..b25b266c9 --- /dev/null +++ b/arch/parisc/math-emu/hppa.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ + +#ifdef __NO_PA_HDRS + PA header file -- do not include this header file for non-PA builds. +#endif + + +/* amount is assumed to be a constant between 0 and 32 (non-inclusive) */ +#define Shiftdouble(left,right,amount,dest) \ + /* int left, right, amount, dest; */ \ + dest = ((left) << (32-(amount))) | ((unsigned int)(right) >> (amount)) + +/* amount must be less than 32 */ +#define Variableshiftdouble(left,right,amount,dest) \ + /* unsigned int left, right; int amount, dest; */ \ + if (amount == 0) dest = right; \ + else dest = ((((unsigned) left)&0x7fffffff) << (32-(amount))) | \ + ((unsigned) right >> (amount)) + +/* amount must be between 0 and 32 (non-inclusive) */ +#define Variable_shift_double(left,right,amount,dest) \ + /* unsigned int left, right; int amount, dest; */ \ + dest = (left << (32-(amount))) | ((unsigned) right >> (amount)) diff --git a/arch/parisc/math-emu/math-emu.h b/arch/parisc/math-emu/math-emu.h new file mode 100644 index 000000000..a7b6fac16 --- /dev/null +++ b/arch/parisc/math-emu/math-emu.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +#ifndef _PARISC_MATH_EMU_H +#define _PARISC_MATH_EMU_H + +#include <asm/ptrace.h> +extern int handle_fpe(struct pt_regs *regs); + +#endif diff --git a/arch/parisc/math-emu/sfadd.c b/arch/parisc/math-emu/sfadd.c new file mode 100644 index 000000000..9b98c874d --- /dev/null +++ b/arch/parisc/math-emu/sfadd.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/sfadd.c $Revision: 1.1 $ + * + * Purpose: + * Single_add: add two single precision values. + * + * External Interfaces: + * sgl_fadd(leftptr, rightptr, dstptr, status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" + +/* + * Single_add: add two single precision values. + */ +int +sgl_fadd( + sgl_floating_point *leftptr, + sgl_floating_point *rightptr, + sgl_floating_point *dstptr, + unsigned int *status) + { + register unsigned int left, right, result, extent; + register unsigned int signless_upper_left, signless_upper_right, save; + + + register int result_exponent, right_exponent, diff_exponent; + register int sign_save, jumpsize; + register boolean inexact = FALSE; + register boolean underflowtrap; + + /* Create local copies of the numbers */ + left = *leftptr; + right = *rightptr; + + /* A zero "save" helps discover equal operands (for later), * + * and is used in swapping operands (if needed). */ + Sgl_xortointp1(left,right,/*to*/save); + + /* + * check first operand for NaN's or infinity + */ + if ((result_exponent = Sgl_exponent(left)) == SGL_INFINITY_EXPONENT) + { + if (Sgl_iszero_mantissa(left)) + { + if (Sgl_isnotnan(right)) + { + if (Sgl_isinfinity(right) && save!=0) + { + /* + * invalid since operands are opposite signed infinity's + */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * return infinity + */ + *dstptr = left; + return(NOEXCEPTION); + } + } + else + { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(left)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(left); + } + /* + * is second operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(right)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(right); + *dstptr = right; + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + *dstptr = left; + return(NOEXCEPTION); + } + } /* End left NaN or Infinity processing */ + /* + * check second operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(right)) + { + if (Sgl_iszero_mantissa(right)) + { + /* return infinity */ + *dstptr = right; + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(right)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(right); + } + /* + * return quiet NaN + */ + *dstptr = right; + return(NOEXCEPTION); + } /* End right NaN or Infinity processing */ + + /* Invariant: Must be dealing with finite numbers */ + + /* Compare operands by removing the sign */ + Sgl_copytoint_exponentmantissa(left,signless_upper_left); + Sgl_copytoint_exponentmantissa(right,signless_upper_right); + + /* sign difference selects add or sub operation. */ + if(Sgl_ismagnitudeless(signless_upper_left,signless_upper_right)) + { + /* Set the left operand to the larger one by XOR swap * + * First finish the first word using "save" */ + Sgl_xorfromintp1(save,right,/*to*/right); + Sgl_xorfromintp1(save,left,/*to*/left); + result_exponent = Sgl_exponent(left); + } + /* Invariant: left is not smaller than right. */ + + if((right_exponent = Sgl_exponent(right)) == 0) + { + /* Denormalized operands. First look for zeroes */ + if(Sgl_iszero_mantissa(right)) + { + /* right is zero */ + if(Sgl_iszero_exponentmantissa(left)) + { + /* Both operands are zeros */ + if(Is_rounding_mode(ROUNDMINUS)) + { + Sgl_or_signs(left,/*with*/right); + } + else + { + Sgl_and_signs(left,/*with*/right); + } + } + else + { + /* Left is not a zero and must be the result. Trapped + * underflows are signaled if left is denormalized. Result + * is always exact. */ + if( (result_exponent == 0) && Is_underflowtrap_enabled() ) + { + /* need to normalize results mantissa */ + sign_save = Sgl_signextendedsign(left); + Sgl_leftshiftby1(left); + Sgl_normalize(left,result_exponent); + Sgl_set_sign(left,/*using*/sign_save); + Sgl_setwrapped_exponent(left,result_exponent,unfl); + *dstptr = left; + return(UNDERFLOWEXCEPTION); + } + } + *dstptr = left; + return(NOEXCEPTION); + } + + /* Neither are zeroes */ + Sgl_clear_sign(right); /* Exponent is already cleared */ + if(result_exponent == 0 ) + { + /* Both operands are denormalized. The result must be exact + * and is simply calculated. A sum could become normalized and a + * difference could cancel to a true zero. */ + if( (/*signed*/int) save < 0 ) + { + Sgl_subtract(left,/*minus*/right,/*into*/result); + if(Sgl_iszero_mantissa(result)) + { + if(Is_rounding_mode(ROUNDMINUS)) + { + Sgl_setone_sign(result); + } + else + { + Sgl_setzero_sign(result); + } + *dstptr = result; + return(NOEXCEPTION); + } + } + else + { + Sgl_addition(left,right,/*into*/result); + if(Sgl_isone_hidden(result)) + { + *dstptr = result; + return(NOEXCEPTION); + } + } + if(Is_underflowtrap_enabled()) + { + /* need to normalize result */ + sign_save = Sgl_signextendedsign(result); + Sgl_leftshiftby1(result); + Sgl_normalize(result,result_exponent); + Sgl_set_sign(result,/*using*/sign_save); + Sgl_setwrapped_exponent(result,result_exponent,unfl); + *dstptr = result; + return(UNDERFLOWEXCEPTION); + } + *dstptr = result; + return(NOEXCEPTION); + } + right_exponent = 1; /* Set exponent to reflect different bias + * with denormalized numbers. */ + } + else + { + Sgl_clear_signexponent_set_hidden(right); + } + Sgl_clear_exponent_set_hidden(left); + diff_exponent = result_exponent - right_exponent; + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for this + * infrequent case. + */ + if(diff_exponent > SGL_THRESHOLD) + { + diff_exponent = SGL_THRESHOLD; + } + + /* Align right operand by shifting to right */ + Sgl_right_align(/*operand*/right,/*shifted by*/diff_exponent, + /*and lower to*/extent); + + /* Treat sum and difference of the operands separately. */ + if( (/*signed*/int) save < 0 ) + { + /* + * Difference of the two operands. Their can be no overflow. A + * borrow can occur out of the hidden bit and force a post + * normalization phase. + */ + Sgl_subtract_withextension(left,/*minus*/right,/*with*/extent,/*into*/result); + if(Sgl_iszero_hidden(result)) + { + /* Handle normalization */ + /* A straightforward algorithm would now shift the result + * and extension left until the hidden bit becomes one. Not + * all of the extension bits need participate in the shift. + * Only the two most significant bits (round and guard) are + * needed. If only a single shift is needed then the guard + * bit becomes a significant low order bit and the extension + * must participate in the rounding. If more than a single + * shift is needed, then all bits to the right of the guard + * bit are zeros, and the guard bit may or may not be zero. */ + sign_save = Sgl_signextendedsign(result); + Sgl_leftshiftby1_withextent(result,extent,result); + + /* Need to check for a zero result. The sign and exponent + * fields have already been zeroed. The more efficient test + * of the full object can be used. + */ + if(Sgl_iszero(result)) + /* Must have been "x-x" or "x+(-x)". */ + { + if(Is_rounding_mode(ROUNDMINUS)) Sgl_setone_sign(result); + *dstptr = result; + return(NOEXCEPTION); + } + result_exponent--; + /* Look to see if normalization is finished. */ + if(Sgl_isone_hidden(result)) + { + if(result_exponent==0) + { + /* Denormalized, exponent should be zero. Left operand * + * was normalized, so extent (guard, round) was zero */ + goto underflow; + } + else + { + /* No further normalization is needed. */ + Sgl_set_sign(result,/*using*/sign_save); + Ext_leftshiftby1(extent); + goto round; + } + } + + /* Check for denormalized, exponent should be zero. Left * + * operand was normalized, so extent (guard, round) was zero */ + if(!(underflowtrap = Is_underflowtrap_enabled()) && + result_exponent==0) goto underflow; + + /* Shift extension to complete one bit of normalization and + * update exponent. */ + Ext_leftshiftby1(extent); + + /* Discover first one bit to determine shift amount. Use a + * modified binary search. We have already shifted the result + * one position right and still not found a one so the remainder + * of the extension must be zero and simplifies rounding. */ + /* Scan bytes */ + while(Sgl_iszero_hiddenhigh7mantissa(result)) + { + Sgl_leftshiftby8(result); + if((result_exponent -= 8) <= 0 && !underflowtrap) + goto underflow; + } + /* Now narrow it down to the nibble */ + if(Sgl_iszero_hiddenhigh3mantissa(result)) + { + /* The lower nibble contains the normalizing one */ + Sgl_leftshiftby4(result); + if((result_exponent -= 4) <= 0 && !underflowtrap) + goto underflow; + } + /* Select case were first bit is set (already normalized) + * otherwise select the proper shift. */ + if((jumpsize = Sgl_hiddenhigh3mantissa(result)) > 7) + { + /* Already normalized */ + if(result_exponent <= 0) goto underflow; + Sgl_set_sign(result,/*using*/sign_save); + Sgl_set_exponent(result,/*using*/result_exponent); + *dstptr = result; + return(NOEXCEPTION); + } + Sgl_sethigh4bits(result,/*using*/sign_save); + switch(jumpsize) + { + case 1: + { + Sgl_leftshiftby3(result); + result_exponent -= 3; + break; + } + case 2: + case 3: + { + Sgl_leftshiftby2(result); + result_exponent -= 2; + break; + } + case 4: + case 5: + case 6: + case 7: + { + Sgl_leftshiftby1(result); + result_exponent -= 1; + break; + } + } + if(result_exponent > 0) + { + Sgl_set_exponent(result,/*using*/result_exponent); + *dstptr = result; + return(NOEXCEPTION); /* Sign bit is already set */ + } + /* Fixup potential underflows */ + underflow: + if(Is_underflowtrap_enabled()) + { + Sgl_set_sign(result,sign_save); + Sgl_setwrapped_exponent(result,result_exponent,unfl); + *dstptr = result; + /* inexact = FALSE; */ + return(UNDERFLOWEXCEPTION); + } + /* + * Since we cannot get an inexact denormalized result, + * we can now return. + */ + Sgl_right_align(result,/*by*/(1-result_exponent),extent); + Sgl_clear_signexponent(result); + Sgl_set_sign(result,sign_save); + *dstptr = result; + return(NOEXCEPTION); + } /* end if(hidden...)... */ + /* Fall through and round */ + } /* end if(save < 0)... */ + else + { + /* Add magnitudes */ + Sgl_addition(left,right,/*to*/result); + if(Sgl_isone_hiddenoverflow(result)) + { + /* Prenormalization required. */ + Sgl_rightshiftby1_withextent(result,extent,extent); + Sgl_arithrightshiftby1(result); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...add magnitudes... */ + + /* Round the result. If the extension is all zeros,then the result is + * exact. Otherwise round in the correct direction. No underflow is + * possible. If a postnormalization is necessary, then the mantissa is + * all zeros so no shift is needed. */ + round: + if(Ext_isnotzero(extent)) + { + inexact = TRUE; + switch(Rounding_mode()) + { + case ROUNDNEAREST: /* The default. */ + if(Ext_isone_sign(extent)) + { + /* at least 1/2 ulp */ + if(Ext_isnotzero_lower(extent) || + Sgl_isone_lowmantissa(result)) + { + /* either exactly half way and odd or more than 1/2ulp */ + Sgl_increment(result); + } + } + break; + + case ROUNDPLUS: + if(Sgl_iszero_sign(result)) + { + /* Round up positive results */ + Sgl_increment(result); + } + break; + + case ROUNDMINUS: + if(Sgl_isone_sign(result)) + { + /* Round down negative results */ + Sgl_increment(result); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if(Sgl_isone_hiddenoverflow(result)) result_exponent++; + } + if(result_exponent == SGL_INFINITY_EXPONENT) + { + /* Overflow */ + if(Is_overflowtrap_enabled()) + { + Sgl_setwrapped_exponent(result,result_exponent,ovfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + else + { + Set_overflowflag(); + inexact = TRUE; + Sgl_setoverflow(result); + } + } + else Sgl_set_exponent(result,result_exponent); + *dstptr = result; + if(inexact) + if(Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); + } diff --git a/arch/parisc/math-emu/sfcmp.c b/arch/parisc/math-emu/sfcmp.c new file mode 100644 index 000000000..4a708f6c6 --- /dev/null +++ b/arch/parisc/math-emu/sfcmp.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/sfcmp.c $Revision: 1.1 $ + * + * Purpose: + * sgl_cmp: compare two values + * + * External Interfaces: + * sgl_fcmp(leftptr, rightptr, cond, status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" + +/* + * sgl_cmp: compare two values + */ +int +sgl_fcmp (sgl_floating_point * leftptr, sgl_floating_point * rightptr, + unsigned int cond, unsigned int *status) + + /* The predicate to be tested */ + + { + register unsigned int left, right; + register int xorresult; + + /* Create local copies of the numbers */ + left = *leftptr; + right = *rightptr; + + /* + * Test for NaN + */ + if( (Sgl_exponent(left) == SGL_INFINITY_EXPONENT) + || (Sgl_exponent(right) == SGL_INFINITY_EXPONENT) ) + { + /* Check if a NaN is involved. Signal an invalid exception when + * comparing a signaling NaN or when comparing quiet NaNs and the + * low bit of the condition is set */ + if( ( (Sgl_exponent(left) == SGL_INFINITY_EXPONENT) + && Sgl_isnotzero_mantissa(left) + && (Exception(cond) || Sgl_isone_signaling(left))) + || + ( (Sgl_exponent(right) == SGL_INFINITY_EXPONENT) + && Sgl_isnotzero_mantissa(right) + && (Exception(cond) || Sgl_isone_signaling(right)) ) ) + { + if( Is_invalidtrap_enabled() ) { + Set_status_cbit(Unordered(cond)); + return(INVALIDEXCEPTION); + } + else Set_invalidflag(); + Set_status_cbit(Unordered(cond)); + return(NOEXCEPTION); + } + /* All the exceptional conditions are handled, now special case + NaN compares */ + else if( ((Sgl_exponent(left) == SGL_INFINITY_EXPONENT) + && Sgl_isnotzero_mantissa(left)) + || + ((Sgl_exponent(right) == SGL_INFINITY_EXPONENT) + && Sgl_isnotzero_mantissa(right)) ) + { + /* NaNs always compare unordered. */ + Set_status_cbit(Unordered(cond)); + return(NOEXCEPTION); + } + /* infinities will drop down to the normal compare mechanisms */ + } + /* First compare for unequal signs => less or greater or + * special equal case */ + Sgl_xortointp1(left,right,xorresult); + if( xorresult < 0 ) + { + /* left negative => less, left positive => greater. + * equal is possible if both operands are zeros. */ + if( Sgl_iszero_exponentmantissa(left) + && Sgl_iszero_exponentmantissa(right) ) + { + Set_status_cbit(Equal(cond)); + } + else if( Sgl_isone_sign(left) ) + { + Set_status_cbit(Lessthan(cond)); + } + else + { + Set_status_cbit(Greaterthan(cond)); + } + } + /* Signs are the same. Treat negative numbers separately + * from the positives because of the reversed sense. */ + else if( Sgl_all(left) == Sgl_all(right) ) + { + Set_status_cbit(Equal(cond)); + } + else if( Sgl_iszero_sign(left) ) + { + /* Positive compare */ + if( Sgl_all(left) < Sgl_all(right) ) + { + Set_status_cbit(Lessthan(cond)); + } + else + { + Set_status_cbit(Greaterthan(cond)); + } + } + else + { + /* Negative compare. Signed or unsigned compares + * both work the same. That distinction is only + * important when the sign bits differ. */ + if( Sgl_all(left) > Sgl_all(right) ) + { + Set_status_cbit(Lessthan(cond)); + } + else + { + Set_status_cbit(Greaterthan(cond)); + } + } + return(NOEXCEPTION); + } diff --git a/arch/parisc/math-emu/sfdiv.c b/arch/parisc/math-emu/sfdiv.c new file mode 100644 index 000000000..f1b439365 --- /dev/null +++ b/arch/parisc/math-emu/sfdiv.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/sfdiv.c $Revision: 1.1 $ + * + * Purpose: + * Single Precision Floating-point Divide + * + * External Interfaces: + * sgl_fdiv(srcptr1,srcptr2,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" + +/* + * Single Precision Floating-point Divide + */ + +int +sgl_fdiv (sgl_floating_point * srcptr1, sgl_floating_point * srcptr2, + sgl_floating_point * dstptr, unsigned int *status) +{ + register unsigned int opnd1, opnd2, opnd3, result; + register int dest_exponent, count; + register boolean inexact = FALSE, guardbit = FALSE, stickybit = FALSE; + boolean is_tiny; + + opnd1 = *srcptr1; + opnd2 = *srcptr2; + /* + * set sign bit of result + */ + if (Sgl_sign(opnd1) ^ Sgl_sign(opnd2)) Sgl_setnegativezero(result); + else Sgl_setzero(result); + /* + * check first operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd1)) { + if (Sgl_iszero_mantissa(opnd1)) { + if (Sgl_isnotnan(opnd2)) { + if (Sgl_isinfinity(opnd2)) { + /* + * invalid since both operands + * are infinity + */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * return infinity + */ + Sgl_setinfinity_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd1); + } + /* + * is second operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + *dstptr = opnd2; + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + *dstptr = opnd1; + return(NOEXCEPTION); + } + } + /* + * check second operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd2)) { + if (Sgl_iszero_mantissa(opnd2)) { + /* + * return zero + */ + Sgl_setzero_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + } + /* + * return quiet NaN + */ + *dstptr = opnd2; + return(NOEXCEPTION); + } + /* + * check for division by zero + */ + if (Sgl_iszero_exponentmantissa(opnd2)) { + if (Sgl_iszero_exponentmantissa(opnd1)) { + /* invalid since both operands are zero */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(result); + *dstptr = result; + return(NOEXCEPTION); + } + if (Is_divisionbyzerotrap_enabled()) + return(DIVISIONBYZEROEXCEPTION); + Set_divisionbyzeroflag(); + Sgl_setinfinity_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * Generate exponent + */ + dest_exponent = Sgl_exponent(opnd1) - Sgl_exponent(opnd2) + SGL_BIAS; + + /* + * Generate mantissa + */ + if (Sgl_isnotzero_exponent(opnd1)) { + /* set hidden bit */ + Sgl_clear_signexponent_set_hidden(opnd1); + } + else { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd1)) { + Sgl_setzero_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* is denormalized; want to normalize */ + Sgl_clear_signexponent(opnd1); + Sgl_leftshiftby1(opnd1); + Sgl_normalize(opnd1,dest_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Sgl_isnotzero_exponent(opnd2)) { + Sgl_clear_signexponent_set_hidden(opnd2); + } + else { + /* is denormalized; want to normalize */ + Sgl_clear_signexponent(opnd2); + Sgl_leftshiftby1(opnd2); + while(Sgl_iszero_hiddenhigh7mantissa(opnd2)) { + Sgl_leftshiftby8(opnd2); + dest_exponent += 8; + } + if(Sgl_iszero_hiddenhigh3mantissa(opnd2)) { + Sgl_leftshiftby4(opnd2); + dest_exponent += 4; + } + while(Sgl_iszero_hidden(opnd2)) { + Sgl_leftshiftby1(opnd2); + dest_exponent += 1; + } + } + + /* Divide the source mantissas */ + + /* + * A non_restoring divide algorithm is used. + */ + Sgl_subtract(opnd1,opnd2,opnd1); + Sgl_setzero(opnd3); + for (count=1;count<=SGL_P && Sgl_all(opnd1);count++) { + Sgl_leftshiftby1(opnd1); + Sgl_leftshiftby1(opnd3); + if (Sgl_iszero_sign(opnd1)) { + Sgl_setone_lowmantissa(opnd3); + Sgl_subtract(opnd1,opnd2,opnd1); + } + else Sgl_addition(opnd1,opnd2,opnd1); + } + if (count <= SGL_P) { + Sgl_leftshiftby1(opnd3); + Sgl_setone_lowmantissa(opnd3); + Sgl_leftshift(opnd3,SGL_P-count); + if (Sgl_iszero_hidden(opnd3)) { + Sgl_leftshiftby1(opnd3); + dest_exponent--; + } + } + else { + if (Sgl_iszero_hidden(opnd3)) { + /* need to get one more bit of result */ + Sgl_leftshiftby1(opnd1); + Sgl_leftshiftby1(opnd3); + if (Sgl_iszero_sign(opnd1)) { + Sgl_setone_lowmantissa(opnd3); + Sgl_subtract(opnd1,opnd2,opnd1); + } + else Sgl_addition(opnd1,opnd2,opnd1); + dest_exponent--; + } + if (Sgl_iszero_sign(opnd1)) guardbit = TRUE; + stickybit = Sgl_all(opnd1); + } + inexact = guardbit | stickybit; + + /* + * round result + */ + if (inexact && (dest_exponent > 0 || Is_underflowtrap_enabled())) { + Sgl_clear_signexponent(opnd3); + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) + Sgl_increment_mantissa(opnd3); + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) + Sgl_increment_mantissa(opnd3); + break; + case ROUNDNEAREST: + if (guardbit) { + if (stickybit || Sgl_isone_lowmantissa(opnd3)) + Sgl_increment_mantissa(opnd3); + } + } + if (Sgl_isone_hidden(opnd3)) dest_exponent++; + } + Sgl_set_mantissa(result,opnd3); + + /* + * Test for overflow + */ + if (dest_exponent >= SGL_INFINITY_EXPONENT) { + /* trap if OVERFLOWTRAP enabled */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(result,dest_exponent,ovfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + Set_overflowflag(); + /* set result to infinity or largest number */ + Sgl_setoverflow(result); + inexact = TRUE; + } + /* + * Test for underflow + */ + else if (dest_exponent <= 0) { + /* trap if UNDERFLOWTRAP enabled */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(result,dest_exponent,unfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(UNDERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(UNDERFLOWEXCEPTION); + } + + /* Determine if should set underflow flag */ + is_tiny = TRUE; + if (dest_exponent == 0 && inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) { + Sgl_increment(opnd3); + if (Sgl_isone_hiddenoverflow(opnd3)) + is_tiny = FALSE; + Sgl_decrement(opnd3); + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) { + Sgl_increment(opnd3); + if (Sgl_isone_hiddenoverflow(opnd3)) + is_tiny = FALSE; + Sgl_decrement(opnd3); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Sgl_isone_lowmantissa(opnd3))) { + Sgl_increment(opnd3); + if (Sgl_isone_hiddenoverflow(opnd3)) + is_tiny = FALSE; + Sgl_decrement(opnd3); + } + break; + } + } + + /* + * denormalize result or set to signed zero + */ + stickybit = inexact; + Sgl_denormalize(opnd3,dest_exponent,guardbit,stickybit,inexact); + + /* return rounded number */ + if (inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) { + Sgl_increment(opnd3); + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) { + Sgl_increment(opnd3); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Sgl_isone_lowmantissa(opnd3))) { + Sgl_increment(opnd3); + } + break; + } + if (is_tiny) Set_underflowflag(); + } + Sgl_set_exponentmantissa(result,opnd3); + } + else Sgl_set_exponent(result,dest_exponent); + *dstptr = result; + /* check for inexact */ + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/sfmpy.c b/arch/parisc/math-emu/sfmpy.c new file mode 100644 index 000000000..7f4518679 --- /dev/null +++ b/arch/parisc/math-emu/sfmpy.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/sfmpy.c $Revision: 1.1 $ + * + * Purpose: + * Single Precision Floating-point Multiply + * + * External Interfaces: + * sgl_fmpy(srcptr1,srcptr2,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" + +/* + * Single Precision Floating-point Multiply + */ + +int +sgl_fmpy( + sgl_floating_point *srcptr1, + sgl_floating_point *srcptr2, + sgl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int opnd1, opnd2, opnd3, result; + register int dest_exponent, count; + register boolean inexact = FALSE, guardbit = FALSE, stickybit = FALSE; + boolean is_tiny; + + opnd1 = *srcptr1; + opnd2 = *srcptr2; + /* + * set sign bit of result + */ + if (Sgl_sign(opnd1) ^ Sgl_sign(opnd2)) Sgl_setnegativezero(result); + else Sgl_setzero(result); + /* + * check first operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd1)) { + if (Sgl_iszero_mantissa(opnd1)) { + if (Sgl_isnotnan(opnd2)) { + if (Sgl_iszero_exponentmantissa(opnd2)) { + /* + * invalid since operands are infinity + * and zero + */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * return infinity + */ + Sgl_setinfinity_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd1); + } + /* + * is second operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + *dstptr = opnd2; + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + *dstptr = opnd1; + return(NOEXCEPTION); + } + } + /* + * check second operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(opnd2)) { + if (Sgl_iszero_mantissa(opnd2)) { + if (Sgl_iszero_exponentmantissa(opnd1)) { + /* invalid since operands are zero & infinity */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(opnd2); + *dstptr = opnd2; + return(NOEXCEPTION); + } + /* + * return infinity + */ + Sgl_setinfinity_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + } + /* + * return quiet NaN + */ + *dstptr = opnd2; + return(NOEXCEPTION); + } + /* + * Generate exponent + */ + dest_exponent = Sgl_exponent(opnd1) + Sgl_exponent(opnd2) - SGL_BIAS; + + /* + * Generate mantissa + */ + if (Sgl_isnotzero_exponent(opnd1)) { + /* set hidden bit */ + Sgl_clear_signexponent_set_hidden(opnd1); + } + else { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd1)) { + Sgl_setzero_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* is denormalized, adjust exponent */ + Sgl_clear_signexponent(opnd1); + Sgl_leftshiftby1(opnd1); + Sgl_normalize(opnd1,dest_exponent); + } + /* opnd2 needs to have hidden bit set with msb in hidden bit */ + if (Sgl_isnotzero_exponent(opnd2)) { + Sgl_clear_signexponent_set_hidden(opnd2); + } + else { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd2)) { + Sgl_setzero_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* is denormalized; want to normalize */ + Sgl_clear_signexponent(opnd2); + Sgl_leftshiftby1(opnd2); + Sgl_normalize(opnd2,dest_exponent); + } + + /* Multiply two source mantissas together */ + + Sgl_leftshiftby4(opnd2); /* make room for guard bits */ + Sgl_setzero(opnd3); + /* + * Four bits at a time are inspected in each loop, and a + * simple shift and add multiply algorithm is used. + */ + for (count=1;count<SGL_P;count+=4) { + stickybit |= Slow4(opnd3); + Sgl_rightshiftby4(opnd3); + if (Sbit28(opnd1)) Sall(opnd3) += (Sall(opnd2) << 3); + if (Sbit29(opnd1)) Sall(opnd3) += (Sall(opnd2) << 2); + if (Sbit30(opnd1)) Sall(opnd3) += (Sall(opnd2) << 1); + if (Sbit31(opnd1)) Sall(opnd3) += Sall(opnd2); + Sgl_rightshiftby4(opnd1); + } + /* make sure result is left-justified */ + if (Sgl_iszero_sign(opnd3)) { + Sgl_leftshiftby1(opnd3); + } + else { + /* result mantissa >= 2. */ + dest_exponent++; + } + /* check for denormalized result */ + while (Sgl_iszero_sign(opnd3)) { + Sgl_leftshiftby1(opnd3); + dest_exponent--; + } + /* + * check for guard, sticky and inexact bits + */ + stickybit |= Sgl_all(opnd3) << (SGL_BITLENGTH - SGL_EXP_LENGTH + 1); + guardbit = Sbit24(opnd3); + inexact = guardbit | stickybit; + + /* re-align mantissa */ + Sgl_rightshiftby8(opnd3); + + /* + * round result + */ + if (inexact && (dest_exponent>0 || Is_underflowtrap_enabled())) { + Sgl_clear_signexponent(opnd3); + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) + Sgl_increment(opnd3); + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) + Sgl_increment(opnd3); + break; + case ROUNDNEAREST: + if (guardbit) { + if (stickybit || Sgl_isone_lowmantissa(opnd3)) + Sgl_increment(opnd3); + } + } + if (Sgl_isone_hidden(opnd3)) dest_exponent++; + } + Sgl_set_mantissa(result,opnd3); + + /* + * Test for overflow + */ + if (dest_exponent >= SGL_INFINITY_EXPONENT) { + /* trap if OVERFLOWTRAP enabled */ + if (Is_overflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(result,dest_exponent,ovfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + inexact = TRUE; + Set_overflowflag(); + /* set result to infinity or largest number */ + Sgl_setoverflow(result); + } + /* + * Test for underflow + */ + else if (dest_exponent <= 0) { + /* trap if UNDERFLOWTRAP enabled */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(result,dest_exponent,unfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(UNDERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(UNDERFLOWEXCEPTION); + } + + /* Determine if should set underflow flag */ + is_tiny = TRUE; + if (dest_exponent == 0 && inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) { + Sgl_increment(opnd3); + if (Sgl_isone_hiddenoverflow(opnd3)) + is_tiny = FALSE; + Sgl_decrement(opnd3); + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) { + Sgl_increment(opnd3); + if (Sgl_isone_hiddenoverflow(opnd3)) + is_tiny = FALSE; + Sgl_decrement(opnd3); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Sgl_isone_lowmantissa(opnd3))) { + Sgl_increment(opnd3); + if (Sgl_isone_hiddenoverflow(opnd3)) + is_tiny = FALSE; + Sgl_decrement(opnd3); + } + break; + } + } + + /* + * denormalize result or set to signed zero + */ + stickybit = inexact; + Sgl_denormalize(opnd3,dest_exponent,guardbit,stickybit,inexact); + + /* return zero or smallest number */ + if (inexact) { + switch (Rounding_mode()) { + case ROUNDPLUS: + if (Sgl_iszero_sign(result)) { + Sgl_increment(opnd3); + } + break; + case ROUNDMINUS: + if (Sgl_isone_sign(result)) { + Sgl_increment(opnd3); + } + break; + case ROUNDNEAREST: + if (guardbit && (stickybit || + Sgl_isone_lowmantissa(opnd3))) { + Sgl_increment(opnd3); + } + break; + } + if (is_tiny) Set_underflowflag(); + } + Sgl_set_exponentmantissa(result,opnd3); + } + else Sgl_set_exponent(result,dest_exponent); + *dstptr = result; + + /* check for inexact */ + if (inexact) { + if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + } + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/sfrem.c b/arch/parisc/math-emu/sfrem.c new file mode 100644 index 000000000..4ac88d829 --- /dev/null +++ b/arch/parisc/math-emu/sfrem.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/sfrem.c $Revision: 1.1 $ + * + * Purpose: + * Single Precision Floating-point Remainder + * + * External Interfaces: + * sgl_frem(srcptr1,srcptr2,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + + +#include "float.h" +#include "sgl_float.h" + +/* + * Single Precision Floating-point Remainder + */ + +int +sgl_frem (sgl_floating_point * srcptr1, sgl_floating_point * srcptr2, + sgl_floating_point * dstptr, unsigned int *status) +{ + register unsigned int opnd1, opnd2, result; + register int opnd1_exponent, opnd2_exponent, dest_exponent, stepcount; + register boolean roundup = FALSE; + + opnd1 = *srcptr1; + opnd2 = *srcptr2; + /* + * check first operand for NaN's or infinity + */ + if ((opnd1_exponent = Sgl_exponent(opnd1)) == SGL_INFINITY_EXPONENT) { + if (Sgl_iszero_mantissa(opnd1)) { + if (Sgl_isnotnan(opnd2)) { + /* invalid since first operand is infinity */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(result); + *dstptr = result; + return(NOEXCEPTION); + } + } + else { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd1)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd1); + } + /* + * is second operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) + return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + *dstptr = opnd2; + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + *dstptr = opnd1; + return(NOEXCEPTION); + } + } + /* + * check second operand for NaN's or infinity + */ + if ((opnd2_exponent = Sgl_exponent(opnd2)) == SGL_INFINITY_EXPONENT) { + if (Sgl_iszero_mantissa(opnd2)) { + /* + * return first operand + */ + *dstptr = opnd1; + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(opnd2)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(opnd2); + } + /* + * return quiet NaN + */ + *dstptr = opnd2; + return(NOEXCEPTION); + } + /* + * check second operand for zero + */ + if (Sgl_iszero_exponentmantissa(opnd2)) { + /* invalid since second operand is zero */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(result); + *dstptr = result; + return(NOEXCEPTION); + } + + /* + * get sign of result + */ + result = opnd1; + + /* + * check for denormalized operands + */ + if (opnd1_exponent == 0) { + /* check for zero */ + if (Sgl_iszero_mantissa(opnd1)) { + *dstptr = opnd1; + return(NOEXCEPTION); + } + /* normalize, then continue */ + opnd1_exponent = 1; + Sgl_normalize(opnd1,opnd1_exponent); + } + else { + Sgl_clear_signexponent_set_hidden(opnd1); + } + if (opnd2_exponent == 0) { + /* normalize, then continue */ + opnd2_exponent = 1; + Sgl_normalize(opnd2,opnd2_exponent); + } + else { + Sgl_clear_signexponent_set_hidden(opnd2); + } + + /* find result exponent and divide step loop count */ + dest_exponent = opnd2_exponent - 1; + stepcount = opnd1_exponent - opnd2_exponent; + + /* + * check for opnd1/opnd2 < 1 + */ + if (stepcount < 0) { + /* + * check for opnd1/opnd2 > 1/2 + * + * In this case n will round to 1, so + * r = opnd1 - opnd2 + */ + if (stepcount == -1 && Sgl_isgreaterthan(opnd1,opnd2)) { + Sgl_all(result) = ~Sgl_all(result); /* set sign */ + /* align opnd2 with opnd1 */ + Sgl_leftshiftby1(opnd2); + Sgl_subtract(opnd2,opnd1,opnd2); + /* now normalize */ + while (Sgl_iszero_hidden(opnd2)) { + Sgl_leftshiftby1(opnd2); + dest_exponent--; + } + Sgl_set_exponentmantissa(result,opnd2); + goto testforunderflow; + } + /* + * opnd1/opnd2 <= 1/2 + * + * In this case n will round to zero, so + * r = opnd1 + */ + Sgl_set_exponentmantissa(result,opnd1); + dest_exponent = opnd1_exponent; + goto testforunderflow; + } + + /* + * Generate result + * + * Do iterative subtract until remainder is less than operand 2. + */ + while (stepcount-- > 0 && Sgl_all(opnd1)) { + if (Sgl_isnotlessthan(opnd1,opnd2)) + Sgl_subtract(opnd1,opnd2,opnd1); + Sgl_leftshiftby1(opnd1); + } + /* + * Do last subtract, then determine which way to round if remainder + * is exactly 1/2 of opnd2 + */ + if (Sgl_isnotlessthan(opnd1,opnd2)) { + Sgl_subtract(opnd1,opnd2,opnd1); + roundup = TRUE; + } + if (stepcount > 0 || Sgl_iszero(opnd1)) { + /* division is exact, remainder is zero */ + Sgl_setzero_exponentmantissa(result); + *dstptr = result; + return(NOEXCEPTION); + } + + /* + * Check for cases where opnd1/opnd2 < n + * + * In this case the result's sign will be opposite that of + * opnd1. The mantissa also needs some correction. + */ + Sgl_leftshiftby1(opnd1); + if (Sgl_isgreaterthan(opnd1,opnd2)) { + Sgl_invert_sign(result); + Sgl_subtract((opnd2<<1),opnd1,opnd1); + } + /* check for remainder being exactly 1/2 of opnd2 */ + else if (Sgl_isequal(opnd1,opnd2) && roundup) { + Sgl_invert_sign(result); + } + + /* normalize result's mantissa */ + while (Sgl_iszero_hidden(opnd1)) { + dest_exponent--; + Sgl_leftshiftby1(opnd1); + } + Sgl_set_exponentmantissa(result,opnd1); + + /* + * Test for underflow + */ + testforunderflow: + if (dest_exponent <= 0) { + /* trap if UNDERFLOWTRAP enabled */ + if (Is_underflowtrap_enabled()) { + /* + * Adjust bias of result + */ + Sgl_setwrapped_exponent(result,dest_exponent,unfl); + *dstptr = result; + /* frem is always exact */ + return(UNDERFLOWEXCEPTION); + } + /* + * denormalize result or set to signed zero + */ + if (dest_exponent >= (1 - SGL_P)) { + Sgl_rightshift_exponentmantissa(result,1-dest_exponent); + } + else { + Sgl_setzero_exponentmantissa(result); + } + } + else Sgl_set_exponent(result,dest_exponent); + *dstptr = result; + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/sfsqrt.c b/arch/parisc/math-emu/sfsqrt.c new file mode 100644 index 000000000..8e9e023e7 --- /dev/null +++ b/arch/parisc/math-emu/sfsqrt.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/sfsqrt.c $Revision: 1.1 $ + * + * Purpose: + * Single Floating-point Square Root + * + * External Interfaces: + * sgl_fsqrt(srcptr,_nullptr,dstptr,status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" + +/* + * Single Floating-point Square Root + */ + +/*ARGSUSED*/ +unsigned int +sgl_fsqrt( + sgl_floating_point *srcptr, + unsigned int *_nullptr, + sgl_floating_point *dstptr, + unsigned int *status) +{ + register unsigned int src, result; + register int src_exponent; + register unsigned int newbit, sum; + register boolean guardbit = FALSE, even_exponent; + + src = *srcptr; + /* + * check source operand for NaN or infinity + */ + if ((src_exponent = Sgl_exponent(src)) == SGL_INFINITY_EXPONENT) { + /* + * is signaling NaN? + */ + if (Sgl_isone_signaling(src)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(src); + } + /* + * Return quiet NaN or positive infinity. + * Fall through to negative test if negative infinity. + */ + if (Sgl_iszero_sign(src) || Sgl_isnotzero_mantissa(src)) { + *dstptr = src; + return(NOEXCEPTION); + } + } + + /* + * check for zero source operand + */ + if (Sgl_iszero_exponentmantissa(src)) { + *dstptr = src; + return(NOEXCEPTION); + } + + /* + * check for negative source operand + */ + if (Sgl_isone_sign(src)) { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_makequietnan(src); + *dstptr = src; + return(NOEXCEPTION); + } + + /* + * Generate result + */ + if (src_exponent > 0) { + even_exponent = Sgl_hidden(src); + Sgl_clear_signexponent_set_hidden(src); + } + else { + /* normalize operand */ + Sgl_clear_signexponent(src); + src_exponent++; + Sgl_normalize(src,src_exponent); + even_exponent = src_exponent & 1; + } + if (even_exponent) { + /* exponent is even */ + /* Add comment here. Explain why odd exponent needs correction */ + Sgl_leftshiftby1(src); + } + /* + * Add comment here. Explain following algorithm. + * + * Trust me, it works. + * + */ + Sgl_setzero(result); + newbit = 1 << SGL_P; + while (newbit && Sgl_isnotzero(src)) { + Sgl_addition(result,newbit,sum); + if(sum <= Sgl_all(src)) { + /* update result */ + Sgl_addition(result,(newbit<<1),result); + Sgl_subtract(src,sum,src); + } + Sgl_rightshiftby1(newbit); + Sgl_leftshiftby1(src); + } + /* correct exponent for pre-shift */ + if (even_exponent) { + Sgl_rightshiftby1(result); + } + + /* check for inexact */ + if (Sgl_isnotzero(src)) { + if (!even_exponent && Sgl_islessthan(result,src)) + Sgl_increment(result); + guardbit = Sgl_lowmantissa(result); + Sgl_rightshiftby1(result); + + /* now round result */ + switch (Rounding_mode()) { + case ROUNDPLUS: + Sgl_increment(result); + break; + case ROUNDNEAREST: + /* stickybit is always true, so guardbit + * is enough to determine rounding */ + if (guardbit) { + Sgl_increment(result); + } + break; + } + /* increment result exponent by 1 if mantissa overflowed */ + if (Sgl_isone_hiddenoverflow(result)) src_exponent+=2; + + if (Is_inexacttrap_enabled()) { + Sgl_set_exponent(result, + ((src_exponent-SGL_BIAS)>>1)+SGL_BIAS); + *dstptr = result; + return(INEXACTEXCEPTION); + } + else Set_inexactflag(); + } + else { + Sgl_rightshiftby1(result); + } + Sgl_set_exponent(result,((src_exponent-SGL_BIAS)>>1)+SGL_BIAS); + *dstptr = result; + return(NOEXCEPTION); +} diff --git a/arch/parisc/math-emu/sfsub.c b/arch/parisc/math-emu/sfsub.c new file mode 100644 index 000000000..29d9eed09 --- /dev/null +++ b/arch/parisc/math-emu/sfsub.c @@ -0,0 +1,508 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ +/* + * BEGIN_DESC + * + * File: + * @(#) pa/spmath/sfsub.c $Revision: 1.1 $ + * + * Purpose: + * Single_subtract: subtract two single precision values. + * + * External Interfaces: + * sgl_fsub(leftptr, rightptr, dstptr, status) + * + * Internal Interfaces: + * + * Theory: + * <<please update with a overview of the operation of this file>> + * + * END_DESC +*/ + + +#include "float.h" +#include "sgl_float.h" + +/* + * Single_subtract: subtract two single precision values. + */ +int +sgl_fsub( + sgl_floating_point *leftptr, + sgl_floating_point *rightptr, + sgl_floating_point *dstptr, + unsigned int *status) + { + register unsigned int left, right, result, extent; + register unsigned int signless_upper_left, signless_upper_right, save; + + register int result_exponent, right_exponent, diff_exponent; + register int sign_save, jumpsize; + register boolean inexact = FALSE, underflowtrap; + + /* Create local copies of the numbers */ + left = *leftptr; + right = *rightptr; + + /* A zero "save" helps discover equal operands (for later), * + * and is used in swapping operands (if needed). */ + Sgl_xortointp1(left,right,/*to*/save); + + /* + * check first operand for NaN's or infinity + */ + if ((result_exponent = Sgl_exponent(left)) == SGL_INFINITY_EXPONENT) + { + if (Sgl_iszero_mantissa(left)) + { + if (Sgl_isnotnan(right)) + { + if (Sgl_isinfinity(right) && save==0) + { + /* + * invalid since operands are same signed infinity's + */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + Set_invalidflag(); + Sgl_makequietnan(result); + *dstptr = result; + return(NOEXCEPTION); + } + /* + * return infinity + */ + *dstptr = left; + return(NOEXCEPTION); + } + } + else + { + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(left)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(left); + } + /* + * is second operand a signaling NaN? + */ + else if (Sgl_is_signalingnan(right)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(right); + *dstptr = right; + return(NOEXCEPTION); + } + /* + * return quiet NaN + */ + *dstptr = left; + return(NOEXCEPTION); + } + } /* End left NaN or Infinity processing */ + /* + * check second operand for NaN's or infinity + */ + if (Sgl_isinfinity_exponent(right)) + { + if (Sgl_iszero_mantissa(right)) + { + /* return infinity */ + Sgl_invert_sign(right); + *dstptr = right; + return(NOEXCEPTION); + } + /* + * is NaN; signaling or quiet? + */ + if (Sgl_isone_signaling(right)) + { + /* trap if INVALIDTRAP enabled */ + if (Is_invalidtrap_enabled()) return(INVALIDEXCEPTION); + /* make NaN quiet */ + Set_invalidflag(); + Sgl_set_quiet(right); + } + /* + * return quiet NaN + */ + *dstptr = right; + return(NOEXCEPTION); + } /* End right NaN or Infinity processing */ + + /* Invariant: Must be dealing with finite numbers */ + + /* Compare operands by removing the sign */ + Sgl_copytoint_exponentmantissa(left,signless_upper_left); + Sgl_copytoint_exponentmantissa(right,signless_upper_right); + + /* sign difference selects sub or add operation. */ + if(Sgl_ismagnitudeless(signless_upper_left,signless_upper_right)) + { + /* Set the left operand to the larger one by XOR swap * + * First finish the first word using "save" */ + Sgl_xorfromintp1(save,right,/*to*/right); + Sgl_xorfromintp1(save,left,/*to*/left); + result_exponent = Sgl_exponent(left); + Sgl_invert_sign(left); + } + /* Invariant: left is not smaller than right. */ + + if((right_exponent = Sgl_exponent(right)) == 0) + { + /* Denormalized operands. First look for zeroes */ + if(Sgl_iszero_mantissa(right)) + { + /* right is zero */ + if(Sgl_iszero_exponentmantissa(left)) + { + /* Both operands are zeros */ + Sgl_invert_sign(right); + if(Is_rounding_mode(ROUNDMINUS)) + { + Sgl_or_signs(left,/*with*/right); + } + else + { + Sgl_and_signs(left,/*with*/right); + } + } + else + { + /* Left is not a zero and must be the result. Trapped + * underflows are signaled if left is denormalized. Result + * is always exact. */ + if( (result_exponent == 0) && Is_underflowtrap_enabled() ) + { + /* need to normalize results mantissa */ + sign_save = Sgl_signextendedsign(left); + Sgl_leftshiftby1(left); + Sgl_normalize(left,result_exponent); + Sgl_set_sign(left,/*using*/sign_save); + Sgl_setwrapped_exponent(left,result_exponent,unfl); + *dstptr = left; + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + } + *dstptr = left; + return(NOEXCEPTION); + } + + /* Neither are zeroes */ + Sgl_clear_sign(right); /* Exponent is already cleared */ + if(result_exponent == 0 ) + { + /* Both operands are denormalized. The result must be exact + * and is simply calculated. A sum could become normalized and a + * difference could cancel to a true zero. */ + if( (/*signed*/int) save >= 0 ) + { + Sgl_subtract(left,/*minus*/right,/*into*/result); + if(Sgl_iszero_mantissa(result)) + { + if(Is_rounding_mode(ROUNDMINUS)) + { + Sgl_setone_sign(result); + } + else + { + Sgl_setzero_sign(result); + } + *dstptr = result; + return(NOEXCEPTION); + } + } + else + { + Sgl_addition(left,right,/*into*/result); + if(Sgl_isone_hidden(result)) + { + *dstptr = result; + return(NOEXCEPTION); + } + } + if(Is_underflowtrap_enabled()) + { + /* need to normalize result */ + sign_save = Sgl_signextendedsign(result); + Sgl_leftshiftby1(result); + Sgl_normalize(result,result_exponent); + Sgl_set_sign(result,/*using*/sign_save); + Sgl_setwrapped_exponent(result,result_exponent,unfl); + *dstptr = result; + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + *dstptr = result; + return(NOEXCEPTION); + } + right_exponent = 1; /* Set exponent to reflect different bias + * with denormalized numbers. */ + } + else + { + Sgl_clear_signexponent_set_hidden(right); + } + Sgl_clear_exponent_set_hidden(left); + diff_exponent = result_exponent - right_exponent; + + /* + * Special case alignment of operands that would force alignment + * beyond the extent of the extension. A further optimization + * could special case this but only reduces the path length for this + * infrequent case. + */ + if(diff_exponent > SGL_THRESHOLD) + { + diff_exponent = SGL_THRESHOLD; + } + + /* Align right operand by shifting to right */ + Sgl_right_align(/*operand*/right,/*shifted by*/diff_exponent, + /*and lower to*/extent); + + /* Treat sum and difference of the operands separately. */ + if( (/*signed*/int) save >= 0 ) + { + /* + * Difference of the two operands. Their can be no overflow. A + * borrow can occur out of the hidden bit and force a post + * normalization phase. + */ + Sgl_subtract_withextension(left,/*minus*/right,/*with*/extent,/*into*/result); + if(Sgl_iszero_hidden(result)) + { + /* Handle normalization */ + /* A straightforward algorithm would now shift the result + * and extension left until the hidden bit becomes one. Not + * all of the extension bits need participate in the shift. + * Only the two most significant bits (round and guard) are + * needed. If only a single shift is needed then the guard + * bit becomes a significant low order bit and the extension + * must participate in the rounding. If more than a single + * shift is needed, then all bits to the right of the guard + * bit are zeros, and the guard bit may or may not be zero. */ + sign_save = Sgl_signextendedsign(result); + Sgl_leftshiftby1_withextent(result,extent,result); + + /* Need to check for a zero result. The sign and exponent + * fields have already been zeroed. The more efficient test + * of the full object can be used. + */ + if(Sgl_iszero(result)) + /* Must have been "x-x" or "x+(-x)". */ + { + if(Is_rounding_mode(ROUNDMINUS)) Sgl_setone_sign(result); + *dstptr = result; + return(NOEXCEPTION); + } + result_exponent--; + /* Look to see if normalization is finished. */ + if(Sgl_isone_hidden(result)) + { + if(result_exponent==0) + { + /* Denormalized, exponent should be zero. Left operand * + * was normalized, so extent (guard, round) was zero */ + goto underflow; + } + else + { + /* No further normalization is needed. */ + Sgl_set_sign(result,/*using*/sign_save); + Ext_leftshiftby1(extent); + goto round; + } + } + + /* Check for denormalized, exponent should be zero. Left * + * operand was normalized, so extent (guard, round) was zero */ + if(!(underflowtrap = Is_underflowtrap_enabled()) && + result_exponent==0) goto underflow; + + /* Shift extension to complete one bit of normalization and + * update exponent. */ + Ext_leftshiftby1(extent); + + /* Discover first one bit to determine shift amount. Use a + * modified binary search. We have already shifted the result + * one position right and still not found a one so the remainder + * of the extension must be zero and simplifies rounding. */ + /* Scan bytes */ + while(Sgl_iszero_hiddenhigh7mantissa(result)) + { + Sgl_leftshiftby8(result); + if((result_exponent -= 8) <= 0 && !underflowtrap) + goto underflow; + } + /* Now narrow it down to the nibble */ + if(Sgl_iszero_hiddenhigh3mantissa(result)) + { + /* The lower nibble contains the normalizing one */ + Sgl_leftshiftby4(result); + if((result_exponent -= 4) <= 0 && !underflowtrap) + goto underflow; + } + /* Select case were first bit is set (already normalized) + * otherwise select the proper shift. */ + if((jumpsize = Sgl_hiddenhigh3mantissa(result)) > 7) + { + /* Already normalized */ + if(result_exponent <= 0) goto underflow; + Sgl_set_sign(result,/*using*/sign_save); + Sgl_set_exponent(result,/*using*/result_exponent); + *dstptr = result; + return(NOEXCEPTION); + } + Sgl_sethigh4bits(result,/*using*/sign_save); + switch(jumpsize) + { + case 1: + { + Sgl_leftshiftby3(result); + result_exponent -= 3; + break; + } + case 2: + case 3: + { + Sgl_leftshiftby2(result); + result_exponent -= 2; + break; + } + case 4: + case 5: + case 6: + case 7: + { + Sgl_leftshiftby1(result); + result_exponent -= 1; + break; + } + } + if(result_exponent > 0) + { + Sgl_set_exponent(result,/*using*/result_exponent); + *dstptr = result; /* Sign bit is already set */ + return(NOEXCEPTION); + } + /* Fixup potential underflows */ + underflow: + if(Is_underflowtrap_enabled()) + { + Sgl_set_sign(result,sign_save); + Sgl_setwrapped_exponent(result,result_exponent,unfl); + *dstptr = result; + /* inexact = FALSE */ + return(UNDERFLOWEXCEPTION); + } + /* + * Since we cannot get an inexact denormalized result, + * we can now return. + */ + Sgl_right_align(result,/*by*/(1-result_exponent),extent); + Sgl_clear_signexponent(result); + Sgl_set_sign(result,sign_save); + *dstptr = result; + return(NOEXCEPTION); + } /* end if(hidden...)... */ + /* Fall through and round */ + } /* end if(save >= 0)... */ + else + { + /* Add magnitudes */ + Sgl_addition(left,right,/*to*/result); + if(Sgl_isone_hiddenoverflow(result)) + { + /* Prenormalization required. */ + Sgl_rightshiftby1_withextent(result,extent,extent); + Sgl_arithrightshiftby1(result); + result_exponent++; + } /* end if hiddenoverflow... */ + } /* end else ...sub magnitudes... */ + + /* Round the result. If the extension is all zeros,then the result is + * exact. Otherwise round in the correct direction. No underflow is + * possible. If a postnormalization is necessary, then the mantissa is + * all zeros so no shift is needed. */ + round: + if(Ext_isnotzero(extent)) + { + inexact = TRUE; + switch(Rounding_mode()) + { + case ROUNDNEAREST: /* The default. */ + if(Ext_isone_sign(extent)) + { + /* at least 1/2 ulp */ + if(Ext_isnotzero_lower(extent) || + Sgl_isone_lowmantissa(result)) + { + /* either exactly half way and odd or more than 1/2ulp */ + Sgl_increment(result); + } + } + break; + + case ROUNDPLUS: + if(Sgl_iszero_sign(result)) + { + /* Round up positive results */ + Sgl_increment(result); + } + break; + + case ROUNDMINUS: + if(Sgl_isone_sign(result)) + { + /* Round down negative results */ + Sgl_increment(result); + } + + case ROUNDZERO:; + /* truncate is simple */ + } /* end switch... */ + if(Sgl_isone_hiddenoverflow(result)) result_exponent++; + } + if(result_exponent == SGL_INFINITY_EXPONENT) + { + /* Overflow */ + if(Is_overflowtrap_enabled()) + { + Sgl_setwrapped_exponent(result,result_exponent,ovfl); + *dstptr = result; + if (inexact) + if (Is_inexacttrap_enabled()) + return(OVERFLOWEXCEPTION | INEXACTEXCEPTION); + else Set_inexactflag(); + return(OVERFLOWEXCEPTION); + } + else + { + Set_overflowflag(); + inexact = TRUE; + Sgl_setoverflow(result); + } + } + else Sgl_set_exponent(result,result_exponent); + *dstptr = result; + if(inexact) + if(Is_inexacttrap_enabled()) return(INEXACTEXCEPTION); + else Set_inexactflag(); + return(NOEXCEPTION); + } diff --git a/arch/parisc/math-emu/sgl_float.h b/arch/parisc/math-emu/sgl_float.h new file mode 100644 index 000000000..6ec2662cc --- /dev/null +++ b/arch/parisc/math-emu/sgl_float.h @@ -0,0 +1,473 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux/PA-RISC Project (http://www.parisc-linux.org/) + * + * Floating-point emulation code + * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> + */ + +#ifdef __NO_PA_HDRS + PA header file -- do not include this header file for non-PA builds. +#endif + +/* 32-bit word grabbing functions */ +#define Sgl_firstword(value) Sall(value) +#define Sgl_secondword(value) dummy_location +#define Sgl_thirdword(value) dummy_location +#define Sgl_fourthword(value) dummy_location + +#define Sgl_sign(object) Ssign(object) +#define Sgl_exponent(object) Sexponent(object) +#define Sgl_signexponent(object) Ssignexponent(object) +#define Sgl_mantissa(object) Smantissa(object) +#define Sgl_exponentmantissa(object) Sexponentmantissa(object) +#define Sgl_all(object) Sall(object) + +/* sgl_and_signs ANDs the sign bits of each argument and puts the result + * into the first argument. sgl_or_signs ors those same sign bits */ +#define Sgl_and_signs( src1dst, src2) \ + Sall(src1dst) = (Sall(src2)|~((unsigned int)1<<31)) & Sall(src1dst) +#define Sgl_or_signs( src1dst, src2) \ + Sall(src1dst) = (Sall(src2)&((unsigned int)1<<31)) | Sall(src1dst) + +/* The hidden bit is always the low bit of the exponent */ +#define Sgl_clear_exponent_set_hidden(srcdst) Deposit_sexponent(srcdst,1) +#define Sgl_clear_signexponent_set_hidden(srcdst) \ + Deposit_ssignexponent(srcdst,1) +#define Sgl_clear_sign(srcdst) Sall(srcdst) &= ~((unsigned int)1<<31) +#define Sgl_clear_signexponent(srcdst) Sall(srcdst) &= 0x007fffff + +/* varamount must be less than 32 for the next three functions */ +#define Sgl_rightshift(srcdst, varamount) \ + Sall(srcdst) >>= varamount +#define Sgl_leftshift(srcdst, varamount) \ + Sall(srcdst) <<= varamount +#define Sgl_rightshift_exponentmantissa(srcdst, varamount) \ + Sall(srcdst) = \ + (Sexponentmantissa(srcdst) >> varamount) | \ + (Sall(srcdst) & ((unsigned int)1<<31)) + +#define Sgl_leftshiftby1_withextent(left,right,result) \ + Shiftdouble(Sall(left),Extall(right),31,Sall(result)) + +#define Sgl_rightshiftby1_withextent(left,right,dst) \ + Shiftdouble(Sall(left),Extall(right),1,Extall(right)) +#define Sgl_arithrightshiftby1(srcdst) \ + Sall(srcdst) = (int)Sall(srcdst) >> 1 + +/* Sign extend the sign bit with an integer destination */ +#define Sgl_signextendedsign(value) Ssignedsign(value) + +#define Sgl_isone_hidden(sgl_value) (Shidden(sgl_value)) +#define Sgl_increment(sgl_value) Sall(sgl_value) += 1 +#define Sgl_increment_mantissa(sgl_value) \ + Deposit_smantissa(sgl_value,sgl_value+1) +#define Sgl_decrement(sgl_value) Sall(sgl_value) -= 1 + +#define Sgl_isone_sign(sgl_value) (Is_ssign(sgl_value)!=0) +#define Sgl_isone_hiddenoverflow(sgl_value) \ + (Is_shiddenoverflow(sgl_value)!=0) +#define Sgl_isone_lowmantissa(sgl_value) (Is_slow(sgl_value)!=0) +#define Sgl_isone_signaling(sgl_value) (Is_ssignaling(sgl_value)!=0) +#define Sgl_is_signalingnan(sgl_value) (Ssignalingnan(sgl_value)==0x1ff) +#define Sgl_isnotzero(sgl_value) (Sall(sgl_value)!=0) +#define Sgl_isnotzero_hiddenhigh7mantissa(sgl_value) \ + (Shiddenhigh7mantissa(sgl_value)!=0) +#define Sgl_isnotzero_low4(sgl_value) (Slow4(sgl_value)!=0) +#define Sgl_isnotzero_exponent(sgl_value) (Sexponent(sgl_value)!=0) +#define Sgl_isnotzero_mantissa(sgl_value) (Smantissa(sgl_value)!=0) +#define Sgl_isnotzero_exponentmantissa(sgl_value) \ + (Sexponentmantissa(sgl_value)!=0) +#define Sgl_iszero(sgl_value) (Sall(sgl_value)==0) +#define Sgl_iszero_signaling(sgl_value) (Is_ssignaling(sgl_value)==0) +#define Sgl_iszero_hidden(sgl_value) (Is_shidden(sgl_value)==0) +#define Sgl_iszero_hiddenoverflow(sgl_value) \ + (Is_shiddenoverflow(sgl_value)==0) +#define Sgl_iszero_hiddenhigh3mantissa(sgl_value) \ + (Shiddenhigh3mantissa(sgl_value)==0) +#define Sgl_iszero_hiddenhigh7mantissa(sgl_value) \ + (Shiddenhigh7mantissa(sgl_value)==0) +#define Sgl_iszero_sign(sgl_value) (Is_ssign(sgl_value)==0) +#define Sgl_iszero_exponent(sgl_value) (Sexponent(sgl_value)==0) +#define Sgl_iszero_mantissa(sgl_value) (Smantissa(sgl_value)==0) +#define Sgl_iszero_exponentmantissa(sgl_value) \ + (Sexponentmantissa(sgl_value)==0) +#define Sgl_isinfinity_exponent(sgl_value) \ + (Sgl_exponent(sgl_value)==SGL_INFINITY_EXPONENT) +#define Sgl_isnotinfinity_exponent(sgl_value) \ + (Sgl_exponent(sgl_value)!=SGL_INFINITY_EXPONENT) +#define Sgl_isinfinity(sgl_value) \ + (Sgl_exponent(sgl_value)==SGL_INFINITY_EXPONENT && \ + Sgl_mantissa(sgl_value)==0) +#define Sgl_isnan(sgl_value) \ + (Sgl_exponent(sgl_value)==SGL_INFINITY_EXPONENT && \ + Sgl_mantissa(sgl_value)!=0) +#define Sgl_isnotnan(sgl_value) \ + (Sgl_exponent(sgl_value)!=SGL_INFINITY_EXPONENT || \ + Sgl_mantissa(sgl_value)==0) +#define Sgl_islessthan(sgl_op1,sgl_op2) \ + (Sall(sgl_op1) < Sall(sgl_op2)) +#define Sgl_isgreaterthan(sgl_op1,sgl_op2) \ + (Sall(sgl_op1) > Sall(sgl_op2)) +#define Sgl_isnotlessthan(sgl_op1,sgl_op2) \ + (Sall(sgl_op1) >= Sall(sgl_op2)) +#define Sgl_isequal(sgl_op1,sgl_op2) \ + (Sall(sgl_op1) == Sall(sgl_op2)) + +#define Sgl_leftshiftby8(sgl_value) \ + Sall(sgl_value) <<= 8 +#define Sgl_leftshiftby4(sgl_value) \ + Sall(sgl_value) <<= 4 +#define Sgl_leftshiftby3(sgl_value) \ + Sall(sgl_value) <<= 3 +#define Sgl_leftshiftby2(sgl_value) \ + Sall(sgl_value) <<= 2 +#define Sgl_leftshiftby1(sgl_value) \ + Sall(sgl_value) <<= 1 +#define Sgl_rightshiftby1(sgl_value) \ + Sall(sgl_value) >>= 1 +#define Sgl_rightshiftby4(sgl_value) \ + Sall(sgl_value) >>= 4 +#define Sgl_rightshiftby8(sgl_value) \ + Sall(sgl_value) >>= 8 + +#define Sgl_ismagnitudeless(signlessleft,signlessright) \ +/* unsigned int signlessleft, signlessright; */ \ + (signlessleft < signlessright) + + +#define Sgl_copytoint_exponentmantissa(source,dest) \ + dest = Sexponentmantissa(source) + +/* A quiet NaN has the high mantissa bit clear and at least on other (in this + * case the adjacent bit) bit set. */ +#define Sgl_set_quiet(sgl_value) Deposit_shigh2mantissa(sgl_value,1) +#define Sgl_set_exponent(sgl_value,exp) Deposit_sexponent(sgl_value,exp) + +#define Sgl_set_mantissa(dest,value) Deposit_smantissa(dest,value) +#define Sgl_set_exponentmantissa(dest,value) \ + Deposit_sexponentmantissa(dest,value) + +/* An infinity is represented with the max exponent and a zero mantissa */ +#define Sgl_setinfinity_exponent(sgl_value) \ + Deposit_sexponent(sgl_value,SGL_INFINITY_EXPONENT) +#define Sgl_setinfinity_exponentmantissa(sgl_value) \ + Deposit_sexponentmantissa(sgl_value, \ + (SGL_INFINITY_EXPONENT << (32-(1+SGL_EXP_LENGTH)))) +#define Sgl_setinfinitypositive(sgl_value) \ + Sall(sgl_value) = (SGL_INFINITY_EXPONENT << (32-(1+SGL_EXP_LENGTH))) +#define Sgl_setinfinitynegative(sgl_value) \ + Sall(sgl_value) = (SGL_INFINITY_EXPONENT << (32-(1+SGL_EXP_LENGTH))) \ + | ((unsigned int)1<<31) +#define Sgl_setinfinity(sgl_value,sign) \ + Sall(sgl_value) = (SGL_INFINITY_EXPONENT << (32-(1+SGL_EXP_LENGTH))) | \ + ((unsigned int)sign << 31) +#define Sgl_sethigh4bits(sgl_value, extsign) \ + Deposit_shigh4(sgl_value,extsign) +#define Sgl_set_sign(sgl_value,sign) Deposit_ssign(sgl_value,sign) +#define Sgl_invert_sign(sgl_value) \ + Deposit_ssign(sgl_value,~Ssign(sgl_value)) +#define Sgl_setone_sign(sgl_value) Deposit_ssign(sgl_value,1) +#define Sgl_setone_lowmantissa(sgl_value) Deposit_slow(sgl_value,1) +#define Sgl_setzero_sign(sgl_value) Sall(sgl_value) &= 0x7fffffff +#define Sgl_setzero_exponent(sgl_value) Sall(sgl_value) &= 0x807fffff +#define Sgl_setzero_mantissa(sgl_value) Sall(sgl_value) &= 0xff800000 +#define Sgl_setzero_exponentmantissa(sgl_value) Sall(sgl_value) &= 0x80000000 +#define Sgl_setzero(sgl_value) Sall(sgl_value) = 0 +#define Sgl_setnegativezero(sgl_value) Sall(sgl_value) = (unsigned int)1 << 31 + +/* Use following macro for both overflow & underflow conditions */ +#define ovfl - +#define unfl + +#define Sgl_setwrapped_exponent(sgl_value,exponent,op) \ + Deposit_sexponent(sgl_value,(exponent op SGL_WRAP)) + +#define Sgl_setlargestpositive(sgl_value) \ + Sall(sgl_value) = ((SGL_EMAX+SGL_BIAS) << (32-(1+SGL_EXP_LENGTH))) \ + | ((1<<(32-(1+SGL_EXP_LENGTH))) - 1 ) +#define Sgl_setlargestnegative(sgl_value) \ + Sall(sgl_value) = ((SGL_EMAX+SGL_BIAS) << (32-(1+SGL_EXP_LENGTH))) \ + | ((1<<(32-(1+SGL_EXP_LENGTH))) - 1 ) \ + | ((unsigned int)1<<31) + +#define Sgl_setnegativeinfinity(sgl_value) \ + Sall(sgl_value) = \ + ((1<<SGL_EXP_LENGTH) | SGL_INFINITY_EXPONENT) << (32-(1+SGL_EXP_LENGTH)) +#define Sgl_setlargest(sgl_value,sign) \ + Sall(sgl_value) = (unsigned int)sign << 31 | \ + (((SGL_EMAX+SGL_BIAS) << (32-(1+SGL_EXP_LENGTH))) \ + | ((1 << (32-(1+SGL_EXP_LENGTH))) - 1 )) +#define Sgl_setlargest_exponentmantissa(sgl_value) \ + Sall(sgl_value) = Sall(sgl_value) & ((unsigned int)1<<31) | \ + (((SGL_EMAX+SGL_BIAS) << (32-(1+SGL_EXP_LENGTH))) \ + | ((1 << (32-(1+SGL_EXP_LENGTH))) - 1 )) + +/* The high bit is always zero so arithmetic or logical shifts will work. */ +#define Sgl_right_align(srcdst,shift,extent) \ + /* sgl_floating_point srcdst; int shift; extension extent */ \ + if (shift < 32) { \ + Extall(extent) = Sall(srcdst) << (32-(shift)); \ + Sall(srcdst) >>= shift; \ + } \ + else { \ + Extall(extent) = Sall(srcdst); \ + Sall(srcdst) = 0; \ + } +#define Sgl_hiddenhigh3mantissa(sgl_value) Shiddenhigh3mantissa(sgl_value) +#define Sgl_hidden(sgl_value) Shidden(sgl_value) +#define Sgl_lowmantissa(sgl_value) Slow(sgl_value) + +/* The left argument is never smaller than the right argument */ +#define Sgl_subtract(sgl_left,sgl_right,sgl_result) \ + Sall(sgl_result) = Sall(sgl_left) - Sall(sgl_right) + +/* Subtract right augmented with extension from left augmented with zeros and + * store into result and extension. */ +#define Sgl_subtract_withextension(left,right,extent,result) \ + /* sgl_floating_point left,right,result; extension extent */ \ + Sgl_subtract(left,right,result); \ + if((Extall(extent) = 0-Extall(extent))) \ + Sall(result) = Sall(result)-1 + +#define Sgl_addition(sgl_left,sgl_right,sgl_result) \ + Sall(sgl_result) = Sall(sgl_left) + Sall(sgl_right) + +#define Sgl_xortointp1(left,right,result) \ + result = Sall(left) XOR Sall(right); + +#define Sgl_xorfromintp1(left,right,result) \ + Sall(result) = left XOR Sall(right) + +/* Need to Initialize */ +#define Sgl_makequietnan(dest) \ + Sall(dest) = ((SGL_EMAX+SGL_BIAS)+1)<< (32-(1+SGL_EXP_LENGTH)) \ + | (1<<(32-(1+SGL_EXP_LENGTH+2))) +#define Sgl_makesignalingnan(dest) \ + Sall(dest) = ((SGL_EMAX+SGL_BIAS)+1)<< (32-(1+SGL_EXP_LENGTH)) \ + | (1<<(32-(1+SGL_EXP_LENGTH+1))) + +#define Sgl_normalize(sgl_opnd,exponent) \ + while(Sgl_iszero_hiddenhigh7mantissa(sgl_opnd)) { \ + Sgl_leftshiftby8(sgl_opnd); \ + exponent -= 8; \ + } \ + if(Sgl_iszero_hiddenhigh3mantissa(sgl_opnd)) { \ + Sgl_leftshiftby4(sgl_opnd); \ + exponent -= 4; \ + } \ + while(Sgl_iszero_hidden(sgl_opnd)) { \ + Sgl_leftshiftby1(sgl_opnd); \ + exponent -= 1; \ + } + +#define Sgl_setoverflow(sgl_opnd) \ + /* set result to infinity or largest number */ \ + switch (Rounding_mode()) { \ + case ROUNDPLUS: \ + if (Sgl_isone_sign(sgl_opnd)) { \ + Sgl_setlargestnegative(sgl_opnd); \ + } \ + else { \ + Sgl_setinfinitypositive(sgl_opnd); \ + } \ + break; \ + case ROUNDMINUS: \ + if (Sgl_iszero_sign(sgl_opnd)) { \ + Sgl_setlargestpositive(sgl_opnd); \ + } \ + else { \ + Sgl_setinfinitynegative(sgl_opnd); \ + } \ + break; \ + case ROUNDNEAREST: \ + Sgl_setinfinity_exponentmantissa(sgl_opnd); \ + break; \ + case ROUNDZERO: \ + Sgl_setlargest_exponentmantissa(sgl_opnd); \ + } + +#define Sgl_denormalize(opnd,exponent,guard,sticky,inexact) \ + Sgl_clear_signexponent_set_hidden(opnd); \ + if (exponent >= (1 - SGL_P)) { \ + guard = (Sall(opnd) >> -exponent) & 1; \ + if (exponent < 0) sticky |= Sall(opnd) << (32+exponent); \ + inexact = guard | sticky; \ + Sall(opnd) >>= (1-exponent); \ + } \ + else { \ + guard = 0; \ + sticky |= Sall(opnd); \ + inexact = sticky; \ + Sgl_setzero(opnd); \ + } + +/* + * The fused multiply add instructions requires a single extended format, + * with 48 bits of mantissa. + */ +#define SGLEXT_THRESHOLD 48 + +#define Sglext_setzero(valA,valB) \ + Sextallp1(valA) = 0; Sextallp2(valB) = 0 + +#define Sglext_isnotzero_mantissap2(valB) (Sextallp2(valB)!=0) +#define Sglext_isone_lowp1(val) (Sextlowp1(val)!=0) +#define Sglext_isone_highp2(val) (Sexthighp2(val)!=0) +#define Sglext_isnotzero_low31p2(val) (Sextlow31p2(val)!=0) +#define Sglext_iszero(valA,valB) (Sextallp1(valA)==0 && Sextallp2(valB)==0) + +#define Sgl_copytoptr(src,destptr) *destptr = src +#define Sgl_copyfromptr(srcptr,dest) dest = *srcptr +#define Sglext_copy(srca,srcb,desta,destb) \ + Sextallp1(desta) = Sextallp1(srca); \ + Sextallp2(destb) = Sextallp2(srcb) +#define Sgl_copyto_sglext(src1,dest1,dest2) \ + Sextallp1(dest1) = Sall(src1); Sextallp2(dest2) = 0 + +#define Sglext_swap_lower(leftp2,rightp2) \ + Sextallp2(leftp2) = Sextallp2(leftp2) XOR Sextallp2(rightp2); \ + Sextallp2(rightp2) = Sextallp2(leftp2) XOR Sextallp2(rightp2); \ + Sextallp2(leftp2) = Sextallp2(leftp2) XOR Sextallp2(rightp2) + +#define Sglext_setone_lowmantissap2(value) Deposit_dlowp2(value,1) + +/* The high bit is always zero so arithmetic or logical shifts will work. */ +#define Sglext_right_align(srcdstA,srcdstB,shift) \ + {int shiftamt, sticky; \ + shiftamt = shift % 32; \ + sticky = 0; \ + switch (shift/32) { \ + case 0: if (shiftamt > 0) { \ + sticky = Sextallp2(srcdstB) << 32 - (shiftamt); \ + Variable_shift_double(Sextallp1(srcdstA), \ + Sextallp2(srcdstB),shiftamt,Sextallp2(srcdstB)); \ + Sextallp1(srcdstA) >>= shiftamt; \ + } \ + break; \ + case 1: if (shiftamt > 0) { \ + sticky = (Sextallp1(srcdstA) << 32 - (shiftamt)) | \ + Sextallp2(srcdstB); \ + } \ + else { \ + sticky = Sextallp2(srcdstB); \ + } \ + Sextallp2(srcdstB) = Sextallp1(srcdstA) >> shiftamt; \ + Sextallp1(srcdstA) = 0; \ + break; \ + } \ + if (sticky) Sglext_setone_lowmantissap2(srcdstB); \ + } + +/* The left argument is never smaller than the right argument */ +#define Sglext_subtract(lefta,leftb,righta,rightb,resulta,resultb) \ + if( Sextallp2(rightb) > Sextallp2(leftb) ) Sextallp1(lefta)--; \ + Sextallp2(resultb) = Sextallp2(leftb) - Sextallp2(rightb); \ + Sextallp1(resulta) = Sextallp1(lefta) - Sextallp1(righta) + +#define Sglext_addition(lefta,leftb,righta,rightb,resulta,resultb) \ + /* If the sum of the low words is less than either source, then \ + * an overflow into the next word occurred. */ \ + if ((Sextallp2(resultb) = Sextallp2(leftb)+Sextallp2(rightb)) < \ + Sextallp2(rightb)) \ + Sextallp1(resulta) = Sextallp1(lefta)+Sextallp1(righta)+1; \ + else Sextallp1(resulta) = Sextallp1(lefta)+Sextallp1(righta) + + +#define Sglext_arithrightshiftby1(srcdstA,srcdstB) \ + Shiftdouble(Sextallp1(srcdstA),Sextallp2(srcdstB),1,Sextallp2(srcdstB)); \ + Sextallp1(srcdstA) = (int)Sextallp1(srcdstA) >> 1 + +#define Sglext_leftshiftby8(valA,valB) \ + Shiftdouble(Sextallp1(valA),Sextallp2(valB),24,Sextallp1(valA)); \ + Sextallp2(valB) <<= 8 +#define Sglext_leftshiftby4(valA,valB) \ + Shiftdouble(Sextallp1(valA),Sextallp2(valB),28,Sextallp1(valA)); \ + Sextallp2(valB) <<= 4 +#define Sglext_leftshiftby3(valA,valB) \ + Shiftdouble(Sextallp1(valA),Sextallp2(valB),29,Sextallp1(valA)); \ + Sextallp2(valB) <<= 3 +#define Sglext_leftshiftby2(valA,valB) \ + Shiftdouble(Sextallp1(valA),Sextallp2(valB),30,Sextallp1(valA)); \ + Sextallp2(valB) <<= 2 +#define Sglext_leftshiftby1(valA,valB) \ + Shiftdouble(Sextallp1(valA),Sextallp2(valB),31,Sextallp1(valA)); \ + Sextallp2(valB) <<= 1 + +#define Sglext_rightshiftby4(valueA,valueB) \ + Shiftdouble(Sextallp1(valueA),Sextallp2(valueB),4,Sextallp2(valueB)); \ + Sextallp1(valueA) >>= 4 +#define Sglext_rightshiftby3(valueA,valueB) \ + Shiftdouble(Sextallp1(valueA),Sextallp2(valueB),3,Sextallp2(valueB)); \ + Sextallp1(valueA) >>= 3 +#define Sglext_rightshiftby1(valueA,valueB) \ + Shiftdouble(Sextallp1(valueA),Sextallp2(valueB),1,Sextallp2(valueB)); \ + Sextallp1(valueA) >>= 1 + +#define Sglext_xortointp1(left,right,result) Sgl_xortointp1(left,right,result) +#define Sglext_xorfromintp1(left,right,result) \ + Sgl_xorfromintp1(left,right,result) +#define Sglext_copytoint_exponentmantissa(src,dest) \ + Sgl_copytoint_exponentmantissa(src,dest) +#define Sglext_ismagnitudeless(signlessleft,signlessright) \ + Sgl_ismagnitudeless(signlessleft,signlessright) + +#define Sglext_set_sign(dbl_value,sign) Sgl_set_sign(dbl_value,sign) +#define Sglext_clear_signexponent_set_hidden(srcdst) \ + Sgl_clear_signexponent_set_hidden(srcdst) +#define Sglext_clear_signexponent(srcdst) Sgl_clear_signexponent(srcdst) +#define Sglext_clear_sign(srcdst) Sgl_clear_sign(srcdst) +#define Sglext_isone_hidden(dbl_value) Sgl_isone_hidden(dbl_value) + +#define Sglext_denormalize(opndp1,opndp2,exponent,is_tiny) \ + {int sticky; \ + is_tiny = TRUE; \ + if (exponent == 0 && Sextallp2(opndp2)) { \ + switch (Rounding_mode()) { \ + case ROUNDPLUS: \ + if (Sgl_iszero_sign(opndp1)) \ + if (Sgl_isone_hiddenoverflow(opndp1 + 1)) \ + is_tiny = FALSE; \ + break; \ + case ROUNDMINUS: \ + if (Sgl_isone_sign(opndp1)) { \ + if (Sgl_isone_hiddenoverflow(opndp1 + 1)) \ + is_tiny = FALSE; \ + } \ + break; \ + case ROUNDNEAREST: \ + if (Sglext_isone_highp2(opndp2) && \ + (Sglext_isone_lowp1(opndp1) || \ + Sglext_isnotzero_low31p2(opndp2))) \ + if (Sgl_isone_hiddenoverflow(opndp1 + 1)) \ + is_tiny = FALSE; \ + break; \ + } \ + } \ + Sglext_clear_signexponent_set_hidden(opndp1); \ + if (exponent >= (1-DBL_P)) { \ + if (exponent >= -31) { \ + if (exponent > -31) { \ + sticky = Sextallp2(opndp2) << 31+exponent; \ + Variable_shift_double(opndp1,opndp2,1-exponent,opndp2); \ + Sextallp1(opndp1) >>= 1-exponent; \ + } \ + else { \ + sticky = Sextallp2(opndp2); \ + Sextallp2(opndp2) = Sextallp1(opndp1); \ + Sextallp1(opndp1) = 0; \ + } \ + } \ + else { \ + sticky = (Sextallp1(opndp1) << 31+exponent) | \ + Sextallp2(opndp2); \ + Sextallp2(opndp2) = Sextallp1(opndp1) >> -31-exponent; \ + Sextallp1(opndp1) = 0; \ + } \ + } \ + else { \ + sticky = Sextallp1(opndp1) | Sextallp2(opndp2); \ + Sglext_setzero(opndp1,opndp2); \ + } \ + if (sticky) Sglext_setone_lowmantissap2(opndp2); \ + exponent = 0; \ + } diff --git a/arch/parisc/mm/Makefile b/arch/parisc/mm/Makefile new file mode 100644 index 000000000..ffdb5c0a8 --- /dev/null +++ b/arch/parisc/mm/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for arch/parisc/mm +# + +obj-y := init.o fault.o ioremap.o fixmap.o +obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c new file mode 100644 index 000000000..f1785640b --- /dev/null +++ b/arch/parisc/mm/fault.c @@ -0,0 +1,535 @@ +/* + * 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) 1995, 1996, 1997, 1998 by Ralf Baechle + * Copyright 1999 SuSE GmbH (Philipp Rumpf, prumpf@tux.org) + * Copyright 1999 Hewlett Packard Co. + * + */ + +#include <linux/mm.h> +#include <linux/ptrace.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/interrupt.h> +#include <linux/extable.h> +#include <linux/uaccess.h> +#include <linux/hugetlb.h> +#include <linux/perf_event.h> + +#include <asm/traps.h> + +#define DEBUG_NATLB 0 + +/* Various important other fields */ +#define bit22set(x) (x & 0x00000200) +#define bits23_25set(x) (x & 0x000001c0) +#define isGraphicsFlushRead(x) ((x & 0xfc003fdf) == 0x04001a80) + /* extended opcode is 0x6a */ + +#define BITSSET 0x1c0 /* for identifying LDCW */ + + +int show_unhandled_signals = 1; + +/* + * parisc_acctyp(unsigned int inst) -- + * Given a PA-RISC memory access instruction, determine if the + * instruction would perform a memory read or memory write + * operation. + * + * This function assumes that the given instruction is a memory access + * instruction (i.e. you should really only call it if you know that + * the instruction has generated some sort of a memory access fault). + * + * Returns: + * VM_READ if read operation + * VM_WRITE if write operation + * VM_EXEC if execute operation + */ +unsigned long +parisc_acctyp(unsigned long code, unsigned int inst) +{ + if (code == 6 || code == 16) + return VM_EXEC; + + switch (inst & 0xf0000000) { + case 0x40000000: /* load */ + case 0x50000000: /* new load */ + return VM_READ; + + case 0x60000000: /* store */ + case 0x70000000: /* new store */ + return VM_WRITE; + + case 0x20000000: /* coproc */ + case 0x30000000: /* coproc2 */ + if (bit22set(inst)) + return VM_WRITE; + fallthrough; + + case 0x0: /* indexed/memory management */ + if (bit22set(inst)) { + /* + * Check for the 'Graphics Flush Read' instruction. + * It resembles an FDC instruction, except for bits + * 20 and 21. Any combination other than zero will + * utilize the block mover functionality on some + * older PA-RISC platforms. The case where a block + * move is performed from VM to graphics IO space + * should be treated as a READ. + * + * The significance of bits 20,21 in the FDC + * instruction is: + * + * 00 Flush data cache (normal instruction behavior) + * 01 Graphics flush write (IO space -> VM) + * 10 Graphics flush read (VM -> IO space) + * 11 Graphics flush read/write (VM <-> IO space) + */ + if (isGraphicsFlushRead(inst)) + return VM_READ; + return VM_WRITE; + } else { + /* + * Check for LDCWX and LDCWS (semaphore instructions). + * If bits 23 through 25 are all 1's it is one of + * the above two instructions and is a write. + * + * Note: With the limited bits we are looking at, + * this will also catch PROBEW and PROBEWI. However, + * these should never get in here because they don't + * generate exceptions of the type: + * Data TLB miss fault/data page fault + * Data memory protection trap + */ + if (bits23_25set(inst) == BITSSET) + return VM_WRITE; + } + return VM_READ; /* Default */ + } + return VM_READ; /* Default */ +} + +#undef bit22set +#undef bits23_25set +#undef isGraphicsFlushRead +#undef BITSSET + + +#if 0 +/* This is the treewalk to find a vma which is the highest that has + * a start < addr. We're using find_vma_prev instead right now, but + * we might want to use this at some point in the future. Probably + * not, but I want it committed to CVS so I don't lose it :-) + */ + while (tree != vm_avl_empty) { + if (tree->vm_start > addr) { + tree = tree->vm_avl_left; + } else { + prev = tree; + if (prev->vm_next == NULL) + break; + if (prev->vm_next->vm_start > addr) + break; + tree = tree->vm_avl_right; + } + } +#endif + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fix; + + fix = search_exception_tables(regs->iaoq[0]); + if (fix) { + /* + * Fix up get_user() and put_user(). + * ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant + * bit in the relative address of the fixup routine to indicate + * that the register encoded in the "or %r0,%r0,register" + * opcode should be loaded with -EFAULT to report a userspace + * access error. + */ + if (fix->fixup & 1) { + int fault_error_reg = fix->err_opcode & 0x1f; + if (!WARN_ON(!fault_error_reg)) + regs->gr[fault_error_reg] = -EFAULT; + pr_debug("Unalignment fixup of register %d at %pS\n", + fault_error_reg, (void*)regs->iaoq[0]); + + /* zero target register for get_user() */ + if (parisc_acctyp(0, regs->iir) == VM_READ) { + int treg = regs->iir & 0x1f; + BUG_ON(treg == 0); + regs->gr[treg] = 0; + } + } + + regs->iaoq[0] = (unsigned long)&fix->fixup + fix->fixup; + regs->iaoq[0] &= ~3; + /* + * NOTE: In some cases the faulting instruction + * may be in the delay slot of a branch. We + * don't want to take the branch, so we don't + * increment iaoq[1], instead we set it to be + * iaoq[0]+4, and clear the B bit in the PSW + */ + regs->iaoq[1] = regs->iaoq[0] + 4; + regs->gr[0] &= ~PSW_B; /* IPSW in gr[0] */ + + return 1; + } + + return 0; +} + +/* + * parisc hardware trap list + * + * Documented in section 3 "Addressing and Access Control" of the + * "PA-RISC 1.1 Architecture and Instruction Set Reference Manual" + * https://parisc.wiki.kernel.org/index.php/File:Pa11_acd.pdf + * + * For implementation see handle_interruption() in traps.c + */ +static const char * const trap_description[] = { + [1] = "High-priority machine check (HPMC)", + [2] = "Power failure interrupt", + [3] = "Recovery counter trap", + [5] = "Low-priority machine check", + [6] = "Instruction TLB miss fault", + [7] = "Instruction access rights / protection trap", + [8] = "Illegal instruction trap", + [9] = "Break instruction trap", + [10] = "Privileged operation trap", + [11] = "Privileged register trap", + [12] = "Overflow trap", + [13] = "Conditional trap", + [14] = "FP Assist Exception trap", + [15] = "Data TLB miss fault", + [16] = "Non-access ITLB miss fault", + [17] = "Non-access DTLB miss fault", + [18] = "Data memory protection/unaligned access trap", + [19] = "Data memory break trap", + [20] = "TLB dirty bit trap", + [21] = "Page reference trap", + [22] = "Assist emulation trap", + [25] = "Taken branch trap", + [26] = "Data memory access rights trap", + [27] = "Data memory protection ID trap", + [28] = "Unaligned data reference trap", +}; + +const char *trap_name(unsigned long code) +{ + const char *t = NULL; + + if (code < ARRAY_SIZE(trap_description)) + t = trap_description[code]; + + return t ? t : "Unknown trap"; +} + +/* + * Print out info about fatal segfaults, if the show_unhandled_signals + * sysctl is set: + */ +static inline void +show_signal_msg(struct pt_regs *regs, unsigned long code, + unsigned long address, struct task_struct *tsk, + struct vm_area_struct *vma) +{ + if (!unhandled_signal(tsk, SIGSEGV)) + return; + + if (!printk_ratelimit()) + return; + + pr_warn("\n"); + pr_warn("do_page_fault() command='%s' type=%lu address=0x%08lx", + tsk->comm, code, address); + print_vma_addr(KERN_CONT " in ", regs->iaoq[0]); + + pr_cont("\ntrap #%lu: %s%c", code, trap_name(code), + vma ? ',':'\n'); + + if (vma) + pr_cont(" vm_start = 0x%08lx, vm_end = 0x%08lx\n", + vma->vm_start, vma->vm_end); + + show_regs(regs); +} + +void do_page_fault(struct pt_regs *regs, unsigned long code, + unsigned long address) +{ + struct vm_area_struct *vma, *prev_vma; + struct task_struct *tsk; + struct mm_struct *mm; + unsigned long acc_type; + vm_fault_t fault = 0; + unsigned int flags; + char *msg; + + tsk = current; + mm = tsk->mm; + if (!mm) { + msg = "Page fault: no context"; + goto no_context; + } + + flags = FAULT_FLAG_DEFAULT; + if (user_mode(regs)) + flags |= FAULT_FLAG_USER; + + acc_type = parisc_acctyp(code, regs->iir); + if (acc_type & VM_WRITE) + flags |= FAULT_FLAG_WRITE; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); +retry: + mmap_read_lock(mm); + vma = find_vma_prev(mm, address, &prev_vma); + if (!vma || address < vma->vm_start) { + if (!prev_vma || !(prev_vma->vm_flags & VM_GROWSUP)) + goto bad_area; + vma = expand_stack(mm, address); + if (!vma) + goto bad_area_nosemaphore; + } + +/* + * Ok, we have a good vm_area for this memory access. We still need to + * check the access permissions. + */ + + if ((vma->vm_flags & acc_type) != acc_type) + goto bad_area; + + /* + * If for any reason at all we couldn't handle the fault, make + * sure we exit gracefully rather than endlessly redo the + * fault. + */ + + fault = handle_mm_fault(vma, address, flags, regs); + + if (fault_signal_pending(fault, regs)) { + if (!user_mode(regs)) { + msg = "Page fault: fault signal on kernel memory"; + goto no_context; + } + return; + } + + /* The fault is fully completed (including releasing mmap lock) */ + if (fault & VM_FAULT_COMPLETED) + return; + + if (unlikely(fault & VM_FAULT_ERROR)) { + /* + * We hit a shared mapping outside of the file, or some + * other thing happened to us that made us unable to + * handle the page fault gracefully. + */ + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGSEGV) + goto bad_area; + else if (fault & (VM_FAULT_SIGBUS|VM_FAULT_HWPOISON| + VM_FAULT_HWPOISON_LARGE)) + goto bad_area; + BUG(); + } + if (fault & VM_FAULT_RETRY) { + /* + * No need to mmap_read_unlock(mm) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + flags |= FAULT_FLAG_TRIED; + goto retry; + } + mmap_read_unlock(mm); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + */ +bad_area: + mmap_read_unlock(mm); + +bad_area_nosemaphore: + if (!user_mode(regs) && fixup_exception(regs)) { + return; + } + + if (user_mode(regs)) { + int signo, si_code; + + switch (code) { + case 15: /* Data TLB miss fault/Data page fault */ + /* send SIGSEGV when outside of vma */ + if (!vma || + address < vma->vm_start || address >= vma->vm_end) { + signo = SIGSEGV; + si_code = SEGV_MAPERR; + break; + } + + /* send SIGSEGV for wrong permissions */ + if ((vma->vm_flags & acc_type) != acc_type) { + signo = SIGSEGV; + si_code = SEGV_ACCERR; + break; + } + + /* probably address is outside of mapped file */ + fallthrough; + case 17: /* NA data TLB miss / page fault */ + case 18: /* Unaligned access - PCXS only */ + signo = SIGBUS; + si_code = (code == 18) ? BUS_ADRALN : BUS_ADRERR; + break; + case 16: /* Non-access instruction TLB miss fault */ + case 26: /* PCXL: Data memory access rights trap */ + default: + signo = SIGSEGV; + si_code = (code == 26) ? SEGV_ACCERR : SEGV_MAPERR; + break; + } +#ifdef CONFIG_MEMORY_FAILURE + if (fault & (VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)) { + unsigned int lsb = 0; + printk(KERN_ERR + "MCE: Killing %s:%d due to hardware memory corruption fault at %08lx\n", + tsk->comm, tsk->pid, address); + /* + * Either small page or large page may be poisoned. + * In other words, VM_FAULT_HWPOISON_LARGE and + * VM_FAULT_HWPOISON are mutually exclusive. + */ + if (fault & VM_FAULT_HWPOISON_LARGE) + lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); + else if (fault & VM_FAULT_HWPOISON) + lsb = PAGE_SHIFT; + + force_sig_mceerr(BUS_MCEERR_AR, (void __user *) address, + lsb); + return; + } +#endif + show_signal_msg(regs, code, address, tsk, vma); + + force_sig_fault(signo, si_code, (void __user *) address); + return; + } + msg = "Page fault: bad address"; + +no_context: + + if (!user_mode(regs) && fixup_exception(regs)) { + return; + } + + parisc_terminate(msg, regs, code, address); + +out_of_memory: + mmap_read_unlock(mm); + if (!user_mode(regs)) { + msg = "Page fault: out of memory"; + goto no_context; + } + pagefault_out_of_memory(); +} + +/* Handle non-access data TLB miss faults. + * + * For probe instructions, accesses to userspace are considered allowed + * if they lie in a valid VMA and the access type matches. We are not + * allowed to handle MM faults here so there may be situations where an + * actual access would fail even though a probe was successful. + */ +int +handle_nadtlb_fault(struct pt_regs *regs) +{ + unsigned long insn = regs->iir; + int breg, treg, xreg, val = 0; + struct vm_area_struct *vma; + struct task_struct *tsk; + struct mm_struct *mm; + unsigned long address; + unsigned long acc_type; + + switch (insn & 0x380) { + case 0x280: + /* FDC instruction */ + fallthrough; + case 0x380: + /* PDC and FIC instructions */ + if (DEBUG_NATLB && printk_ratelimit()) { + pr_warn("WARNING: nullifying cache flush/purge instruction\n"); + show_regs(regs); + } + if (insn & 0x20) { + /* Base modification */ + breg = (insn >> 21) & 0x1f; + xreg = (insn >> 16) & 0x1f; + if (breg && xreg) + regs->gr[breg] += regs->gr[xreg]; + } + regs->gr[0] |= PSW_N; + return 1; + + case 0x180: + /* PROBE instruction */ + treg = insn & 0x1f; + if (regs->isr) { + tsk = current; + mm = tsk->mm; + if (mm) { + /* Search for VMA */ + address = regs->ior; + mmap_read_lock(mm); + vma = vma_lookup(mm, address); + mmap_read_unlock(mm); + + /* + * Check if access to the VMA is okay. + * We don't allow for stack expansion. + */ + acc_type = (insn & 0x40) ? VM_WRITE : VM_READ; + if (vma + && (vma->vm_flags & acc_type) == acc_type) + val = 1; + } + } + if (treg) + regs->gr[treg] = val; + regs->gr[0] |= PSW_N; + return 1; + + case 0x300: + /* LPA instruction */ + if (insn & 0x20) { + /* Base modification */ + breg = (insn >> 21) & 0x1f; + xreg = (insn >> 16) & 0x1f; + if (breg && xreg) + regs->gr[breg] += regs->gr[xreg]; + } + treg = insn & 0x1f; + if (treg) + regs->gr[treg] = 0; + regs->gr[0] |= PSW_N; + return 1; + + default: + break; + } + + return 0; +} diff --git a/arch/parisc/mm/fixmap.c b/arch/parisc/mm/fixmap.c new file mode 100644 index 000000000..ae3493dae --- /dev/null +++ b/arch/parisc/mm/fixmap.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fixmaps for parisc + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + */ + +#include <linux/kprobes.h> +#include <linux/mm.h> +#include <asm/cacheflush.h> +#include <asm/fixmap.h> + +void notrace set_fixmap(enum fixed_addresses idx, phys_addr_t phys) +{ + unsigned long vaddr = __fix_to_virt(idx); + pgd_t *pgd = pgd_offset_k(vaddr); + p4d_t *p4d = p4d_offset(pgd, vaddr); + pud_t *pud = pud_offset(p4d, vaddr); + pmd_t *pmd = pmd_offset(pud, vaddr); + pte_t *pte; + + pte = pte_offset_kernel(pmd, vaddr); + set_pte_at(&init_mm, vaddr, pte, __mk_pte(phys, PAGE_KERNEL_RWX)); + flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE); +} + +void notrace clear_fixmap(enum fixed_addresses idx) +{ + unsigned long vaddr = __fix_to_virt(idx); + pte_t *pte = virt_to_kpte(vaddr); + + if (WARN_ON(pte_none(*pte))) + return; + + pte_clear(&init_mm, vaddr, pte); + + flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE); +} diff --git a/arch/parisc/mm/hugetlbpage.c b/arch/parisc/mm/hugetlbpage.c new file mode 100644 index 000000000..a94fe546d --- /dev/null +++ b/arch/parisc/mm/hugetlbpage.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PARISC64 Huge TLB page support. + * + * This parisc implementation is heavily based on the SPARC and x86 code. + * + * Copyright (C) 2015 Helge Deller <deller@gmx.de> + */ + +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/sched/mm.h> +#include <linux/hugetlb.h> +#include <linux/pagemap.h> +#include <linux/sysctl.h> + +#include <asm/mman.h> +#include <asm/tlb.h> +#include <asm/tlbflush.h> +#include <asm/cacheflush.h> +#include <asm/mmu_context.h> + + + + +pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, + unsigned long addr, unsigned long sz) +{ + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + pte_t *pte = NULL; + + /* We must align the address, because our caller will run + * set_huge_pte_at() on whatever we return, which writes out + * all of the sub-ptes for the hugepage range. So we have + * to give it the first such sub-pte. + */ + addr &= HPAGE_MASK; + + pgd = pgd_offset(mm, addr); + p4d = p4d_offset(pgd, addr); + pud = pud_alloc(mm, p4d, addr); + if (pud) { + pmd = pmd_alloc(mm, pud, addr); + if (pmd) + pte = pte_alloc_huge(mm, pmd, addr); + } + return pte; +} + +pte_t *huge_pte_offset(struct mm_struct *mm, + unsigned long addr, unsigned long sz) +{ + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + pte_t *pte = NULL; + + addr &= HPAGE_MASK; + + pgd = pgd_offset(mm, addr); + 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)) + pte = pte_offset_huge(pmd, addr); + } + } + } + return pte; +} + +/* Purge data and instruction TLB entries. Must be called holding + * the pa_tlb_lock. The TLB purge instructions are slow on SMP + * machines since the purge must be broadcast to all CPUs. + */ +static inline void purge_tlb_entries_huge(struct mm_struct *mm, unsigned long addr) +{ + int i; + + /* We may use multiple physical huge pages (e.g. 2x1 MB) to emulate + * Linux standard huge pages (e.g. 2 MB) */ + BUILD_BUG_ON(REAL_HPAGE_SHIFT > HPAGE_SHIFT); + + addr &= HPAGE_MASK; + addr |= _HUGE_PAGE_SIZE_ENCODING_DEFAULT; + + for (i = 0; i < (1 << (HPAGE_SHIFT-REAL_HPAGE_SHIFT)); i++) { + purge_tlb_entries(mm, addr); + addr += (1UL << REAL_HPAGE_SHIFT); + } +} + +/* __set_huge_pte_at() must be called holding the pa_tlb_lock. */ +static void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t entry) +{ + unsigned long addr_start; + int i; + + addr &= HPAGE_MASK; + addr_start = addr; + + for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { + set_pte(ptep, entry); + ptep++; + + addr += PAGE_SIZE; + pte_val(entry) += PAGE_SIZE; + } + + purge_tlb_entries_huge(mm, addr_start); +} + +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t entry, unsigned long sz) +{ + __set_huge_pte_at(mm, addr, ptep, entry); +} + + +pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long sz) +{ + pte_t entry; + + entry = *ptep; + __set_huge_pte_at(mm, addr, ptep, __pte(0)); + + return entry; +} + + +void huge_ptep_set_wrprotect(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + pte_t old_pte; + + old_pte = *ptep; + __set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); +} + +int huge_ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + pte_t pte, int dirty) +{ + int changed; + struct mm_struct *mm = vma->vm_mm; + + changed = !pte_same(*ptep, pte); + if (changed) { + __set_huge_pte_at(mm, addr, ptep, pte); + } + return changed; +} diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c new file mode 100644 index 000000000..be3380c9b --- /dev/null +++ b/arch/parisc/mm/init.c @@ -0,0 +1,994 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/parisc/mm/init.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright 1999 SuSE GmbH + * changed by Philipp Rumpf + * Copyright 1999 Philipp Rumpf (prumpf@tux.org) + * Copyright 2004 Randolph Chung (tausq@debian.org) + * Copyright 2006-2007 Helge Deller (deller@gmx.de) + * + */ + + +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/memblock.h> +#include <linux/gfp.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/initrd.h> +#include <linux/swap.h> +#include <linux/unistd.h> +#include <linux/nodemask.h> /* for node_online_map */ +#include <linux/pagemap.h> /* for release_pages */ +#include <linux/compat.h> +#include <linux/execmem.h> + +#include <asm/pgalloc.h> +#include <asm/tlb.h> +#include <asm/pdc_chassis.h> +#include <asm/mmzone.h> +#include <asm/sections.h> +#include <asm/msgbuf.h> +#include <asm/sparsemem.h> +#include <asm/asm-offsets.h> +#include <asm/shmbuf.h> + +extern int data_start; +extern void parisc_kernel_start(void); /* Kernel entry point in head.S */ + +#if CONFIG_PGTABLE_LEVELS == 3 +pmd_t pmd0[PTRS_PER_PMD] __section(".data..vm0.pmd") __attribute__ ((aligned(PAGE_SIZE))); +#endif + +pgd_t swapper_pg_dir[PTRS_PER_PGD] __section(".data..vm0.pgd") __attribute__ ((aligned(PAGE_SIZE))); +pte_t pg0[PT_INITIAL * PTRS_PER_PTE] __section(".data..vm0.pte") __attribute__ ((aligned(PAGE_SIZE))); + +static struct resource data_resource = { + .name = "Kernel data", + .flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM, +}; + +static struct resource code_resource = { + .name = "Kernel code", + .flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM, +}; + +static struct resource pdcdata_resource = { + .name = "PDC data (Page Zero)", + .start = 0, + .end = 0x9ff, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM, +}; + +static struct resource sysram_resources[MAX_PHYSMEM_RANGES] __ro_after_init; + +/* The following array is initialized from the firmware specific + * information retrieved in kernel/inventory.c. + */ + +physmem_range_t pmem_ranges[MAX_PHYSMEM_RANGES] __initdata; +int npmem_ranges __initdata; + +#ifdef CONFIG_64BIT +#define MAX_MEM (1UL << MAX_PHYSMEM_BITS) +#else /* !CONFIG_64BIT */ +#define MAX_MEM (3584U*1024U*1024U) +#endif /* !CONFIG_64BIT */ + +static unsigned long mem_limit __read_mostly = MAX_MEM; + +static void __init mem_limit_func(void) +{ + char *cp, *end; + unsigned long limit; + + /* We need this before __setup() functions are called */ + + limit = MAX_MEM; + for (cp = boot_command_line; *cp; ) { + if (memcmp(cp, "mem=", 4) == 0) { + cp += 4; + limit = memparse(cp, &end); + if (end != cp) + break; + cp = end; + } else { + while (*cp != ' ' && *cp) + ++cp; + while (*cp == ' ') + ++cp; + } + } + + if (limit < mem_limit) + mem_limit = limit; +} + +#define MAX_GAP (0x40000000UL >> PAGE_SHIFT) + +static void __init setup_bootmem(void) +{ + unsigned long mem_max; +#ifndef CONFIG_SPARSEMEM + physmem_range_t pmem_holes[MAX_PHYSMEM_RANGES - 1]; + int npmem_holes; +#endif + int i, sysram_resource_count; + + disable_sr_hashing(); /* Turn off space register hashing */ + + /* + * Sort the ranges. Since the number of ranges is typically + * small, and performance is not an issue here, just do + * a simple insertion sort. + */ + + for (i = 1; i < npmem_ranges; i++) { + int j; + + for (j = i; j > 0; j--) { + if (pmem_ranges[j-1].start_pfn < + pmem_ranges[j].start_pfn) { + + break; + } + swap(pmem_ranges[j-1], pmem_ranges[j]); + } + } + +#ifndef CONFIG_SPARSEMEM + /* + * Throw out ranges that are too far apart (controlled by + * MAX_GAP). + */ + + for (i = 1; i < npmem_ranges; i++) { + if (pmem_ranges[i].start_pfn - + (pmem_ranges[i-1].start_pfn + + pmem_ranges[i-1].pages) > MAX_GAP) { + npmem_ranges = i; + printk("Large gap in memory detected (%ld pages). " + "Consider turning on CONFIG_SPARSEMEM\n", + pmem_ranges[i].start_pfn - + (pmem_ranges[i-1].start_pfn + + pmem_ranges[i-1].pages)); + break; + } + } +#endif + + /* Print the memory ranges */ + pr_info("Memory Ranges:\n"); + + for (i = 0; i < npmem_ranges; i++) { + struct resource *res = &sysram_resources[i]; + unsigned long start; + unsigned long size; + + size = (pmem_ranges[i].pages << PAGE_SHIFT); + start = (pmem_ranges[i].start_pfn << PAGE_SHIFT); + pr_info("%2d) Start 0x%016lx End 0x%016lx Size %6ld MB\n", + i, start, start + (size - 1), size >> 20); + + /* request memory resource */ + res->name = "System RAM"; + res->start = start; + res->end = start + size - 1; + res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; + request_resource(&iomem_resource, res); + } + + sysram_resource_count = npmem_ranges; + + /* + * For 32 bit kernels we limit the amount of memory we can + * support, in order to preserve enough kernel address space + * for other purposes. For 64 bit kernels we don't normally + * limit the memory, but this mechanism can be used to + * artificially limit the amount of memory (and it is written + * to work with multiple memory ranges). + */ + + mem_limit_func(); /* check for "mem=" argument */ + + mem_max = 0; + for (i = 0; i < npmem_ranges; i++) { + unsigned long rsize; + + rsize = pmem_ranges[i].pages << PAGE_SHIFT; + if ((mem_max + rsize) > mem_limit) { + printk(KERN_WARNING "Memory truncated to %ld MB\n", mem_limit >> 20); + if (mem_max == mem_limit) + npmem_ranges = i; + else { + pmem_ranges[i].pages = (mem_limit >> PAGE_SHIFT) + - (mem_max >> PAGE_SHIFT); + npmem_ranges = i + 1; + mem_max = mem_limit; + } + break; + } + mem_max += rsize; + } + + printk(KERN_INFO "Total Memory: %ld MB\n",mem_max >> 20); + +#ifndef CONFIG_SPARSEMEM + /* Merge the ranges, keeping track of the holes */ + { + unsigned long end_pfn; + unsigned long hole_pages; + + npmem_holes = 0; + end_pfn = pmem_ranges[0].start_pfn + pmem_ranges[0].pages; + for (i = 1; i < npmem_ranges; i++) { + + hole_pages = pmem_ranges[i].start_pfn - end_pfn; + if (hole_pages) { + pmem_holes[npmem_holes].start_pfn = end_pfn; + pmem_holes[npmem_holes++].pages = hole_pages; + end_pfn += hole_pages; + } + end_pfn += pmem_ranges[i].pages; + } + + pmem_ranges[0].pages = end_pfn - pmem_ranges[0].start_pfn; + npmem_ranges = 1; + } +#endif + + /* + * Initialize and free the full range of memory in each range. + */ + + max_pfn = 0; + for (i = 0; i < npmem_ranges; i++) { + unsigned long start_pfn; + unsigned long npages; + unsigned long start; + unsigned long size; + + start_pfn = pmem_ranges[i].start_pfn; + npages = pmem_ranges[i].pages; + + start = start_pfn << PAGE_SHIFT; + size = npages << PAGE_SHIFT; + + /* add system RAM memblock */ + memblock_add(start, size); + + if ((start_pfn + npages) > max_pfn) + max_pfn = start_pfn + npages; + } + + /* + * We can't use memblock top-down allocations because we only + * created the initial mapping up to KERNEL_INITIAL_SIZE in + * the assembly bootup code. + */ + memblock_set_bottom_up(true); + + /* IOMMU is always used to access "high mem" on those boxes + * that can support enough mem that a PCI device couldn't + * directly DMA to any physical addresses. + * ISA DMA support will need to revisit this. + */ + max_low_pfn = max_pfn; + + /* reserve PAGE0 pdc memory, kernel text/data/bss & bootmap */ + +#define PDC_CONSOLE_IO_IODC_SIZE 32768 + + memblock_reserve(0UL, (unsigned long)(PAGE0->mem_free + + PDC_CONSOLE_IO_IODC_SIZE)); + memblock_reserve(__pa(KERNEL_BINARY_TEXT_START), + (unsigned long)(_end - KERNEL_BINARY_TEXT_START)); + +#ifndef CONFIG_SPARSEMEM + + /* reserve the holes */ + + for (i = 0; i < npmem_holes; i++) { + memblock_reserve((pmem_holes[i].start_pfn << PAGE_SHIFT), + (pmem_holes[i].pages << PAGE_SHIFT)); + } +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) { + printk(KERN_INFO "initrd: %08lx-%08lx\n", initrd_start, initrd_end); + if (__pa(initrd_start) < mem_max) { + unsigned long initrd_reserve; + + if (__pa(initrd_end) > mem_max) { + initrd_reserve = mem_max - __pa(initrd_start); + } else { + initrd_reserve = initrd_end - initrd_start; + } + initrd_below_start_ok = 1; + printk(KERN_INFO "initrd: reserving %08lx-%08lx (mem_max %08lx)\n", __pa(initrd_start), __pa(initrd_start) + initrd_reserve, mem_max); + + memblock_reserve(__pa(initrd_start), initrd_reserve); + } + } +#endif + + data_resource.start = virt_to_phys(&data_start); + data_resource.end = virt_to_phys(_end) - 1; + code_resource.start = virt_to_phys(_text); + code_resource.end = virt_to_phys(&data_start)-1; + + /* We don't know which region the kernel will be in, so try + * all of them. + */ + for (i = 0; i < sysram_resource_count; i++) { + struct resource *res = &sysram_resources[i]; + request_resource(res, &code_resource); + request_resource(res, &data_resource); + } + request_resource(&sysram_resources[0], &pdcdata_resource); + + /* Initialize Page Deallocation Table (PDT) and check for bad memory. */ + pdc_pdt_init(); + + memblock_allow_resize(); + memblock_dump_all(); +} + +static bool kernel_set_to_readonly; + +static void __ref map_pages(unsigned long start_vaddr, + unsigned long start_paddr, unsigned long size, + pgprot_t pgprot, int force) +{ + pmd_t *pmd; + pte_t *pg_table; + unsigned long end_paddr; + unsigned long start_pmd; + unsigned long start_pte; + unsigned long tmp1; + unsigned long tmp2; + unsigned long address; + unsigned long vaddr; + unsigned long ro_start; + unsigned long ro_end; + unsigned long kernel_start, kernel_end; + + ro_start = __pa((unsigned long)_text); + ro_end = __pa((unsigned long)&data_start); + kernel_start = __pa((unsigned long)&__init_begin); + kernel_end = __pa((unsigned long)&_end); + + end_paddr = start_paddr + size; + + /* for 2-level configuration PTRS_PER_PMD is 0 so start_pmd will be 0 */ + start_pmd = ((start_vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1)); + start_pte = ((start_vaddr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)); + + address = start_paddr; + vaddr = start_vaddr; + while (address < end_paddr) { + pgd_t *pgd = pgd_offset_k(vaddr); + p4d_t *p4d = p4d_offset(pgd, vaddr); + pud_t *pud = pud_offset(p4d, vaddr); + +#if CONFIG_PGTABLE_LEVELS == 3 + if (pud_none(*pud)) { + pmd = memblock_alloc_or_panic(PAGE_SIZE << PMD_TABLE_ORDER, + PAGE_SIZE << PMD_TABLE_ORDER); + pud_populate(NULL, pud, pmd); + } +#endif + + pmd = pmd_offset(pud, vaddr); + for (tmp1 = start_pmd; tmp1 < PTRS_PER_PMD; tmp1++, pmd++) { + if (pmd_none(*pmd)) { + pg_table = memblock_alloc_or_panic(PAGE_SIZE, PAGE_SIZE); + pmd_populate_kernel(NULL, pmd, pg_table); + } + + pg_table = pte_offset_kernel(pmd, vaddr); + for (tmp2 = start_pte; tmp2 < PTRS_PER_PTE; tmp2++, pg_table++) { + pte_t pte; + pgprot_t prot; + bool huge = false; + + if (force) { + prot = pgprot; + } else if (address < kernel_start || address >= kernel_end) { + /* outside kernel memory */ + prot = PAGE_KERNEL; + } else if (!kernel_set_to_readonly) { + /* still initializing, allow writing to RO memory */ + prot = PAGE_KERNEL_RWX; + huge = true; + } else if (address >= ro_start) { + /* Code (ro) and Data areas */ + prot = (address < ro_end) ? + PAGE_KERNEL_EXEC : PAGE_KERNEL; + huge = true; + } else { + prot = PAGE_KERNEL; + } + + pte = __mk_pte(address, prot); + if (huge) + pte = pte_mkhuge(pte); + + if (address >= end_paddr) + break; + + set_pte(pg_table, pte); + + address += PAGE_SIZE; + vaddr += PAGE_SIZE; + } + start_pte = 0; + + if (address >= end_paddr) + break; + } + start_pmd = 0; + } +} + +void __init set_kernel_text_rw(int enable_read_write) +{ + unsigned long start = (unsigned long) __init_begin; + unsigned long end = (unsigned long) &data_start; + + map_pages(start, __pa(start), end-start, + PAGE_KERNEL_RWX, enable_read_write ? 1:0); + + /* force the kernel to see the new page table entries */ + flush_cache_all(); + flush_tlb_all(); +} + +void free_initmem(void) +{ + unsigned long init_begin = (unsigned long)__init_begin; + unsigned long init_end = (unsigned long)__init_end; + unsigned long kernel_end = (unsigned long)&_end; + + /* Remap kernel text and data, but do not touch init section yet. */ + map_pages(init_end, __pa(init_end), kernel_end - init_end, + PAGE_KERNEL, 0); + + /* The init text pages are marked R-X. We have to + * flush the icache and mark them RW- + * + * Do a dummy remap of the data section first (the data + * section is already PAGE_KERNEL) to pull in the TLB entries + * for map_kernel */ + map_pages(init_begin, __pa(init_begin), init_end - init_begin, + PAGE_KERNEL_RWX, 1); + /* now remap at PAGE_KERNEL since the TLB is pre-primed to execute + * map_pages */ + map_pages(init_begin, __pa(init_begin), init_end - init_begin, + PAGE_KERNEL, 1); + + /* force the kernel to see the new TLB entries */ + __flush_tlb_range(0, init_begin, kernel_end); + + /* finally dump all the instructions which were cached, since the + * pages are no-longer executable */ + flush_icache_range(init_begin, init_end); + + free_initmem_default(POISON_FREE_INITMEM); + + /* set up a new led state on systems shipped LED State panel */ + pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BCOMPLETE); +} + + +#ifdef CONFIG_STRICT_KERNEL_RWX +void mark_rodata_ro(void) +{ + unsigned long start = (unsigned long) &__start_rodata; + unsigned long end = (unsigned long) &__end_rodata; + + pr_info("Write protecting the kernel read-only data: %luk\n", + (end - start) >> 10); + + kernel_set_to_readonly = true; + map_pages(start, __pa(start), end - start, PAGE_KERNEL, 0); + + /* force the kernel to see the new page table entries */ + flush_cache_all(); + flush_tlb_all(); +} +#endif + + +/* + * Just an arbitrary offset to serve as a "hole" between mapping areas + * (between top of physical memory and a potential pcxl dma mapping + * area, and below the vmalloc mapping area). + * + * The current 32K value just means that there will be a 32K "hole" + * between mapping areas. That means that any out-of-bounds memory + * accesses will hopefully be caught. The vmalloc() routines leaves + * a hole of 4kB between each vmalloced area for the same reason. + */ + + /* Leave room for gateway page expansion */ +#if KERNEL_MAP_START < GATEWAY_PAGE_SIZE +#error KERNEL_MAP_START is in gateway reserved region +#endif +#define MAP_START (KERNEL_MAP_START) + +#define VM_MAP_OFFSET (32*1024) +#define SET_MAP_OFFSET(x) ((void *)(((unsigned long)(x) + VM_MAP_OFFSET) \ + & ~(VM_MAP_OFFSET-1))) + +void *parisc_vmalloc_start __ro_after_init; +EXPORT_SYMBOL(parisc_vmalloc_start); + +void __init mem_init(void) +{ + /* Do sanity checks on IPC (compat) structures */ + BUILD_BUG_ON(sizeof(struct ipc64_perm) != 48); +#ifndef CONFIG_64BIT + BUILD_BUG_ON(sizeof(struct semid64_ds) != 80); + BUILD_BUG_ON(sizeof(struct msqid64_ds) != 104); + BUILD_BUG_ON(sizeof(struct shmid64_ds) != 104); +#endif +#ifdef CONFIG_COMPAT + BUILD_BUG_ON(sizeof(struct compat_ipc64_perm) != sizeof(struct ipc64_perm)); + BUILD_BUG_ON(sizeof(struct compat_semid64_ds) != 80); + BUILD_BUG_ON(sizeof(struct compat_msqid64_ds) != 104); + BUILD_BUG_ON(sizeof(struct compat_shmid64_ds) != 104); +#endif + + /* Do sanity checks on page table constants */ + BUILD_BUG_ON(PTE_ENTRY_SIZE != sizeof(pte_t)); + BUILD_BUG_ON(PMD_ENTRY_SIZE != sizeof(pmd_t)); + BUILD_BUG_ON(PGD_ENTRY_SIZE != sizeof(pgd_t)); + BUILD_BUG_ON(PAGE_SHIFT + BITS_PER_PTE + BITS_PER_PMD + BITS_PER_PGD + > BITS_PER_LONG); +#if CONFIG_PGTABLE_LEVELS == 3 + BUILD_BUG_ON(PT_INITIAL > PTRS_PER_PMD); +#else + BUILD_BUG_ON(PT_INITIAL > PTRS_PER_PGD); +#endif + +#ifdef CONFIG_64BIT + /* avoid ldil_%L() asm statements to sign-extend into upper 32-bits */ + BUILD_BUG_ON(__PAGE_OFFSET >= 0x80000000); + BUILD_BUG_ON(TMPALIAS_MAP_START >= 0x80000000); +#endif + +#ifdef CONFIG_PA11 + if (boot_cpu_data.cpu_type == pcxl2 || boot_cpu_data.cpu_type == pcxl) { + pcxl_dma_start = (unsigned long)SET_MAP_OFFSET(MAP_START); + parisc_vmalloc_start = SET_MAP_OFFSET(pcxl_dma_start + + PCXL_DMA_MAP_SIZE); + } else +#endif + parisc_vmalloc_start = SET_MAP_OFFSET(MAP_START); + +#if 0 + /* + * Do not expose the virtual kernel memory layout to userspace. + * But keep code for debugging purposes. + */ + printk("virtual kernel memory layout:\n" + " vmalloc : 0x%px - 0x%px (%4ld MB)\n" + " fixmap : 0x%px - 0x%px (%4ld kB)\n" + " memory : 0x%px - 0x%px (%4ld MB)\n" + " .init : 0x%px - 0x%px (%4ld kB)\n" + " .data : 0x%px - 0x%px (%4ld kB)\n" + " .text : 0x%px - 0x%px (%4ld kB)\n", + + (void*)VMALLOC_START, (void*)VMALLOC_END, + (VMALLOC_END - VMALLOC_START) >> 20, + + (void *)FIXMAP_START, (void *)(FIXMAP_START + FIXMAP_SIZE), + (unsigned long)(FIXMAP_SIZE / 1024), + + __va(0), high_memory, + ((unsigned long)high_memory - (unsigned long)__va(0)) >> 20, + + __init_begin, __init_end, + ((unsigned long)__init_end - (unsigned long)__init_begin) >> 10, + + _etext, _edata, + ((unsigned long)_edata - (unsigned long)_etext) >> 10, + + _text, _etext, + ((unsigned long)_etext - (unsigned long)_text) >> 10); +#endif +} + +/* + * pagetable_init() sets up the page tables + * + * Note that gateway_init() places the Linux gateway page at page 0. + * Since gateway pages cannot be dereferenced this has the desirable + * side effect of trapping those pesky NULL-reference errors in the + * kernel. + */ +static void __init pagetable_init(void) +{ + int range; + + /* Map each physical memory range to its kernel vaddr */ + + for (range = 0; range < npmem_ranges; range++) { + unsigned long start_paddr; + unsigned long size; + + start_paddr = pmem_ranges[range].start_pfn << PAGE_SHIFT; + size = pmem_ranges[range].pages << PAGE_SHIFT; + + map_pages((unsigned long)__va(start_paddr), start_paddr, + size, PAGE_KERNEL, 0); + } + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_end && initrd_end > mem_limit) { + printk(KERN_INFO "initrd: mapping %08lx-%08lx\n", initrd_start, initrd_end); + map_pages(initrd_start, __pa(initrd_start), + initrd_end - initrd_start, PAGE_KERNEL, 0); + } +#endif +} + +static void __init gateway_init(void) +{ + unsigned long linux_gateway_page_addr; + /* FIXME: This is 'const' in order to trick the compiler + into not treating it as DP-relative data. */ + extern void * const linux_gateway_page; + + linux_gateway_page_addr = LINUX_GATEWAY_ADDR & PAGE_MASK; + + /* + * Setup Linux Gateway page. + * + * The Linux gateway page will reside in kernel space (on virtual + * page 0), so it doesn't need to be aliased into user space. + */ + + map_pages(linux_gateway_page_addr, __pa(&linux_gateway_page), + PAGE_SIZE, PAGE_GATEWAY, 1); +} + +static void __init fixmap_init(void) +{ + unsigned long addr = FIXMAP_START; + unsigned long end = FIXMAP_START + FIXMAP_SIZE; + pgd_t *pgd = pgd_offset_k(addr); + p4d_t *p4d = p4d_offset(pgd, addr); + pud_t *pud = pud_offset(p4d, addr); + pmd_t *pmd; + + BUILD_BUG_ON(FIXMAP_SIZE > PMD_SIZE); + +#if CONFIG_PGTABLE_LEVELS == 3 + if (pud_none(*pud)) { + pmd = memblock_alloc_or_panic(PAGE_SIZE << PMD_TABLE_ORDER, + PAGE_SIZE << PMD_TABLE_ORDER); + pud_populate(NULL, pud, pmd); + } +#endif + + pmd = pmd_offset(pud, addr); + do { + pte_t *pte = memblock_alloc_or_panic(PAGE_SIZE, PAGE_SIZE); + + pmd_populate_kernel(&init_mm, pmd, pte); + + addr += PAGE_SIZE; + } while (addr < end); +} + +void __init arch_zone_limits_init(unsigned long *max_zone_pfns) +{ + max_zone_pfns[ZONE_NORMAL] = PFN_DOWN(memblock_end_of_DRAM()); +} + +void __init paging_init(void) +{ + setup_bootmem(); + pagetable_init(); + gateway_init(); + fixmap_init(); + flush_cache_all_local(); /* start with known state */ + flush_tlb_all_local(NULL); +} + +static void alloc_btlb(unsigned long start, unsigned long end, int *slot, + unsigned long entry_info) +{ + const int slot_max = btlb_info.fixed_range_info.num_comb; + int min_num_pages = btlb_info.min_size; + unsigned long size; + + /* map at minimum 4 pages */ + if (min_num_pages < 4) + min_num_pages = 4; + + size = HUGEPAGE_SIZE; + while (start < end && *slot < slot_max && size >= PAGE_SIZE) { + /* starting address must have same alignment as size! */ + /* if correctly aligned and fits in double size, increase */ + if (((start & (2 * size - 1)) == 0) && + (end - start) >= (2 * size)) { + size <<= 1; + continue; + } + /* if current size alignment is too big, try smaller size */ + if ((start & (size - 1)) != 0) { + size >>= 1; + continue; + } + if ((end - start) >= size) { + if ((size >> PAGE_SHIFT) >= min_num_pages) + pdc_btlb_insert(start >> PAGE_SHIFT, __pa(start) >> PAGE_SHIFT, + size >> PAGE_SHIFT, entry_info, *slot); + (*slot)++; + start += size; + continue; + } + size /= 2; + continue; + } +} + +void btlb_init_per_cpu(void) +{ + unsigned long s, t, e; + int slot; + + /* BTLBs are not available on 64-bit CPUs */ + if (IS_ENABLED(CONFIG_PA20)) + return; + else if (pdc_btlb_info(&btlb_info) < 0) { + memset(&btlb_info, 0, sizeof btlb_info); + } + + /* insert BLTLBs for code and data segments */ + s = (uintptr_t) dereference_function_descriptor(&_stext); + e = (uintptr_t) dereference_function_descriptor(&_etext); + t = (uintptr_t) dereference_function_descriptor(&_sdata); + BUG_ON(t != e); + + /* code segments */ + slot = 0; + alloc_btlb(s, e, &slot, 0x13800000); + + /* sanity check */ + t = (uintptr_t) dereference_function_descriptor(&_edata); + e = (uintptr_t) dereference_function_descriptor(&__bss_start); + BUG_ON(t != e); + + /* data segments */ + s = (uintptr_t) dereference_function_descriptor(&_sdata); + e = (uintptr_t) dereference_function_descriptor(&__bss_stop); + alloc_btlb(s, e, &slot, 0x11800000); +} + +#ifdef CONFIG_PA20 + +/* + * Currently, all PA20 chips have 18 bit protection IDs, which is the + * limiting factor (space ids are 32 bits). + */ + +#define NR_SPACE_IDS 262144 + +#else + +/* + * Currently we have a one-to-one relationship between space IDs and + * protection IDs. Older parisc chips (PCXS, PCXT, PCXL, PCXL2) only + * support 15 bit protection IDs, so that is the limiting factor. + * PCXT' has 18 bit protection IDs, but only 16 bit spaceids, so it's + * probably not worth the effort for a special case here. + */ + +#define NR_SPACE_IDS 32768 + +#endif /* !CONFIG_PA20 */ + +#define RECYCLE_THRESHOLD (NR_SPACE_IDS / 2) +#define SID_ARRAY_SIZE (NR_SPACE_IDS / (8 * sizeof(long))) + +static unsigned long space_id[SID_ARRAY_SIZE] = { 1 }; /* disallow space 0 */ +static unsigned long dirty_space_id[SID_ARRAY_SIZE]; +static unsigned long space_id_index; +static unsigned long free_space_ids = NR_SPACE_IDS - 1; +static unsigned long dirty_space_ids; + +static DEFINE_SPINLOCK(sid_lock); + +unsigned long alloc_sid(void) +{ + unsigned long index; + + spin_lock(&sid_lock); + + if (free_space_ids == 0) { + if (dirty_space_ids != 0) { + spin_unlock(&sid_lock); + flush_tlb_all(); /* flush_tlb_all() calls recycle_sids() */ + spin_lock(&sid_lock); + } + BUG_ON(free_space_ids == 0); + } + + free_space_ids--; + + index = find_next_zero_bit(space_id, NR_SPACE_IDS, space_id_index); + space_id[BIT_WORD(index)] |= BIT_MASK(index); + space_id_index = index; + + spin_unlock(&sid_lock); + + return index << SPACEID_SHIFT; +} + +void free_sid(unsigned long spaceid) +{ + unsigned long index = spaceid >> SPACEID_SHIFT; + unsigned long *dirty_space_offset, mask; + + dirty_space_offset = &dirty_space_id[BIT_WORD(index)]; + mask = BIT_MASK(index); + + spin_lock(&sid_lock); + + BUG_ON(*dirty_space_offset & mask); /* attempt to free space id twice */ + + *dirty_space_offset |= mask; + dirty_space_ids++; + + spin_unlock(&sid_lock); +} + + +#ifdef CONFIG_SMP +static void get_dirty_sids(unsigned long *ndirtyptr,unsigned long *dirty_array) +{ + int i; + + /* NOTE: sid_lock must be held upon entry */ + + *ndirtyptr = dirty_space_ids; + if (dirty_space_ids != 0) { + for (i = 0; i < SID_ARRAY_SIZE; i++) { + dirty_array[i] = dirty_space_id[i]; + dirty_space_id[i] = 0; + } + dirty_space_ids = 0; + } + + return; +} + +static void recycle_sids(unsigned long ndirty,unsigned long *dirty_array) +{ + int i; + + /* NOTE: sid_lock must be held upon entry */ + + if (ndirty != 0) { + for (i = 0; i < SID_ARRAY_SIZE; i++) { + space_id[i] ^= dirty_array[i]; + } + + free_space_ids += ndirty; + space_id_index = 0; + } +} + +#else /* CONFIG_SMP */ + +static void recycle_sids(void) +{ + int i; + + /* NOTE: sid_lock must be held upon entry */ + + if (dirty_space_ids != 0) { + for (i = 0; i < SID_ARRAY_SIZE; i++) { + space_id[i] ^= dirty_space_id[i]; + dirty_space_id[i] = 0; + } + + free_space_ids += dirty_space_ids; + dirty_space_ids = 0; + space_id_index = 0; + } +} +#endif + +/* + * flush_tlb_all() calls recycle_sids(), since whenever the entire tlb is + * purged, we can safely reuse the space ids that were released but + * not flushed from the tlb. + */ + +#ifdef CONFIG_SMP + +static unsigned long recycle_ndirty; +static unsigned long recycle_dirty_array[SID_ARRAY_SIZE]; +static unsigned int recycle_inuse; + +void flush_tlb_all(void) +{ + int do_recycle; + + do_recycle = 0; + spin_lock(&sid_lock); + __inc_irq_stat(irq_tlb_count); + if (dirty_space_ids > RECYCLE_THRESHOLD) { + BUG_ON(recycle_inuse); /* FIXME: Use a semaphore/wait queue here */ + get_dirty_sids(&recycle_ndirty,recycle_dirty_array); + recycle_inuse++; + do_recycle++; + } + spin_unlock(&sid_lock); + on_each_cpu(flush_tlb_all_local, NULL, 1); + if (do_recycle) { + spin_lock(&sid_lock); + recycle_sids(recycle_ndirty,recycle_dirty_array); + recycle_inuse = 0; + spin_unlock(&sid_lock); + } +} +#else +void flush_tlb_all(void) +{ + spin_lock(&sid_lock); + __inc_irq_stat(irq_tlb_count); + flush_tlb_all_local(NULL); + recycle_sids(); + spin_unlock(&sid_lock); +} +#endif + +static const pgprot_t protection_map[16] = { + [VM_NONE] = PAGE_NONE, + [VM_READ] = PAGE_READONLY, + [VM_WRITE] = PAGE_NONE, + [VM_WRITE | VM_READ] = PAGE_READONLY, + [VM_EXEC] = PAGE_EXECREAD, + [VM_EXEC | VM_READ] = PAGE_EXECREAD, + [VM_EXEC | VM_WRITE] = PAGE_EXECREAD, + [VM_EXEC | VM_WRITE | VM_READ] = PAGE_EXECREAD, + [VM_SHARED] = PAGE_NONE, + [VM_SHARED | VM_READ] = PAGE_READONLY, + [VM_SHARED | VM_WRITE] = PAGE_WRITEONLY, + [VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED, + [VM_SHARED | VM_EXEC] = PAGE_EXECREAD, + [VM_SHARED | VM_EXEC | VM_READ] = PAGE_EXECREAD, + [VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_RWX, + [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_RWX +}; +DECLARE_VM_GET_PAGE_PROT + +#ifdef CONFIG_EXECMEM +static struct execmem_info execmem_info __ro_after_init; + +struct execmem_info __init *execmem_arch_setup(void) +{ + execmem_info = (struct execmem_info){ + .ranges = { + [EXECMEM_DEFAULT] = { + .start = VMALLOC_START, + .end = VMALLOC_END, + .pgprot = PAGE_KERNEL_RWX, + .alignment = 1, + }, + }, + }; + + return &execmem_info; +} +#endif /* CONFIG_EXECMEM */ diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c new file mode 100644 index 000000000..0b65c4b3b --- /dev/null +++ b/arch/parisc/mm/ioremap.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * arch/parisc/mm/ioremap.c + * + * (C) Copyright 1995 1996 Linus Torvalds + * (C) Copyright 2001-2019 Helge Deller <deller@gmx.de> + * (C) Copyright 2005 Kyle McMartin <kyle@parisc-linux.org> + */ + +#include <linux/vmalloc.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/mm.h> + +void __iomem *ioremap_prot(phys_addr_t phys_addr, size_t size, + pgprot_t prot) +{ +#ifdef CONFIG_EISA + unsigned long end = phys_addr + size - 1; + /* Support EISA addresses */ + if ((phys_addr >= 0x00080000 && end < 0x000fffff) || + (phys_addr >= 0x00500000 && end < 0x03bfffff)) + phys_addr |= F_EXTEND(0xfc000000); +#endif + + /* + * Don't allow anybody to remap normal RAM that we're using.. + */ + if (phys_addr < virt_to_phys(high_memory)) { + char *t_addr, *t_end; + struct page *page; + + t_addr = __va(phys_addr); + t_end = t_addr + (size - 1); + + for (page = virt_to_page(t_addr); + page <= virt_to_page(t_end); page++) { + if(!PageReserved(page)) + return NULL; + } + } + + return generic_ioremap_prot(phys_addr, size, prot); +} +EXPORT_SYMBOL(ioremap_prot); diff --git a/arch/parisc/net/Makefile b/arch/parisc/net/Makefile new file mode 100644 index 000000000..22b12024d --- /dev/null +++ b/arch/parisc/net/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_BPF_JIT) += bpf_jit_core.o + +ifeq ($(CONFIG_64BIT),y) + obj-$(CONFIG_BPF_JIT) += bpf_jit_comp64.o +else + obj-$(CONFIG_BPF_JIT) += bpf_jit_comp32.o +endif diff --git a/arch/parisc/net/bpf_jit.h b/arch/parisc/net/bpf_jit.h new file mode 100644 index 000000000..8b8896959 --- /dev/null +++ b/arch/parisc/net/bpf_jit.h @@ -0,0 +1,479 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Common functionality for PARISC32 and PARISC64 BPF JIT compilers + * + * Copyright (c) 2023 Helge Deller <deller@gmx.de> + * + */ + +#ifndef _BPF_JIT_H +#define _BPF_JIT_H + +#include <linux/bpf.h> +#include <linux/filter.h> +#include <asm/cacheflush.h> + +#define HPPA_JIT_DEBUG 0 +#define HPPA_JIT_REBOOT 0 +#define HPPA_JIT_DUMP 0 + +#define OPTIMIZE_HPPA 1 /* enable some asm optimizations */ +// echo 1 > /proc/sys/net/core/bpf_jit_enable + +#define HPPA_R(nr) nr /* use HPPA register #nr */ + +enum { + HPPA_REG_ZERO = 0, /* The constant value 0 */ + HPPA_REG_R1 = 1, /* used for addil */ + HPPA_REG_RP = 2, /* Return address */ + + HPPA_REG_ARG7 = 19, /* ARG4-7 used in 64-bit ABI */ + HPPA_REG_ARG6 = 20, + HPPA_REG_ARG5 = 21, + HPPA_REG_ARG4 = 22, + + HPPA_REG_ARG3 = 23, /* ARG0-3 in 32- and 64-bit ABI */ + HPPA_REG_ARG2 = 24, + HPPA_REG_ARG1 = 25, + HPPA_REG_ARG0 = 26, + HPPA_REG_GP = 27, /* Global pointer */ + HPPA_REG_RET0 = 28, /* Return value, HI in 32-bit */ + HPPA_REG_RET1 = 29, /* Return value, LOW in 32-bit */ + HPPA_REG_SP = 30, /* Stack pointer */ + HPPA_REG_R31 = 31, + +#ifdef CONFIG_64BIT + HPPA_REG_TCC = 3, + HPPA_REG_TCC_SAVED = 4, + HPPA_REG_TCC_IN_INIT = HPPA_REG_R31, +#else + HPPA_REG_TCC = 18, + HPPA_REG_TCC_SAVED = 17, + HPPA_REG_TCC_IN_INIT = HPPA_REG_R31, +#endif + + HPPA_REG_T0 = HPPA_REG_R1, /* Temporaries */ + HPPA_REG_T1 = HPPA_REG_R31, + HPPA_REG_T2 = HPPA_REG_ARG4, +#ifndef CONFIG_64BIT + HPPA_REG_T3 = HPPA_REG_ARG5, /* not used in 64-bit */ + HPPA_REG_T4 = HPPA_REG_ARG6, + HPPA_REG_T5 = HPPA_REG_ARG7, +#endif +}; + +struct hppa_jit_context { + struct bpf_prog *prog; + u32 *insns; /* HPPA insns */ + int ninsns; + int reg_seen_collect; + int reg_seen; + int body_len; + int epilogue_offset; + int prologue_len; + int *offset; /* BPF to HPPA */ +}; + +#define REG_SET_SEEN(ctx, nr) { if (ctx->reg_seen_collect) ctx->reg_seen |= BIT(nr); } +#define REG_SET_SEEN_ALL(ctx) { if (ctx->reg_seen_collect) ctx->reg_seen = -1; } +#define REG_FORCE_SEEN(ctx, nr) { ctx->reg_seen |= BIT(nr); } +#define REG_WAS_SEEN(ctx, nr) (ctx->reg_seen & BIT(nr)) +#define REG_ALL_SEEN(ctx) (ctx->reg_seen == -1) + +#define HPPA_INSN_SIZE 4 /* bytes per HPPA asm instruction */ +#define REG_SIZE REG_SZ /* bytes per native "long" word */ + +/* subtract hppa displacement on branches which is .+8 */ +#define HPPA_BRANCH_DISPLACEMENT 2 /* instructions */ + +/* asm statement indicator to execute delay slot */ +#define EXEC_NEXT_INSTR 0 +#define NOP_NEXT_INSTR 1 + +#define im11(val) (((u32)(val)) & 0x07ff) + +#define hppa_ldil(addr, reg) \ + hppa_t5_insn(0x08, reg, ((u32)(addr)) >> 11) /* ldil im21,reg */ +#define hppa_addil(addr, reg) \ + hppa_t5_insn(0x0a, reg, ((u32)(addr)) >> 11) /* addil im21,reg -> result in gr1 */ +#define hppa_ldo(im14, reg, target) \ + hppa_t1_insn(0x0d, reg, target, im14) /* ldo val14(reg),target */ +#define hppa_ldi(im14, reg) \ + hppa_ldo(im14, HPPA_REG_ZERO, reg) /* ldi val14,reg */ +#define hppa_or(reg1, reg2, target) \ + hppa_t6_insn(0x02, reg2, reg1, 0, 0, 0x09, target) /* or reg1,reg2,target */ +#define hppa_or_cond(reg1, reg2, cond, f, target) \ + hppa_t6_insn(0x02, reg2, reg1, cond, f, 0x09, target) +#define hppa_and(reg1, reg2, target) \ + hppa_t6_insn(0x02, reg2, reg1, 0, 0, 0x08, target) /* and reg1,reg2,target */ +#define hppa_and_cond(reg1, reg2, cond, f, target) \ + hppa_t6_insn(0x02, reg2, reg1, cond, f, 0x08, target) +#define hppa_xor(reg1, reg2, target) \ + hppa_t6_insn(0x02, reg2, reg1, 0, 0, 0x0a, target) /* xor reg1,reg2,target */ +#define hppa_add(reg1, reg2, target) \ + hppa_t6_insn(0x02, reg2, reg1, 0, 0, 0x18, target) /* add reg1,reg2,target */ +#define hppa_addc(reg1, reg2, target) \ + hppa_t6_insn(0x02, reg2, reg1, 0, 0, 0x1c, target) /* add,c reg1,reg2,target */ +#define hppa_sub(reg1, reg2, target) \ + hppa_t6_insn(0x02, reg2, reg1, 0, 0, 0x10, target) /* sub reg1,reg2,target */ +#define hppa_subb(reg1, reg2, target) \ + hppa_t6_insn(0x02, reg2, reg1, 0, 0, 0x14, target) /* sub,b reg1,reg2,target */ +#define hppa_nop() \ + hppa_or(0,0,0) /* nop: or 0,0,0 */ +#define hppa_addi(val11, reg, target) \ + hppa_t7_insn(0x2d, reg, target, val11) /* addi im11,reg,target */ +#define hppa_subi(val11, reg, target) \ + hppa_t7_insn(0x25, reg, target, val11) /* subi im11,reg,target */ +#define hppa_copy(reg, target) \ + hppa_or(reg, HPPA_REG_ZERO, target) /* copy reg,target */ +#define hppa_ldw(val14, reg, target) \ + hppa_t1_insn(0x12, reg, target, val14) /* ldw im14(reg),target */ +#define hppa_ldb(val14, reg, target) \ + hppa_t1_insn(0x10, reg, target, val14) /* ldb im14(reg),target */ +#define hppa_ldh(val14, reg, target) \ + hppa_t1_insn(0x11, reg, target, val14) /* ldh im14(reg),target */ +#define hppa_stw(reg, val14, base) \ + hppa_t1_insn(0x1a, base, reg, val14) /* stw reg,im14(base) */ +#define hppa_stb(reg, val14, base) \ + hppa_t1_insn(0x18, base, reg, val14) /* stb reg,im14(base) */ +#define hppa_sth(reg, val14, base) \ + hppa_t1_insn(0x19, base, reg, val14) /* sth reg,im14(base) */ +#define hppa_stwma(reg, val14, base) \ + hppa_t1_insn(0x1b, base, reg, val14) /* stw,ma reg,im14(base) */ +#define hppa_bv(reg, base, nop) \ + hppa_t11_insn(0x3a, base, reg, 0x06, 0, nop) /* bv(,n) reg(base) */ +#define hppa_be(offset, base) \ + hppa_t12_insn(0x38, base, offset, 0x00, 1) /* be,n offset(0,base) */ +#define hppa_be_l(offset, base, nop) \ + hppa_t12_insn(0x39, base, offset, 0x00, nop) /* ble(,nop) offset(0,base) */ +#define hppa_mtctl(reg, cr) \ + hppa_t21_insn(0x00, cr, reg, 0xc2, 0) /* mtctl reg,cr */ +#define hppa_mtsar(reg) \ + hppa_mtctl(reg, 11) /* mtsar reg */ +#define hppa_zdep(r, p, len, target) \ + hppa_t10_insn(0x35, target, r, 0, 2, p, len) /* zdep r,a,b,t */ +#define hppa_shl(r, len, target) \ + hppa_zdep(r, len, len, lo(rd)) +#define hppa_depwz(r, p, len, target) \ + hppa_t10_insn(0x35, target, r, 0, 3, 31-(p), 32-(len)) /* depw,z r,p,len,ret1 */ +#define hppa_depwz_sar(reg, target) \ + hppa_t1_insn(0x35, target, reg, 0) /* depw,z reg,sar,32,target */ +#define hppa_shrpw_sar(reg, target) \ + hppa_t10_insn(0x34, reg, 0, 0, 0, 0, target) /* shrpw r0,reg,sar,target */ +#define hppa_shrpw(r1, r2, p, target) \ + hppa_t10_insn(0x34, r2, r1, 0, 2, 31-(p), target) /* shrpw r1,r2,p,target */ +#define hppa_shd(r1, r2, p, target) \ + hppa_t10_insn(0x34, r2, r1, 0, 2, 31-(p), target) /* shrpw r1,r2,p,tarfer */ +#define hppa_extrws_sar(reg, target) \ + hppa_t10_insn(0x34, reg, target, 0, 5, 0, 0) /* extrw,s reg,sar,32,ret0 */ +#define hppa_extrws(reg, p, len, target) \ + hppa_t10_insn(0x34, reg, target, 0, 7, p, len) /* extrw,s reg,p,len,target */ +#define hppa_extru(r, p, len, target) \ + hppa_t10_insn(0x34, r, target, 0, 6, p, 32-(len)) +#define hppa_shr(r, len, target) \ + hppa_extru(r, 31-(len), 32-(len), target) +#define hppa_bl(imm17, rp) \ + hppa_t12_insn(0x3a, rp, imm17, 0x00, 1) /* bl,n target_addr,rp */ +#define hppa_sh2add(r1, r2, target) \ + hppa_t6_insn(0x02, r2, r1, 0, 0, 0x1a, target) /* sh2add r1,r2,target */ + +#define hppa_combt(r1, r2, target_addr, condition, nop) \ + hppa_t11_insn(IS_ENABLED(CONFIG_64BIT) ? 0x27 : 0x20, \ + r2, r1, condition, target_addr, nop) /* combt,cond,n r1,r2,addr */ +#define hppa_beq(r1, r2, target_addr) \ + hppa_combt(r1, r2, target_addr, 1, NOP_NEXT_INSTR) +#define hppa_blt(r1, r2, target_addr) \ + hppa_combt(r1, r2, target_addr, 2, NOP_NEXT_INSTR) +#define hppa_ble(r1, r2, target_addr) \ + hppa_combt(r1, r2, target_addr, 3, NOP_NEXT_INSTR) +#define hppa_bltu(r1, r2, target_addr) \ + hppa_combt(r1, r2, target_addr, 4, NOP_NEXT_INSTR) +#define hppa_bleu(r1, r2, target_addr) \ + hppa_combt(r1, r2, target_addr, 5, NOP_NEXT_INSTR) + +#define hppa_combf(r1, r2, target_addr, condition, nop) \ + hppa_t11_insn(IS_ENABLED(CONFIG_64BIT) ? 0x2f : 0x22, \ + r2, r1, condition, target_addr, nop) /* combf,cond,n r1,r2,addr */ +#define hppa_bne(r1, r2, target_addr) \ + hppa_combf(r1, r2, target_addr, 1, NOP_NEXT_INSTR) +#define hppa_bge(r1, r2, target_addr) \ + hppa_combf(r1, r2, target_addr, 2, NOP_NEXT_INSTR) +#define hppa_bgt(r1, r2, target_addr) \ + hppa_combf(r1, r2, target_addr, 3, NOP_NEXT_INSTR) +#define hppa_bgeu(r1, r2, target_addr) \ + hppa_combf(r1, r2, target_addr, 4, NOP_NEXT_INSTR) +#define hppa_bgtu(r1, r2, target_addr) \ + hppa_combf(r1, r2, target_addr, 5, NOP_NEXT_INSTR) + +/* 64-bit instructions */ +#ifdef CONFIG_64BIT +#define hppa64_ldd_reg(reg, b, target) \ + hppa_t10_insn(0x03, b, reg, 0, 0, 3<<1, target) +#define hppa64_ldd_im5(im5, b, target) \ + hppa_t10_insn(0x03, b, low_sign_unext(im5,5), 0, 1<<2, 3<<1, target) +#define hppa64_ldd_im16(im16, b, target) \ + hppa_t10_insn(0x14, b, target, 0, 0, 0, 0) | re_assemble_16(im16) +#define hppa64_std_im5(src, im5, b) \ + hppa_t10_insn(0x03, b, src, 0, 1<<2, 0xB<<1, low_sign_unext(im5,5)) +#define hppa64_std_im16(src, im16, b) \ + hppa_t10_insn(0x1c, b, src, 0, 0, 0, 0) | re_assemble_16(im16) +#define hppa64_bl_long(offs22) \ + hppa_t12_L_insn(0x3a, offs22, 1) +#define hppa64_mtsarcm(reg) \ + hppa_t21_insn(0x00, 11, reg, 0xc6, 0) +#define hppa64_shrpd_sar(reg, target) \ + hppa_t10_insn(0x34, reg, 0, 0, 0, 1<<4, target) +#define hppa64_shladd(r1, sa, r2, target) \ + hppa_t6_insn(0x02, r2, r1, 0, 0, 1<<4|1<<3|sa, target) +#define hppa64_depdz_sar(reg, target) \ + hppa_t21_insn(0x35, target, reg, 3<<3, 0) +#define hppa_extrd_sar(reg, target, se) \ + hppa_t10_insn(0x34, reg, target, 0, 0, 0, 0) | 2<<11 | (se&1)<<10 | 1<<9 | 1<<8 +#define hppa64_bve_l_rp(base) \ + (0x3a << 26) | (base << 21) | 0xf000 +#define hppa64_permh_3210(r, target) \ + (0x3e << 26) | (r << 21) | (r << 16) | (target) | 0x00006900 +#define hppa64_hshl(r, sa, target) \ + (0x3e << 26) | (0 << 21) | (r << 16) | (sa << 6) | (target) | 0x00008800 +#define hppa64_hshr_u(r, sa, target) \ + (0x3e << 26) | (r << 21) | (0 << 16) | (sa << 6) | (target) | 0x0000c800 +#endif + +struct hppa_jit_data { + struct bpf_binary_header *header; + u8 *image; + struct hppa_jit_context ctx; +}; + +static inline void bpf_fill_ill_insns(void *area, unsigned int size) +{ + memset(area, 0, size); +} + +static inline void bpf_flush_icache(void *start, void *end) +{ + flush_icache_range((unsigned long)start, (unsigned long)end); +} + +/* Emit a 4-byte HPPA instruction. */ +static inline void emit(const u32 insn, struct hppa_jit_context *ctx) +{ + if (ctx->insns) { + ctx->insns[ctx->ninsns] = insn; + } + + ctx->ninsns++; +} + +static inline int epilogue_offset(struct hppa_jit_context *ctx) +{ + int to = ctx->epilogue_offset, from = ctx->ninsns; + + return (to - from); +} + +/* Return -1 or inverted cond. */ +static inline int invert_bpf_cond(u8 cond) +{ + switch (cond) { + case BPF_JEQ: + return BPF_JNE; + case BPF_JGT: + return BPF_JLE; + case BPF_JLT: + return BPF_JGE; + case BPF_JGE: + return BPF_JLT; + case BPF_JLE: + return BPF_JGT; + case BPF_JNE: + return BPF_JEQ; + case BPF_JSGT: + return BPF_JSLE; + case BPF_JSLT: + return BPF_JSGE; + case BPF_JSGE: + return BPF_JSLT; + case BPF_JSLE: + return BPF_JSGT; + } + return -1; +} + + +static inline signed long hppa_offset(int insn, int off, struct hppa_jit_context *ctx) +{ + signed long from, to; + + off++; /* BPF branch is from PC+1 */ + from = (insn > 0) ? ctx->offset[insn - 1] : 0; + to = (insn + off > 0) ? ctx->offset[insn + off - 1] : 0; + return (to - from); +} + +/* does the signed value fits into a given number of bits ? */ +static inline int check_bits_int(signed long val, int bits) +{ + return ((val >= 0) && ((val >> bits) == 0)) || + ((val < 0) && (((~((u32)val)) >> (bits-1)) == 0)); +} + +/* can the signed value be used in relative code ? */ +static inline int relative_bits_ok(signed long val, int bits) +{ + return ((val >= 0) && (val < (1UL << (bits-1)))) || /* XXX */ + ((val < 0) && (((~((unsigned long)val)) >> (bits-1)) == 0) + && (val & (1UL << (bits-1)))); +} + +/* can the signed value be used in relative branches ? */ +static inline int relative_branch_ok(signed long val, int bits) +{ + return ((val >= 0) && (val < (1UL << (bits-2)))) || /* XXX */ + ((val < 0) && (((~((unsigned long)val)) < (1UL << (bits-2)))) + && (val & (1UL << (bits-1)))); +} + + +#define is_5b_int(val) check_bits_int(val, 5) + +static inline unsigned sign_unext(unsigned x, unsigned len) +{ + unsigned len_ones; + + len_ones = (1 << len) - 1; + return x & len_ones; +} + +static inline unsigned low_sign_unext(unsigned x, unsigned len) +{ + unsigned temp; + unsigned sign; + + sign = (x >> (len-1)) & 1; + temp = sign_unext (x, len-1); + return (temp << 1) | sign; +} + +static inline unsigned re_assemble_12(unsigned as12) +{ + return (( (as12 & 0x800) >> 11) + | ((as12 & 0x400) >> (10 - 2)) + | ((as12 & 0x3ff) << (1 + 2))); +} + +static inline unsigned re_assemble_14(unsigned as14) +{ + return (( (as14 & 0x1fff) << 1) + | ((as14 & 0x2000) >> 13)); +} + +#ifdef CONFIG_64BIT +static inline unsigned re_assemble_16(unsigned as16) +{ + unsigned s, t; + + /* Unusual 16-bit encoding, for wide mode only. */ + t = (as16 << 1) & 0xffff; + s = (as16 & 0x8000); + return (t ^ s ^ (s >> 1)) | (s >> 15); +} +#endif + +static inline unsigned re_assemble_17(unsigned as17) +{ + return (( (as17 & 0x10000) >> 16) + | ((as17 & 0x0f800) << (16 - 11)) + | ((as17 & 0x00400) >> (10 - 2)) + | ((as17 & 0x003ff) << (1 + 2))); +} + +static inline unsigned re_assemble_21(unsigned as21) +{ + return (( (as21 & 0x100000) >> 20) + | ((as21 & 0x0ffe00) >> 8) + | ((as21 & 0x000180) << 7) + | ((as21 & 0x00007c) << 14) + | ((as21 & 0x000003) << 12)); +} + +static inline unsigned re_assemble_22(unsigned as22) +{ + return (( (as22 & 0x200000) >> 21) + | ((as22 & 0x1f0000) << (21 - 16)) + | ((as22 & 0x00f800) << (16 - 11)) + | ((as22 & 0x000400) >> (10 - 2)) + | ((as22 & 0x0003ff) << (1 + 2))); +} + +/* Various HPPA instruction formats. */ +/* see https://parisc.wiki.kernel.org/images-parisc/6/68/Pa11_acd.pdf, appendix C */ + +static inline u32 hppa_t1_insn(u8 opcode, u8 b, u8 r, s16 im14) +{ + return ((opcode << 26) | (b << 21) | (r << 16) | re_assemble_14(im14)); +} + +static inline u32 hppa_t5_insn(u8 opcode, u8 tr, u32 val21) +{ + return ((opcode << 26) | (tr << 21) | re_assemble_21(val21)); +} + +static inline u32 hppa_t6_insn(u8 opcode, u8 r2, u8 r1, u8 c, u8 f, u8 ext6, u16 t) +{ + return ((opcode << 26) | (r2 << 21) | (r1 << 16) | (c << 13) | (f << 12) | + (ext6 << 6) | t); +} + +/* 7. Arithmetic immediate */ +static inline u32 hppa_t7_insn(u8 opcode, u8 r, u8 t, u32 im11) +{ + return ((opcode << 26) | (r << 21) | (t << 16) | low_sign_unext(im11, 11)); +} + +/* 10. Shift instructions */ +static inline u32 hppa_t10_insn(u8 opcode, u8 r2, u8 r1, u8 c, u8 ext3, u8 cp, u8 t) +{ + return ((opcode << 26) | (r2 << 21) | (r1 << 16) | (c << 13) | + (ext3 << 10) | (cp << 5) | t); +} + +/* 11. Conditional branch instructions */ +static inline u32 hppa_t11_insn(u8 opcode, u8 r2, u8 r1, u8 c, u32 w, u8 nop) +{ + u32 ra = re_assemble_12(w); + // ra = low_sign_unext(w,11) | (w & (1<<10) + return ((opcode << 26) | (r2 << 21) | (r1 << 16) | (c << 13) | (nop << 1) | ra); +} + +/* 12. Branch instructions */ +static inline u32 hppa_t12_insn(u8 opcode, u8 rp, u32 w, u8 ext3, u8 nop) +{ + return ((opcode << 26) | (rp << 21) | (ext3 << 13) | (nop << 1) | re_assemble_17(w)); +} + +static inline u32 hppa_t12_L_insn(u8 opcode, u32 w, u8 nop) +{ + return ((opcode << 26) | (0x05 << 13) | (nop << 1) | re_assemble_22(w)); +} + +/* 21. Move to control register */ +static inline u32 hppa_t21_insn(u8 opcode, u8 r2, u8 r1, u8 ext8, u8 t) +{ + return ((opcode << 26) | (r2 << 21) | (r1 << 16) | (ext8 << 5) | t); +} + +/* Helper functions called by jit code on HPPA32 and HPPA64. */ + +u64 hppa_div64(u64 div, u64 divisor); +u64 hppa_div64_rem(u64 div, u64 divisor); + +/* Helper functions that emit HPPA instructions when possible. */ + +void bpf_jit_build_prologue(struct hppa_jit_context *ctx); +void bpf_jit_build_epilogue(struct hppa_jit_context *ctx); + +int bpf_jit_emit_insn(const struct bpf_insn *insn, struct hppa_jit_context *ctx, + bool extra_pass); + +#endif /* _BPF_JIT_H */ diff --git a/arch/parisc/net/bpf_jit_comp32.c b/arch/parisc/net/bpf_jit_comp32.c new file mode 100644 index 000000000..5ff0cf925 --- /dev/null +++ b/arch/parisc/net/bpf_jit_comp32.c @@ -0,0 +1,1615 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BPF JIT compiler for PA-RISC (32-bit) + * + * Copyright (c) 2023 Helge Deller <deller@gmx.de> + * + * The code is based on the BPF JIT compiler for RV64 by Björn Töpel and + * the BPF JIT compiler for 32-bit ARM by Shubham Bansal and Mircea Gherzan. + */ + +#include <linux/bpf.h> +#include <linux/filter.h> +#include <linux/libgcc.h> +#include "bpf_jit.h" + +/* + * Stack layout during BPF program execution (note: stack grows up): + * + * high + * HPPA32 sp => +----------+ <= HPPA32 fp + * | saved sp | + * | saved rp | + * | ... | HPPA32 callee-saved registers + * | curr args| + * | local var| + * +----------+ <= (sp - 4 * NR_SAVED_REGISTERS) + * | lo(R9) | + * | hi(R9) | + * | lo(FP) | JIT scratch space for BPF registers + * | hi(FP) | + * | ... | + * +----------+ <= (sp - 4 * NR_SAVED_REGISTERS + * | | - 4 * BPF_JIT_SCRATCH_REGS) + * | | + * | ... | BPF program stack + * | | + * | ... | Function call stack + * | | + * +----------+ + * low + */ + +enum { + /* Stack layout - these are offsets from top of JIT scratch space. */ + BPF_R8_HI, + BPF_R8_LO, + BPF_R9_HI, + BPF_R9_LO, + BPF_FP_HI, + BPF_FP_LO, + BPF_AX_HI, + BPF_AX_LO, + BPF_R0_TEMP_HI, + BPF_R0_TEMP_LO, + BPF_JIT_SCRATCH_REGS, +}; + +/* Number of callee-saved registers stored to stack: rp, r3-r18. */ +#define NR_SAVED_REGISTERS (18 - 3 + 1 + 8) + +/* Offset from fp for BPF registers stored on stack. */ +#define STACK_OFFSET(k) (- (NR_SAVED_REGISTERS + k + 1)) +#define STACK_ALIGN FRAME_SIZE + +#define EXIT_PTR_LOAD(reg) hppa_ldw(-0x08, HPPA_REG_SP, reg) +#define EXIT_PTR_STORE(reg) hppa_stw(reg, -0x08, HPPA_REG_SP) +#define EXIT_PTR_JUMP(reg, nop) hppa_bv(HPPA_REG_ZERO, reg, nop) + +#define TMP_REG_1 (MAX_BPF_JIT_REG + 0) +#define TMP_REG_2 (MAX_BPF_JIT_REG + 1) +#define TMP_REG_R0 (MAX_BPF_JIT_REG + 2) + +static const s8 regmap[][2] = { + /* Return value from in-kernel function, and exit value from eBPF. */ + [BPF_REG_0] = {HPPA_REG_RET0, HPPA_REG_RET1}, /* HI/LOW */ + + /* Arguments from eBPF program to in-kernel function. */ + [BPF_REG_1] = {HPPA_R(3), HPPA_R(4)}, + [BPF_REG_2] = {HPPA_R(5), HPPA_R(6)}, + [BPF_REG_3] = {HPPA_R(7), HPPA_R(8)}, + [BPF_REG_4] = {HPPA_R(9), HPPA_R(10)}, + [BPF_REG_5] = {HPPA_R(11), HPPA_R(12)}, + + [BPF_REG_6] = {HPPA_R(13), HPPA_R(14)}, + [BPF_REG_7] = {HPPA_R(15), HPPA_R(16)}, + /* + * Callee-saved registers that in-kernel function will preserve. + * Stored on the stack. + */ + [BPF_REG_8] = {STACK_OFFSET(BPF_R8_HI), STACK_OFFSET(BPF_R8_LO)}, + [BPF_REG_9] = {STACK_OFFSET(BPF_R9_HI), STACK_OFFSET(BPF_R9_LO)}, + + /* Read-only frame pointer to access BPF stack. Not needed. */ + [BPF_REG_FP] = {STACK_OFFSET(BPF_FP_HI), STACK_OFFSET(BPF_FP_LO)}, + + /* Temporary register for blinding constants. Stored on the stack. */ + [BPF_REG_AX] = {STACK_OFFSET(BPF_AX_HI), STACK_OFFSET(BPF_AX_LO)}, + /* + * Temporary registers used by the JIT to operate on registers stored + * on the stack. Save t0 and t1 to be used as temporaries in generated + * code. + */ + [TMP_REG_1] = {HPPA_REG_T3, HPPA_REG_T2}, + [TMP_REG_2] = {HPPA_REG_T5, HPPA_REG_T4}, + + /* temporary space for BPF_R0 during libgcc and millicode calls */ + [TMP_REG_R0] = {STACK_OFFSET(BPF_R0_TEMP_HI), STACK_OFFSET(BPF_R0_TEMP_LO)}, +}; + +static s8 hi(const s8 *r) +{ + return r[0]; +} + +static s8 lo(const s8 *r) +{ + return r[1]; +} + +static void emit_hppa_copy(const s8 rs, const s8 rd, struct hppa_jit_context *ctx) +{ + REG_SET_SEEN(ctx, rd); + if (OPTIMIZE_HPPA && (rs == rd)) + return; + REG_SET_SEEN(ctx, rs); + emit(hppa_copy(rs, rd), ctx); +} + +static void emit_hppa_xor(const s8 r1, const s8 r2, const s8 r3, struct hppa_jit_context *ctx) +{ + REG_SET_SEEN(ctx, r1); + REG_SET_SEEN(ctx, r2); + REG_SET_SEEN(ctx, r3); + if (OPTIMIZE_HPPA && (r1 == r2)) { + emit(hppa_copy(HPPA_REG_ZERO, r3), ctx); + } else { + emit(hppa_xor(r1, r2, r3), ctx); + } +} + +static void emit_imm(const s8 rd, s32 imm, struct hppa_jit_context *ctx) +{ + u32 lower = im11(imm); + + REG_SET_SEEN(ctx, rd); + if (OPTIMIZE_HPPA && relative_bits_ok(imm, 14)) { + emit(hppa_ldi(imm, rd), ctx); + return; + } + emit(hppa_ldil(imm, rd), ctx); + if (OPTIMIZE_HPPA && (lower == 0)) + return; + emit(hppa_ldo(lower, rd, rd), ctx); +} + +static void emit_imm32(const s8 *rd, s32 imm, struct hppa_jit_context *ctx) +{ + /* Emit immediate into lower bits. */ + REG_SET_SEEN(ctx, lo(rd)); + emit_imm(lo(rd), imm, ctx); + + /* Sign-extend into upper bits. */ + REG_SET_SEEN(ctx, hi(rd)); + if (imm >= 0) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + else + emit(hppa_ldi(-1, hi(rd)), ctx); +} + +static void emit_imm64(const s8 *rd, s32 imm_hi, s32 imm_lo, + struct hppa_jit_context *ctx) +{ + emit_imm(hi(rd), imm_hi, ctx); + emit_imm(lo(rd), imm_lo, ctx); +} + +static void __build_epilogue(bool is_tail_call, struct hppa_jit_context *ctx) +{ + const s8 *r0 = regmap[BPF_REG_0]; + int i; + + if (is_tail_call) { + /* + * goto *(t0 + 4); + * Skips first instruction of prologue which initializes tail + * call counter. Assumes t0 contains address of target program, + * see emit_bpf_tail_call. + */ + emit(hppa_ldo(1 * HPPA_INSN_SIZE, HPPA_REG_T0, HPPA_REG_T0), ctx); + emit(hppa_bv(HPPA_REG_ZERO, HPPA_REG_T0, EXEC_NEXT_INSTR), ctx); + /* in delay slot: */ + emit(hppa_copy(HPPA_REG_TCC, HPPA_REG_TCC_IN_INIT), ctx); + + return; + } + + /* load epilogue function pointer and jump to it. */ + /* exit point is either directly below, or the outest TCC exit function */ + emit(EXIT_PTR_LOAD(HPPA_REG_RP), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* NOTE: we are 32-bit and big-endian, so return lower 32-bit value */ + emit_hppa_copy(lo(r0), HPPA_REG_RET0, ctx); + + /* Restore callee-saved registers. */ + for (i = 3; i <= 18; i++) { + if (OPTIMIZE_HPPA && !REG_WAS_SEEN(ctx, HPPA_R(i))) + continue; + emit(hppa_ldw(-REG_SIZE * (8 + (i-3)), HPPA_REG_SP, HPPA_R(i)), ctx); + } + + /* load original return pointer (stored by outest TCC function) */ + emit(hppa_ldw(-0x14, HPPA_REG_SP, HPPA_REG_RP), ctx); + emit(hppa_bv(HPPA_REG_ZERO, HPPA_REG_RP, EXEC_NEXT_INSTR), ctx); + /* in delay slot: */ + emit(hppa_ldw(-0x04, HPPA_REG_SP, HPPA_REG_SP), ctx); +} + +static bool is_stacked(s8 reg) +{ + return reg < 0; +} + +static const s8 *bpf_get_reg64_offset(const s8 *reg, const s8 *tmp, + u16 offset_sp, struct hppa_jit_context *ctx) +{ + if (is_stacked(hi(reg))) { + emit(hppa_ldw(REG_SIZE * hi(reg) - offset_sp, HPPA_REG_SP, hi(tmp)), ctx); + emit(hppa_ldw(REG_SIZE * lo(reg) - offset_sp, HPPA_REG_SP, lo(tmp)), ctx); + reg = tmp; + } + REG_SET_SEEN(ctx, hi(reg)); + REG_SET_SEEN(ctx, lo(reg)); + return reg; +} + +static const s8 *bpf_get_reg64(const s8 *reg, const s8 *tmp, + struct hppa_jit_context *ctx) +{ + return bpf_get_reg64_offset(reg, tmp, 0, ctx); +} + +static const s8 *bpf_get_reg64_ref(const s8 *reg, const s8 *tmp, + bool must_load, struct hppa_jit_context *ctx) +{ + if (!OPTIMIZE_HPPA) + return bpf_get_reg64(reg, tmp, ctx); + + if (is_stacked(hi(reg))) { + if (must_load) + emit(hppa_ldw(REG_SIZE * hi(reg), HPPA_REG_SP, hi(tmp)), ctx); + reg = tmp; + } + REG_SET_SEEN(ctx, hi(reg)); + REG_SET_SEEN(ctx, lo(reg)); + return reg; +} + + +static void bpf_put_reg64(const s8 *reg, const s8 *src, + struct hppa_jit_context *ctx) +{ + if (is_stacked(hi(reg))) { + emit(hppa_stw(hi(src), REG_SIZE * hi(reg), HPPA_REG_SP), ctx); + emit(hppa_stw(lo(src), REG_SIZE * lo(reg), HPPA_REG_SP), ctx); + } +} + +static void bpf_save_R0(struct hppa_jit_context *ctx) +{ + bpf_put_reg64(regmap[TMP_REG_R0], regmap[BPF_REG_0], ctx); +} + +static void bpf_restore_R0(struct hppa_jit_context *ctx) +{ + bpf_get_reg64(regmap[TMP_REG_R0], regmap[BPF_REG_0], ctx); +} + + +static const s8 *bpf_get_reg32(const s8 *reg, const s8 *tmp, + struct hppa_jit_context *ctx) +{ + if (is_stacked(lo(reg))) { + emit(hppa_ldw(REG_SIZE * lo(reg), HPPA_REG_SP, lo(tmp)), ctx); + reg = tmp; + } + REG_SET_SEEN(ctx, lo(reg)); + return reg; +} + +static const s8 *bpf_get_reg32_ref(const s8 *reg, const s8 *tmp, + struct hppa_jit_context *ctx) +{ + if (!OPTIMIZE_HPPA) + return bpf_get_reg32(reg, tmp, ctx); + + if (is_stacked(hi(reg))) { + reg = tmp; + } + REG_SET_SEEN(ctx, lo(reg)); + return reg; +} + +static void bpf_put_reg32(const s8 *reg, const s8 *src, + struct hppa_jit_context *ctx) +{ + if (is_stacked(lo(reg))) { + REG_SET_SEEN(ctx, lo(src)); + emit(hppa_stw(lo(src), REG_SIZE * lo(reg), HPPA_REG_SP), ctx); + if (1 && !ctx->prog->aux->verifier_zext) { + REG_SET_SEEN(ctx, hi(reg)); + emit(hppa_stw(HPPA_REG_ZERO, REG_SIZE * hi(reg), HPPA_REG_SP), ctx); + } + } else if (1 && !ctx->prog->aux->verifier_zext) { + REG_SET_SEEN(ctx, hi(reg)); + emit_hppa_copy(HPPA_REG_ZERO, hi(reg), ctx); + } +} + +/* extern hppa millicode functions */ +extern void $$mulI(void); +extern void $$divU(void); +extern void $$remU(void); + +static void emit_call_millicode(void *func, const s8 arg0, + const s8 arg1, u8 opcode, struct hppa_jit_context *ctx) +{ + u32 func_addr; + + emit_hppa_copy(arg0, HPPA_REG_ARG0, ctx); + emit_hppa_copy(arg1, HPPA_REG_ARG1, ctx); + + /* libcgcc overwrites HPPA_REG_RET0/1, save temp. in dest. */ + if (arg0 != HPPA_REG_RET1) + bpf_save_R0(ctx); + + func_addr = (uintptr_t) dereference_function_descriptor(func); + emit(hppa_ldil(func_addr, HPPA_REG_R31), ctx); + /* skip the following be_l instruction if divisor is zero. */ + if (BPF_OP(opcode) == BPF_DIV || BPF_OP(opcode) == BPF_MOD) { + if (BPF_OP(opcode) == BPF_DIV) + emit_hppa_copy(HPPA_REG_ZERO, HPPA_REG_RET1, ctx); + else + emit_hppa_copy(HPPA_REG_ARG0, HPPA_REG_RET1, ctx); + emit(hppa_or_cond(HPPA_REG_ARG1, HPPA_REG_ZERO, 1, 0, HPPA_REG_ZERO), ctx); + } + /* Note: millicode functions use r31 as return pointer instead of rp */ + emit(hppa_be_l(im11(func_addr) >> 2, HPPA_REG_R31, NOP_NEXT_INSTR), ctx); + emit(hppa_nop(), ctx); /* this nop is needed here for delay slot */ + + /* Note: millicode functions return result in RET1, not RET0 */ + emit_hppa_copy(HPPA_REG_RET1, arg0, ctx); + + /* restore HPPA_REG_RET0/1, temp. save in dest. */ + if (arg0 != HPPA_REG_RET1) + bpf_restore_R0(ctx); +} + +static void emit_call_libgcc_ll(void *func, const s8 *arg0, + const s8 *arg1, u8 opcode, struct hppa_jit_context *ctx) +{ + u32 func_addr; + + emit_hppa_copy(lo(arg0), HPPA_REG_ARG0, ctx); + emit_hppa_copy(hi(arg0), HPPA_REG_ARG1, ctx); + emit_hppa_copy(lo(arg1), HPPA_REG_ARG2, ctx); + emit_hppa_copy(hi(arg1), HPPA_REG_ARG3, ctx); + + /* libcgcc overwrites HPPA_REG_RET0/_RET1, so keep copy of R0 on stack */ + if (hi(arg0) != HPPA_REG_RET0) + bpf_save_R0(ctx); + + /* prepare stack */ + emit(hppa_ldo(2 * FRAME_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); + + func_addr = (uintptr_t) dereference_function_descriptor(func); + emit(hppa_ldil(func_addr, HPPA_REG_R31), ctx); + /* zero out the following be_l instruction if divisor is 0 (and set default values) */ + if (BPF_OP(opcode) == BPF_DIV || BPF_OP(opcode) == BPF_MOD) { + emit_hppa_copy(HPPA_REG_ZERO, HPPA_REG_RET0, ctx); + if (BPF_OP(opcode) == BPF_DIV) + emit_hppa_copy(HPPA_REG_ZERO, HPPA_REG_RET1, ctx); + else + emit_hppa_copy(HPPA_REG_ARG0, HPPA_REG_RET1, ctx); + emit(hppa_or_cond(HPPA_REG_ARG2, HPPA_REG_ARG3, 1, 0, HPPA_REG_ZERO), ctx); + } + emit(hppa_be_l(im11(func_addr) >> 2, HPPA_REG_R31, EXEC_NEXT_INSTR), ctx); + emit_hppa_copy(HPPA_REG_R31, HPPA_REG_RP, ctx); + + /* restore stack */ + emit(hppa_ldo(-2 * FRAME_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); + + emit_hppa_copy(HPPA_REG_RET0, hi(arg0), ctx); + emit_hppa_copy(HPPA_REG_RET1, lo(arg0), ctx); + + /* restore HPPA_REG_RET0/_RET1 */ + if (hi(arg0) != HPPA_REG_RET0) + bpf_restore_R0(ctx); +} + +static void emit_jump(s32 paoff, bool force_far, + struct hppa_jit_context *ctx) +{ + unsigned long pc, addr; + + /* Note: allocate 2 instructions for jumps if force_far is set. */ + if (relative_bits_ok(paoff - HPPA_BRANCH_DISPLACEMENT, 17)) { + /* use BL,short branch followed by nop() */ + emit(hppa_bl(paoff - HPPA_BRANCH_DISPLACEMENT, HPPA_REG_ZERO), ctx); + if (force_far) + emit(hppa_nop(), ctx); + return; + } + + pc = (uintptr_t) &ctx->insns[ctx->ninsns]; + addr = pc + (paoff * HPPA_INSN_SIZE); + emit(hppa_ldil(addr, HPPA_REG_R31), ctx); + emit(hppa_be_l(im11(addr) >> 2, HPPA_REG_R31, NOP_NEXT_INSTR), ctx); // be,l,n addr(sr4,r31), %sr0, %r31 +} + +static void emit_alu_i64(const s8 *dst, s32 imm, + struct hppa_jit_context *ctx, const u8 op) +{ + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *rd; + + if (0 && op == BPF_MOV) + rd = bpf_get_reg64_ref(dst, tmp1, false, ctx); + else + rd = bpf_get_reg64(dst, tmp1, ctx); + + /* dst = dst OP imm */ + switch (op) { + case BPF_MOV: + emit_imm32(rd, imm, ctx); + break; + case BPF_AND: + emit_imm(HPPA_REG_T0, imm, ctx); + emit(hppa_and(lo(rd), HPPA_REG_T0, lo(rd)), ctx); + if (imm >= 0) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + break; + case BPF_OR: + emit_imm(HPPA_REG_T0, imm, ctx); + emit(hppa_or(lo(rd), HPPA_REG_T0, lo(rd)), ctx); + if (imm < 0) + emit_imm(hi(rd), -1, ctx); + break; + case BPF_XOR: + emit_imm(HPPA_REG_T0, imm, ctx); + emit_hppa_xor(lo(rd), HPPA_REG_T0, lo(rd), ctx); + if (imm < 0) { + emit_imm(HPPA_REG_T0, -1, ctx); + emit_hppa_xor(hi(rd), HPPA_REG_T0, hi(rd), ctx); + } + break; + case BPF_LSH: + if (imm == 0) + break; + if (imm > 32) { + imm -= 32; + emit(hppa_zdep(lo(rd), imm, imm, hi(rd)), ctx); + emit_hppa_copy(HPPA_REG_ZERO, lo(rd), ctx); + } else if (imm == 32) { + emit_hppa_copy(lo(rd), hi(rd), ctx); + emit_hppa_copy(HPPA_REG_ZERO, lo(rd), ctx); + } else { + emit(hppa_shd(hi(rd), lo(rd), 32 - imm, hi(rd)), ctx); + emit(hppa_zdep(lo(rd), imm, imm, lo(rd)), ctx); + } + break; + case BPF_RSH: + if (imm == 0) + break; + if (imm > 32) { + imm -= 32; + emit(hppa_shr(hi(rd), imm, lo(rd)), ctx); + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + } else if (imm == 32) { + emit_hppa_copy(hi(rd), lo(rd), ctx); + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + } else { + emit(hppa_shrpw(hi(rd), lo(rd), imm, lo(rd)), ctx); + emit(hppa_shr(hi(rd), imm, hi(rd)), ctx); + } + break; + case BPF_ARSH: + if (imm == 0) + break; + if (imm > 32) { + imm -= 32; + emit(hppa_extrws(hi(rd), 31 - imm, imm, lo(rd)), ctx); + emit(hppa_extrws(hi(rd), 0, 31, hi(rd)), ctx); + } else if (imm == 32) { + emit_hppa_copy(hi(rd), lo(rd), ctx); + emit(hppa_extrws(hi(rd), 0, 31, hi(rd)), ctx); + } else { + emit(hppa_shrpw(hi(rd), lo(rd), imm, lo(rd)), ctx); + emit(hppa_extrws(hi(rd), 31 - imm, imm, hi(rd)), ctx); + } + break; + default: + WARN_ON(1); + } + + bpf_put_reg64(dst, rd, ctx); +} + +static void emit_alu_i32(const s8 *dst, s32 imm, + struct hppa_jit_context *ctx, const u8 op) +{ + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *rd = bpf_get_reg32(dst, tmp1, ctx); + + if (op == BPF_MOV) + rd = bpf_get_reg32_ref(dst, tmp1, ctx); + else + rd = bpf_get_reg32(dst, tmp1, ctx); + + /* dst = dst OP imm */ + switch (op) { + case BPF_MOV: + emit_imm(lo(rd), imm, ctx); + break; + case BPF_ADD: + emit_imm(HPPA_REG_T0, imm, ctx); + emit(hppa_add(lo(rd), HPPA_REG_T0, lo(rd)), ctx); + break; + case BPF_SUB: + emit_imm(HPPA_REG_T0, imm, ctx); + emit(hppa_sub(lo(rd), HPPA_REG_T0, lo(rd)), ctx); + break; + case BPF_AND: + emit_imm(HPPA_REG_T0, imm, ctx); + emit(hppa_and(lo(rd), HPPA_REG_T0, lo(rd)), ctx); + break; + case BPF_OR: + emit_imm(HPPA_REG_T0, imm, ctx); + emit(hppa_or(lo(rd), HPPA_REG_T0, lo(rd)), ctx); + break; + case BPF_XOR: + emit_imm(HPPA_REG_T0, imm, ctx); + emit_hppa_xor(lo(rd), HPPA_REG_T0, lo(rd), ctx); + break; + case BPF_LSH: + if (imm != 0) + emit(hppa_zdep(lo(rd), imm, imm, lo(rd)), ctx); + break; + case BPF_RSH: + if (imm != 0) + emit(hppa_shr(lo(rd), imm, lo(rd)), ctx); + break; + case BPF_ARSH: + if (imm != 0) + emit(hppa_extrws(lo(rd), 31 - imm, imm, lo(rd)), ctx); + break; + default: + WARN_ON(1); + } + + bpf_put_reg32(dst, rd, ctx); +} + +static void emit_alu_r64(const s8 *dst, const s8 *src, + struct hppa_jit_context *ctx, const u8 op) +{ + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *tmp2 = regmap[TMP_REG_2]; + const s8 *rd; + const s8 *rs = bpf_get_reg64(src, tmp2, ctx); + + if (op == BPF_MOV) + rd = bpf_get_reg64_ref(dst, tmp1, false, ctx); + else + rd = bpf_get_reg64(dst, tmp1, ctx); + + /* dst = dst OP src */ + switch (op) { + case BPF_MOV: + emit_hppa_copy(lo(rs), lo(rd), ctx); + emit_hppa_copy(hi(rs), hi(rd), ctx); + break; + case BPF_ADD: + emit(hppa_add(lo(rd), lo(rs), lo(rd)), ctx); + emit(hppa_addc(hi(rd), hi(rs), hi(rd)), ctx); + break; + case BPF_SUB: + emit(hppa_sub(lo(rd), lo(rs), lo(rd)), ctx); + emit(hppa_subb(hi(rd), hi(rs), hi(rd)), ctx); + break; + case BPF_AND: + emit(hppa_and(lo(rd), lo(rs), lo(rd)), ctx); + emit(hppa_and(hi(rd), hi(rs), hi(rd)), ctx); + break; + case BPF_OR: + emit(hppa_or(lo(rd), lo(rs), lo(rd)), ctx); + emit(hppa_or(hi(rd), hi(rs), hi(rd)), ctx); + break; + case BPF_XOR: + emit_hppa_xor(lo(rd), lo(rs), lo(rd), ctx); + emit_hppa_xor(hi(rd), hi(rs), hi(rd), ctx); + break; + case BPF_MUL: + emit_call_libgcc_ll(__muldi3, rd, rs, op, ctx); + break; + case BPF_DIV: + emit_call_libgcc_ll(&hppa_div64, rd, rs, op, ctx); + break; + case BPF_MOD: + emit_call_libgcc_ll(&hppa_div64_rem, rd, rs, op, ctx); + break; + case BPF_LSH: + emit_call_libgcc_ll(__ashldi3, rd, rs, op, ctx); + break; + case BPF_RSH: + emit_call_libgcc_ll(__lshrdi3, rd, rs, op, ctx); + break; + case BPF_ARSH: + emit_call_libgcc_ll(__ashrdi3, rd, rs, op, ctx); + break; + case BPF_NEG: + emit(hppa_sub(HPPA_REG_ZERO, lo(rd), lo(rd)), ctx); + emit(hppa_subb(HPPA_REG_ZERO, hi(rd), hi(rd)), ctx); + break; + default: + WARN_ON(1); + } + + bpf_put_reg64(dst, rd, ctx); +} + +static void emit_alu_r32(const s8 *dst, const s8 *src, + struct hppa_jit_context *ctx, const u8 op) +{ + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *tmp2 = regmap[TMP_REG_2]; + const s8 *rd; + const s8 *rs = bpf_get_reg32(src, tmp2, ctx); + + if (op == BPF_MOV) + rd = bpf_get_reg32_ref(dst, tmp1, ctx); + else + rd = bpf_get_reg32(dst, tmp1, ctx); + + /* dst = dst OP src */ + switch (op) { + case BPF_MOV: + emit_hppa_copy(lo(rs), lo(rd), ctx); + break; + case BPF_ADD: + emit(hppa_add(lo(rd), lo(rs), lo(rd)), ctx); + break; + case BPF_SUB: + emit(hppa_sub(lo(rd), lo(rs), lo(rd)), ctx); + break; + case BPF_AND: + emit(hppa_and(lo(rd), lo(rs), lo(rd)), ctx); + break; + case BPF_OR: + emit(hppa_or(lo(rd), lo(rs), lo(rd)), ctx); + break; + case BPF_XOR: + emit_hppa_xor(lo(rd), lo(rs), lo(rd), ctx); + break; + case BPF_MUL: + emit_call_millicode($$mulI, lo(rd), lo(rs), op, ctx); + break; + case BPF_DIV: + emit_call_millicode($$divU, lo(rd), lo(rs), op, ctx); + break; + case BPF_MOD: + emit_call_millicode($$remU, lo(rd), lo(rs), op, ctx); + break; + case BPF_LSH: + emit(hppa_subi(0x1f, lo(rs), HPPA_REG_T0), ctx); + emit(hppa_mtsar(HPPA_REG_T0), ctx); + emit(hppa_depwz_sar(lo(rd), lo(rd)), ctx); + break; + case BPF_RSH: + emit(hppa_mtsar(lo(rs)), ctx); + emit(hppa_shrpw_sar(lo(rd), lo(rd)), ctx); + break; + case BPF_ARSH: /* sign extending arithmetic shift right */ + // emit(hppa_beq(lo(rs), HPPA_REG_ZERO, 2), ctx); + emit(hppa_subi(0x1f, lo(rs), HPPA_REG_T0), ctx); + emit(hppa_mtsar(HPPA_REG_T0), ctx); + emit(hppa_extrws_sar(lo(rd), lo(rd)), ctx); + break; + case BPF_NEG: + emit(hppa_sub(HPPA_REG_ZERO, lo(rd), lo(rd)), ctx); // sub r0,rd,rd + break; + default: + WARN_ON(1); + } + + bpf_put_reg32(dst, rd, ctx); +} + +static int emit_branch_r64(const s8 *src1, const s8 *src2, s32 paoff, + struct hppa_jit_context *ctx, const u8 op) +{ + int e, s = ctx->ninsns; + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *tmp2 = regmap[TMP_REG_2]; + + const s8 *rs1 = bpf_get_reg64(src1, tmp1, ctx); + const s8 *rs2 = bpf_get_reg64(src2, tmp2, ctx); + + /* + * NO_JUMP skips over the rest of the instructions and the + * emit_jump, meaning the BPF branch is not taken. + * JUMP skips directly to the emit_jump, meaning + * the BPF branch is taken. + * + * The fallthrough case results in the BPF branch being taken. + */ +#define NO_JUMP(idx) (2 + (idx) - 1) +#define JUMP(idx) (0 + (idx) - 1) + + switch (op) { + case BPF_JEQ: + emit(hppa_bne(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bne(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JGT: + emit(hppa_bgtu(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_bltu(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bleu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JLT: + emit(hppa_bltu(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_bgtu(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bgeu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JGE: + emit(hppa_bgtu(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_bltu(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bltu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JLE: + emit(hppa_bltu(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_bgtu(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bgtu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JNE: + emit(hppa_bne(hi(rs1), hi(rs2), JUMP(1)), ctx); + emit(hppa_beq(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JSGT: + emit(hppa_bgt(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_blt(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bleu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JSLT: + emit(hppa_blt(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_bgt(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bgeu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JSGE: + emit(hppa_bgt(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_blt(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bltu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JSLE: + emit(hppa_blt(hi(rs1), hi(rs2), JUMP(2)), ctx); + emit(hppa_bgt(hi(rs1), hi(rs2), NO_JUMP(1)), ctx); + emit(hppa_bgtu(lo(rs1), lo(rs2), NO_JUMP(0)), ctx); + break; + case BPF_JSET: + emit(hppa_and(hi(rs1), hi(rs2), HPPA_REG_T0), ctx); + emit(hppa_and(lo(rs1), lo(rs2), HPPA_REG_T1), ctx); + emit(hppa_bne(HPPA_REG_T0, HPPA_REG_ZERO, JUMP(1)), ctx); + emit(hppa_beq(HPPA_REG_T1, HPPA_REG_ZERO, NO_JUMP(0)), ctx); + break; + default: + WARN_ON(1); + } + +#undef NO_JUMP +#undef JUMP + + e = ctx->ninsns; + /* Adjust for extra insns. */ + paoff -= (e - s); + emit_jump(paoff, true, ctx); + return 0; +} + +static int emit_bcc(u8 op, u8 rd, u8 rs, int paoff, struct hppa_jit_context *ctx) +{ + int e, s; + bool far = false; + int off; + + if (op == BPF_JSET) { + /* + * BPF_JSET is a special case: it has no inverse so we always + * treat it as a far branch. + */ + emit(hppa_and(rd, rs, HPPA_REG_T0), ctx); + paoff -= 1; /* reduce offset due to hppa_and() above */ + rd = HPPA_REG_T0; + rs = HPPA_REG_ZERO; + op = BPF_JNE; + } + + s = ctx->ninsns; + + if (!relative_bits_ok(paoff - HPPA_BRANCH_DISPLACEMENT, 12)) { + op = invert_bpf_cond(op); + far = true; + } + + /* + * For a far branch, the condition is negated and we jump over the + * branch itself, and the three instructions from emit_jump. + * For a near branch, just use paoff. + */ + off = far ? (HPPA_BRANCH_DISPLACEMENT - 1) : paoff - HPPA_BRANCH_DISPLACEMENT; + + switch (op) { + /* IF (dst COND src) JUMP off */ + case BPF_JEQ: + emit(hppa_beq(rd, rs, off), ctx); + break; + case BPF_JGT: + emit(hppa_bgtu(rd, rs, off), ctx); + break; + case BPF_JLT: + emit(hppa_bltu(rd, rs, off), ctx); + break; + case BPF_JGE: + emit(hppa_bgeu(rd, rs, off), ctx); + break; + case BPF_JLE: + emit(hppa_bleu(rd, rs, off), ctx); + break; + case BPF_JNE: + emit(hppa_bne(rd, rs, off), ctx); + break; + case BPF_JSGT: + emit(hppa_bgt(rd, rs, off), ctx); + break; + case BPF_JSLT: + emit(hppa_blt(rd, rs, off), ctx); + break; + case BPF_JSGE: + emit(hppa_bge(rd, rs, off), ctx); + break; + case BPF_JSLE: + emit(hppa_ble(rd, rs, off), ctx); + break; + default: + WARN_ON(1); + } + + if (far) { + e = ctx->ninsns; + /* Adjust for extra insns. */ + paoff -= (e - s); + emit_jump(paoff, true, ctx); + } + return 0; +} + +static int emit_branch_r32(const s8 *src1, const s8 *src2, s32 paoff, + struct hppa_jit_context *ctx, const u8 op) +{ + int e, s = ctx->ninsns; + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *tmp2 = regmap[TMP_REG_2]; + + const s8 *rs1 = bpf_get_reg32(src1, tmp1, ctx); + const s8 *rs2 = bpf_get_reg32(src2, tmp2, ctx); + + e = ctx->ninsns; + /* Adjust for extra insns. */ + paoff -= (e - s); + + if (emit_bcc(op, lo(rs1), lo(rs2), paoff, ctx)) + return -1; + + return 0; +} + +static void emit_call(bool fixed, u64 addr, struct hppa_jit_context *ctx) +{ + const s8 *tmp = regmap[TMP_REG_1]; + const s8 *r0 = regmap[BPF_REG_0]; + const s8 *reg; + const int offset_sp = 2 * STACK_ALIGN; + + /* prepare stack */ + emit(hppa_ldo(offset_sp, HPPA_REG_SP, HPPA_REG_SP), ctx); + + /* load R1 & R2 in registers, R3-R5 to stack. */ + reg = bpf_get_reg64_offset(regmap[BPF_REG_5], tmp, offset_sp, ctx); + emit(hppa_stw(hi(reg), -0x48, HPPA_REG_SP), ctx); + emit(hppa_stw(lo(reg), -0x44, HPPA_REG_SP), ctx); + + reg = bpf_get_reg64_offset(regmap[BPF_REG_4], tmp, offset_sp, ctx); + emit(hppa_stw(hi(reg), -0x40, HPPA_REG_SP), ctx); + emit(hppa_stw(lo(reg), -0x3c, HPPA_REG_SP), ctx); + + reg = bpf_get_reg64_offset(regmap[BPF_REG_3], tmp, offset_sp, ctx); + emit(hppa_stw(hi(reg), -0x38, HPPA_REG_SP), ctx); + emit(hppa_stw(lo(reg), -0x34, HPPA_REG_SP), ctx); + + reg = bpf_get_reg64_offset(regmap[BPF_REG_2], tmp, offset_sp, ctx); + emit_hppa_copy(hi(reg), HPPA_REG_ARG3, ctx); + emit_hppa_copy(lo(reg), HPPA_REG_ARG2, ctx); + + reg = bpf_get_reg64_offset(regmap[BPF_REG_1], tmp, offset_sp, ctx); + emit_hppa_copy(hi(reg), HPPA_REG_ARG1, ctx); + emit_hppa_copy(lo(reg), HPPA_REG_ARG0, ctx); + + /* backup TCC */ + if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) + emit(hppa_copy(HPPA_REG_TCC, HPPA_REG_TCC_SAVED), ctx); + + /* + * Use ldil() to load absolute address. Don't use emit_imm as the + * number of emitted instructions should not depend on the value of + * addr. + */ + emit(hppa_ldil(addr, HPPA_REG_R31), ctx); + emit(hppa_be_l(im11(addr) >> 2, HPPA_REG_R31, EXEC_NEXT_INSTR), ctx); + /* set return address in delay slot */ + emit_hppa_copy(HPPA_REG_R31, HPPA_REG_RP, ctx); + + /* restore TCC */ + if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) + emit(hppa_copy(HPPA_REG_TCC_SAVED, HPPA_REG_TCC), ctx); + + /* restore stack */ + emit(hppa_ldo(-offset_sp, HPPA_REG_SP, HPPA_REG_SP), ctx); + + /* set return value. */ + emit_hppa_copy(HPPA_REG_RET0, hi(r0), ctx); + emit_hppa_copy(HPPA_REG_RET1, lo(r0), ctx); +} + +static int emit_bpf_tail_call(int insn, struct hppa_jit_context *ctx) +{ + /* + * R1 -> &ctx + * R2 -> &array + * R3 -> index + */ + int off; + const s8 *arr_reg = regmap[BPF_REG_2]; + const s8 *idx_reg = regmap[BPF_REG_3]; + struct bpf_array bpfa; + struct bpf_prog bpfp; + + /* get address of TCC main exit function for error case into rp */ + emit(EXIT_PTR_LOAD(HPPA_REG_RP), ctx); + + /* max_entries = array->map.max_entries; */ + off = offsetof(struct bpf_array, map.max_entries); + BUILD_BUG_ON(sizeof(bpfa.map.max_entries) != 4); + emit(hppa_ldw(off, lo(arr_reg), HPPA_REG_T1), ctx); + + /* + * if (index >= max_entries) + * goto out; + */ + emit(hppa_bltu(lo(idx_reg), HPPA_REG_T1, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* + * if (--tcc < 0) + * goto out; + */ + REG_FORCE_SEEN(ctx, HPPA_REG_TCC); + emit(hppa_ldo(-1, HPPA_REG_TCC, HPPA_REG_TCC), ctx); + emit(hppa_bge(HPPA_REG_TCC, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* + * prog = array->ptrs[index]; + * if (!prog) + * goto out; + */ + BUILD_BUG_ON(sizeof(bpfa.ptrs[0]) != 4); + emit(hppa_sh2add(lo(idx_reg), lo(arr_reg), HPPA_REG_T0), ctx); + off = offsetof(struct bpf_array, ptrs); + BUILD_BUG_ON(!relative_bits_ok(off, 11)); + emit(hppa_ldw(off, HPPA_REG_T0, HPPA_REG_T0), ctx); + emit(hppa_bne(HPPA_REG_T0, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* + * tcc = temp_tcc; + * goto *(prog->bpf_func + 4); + */ + off = offsetof(struct bpf_prog, bpf_func); + BUILD_BUG_ON(!relative_bits_ok(off, 11)); + BUILD_BUG_ON(sizeof(bpfp.bpf_func) != 4); + emit(hppa_ldw(off, HPPA_REG_T0, HPPA_REG_T0), ctx); + /* Epilogue jumps to *(t0 + 4). */ + __build_epilogue(true, ctx); + return 0; +} + +static int emit_load_r64(const s8 *dst, const s8 *src, s16 off, + struct hppa_jit_context *ctx, const u8 size) +{ + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *tmp2 = regmap[TMP_REG_2]; + const s8 *rd = bpf_get_reg64_ref(dst, tmp1, ctx->prog->aux->verifier_zext, ctx); + const s8 *rs = bpf_get_reg64(src, tmp2, ctx); + s8 srcreg; + + /* need to calculate address since offset does not fit in 14 bits? */ + if (relative_bits_ok(off, 14)) + srcreg = lo(rs); + else { + /* need to use R1 here, since addil puts result into R1 */ + srcreg = HPPA_REG_R1; + emit(hppa_addil(off, lo(rs)), ctx); + off = im11(off); + } + + /* LDX: dst = *(size *)(src + off) */ + switch (size) { + case BPF_B: + emit(hppa_ldb(off + 0, srcreg, lo(rd)), ctx); + if (!ctx->prog->aux->verifier_zext) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + break; + case BPF_H: + emit(hppa_ldh(off + 0, srcreg, lo(rd)), ctx); + if (!ctx->prog->aux->verifier_zext) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + break; + case BPF_W: + emit(hppa_ldw(off + 0, srcreg, lo(rd)), ctx); + if (!ctx->prog->aux->verifier_zext) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + break; + case BPF_DW: + emit(hppa_ldw(off + 0, srcreg, hi(rd)), ctx); + emit(hppa_ldw(off + 4, srcreg, lo(rd)), ctx); + break; + } + + bpf_put_reg64(dst, rd, ctx); + return 0; +} + +static int emit_store_r64(const s8 *dst, const s8 *src, s16 off, + struct hppa_jit_context *ctx, const u8 size, + const u8 mode) +{ + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *tmp2 = regmap[TMP_REG_2]; + const s8 *rd = bpf_get_reg64(dst, tmp1, ctx); + const s8 *rs = bpf_get_reg64(src, tmp2, ctx); + s8 dstreg; + + /* need to calculate address since offset does not fit in 14 bits? */ + if (relative_bits_ok(off, 14)) + dstreg = lo(rd); + else { + /* need to use R1 here, since addil puts result into R1 */ + dstreg = HPPA_REG_R1; + emit(hppa_addil(off, lo(rd)), ctx); + off = im11(off); + } + + /* ST: *(size *)(dst + off) = imm */ + switch (size) { + case BPF_B: + emit(hppa_stb(lo(rs), off + 0, dstreg), ctx); + break; + case BPF_H: + emit(hppa_sth(lo(rs), off + 0, dstreg), ctx); + break; + case BPF_W: + emit(hppa_stw(lo(rs), off + 0, dstreg), ctx); + break; + case BPF_DW: + emit(hppa_stw(hi(rs), off + 0, dstreg), ctx); + emit(hppa_stw(lo(rs), off + 4, dstreg), ctx); + break; + } + + return 0; +} + +static void emit_rev16(const s8 rd, struct hppa_jit_context *ctx) +{ + emit(hppa_extru(rd, 23, 8, HPPA_REG_T1), ctx); + emit(hppa_depwz(rd, 23, 8, HPPA_REG_T1), ctx); + emit(hppa_extru(HPPA_REG_T1, 31, 16, rd), ctx); +} + +static void emit_rev32(const s8 rs, const s8 rd, struct hppa_jit_context *ctx) +{ + emit(hppa_shrpw(rs, rs, 16, HPPA_REG_T1), ctx); + emit(hppa_depwz(HPPA_REG_T1, 15, 8, HPPA_REG_T1), ctx); + emit(hppa_shrpw(rs, HPPA_REG_T1, 8, rd), ctx); +} + +static void emit_zext64(const s8 *dst, struct hppa_jit_context *ctx) +{ + const s8 *rd; + const s8 *tmp1 = regmap[TMP_REG_1]; + + rd = bpf_get_reg64(dst, tmp1, ctx); + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + bpf_put_reg64(dst, rd, ctx); +} + +int bpf_jit_emit_insn(const struct bpf_insn *insn, struct hppa_jit_context *ctx, + bool extra_pass) +{ + bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 || + BPF_CLASS(insn->code) == BPF_JMP; + int s, e, paoff, i = insn - ctx->prog->insnsi; + u8 code = insn->code; + s16 off = insn->off; + s32 imm = insn->imm; + + const s8 *dst = regmap[insn->dst_reg]; + const s8 *src = regmap[insn->src_reg]; + const s8 *tmp1 = regmap[TMP_REG_1]; + const s8 *tmp2 = regmap[TMP_REG_2]; + + if (0) printk("CLASS %03d CODE %#02x ALU64:%d BPF_SIZE %#02x " + "BPF_CODE %#02x src_reg %d dst_reg %d\n", + BPF_CLASS(code), code, (code & BPF_ALU64) ? 1:0, BPF_SIZE(code), + BPF_OP(code), insn->src_reg, insn->dst_reg); + + switch (code) { + /* dst = src */ + case BPF_ALU64 | BPF_MOV | BPF_X: + + case BPF_ALU64 | BPF_ADD | BPF_X: + case BPF_ALU64 | BPF_ADD | BPF_K: + + case BPF_ALU64 | BPF_SUB | BPF_X: + case BPF_ALU64 | BPF_SUB | BPF_K: + + case BPF_ALU64 | BPF_AND | BPF_X: + case BPF_ALU64 | BPF_OR | BPF_X: + case BPF_ALU64 | BPF_XOR | BPF_X: + + case BPF_ALU64 | BPF_MUL | BPF_X: + case BPF_ALU64 | BPF_MUL | BPF_K: + + case BPF_ALU64 | BPF_DIV | BPF_X: + case BPF_ALU64 | BPF_DIV | BPF_K: + + case BPF_ALU64 | BPF_MOD | BPF_X: + case BPF_ALU64 | BPF_MOD | BPF_K: + + case BPF_ALU64 | BPF_LSH | BPF_X: + case BPF_ALU64 | BPF_RSH | BPF_X: + case BPF_ALU64 | BPF_ARSH | BPF_X: + if (BPF_SRC(code) == BPF_K) { + emit_imm32(tmp2, imm, ctx); + src = tmp2; + } + emit_alu_r64(dst, src, ctx, BPF_OP(code)); + break; + + /* dst = -dst */ + case BPF_ALU64 | BPF_NEG: + emit_alu_r64(dst, tmp2, ctx, BPF_OP(code)); + break; + + case BPF_ALU64 | BPF_MOV | BPF_K: + case BPF_ALU64 | BPF_AND | BPF_K: + case BPF_ALU64 | BPF_OR | BPF_K: + case BPF_ALU64 | BPF_XOR | BPF_K: + case BPF_ALU64 | BPF_LSH | BPF_K: + case BPF_ALU64 | BPF_RSH | BPF_K: + case BPF_ALU64 | BPF_ARSH | BPF_K: + emit_alu_i64(dst, imm, ctx, BPF_OP(code)); + break; + + case BPF_ALU | BPF_MOV | BPF_X: + if (imm == 1) { + /* Special mov32 for zext. */ + emit_zext64(dst, ctx); + break; + } + fallthrough; + /* dst = dst OP src */ + case BPF_ALU | BPF_ADD | BPF_X: + case BPF_ALU | BPF_SUB | BPF_X: + case BPF_ALU | BPF_AND | BPF_X: + case BPF_ALU | BPF_OR | BPF_X: + case BPF_ALU | BPF_XOR | BPF_X: + + case BPF_ALU | BPF_MUL | BPF_X: + case BPF_ALU | BPF_MUL | BPF_K: + + case BPF_ALU | BPF_DIV | BPF_X: + case BPF_ALU | BPF_DIV | BPF_K: + + case BPF_ALU | BPF_MOD | BPF_X: + case BPF_ALU | BPF_MOD | BPF_K: + + case BPF_ALU | BPF_LSH | BPF_X: + case BPF_ALU | BPF_RSH | BPF_X: + case BPF_ALU | BPF_ARSH | BPF_X: + if (BPF_SRC(code) == BPF_K) { + emit_imm32(tmp2, imm, ctx); + src = tmp2; + } + emit_alu_r32(dst, src, ctx, BPF_OP(code)); + break; + + /* dst = dst OP imm */ + case BPF_ALU | BPF_MOV | BPF_K: + case BPF_ALU | BPF_ADD | BPF_K: + case BPF_ALU | BPF_SUB | BPF_K: + case BPF_ALU | BPF_AND | BPF_K: + case BPF_ALU | BPF_OR | BPF_K: + case BPF_ALU | BPF_XOR | BPF_K: + case BPF_ALU | BPF_LSH | BPF_K: + case BPF_ALU | BPF_RSH | BPF_K: + case BPF_ALU | BPF_ARSH | BPF_K: + /* + * mul,div,mod are handled in the BPF_X case. + */ + emit_alu_i32(dst, imm, ctx, BPF_OP(code)); + break; + + /* dst = -dst */ + case BPF_ALU | BPF_NEG: + /* + * src is ignored---choose tmp2 as a dummy register since it + * is not on the stack. + */ + emit_alu_r32(dst, tmp2, ctx, BPF_OP(code)); + break; + + /* dst = BSWAP##imm(dst) */ + case BPF_ALU | BPF_END | BPF_FROM_BE: + { + const s8 *rd = bpf_get_reg64(dst, tmp1, ctx); + + switch (imm) { + case 16: + /* zero-extend 16 bits into 64 bits */ + emit(hppa_extru(lo(rd), 31, 16, lo(rd)), ctx); + fallthrough; + case 32: + /* zero-extend 32 bits into 64 bits */ + if (!ctx->prog->aux->verifier_zext) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + break; + case 64: + /* Do nothing. */ + break; + default: + pr_err("bpf-jit: BPF_END imm %d invalid\n", imm); + return -1; + } + + bpf_put_reg64(dst, rd, ctx); + break; + } + + case BPF_ALU | BPF_END | BPF_FROM_LE: + { + const s8 *rd = bpf_get_reg64(dst, tmp1, ctx); + + switch (imm) { + case 16: + emit_rev16(lo(rd), ctx); + if (!ctx->prog->aux->verifier_zext) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + break; + case 32: + emit_rev32(lo(rd), lo(rd), ctx); + if (!ctx->prog->aux->verifier_zext) + emit_hppa_copy(HPPA_REG_ZERO, hi(rd), ctx); + break; + case 64: + /* Swap upper and lower halves, then each half. */ + emit_hppa_copy(hi(rd), HPPA_REG_T0, ctx); + emit_rev32(lo(rd), hi(rd), ctx); + emit_rev32(HPPA_REG_T0, lo(rd), ctx); + break; + default: + pr_err("bpf-jit: BPF_END imm %d invalid\n", imm); + return -1; + } + + bpf_put_reg64(dst, rd, ctx); + break; + } + /* JUMP off */ + case BPF_JMP | BPF_JA: + paoff = hppa_offset(i, off, ctx); + emit_jump(paoff, false, ctx); + break; + /* function call */ + case BPF_JMP | BPF_CALL: + { + bool fixed; + int ret; + u64 addr; + + ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, &addr, + &fixed); + if (ret < 0) + return ret; + emit_call(fixed, addr, ctx); + break; + } + /* tail call */ + case BPF_JMP | BPF_TAIL_CALL: + REG_SET_SEEN_ALL(ctx); + if (emit_bpf_tail_call(i, ctx)) + return -1; + break; + /* IF (dst COND imm) JUMP off */ + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP32 | BPF_JEQ | BPF_X: + case BPF_JMP32 | BPF_JEQ | BPF_K: + + case BPF_JMP | BPF_JNE | BPF_X: + case BPF_JMP | BPF_JNE | BPF_K: + case BPF_JMP32 | BPF_JNE | BPF_X: + case BPF_JMP32 | BPF_JNE | BPF_K: + + case BPF_JMP | BPF_JLE | BPF_X: + case BPF_JMP | BPF_JLE | BPF_K: + case BPF_JMP32 | BPF_JLE | BPF_X: + case BPF_JMP32 | BPF_JLE | BPF_K: + + case BPF_JMP | BPF_JLT | BPF_X: + case BPF_JMP | BPF_JLT | BPF_K: + case BPF_JMP32 | BPF_JLT | BPF_X: + case BPF_JMP32 | BPF_JLT | BPF_K: + + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP32 | BPF_JGE | BPF_X: + case BPF_JMP32 | BPF_JGE | BPF_K: + + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP32 | BPF_JGT | BPF_X: + case BPF_JMP32 | BPF_JGT | BPF_K: + + case BPF_JMP | BPF_JSLE | BPF_X: + case BPF_JMP | BPF_JSLE | BPF_K: + case BPF_JMP32 | BPF_JSLE | BPF_X: + case BPF_JMP32 | BPF_JSLE | BPF_K: + + case BPF_JMP | BPF_JSLT | BPF_X: + case BPF_JMP | BPF_JSLT | BPF_K: + case BPF_JMP32 | BPF_JSLT | BPF_X: + case BPF_JMP32 | BPF_JSLT | BPF_K: + + case BPF_JMP | BPF_JSGE | BPF_X: + case BPF_JMP | BPF_JSGE | BPF_K: + case BPF_JMP32 | BPF_JSGE | BPF_X: + case BPF_JMP32 | BPF_JSGE | BPF_K: + + case BPF_JMP | BPF_JSGT | BPF_X: + case BPF_JMP | BPF_JSGT | BPF_K: + case BPF_JMP32 | BPF_JSGT | BPF_X: + case BPF_JMP32 | BPF_JSGT | BPF_K: + + case BPF_JMP | BPF_JSET | BPF_X: + case BPF_JMP | BPF_JSET | BPF_K: + case BPF_JMP32 | BPF_JSET | BPF_X: + case BPF_JMP32 | BPF_JSET | BPF_K: + paoff = hppa_offset(i, off, ctx); + if (BPF_SRC(code) == BPF_K) { + s = ctx->ninsns; + emit_imm32(tmp2, imm, ctx); + src = tmp2; + e = ctx->ninsns; + paoff -= (e - s); + } + if (is64) + emit_branch_r64(dst, src, paoff, ctx, BPF_OP(code)); + else + emit_branch_r32(dst, src, paoff, ctx, BPF_OP(code)); + break; + /* function return */ + case BPF_JMP | BPF_EXIT: + if (i == ctx->prog->len - 1) + break; + /* load epilogue function pointer and jump to it. */ + emit(EXIT_PTR_LOAD(HPPA_REG_RP), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + break; + + /* dst = imm64 */ + case BPF_LD | BPF_IMM | BPF_DW: + { + struct bpf_insn insn1 = insn[1]; + u32 upper = insn1.imm; + u32 lower = imm; + const s8 *rd = bpf_get_reg64_ref(dst, tmp1, false, ctx); + + if (0 && bpf_pseudo_func(insn)) { + WARN_ON(upper); /* we are 32-bit! */ + upper = 0; + lower = (uintptr_t) dereference_function_descriptor(lower); + } + + emit_imm64(rd, upper, lower, ctx); + bpf_put_reg64(dst, rd, ctx); + return 1; + } + + /* LDX: dst = *(size *)(src + off) */ + case BPF_LDX | BPF_MEM | BPF_B: + case BPF_LDX | BPF_MEM | BPF_H: + case BPF_LDX | BPF_MEM | BPF_W: + case BPF_LDX | BPF_MEM | BPF_DW: + if (emit_load_r64(dst, src, off, ctx, BPF_SIZE(code))) + return -1; + break; + + /* speculation barrier */ + case BPF_ST | BPF_NOSPEC: + break; + + /* ST: *(size *)(dst + off) = imm */ + case BPF_ST | BPF_MEM | BPF_B: + case BPF_ST | BPF_MEM | BPF_H: + case BPF_ST | BPF_MEM | BPF_W: + case BPF_ST | BPF_MEM | BPF_DW: + + case BPF_STX | BPF_MEM | BPF_B: + case BPF_STX | BPF_MEM | BPF_H: + case BPF_STX | BPF_MEM | BPF_W: + case BPF_STX | BPF_MEM | BPF_DW: + if (BPF_CLASS(code) == BPF_ST) { + emit_imm32(tmp2, imm, ctx); + src = tmp2; + } + + if (emit_store_r64(dst, src, off, ctx, BPF_SIZE(code), + BPF_MODE(code))) + return -1; + break; + + case BPF_STX | BPF_ATOMIC | BPF_W: + case BPF_STX | BPF_ATOMIC | BPF_DW: + pr_info_once( + "bpf-jit: not supported: atomic operation %02x ***\n", + insn->imm); + return -EFAULT; + + default: + pr_err("bpf-jit: unknown opcode %02x\n", code); + return -EINVAL; + } + + return 0; +} + +void bpf_jit_build_prologue(struct hppa_jit_context *ctx) +{ + const s8 *tmp = regmap[TMP_REG_1]; + const s8 *dst, *reg; + int stack_adjust = 0; + int i; + unsigned long addr; + int bpf_stack_adjust; + + /* + * stack on hppa grows up, so if tail calls are used we need to + * allocate the maximum stack size + */ + if (REG_ALL_SEEN(ctx)) + bpf_stack_adjust = MAX_BPF_STACK; + else + bpf_stack_adjust = ctx->prog->aux->stack_depth; + bpf_stack_adjust = round_up(bpf_stack_adjust, STACK_ALIGN); + + /* make space for callee-saved registers. */ + stack_adjust += NR_SAVED_REGISTERS * REG_SIZE; + /* make space for BPF registers on stack. */ + stack_adjust += BPF_JIT_SCRATCH_REGS * REG_SIZE; + /* make space for BPF stack. */ + stack_adjust += bpf_stack_adjust; + /* round up for stack alignment. */ + stack_adjust = round_up(stack_adjust, STACK_ALIGN); + + /* + * The first instruction sets the tail-call-counter (TCC) register. + * This instruction is skipped by tail calls. + * Use a temporary register instead of a caller-saved register initially. + */ + emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_TCC_IN_INIT), ctx); + + /* + * skip all initializations when called as BPF TAIL call. + */ + emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_R1), ctx); + emit(hppa_bne(HPPA_REG_TCC_IN_INIT, HPPA_REG_R1, ctx->prologue_len - 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + + /* set up hppa stack frame. */ + emit_hppa_copy(HPPA_REG_SP, HPPA_REG_R1, ctx); // copy sp,r1 (=prev_sp) + emit(hppa_ldo(stack_adjust, HPPA_REG_SP, HPPA_REG_SP), ctx); // ldo stack_adjust(sp),sp (increase stack) + emit(hppa_stw(HPPA_REG_R1, -REG_SIZE, HPPA_REG_SP), ctx); // stw prev_sp,-0x04(sp) + emit(hppa_stw(HPPA_REG_RP, -0x14, HPPA_REG_SP), ctx); // stw rp,-0x14(sp) + + REG_FORCE_SEEN(ctx, HPPA_REG_T0); + REG_FORCE_SEEN(ctx, HPPA_REG_T1); + REG_FORCE_SEEN(ctx, HPPA_REG_T2); + REG_FORCE_SEEN(ctx, HPPA_REG_T3); + REG_FORCE_SEEN(ctx, HPPA_REG_T4); + REG_FORCE_SEEN(ctx, HPPA_REG_T5); + + /* save callee-save registers. */ + for (i = 3; i <= 18; i++) { + if (OPTIMIZE_HPPA && !REG_WAS_SEEN(ctx, HPPA_R(i))) + continue; + emit(hppa_stw(HPPA_R(i), -REG_SIZE * (8 + (i-3)), HPPA_REG_SP), ctx); // stw ri,-save_area(sp) + } + + /* + * now really set the tail call counter (TCC) register. + */ + if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) + emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_TCC), ctx); + + /* + * save epilogue function pointer for outer TCC call chain. + * The main TCC call stores the final RP on stack. + */ + addr = (uintptr_t) &ctx->insns[ctx->epilogue_offset]; + /* skip first two instructions of exit function, which jump to exit */ + addr += 2 * HPPA_INSN_SIZE; + emit(hppa_ldil(addr, HPPA_REG_T2), ctx); + emit(hppa_ldo(im11(addr), HPPA_REG_T2, HPPA_REG_T2), ctx); + emit(EXIT_PTR_STORE(HPPA_REG_T2), ctx); + + /* load R1 & R2 from registers, R3-R5 from stack. */ + /* use HPPA_REG_R1 which holds the old stack value */ + dst = regmap[BPF_REG_5]; + reg = bpf_get_reg64_ref(dst, tmp, false, ctx); + if (REG_WAS_SEEN(ctx, lo(reg)) | REG_WAS_SEEN(ctx, hi(reg))) { + if (REG_WAS_SEEN(ctx, hi(reg))) + emit(hppa_ldw(-0x48, HPPA_REG_R1, hi(reg)), ctx); + if (REG_WAS_SEEN(ctx, lo(reg))) + emit(hppa_ldw(-0x44, HPPA_REG_R1, lo(reg)), ctx); + bpf_put_reg64(dst, tmp, ctx); + } + + dst = regmap[BPF_REG_4]; + reg = bpf_get_reg64_ref(dst, tmp, false, ctx); + if (REG_WAS_SEEN(ctx, lo(reg)) | REG_WAS_SEEN(ctx, hi(reg))) { + if (REG_WAS_SEEN(ctx, hi(reg))) + emit(hppa_ldw(-0x40, HPPA_REG_R1, hi(reg)), ctx); + if (REG_WAS_SEEN(ctx, lo(reg))) + emit(hppa_ldw(-0x3c, HPPA_REG_R1, lo(reg)), ctx); + bpf_put_reg64(dst, tmp, ctx); + } + + dst = regmap[BPF_REG_3]; + reg = bpf_get_reg64_ref(dst, tmp, false, ctx); + if (REG_WAS_SEEN(ctx, lo(reg)) | REG_WAS_SEEN(ctx, hi(reg))) { + if (REG_WAS_SEEN(ctx, hi(reg))) + emit(hppa_ldw(-0x38, HPPA_REG_R1, hi(reg)), ctx); + if (REG_WAS_SEEN(ctx, lo(reg))) + emit(hppa_ldw(-0x34, HPPA_REG_R1, lo(reg)), ctx); + bpf_put_reg64(dst, tmp, ctx); + } + + dst = regmap[BPF_REG_2]; + reg = bpf_get_reg64_ref(dst, tmp, false, ctx); + if (REG_WAS_SEEN(ctx, lo(reg)) | REG_WAS_SEEN(ctx, hi(reg))) { + if (REG_WAS_SEEN(ctx, hi(reg))) + emit_hppa_copy(HPPA_REG_ARG3, hi(reg), ctx); + if (REG_WAS_SEEN(ctx, lo(reg))) + emit_hppa_copy(HPPA_REG_ARG2, lo(reg), ctx); + bpf_put_reg64(dst, tmp, ctx); + } + + dst = regmap[BPF_REG_1]; + reg = bpf_get_reg64_ref(dst, tmp, false, ctx); + if (REG_WAS_SEEN(ctx, lo(reg)) | REG_WAS_SEEN(ctx, hi(reg))) { + if (REG_WAS_SEEN(ctx, hi(reg))) + emit_hppa_copy(HPPA_REG_ARG1, hi(reg), ctx); + if (REG_WAS_SEEN(ctx, lo(reg))) + emit_hppa_copy(HPPA_REG_ARG0, lo(reg), ctx); + bpf_put_reg64(dst, tmp, ctx); + } + + /* Set up BPF frame pointer. */ + dst = regmap[BPF_REG_FP]; + reg = bpf_get_reg64_ref(dst, tmp, false, ctx); + if (REG_WAS_SEEN(ctx, lo(reg)) | REG_WAS_SEEN(ctx, hi(reg))) { + if (REG_WAS_SEEN(ctx, lo(reg))) + emit(hppa_ldo(-REG_SIZE * (NR_SAVED_REGISTERS + BPF_JIT_SCRATCH_REGS), + HPPA_REG_SP, lo(reg)), ctx); + if (REG_WAS_SEEN(ctx, hi(reg))) + emit_hppa_copy(HPPA_REG_ZERO, hi(reg), ctx); + bpf_put_reg64(dst, tmp, ctx); + } + + emit(hppa_nop(), ctx); +} + +void bpf_jit_build_epilogue(struct hppa_jit_context *ctx) +{ + __build_epilogue(false, ctx); +} diff --git a/arch/parisc/net/bpf_jit_comp64.c b/arch/parisc/net/bpf_jit_comp64.c new file mode 100644 index 000000000..54b0d5e25 --- /dev/null +++ b/arch/parisc/net/bpf_jit_comp64.c @@ -0,0 +1,1209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BPF JIT compiler for PA-RISC (64-bit) + * + * Copyright(c) 2023 Helge Deller <deller@gmx.de> + * + * The code is based on the BPF JIT compiler for RV64 by Björn Töpel. + * + * TODO: + * - check if bpf_jit_needs_zext() is needed (currently enabled) + * - implement arch_prepare_bpf_trampoline(), poke(), ... + */ + +#include <linux/bitfield.h> +#include <linux/bpf.h> +#include <linux/filter.h> +#include <linux/libgcc.h> +#include "bpf_jit.h" + +static const int regmap[] = { + [BPF_REG_0] = HPPA_REG_RET0, + [BPF_REG_1] = HPPA_R(5), + [BPF_REG_2] = HPPA_R(6), + [BPF_REG_3] = HPPA_R(7), + [BPF_REG_4] = HPPA_R(8), + [BPF_REG_5] = HPPA_R(9), + [BPF_REG_6] = HPPA_R(10), + [BPF_REG_7] = HPPA_R(11), + [BPF_REG_8] = HPPA_R(12), + [BPF_REG_9] = HPPA_R(13), + [BPF_REG_FP] = HPPA_R(14), + [BPF_REG_AX] = HPPA_R(15), +}; + +/* + * Stack layout during BPF program execution (note: stack grows up): + * + * high + * HPPA64 sp => +----------+ <= HPPA64 fp + * | saved sp | + * | saved rp | + * | ... | HPPA64 callee-saved registers + * | curr args| + * | local var| + * +----------+ <= (BPF FP) + * | | + * | ... | BPF program stack + * | | + * | ... | Function call stack + * | | + * +----------+ + * low + */ + +/* Offset from fp for BPF registers stored on stack. */ +#define STACK_ALIGN FRAME_SIZE + +#define EXIT_PTR_LOAD(reg) hppa64_ldd_im16(-FRAME_SIZE, HPPA_REG_SP, reg) +#define EXIT_PTR_STORE(reg) hppa64_std_im16(reg, -FRAME_SIZE, HPPA_REG_SP) +#define EXIT_PTR_JUMP(reg, nop) hppa_bv(HPPA_REG_ZERO, reg, nop) + +static u8 bpf_to_hppa_reg(int bpf_reg, struct hppa_jit_context *ctx) +{ + u8 reg = regmap[bpf_reg]; + + REG_SET_SEEN(ctx, reg); + return reg; +}; + +static void emit_hppa_copy(const s8 rs, const s8 rd, struct hppa_jit_context *ctx) +{ + REG_SET_SEEN(ctx, rd); + if (OPTIMIZE_HPPA && (rs == rd)) + return; + REG_SET_SEEN(ctx, rs); + emit(hppa_copy(rs, rd), ctx); +} + +static void emit_hppa64_depd(u8 src, u8 pos, u8 len, u8 target, bool no_zero, struct hppa_jit_context *ctx) +{ + int c; + + pos &= (BITS_PER_LONG - 1); + pos = 63 - pos; + len = 64 - len; + c = (len < 32) ? 0x4 : 0; + c |= (pos >= 32) ? 0x2 : 0; + c |= (no_zero) ? 0x1 : 0; + emit(hppa_t10_insn(0x3c, target, src, 0, c, pos & 0x1f, len & 0x1f), ctx); +} + +static void emit_hppa64_shld(u8 src, int num, u8 target, struct hppa_jit_context *ctx) +{ + emit_hppa64_depd(src, 63-num, 64-num, target, 0, ctx); +} + +static void emit_hppa64_extrd(u8 src, u8 pos, u8 len, u8 target, bool signed_op, struct hppa_jit_context *ctx) +{ + int c; + + pos &= (BITS_PER_LONG - 1); + len = 64 - len; + c = (len < 32) ? 0x4 : 0; + c |= (pos >= 32) ? 0x2 : 0; + c |= signed_op ? 0x1 : 0; + emit(hppa_t10_insn(0x36, src, target, 0, c, pos & 0x1f, len & 0x1f), ctx); +} + +static void emit_hppa64_extrw(u8 src, u8 pos, u8 len, u8 target, bool signed_op, struct hppa_jit_context *ctx) +{ + int c; + + pos &= (32 - 1); + len = 32 - len; + c = 0x06 | (signed_op ? 1 : 0); + emit(hppa_t10_insn(0x34, src, target, 0, c, pos, len), ctx); +} + +#define emit_hppa64_zext32(r, target, ctx) \ + emit_hppa64_extrd(r, 63, 32, target, false, ctx) +#define emit_hppa64_sext32(r, target, ctx) \ + emit_hppa64_extrd(r, 63, 32, target, true, ctx) + +static void emit_hppa64_shrd(u8 src, int num, u8 target, bool signed_op, struct hppa_jit_context *ctx) +{ + emit_hppa64_extrd(src, 63-num, 64-num, target, signed_op, ctx); +} + +static void emit_hppa64_shrw(u8 src, int num, u8 target, bool signed_op, struct hppa_jit_context *ctx) +{ + emit_hppa64_extrw(src, 31-num, 32-num, target, signed_op, ctx); +} + +/* Emit variable-length instructions for 32-bit imm */ +static void emit_imm32(u8 rd, s32 imm, struct hppa_jit_context *ctx) +{ + u32 lower = im11(imm); + + REG_SET_SEEN(ctx, rd); + if (OPTIMIZE_HPPA && relative_bits_ok(imm, 14)) { + emit(hppa_ldi(imm, rd), ctx); + return; + } + if (OPTIMIZE_HPPA && lower == imm) { + emit(hppa_ldo(lower, HPPA_REG_ZERO, rd), ctx); + return; + } + emit(hppa_ldil(imm, rd), ctx); + if (OPTIMIZE_HPPA && (lower == 0)) + return; + emit(hppa_ldo(lower, rd, rd), ctx); +} + +static bool is_32b_int(s64 val) +{ + return val == (s32) val; +} + +/* Emit variable-length instructions for 64-bit imm */ +static void emit_imm(u8 rd, s64 imm, u8 tmpreg, struct hppa_jit_context *ctx) +{ + u32 upper32; + + /* get lower 32-bits into rd, sign extended */ + emit_imm32(rd, imm, ctx); + + /* do we have upper 32-bits too ? */ + if (OPTIMIZE_HPPA && is_32b_int(imm)) + return; + + /* load upper 32-bits into lower tmpreg and deposit into rd */ + upper32 = imm >> 32; + if (upper32 || !OPTIMIZE_HPPA) { + emit_imm32(tmpreg, upper32, ctx); + emit_hppa64_depd(tmpreg, 31, 32, rd, 1, ctx); + } else + emit_hppa64_depd(HPPA_REG_ZERO, 31, 32, rd, 1, ctx); + +} + +static int emit_jump(signed long paoff, bool force_far, + struct hppa_jit_context *ctx) +{ + unsigned long pc, addr; + + /* Note: Use 2 instructions for jumps if force_far is set. */ + if (relative_bits_ok(paoff - HPPA_BRANCH_DISPLACEMENT, 22)) { + /* use BL,long branch followed by nop() */ + emit(hppa64_bl_long(paoff - HPPA_BRANCH_DISPLACEMENT), ctx); + if (force_far) + emit(hppa_nop(), ctx); + return 0; + } + + pc = (uintptr_t) &ctx->insns[ctx->ninsns]; + addr = pc + (paoff * HPPA_INSN_SIZE); + /* even the 64-bit kernel runs in memory below 4GB */ + if (WARN_ON_ONCE(addr >> 32)) + return -E2BIG; + emit(hppa_ldil(addr, HPPA_REG_R31), ctx); + emit(hppa_be_l(im11(addr) >> 2, HPPA_REG_R31, NOP_NEXT_INSTR), ctx); + return 0; +} + +static void __build_epilogue(bool is_tail_call, struct hppa_jit_context *ctx) +{ + int i; + + if (is_tail_call) { + /* + * goto *(t0 + 4); + * Skips first instruction of prologue which initializes tail + * call counter. Assumes t0 contains address of target program, + * see emit_bpf_tail_call. + */ + emit(hppa_ldo(1 * HPPA_INSN_SIZE, HPPA_REG_T0, HPPA_REG_T0), ctx); + emit(hppa_bv(HPPA_REG_ZERO, HPPA_REG_T0, EXEC_NEXT_INSTR), ctx); + /* in delay slot: */ + emit(hppa_copy(HPPA_REG_TCC, HPPA_REG_TCC_IN_INIT), ctx); + + return; + } + + /* load epilogue function pointer and jump to it. */ + /* exit point is either at next instruction, or the outest TCC exit function */ + emit(EXIT_PTR_LOAD(HPPA_REG_RP), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* NOTE: we are 64-bit and big-endian, so return lower sign-extended 32-bit value */ + emit_hppa64_sext32(regmap[BPF_REG_0], HPPA_REG_RET0, ctx); + + /* Restore callee-saved registers. */ + for (i = 3; i <= 15; i++) { + if (OPTIMIZE_HPPA && !REG_WAS_SEEN(ctx, HPPA_R(i))) + continue; + emit(hppa64_ldd_im16(-REG_SIZE * i, HPPA_REG_SP, HPPA_R(i)), ctx); + } + + /* load original return pointer (stored by outest TCC function) */ + emit(hppa64_ldd_im16(-2*REG_SIZE, HPPA_REG_SP, HPPA_REG_RP), ctx); + emit(hppa_bv(HPPA_REG_ZERO, HPPA_REG_RP, EXEC_NEXT_INSTR), ctx); + /* in delay slot: */ + emit(hppa64_ldd_im5(-REG_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); + + emit(hppa_nop(), ctx); // XXX WARUM einer zu wenig ?? +} + +static int emit_branch(u8 op, u8 rd, u8 rs, signed long paoff, + struct hppa_jit_context *ctx) +{ + int e, s; + bool far = false; + int off; + + if (op == BPF_JSET) { + /* + * BPF_JSET is a special case: it has no inverse so translate + * to and() function and compare against zero + */ + emit(hppa_and(rd, rs, HPPA_REG_T0), ctx); + paoff -= 1; /* reduce offset due to hppa_and() above */ + rd = HPPA_REG_T0; + rs = HPPA_REG_ZERO; + op = BPF_JNE; + } + + /* set start after BPF_JSET */ + s = ctx->ninsns; + + if (!relative_branch_ok(paoff - HPPA_BRANCH_DISPLACEMENT + 1, 12)) { + op = invert_bpf_cond(op); + far = true; + } + + /* + * For a far branch, the condition is negated and we jump over the + * branch itself, and the two instructions from emit_jump. + * For a near branch, just use paoff. + */ + off = far ? (2 - HPPA_BRANCH_DISPLACEMENT) : paoff - HPPA_BRANCH_DISPLACEMENT; + + switch (op) { + /* IF (dst COND src) JUMP off */ + case BPF_JEQ: + emit(hppa_beq(rd, rs, off), ctx); + break; + case BPF_JGT: + emit(hppa_bgtu(rd, rs, off), ctx); + break; + case BPF_JLT: + emit(hppa_bltu(rd, rs, off), ctx); + break; + case BPF_JGE: + emit(hppa_bgeu(rd, rs, off), ctx); + break; + case BPF_JLE: + emit(hppa_bleu(rd, rs, off), ctx); + break; + case BPF_JNE: + emit(hppa_bne(rd, rs, off), ctx); + break; + case BPF_JSGT: + emit(hppa_bgt(rd, rs, off), ctx); + break; + case BPF_JSLT: + emit(hppa_blt(rd, rs, off), ctx); + break; + case BPF_JSGE: + emit(hppa_bge(rd, rs, off), ctx); + break; + case BPF_JSLE: + emit(hppa_ble(rd, rs, off), ctx); + break; + default: + WARN_ON(1); + } + + if (far) { + int ret; + e = ctx->ninsns; + /* Adjust for extra insns. */ + paoff -= (e - s); + ret = emit_jump(paoff, true, ctx); + if (ret) + return ret; + } else { + /* + * always allocate 2 nops instead of the far branch to + * reduce translation loops + */ + emit(hppa_nop(), ctx); + emit(hppa_nop(), ctx); + } + return 0; +} + +static void emit_zext_32(u8 reg, struct hppa_jit_context *ctx) +{ + emit_hppa64_zext32(reg, reg, ctx); +} + +static void emit_bpf_tail_call(int insn, struct hppa_jit_context *ctx) +{ + /* + * R1 -> &ctx + * R2 -> &array + * R3 -> index + */ + int off; + const s8 arr_reg = regmap[BPF_REG_2]; + const s8 idx_reg = regmap[BPF_REG_3]; + struct bpf_array bpfa; + struct bpf_prog bpfp; + + /* if there is any tail call, we need to save & restore all registers */ + REG_SET_SEEN_ALL(ctx); + + /* get address of TCC main exit function for error case into rp */ + emit(EXIT_PTR_LOAD(HPPA_REG_RP), ctx); + + /* max_entries = array->map.max_entries; */ + off = offsetof(struct bpf_array, map.max_entries); + BUILD_BUG_ON(sizeof(bpfa.map.max_entries) != 4); + emit(hppa_ldw(off, arr_reg, HPPA_REG_T1), ctx); + + /* + * if (index >= max_entries) + * goto out; + */ + emit(hppa_bltu(idx_reg, HPPA_REG_T1, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* + * if (--tcc < 0) + * goto out; + */ + REG_FORCE_SEEN(ctx, HPPA_REG_TCC); + emit(hppa_ldo(-1, HPPA_REG_TCC, HPPA_REG_TCC), ctx); + emit(hppa_bge(HPPA_REG_TCC, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* + * prog = array->ptrs[index]; + * if (!prog) + * goto out; + */ + BUILD_BUG_ON(sizeof(bpfa.ptrs[0]) != 8); + emit(hppa64_shladd(idx_reg, 3, arr_reg, HPPA_REG_T0), ctx); + off = offsetof(struct bpf_array, ptrs); + BUILD_BUG_ON(off < 16); + emit(hppa64_ldd_im16(off, HPPA_REG_T0, HPPA_REG_T0), ctx); + emit(hppa_bne(HPPA_REG_T0, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); + + /* + * tcc = temp_tcc; + * goto *(prog->bpf_func + 4); + */ + off = offsetof(struct bpf_prog, bpf_func); + BUILD_BUG_ON(off < 16); + BUILD_BUG_ON(sizeof(bpfp.bpf_func) != 8); + emit(hppa64_ldd_im16(off, HPPA_REG_T0, HPPA_REG_T0), ctx); + /* Epilogue jumps to *(t0 + 4). */ + __build_epilogue(true, ctx); +} + +static void init_regs(u8 *rd, u8 *rs, const struct bpf_insn *insn, + struct hppa_jit_context *ctx) +{ + u8 code = insn->code; + + switch (code) { + case BPF_JMP | BPF_JA: + case BPF_JMP | BPF_CALL: + case BPF_JMP | BPF_EXIT: + case BPF_JMP | BPF_TAIL_CALL: + break; + default: + *rd = bpf_to_hppa_reg(insn->dst_reg, ctx); + } + + if (code & (BPF_ALU | BPF_X) || code & (BPF_ALU64 | BPF_X) || + code & (BPF_JMP | BPF_X) || code & (BPF_JMP32 | BPF_X) || + code & BPF_LDX || code & BPF_STX) + *rs = bpf_to_hppa_reg(insn->src_reg, ctx); +} + +static void emit_zext_32_rd_rs(u8 *rd, u8 *rs, struct hppa_jit_context *ctx) +{ + emit_hppa64_zext32(*rd, HPPA_REG_T2, ctx); + *rd = HPPA_REG_T2; + emit_hppa64_zext32(*rs, HPPA_REG_T1, ctx); + *rs = HPPA_REG_T1; +} + +static void emit_sext_32_rd_rs(u8 *rd, u8 *rs, struct hppa_jit_context *ctx) +{ + emit_hppa64_sext32(*rd, HPPA_REG_T2, ctx); + *rd = HPPA_REG_T2; + emit_hppa64_sext32(*rs, HPPA_REG_T1, ctx); + *rs = HPPA_REG_T1; +} + +static void emit_zext_32_rd_t1(u8 *rd, struct hppa_jit_context *ctx) +{ + emit_hppa64_zext32(*rd, HPPA_REG_T2, ctx); + *rd = HPPA_REG_T2; + emit_zext_32(HPPA_REG_T1, ctx); +} + +static void emit_sext_32_rd(u8 *rd, struct hppa_jit_context *ctx) +{ + emit_hppa64_sext32(*rd, HPPA_REG_T2, ctx); + *rd = HPPA_REG_T2; +} + +static bool is_signed_bpf_cond(u8 cond) +{ + return cond == BPF_JSGT || cond == BPF_JSLT || + cond == BPF_JSGE || cond == BPF_JSLE; +} + +static void emit_call(u64 addr, bool fixed, struct hppa_jit_context *ctx) +{ + const int offset_sp = 2*FRAME_SIZE; + + emit(hppa_ldo(offset_sp, HPPA_REG_SP, HPPA_REG_SP), ctx); + + emit_hppa_copy(regmap[BPF_REG_1], HPPA_REG_ARG0, ctx); + emit_hppa_copy(regmap[BPF_REG_2], HPPA_REG_ARG1, ctx); + emit_hppa_copy(regmap[BPF_REG_3], HPPA_REG_ARG2, ctx); + emit_hppa_copy(regmap[BPF_REG_4], HPPA_REG_ARG3, ctx); + emit_hppa_copy(regmap[BPF_REG_5], HPPA_REG_ARG4, ctx); + + /* Backup TCC. */ + REG_FORCE_SEEN(ctx, HPPA_REG_TCC_SAVED); + if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) + emit(hppa_copy(HPPA_REG_TCC, HPPA_REG_TCC_SAVED), ctx); + + /* + * Use ldil() to load absolute address. Don't use emit_imm as the + * number of emitted instructions should not depend on the value of + * addr. + */ + WARN_ON(addr >> 32); + /* load function address and gp from Elf64_Fdesc descriptor */ + emit(hppa_ldil(addr, HPPA_REG_R31), ctx); + emit(hppa_ldo(im11(addr), HPPA_REG_R31, HPPA_REG_R31), ctx); + emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, addr), + HPPA_REG_R31, HPPA_REG_RP), ctx); + emit(hppa64_bve_l_rp(HPPA_REG_RP), ctx); + emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, gp), + HPPA_REG_R31, HPPA_REG_GP), ctx); + + /* Restore TCC. */ + if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) + emit(hppa_copy(HPPA_REG_TCC_SAVED, HPPA_REG_TCC), ctx); + + emit(hppa_ldo(-offset_sp, HPPA_REG_SP, HPPA_REG_SP), ctx); + + /* Set return value. */ + emit_hppa_copy(HPPA_REG_RET0, regmap[BPF_REG_0], ctx); +} + +static void emit_call_libgcc_ll(void *func, const s8 arg0, + const s8 arg1, u8 opcode, struct hppa_jit_context *ctx) +{ + u64 func_addr; + + if (BPF_CLASS(opcode) == BPF_ALU) { + emit_hppa64_zext32(arg0, HPPA_REG_ARG0, ctx); + emit_hppa64_zext32(arg1, HPPA_REG_ARG1, ctx); + } else { + emit_hppa_copy(arg0, HPPA_REG_ARG0, ctx); + emit_hppa_copy(arg1, HPPA_REG_ARG1, ctx); + } + + /* libcgcc overwrites HPPA_REG_RET0, so keep copy in HPPA_REG_TCC_SAVED */ + if (arg0 != HPPA_REG_RET0) { + REG_SET_SEEN(ctx, HPPA_REG_TCC_SAVED); + emit(hppa_copy(HPPA_REG_RET0, HPPA_REG_TCC_SAVED), ctx); + } + + /* set up stack */ + emit(hppa_ldo(FRAME_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); + + func_addr = (uintptr_t) func; + /* load function func_address and gp from Elf64_Fdesc descriptor */ + emit_imm(HPPA_REG_R31, func_addr, arg0, ctx); + emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, addr), + HPPA_REG_R31, HPPA_REG_RP), ctx); + /* skip the following bve_l instruction if divisor is 0. */ + if (BPF_OP(opcode) == BPF_DIV || BPF_OP(opcode) == BPF_MOD) { + if (BPF_OP(opcode) == BPF_DIV) + emit_hppa_copy(HPPA_REG_ZERO, HPPA_REG_RET0, ctx); + else { + emit_hppa_copy(HPPA_REG_ARG0, HPPA_REG_RET0, ctx); + } + emit(hppa_beq(HPPA_REG_ARG1, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); + } + emit(hppa64_bve_l_rp(HPPA_REG_RP), ctx); + emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, gp), + HPPA_REG_R31, HPPA_REG_GP), ctx); + + emit(hppa_ldo(-FRAME_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); + + emit_hppa_copy(HPPA_REG_RET0, arg0, ctx); + + /* restore HPPA_REG_RET0 */ + if (arg0 != HPPA_REG_RET0) + emit(hppa_copy(HPPA_REG_TCC_SAVED, HPPA_REG_RET0), ctx); +} + +static void emit_store(const s8 rd, const s8 rs, s16 off, + struct hppa_jit_context *ctx, const u8 size, + const u8 mode) +{ + s8 dstreg; + + /* need to calculate address since offset does not fit in 14 bits? */ + if (relative_bits_ok(off, 14)) + dstreg = rd; + else { + /* need to use R1 here, since addil puts result into R1 */ + dstreg = HPPA_REG_R1; + emit(hppa_addil(off, rd), ctx); + off = im11(off); + } + + switch (size) { + case BPF_B: + emit(hppa_stb(rs, off, dstreg), ctx); + break; + case BPF_H: + emit(hppa_sth(rs, off, dstreg), ctx); + break; + case BPF_W: + emit(hppa_stw(rs, off, dstreg), ctx); + break; + case BPF_DW: + if (off & 7) { + emit(hppa_ldo(off, dstreg, HPPA_REG_R1), ctx); + emit(hppa64_std_im5(rs, 0, HPPA_REG_R1), ctx); + } else if (off >= -16 && off <= 15) + emit(hppa64_std_im5(rs, off, dstreg), ctx); + else + emit(hppa64_std_im16(rs, off, dstreg), ctx); + break; + } +} + +int bpf_jit_emit_insn(const struct bpf_insn *insn, struct hppa_jit_context *ctx, + bool extra_pass) +{ + bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 || + BPF_CLASS(insn->code) == BPF_JMP; + int s, e, ret, i = insn - ctx->prog->insnsi; + s64 paoff; + struct bpf_prog_aux *aux = ctx->prog->aux; + u8 rd = -1, rs = -1, code = insn->code; + s16 off = insn->off; + s32 imm = insn->imm; + + init_regs(&rd, &rs, insn, ctx); + + switch (code) { + /* dst = src */ + case BPF_ALU | BPF_MOV | BPF_X: + case BPF_ALU64 | BPF_MOV | BPF_X: + if (imm == 1) { + /* Special mov32 for zext */ + emit_zext_32(rd, ctx); + break; + } + if (!is64 && !aux->verifier_zext) + emit_hppa64_zext32(rs, rd, ctx); + else + emit_hppa_copy(rs, rd, ctx); + break; + + /* dst = dst OP src */ + case BPF_ALU | BPF_ADD | BPF_X: + case BPF_ALU64 | BPF_ADD | BPF_X: + emit(hppa_add(rd, rs, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_SUB | BPF_X: + case BPF_ALU64 | BPF_SUB | BPF_X: + emit(hppa_sub(rd, rs, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_AND | BPF_X: + case BPF_ALU64 | BPF_AND | BPF_X: + emit(hppa_and(rd, rs, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_OR | BPF_X: + case BPF_ALU64 | BPF_OR | BPF_X: + emit(hppa_or(rd, rs, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_XOR | BPF_X: + case BPF_ALU64 | BPF_XOR | BPF_X: + emit(hppa_xor(rd, rs, rd), ctx); + if (!is64 && !aux->verifier_zext && rs != rd) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_MUL | BPF_K: + case BPF_ALU64 | BPF_MUL | BPF_K: + emit_imm(HPPA_REG_T1, is64 ? (s64)(s32)imm : (u32)imm, HPPA_REG_T2, ctx); + rs = HPPA_REG_T1; + fallthrough; + case BPF_ALU | BPF_MUL | BPF_X: + case BPF_ALU64 | BPF_MUL | BPF_X: + emit_call_libgcc_ll(__muldi3, rd, rs, code, ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_DIV | BPF_K: + case BPF_ALU64 | BPF_DIV | BPF_K: + emit_imm(HPPA_REG_T1, is64 ? (s64)(s32)imm : (u32)imm, HPPA_REG_T2, ctx); + rs = HPPA_REG_T1; + fallthrough; + case BPF_ALU | BPF_DIV | BPF_X: + case BPF_ALU64 | BPF_DIV | BPF_X: + emit_call_libgcc_ll(&hppa_div64, rd, rs, code, ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_MOD | BPF_K: + case BPF_ALU64 | BPF_MOD | BPF_K: + emit_imm(HPPA_REG_T1, is64 ? (s64)(s32)imm : (u32)imm, HPPA_REG_T2, ctx); + rs = HPPA_REG_T1; + fallthrough; + case BPF_ALU | BPF_MOD | BPF_X: + case BPF_ALU64 | BPF_MOD | BPF_X: + emit_call_libgcc_ll(&hppa_div64_rem, rd, rs, code, ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + + case BPF_ALU | BPF_LSH | BPF_X: + case BPF_ALU64 | BPF_LSH | BPF_X: + emit_hppa64_sext32(rs, HPPA_REG_T0, ctx); + emit(hppa64_mtsarcm(HPPA_REG_T0), ctx); + if (is64) + emit(hppa64_depdz_sar(rd, rd), ctx); + else + emit(hppa_depwz_sar(rd, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_RSH | BPF_X: + case BPF_ALU64 | BPF_RSH | BPF_X: + emit(hppa_mtsar(rs), ctx); + if (is64) + emit(hppa64_shrpd_sar(rd, rd), ctx); + else + emit(hppa_shrpw_sar(rd, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_ARSH | BPF_X: + case BPF_ALU64 | BPF_ARSH | BPF_X: + emit_hppa64_sext32(rs, HPPA_REG_T0, ctx); + emit(hppa64_mtsarcm(HPPA_REG_T0), ctx); + if (is64) + emit(hppa_extrd_sar(rd, rd, 1), ctx); + else + emit(hppa_extrws_sar(rd, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + + /* dst = -dst */ + case BPF_ALU | BPF_NEG: + case BPF_ALU64 | BPF_NEG: + emit(hppa_sub(HPPA_REG_ZERO, rd, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + + /* dst = BSWAP##imm(dst) */ + case BPF_ALU | BPF_END | BPF_FROM_BE: + switch (imm) { + case 16: + /* zero-extend 16 bits into 64 bits */ + emit_hppa64_depd(HPPA_REG_ZERO, 63-16, 64-16, rd, 1, ctx); + break; + case 32: + if (!aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case 64: + /* Do nothing */ + break; + } + break; + + case BPF_ALU | BPF_END | BPF_FROM_LE: + switch (imm) { + case 16: + emit(hppa_extru(rd, 31 - 8, 8, HPPA_REG_T1), ctx); + emit(hppa_depwz(rd, 23, 8, HPPA_REG_T1), ctx); + emit(hppa_extru(HPPA_REG_T1, 31, 16, rd), ctx); + emit_hppa64_extrd(HPPA_REG_T1, 63, 16, rd, 0, ctx); + break; + case 32: + emit(hppa_shrpw(rd, rd, 16, HPPA_REG_T1), ctx); + emit_hppa64_depd(HPPA_REG_T1, 63-16, 8, HPPA_REG_T1, 1, ctx); + emit(hppa_shrpw(rd, HPPA_REG_T1, 8, HPPA_REG_T1), ctx); + emit_hppa64_extrd(HPPA_REG_T1, 63, 32, rd, 0, ctx); + break; + case 64: + emit(hppa64_permh_3210(rd, HPPA_REG_T1), ctx); + emit(hppa64_hshl(HPPA_REG_T1, 8, HPPA_REG_T2), ctx); + emit(hppa64_hshr_u(HPPA_REG_T1, 8, HPPA_REG_T1), ctx); + emit(hppa_or(HPPA_REG_T2, HPPA_REG_T1, rd), ctx); + break; + default: + pr_err("bpf-jit: BPF_END imm %d invalid\n", imm); + return -1; + } + break; + + /* dst = imm */ + case BPF_ALU | BPF_MOV | BPF_K: + case BPF_ALU64 | BPF_MOV | BPF_K: + emit_imm(rd, imm, HPPA_REG_T2, ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + + /* dst = dst OP imm */ + case BPF_ALU | BPF_ADD | BPF_K: + case BPF_ALU64 | BPF_ADD | BPF_K: + if (relative_bits_ok(imm, 14)) { + emit(hppa_ldo(imm, rd, rd), ctx); + } else { + emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); + emit(hppa_add(rd, HPPA_REG_T1, rd), ctx); + } + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_SUB | BPF_K: + case BPF_ALU64 | BPF_SUB | BPF_K: + if (relative_bits_ok(-imm, 14)) { + emit(hppa_ldo(-imm, rd, rd), ctx); + } else { + emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); + emit(hppa_sub(rd, HPPA_REG_T1, rd), ctx); + } + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_AND | BPF_K: + case BPF_ALU64 | BPF_AND | BPF_K: + emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); + emit(hppa_and(rd, HPPA_REG_T1, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_OR | BPF_K: + case BPF_ALU64 | BPF_OR | BPF_K: + emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); + emit(hppa_or(rd, HPPA_REG_T1, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_XOR | BPF_K: + case BPF_ALU64 | BPF_XOR | BPF_K: + emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); + emit(hppa_xor(rd, HPPA_REG_T1, rd), ctx); + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_LSH | BPF_K: + case BPF_ALU64 | BPF_LSH | BPF_K: + if (imm != 0) { + emit_hppa64_shld(rd, imm, rd, ctx); + } + + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_RSH | BPF_K: + case BPF_ALU64 | BPF_RSH | BPF_K: + if (imm != 0) { + if (is64) + emit_hppa64_shrd(rd, imm, rd, false, ctx); + else + emit_hppa64_shrw(rd, imm, rd, false, ctx); + } + + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + case BPF_ALU | BPF_ARSH | BPF_K: + case BPF_ALU64 | BPF_ARSH | BPF_K: + if (imm != 0) { + if (is64) + emit_hppa64_shrd(rd, imm, rd, true, ctx); + else + emit_hppa64_shrw(rd, imm, rd, true, ctx); + } + + if (!is64 && !aux->verifier_zext) + emit_zext_32(rd, ctx); + break; + + /* JUMP off */ + case BPF_JMP | BPF_JA: + paoff = hppa_offset(i, off, ctx); + ret = emit_jump(paoff, false, ctx); + if (ret) + return ret; + break; + + /* IF (dst COND src) JUMP off */ + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP32 | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP32 | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JLT | BPF_X: + case BPF_JMP32 | BPF_JLT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP32 | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JLE | BPF_X: + case BPF_JMP32 | BPF_JLE | BPF_X: + case BPF_JMP | BPF_JNE | BPF_X: + case BPF_JMP32 | BPF_JNE | BPF_X: + case BPF_JMP | BPF_JSGT | BPF_X: + case BPF_JMP32 | BPF_JSGT | BPF_X: + case BPF_JMP | BPF_JSLT | BPF_X: + case BPF_JMP32 | BPF_JSLT | BPF_X: + case BPF_JMP | BPF_JSGE | BPF_X: + case BPF_JMP32 | BPF_JSGE | BPF_X: + case BPF_JMP | BPF_JSLE | BPF_X: + case BPF_JMP32 | BPF_JSLE | BPF_X: + case BPF_JMP | BPF_JSET | BPF_X: + case BPF_JMP32 | BPF_JSET | BPF_X: + paoff = hppa_offset(i, off, ctx); + if (!is64) { + s = ctx->ninsns; + if (is_signed_bpf_cond(BPF_OP(code))) + emit_sext_32_rd_rs(&rd, &rs, ctx); + else + emit_zext_32_rd_rs(&rd, &rs, ctx); + e = ctx->ninsns; + + /* Adjust for extra insns */ + paoff -= (e - s); + } + if (BPF_OP(code) == BPF_JSET) { + /* Adjust for and */ + paoff -= 1; + emit(hppa_and(rs, rd, HPPA_REG_T1), ctx); + emit_branch(BPF_JNE, HPPA_REG_T1, HPPA_REG_ZERO, paoff, + ctx); + } else { + emit_branch(BPF_OP(code), rd, rs, paoff, ctx); + } + break; + + /* IF (dst COND imm) JUMP off */ + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP32 | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP32 | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JLT | BPF_K: + case BPF_JMP32 | BPF_JLT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP32 | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JLE | BPF_K: + case BPF_JMP32 | BPF_JLE | BPF_K: + case BPF_JMP | BPF_JNE | BPF_K: + case BPF_JMP32 | BPF_JNE | BPF_K: + case BPF_JMP | BPF_JSGT | BPF_K: + case BPF_JMP32 | BPF_JSGT | BPF_K: + case BPF_JMP | BPF_JSLT | BPF_K: + case BPF_JMP32 | BPF_JSLT | BPF_K: + case BPF_JMP | BPF_JSGE | BPF_K: + case BPF_JMP32 | BPF_JSGE | BPF_K: + case BPF_JMP | BPF_JSLE | BPF_K: + case BPF_JMP32 | BPF_JSLE | BPF_K: + paoff = hppa_offset(i, off, ctx); + s = ctx->ninsns; + if (imm) { + emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); + rs = HPPA_REG_T1; + } else { + rs = HPPA_REG_ZERO; + } + if (!is64) { + if (is_signed_bpf_cond(BPF_OP(code))) + emit_sext_32_rd(&rd, ctx); + else + emit_zext_32_rd_t1(&rd, ctx); + } + e = ctx->ninsns; + + /* Adjust for extra insns */ + paoff -= (e - s); + emit_branch(BPF_OP(code), rd, rs, paoff, ctx); + break; + case BPF_JMP | BPF_JSET | BPF_K: + case BPF_JMP32 | BPF_JSET | BPF_K: + paoff = hppa_offset(i, off, ctx); + s = ctx->ninsns; + emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); + emit(hppa_and(HPPA_REG_T1, rd, HPPA_REG_T1), ctx); + /* For jset32, we should clear the upper 32 bits of t1, but + * sign-extension is sufficient here and saves one instruction, + * as t1 is used only in comparison against zero. + */ + if (!is64 && imm < 0) + emit_hppa64_sext32(HPPA_REG_T1, HPPA_REG_T1, ctx); + e = ctx->ninsns; + paoff -= (e - s); + emit_branch(BPF_JNE, HPPA_REG_T1, HPPA_REG_ZERO, paoff, ctx); + break; + /* function call */ + case BPF_JMP | BPF_CALL: + { + bool fixed_addr; + u64 addr; + + ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, + &addr, &fixed_addr); + if (ret < 0) + return ret; + + REG_SET_SEEN_ALL(ctx); + emit_call(addr, fixed_addr, ctx); + break; + } + /* tail call */ + case BPF_JMP | BPF_TAIL_CALL: + emit_bpf_tail_call(i, ctx); + break; + + /* function return */ + case BPF_JMP | BPF_EXIT: + if (i == ctx->prog->len - 1) + break; + + paoff = epilogue_offset(ctx); + ret = emit_jump(paoff, false, ctx); + if (ret) + return ret; + break; + + /* dst = imm64 */ + case BPF_LD | BPF_IMM | BPF_DW: + { + struct bpf_insn insn1 = insn[1]; + u64 imm64 = (u64)insn1.imm << 32 | (u32)imm; + if (bpf_pseudo_func(insn)) + imm64 = (uintptr_t)dereference_function_descriptor((void*)imm64); + emit_imm(rd, imm64, HPPA_REG_T2, ctx); + + return 1; + } + + /* LDX: dst = *(size *)(src + off) */ + case BPF_LDX | BPF_MEM | BPF_B: + case BPF_LDX | BPF_MEM | BPF_H: + case BPF_LDX | BPF_MEM | BPF_W: + case BPF_LDX | BPF_MEM | BPF_DW: + case BPF_LDX | BPF_PROBE_MEM | BPF_B: + case BPF_LDX | BPF_PROBE_MEM | BPF_H: + case BPF_LDX | BPF_PROBE_MEM | BPF_W: + case BPF_LDX | BPF_PROBE_MEM | BPF_DW: + { + u8 srcreg; + + /* need to calculate address since offset does not fit in 14 bits? */ + if (relative_bits_ok(off, 14)) + srcreg = rs; + else { + /* need to use R1 here, since addil puts result into R1 */ + srcreg = HPPA_REG_R1; + BUG_ON(rs == HPPA_REG_R1); + BUG_ON(rd == HPPA_REG_R1); + emit(hppa_addil(off, rs), ctx); + off = im11(off); + } + + switch (BPF_SIZE(code)) { + case BPF_B: + emit(hppa_ldb(off, srcreg, rd), ctx); + if (insn_is_zext(&insn[1])) + return 1; + break; + case BPF_H: + emit(hppa_ldh(off, srcreg, rd), ctx); + if (insn_is_zext(&insn[1])) + return 1; + break; + case BPF_W: + emit(hppa_ldw(off, srcreg, rd), ctx); + if (insn_is_zext(&insn[1])) + return 1; + break; + case BPF_DW: + if (off & 7) { + emit(hppa_ldo(off, srcreg, HPPA_REG_R1), ctx); + emit(hppa64_ldd_reg(HPPA_REG_ZERO, HPPA_REG_R1, rd), ctx); + } else if (off >= -16 && off <= 15) + emit(hppa64_ldd_im5(off, srcreg, rd), ctx); + else + emit(hppa64_ldd_im16(off, srcreg, rd), ctx); + break; + } + break; + } + /* speculation barrier */ + case BPF_ST | BPF_NOSPEC: + break; + + /* ST: *(size *)(dst + off) = imm */ + /* STX: *(size *)(dst + off) = src */ + case BPF_ST | BPF_MEM | BPF_B: + case BPF_ST | BPF_MEM | BPF_H: + case BPF_ST | BPF_MEM | BPF_W: + case BPF_ST | BPF_MEM | BPF_DW: + + case BPF_STX | BPF_MEM | BPF_B: + case BPF_STX | BPF_MEM | BPF_H: + case BPF_STX | BPF_MEM | BPF_W: + case BPF_STX | BPF_MEM | BPF_DW: + if (BPF_CLASS(code) == BPF_ST) { + emit_imm(HPPA_REG_T2, imm, HPPA_REG_T1, ctx); + rs = HPPA_REG_T2; + } + + emit_store(rd, rs, off, ctx, BPF_SIZE(code), BPF_MODE(code)); + break; + + case BPF_STX | BPF_ATOMIC | BPF_W: + case BPF_STX | BPF_ATOMIC | BPF_DW: + pr_info_once( + "bpf-jit: not supported: atomic operation %02x ***\n", + insn->imm); + return -EFAULT; + + default: + pr_err("bpf-jit: unknown opcode %02x\n", code); + return -EINVAL; + } + + return 0; +} + +void bpf_jit_build_prologue(struct hppa_jit_context *ctx) +{ + int bpf_stack_adjust, stack_adjust, i; + unsigned long addr; + s8 reg; + + /* + * stack on hppa grows up, so if tail calls are used we need to + * allocate the maximum stack size + */ + if (REG_ALL_SEEN(ctx)) + bpf_stack_adjust = MAX_BPF_STACK; + else + bpf_stack_adjust = ctx->prog->aux->stack_depth; + bpf_stack_adjust = round_up(bpf_stack_adjust, STACK_ALIGN); + + stack_adjust = FRAME_SIZE + bpf_stack_adjust; + stack_adjust = round_up(stack_adjust, STACK_ALIGN); + + /* + * NOTE: We construct an Elf64_Fdesc descriptor here. + * The first 4 words initialize the TCC and compares them. + * Then follows the virtual address of the eBPF function, + * and the gp for this function. + * + * The first instruction sets the tail-call-counter (TCC) register. + * This instruction is skipped by tail calls. + * Use a temporary register instead of a caller-saved register initially. + */ + REG_FORCE_SEEN(ctx, HPPA_REG_TCC_IN_INIT); + emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_TCC_IN_INIT), ctx); + + /* + * Skip all initializations when called as BPF TAIL call. + */ + emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_R1), ctx); + emit(hppa_beq(HPPA_REG_TCC_IN_INIT, HPPA_REG_R1, 6 - HPPA_BRANCH_DISPLACEMENT), ctx); + emit(hppa64_bl_long(ctx->prologue_len - 3 - HPPA_BRANCH_DISPLACEMENT), ctx); + + /* store entry address of this eBPF function */ + addr = (uintptr_t) &ctx->insns[0]; + emit(addr >> 32, ctx); + emit(addr & 0xffffffff, ctx); + + /* store gp of this eBPF function */ + asm("copy %%r27,%0" : "=r" (addr) ); + emit(addr >> 32, ctx); + emit(addr & 0xffffffff, ctx); + + /* Set up hppa stack frame. */ + emit_hppa_copy(HPPA_REG_SP, HPPA_REG_R1, ctx); + emit(hppa_ldo(stack_adjust, HPPA_REG_SP, HPPA_REG_SP), ctx); + emit(hppa64_std_im5 (HPPA_REG_R1, -REG_SIZE, HPPA_REG_SP), ctx); + emit(hppa64_std_im16(HPPA_REG_RP, -2*REG_SIZE, HPPA_REG_SP), ctx); + + /* Save callee-save registers. */ + for (i = 3; i <= 15; i++) { + if (OPTIMIZE_HPPA && !REG_WAS_SEEN(ctx, HPPA_R(i))) + continue; + emit(hppa64_std_im16(HPPA_R(i), -REG_SIZE * i, HPPA_REG_SP), ctx); + } + + /* load function parameters; load all if we use tail functions */ + #define LOAD_PARAM(arg, dst) \ + if (REG_WAS_SEEN(ctx, regmap[dst]) || \ + REG_WAS_SEEN(ctx, HPPA_REG_TCC)) \ + emit_hppa_copy(arg, regmap[dst], ctx) + LOAD_PARAM(HPPA_REG_ARG0, BPF_REG_1); + LOAD_PARAM(HPPA_REG_ARG1, BPF_REG_2); + LOAD_PARAM(HPPA_REG_ARG2, BPF_REG_3); + LOAD_PARAM(HPPA_REG_ARG3, BPF_REG_4); + LOAD_PARAM(HPPA_REG_ARG4, BPF_REG_5); + #undef LOAD_PARAM + + REG_FORCE_SEEN(ctx, HPPA_REG_T0); + REG_FORCE_SEEN(ctx, HPPA_REG_T1); + REG_FORCE_SEEN(ctx, HPPA_REG_T2); + + /* + * Now really set the tail call counter (TCC) register. + */ + if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) + emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_TCC), ctx); + + /* + * Save epilogue function pointer for outer TCC call chain. + * The main TCC call stores the final RP on stack. + */ + addr = (uintptr_t) &ctx->insns[ctx->epilogue_offset]; + /* skip first two instructions which jump to exit */ + addr += 2 * HPPA_INSN_SIZE; + emit_imm(HPPA_REG_T2, addr, HPPA_REG_T1, ctx); + emit(EXIT_PTR_STORE(HPPA_REG_T2), ctx); + + /* Set up BPF frame pointer. */ + reg = regmap[BPF_REG_FP]; /* -> HPPA_REG_FP */ + if (REG_WAS_SEEN(ctx, reg)) { + emit(hppa_ldo(-FRAME_SIZE, HPPA_REG_SP, reg), ctx); + } +} + +void bpf_jit_build_epilogue(struct hppa_jit_context *ctx) +{ + __build_epilogue(false, ctx); +} + +bool bpf_jit_supports_kfunc_call(void) +{ + return true; +} diff --git a/arch/parisc/net/bpf_jit_core.c b/arch/parisc/net/bpf_jit_core.c new file mode 100644 index 000000000..172770132 --- /dev/null +++ b/arch/parisc/net/bpf_jit_core.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common functionality for HPPA32 and HPPA64 BPF JIT compilers + * + * Copyright (c) 2023 Helge Deller <deller@gmx.de> + * + */ + +#include <linux/bpf.h> +#include <linux/filter.h> +#include "bpf_jit.h" + +/* Number of iterations to try until offsets converge. */ +#define NR_JIT_ITERATIONS 35 + +static int build_body(struct hppa_jit_context *ctx, bool extra_pass, int *offset) +{ + const struct bpf_prog *prog = ctx->prog; + int i; + + ctx->reg_seen_collect = true; + for (i = 0; i < prog->len; i++) { + const struct bpf_insn *insn = &prog->insnsi[i]; + int ret; + + ret = bpf_jit_emit_insn(insn, ctx, extra_pass); + /* BPF_LD | BPF_IMM | BPF_DW: skip the next instruction. */ + if (ret > 0) + i++; + if (offset) + offset[i] = ctx->ninsns; + if (ret < 0) + return ret; + } + ctx->reg_seen_collect = false; + return 0; +} + +bool bpf_jit_needs_zext(void) +{ + return true; +} + +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) +{ + unsigned int prog_size = 0, extable_size = 0; + bool extra_pass = false; + int pass = 0, prev_ninsns = 0, prologue_len, i; + struct hppa_jit_data *jit_data; + struct hppa_jit_context *ctx; + + if (!prog->jit_requested) + return prog; + + jit_data = prog->aux->jit_data; + if (!jit_data) { + jit_data = kzalloc_obj(*jit_data); + if (!jit_data) + return prog; + prog->aux->jit_data = jit_data; + } + + ctx = &jit_data->ctx; + + if (ctx->offset) { + extra_pass = true; + prog_size = sizeof(*ctx->insns) * ctx->ninsns; + goto skip_init_ctx; + } + + ctx->prog = prog; + ctx->offset = kzalloc_objs(int, prog->len); + if (!ctx->offset) + goto out_err; + for (i = 0; i < prog->len; i++) { + prev_ninsns += 20; + ctx->offset[i] = prev_ninsns; + } + + for (i = 0; i < NR_JIT_ITERATIONS; i++) { + pass++; + ctx->ninsns = 0; + if (build_body(ctx, extra_pass, ctx->offset)) + goto out_err; + ctx->body_len = ctx->ninsns; + bpf_jit_build_prologue(ctx); + ctx->prologue_len = ctx->ninsns - ctx->body_len; + ctx->epilogue_offset = ctx->ninsns; + bpf_jit_build_epilogue(ctx); + + if (ctx->ninsns == prev_ninsns) { + if (jit_data->header) + break; + /* obtain the actual image size */ + extable_size = prog->aux->num_exentries * + sizeof(struct exception_table_entry); + prog_size = sizeof(*ctx->insns) * ctx->ninsns; + + jit_data->header = + bpf_jit_binary_alloc(prog_size + extable_size, + &jit_data->image, + sizeof(long), + bpf_fill_ill_insns); + if (!jit_data->header) + goto out_err; + + ctx->insns = (u32 *)jit_data->image; + /* + * Now, when the image is allocated, the image can + * potentially shrink more (auipc/jalr -> jal). + */ + } + prev_ninsns = ctx->ninsns; + } + + if (i == NR_JIT_ITERATIONS) { + pr_err("bpf-jit: image did not converge in <%d passes!\n", i); + if (jit_data->header) + bpf_jit_binary_free(jit_data->header); + goto out_err; + } + + if (extable_size) + prog->aux->extable = (void *)ctx->insns + prog_size; + +skip_init_ctx: + pass++; + ctx->ninsns = 0; + + bpf_jit_build_prologue(ctx); + if (build_body(ctx, extra_pass, NULL)) { + bpf_jit_binary_free(jit_data->header); + goto out_err; + } + bpf_jit_build_epilogue(ctx); + + if (HPPA_JIT_DEBUG || bpf_jit_enable > 1) { + if (HPPA_JIT_DUMP) + bpf_jit_dump(prog->len, prog_size, pass, ctx->insns); + if (HPPA_JIT_REBOOT) + { extern int machine_restart(char *); machine_restart(""); } + } + + if (!prog->is_func || extra_pass) { + if (bpf_jit_binary_lock_ro(jit_data->header)) { + bpf_jit_binary_free(jit_data->header); + goto out_err; + } + bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns); + } + + prog->bpf_func = (void *)ctx->insns; + prog->jited = 1; + prog->jited_len = prog_size; + + if (!prog->is_func || extra_pass) { + prologue_len = ctx->epilogue_offset - ctx->body_len; + for (i = 0; i < prog->len; i++) + ctx->offset[i] += prologue_len; + bpf_prog_fill_jited_linfo(prog, ctx->offset); +out_offset: + kfree(ctx->offset); + kfree(jit_data); + prog->aux->jit_data = NULL; + } + + if (HPPA_JIT_REBOOT) + { extern int machine_restart(char *); machine_restart(""); } + + return prog; + +out_err: + if (extra_pass) { + prog->bpf_func = NULL; + prog->jited = 0; + prog->jited_len = 0; + } + goto out_offset; +} + +u64 hppa_div64(u64 div, u64 divisor) +{ + div = div64_u64(div, divisor); + return div; +} + +u64 hppa_div64_rem(u64 div, u64 divisor) +{ + u64 rem; + div64_u64_rem(div, divisor, &rem); + return rem; +} diff --git a/arch/parisc/video/Makefile b/arch/parisc/video/Makefile new file mode 100644 index 000000000..b5db5b428 --- /dev/null +++ b/arch/parisc/video/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_STI_CORE) += video-sti.o diff --git a/arch/parisc/video/video-sti.c b/arch/parisc/video/video-sti.c new file mode 100644 index 000000000..564661e87 --- /dev/null +++ b/arch/parisc/video/video-sti.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2001-2020 Helge Deller <deller@gmx.de> + * Copyright (C) 2001-2002 Thomas Bogendoerfer <tsbogend@alpha.franken.de> + */ + +#include <linux/module.h> + +#include <video/sticore.h> + +#include <asm/video.h> + +bool video_is_primary_device(struct device *dev) +{ + struct sti_struct *sti; + + sti = sti_get_rom(0); + + /* if no built-in graphics card found, allow any fb driver as default */ + if (!sti) + return true; + + /* return true if it's the default built-in framebuffer driver */ + return (sti->dev == dev); +} +EXPORT_SYMBOL(video_is_primary_device); |
