diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 694e8f80738a..491aa4a9dd25 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4032,7 +4032,7 @@ Kernel parameters poison checking when freeing elements back to the pool. Useful for debugging mempool corruption. - memtest= [KNL,X86,ARM,M68K,PPC,RISCV,EARLY] Enable memtest + memtest= [KNL,ALPHA,X86,ARM,M68K,PPC,RISCV,EARLY] Enable memtest Format: default : 0 Specifies the number of memtest passes to be diff --git a/Documentation/features/debug/debug-vm-pgtable/arch-support.txt b/Documentation/features/debug/debug-vm-pgtable/arch-support.txt index 156687a7436d..b4e2374fe201 100644 --- a/Documentation/features/debug/debug-vm-pgtable/arch-support.txt +++ b/Documentation/features/debug/debug-vm-pgtable/arch-support.txt @@ -6,7 +6,7 @@ ----------------------- | arch |status| ----------------------- - | alpha: | TODO | + | alpha: | ok | | arc: | ok | | arm: | TODO | | arm64: | ok | diff --git a/Documentation/features/debug/gcov-profile-all/arch-support.txt b/Documentation/features/debug/gcov-profile-all/arch-support.txt index 63494bddc263..4701209b869d 100644 --- a/Documentation/features/debug/gcov-profile-all/arch-support.txt +++ b/Documentation/features/debug/gcov-profile-all/arch-support.txt @@ -6,7 +6,7 @@ ----------------------- | arch |status| ----------------------- - | alpha: | TODO | + | alpha: | ok | | arc: | TODO | | arm: | ok | | arm64: | ok | diff --git a/Documentation/features/debug/kmemleak/arch-support.txt b/Documentation/features/debug/kmemleak/arch-support.txt index f45149cfa313..b65b3f8c0e89 100644 --- a/Documentation/features/debug/kmemleak/arch-support.txt +++ b/Documentation/features/debug/kmemleak/arch-support.txt @@ -6,7 +6,7 @@ ----------------------- | arch |status| ----------------------- - | alpha: | TODO | + | alpha: | ok | | arc: | ok | | arm: | ok | | arm64: | ok | diff --git a/Documentation/features/perf/perf-regs/arch-support.txt b/Documentation/features/perf/perf-regs/arch-support.txt index 4c9a5a012075..7d736d678fd4 100644 --- a/Documentation/features/perf/perf-regs/arch-support.txt +++ b/Documentation/features/perf/perf-regs/arch-support.txt @@ -6,7 +6,7 @@ ----------------------- | arch |status| ----------------------- - | alpha: | TODO | + | alpha: | ok | | arc: | TODO | | arm: | ok | | arm64: | ok | diff --git a/Documentation/features/perf/perf-stackdump/arch-support.txt b/Documentation/features/perf/perf-stackdump/arch-support.txt index dd362b5cb638..17b84ea7769d 100644 --- a/Documentation/features/perf/perf-stackdump/arch-support.txt +++ b/Documentation/features/perf/perf-stackdump/arch-support.txt @@ -6,7 +6,7 @@ ----------------------- | arch |status| ----------------------- - | alpha: | TODO | + | alpha: | ok | | arc: | TODO | | arm: | ok | | arm64: | ok | diff --git a/Documentation/features/vm/pte_special/arch-support.txt b/Documentation/features/vm/pte_special/arch-support.txt index 3f777f8b67d5..1485bb7c73eb 100644 --- a/Documentation/features/vm/pte_special/arch-support.txt +++ b/Documentation/features/vm/pte_special/arch-support.txt @@ -6,7 +6,7 @@ ----------------------- | arch |status| ----------------------- - | alpha: | TODO | + | alpha: | ok | | arc: | ok | | arm: | ok | | arm64: | ok | diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index e53ef2d88463..8fafbfabd1e5 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -2,37 +2,66 @@ config ALPHA bool default y + select GENERIC_ENTRY + select HAVE_SYSCALL_TRACEPOINTS select ARCH_32BIT_USTAT_F_TINODE select ARCH_HAS_CURRENT_STACK_POINTER + select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DMA_OPS if PCI + select ARCH_HAS_FAST_MULTIPLIER + select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE + select ARCH_HAS_PTE_SPECIAL + select ARCH_HAS_UBSAN select ARCH_MIGHT_HAVE_PC_PARPORT select ARCH_MIGHT_HAVE_PC_SERIO select ARCH_MODULE_NEEDS_WEAK_PER_CPU if SMP select ARCH_NO_PREEMPT select ARCH_NO_SG_CHAIN + select ARCH_SUPPORTS_ATOMIC_RMW + select ARCH_SUPPORTS_HUGETLBFS + select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 + select ARCH_SUPPORTS_PAGE_TABLE_CHECK select ARCH_USE_CMPXCHG_LOCKREF + select ARCH_USE_MEMTEST select FORCE_PCI select PCI_DOMAINS if PCI select PCI_SYSCALL if PCI + select HAVE_ARCH_COMPILER_H select HAVE_ASM_MODVERSIONS + select HAVE_DEBUG_KMEMLEAK + select HAVE_GUP_FAST + 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 HAVE_KERNEL_ZSTD select HAVE_PAGE_SIZE_8KB select HAVE_PCSPKR_PLATFORM select HAVE_PERF_EVENTS + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP select NEED_DMA_MAP_STATE select NEED_SG_DMA_LENGTH select GENERIC_IRQ_PROBE select GENERIC_PCI_IOMAP select AUTO_IRQ_AFFINITY if SMP select GENERIC_IRQ_SHOW + select ARCH_WANT_IRQS_OFF_ACTIVATE_MM select ARCH_WANT_IPC_PARSE_VERSION + select ARCH_WANT_LD_ORPHAN_WARN select ARCH_HAVE_NMI_SAFE_CMPXCHG select AUDIT_ARCH + select EDAC_SUPPORT select GENERIC_CPU_VULNERABILITIES select GENERIC_SMP_IDLE_THREAD select HAS_IOPORT select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_SECCOMP select HAVE_ARCH_SECCOMP_FILTER + select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_TRACEHOOK select HAVE_MOD_ARCH_SPECIFIC select LOCK_MM_AND_FIND_VMA @@ -43,7 +72,9 @@ config ALPHA select CPU_NO_EFFICIENT_FFS if !ALPHA_EV67 select MMU_GATHER_NO_RANGE select MMU_GATHER_RCU_TABLE_FREE + select SPARSE_IRQ select SPARSEMEM_EXTREME if SPARSEMEM + select SYSCTL_EXCEPTION_TRACE select ZONE_DMA select TRACE_IRQFLAGS_SUPPORT select ARCH_WANT_FRAME_POINTERS @@ -98,27 +129,35 @@ choice This is the system type of your hardware. A "generic" kernel will run on any supported Alpha system. However, if you configure a kernel for your specific system, it will be faster and smaller. + Only EV56 (21164A) and later CPUs are supported. - To find out what type of Alpha system you have, you may want to - check out the Linux/Alpha FAQ, accessible on the WWW from - . In summary: + To find out what type of Alpha system you have, see the system and + motherboard lists at + and + . In summary: - Alcor/Alpha-XLT AS 600, AS 500, XL-300, XL-366 - DP264 DP264 / DS20 / ES40 / DS10 / DS10L + Alcor/Alpha-XLT AlphaStation 500, Alpha XL 366, 433 + DP264 DP264, UP2000(+), DS10, DS10L, DS20, DS20E, + ES40, XP900, XP1000 + Eiger Eiger (OEM) LX164 AlphaPC164-LX + Marvel AlphaServer ES47, ES80, GS1280 Miata Personal Workstation 433/500/600 a/au - Marvel AlphaServer ES47 / ES80 / GS1280 - Mikasa AS 1000 - Noritake AS 1000A, AS 600A, AS 800 + Mikasa AlphaServer 1000 + Nautilus UP1000, UP1100, UP1500 + Noritake AlphaServer 1000A, 800, AlphaStation 600A, + Digital Server 3300 PC164 AlphaPC164 - Rawhide AS 1200, AS 4000, AS 4100 + Rawhide AlphaServer 1200, 4000, 4100, + Digital Server 5300, 7300 Ruffian RPX164-2, AlphaPC164-UX, AlphaPC164-BX + RX164 AlphaPC164-RX SX164 AlphaPC164-SX - Sable AS 2000, AS 2100 - Shark DS 20L + Sable AlphaServer 2000, 2100 + Shark AlphaServer DS20L, API NetWorks CS20 Takara Takara (OEM) - Titan AlphaServer ES45 / DS25 / DS15 - Wildfire AlphaServer GS 40/80/160/320 + Titan AlphaServer DS15, DS25, ES45, TS202c + Wildfire AlphaServer GS80, GS160, GS320 If you don't know what to do, choose "generic". @@ -139,13 +178,24 @@ config ALPHA_ALCOR controller (256-bit memory bus) and a PCI interface. It also does all the work required to support an external Bcache and to maintain memory coherence when a PCI device DMAs into (or out of) memory. + It is used by the AlphaStation 500 and the Alpha XL 366 and 433. + + See . config ALPHA_DP264 bool "DP264" help Various 21264 systems with the tsunami core logic chipset. - API Networks: 264DP, UP2000(+), CS20; - Compaq: DS10(E,L), XP900, XP1000, DS20(E), ES40. + API NetWorks: 264DP, UP2000(+); + Compaq: DS10, DS10L, XP900, XP1000, DS20, DS20E, ES40. + + See: + + + + + + config ALPHA_EIGER bool "Eiger" @@ -153,63 +203,100 @@ config ALPHA_EIGER Apparently an obscure OEM single-board computer based on the Typhoon/Tsunami chipset family. Information on it is scanty. + See . + config ALPHA_LX164 bool "LX164" help - A technical overview of this board is available at - . + AlphaPC164-LX, a 21164A-based single-board computer using the + Pyxis chipset. + + See . config ALPHA_MARVEL bool "Marvel" help AlphaServer ES47 / ES80 / GS1280 based on EV7. + See . + config ALPHA_MIATA bool "Miata" select HAVE_EISA help - The Digital PersonalWorkStation (PWS 433a, 433au, 500a, 500au, 600a, - or 600au). + The Digital Personal Workstation (PWS 433a, 433au, 500a, 500au, + 600a, or 600au). + + See . config ALPHA_MIKASA bool "Mikasa" help AlphaServer 1000-based Alpha systems. + See . + config ALPHA_NAUTILUS bool "Nautilus" help - Alpha systems based on the AMD 751 & ALI 1543C chipsets. + UP1000, UP1100 and UP1500 systems based on the AMD 751 or 761 + chipset. + + See: + + + config ALPHA_NORITAKE bool "Noritake" select HAVE_EISA help - AlphaServer 1000A, AlphaServer 600A, and AlphaServer 800-based - systems. + AlphaServer 1000A, AlphaServer 800, AlphaStation 600A and + Digital Server 3300 systems. + + See . config ALPHA_PC164 bool "PC164" + help + AlphaPC164, a 21164A-based single-board computer using the CIA + chipset. + + See . config ALPHA_RAWHIDE bool "Rawhide" select HAVE_EISA help - AlphaServer 1200, AlphaServer 4000 and AlphaServer 4100 machines. - See HOWTO at - . + AlphaServer 1200, 4000 and 4100 and Digital Server 5300 and 7300 + machines. + + See . config ALPHA_RUFFIAN bool "Ruffian" help - Samsung APC164UX. There is a page on known problems and workarounds - at . + DeskStation RPX164-2, AlphaPC164-UX and AlphaPC164-BX boards. + They have no SRM console and can only boot Linux through MILO. + + See . config ALPHA_RX164 bool "RX164" + help + AlphaPC164-RX, a 21164PC-based single-board computer using the + Polaris chipset. It has no SRM console and can only boot Linux + through MILO. + + See . config ALPHA_SX164 bool "SX164" + help + AlphaPC164-SX, a 21164PC-based single-board computer using the + Pyxis chipset. + + See . config ALPHA_SABLE bool "Sable" @@ -217,23 +304,38 @@ config ALPHA_SABLE help Digital AlphaServer 2000 and 2100-based systems. + See . + config ALPHA_SHARK bool "Shark" + help + AlphaServer DS20L and API NetWorks CS20 systems based on the + Tsunami chipset. + + See . config ALPHA_TAKARA bool "Takara" help - Alpha 11164-based OEM single-board computer. + Alpha 21164-based OEM single-board computer. + + See . config ALPHA_TITAN bool "Titan" help - AlphaServer ES45/DS25 SMP based on EV68 and Titan chipset. + AlphaServer DS15, DS25, ES45 and TS202c based on EV68 and the + Titan chipset. + + See . config ALPHA_WILDFIRE bool "Wildfire" help - AlphaServer GS 40/80/160/320 SMP based on the EV67 core. + AlphaServer GS80, GS160 and GS320 SMP based on the EV67 or EV68 + core. + + See . endchoice @@ -344,9 +446,7 @@ config ALPHA_SRM help There are two different types of booting firmware on Alphas: SRM, which is command line driven, and ARC, which uses menus and arrow - keys. Details about the Linux/Alpha booting process are contained in - the Linux/Alpha FAQ, accessible on the WWW from - . + keys. The usual way to load Linux on an Alpha machine is to use MILO (a bootloader that lets you pass command line parameters to the @@ -354,10 +454,9 @@ config ALPHA_SRM loaded either from ARC or can be installed directly as a permanent firmware replacement from floppy (which requires changing a certain jumper on the motherboard). If you want to do either of these, say N - here. If MILO doesn't work on your system (true for Jensen - motherboards), you can bypass it altogether and boot Linux directly - from an SRM console; say Y here in order to do that. Note that you - won't be able to boot from an IDE disk using SRM. + here. If MILO doesn't work on your system, you can bypass it + altogether and boot Linux directly from an SRM console; say Y here + in order to do that. If unsure, say N. @@ -387,11 +486,11 @@ config NR_CPUS int "Maximum number of CPUs (2-32)" range 2 32 depends on SMP - default "32" if ALPHA_GENERIC || ALPHA_MARVEL - default "4" if !ALPHA_GENERIC && !ALPHA_MARVEL + default "32" if ALPHA_GENERIC || ALPHA_MARVEL || ALPHA_WILDFIRE + default "4" help - MARVEL support can handle a maximum of 32 CPUs, all the others - with working support have a maximum of 4 CPUs. + The kernel supports at most 32 CPUs. Only Marvel and Wildfire + systems have more than 4. config ARCH_SPARSEMEM_ENABLE bool "Sparse Memory Support" @@ -415,8 +514,8 @@ config ALPHA_WTINT For emulation under QEMU, definitely say Y here, as we have other mechanisms for measuring time than the cycle counter. - For EV4 (but not LCA), EV5 and EV56 systems, or for systems running - MILO, sleep mode is not supported so you might as well say N here. + For EV56 systems, or for systems running MILO, sleep mode is not + supported so you might as well say N here. For SMP systems we cannot use the cycle counter for timing anyway, so you might as well say Y here. diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile index 7074602dca94..405d4bfd644c 100644 --- a/arch/alpha/Makefile +++ b/arch/alpha/Makefile @@ -47,9 +47,9 @@ export LIBS_Y boot := arch/alpha/boot #Default target when executing make with no arguments -all boot: $(boot)/vmlinux.gz +all boot: $(boot)/vmlinuz -$(boot)/vmlinux.gz: vmlinux +$(boot)/vmlinuz: vmlinux $(Q)$(MAKE) $(build)=$(boot) $@ bootimage bootpfile bootpzfile: vmlinux @@ -59,7 +59,7 @@ archheaders: $(Q)$(MAKE) $(build)=arch/alpha/kernel/syscalls all define archhelp - echo '* boot - Compressed kernel image (arch/alpha/boot/vmlinux.gz)' + echo '* boot - Compressed kernel image (arch/alpha/boot/vmlinuz)' echo ' bootimage - SRM bootable image (arch/alpha/boot/bootimage)' echo ' bootpfile - BOOTP bootable image (arch/alpha/boot/bootpfile)' echo ' bootpzfile - compressed kernel BOOTP image (arch/alpha/boot/bootpzfile)' diff --git a/arch/alpha/boot/.gitignore b/arch/alpha/boot/.gitignore new file mode 100644 index 000000000000..4125506521f6 --- /dev/null +++ b/arch/alpha/boot/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +vmlinuz diff --git a/arch/alpha/boot/Makefile b/arch/alpha/boot/Makefile index d8dba85e606c..7c2a41c1e2a0 100644 --- a/arch/alpha/boot/Makefile +++ b/arch/alpha/boot/Makefile @@ -9,9 +9,10 @@ # hostprogs := tools/mkbb tools/objstrip -targets := vmlinux.gz vmlinux \ +targets := vmlinuz vmlinux \ vmlinux.nh tools/lxboot tools/bootlx tools/bootph \ - tools/bootpzh bootloader bootpheader bootpzheader + tools/bootpzh bootloader bootpheader bootpzheader +subdir- := compressed OBJSTRIP := $(obj)/tools/objstrip KBUILD_HOSTCFLAGS := -Wall -I$(objtree)/usr/include @@ -38,9 +39,14 @@ ifdef INITRD cat $(INITRD) >> $@ endif -# Compressed kernel image -$(obj)/vmlinux.gz: $(obj)/vmlinux FORCE - $(call if_changed,gzip) +# Self-extracting compressed kernel image: a small ELF stub (see +# compressed/) with the real vmlinux linked in compressed, which aboot +# loads and enters like a plain vmlinux. +$(obj)/compressed/vmlinux: vmlinux FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + +$(obj)/vmlinuz: $(obj)/compressed/vmlinux FORCE + $(call if_changed,copy) @echo ' Kernel $@ is ready' $(obj)/main.o: $(obj)/ksize.h diff --git a/arch/alpha/boot/compressed/.gitignore b/arch/alpha/boot/compressed/.gitignore new file mode 100644 index 000000000000..765a08f1bd77 --- /dev/null +++ b/arch/alpha/boot/compressed/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +vmlinux +vmlinux.lds diff --git a/arch/alpha/boot/compressed/Makefile b/arch/alpha/boot/compressed/Makefile new file mode 100644 index 000000000000..692a29588aad --- /dev/null +++ b/arch/alpha/boot/compressed/Makefile @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Create a self-extracting compressed vmlinux image from the original +# vmlinux. aboot loads and enters it like a plain vmlinux. +# + +OBJECTS := head.o misc.o trampoline.o piggy.o +OBJECTS += memcpy.o memmove.o memset.o +OBJECTS += __divqu.o __remqu.o __divlu.o __remlu.o +targets := vmlinux.lds vmlinux vmlinux.bin +targets += vmlinux.bin.bz2 vmlinux.bin.gz vmlinux.bin.lz4 +targets += vmlinux.bin.lzma vmlinux.bin.lzo vmlinux.bin.xz +targets += vmlinux.bin.zst +targets += $(OBJECTS) + +# The stub runs before the kernel has identified the CPU it booted on, so +# build it for the base Alpha instruction set rather than inheriting the +# kernel's -mcpu=. +KBUILD_CFLAGS := -D__KERNEL__ -std=gnu11 -Os -fno-strict-aliasing +KBUILD_CFLAGS += -fno-PIE -ffreestanding -fno-stack-protector +KBUILD_CFLAGS += -fno-asynchronous-unwind-tables +KBUILD_CFLAGS += -pipe -mcpu=ev4 -mno-fp-regs -ffixed-8 +KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS +KBUILD_CFLAGS += $(CC_FLAGS_DIALECT) +KBUILD_AFLAGS := -D__ASSEMBLY__ $(KBUILD_CFLAGS) + +# The stub has no sanitizer or coverage runtime. arch/alpha/lib/lib.a is +# built with the kernel's instrumentation and -mcpu=, so build private +# copies of the library routines the stub needs instead of linking it. +UBSAN_SANITIZE := n +GCOV_PROFILE := n + +$(obj)/memcpy.o: $(srctree)/arch/alpha/lib/memcpy.c FORCE + $(call if_changed_dep,cc_o_c) + +$(obj)/memmove.o: $(srctree)/arch/alpha/lib/memmove.S FORCE + $(call if_changed_dep,as_o_S) + +$(obj)/memset.o: $(srctree)/arch/alpha/lib/memset.S FORCE + $(call if_changed_dep,as_o_S) + +AFLAGS___divqu.o := -DDIV +AFLAGS___remqu.o := -DREM +AFLAGS___divlu.o := -DDIV -DINTSIZE +AFLAGS___remlu.o := -DREM -DINTSIZE + +$(addprefix $(obj)/,__divqu.o __remqu.o __divlu.o __remlu.o): $(srctree)/arch/alpha/lib/divide.S FORCE + $(call if_changed_dep,as_o_S) + +# zstd's decompressor uses __builtin_ctzll()/__builtin_clzll(). Those are +# inline code only with the CIX extension, which the base instruction set +# the stub is built for does not have, so they become calls to libgcc's +# __ctzdi2/__clzdi2. +LIBGCC := $(shell $(CC) -print-libgcc-file-name) + +LDFLAGS_vmlinux := -X --orphan-handling=warn -T +$(obj)/vmlinux: $(obj)/vmlinux.lds $(addprefix $(obj)/, $(OBJECTS)) $(LIBGCC) FORCE + $(call if_changed,ld) + +OBJCOPYFLAGS_vmlinux.bin := -R .comment -R .note -S +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +suffix-$(CONFIG_KERNEL_BZIP2) := bz2 +suffix-$(CONFIG_KERNEL_GZIP) := gz +suffix-$(CONFIG_KERNEL_LZ4) := lz4 +suffix-$(CONFIG_KERNEL_LZMA) := lzma +suffix-$(CONFIG_KERNEL_LZO) := lzo +suffix-$(CONFIG_KERNEL_XZ) := xz +suffix-$(CONFIG_KERNEL_ZSTD) := zst + +$(obj)/vmlinux.bin.bz2: $(obj)/vmlinux.bin FORCE + $(call if_changed,bzip2_with_size) +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) +$(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) +$(obj)/vmlinux.bin.zst: $(obj)/vmlinux.bin FORCE + $(call if_changed,zstd22_with_size) + +LDFLAGS_piggy.o := -r --format binary --oformat elf64-alpha -T +$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.$(suffix-y) FORCE + $(call if_changed,ld) diff --git a/arch/alpha/boot/compressed/head.S b/arch/alpha/boot/compressed/head.S new file mode 100644 index 000000000000..c689ac439d15 --- /dev/null +++ b/arch/alpha/boot/compressed/head.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Entry point for the self-extracting compressed kernel stub. aboot + * jumps here with the OSF/1 parameter page (ZERO_PGE) already populated + * and $30 set to INIT_STACK, exactly as it would for an uncompressed + * vmlinux. + */ + +#include +#include + + .set noreorder + +__HEAD + .globl __start + .ent __start +__start: + br $29,1f +1: ldgp $29,0($29) + + /* + * Clear our own bss. The decompressors keep state there, and + * nothing guarantees that aboot zeroed the part of our PT_LOAD + * segment that has no file backing. + */ + lda $1,_bss + lda $2,_ebss +2: cmpult $1,$2,$3 + beq $3,3f + stq $31,0($1) + addq $1,8,$1 + br $31,2b + +3: jsr $26,decompress_and_boot + call_pal PAL_halt + .end __start diff --git a/arch/alpha/boot/compressed/misc.c b/arch/alpha/boot/compressed/misc.c new file mode 100644 index 000000000000..1b5b8e716613 --- /dev/null +++ b/arch/alpha/boot/compressed/misc.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Decompression and self-relocation for the compressed kernel boot stub, + * based on arch/parisc/boot/compressed/misc.c. + * + * aboot loads the stub as an ordinary ELF at START_ADDR and jumps to it + * with the parameter page and stack already set up; vmlinux.lds.S + * explains why the stub has to be linked there. We decompress above our + * own bss and never write below START_ADDR, so what aboot prepared stays + * valid for the real kernel. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define STATIC static +#define memzero(s, n) memset((s), 0, (n)) + +/* + * decompress_unxz.c supplies its own memmove() unless the identifier is + * already a macro. We link our own build of arch/alpha/lib/memmove.S, + * which is better, so take that instead. + */ +#define memmove memmove + +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 _ebss[]; +extern char trampoline_start[], trampoline_end[]; + +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; +static unsigned long mem_end; + +/* The stub has no console, so the message is only for debuggers. */ +void __noreturn error(char *m) +{ + while (1) + __halt(); +} + +#ifdef CONFIG_KERNEL_BZIP2 +#include "../../../../lib/decompress_bunzip2.c" +#endif + +#ifdef CONFIG_KERNEL_GZIP +#include "../../../../lib/decompress_inflate.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 + +#ifdef CONFIG_KERNEL_ZSTD +#include "../../../../lib/decompress_unzstd.c" +#endif + +/* Scratch heap for the decompressors, larger than any of them needs. */ +#define STUB_HEAP_SIZE (4 * 1024 * 1024) + +/* + * Type punning through a packed struct, to avoid the warnings the regular + * get_unaligned_le32() produces when reading a char-typed symbol as an + * integer. + */ +static u32 punned_get_unaligned_le32(const void *p) +{ + const struct { __le32 x; } __packed *pp = p; + + return le32_to_cpu(pp->x); +} + +/* + * End of the usable memory cluster that holds the kernel, or 0 if the + * HWRPB does not describe one. Used to check that the decompressed image + * and the stub's scratch space fit before anything is written. + */ +static unsigned long usable_memory_end(void) +{ + struct hwrpb_struct *hwrpb; + struct memdesc_struct *memdesc; + unsigned long pfn = (START_ADDR - PAGE_OFFSET) >> PAGE_SHIFT; + unsigned long i; + + hwrpb = (struct hwrpb_struct *)(PAGE_OFFSET + INIT_HWRPB->phys_addr); + memdesc = (struct memdesc_struct *)((unsigned long)hwrpb + + hwrpb->mddt_offset); + + for (i = 0; i < memdesc->numclusters; i++) { + struct memclust_struct *cluster = &memdesc->cluster[i]; + unsigned long end_pfn = cluster->start_pfn + cluster->numpages; + + if (cluster->usage) + continue; + if (pfn >= cluster->start_pfn && pfn < end_pfn) + return PAGE_OFFSET + (end_pfn << PAGE_SHIFT); + } + + return 0; +} + +/* + * Move the decompressed kernel's single PT_LOAD segment down onto + * START_ADDR and jump to its entry point. The kernel is always linked + * with exactly one PT_LOAD whose p_vaddr equals its e_entry equals + * START_ADDR (see arch/alpha/kernel/vmlinux.lds.S); anything else we do + * not know how to boot. + * + * The copy cannot run from this stub, which is itself executing at + * START_ADDR; see trampoline.S. + */ +static void __noreturn relocate_and_jump(void *image) +{ + void (*trampoline)(unsigned long dest, unsigned long src, + unsigned long filesz, unsigned long memsz, + unsigned long entry); + unsigned long tramp_len = trampoline_end - trampoline_start; + Elf64_Phdr phdr, load_phdr = {}; + Elf64_Ehdr ehdr; + int i, found_load = 0; + unsigned long tramp; + + memcpy(&ehdr, image, 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 || + ehdr.e_ident[EI_CLASS] != ELFCLASS64 || + ehdr.e_ident[EI_DATA] != ELFDATA2LSB || + ehdr.e_machine != EM_ALPHA || + ehdr.e_type != ET_EXEC || + ehdr.e_entry != START_ADDR) + error("Not a bootable Alpha kernel image"); + + for (i = 0; i < ehdr.e_phnum; i++) { + memcpy(&phdr, image + ehdr.e_phoff + i * ehdr.e_phentsize, + sizeof(phdr)); + + if (phdr.p_type != PT_LOAD) + continue; + if (found_load || phdr.p_vaddr != START_ADDR) + error("Unexpected kernel segment layout"); + found_load = 1; + load_phdr = phdr; + } + if (!found_load) + error("Kernel image has no PT_LOAD segment"); + + /* + * The trampoline has to outlive both the scratch heap it is copied + * into and the region it is about to write, which extends past the + * kernel's file image to the end of its bss. + */ + tramp = ALIGN(max(free_mem_end_ptr, START_ADDR + load_phdr.p_memsz), 8); + if (mem_end && tramp + tramp_len > mem_end) + error("Not enough memory to relocate the kernel"); + + memcpy((void *)tramp, trampoline_start, tramp_len); + imb(); + + trampoline = (void *)tramp; + trampoline(load_phdr.p_vaddr, + (unsigned long)image + load_phdr.p_offset, + load_phdr.p_filesz, load_phdr.p_memsz, ehdr.e_entry); + unreachable(); +} + +void __noreturn decompress_and_boot(void) +{ + unsigned long image_len = punned_get_unaligned_le32(&output_len); + void *image = (void *)ALIGN((unsigned long)_ebss, PAGE_SIZE); + + free_mem_ptr = ALIGN((unsigned long)image + image_len, PAGE_SIZE); + free_mem_end_ptr = free_mem_ptr + STUB_HEAP_SIZE; + + mem_end = usable_memory_end(); + if (mem_end && free_mem_end_ptr > mem_end) + error("Not enough memory to decompress the kernel"); + + /* Some decompressors report failure only through the return value. */ + if (__decompress(input_data, input_len, NULL, NULL, image, image_len, + NULL, error)) + error("Kernel decompression failed"); + + relocate_and_jump(image); +} diff --git a/arch/alpha/boot/compressed/trampoline.S b/arch/alpha/boot/compressed/trampoline.S new file mode 100644 index 000000000000..bf57d3b0fd7f --- /dev/null +++ b/arch/alpha/boot/compressed/trampoline.S @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copy the decompressed kernel onto START_ADDR, clear its bss and jump to + * its entry point. See relocate_and_jump() in misc.c for why this cannot + * run from the stub itself. It is position independent (no data or + * rodata references, no gp) and clobbers only its argument registers and + * $1, so misc.c can copy it anywhere and call it there. + */ + + .set noreorder + + .globl trampoline_start + .globl trampoline_end + .ent trampoline_start +trampoline_start: + /* $16 = dest, $17 = src, $18 = p_filesz, $19 = p_memsz, $20 = entry */ + + /* + * dest and src are both page aligned, and everything above + * p_filesz is bss, so round both lengths up and work in quadwords + * only, using instructions every Alpha implements. + */ + addq $18,7,$18 + bic $18,7,$18 + addq $19,7,$19 + bic $19,7,$19 + subq $19,$18,$19 /* bytes to zero after the copy */ + +1: beq $18,2f + ldq $1,0($17) + stq $1,0($16) + addq $17,8,$17 + addq $16,8,$16 + subq $18,8,$18 + br $31,1b + +2: beq $19,3f + stq $31,0($16) + addq $16,8,$16 + subq $19,8,$19 + br $31,2b + +3: imb + jmp $31,($20) + .end trampoline_start +trampoline_end: diff --git a/arch/alpha/boot/compressed/vmlinux.lds.S b/arch/alpha/boot/compressed/vmlinux.lds.S new file mode 100644 index 000000000000..5328c1ccaccf --- /dev/null +++ b/arch/alpha/boot/compressed/vmlinux.lds.S @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include + +OUTPUT_FORMAT("elf64-alpha") +OUTPUT_ARCH(alpha) +ENTRY(__start) + +SECTIONS +{ + /* + * Link the stub at the address the real, uncompressed vmlinux has + * always occupied. aboot derives the OSF/1 parameter-page + * (ZERO_PGE) and initial stack (INIT_STACK) addresses from the + * loaded image's own p_vaddr, so a stub linked anywhere else would + * hand the real kernel a parameter page it does not expect. + */ + . = START_ADDR; + + .head.text : { + _head = .; + HEAD_TEXT + _ehead = .; + } + + . = ALIGN(8); + .rodata.compressed : { + *(.rodata.compressed) + } + + . = ALIGN(PAGE_SIZE); + .text : { + _text = .; + *(.text) + *(.text.*) + _etext = .; + } + . = ALIGN(8); + .rodata : { + *(.rodata) + *(.rodata.*) + } + . = ALIGN(8); + .data : { + *(.data) + *(.data.*) + } + .got : { + *(.got) + } + .sdata : { + *(.sdata) + } + /* + * misc.c decompresses to _ebss, so nothing that the stub still + * needs may be placed after .bss. --orphan-handling=warn keeps a + * new section from silently landing there. + */ + . = ALIGN(8); + .bss : { + _bss = .; + *(.sbss) + *(.scommon) + *(.bss) + *(.bss.*) + *(COMMON) + . = ALIGN(8); + _ebss = .; + } + + STABS_DEBUG + DWARF_DEBUG + ELF_DETAILS + + DISCARDS + /DISCARD/ : { + *(.eh_frame) + *(.modinfo) + *(.note) + *(.note.*) + } +} diff --git a/arch/alpha/boot/compressed/vmlinux.scr b/arch/alpha/boot/compressed/vmlinux.scr new file mode 100644 index 000000000000..dac2d142bcfa --- /dev/null +++ b/arch/alpha/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/alpha/include/asm/asm-prototypes.h b/arch/alpha/include/asm/asm-prototypes.h index c8ae46fc2e74..672fea15032f 100644 --- a/arch/alpha/include/asm/asm-prototypes.h +++ b/arch/alpha/include/asm/asm-prototypes.h @@ -17,3 +17,9 @@ extern void __remlu(void); extern void __divqu(void); extern void __remqu(void); extern unsigned long __udiv_qrnnd(unsigned long *, unsigned long, unsigned long , unsigned long); + +#ifdef CONFIG_ARCH_SUPPORTS_INT128 +extern __int128_t __ashlti3(__int128_t a, int shift); +extern __int128_t __ashrti3(__int128_t a, int shift); +extern __int128_t __lshrti3(__int128_t a, int shift); +#endif diff --git a/arch/alpha/include/asm/core_tsunami.h b/arch/alpha/include/asm/core_tsunami.h index 3391e95754f2..5c5d3ff849ed 100644 --- a/arch/alpha/include/asm/core_tsunami.h +++ b/arch/alpha/include/asm/core_tsunami.h @@ -105,7 +105,6 @@ extern int TSUNAMI_bootcpu; #define perror_m_ta 0x40 #define perror_m_rdpe 0x80 #define perror_m_nds 0x100 -#define perror_m_rto 0x200 #define perror_m_uecc 0x400 #define perror_m_cre 0x800 #define perror_m_addrl 0xFFFFFFFF0000UL @@ -123,7 +122,7 @@ union TPchipPERROR { unsigned perror_v_ta : 1; unsigned perror_v_rdpe : 1; unsigned perror_v_nds : 1; - unsigned perror_v_rto : 1; + unsigned perror_v_reserved : 1; /* PERROR<9>: MBZ, RAZ */ unsigned perror_v_uecc : 1; unsigned perror_v_cre : 1; unsigned perror_v_rsvd1 : 4; @@ -283,6 +282,11 @@ union TPchipPERRMASK { * Data structure for handling TSUNAMI machine checks: */ struct el_TSUNAMI_sysdata_mcheck { + u64 sesf; + u64 dir; + u64 misc; + u64 p0_perror; + u64 p1_perror; }; diff --git a/arch/alpha/include/asm/entry-common.h b/arch/alpha/include/asm/entry-common.h new file mode 100644 index 000000000000..a811c73454d2 --- /dev/null +++ b/arch/alpha/include/asm/entry-common.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ARCH_ALPHA_ENTRY_COMMON_H +#define ARCH_ALPHA_ENTRY_COMMON_H + +#include /* For on_thread_stack() */ +#include + +#define arch_exit_to_user_mode_work arch_exit_to_user_mode_work + +static __always_inline void arch_exit_to_user_mode_work(struct pt_regs *regs, + unsigned long ti_work) +{ +} +#endif diff --git a/arch/alpha/include/asm/ftrace.h b/arch/alpha/include/asm/ftrace.h index 7ec44134c804..5b75a294a7c9 100644 --- a/arch/alpha/include/asm/ftrace.h +++ b/arch/alpha/include/asm/ftrace.h @@ -2,8 +2,6 @@ #ifndef _ASM_ALPHA_FTRACE_H #define _ASM_ALPHA_FTRACE_H -#ifdef CONFIG_FRAME_POINTER - static void *alpha_ftrace_return_address0(void) noinline notrace; static void *alpha_ftrace_return_address0(void) @@ -13,6 +11,8 @@ static void *alpha_ftrace_return_address0(void) #define ftrace_return_address0 alpha_ftrace_return_address0() +#ifdef CONFIG_FRAME_POINTER + /* * __builtin_return_address() requires a constant integer argument. * Keep this as a macro so the value is seen at the callsite. @@ -21,7 +21,6 @@ static void *alpha_ftrace_return_address0(void) #else /* !CONFIG_FRAME_POINTER */ -#define ftrace_return_address0 0UL #define ftrace_return_address(n) ((void)(n), 0UL) #endif /* CONFIG_FRAME_POINTER */ diff --git a/arch/alpha/include/asm/hugetlb.h b/arch/alpha/include/asm/hugetlb.h new file mode 100644 index 000000000000..ff69e24c775d --- /dev/null +++ b/arch/alpha/include/asm/hugetlb.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ALPHA_HUGETLB_H +#define _ALPHA_HUGETLB_H + +#include + +pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags); +#define arch_make_huge_pte arch_make_huge_pte + +#define __HAVE_ARCH_HUGE_PTEP_GET +extern pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, + pte_t *ptep); +#define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT +extern 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 +extern 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 +extern pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep); +#define __HAVE_ARCH_HUGE_PTEP_SET_WRPROTECT +extern void huge_ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, + pte_t *ptep); +#define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS +extern int huge_ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + pte_t pte, int dirty); +#define __HAVE_ARCH_HUGE_PTE_CLEAR +extern void huge_pte_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long sz); + +/* + * The generic version does not flush, which would leave the old translation + * live across a restrictive change to PTE bits <15:0>. + */ +#define huge_ptep_modify_prot_start huge_ptep_modify_prot_start +extern pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep); + +#include + +#endif /* _ALPHA_HUGETLB_H */ diff --git a/arch/alpha/include/asm/mmu_context.h b/arch/alpha/include/asm/mmu_context.h index eee8fe836a59..09f356934b90 100644 --- a/arch/alpha/include/asm/mmu_context.h +++ b/arch/alpha/include/asm/mmu_context.h @@ -130,6 +130,8 @@ __get_new_mm_context(struct mm_struct *mm, long cpu) return next; } +extern void __load_new_mm_context(struct mm_struct *); + __EXTERN_INLINE void ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, struct task_struct *next) @@ -139,6 +141,16 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, unsigned long mmc; long cpu = smp_processor_id(); + /* + * kthread_use_mm(), sched_force_init_mm() and do_shoot_lazy_tlb() + * switch current's mm without alpha_switch_to(), which loads the + * context. The latter two require options Alpha does not select. + */ + if (next == current) { + __load_new_mm_context(next_mm); + return; + } + #ifdef CONFIG_SMP cpu_data[cpu].asn_lock = 1; barrier(); @@ -147,11 +159,16 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, mmc = next_mm->context[cpu]; if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) { mmc = __get_new_mm_context(next_mm, cpu); - next_mm->context[cpu] = mmc; + /* + * Ordered before any user access through the mm by the + * mb() in arch_spin_unlock(), when finish_lock_switch() + * drops the rq lock. + */ + WRITE_ONCE(next_mm->context[cpu], mmc); } #ifdef CONFIG_SMP - else - cpu_data[cpu].need_new_asn = 1; + /* A deferred shootdown can also invalidate a newly allocated ASN. */ + cpu_data[cpu].need_new_asn = 1; #endif /* Always update the PCB ASN. Another thread may have allocated @@ -160,7 +177,6 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, task_thread_info(next)->pcb.asn = mmc & HARDWARE_ASN_MASK; } -extern void __load_new_mm_context(struct mm_struct *); asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause, struct pt_regs *regs); @@ -181,6 +197,17 @@ do { \ #define check_mmu_context() do { } while(0) #endif +/* + * Also called from kthread_use_mm() with preemption enabled. Alpha has + * no kernel preemption, so the task is still on the CPU that ran + * switch_mm(). + */ +#define finish_arch_post_lock_switch finish_arch_post_lock_switch +static inline void finish_arch_post_lock_switch(void) +{ + check_mmu_context(); +} + __EXTERN_INLINE void ev5_activate_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm) { diff --git a/arch/alpha/include/asm/page.h b/arch/alpha/include/asm/page.h index 59d01f9b77f6..67053c52953f 100644 --- a/arch/alpha/include/asm/page.h +++ b/arch/alpha/include/asm/page.h @@ -6,6 +6,19 @@ #include #include +#ifdef CONFIG_HUGETLB_PAGE +/* + * The default huge page is the largest granularity hint block, 4MB. All + * three hint sizes are registered as hstates, hence HUGE_MAX_HSTATE, which + * linux/hugetlb.h needs before it includes asm/hugetlb.h. + */ +#define HPAGE_SHIFT 22 +#define HPAGE_SIZE (_AC(1, UL) << HPAGE_SHIFT) +#define HPAGE_MASK (~(HPAGE_SIZE - 1)) +#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) +#define HUGE_MAX_HSTATE 3 +#endif + #ifndef __ASSEMBLER__ #define STRICT_MM_TYPECHECKS diff --git a/arch/alpha/include/asm/perf_event.h b/arch/alpha/include/asm/perf_event.h index 5996e7a6757e..72536566014a 100644 --- a/arch/alpha/include/asm/perf_event.h +++ b/arch/alpha/include/asm/perf_event.h @@ -1,4 +1,11 @@ #ifndef __ASM_ALPHA_PERF_EVENT_H #define __ASM_ALPHA_PERF_EVENT_H +/* + * Software events have no trap frame, so perf_fetch_caller_regs() fills + * in a zeroed pt_regs. A zero ps already means kernel mode; only pc is + * missing. + */ +#define perf_arch_fetch_caller_regs(regs, __ip) ((regs)->pc = (__ip)) + #endif /* __ASM_ALPHA_PERF_EVENT_H */ diff --git a/arch/alpha/include/asm/pgtable.h b/arch/alpha/include/asm/pgtable.h index 8e00cf9dc39d..7cf685a694b6 100644 --- a/arch/alpha/include/asm/pgtable.h +++ b/arch/alpha/include/asm/pgtable.h @@ -65,14 +65,17 @@ struct vm_area_struct; #define _PAGE_FOW 0x0004 /* used for page protection (fault on write) */ #define _PAGE_FOE 0x0008 /* used for page protection (fault on exec) */ #define _PAGE_ASM 0x0010 -#define _PAGE_KRE 0x0100 /* xxx - see below on the "accessed" bit */ -#define _PAGE_URE 0x0200 /* xxx */ -#define _PAGE_KWE 0x1000 /* used to do the dirty bit in software */ -#define _PAGE_UWE 0x2000 /* used to do the dirty bit in software */ +#define _PAGE_GH_MASK 0x0060 /* granularity hint, PTE<6:5> */ +#define _PAGE_GH_SHIFT 5 +#define _PAGE_KRE 0x0100 /* kernel read enable, in __ACCESS_BITS */ +#define _PAGE_URE 0x0200 /* user read enable, in __ACCESS_BITS */ +#define _PAGE_KWE 0x1000 /* kernel write enable, in __DIRTY_BITS */ +#define _PAGE_UWE 0x2000 /* user write enable, in __DIRTY_BITS */ /* .. and these are ours ... */ #define _PAGE_DIRTY 0x20000 #define _PAGE_ACCESSED 0x40000 +#define _PAGE_SPECIAL 0x80000 /* We borrow bit 39 to store the exclusive marker in swap PTEs. */ #define _PAGE_SWP_EXCLUSIVE 0x8000000000UL @@ -94,7 +97,36 @@ struct vm_area_struct; #define _PFN_MASK 0xFFFFFFFF00000000UL #define _PAGE_TABLE (_PAGE_VALID | __DIRTY_BITS | __ACCESS_BITS) -#define _PAGE_CHG_MASK (_PFN_MASK | __DIRTY_BITS | __ACCESS_BITS) +/* + * pte_modify() must preserve the granularity hint, or + * hugetlb_change_protection() would turn a block into single-page PTEs. + */ +#define _PAGE_CHG_MASK (_PFN_MASK | __DIRTY_BITS | __ACCESS_BITS | \ + _PAGE_GH_MASK | _PAGE_SPECIAL) + +/* + * Granularity hints. A PTE with hint order N is one of a block of 8^N + * physically contiguous pages, naturally aligned both virtually and + * physically, that the TB may map with a single entry. With 8KB pages the + * four encodings give 8KB, 64KB, 512KB and 4MB. + * + * Every PTE of a block must still carry the PFN of its own page, and all of + * them must agree in bits <15:0>: the protection, fault, hint and valid bits. + * __ACCESS_BITS and __DIRTY_BITS reach into that range, so young and dirty + * updates have to be applied to the whole block too. + * + * The hint is advisory: an implementation that ignores it still translates + * correctly through the individual PTEs, so it is safe to set on any Alpha. + */ +#define GH_ORDER_MAX 4 /* hint orders 0..3 */ + +#define gh_cont_shift(order) (PAGE_SHIFT + 3 * (order)) +#define gh_cont_size(order) (1UL << gh_cont_shift(order)) +#define gh_cont_mask(order) (~(gh_cont_size(order) - 1)) +#define gh_pte_num(order) (1UL << (3 * (order))) + +#define for_each_gh_order(order) \ + for ((order) = 1; (order) < GH_ORDER_MAX; (order)++) /* * All the normal masks have the "page accessed" bits on, as any time they are used, @@ -231,6 +263,25 @@ extern inline void pud_clear(pud_t * pudp) { pud_val(*pudp) = 0; } extern inline int pte_write(pte_t pte) { return !(pte_val(pte) & _PAGE_FOW); } extern inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; } extern inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +extern inline int pte_user(pte_t pte) { return pte_val(pte) & _PAGE_URE; } + +static inline bool pte_user_accessible_page(struct mm_struct *mm, + unsigned long addr, pte_t pte) +{ + return pte_present(pte) && pte_user(pte); +} + +static inline bool pmd_user_accessible_page(struct mm_struct *mm, + unsigned long addr, pmd_t pmd) +{ + return false; +} + +static inline bool pud_user_accessible_page(struct mm_struct *mm, + unsigned long addr, pud_t pud) +{ + return false; +} extern inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) |= _PAGE_FOW; return pte; } extern inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~(__DIRTY_BITS); return pte; } @@ -238,6 +289,26 @@ extern inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~(__ACCESS_BITS); ret extern inline pte_t pte_mkwrite_novma(pte_t pte){ pte_val(pte) &= ~_PAGE_FOW; return pte; } extern inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= __DIRTY_BITS; return pte; } extern inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= __ACCESS_BITS; return pte; } +extern inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; } +extern inline pte_t pte_mkspecial(pte_t pte) { pte_val(pte) |= _PAGE_SPECIAL; return pte; } + +extern inline unsigned int pte_gh_order(pte_t pte) +{ + return (pte_val(pte) & _PAGE_GH_MASK) >> _PAGE_GH_SHIFT; +} + +extern inline pte_t pte_mkgh(pte_t pte, unsigned int order) +{ + pte_val(pte) &= ~_PAGE_GH_MASK; + pte_val(pte) |= (unsigned long)order << _PAGE_GH_SHIFT; + return pte; +} + +/* + * Swap PTEs leave bits <31:0> clear, so this is false for every swap, + * migration and marker entry, not just for ordinary small pages. + */ +extern inline int pte_huge(pte_t pte) { return pte_val(pte) & _PAGE_GH_MASK; } /* * The smp_rmb() in the following functions are required to order the load of @@ -283,6 +354,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, pte_t pte = READ_ONCE(*ptep); pte_clear(mm, address, ptep); + page_table_check_pte_clear(mm, address, pte); return pte; } @@ -294,7 +366,6 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma, struct mm_struct *mm = vma->vm_mm; pte_t pte = ptep_get_and_clear(mm, addr, ptep); - page_table_check_pte_clear(mm, addr, pte); migrate_flush_tlb_page(vma, addr); return pte; } diff --git a/arch/alpha/include/asm/processor.h b/arch/alpha/include/asm/processor.h index 5dce5518a211..73251cd671e0 100644 --- a/arch/alpha/include/asm/processor.h +++ b/arch/alpha/include/asm/processor.h @@ -26,6 +26,13 @@ struct thread_struct { }; #define INIT_THREAD { } +static inline void arch_thread_struct_whitelist(unsigned long *offset, + unsigned long *size) +{ + *offset = 0; + *size = 0; +} + /* Do necessary setup to start up a newly executed thread. */ struct pt_regs; extern void start_thread(struct pt_regs *, unsigned long, unsigned long); @@ -36,8 +43,7 @@ unsigned long __get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) (task_pt_regs(tsk)->pc) -#define KSTK_ESP(tsk) \ - ((tsk) == current ? rdusp() : task_thread_info(tsk)->pcb.usp) +#define KSTK_ESP(tsk) user_stack_pointer(task_pt_regs(tsk)) #define cpu_relax() barrier() diff --git a/arch/alpha/include/asm/ptrace.h b/arch/alpha/include/asm/ptrace.h index 8e0a589e2d15..45d1229344b6 100644 --- a/arch/alpha/include/asm/ptrace.h +++ b/arch/alpha/include/asm/ptrace.h @@ -3,7 +3,7 @@ #define _ASMAXP_PTRACE_H #include - +#include #define arch_has_single_step() (1) #define user_mode(regs) (((regs)->ps & 8) != 0) @@ -17,18 +17,23 @@ #define current_pt_regs() \ ((struct pt_regs *) ((char *)current_thread_info() + 2*PAGE_SIZE) - 1) -#define force_successful_syscall_return() (current_pt_regs()->r0 = 0) +#define force_successful_syscall_return() \ + (current_thread_info()->syscall_meta \ + |= ALPHA_SYSCALL_META_FORCE_SUCCESS) static inline unsigned long regs_return_value(struct pt_regs *regs) { return regs->r0; } -/* Helpers for working with the user stack pointer */ -static inline unsigned long user_stack_pointer(struct pt_regs *regs) +unsigned long user_stack_pointer(struct pt_regs *regs); + +static __always_inline bool regs_irqs_disabled(struct pt_regs *regs) { - /* Valid for user-mode regs */ - return regs->usp; + return arch_irqs_disabled_flags(regs->ps); } +/* Syscall emulation defines */ +#define PTRACE_SYSEMU 0x1d +#define PTRACE_SYSEMU_SINGLESTEP 0x1e #endif diff --git a/arch/alpha/include/asm/stacktrace.h b/arch/alpha/include/asm/stacktrace.h new file mode 100644 index 000000000000..f006d6f00fd0 --- /dev/null +++ b/arch/alpha/include/asm/stacktrace.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ALPHA_STACKTRACE_H +#define _ASM_ALPHA_STACKTRACE_H + +#include +#include + +#include +#include +#include + +static __always_inline bool on_thread_stack(void) +{ + unsigned long base = (unsigned long)current->stack; + unsigned long sp = (unsigned long)current_stack_pointer; + + return !((base ^ sp) & ~(THREAD_SIZE - 1)); +} + +#endif /* _ASM_ALPHA_STACKTRACE_H */ diff --git a/arch/alpha/include/asm/switch_to.h b/arch/alpha/include/asm/switch_to.h index 762b7f975310..35c4b2c9d992 100644 --- a/arch/alpha/include/asm/switch_to.h +++ b/arch/alpha/include/asm/switch_to.h @@ -9,7 +9,6 @@ extern struct task_struct *alpha_switch_to(unsigned long, struct task_struct *); #define switch_to(P,N,L) \ do { \ (L) = alpha_switch_to(virt_to_phys(&task_thread_info(N)->pcb), (P)); \ - check_mmu_context(); \ } while (0) #endif /* __ALPHA_SWITCH_TO_H */ diff --git a/arch/alpha/include/asm/syscall.h b/arch/alpha/include/asm/syscall.h index 1e78cbd46faf..c1394910f584 100644 --- a/arch/alpha/include/asm/syscall.h +++ b/arch/alpha/include/asm/syscall.h @@ -8,6 +8,8 @@ #include #include +extern void *sys_call_table[]; + static inline int syscall_get_arch(struct task_struct *task) { return AUDIT_ARCH_ALPHA; @@ -104,10 +106,17 @@ static inline void syscall_set_return_value(struct task_struct *task, } /* Restore the original syscall nr after seccomp/ptrace modified regs->r1. */ + static inline void syscall_rollback(struct task_struct *task, struct pt_regs *regs) { - regs->r1 = regs->r2; + unsigned long nr = task_thread_info(task)->syscall_saved_nr; + + regs->r1 = nr; } +static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs) +{ + return false; +} #endif /* _ASM_ALPHA_SYSCALL_H */ diff --git a/arch/alpha/include/asm/thread_info.h b/arch/alpha/include/asm/thread_info.h index 1552ecca8520..d781ac79106d 100644 --- a/arch/alpha/include/asm/thread_info.h +++ b/arch/alpha/include/asm/thread_info.h @@ -12,16 +12,21 @@ #endif #ifndef __ASSEMBLER__ + struct thread_info { struct pcb_struct pcb; /* palcode state */ struct task_struct *task; /* main task structure */ - unsigned int flags; /* low level flags */ + unsigned long flags; /* low level flags */ + unsigned long syscall_work; /* SYSCALL_WORK_* flags */ unsigned int ieee_state; /* see fpu.h */ unsigned cpu; /* current CPU */ - int preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ unsigned int status; /* thread-synchronous flags */ + unsigned long syscall_saved_r19; + unsigned long syscall_meta; + unsigned long syscall_saved_nr; int bpt_nsaved; unsigned long bpt_addr[2]; /* breakpoint handling */ @@ -50,6 +55,9 @@ register unsigned long *current_stack_pointer __asm__ ("$30"); #define THREAD_SIZE_ORDER 1 #define THREAD_SIZE (2*PAGE_SIZE) +#define ALPHA_SYSCALL_META_SKIP 0x2 +#define ALPHA_SYSCALL_META_FORCE_SUCCESS 0x4 + /* * Thread information flags: * - these are process state flags and used from assembly @@ -68,6 +76,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30"); #define TIF_SECCOMP 6 /* seccomp syscall filtering active */ #define TIF_SYSCALL_TRACEPOINT 7 /* syscall tracepoint instrumentation */ #define TIF_DIE_IF_KERNEL 9 /* dik recursion lock */ +#define TIF_UPROBE 10 /* uprobe breakpoint or singlestep */ #define TIF_MEMDIE 13 /* is terminating due to OOM killer */ #define TIF_POLLING_NRFLAG 14 /* idle is polling for TIF_NEED_RESCHED */ @@ -80,22 +89,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30"); #define _TIF_SECCOMP (1<vm_mm; - if (mm == current->active_mm) + /* A targeted tbi() needs a thread of MM to be current. */ + if (mm == current->mm) flush_tlb_current_page(mm, vma, addr); else flush_tlb_other(mm); diff --git a/arch/alpha/include/uapi/asm/perf_regs.h b/arch/alpha/include/uapi/asm/perf_regs.h new file mode 100644 index 000000000000..b6d2dcf6e184 --- /dev/null +++ b/arch/alpha/include/uapi/asm/perf_regs.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_ALPHA_PERF_REGS_H +#define _ASM_ALPHA_PERF_REGS_H + +/* + * Indices 0-30 match the hardware (and DWARF) register numbers. + * r9-r15 are not saved in pt_regs and cannot be sampled. + */ +enum perf_event_alpha_regs { + PERF_REG_ALPHA_R0, + PERF_REG_ALPHA_R1, + PERF_REG_ALPHA_R2, + PERF_REG_ALPHA_R3, + PERF_REG_ALPHA_R4, + PERF_REG_ALPHA_R5, + PERF_REG_ALPHA_R6, + PERF_REG_ALPHA_R7, + PERF_REG_ALPHA_R8, + PERF_REG_ALPHA_R9, + PERF_REG_ALPHA_R10, + PERF_REG_ALPHA_R11, + PERF_REG_ALPHA_R12, + PERF_REG_ALPHA_R13, + PERF_REG_ALPHA_R14, + PERF_REG_ALPHA_R15, + PERF_REG_ALPHA_R16, + PERF_REG_ALPHA_R17, + PERF_REG_ALPHA_R18, + PERF_REG_ALPHA_R19, + PERF_REG_ALPHA_R20, + PERF_REG_ALPHA_R21, + PERF_REG_ALPHA_R22, + PERF_REG_ALPHA_R23, + PERF_REG_ALPHA_R24, + PERF_REG_ALPHA_R25, + PERF_REG_ALPHA_R26, + PERF_REG_ALPHA_R27, + PERF_REG_ALPHA_R28, + PERF_REG_ALPHA_GP, + PERF_REG_ALPHA_SP, + PERF_REG_ALPHA_PC, + PERF_REG_ALPHA_MAX, +}; + +#endif /* _ASM_ALPHA_PERF_REGS_H */ diff --git a/arch/alpha/include/uapi/asm/ptrace.h b/arch/alpha/include/uapi/asm/ptrace.h index 9d86b2a1526e..72ed913a910f 100644 --- a/arch/alpha/include/uapi/asm/ptrace.h +++ b/arch/alpha/include/uapi/asm/ptrace.h @@ -43,7 +43,7 @@ struct pt_regs { unsigned long trap_a1; unsigned long trap_a2; /* This makes the stack 16-byte aligned as GCC expects */ - unsigned long usp; + unsigned long __pad0; /* These are saved by PAL-code: */ unsigned long ps; unsigned long pc; diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index 4ea5c189e60e..9d35715bd440 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o obj-$(CONFIG_SRM_ENV) += srm_env.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o +obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o obj-$(CONFIG_AUDIT) += audit.o @@ -39,7 +40,7 @@ obj-y += err_ev7.o err_titan.o err_marvel.o endif obj-y += irq_pyxis.o irq_i8259.o irq_srm.o -obj-y += err_ev6.o +obj-y += err_ev6.o err_tsunami.o obj-y += es1888.o smc37c669.o smc37c93x.o pc873xx.o gct.o obj-y += srmcons.o @@ -89,4 +90,6 @@ obj-$(CONFIG_ALPHA_MARVEL) += err_ev7.o err_marvel.o obj-$(CONFIG_ALPHA_NAUTILUS) += err_ev6.o obj-$(CONFIG_ALPHA_TITAN) += err_ev6.o err_titan.o +obj-$(CONFIG_ALPHA_TSUNAMI) += err_ev6.o err_tsunami.o + endif # GENERIC diff --git a/arch/alpha/kernel/asm-offsets.c b/arch/alpha/kernel/asm-offsets.c index 1d3bfca319ae..22ed3c975b1c 100644 --- a/arch/alpha/kernel/asm-offsets.c +++ b/arch/alpha/kernel/asm-offsets.c @@ -22,6 +22,7 @@ static void __used foo(void) DEFINE(SP_OFF, offsetof(struct pt_regs, ps)); DEFINE(SIZEOF_PT_REGS, sizeof(struct pt_regs)); + DEFINE(TI_SYSCALL_META, offsetof(struct thread_info, syscall_meta)); BLANK(); DEFINE(SWITCH_STACK_SIZE, sizeof(struct switch_stack)); @@ -29,5 +30,8 @@ static void __used foo(void) DEFINE(HAE_CACHE, offsetof(struct alpha_machine_vector, hae_cache)); DEFINE(HAE_REG, offsetof(struct alpha_machine_vector, hae_register)); - DEFINE(PT_REGS_USP, offsetof(struct pt_regs, usp)); + + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_SYSCALL_WORK, offsetof(struct thread_info, syscall_work)); + DEFINE(TI_STATUS, offsetof(struct thread_info, status)); } diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c index fc1ab73f23de..d435ec9fb828 100644 --- a/arch/alpha/kernel/core_tsunami.c +++ b/arch/alpha/kernel/core_tsunami.c @@ -448,37 +448,3 @@ tsunami_kill_arch(int mode) if (TSUNAMI_cchip->csc.csr & 1L<<14) tsunami_kill_one_pchip(TSUNAMI_pchip1, 1); } - -static inline void -tsunami_pci_clr_err_1(tsunami_pchip *pchip) -{ - pchip->perror.csr; - pchip->perror.csr = 0x040; - mb(); - pchip->perror.csr; -} - -static inline void -tsunami_pci_clr_err(void) -{ - tsunami_pci_clr_err_1(TSUNAMI_pchip0); - - /* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */ - if (TSUNAMI_cchip->csc.csr & 1L<<14) - tsunami_pci_clr_err_1(TSUNAMI_pchip1); -} - -void -tsunami_machine_check(unsigned long vector, unsigned long la_ptr) -{ - /* Clear error before any reporting. */ - mb(); - mb(); /* magic */ - draina(); - tsunami_pci_clr_err(); - wrmces(0x7); - mb(); - - process_mcheck_info(vector, la_ptr, "TSUNAMI", - mcheck_expected(smp_processor_id())); -} diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index 9f2608de2544..790389a15c3c 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -36,67 +36,10 @@ .size \func, . - \func .endm -/* - * SYSCALL_SKIP_RETURN_RESTART_GATE - * - * Used when syscall dispatch is skipped (seccomp/ptrace injected nr=-1). - * - Ensure we never return r0==-1 with a3==0 (success); convert to ENOSYS. - * - Gate whether syscall restart is allowed by preserving restart context - * only for ERESTART* returns. Result: - * $26 = 0 => restart allowed - * $26 = 1 => restart NOT allowed - * $18 = preserved syscall nr (regs->r2) if restart allowed, else 0 - */ -.macro SYSCALL_SKIP_RETURN_RESTART_GATE - /* Fix up invalid "-1 success" return state. */ - ldq $19, 72($sp) /* a3 */ - bne $19, 1f /* already error => skip fixup */ - - ldq $20, 0($sp) /* r0 */ - lda $21, -1($31) - cmpeq $20, $21, $22 - beq $22, 1f /* r0 != -1 => skip fixup */ - - - lda $20, ENOSYS($31) - stq $20, 0($sp) /* r0 = ENOSYS */ - lda $19, 1($31) - stq $19, 72($sp) /* a3 = 1 */ -1: - /* Restart gating: success is never restartable here. */ - ldq $19, 72($sp) /* a3 */ - beq $19, 3f /* success => not restartable */ - - ldq $20, 0($sp) /* r0 (positive errno if a3==1) */ - lda $21, ERESTARTSYS($31) - cmpeq $20, $21, $22 - bne $22, 2f - lda $21, ERESTARTNOINTR($31) - cmpeq $20, $21, $22 - bne $22, 2f - lda $21, ERESTARTNOHAND($31) - cmpeq $20, $21, $22 - bne $22, 2f - lda $21, ERESTART_RESTARTBLOCK($31) - cmpeq $20, $21, $22 - bne $22, 2f - -3: /* Not a restart code (or success) => restart NOT allowed. */ - addq $31, 1, $26 /* $26=1 => restart NOT allowed */ - mov 0, $18 - br 4f - -2: /* Restart allowed. */ - ldq $18, 16($sp) /* preserved syscall nr (regs->r2) */ - mov $31, $26 /* $26=0 => restart allowed */ - br 4f -4: -.endm - -.macro LOCKDEP_HARDIRQS_ON_RESTORE +.macro LOCKDEP_HARDIRQS_ON_RESTORE psreg #ifdef CONFIG_PROVE_LOCKING /* a0 = saved PS */ - ldq $16, SP_OFF($sp) + ldq $16, \psreg /* a1 = callsite IP for lockdep */ lda $17, 1f @@ -248,7 +191,7 @@ CFI_START_OSF_FRAME entInt SAVE_ALL lda $8, 0x3fff - lda $26, ret_from_sys_call + lda $26, ret_from_exception bic $sp, $8, $8 mov $sp, $19 jsr $31, do_entInt @@ -257,7 +200,7 @@ CFI_END_OSF_FRAME entInt CFI_START_OSF_FRAME entArith SAVE_ALL lda $8, 0x3fff - lda $26, ret_from_sys_call + lda $26, ret_from_exception bic $sp, $8, $8 mov $sp, $18 jsr $31, do_entArith @@ -305,13 +248,13 @@ CFI_START_OSF_FRAME entMM .cfi_restore $15 .cfi_adjust_cfa_offset -64 /* finish up the syscall as normal. */ - br ret_from_sys_call + br ret_from_exception CFI_END_OSF_FRAME entMM CFI_START_OSF_FRAME entIF SAVE_ALL lda $8, 0x3fff - lda $26, ret_from_sys_call + lda $26, ret_from_exception bic $sp, $8, $8 mov $sp, $17 jsr $31, do_entIF @@ -440,7 +383,7 @@ CFI_START_OSF_FRAME entUna .cfi_restore $28 .cfi_restore $29 .cfi_adjust_cfa_offset -256 - LOCKDEP_HARDIRQS_ON_RESTORE + LOCKDEP_HARDIRQS_ON_RESTORE SP_OFF($sp) call_pal PAL_rti .align 4 @@ -487,18 +430,19 @@ entUnaUser: .cfi_restore $14 .cfi_restore $15 .cfi_adjust_cfa_offset -64 - br ret_from_sys_call + br ret_from_exception CFI_END_OSF_FRAME entUna CFI_START_OSF_FRAME entDbg SAVE_ALL lda $8, 0x3fff - lda $26, ret_from_sys_call + lda $26, ret_from_exception bic $sp, $8, $8 mov $sp, $16 jsr $31, do_entDbg CFI_END_OSF_FRAME entDbg + /* * The system call entry point is special. Most importantly, it looks * like a function call to userspace as far as clobbered registers. We @@ -516,9 +460,7 @@ CFI_END_OSF_FRAME entDbg * For seccomp/ptrace/generic syscall helpers we track the syscall * number separately: * - regs->r1: current (mutable) syscall number (may be changed or set to -1) - * - regs->r2: original syscall number for restart/rollback * - * On entry PAL provides the syscall number in r0; copy it into r1/r2. */ .align 4 @@ -531,146 +473,41 @@ CFI_END_OSF_FRAME entDbg .cfi_rel_offset $gp, 16 entSys: SAVE_ALL - ldq $1, 0($sp) /* syscall nr from saved r0 */ - stq $1, 8($sp) /* regs->r1 = shadow syscall nr */ - stq $1, 16($sp) /* regs->r2 = restart syscall nr */ - /* Syscalls always enter from user mode: snapshot USP into pt_regs->usp */ - mov $0, $8 - call_pal PAL_rdusp - stq $0, PT_REGS_USP($sp) - mov $8, $0 - + ldq $1, 0($sp) /* syscall nr from saved r0 */ + stq $1, 8($sp) /* regs->r1 = shadow syscall nr */ lda $8, 0x3fff bic $sp, $8, $8 - lda $4, NR_syscalls($31) + stq $16, SP_OFF+24($sp) - lda $5, sys_call_table - lda $27, sys_ni_syscall - cmpult $0, $4, $4 - ldl $3, TI_FLAGS($8) stq $17, SP_OFF+32($sp) - s8addq $0, $5, $5 stq $18, SP_OFF+40($sp) - .cfi_rel_offset $16, SP_OFF+24 - .cfi_rel_offset $17, SP_OFF+32 - .cfi_rel_offset $18, SP_OFF+40 - lda $6, _TIF_SYSCALL_WORK - and $3, $6, $3 - bne $3, strace - beq $4, 1f - ldq $27, 0($5) -1: ldq $0, 8($sp) /* syscall nr shadow (regs->r1) */ - - jsr $26, ($27), sys_ni_syscall - ldgp $gp, 0($26) - blt $0, $syscall_error /* the call failed */ -$ret_success: - stq $0, 0($sp) - stq $31, 72($sp) /* a3=0 => no error */ - - .align 4 - .globl ret_from_sys_call -ret_from_sys_call: - cmovne $26, 0, $18 /* $18 = 0 => non-restartable */ - ldq $0, SP_OFF($sp) - and $0, 8, $0 - beq $0, ret_to_kernel -ret_to_user: - /* Make sure need_resched and sigpending don't change between - sampling and the rti. */ + mov $0, $1 lda $16, 7 call_pal PAL_swpipl - ldl $17, TI_FLAGS($8) - and $17, _TIF_WORK_MASK, $2 - bne $2, work_pending -restore_all: - ldl $2, TI_STATUS($8) - and $2, TS_SAVED_FP | TS_RESTORE_FP, $3 - bne $3, restore_fpu -restore_other: - .cfi_remember_state - LOCKDEP_HARDIRQS_ON_RESTORE - RESTORE_ALL - call_pal PAL_rti - -ret_to_kernel: - .cfi_restore_state - lda $16, 7 - call_pal PAL_swpipl - br restore_other - - .align 3 -$syscall_error: - /* Restart syscall nr comes from saved r2 (preserved even if r0 overwritten). */ - ldq $18, 16($sp) /* old syscall nr for restart */ - - ldq $19, 72($sp) /* .. and this a3 */ - subq $31, $0, $0 /* with error in v0 */ - addq $31, 1, $1 /* set a3 for errno return */ - stq $0, 0($sp) - mov $31, $26 /* tell "ret_from_sys_call" we can restart */ - stq $1, 72($sp) /* a3 for return */ - br ret_from_sys_call - -/* - * Do all cleanup when returning from all interrupts and system calls. - * - * Arguments: - * $8: current. - * $17: TI_FLAGS. - * $18: The old syscall number, or zero if this is not a return - * from a syscall that errored and is possibly restartable. - * $19: The old a3 value - */ - - .align 4 - .type work_pending, @function -work_pending: - and $17, _TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL, $2 - bne $2, $work_notifysig - -$work_resched: - /* - * We can get here only if we returned from syscall without SIGPENDING - * or got through work_notifysig already. Either case means no syscall - * restarts for us, so let $18 and $19 burn. - */ - jsr $26, alpha_schedule_user_work - mov 0, $18 - br ret_to_user - -$work_notifysig: + mov $1, $0 mov $sp, $16 - DO_SWITCH_STACK - jsr $26, do_work_pending - UNDO_SWITCH_STACK - br restore_all + mov $0, $17 /* pv = selected syscall function */ -/* - * PTRACE syscall handler - */ - - .align 4 - .type strace, @function -strace: - /* set up signal stack, call syscall_trace */ - // NB: if anyone adds preemption, this block will need to be protected - ldl $1, TI_STATUS($8) - and $1, TS_SAVED_FP, $3 - or $1, TS_SAVED_FP, $2 - bne $3, 1f - stl $2, TI_STATUS($8) - bsr $26, __save_fpu -1: DO_SWITCH_STACK - jsr $26, syscall_trace_enter /* returns the syscall number */ + jsr $26, alpha_syscall_enter_select + ldgp $gp, 0($26) UNDO_SWITCH_STACK - stq $0, 8($sp) /* regs->r1 = shadow syscall nr */ + /* + * C returned syscall function pointer in $0. + * It also stored SKIP in TI_SYSCALL_META if dispatch is skipped. + */ + lda $8, 0x3fff + bic $sp, $8, $8 + ldq $3, TI_SYSCALL_META($8) + lda $6, ALPHA_SYSCALL_META_SKIP + and $3, $6, $6 + bne $6, skip_dispatch - /* get the arguments back.. */ + + mov $0, $27 ldq $16, SP_OFF+24($sp) ldq $17, SP_OFF+32($sp) ldq $18, SP_OFF+40($sp) @@ -678,56 +515,70 @@ strace: ldq $20, 80($sp) ldq $21, 88($sp) - /* nr == -1: internal skip-dispatch or userspace syscall(-1)? */ - lda $6, -1($31) - cmpeq $0, $6, $6 - bne $6, $strace_skip_call /* nr == -1 => dispatch */ - - /* get the system call pointer.. */ - lda $1, NR_syscalls($31) - lda $2, sys_call_table - lda $27, sys_ni_syscall - cmpult $0, $1, $1 - s8addq $0, $2, $2 - beq $1, 1f - ldq $27, 0($2) -1: jsr $26, ($27), sys_gettimeofday -ret_from_straced: + jsr $26, ($27), sys_ni_syscall ldgp $gp, 0($26) - /* check return.. */ - blt $0, $strace_error /* the call failed */ -$strace_success: - stq $31, 72($sp) /* a3=0 => no error */ - stq $0, 0($sp) /* save return value */ - -$strace_skip_call: - SYSCALL_SKIP_RETURN_RESTART_GATE - DO_SWITCH_STACK - jsr $26, syscall_trace_leave - UNDO_SWITCH_STACK - br $31, ret_from_sys_call - - .align 3 -$strace_error: - ldq $18, 16($sp) /* restart syscall nr */ - ldq $19, 72($sp) /* .. and this a3 */ - - subq $31, $0, $0 /* with error in v0 */ - addq $31, 1, $1 /* set a3 for errno return */ - stq $0, 0($sp) - stq $1, 72($sp) /* a3 for return */ +skip_dispatch: + mov $0, $17 /* raw ret; ignored if SKIP is set */ + mov $sp, $16 /* regs */ DO_SWITCH_STACK - mov $18, $9 /* save old syscall number */ - mov $19, $10 /* save old a3 */ - jsr $26, syscall_trace_leave - mov $9, $18 - mov $10, $19 + jsr $26, alpha_finish_syscall_to_user_mode + ldgp $gp, 0($26) UNDO_SWITCH_STACK - mov $31, $26 /* tell "ret_from_sys_call" we can restart */ - br ret_from_sys_call +restore_all: + lda $8, 0x3fff + bic $sp, $8, $8 + ldl $2, TI_STATUS($8) + and $2, TS_SAVED_FP | TS_RESTORE_FP, $3 + bne $3, restore_fpu +restore_other: + .cfi_remember_state + RESTORE_ALL + call_pal PAL_rti + +ret_to_user_from_syscall: + lda $8, 0x3fff + bic $sp, $8, $8 + + mov $sp, $16 + DO_SWITCH_STACK + jsr $26, alpha_syscall_exit_to_user_mode + ldgp $gp, 0($26) + UNDO_SWITCH_STACK + br restore_all + + .align 4 + .globl ret_from_sys_call +ret_from_sys_call: + ldq $0, SP_OFF($sp) + and $0, 8, $0 + beq $0, ret_to_kernel + br ret_to_user_from_syscall + +ret_from_exception: + ldq $0, SP_OFF($sp) + and $0, 8, $0 + beq $0, ret_to_kernel + br ret_to_user_from_exception + +ret_to_user_from_exception: + + mov $sp, $16 + DO_SWITCH_STACK + jsr $26, alpha_exit_to_user_mode + ldgp $gp, 0($26) + UNDO_SWITCH_STACK + br restore_all + +ret_to_kernel: + .cfi_restore_state + lda $16, 7 + call_pal PAL_swpipl + LOCKDEP_HARDIRQS_ON_RESTORE SP_OFF($sp) + br restore_other + CFI_END_OSF_FRAME entSys /* @@ -815,7 +666,6 @@ restore_fpu: br restore_other #undef V - /* * The meat of the context switch code. */ @@ -851,7 +701,7 @@ alpha_switch_to: .align 4 .ent ret_from_fork ret_from_fork: - lda $26, ret_to_user + lda $26, ret_to_user_from_exception mov $17, $16 jmp $31, schedule_tail .end ret_from_fork @@ -868,7 +718,7 @@ ret_from_kernel_thread: mov $9, $27 mov $10, $16 jsr $26, ($9) - br $31, ret_to_user + br $31, ret_to_user_from_exception .end ret_from_kernel_thread @@ -910,12 +760,9 @@ fork_like clone3 .ent sys_\name sys_\name: .prologue 0 - lda $9, ret_from_straced - cmpult $26, $9, $9 + mov $sp, $10 lda $sp, -SWITCH_STACK_SIZE($sp) jsr $26, do_\name - bne $9, 1f - jsr $26, syscall_trace_leave 1: br $1, undo_switch_stack br ret_from_sys_call .end sys_\name diff --git a/arch/alpha/kernel/err_common.c b/arch/alpha/kernel/err_common.c index 94e1b3dcf6d4..9271ea425a7b 100644 --- a/arch/alpha/kernel/err_common.c +++ b/arch/alpha/kernel/err_common.c @@ -44,6 +44,19 @@ mchk_dump_mem(void *data, size_t length, char **annotation) } } +/* Preserve rejected frames without trusting their internal offsets. */ +void +mchk_dump_logout_frame_raw(struct el_common *header) +{ + size_t length = header->size; + + /* Bound diagnostic output and never read a partial final quadword. */ + if (length > 1024) + length = 1024; + length &= ~7UL; + mchk_dump_mem(header, length, NULL); +} + void mchk_dump_logout_frame(struct el_common *mchk_header) { diff --git a/arch/alpha/kernel/err_ev6.c b/arch/alpha/kernel/err_ev6.c index 8144f2045b5b..f12299ae1f24 100644 --- a/arch/alpha/kernel/err_ev6.c +++ b/arch/alpha/kernel/err_ev6.c @@ -190,6 +190,25 @@ ev6_process_logout_frame(struct el_common *mchk_header, int print) (struct el_common_EV6_mcheck *)mchk_header; int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + /* + * ES40 correctable frames end the CPU area before EXC_ADDR (Table + * D-19). Validate both regions before decoding or dumping any data. + */ + if (mchk_header->proc_offset != offsetof(struct el_common_EV6_mcheck, + I_STAT) || + mchk_header->sys_offset < offsetof(struct el_common_EV6_mcheck, + EXC_ADDR) || + mchk_header->sys_offset > mchk_header->size || + ((mchk_header->sys_offset | mchk_header->size) & 7)) { + if (print) { + printk("%s Invalid EV6 logout frame: size %x, CPU %x, system %x\n", + err_print_prefix, mchk_header->size, + mchk_header->proc_offset, mchk_header->sys_offset); + mchk_dump_logout_frame_raw(mchk_header); + } + return MCHK_DISPOSITION_UNKNOWN_ERROR; + } + status |= ev6_parse_ibox(ev6mchk->I_STAT, print); status |= ev6_parse_mbox(ev6mchk->MM_STAT, ev6mchk->DC_STAT, ev6mchk->C_STAT, print); @@ -203,16 +222,15 @@ ev6_process_logout_frame(struct el_common *mchk_header, int print) if (status != MCHK_DISPOSITION_DISMISS) { char *saved_err_prefix = err_print_prefix; - /* - * Dump some additional information from the frame - */ - printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx" - " ISUM: 0x%016lx\n" - " PAL_BASE: 0x%016lx I_CTL: 0x%016lx" - " PCTX: 0x%016lx\n", - err_print_prefix, - ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM, - ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX); + /* These registers are absent from short correctable frames. */ + if (mchk_header->sys_offset >= sizeof(*ev6mchk)) + printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx" + " ISUM: 0x%016lx\n" + " PAL_BASE: 0x%016lx I_CTL: 0x%016lx" + " PCTX: 0x%016lx\n", + err_print_prefix, + ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM, + ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX); if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) { printk("%s UNKNOWN error, frame follows:\n", diff --git a/arch/alpha/kernel/err_impl.h b/arch/alpha/kernel/err_impl.h index 737b958a586d..5bef23a00942 100644 --- a/arch/alpha/kernel/err_impl.h +++ b/arch/alpha/kernel/err_impl.h @@ -47,6 +47,7 @@ struct el_subpacket_handler { extern char *err_print_prefix; extern void mchk_dump_mem(void *, size_t, char **); +extern void mchk_dump_logout_frame_raw(struct el_common *header); extern void mchk_dump_logout_frame(struct el_common *); extern void el_print_timestamp(union el_timestamp *); extern void el_process_subpackets(struct el_subpacket *, int); diff --git a/arch/alpha/kernel/err_tsunami.c b/arch/alpha/kernel/err_tsunami.c new file mode 100644 index 000000000000..dacf91d952fe --- /dev/null +++ b/arch/alpha/kernel/err_tsunami.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Machine check handling for Tsunami/Typhoon systems. + * Author: Magnus Lindholm + * Based on Titan error handling by Jeff Wiedemeier (Compaq). + * Register definitions: 21272 Hardware Reference Manual, revision 4.0. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "err_impl.h" +#include "proto.h" +#include "pci_impl.h" + +#define TSUNAMI_PERROR_ERRMASK 0xdffUL +#define TSUNAMI_PERROR_ECCMASK (perror_m_uecc | perror_m_cre) +#define TSUNAMI_PERROR_INV (1UL << 51) +#define TSUNAMI_MISC_NXM (1UL << 28) + +static int +tsunami_frame_valid(struct el_common *header, size_t size) +{ + return !((header->sys_offset | header->size) & 7) && + header->sys_offset >= sizeof(*header) && + header->sys_offset <= header->size && + size <= header->size - header->sys_offset; +} + +static int +tsunami_parse_p_perror(int which, u64 perror, int print) +{ + static const char * const errors[] = { + "Lost error", "SERR asserted", "PERR asserted", + "Delayed-completion retry timeout", "Invalid scatter-gather PTE", + "Address parity error", "Target abort", "Read data parity error", + "No DEVSEL (master abort)", NULL, + "Uncorrectable ECC error", "Correctable ECC error" + }; + static const char * const pci_cmd[] = { + "Interrupt acknowledge", "Special cycle", "I/O read", "I/O write", + "Reserved", "Reserved", "Memory read", "Memory write", + "Reserved", "Reserved", "Configuration read", "Configuration write", + "Memory read multiple", "Dual address cycle", + "Memory read line", "Memory write and invalidate" + }; + unsigned long addr; + unsigned int cmd; + int i; + + if (!(perror & TSUNAMI_PERROR_ERRMASK)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + /* Do not import Titan's BIOS master-abort dismissal policy. */ + if (!print) + return MCHK_DISPOSITION_REPORT; + + printk("%s Pchip %d PERROR: %016llx\n", err_print_prefix, which, perror); + for (i = 0; i < ARRAY_SIZE(errors); i++) + if ((perror & TSUNAMI_PERROR_ERRMASK & (1UL << i)) && errors[i]) + printk("%s %s\n", err_print_prefix, errors[i]); + + /* + * INV invalidates SYN, CMD and ADDR. HRM 8.8.2.6 leaves command/address + * undefined whenever SERR is set, including with other error bits. + */ + if (perror & (TSUNAMI_PERROR_INV | perror_m_serr)) { + printk("%s Transaction information unavailable\n", err_print_prefix); + return MCHK_DISPOSITION_REPORT; + } + cmd = (perror & perror_m_cmd) >> 52; + /* + * Table 10-42 selects the ECC fields whenever CRE or UECC is set. + * The first error freezes PERROR (10.2.5.6); mixed error bits were + * detected together. Only LOST can be set afterwards. + */ + if (perror & TSUNAMI_PERROR_ECCMASK) { + const char *command = "Reserved"; + + if (cmd == 0) + command = "DMA read"; + else if (cmd == 1) + command = "DMA RMW"; + else if (cmd == 3) + command = "SGTE read"; + addr = ((perror >> 19) & 0xffffffffUL) << 3; + printk("%s Command: %s (%x), syndrome: %02llx\n", + err_print_prefix, command, cmd, (perror & perror_m_syn) >> 56); + printk("%s System address: %lx\n", err_print_prefix, addr); + } else { + addr = ((perror >> 18) & 0x3fffffffUL) << 2; + /* SGE captures only PCI address bits <31:13> (8KB page). */ + if (perror & perror_m_sge) + addr &= ~0x1fffUL; + printk("%s Command: %s (%x), PCI address: %lx%s\n", + err_print_prefix, pci_cmd[cmd], cmd, addr, + (perror & perror_m_sge) ? " (8KB page)" : ""); + if (perror & (1UL << 17)) + printk("%s Monster window\n", err_print_prefix); + else if (perror & (1UL << 16)) + printk("%s DAC scatter-gather window 3\n", err_print_prefix); + } + return MCHK_DISPOSITION_REPORT; +} + +/* SESF bits 0/1 identify PCI errors, bit 2 covers ECC on either Pchip/CPU. */ +static u64 +tsunami_pchip_error(struct el_TSUNAMI_sysdata_mcheck *frame, int which) +{ + u64 perror = which ? frame->p1_perror : frame->p0_perror; + + if (frame->sesf & (1UL << which)) + return perror; + if ((frame->sesf & 4) && (perror & TSUNAMI_PERROR_ECCMASK)) + return perror; + return 0; +} + +static struct el_TSUNAMI_sysdata_mcheck * +tsunami_system_frame(struct el_common *header) +{ + if (!tsunami_frame_valid(header, sizeof(struct el_TSUNAMI_sysdata_mcheck))) + return NULL; + return (void *)header + header->sys_offset; +} + +static int +tsunami_process_logout_frame(struct el_common *header, int print) +{ + struct el_TSUNAMI_sysdata_mcheck *frame = tsunami_system_frame(header); + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + int i; + + if (!frame) { + if (print) { + printk("%s Invalid Tsunami system frame; raw frame follows\n", + err_print_prefix); + mchk_dump_logout_frame_raw(header); + } + return MCHK_DISPOSITION_UNKNOWN_ERROR; + } + if (print) + printk("%s SESF: %016llx DIR: %016llx MISC: %016llx\n", + err_print_prefix, frame->sesf, frame->dir, frame->misc); + if (frame->misc & TSUNAMI_MISC_NXM) { + status = MCHK_DISPOSITION_REPORT; + if (print) + printk("%s Non-existent memory, NXS: %llu\n", + err_print_prefix, (frame->misc >> 29) & 7); + } + for (i = 0; i < 2; i++) + status |= tsunami_parse_p_perror(i, tsunami_pchip_error(frame, i), print); + return status; +} + +struct tsunami_error_state { + u64 perror[2]; + u64 misc; +}; + +static void +tsunami_ack_pchip(tsunami_pchip *pchip, u64 perror) +{ + if (!(perror & TSUNAMI_PERROR_ERRMASK)) + return; + pchip->perror.csr = perror & TSUNAMI_PERROR_ERRMASK; + /* Complete the W1C write before releasing the logout frame. */ + mb(); + pchip->perror.csr; +} + +static void +tsunami_clear_errors(struct tsunami_error_state *state) +{ + bool pchip1 = TSUNAMI_cchip->csc.csr & (1UL << 14); + + /* Preserve each chip's transaction and the NXM source before clearing. */ + state->perror[0] = TSUNAMI_pchip0->perror.csr; + state->perror[1] = pchip1 ? TSUNAMI_pchip1->perror.csr : 0; + state->misc = TSUNAMI_cchip->misc.csr; + + /* + * Acknowledge both present Pchips: the saved frame may predate + * live errors. This can consume another chip's pending error + * with INV set before it reaches DRIR, losing its transaction + * details and subsequent system-error notification. + */ + tsunami_ack_pchip(TSUNAMI_pchip0, state->perror[0]); + /* Do not access the second Pchip on single-Pchip systems. */ + if (pchip1) + tsunami_ack_pchip(TSUNAMI_pchip1, state->perror[1]); + if (state->misc & TSUNAMI_MISC_NXM) { + /* Zero writes leave the other MISC fields unchanged. */ + TSUNAMI_cchip->misc.csr = TSUNAMI_MISC_NXM; + /* Complete the NXM acknowledgment before releasing the frame. */ + mb(); + TSUNAMI_cchip->misc.csr; + } +} + +static void +tsunami_report_live_errors(struct tsunami_error_state *state, + struct el_TSUNAMI_sysdata_mcheck *frame) +{ + int i; + + /* PALcode's frame may predate these errors, or describe another chip. */ + for (i = 0; i < 2; i++) { + if (!(state->perror[i] & TSUNAMI_PERROR_ERRMASK) || + (frame && state->perror[i] == tsunami_pchip_error(frame, i))) + continue; + printk("%s Live Pchip status before acknowledgment:\n", err_print_prefix); + tsunami_parse_p_perror(i, state->perror[i], 1); + } + if ((state->misc & TSUNAMI_MISC_NXM) && + (!frame || ((state->misc ^ frame->misc) & (0xfUL << 28)))) + printk("%s Live MISC: %016llx, non-existent memory, NXS: %llu\n", + err_print_prefix, state->misc, (state->misc >> 29) & 7); +} + +void +tsunami_machine_check(unsigned long vector, unsigned long la_ptr) +{ + struct el_common *header = (void *)la_ptr; + struct el_TSUNAMI_sysdata_mcheck *frame = NULL; + struct tsunami_error_state live = {}; + char *saved_err_prefix = err_print_prefix; + bool show_regs = vector == SCB_Q_SYSMCHK; + + /* Synchronize pending accesses before inspecting the chipset. */ + mb(); + mb(); + draina(); + /* CPU/event vectors must not consume a pending Pchip system error. */ + if (vector == SCB_Q_SYSMCHK || vector == SCB_Q_SYSERR) { + tsunami_clear_errors(&live); + frame = tsunami_system_frame(header); + } + +#ifdef CONFIG_VERBOSE_MCHECK + show_regs |= alpha_verbose_mcheck != 0; +#endif + err_print_prefix = KERN_CRIT; + if (vector == SCB_Q_SYSMCHK || vector == SCB_Q_SYSERR) { + printk("%sSystem %s error (vector %lx, code %x) on CPU %d:\n", + err_print_prefix, vector == SCB_Q_SYSERR ? "correctable" : + "uncorrectable", vector, header->code, smp_processor_id()); + tsunami_process_logout_frame(header, 1); + if (show_regs) + dik_show_regs(get_irq_regs(), NULL); + tsunami_report_live_errors(&live, frame); + } else if (vector == SCB_Q_SYSEVENT) { + printk("%sTsunami environmental event (code %x): layout unknown\n", + err_print_prefix, header->code); + mchk_dump_logout_frame_raw(header); + } + err_print_prefix = saved_err_prefix; + + if (vector != SCB_Q_SYSMCHK && vector != SCB_Q_SYSERR && + vector != SCB_Q_SYSEVENT) { + /* EV6 decoding also releases the logout frame. */ + ev6_machine_check(vector, la_ptr); + } else { + wrmces(0x7); + /* Complete acknowledgment before resuming. */ + mb(); + } +} + +static const char * const CLIPPER_EnvQW1SMIR[] = { + "", /* SMIR<0>: retain as raw context until its semantics are known. */ + "OCP or RMC halt detected", + "Sys_DC_Notok failure detected", + "", + "System temperature over 50 degrees C failure", + "PCI Bus #0 is in reset", + "PCI Bus #1 is in reset", + "System is being reset" +}; + +static const char * const CLIPPER_EnvQW2CPUIR[] = { + "CPU0 regulator is enabled", + "CPU1 regulator is enabled", + "CPU2 regulator is enabled", + "CPU3 regulator is enabled", + "CPU0 regulator or configuration sequence fail", + "CPU1 regulator or configuration sequence fail", + "CPU2 regulator or configuration sequence fail", + "CPU3 regulator or configuration sequence fail" +}; + +static const char * const CLIPPER_EnvQW3PSIR[] = { + "Power Supply 0 is enabled", + "Power Supply 1 is enabled", + "Power Supply 2 is enabled", + "", + "Power Supply 0 was enabled but failed", + "Power Supply 1 was enabled but failed", + "Power Supply 2 was enabled but failed" +}; + +static const char * const CLIPPER_EnvQW4LM78ISR[] = { + "PS +3.3V out of tolerance", + "PS +5V out of tolerance", + "PS +12V out of tolerance", + "VTERM out of tolerance", + "Temperature zone 0 (PCI Backplane slots 1-3 area) over limit failure", + "LM75 CPU0-3 Temperature over limit failure", + "System Fan 1 failure", + "System Fan 2 failure", + "CTERM out of tolerance", + "", + "-12V out of tolerance", + "", + "", + "", + "", + "", + "CPU0_VCORE +2V out of tolerance", + "CPU0_VIO +1.5V out of tolerance", + "CPU1_VCORE +2V out of tolerance", + "CPU1_VIO +1.5V out of tolerance", + "Temperature zone 1 (PCI Backplane slots 7-10 area) over limit failure", + "", + "System Fan 4 failure", + "System Fan 5 failure", + "", + "", + "", + "", + "", + "", + "", + "", + "CPU2_VCORE +2V out of tolerance", + "CPU2_VIO +1.5V out of tolerance", + "CPU3_VCORE +2V out of tolerance", + "CPU3_VIO +1.5V out of tolerance", + "Temperature zone 2 (PCI Backplane slots 4-6 area) over limit failure", + "", + "System Fan 3 failure", + "System Fan 6 failure", + "", + "", + "Power supply 3.3V rail above high amperage warning", + "Power supply 5.0V rail above high amperage warning", + "Power supply 12V rail above high amperage warning", + "Power supply high temperature warning", + "Power supply AC input low limit warning", + "Power supply AC input high limit warning" +}; + +static const char * const CLIPPER_EnvQW5DOOR[] = { + "", + "System CPU door is open", + "System Fan door is open", + "System PCI door is open", + "", + "System CPU door is closed", + "System Fan door is closed", + "System PCI door is closed" +}; + +static const char * const CLIPPER_EnvQW6TEMP[] = { + "CPU0 temperature warning fault has occurred", + "CPU1 temperature warning fault has occurred", + "CPU2 temperature warning fault has occurred", + "CPU3 temperature warning fault has occurred", + "System temperature zone 0 warning fault has occurred", + "System temperature zone 1 warning fault has occurred", + "System temperature zone 2 warning fault has occurred" +}; + +static const char * const CLIPPER_EnvQW7FAN[] = { + "System Fan 1 is not responding to RMC Commands", + "System Fan 2 is not responding to RMC Commands", + "System Fan 3 is not responding to RMC Commands", + "System Fan 4 is not responding to RMC Commands", + "System Fan 5 is not responding to RMC Commands", + "System Fan 6 is not responding to RMC Commands", + "", + "", + "CPU fans 5/6 at maximum speed", + "CPU fans 5/6 reduced speed from maximum", + "PCI fans 1-4 at maximum speed", + "PCI fans 1-4 reduced speed from maximum" +}; + +#define CLIPPER_ENV_LM78ISR_MASK 0xfcdf00df05ffUL + +/* ES40 Service Guide EK-ES240-SV A01, live frame: Tables D-20 and D-21. */ +struct el_clipper_envdata { + u64 summary; + u64 dir; + u64 smir; + u64 cpuir; + u64 psir; + u64 fault; + u64 doors; + u64 temp_warn; + u64 fan_ctrl; + u64 fatal_power_down; + u64 reserved; +}; + +static bool +clipper_print_bits(const char * const *messages, size_t count, + u64 value, u64 mask) +{ + size_t i; + + if (!(value & mask)) + return false; + for (i = 0; i < count; i++) + if (value & mask & (1UL << i)) + printk("%s %s\n", err_print_prefix, messages[i]); + return true; +} + +#define CLIPPER_BITS(table, value, mask) \ + clipper_print_bits(table, ARRAY_SIZE(table), value, mask) + +static void +clipper_process_environment(struct el_common *header) +{ + struct el_clipper_envdata *frame; + bool fault = false; + + if (!tsunami_frame_valid(header, sizeof(*frame))) { + printk("%s Truncated Clipper environmental frame\n", err_print_prefix); + mchk_dump_logout_frame_raw(header); + return; + } + frame = (void *)header + header->sys_offset; + /* Only QW1 through QW7 carry error state in the live 680 frame. */ + /* PSU summary and inverted halt/reset signals remain raw context. */ + fault |= CLIPPER_BITS(CLIPPER_EnvQW1SMIR, frame->smir, 0x14); + fault |= CLIPPER_BITS(CLIPPER_EnvQW2CPUIR, frame->cpuir, 0xf0); + fault |= CLIPPER_BITS(CLIPPER_EnvQW3PSIR, frame->psir, 0x70); + fault |= CLIPPER_BITS(CLIPPER_EnvQW4LM78ISR, + frame->fault, CLIPPER_ENV_LM78ISR_MASK); + fault |= CLIPPER_BITS(CLIPPER_EnvQW5DOOR, frame->doors, 0x0e); + fault |= CLIPPER_BITS(CLIPPER_EnvQW6TEMP, frame->temp_warn, 0x7f); + fault |= CLIPPER_BITS(CLIPPER_EnvQW7FAN, frame->fan_ctrl, 0x3f); + /* A notification with no recognized fault can describe recovery. */ + if (!fault) + printk("%s No recognized environmental fault bits set\n", + err_print_prefix); + printk("%s Environmental summary: %016llx DIR: %016llx\n", + err_print_prefix, frame->summary, frame->dir); + printk("%s SMIR: %016llx (PSU summary/halt/reset bits undecoded)\n", + err_print_prefix, frame->smir); + CLIPPER_BITS(CLIPPER_EnvQW2CPUIR, frame->cpuir, 0x0f); + CLIPPER_BITS(CLIPPER_EnvQW3PSIR, frame->psir, 0x07); + CLIPPER_BITS(CLIPPER_EnvQW5DOOR, frame->doors, 0xe0); + CLIPPER_BITS(CLIPPER_EnvQW7FAN, frame->fan_ctrl, 0xf00); + if (frame->fault & (0x3fUL << 42)) + printk("%s Power supply warning source: %llu (3 = reserved)\n", + err_print_prefix, (frame->fault >> 40) & 3); +} + +void +clipper_machine_check(unsigned long vector, unsigned long la_ptr) +{ + struct el_common *header = (void *)la_ptr; + char *saved_err_prefix = err_print_prefix; + + if (vector != SCB_Q_SYSEVENT) { + tsunami_machine_check(vector, la_ptr); + return; + } + /* Drain pending accesses before inspecting the environmental frame. */ + mb(); + draina(); + err_print_prefix = KERN_CRIT; + printk("%sSystem correctable environmental event (code %x) on CPU %d:\n", + err_print_prefix, header->code, smp_processor_id()); + if (header->code == 0x206) + clipper_process_environment(header); + else { + printk("%s Unrecognized environmental event; raw frame follows\n", + err_print_prefix); + mchk_dump_logout_frame_raw(header); + } + err_print_prefix = saved_err_prefix; + wrmces(0x7); + /* Complete acknowledgment before returning from the event. */ + mb(); +} + +static struct el_subpacket * __init +tsunami_process_subpacket(struct el_subpacket *packet) +{ + struct el_common *frame; + size_t offset = offsetof(struct el_subpacket, by_type.regatta_frame.data_start); + int status; + + /* Raw dumps consume whole quadwords, and the next packet must advance. */ + if (packet->length < offsetof(struct el_subpacket, by_type) || + (packet->length & 7)) + return NULL; + if (packet->revision != 1) { + el_annotate_subpacket(packet); + return (void *)packet + packet->length; + } + + switch (packet->type) { + case EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME: + case EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT: + case EL_TYPE__REGATTA__SYSTEM_ERROR_FRAME: + case EL_TYPE__REGATTA__SYSTEM_DBL_ERROR_HALT: + if (packet->length < offset + sizeof(*frame)) + return NULL; + frame = (void *)packet + offset; + if (frame->size > packet->length - offset || frame->size < sizeof(*frame)) + return NULL; + if (packet->type == EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME || + packet->type == EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT) { + printk("%s ** Occurred on CPU %d:\n", err_print_prefix, + (int)packet->by_type.regatta_frame.cpuid); + status = ev6_process_logout_frame(frame, 0); + if (status != MCHK_DISPOSITION_DISMISS) + ev6_process_logout_frame(frame, 1); + } else { + tsunami_process_logout_frame(frame, 1); + } + break; + case EL_TYPE__REGATTA__ENVIRONMENTAL_FRAME: + /* + * ES40 Table D-18 gives sys_offset=0x20, but places the + * summary at frame+0x18. Preserve the complete packet until + * the console layout is verified; do not apply the live layout. + */ + printk("%s Console environmental layout unverified; raw packet follows\n", + err_print_prefix); + el_annotate_subpacket(packet); + break; + default: + el_annotate_subpacket(packet); + break; + } + return (void *)packet + packet->length; +} + +/* + * Clipper console logs have an HWRPB byte count. Do not use the generic + * unbounded dispatcher: a rejected record must not acknowledge the log. + */ +static bool __init +tsunami_console_log(void *data, size_t remaining) +{ + struct el_subpacket *packet; + unsigned char *pos = data; + size_t size, length; + u32 count; + union el_timestamp timestamp; + const char *name; + + while (remaining >= offsetof(struct el_subpacket, by_type)) { + packet = (void *)pos; + size = packet->length; + if (size < offsetof(struct el_subpacket, by_type) || + size > remaining || (size & 7)) + return false; + if (packet->class == EL_CLASS__TERMINATION) + return packet->type == EL_TYPE__TERMINATION__TERMINATION; + if (packet->class != EL_CLASS__HEADER || packet->revision != 1) + return false; + + timestamp.as_int = 0; + /* Check each header's extent before accessing its union member. */ +#define CONSOLE_HEADER_SIZE(member) \ + (offsetof(struct el_subpacket, by_type.member) + \ + sizeof(packet->by_type.member)) + switch (packet->type) { + case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME: + if (size < CONSOLE_HEADER_SIZE(sys_err)) + return false; + name = "SYSTEM ERROR"; + length = packet->by_type.sys_err.frame_length; + count = packet->by_type.sys_err.frame_packet_count; + break; + case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME: + if (size < CONSOLE_HEADER_SIZE(sys_event)) + return false; + name = "SYSTEM EVENT"; + timestamp = packet->by_type.sys_event.timestamp; + length = packet->by_type.sys_event.frame_length; + count = packet->by_type.sys_event.frame_packet_count; + break; + case EL_TYPE__HEADER__HALT_FRAME: + if (size < CONSOLE_HEADER_SIZE(err_halt)) + return false; + name = "ERROR HALT"; + timestamp = packet->by_type.err_halt.timestamp; + length = packet->by_type.err_halt.frame_length; + count = packet->by_type.err_halt.frame_packet_count; + break; + case EL_TYPE__HEADER__LOGOUT_FRAME: + if (size < CONSOLE_HEADER_SIZE(logout_header)) + return false; + name = "LOGOUT FRAME"; + length = packet->by_type.logout_header.frame_length; + count = 1; + break; + default: + return false; + } +#undef CONSOLE_HEADER_SIZE + if (length > remaining - size || (length & 7) || + count > length / offsetof(struct el_subpacket, by_type)) + return false; + printk("%s*** %s: CLASS %u, TYPE %u\n", err_print_prefix, + name, packet->class, packet->type); + el_print_timestamp(×tamp); + if (packet->type == EL_TYPE__HEADER__HALT_FRAME) + printk("%s HALT CODE: %04x\n", err_print_prefix, + packet->by_type.err_halt.halt_code); + pos += size; + remaining -= size + length; + while (count--) { + if (length < offsetof(struct el_subpacket, by_type)) + return false; + packet = (void *)pos; + size = packet->length; + if (size < offsetof(struct el_subpacket, by_type) || + size > length || (size & 7) || + packet->class != EL_CLASS__REGATTA_FAMILY) + return false; + /* This handler checks embedded logout-frame bounds. */ + if (tsunami_process_subpacket(packet) != (void *)pos + size) + return false; + pos += size; + length -= size; + } + if (length) + return false; + } + /* Exhaustion without a complete termination packet is incomplete. */ + return false; +} + +static void __init +tsunami_console_data_log(int cpu, struct percpu_struct *pcpu) +{ + unsigned char *data = (void *)(IDENT_ADDR | pcpu->console_data_log_pa); + size_t length = pcpu->console_data_log_length; + size_t dump = length < 1024 ? length : 1024; + + printk("%sConsole data log for CPU %d (%zu bytes):\n", + err_print_prefix, cpu, length); + if (!(pcpu->console_data_log_pa & 7) && + length <= ~0UL - (unsigned long)data && + tsunami_console_log(data, length)) { + /* Acknowledge only after reaching a valid termination packet. */ + pcpu->console_data_log_pa = 0; + return; + } + printk("%s Incomplete console log; retaining firmware pointer\n", + err_print_prefix); + /* Byte reads preserve partial headers without rounding beyond length. */ + if (dump > ~0UL - (unsigned long)data) + return; + print_hex_dump(err_print_prefix, " ", DUMP_PREFIX_OFFSET, 16, 1, + data, dump, false); + if (dump < length) + printk("%s Raw console log truncated to %zu bytes\n", + err_print_prefix, dump); +} + +void __init +tsunami_check_console_data_log(void) +{ + struct percpu_struct *pcpu; + unsigned long cpu; + + for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) { + pcpu = (void *)hwrpb + hwrpb->processor_offset + + cpu * hwrpb->processor_size; + if (pcpu->console_data_log_pa) + tsunami_console_data_log(cpu, pcpu); + } +} diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 4a6a8b1d5a8b..5867a1655045 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -107,18 +107,21 @@ handle_irq(int irq) * handled by some other CPU. (or is disabled) */ static unsigned int illegal_count=0; - struct irq_desc *desc = irq_to_desc(irq); - + struct irq_desc *desc; + + irq_enter(); + + desc = irq_to_desc(irq); if (!desc || ((unsigned) irq > ACTUAL_NR_IRQS && illegal_count < MAX_ILLEGAL_IRQS)) { irq_err_count++; illegal_count++; printk(KERN_CRIT "device_interrupt: invalid interrupt %d\n", irq); + irq_exit(); return; } - irq_enter(); generic_handle_irq_desc(desc); irq_exit(); } diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c index 1b799aafb7dc..8366d6de439d 100644 --- a/arch/alpha/kernel/irq_alpha.c +++ b/arch/alpha/kernel/irq_alpha.c @@ -116,20 +116,24 @@ void notrace lockdep_on_restore(unsigned long ps, unsigned long ip) { #ifdef CONFIG_PROVE_LOCKING - /* Restoring IPL==7 means interrupts remain disabled. */ + /* + * If PAL_rti will restore IPL == 7, IRQs remain disabled. + * There is no hardirqs-on transition to annotate. + */ if ((ps & 7) == 7) return; /* - * If hardware IRQs are already enabled here, then emitting a - * hardirqs-on transition is redundant. + * This helper is meant to run before PAL_rti, after entry.S has + * forced IPL to 7. If IRQs are already enabled, do not emit a + * fake transition. */ if (!irqs_disabled()) return; /* - * Only emit the transition if lockdep currently believes - * hardirqs are off. + * Only emit an ON transition if lockdep currently tracks hardirqs + * as off. */ if (lockdep_hardirqs_enabled()) return; @@ -148,6 +152,19 @@ common_init_isa_dma(void) outb(0, DMA2_CLR_MASK_REG); } +/* + * The platforms wire up their interrupts by calling into the generic irq + * code with fixed interrupt numbers, so the descriptors have to exist by + * then. Preallocate the machine's full complement rather than take the + * NR_IRQS_LEGACY default, which would leave everything above IRQ 15 + * without a descriptor and silently drop the handler setup. + */ +int __init +arch_probe_nr_irqs(void) +{ + return alpha_mv.nr_irqs; +} + void __init init_IRQ(void) { diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 7b6543d2cca3..61d806176213 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1201,14 +1202,16 @@ SYSCALL_DEFINE1(old_adjtimex, struct timex32 __user *, txc_p) /* Get an address range which is currently unmapped. */ static unsigned long -arch_get_unmapped_area_1(unsigned long addr, unsigned long len, - unsigned long limit) +arch_get_unmapped_area_1(struct file *filp, unsigned long addr, + unsigned long len, unsigned long limit) { struct vm_unmapped_area_info info = {}; info.length = len; info.low_limit = addr; info.high_limit = limit; + if (filp && is_file_hugepages(filp)) + info.align_mask = huge_page_mask_align(filp); return vm_unmapped_area(&info); } @@ -1236,19 +1239,20 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, this feature should be incorporated into all ports? */ if (addr) { - addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit); + addr = arch_get_unmapped_area_1 (filp, PAGE_ALIGN(addr), len, + limit); if (addr != (unsigned long) -ENOMEM) return addr; } /* Next, try allocating at TASK_UNMAPPED_BASE. */ - addr = arch_get_unmapped_area_1 (PAGE_ALIGN(TASK_UNMAPPED_BASE), + addr = arch_get_unmapped_area_1 (filp, PAGE_ALIGN(TASK_UNMAPPED_BASE), len, limit); if (addr != (unsigned long) -ENOMEM) return addr; /* Finally, try allocating in low memory. */ - addr = arch_get_unmapped_area_1 (PAGE_SIZE, len, limit); + addr = arch_get_unmapped_area_1 (filp, PAGE_SIZE, len, limit); return addr; } diff --git a/arch/alpha/kernel/perf_regs.c b/arch/alpha/kernel/perf_regs.c new file mode 100644 index 000000000000..33ea2e831ba0 --- /dev/null +++ b/arch/alpha/kernel/perf_regs.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#define REG(idx, field) \ + [PERF_REG_ALPHA_##idx] = offsetof(struct pt_regs, field) + +#define REG_RESERVED (~GENMASK_ULL(PERF_REG_ALPHA_MAX - 1, 0) | \ + GENMASK_ULL(PERF_REG_ALPHA_R15, PERF_REG_ALPHA_R9)) + +static const unsigned int pt_regs_offset[PERF_REG_ALPHA_MAX] = { + REG(R0, r0), REG(R1, r1), REG(R2, r2), REG(R3, r3), + REG(R4, r4), REG(R5, r5), REG(R6, r6), REG(R7, r7), + REG(R8, r8), REG(R16, r16), REG(R17, r17), REG(R18, r18), + REG(R19, r19), REG(R20, r20), REG(R21, r21), REG(R22, r22), + REG(R23, r23), REG(R24, r24), REG(R25, r25), REG(R26, r26), + REG(R27, r27), REG(R28, r28), REG(GP, gp), REG(PC, pc), +}; + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + if (idx == PERF_REG_ALPHA_SP) { + if (user_mode(regs)) + return user_stack_pointer(regs); + /* + * Software events built by perf_fetch_caller_regs() use a + * per-CPU pt_regs rather than a trap frame, and pt_regs has + * nowhere to record sp, so report none. + */ + if (!object_is_on_stack(regs)) + return 0; + /* Same kernel sp approximation as stacktrace.c. */ + return (unsigned long)(regs + 1); + } + + if (WARN_ON_ONCE((u32)idx >= PERF_REG_ALPHA_MAX || + BIT_ULL(idx) & REG_RESERVED)) + return 0; + + return *(unsigned long *)((void *)regs + pt_regs_offset[idx]); +} + +int perf_reg_validate(u64 mask) +{ + if (!mask || mask & REG_RESERVED) + return -EINVAL; + + return 0; +} + +u64 perf_reg_abi(struct task_struct *task) +{ + 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/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 06522451f018..7861c9e767b7 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -318,7 +318,7 @@ dump_elf_thread(elf_greg_t *dest, struct pt_regs *pt, struct thread_info *ti) dest[27] = pt->r27; dest[28] = pt->r28; dest[29] = pt->gp; - dest[30] = ti == current_thread_info() ? rdusp() : ti->pcb.usp; + dest[30] = user_stack_pointer(pt); dest[31] = pt->pc; /* Once upon a time this was the PS value. Which is stupid diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 9b262ef09a3a..7ece3b635e27 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -2,6 +2,7 @@ #include #include #include +#include /* Prototypes of functions used across modules here in this directory. */ @@ -77,6 +78,8 @@ extern struct _alpha_agp_info *titan_agp_info(void); extern struct pci_ops tsunami_pci_ops; extern void tsunami_init_arch(void); extern void tsunami_kill_arch(int); +extern void tsunami_check_console_data_log(void); +extern void clipper_machine_check(unsigned long vector, unsigned long la_ptr); extern void tsunami_machine_check(unsigned long vector, unsigned long la_ptr); extern void tsunami_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); @@ -164,16 +167,18 @@ extern void pcibios_claim_one_bus(struct pci_bus *); /* ptrace.c */ extern int ptrace_set_bpt (struct task_struct *child); extern int ptrace_cancel_bpt (struct task_struct *child); -extern void syscall_trace_leave(void); -extern unsigned long syscall_trace_enter(void); /* signal.c */ struct sigcontext; extern void do_sigreturn(struct sigcontext __user *); struct rt_sigframe; extern void do_rt_sigreturn(struct rt_sigframe __user *); -extern void do_work_pending(struct pt_regs *, unsigned long, unsigned long, unsigned long); extern void alpha_schedule_user_work(void); +extern void do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19); +extern void alpha_syscall_exit_to_user_mode(struct pt_regs *regs); +extern void alpha_exit_to_user_mode(struct pt_regs *regs); +extern void alpha_finish_syscall_to_user_mode(struct pt_regs *regs, long ret); +extern unsigned long alpha_syscall_enter_select(struct pt_regs *regs, long syscall); /* traps.c */ extern void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15); diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index fc8f6cedbb28..dba7e9905cfa 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -134,18 +134,44 @@ get_reg_addr(struct task_struct * task, unsigned long regno) /* * Get contents of register REGNO in task TASK. */ -static unsigned long -get_reg(struct task_struct * task, unsigned long regno) + +static bool +valid_regno(unsigned long regno) { - /* Special hack for fpcr -- combine hardware and software bits. */ + return regno <= 65; +} + +static long +get_reg(struct task_struct *task, unsigned long regno) +{ + unsigned long *addr; + + if (!valid_regno(regno)) + return -EIO; + + /* + * Special hack for fpcr -- combine hardware and software bits. + */ if (regno == 63) { - unsigned long fpcr = *get_reg_addr(task, regno); - unsigned long swcr - = task_thread_info(task)->ieee_state & IEEE_SW_MASK; + unsigned long fpcr; + unsigned long swcr; + + addr = get_reg_addr(task, regno); + if (!addr) + return -EIO; + + fpcr = *addr; + swcr = task_thread_info(task)->ieee_state & IEEE_SW_MASK; swcr = swcr_update_status(swcr, fpcr); + return fpcr | swcr; } - return *get_reg_addr(task, regno); + + addr = get_reg_addr(task, regno); + if (!addr) + return -EIO; + + return *addr; } static void alpha_elf_fpregs_get(struct task_struct *target, @@ -271,14 +297,17 @@ static void alpha_elf_gregs_set(struct task_struct *child, pt->r19 = 1; } - -/* - * Write contents of register REGNO in task TASK. - */ static int put_reg(struct task_struct *task, unsigned long regno, unsigned long data) { struct pt_regs *regs = task_pt_regs(task); + unsigned long *addr; + unsigned long old_r0 = regs->r0; + + if (regno == 31) + return 0; + if (!valid_regno(regno)) + return -EIO; if (regno == 63) { task_thread_info(task)->ieee_state @@ -287,24 +316,30 @@ put_reg(struct task_struct *task, unsigned long regno, unsigned long data) data = (data & FPCR_DYN_MASK) | ieee_swcr_to_fpcr(data); } - *get_reg_addr(task, regno) = data; + addr = get_reg_addr(task, regno); + if (!addr) + return -EIO; + + *addr = data; /* * Alpha historically exposes r0/v0 as the syscall number at a * syscall-entry stop. The generic-entry conversion keeps the - * mutable syscall number in regs->r1, so old ptrace users such - * as strace that skip a syscall by poking r0 to -1 must also - * update the internal shadow syscall number. - * - * Do not mirror other r0 writes. strace later pokes r0 to the - * injected return value, e.g. 42, while r1 must remain -1. + * mutable syscall number in regs->r1. */ - if (regno == 0 && data == (unsigned long)-1) { + if (regno == 0 && regs->r1 == old_r0 && + (data == (unsigned long)-1 || + (regs->r19 == 0 && data < NR_syscalls))) { regs->r1 = data; - regs->r19 = 0; - } + /* + * Keep the skip path looking like a clean entry-side syscall + * rewrite. Do not touch r19 for ordinary syscall substitution. + */ + if (data == (unsigned long)-1) + regs->r19 = 0; + } return 0; } @@ -405,6 +440,18 @@ ptrace_cancel_bpt(struct task_struct * child) return (nsaved != 0); } +/* + * PALcode keeps the user stack pointer in the PCB, not the trap frame. + * A user-mode pt_regs sits at the top of its task's stack, so the + * thread_info is at the base. + */ +unsigned long user_stack_pointer(struct pt_regs *regs) +{ + struct thread_info *ti = (void *)((unsigned long)regs & -THREAD_SIZE); + + return ti == current_thread_info() ? rdusp() : ti->pcb.usp; +} + void user_enable_single_step(struct task_struct *child) { /* Mark single stepping. */ @@ -435,25 +482,24 @@ long arch_ptrace(struct task_struct *child, long request, switch (request) { /* When I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE); ret = -EIO; if (copied != sizeof(tmp)) break; - force_successful_syscall_return(); ret = tmp; break; - /* Read register number ADDR. */ case PTRACE_PEEKUSR: - force_successful_syscall_return(); ret = get_reg(child, addr); - DBG(DBG_MEM, ("peek $%lu->%#lx\n", addr, ret)); - break; + if (ret == -EIO) + break; + force_successful_syscall_return(); + break; /* When I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: @@ -471,47 +517,6 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -asmlinkage unsigned long syscall_trace_enter(void) -{ - struct pt_regs *regs = current_pt_regs(); - - if (test_thread_flag(TIF_SYSCALL_TRACE) && - !ptrace_report_syscall_permit_entry(regs)) { - syscall_set_nr(current, regs, -1); - if (regs->r19 == 0 && regs->r0 == (unsigned long)-1) - syscall_set_return_value(current, regs, -ENOSYS, 0); - return -1UL; - } - - /* - * Do the secure computing after ptrace; failures should be fast. - * If this fails, seccomp may already have set up the return value - * (e.g. SECCOMP_RET_ERRNO / TRACE). - */ - if (!seccomp_permit_syscall()) { - if (regs->r19 == 0 && regs->r0 == (unsigned long)-1) - syscall_set_return_value(current, regs, -ENOSYS, 0); - syscall_set_nr(current, regs, -1); - return -1UL; - } - -#ifdef CONFIG_AUDITSYSCALL - audit_syscall_entry(syscall_get_nr(current, regs), - regs->r16, regs->r17, regs->r18, regs->r19); -#endif - return syscall_get_nr(current, regs); -} - - - -asmlinkage void -syscall_trace_leave(void) -{ - audit_syscall_exit(current_pt_regs()); - if (test_thread_flag(TIF_SYSCALL_TRACE)) - ptrace_report_syscall_exit(current_pt_regs(), 0); -} - /* * Minimal regset support for Alpha. * @@ -522,7 +527,6 @@ syscall_trace_leave(void) * regset_get should return 0 on success. So call dump_elf_thread() * directly and return membuf_write()'s result. */ - static int alpha_regset_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index ce40a49b8496..0c55a7554d63 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -27,10 +27,9 @@ #include #include #include - +#include #include "proto.h" - #define DEBUG_SIG 0 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) @@ -41,14 +40,6 @@ asmlinkage void ret_from_sys_call(void); * The OSF/1 sigprocmask calling sequence is different from the * C sigprocmask() sequence.. */ - -asmlinkage void alpha_schedule_user_work(void) -{ - local_irq_enable(); - schedule(); - local_irq_disable(); -} - SYSCALL_DEFINE2(osf_sigprocmask, int, how, unsigned long, newmask) { sigset_t oldmask; @@ -465,6 +456,7 @@ syscall_restart(unsigned long r0, unsigned long r19, fallthrough; case ERESTARTNOINTR: regs->r0 = r0; /* reset v0 and a3 and replay syscall */ + regs->r1 = r0; regs->r19 = r19; regs->pc -= 4; break; @@ -488,7 +480,7 @@ syscall_restart(unsigned long r0, unsigned long r19, * restart. "r0" is also used as an indicator whether we can restart at * all (if we get here from anything but a syscall return, it will be 0) */ -static void +void do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19) { unsigned long single_stepping = ptrace_cancel_bpt(current); @@ -511,12 +503,14 @@ do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19) case ERESTARTNOINTR: /* Reset v0 and a3 and replay syscall. */ regs->r0 = r0; + regs->r1 = r0; regs->r19 = r19; regs->pc -= 4; break; case ERESTART_RESTARTBLOCK: /* Set v0 to the restart_syscall and replay */ regs->r0 = __NR_restart_syscall; + regs->r1 = __NR_restart_syscall; regs->pc -= 4; break; } @@ -527,27 +521,133 @@ do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19) ptrace_set_bpt(current); /* re-set breakpoint */ } -void -do_work_pending(struct pt_regs *regs, unsigned long thread_flags, - unsigned long r0, unsigned long r19) +asmlinkage void alpha_exit_to_user_mode(struct pt_regs *regs) { - do { - if (thread_flags & _TIF_NEED_RESCHED) { - local_irq_enable(); - schedule(); - } else { - local_irq_enable(); - if (thread_flags & (_TIF_SIGPENDING|_TIF_NOTIFY_SIGNAL)) { - preempt_disable(); - save_fpu(); - preempt_enable(); - do_signal(regs, r0, r19); - r0 = 0; - } else { - resume_user_mode_work(regs); - } - } - local_irq_disable(); - thread_flags = read_thread_flags(); - } while (thread_flags & _TIF_WORK_MASK); + local_irq_disable(); + irqentry_exit_to_user_mode_prepare(regs); + exit_to_user_mode(); +} + +/* + * Syscall return reaches here after Alpha-specific r0/a3 result encoding. + * Delegate syscall-exit work and final exit-to-user handling to generic + * entry code; low-level PAL restore remains in assembly. + */ +asmlinkage void alpha_syscall_exit_to_user_mode(struct pt_regs *regs) +{ + syscall_exit_to_user_mode(regs); +} + +void arch_do_signal_or_restart(struct pt_regs *regs) +{ + struct thread_info *ti = current_thread_info(); + + preempt_disable(); + save_fpu(); + preempt_enable(); + do_signal(regs, ti->syscall_saved_nr, ti->syscall_saved_r19); +} + +asmlinkage unsigned long +alpha_syscall_enter_select(struct pt_regs *regs, long syscall) +{ + struct thread_info *ti = current_thread_info(); + unsigned long saved_r0 = regs->r0; + unsigned long saved_r19 = regs->r19; + unsigned long fn = (unsigned long)sys_ni_syscall; + long nr = syscall; + bool permit; + + ti->syscall_meta = 0; + ti->syscall_saved_nr = syscall; + + if (!(ti->status & TS_SAVED_FP)) { + ti->status |= TS_SAVED_FP; + __save_fpu(); + } + + permit = syscall_enter_from_user_mode_randomize_stack(regs, &nr); + + /* + * Alpha cannot preset the full -ENOSYS return state before generic + * entry handling, because r19 is still syscall argument 4 on the + * normal dispatch path. So provide the default -ENOSYS result only + * here, after we know no real syscall will be invoked, and only if + * entry work did not already change the return state. + */ + if (unlikely(!permit || (unsigned long)nr >= NR_syscalls)) { + if (regs->r0 == saved_r0 && regs->r19 == saved_r19) + syscall_set_return_value(current, regs, -ENOSYS, 0); + + ti->syscall_meta = ALPHA_SYSCALL_META_SKIP; + return fn; /* ignored by asm when SKIP is set */ + } + + instrumentation_begin(); + { + unsigned long index; + + index = array_index_nospec((unsigned long)nr, NR_syscalls); + fn = (unsigned long)sys_call_table[index]; + } + instrumentation_end(); + + return fn; +} + +asmlinkage noinstr void +alpha_finish_syscall_to_user_mode(struct pt_regs *regs, long ret) +{ + struct thread_info *ti = current_thread_info(); + unsigned long meta = ti->syscall_meta; + + ti->syscall_meta = 0; + ti->syscall_saved_r19 = regs->r19; + + instrumentation_begin(); + + if (meta & ALPHA_SYSCALL_META_SKIP) { + /* + * Skip-dispatch path: ptrace/seccomp may already have installed + * the return state in r0/r19. Preserve it unless the syscall was + * skipped with no explicit return value. + * + * Generic PTRACE_SET_SYSCALL_INFO changes only the syscall-number + * shadow, so r1 == -1 while r0 still contains the original Alpha + * syscall number. Legacy PTRACE_POKEUSR based skipping can leave + * r0 == -1 with a3/r19 still indicating success. Both represent + * an unhandled skipped syscall and should become ENOSYS/a3=1. + */ + if (regs->r1 == (unsigned long)-1 && + (regs->r0 == ti->syscall_saved_nr || + regs->r0 == (unsigned long)-1)) { + regs->r0 = ENOSYS; + regs->r19 = 1; + } + + instrumentation_end(); + + syscall_exit_to_user_mode(regs); + return; + } + + /* + * Some successful syscalls, notably legacy ptrace PEEK requests, + * return arbitrary data in r0. That data may have the bit pattern + * of a negative errno, so do not infer failure from ret < 0 when + * arch code explicitly requested a successful Alpha return. + */ + if (meta & ALPHA_SYSCALL_META_FORCE_SUCCESS) { + regs->r0 = ret; + regs->r19 = 0; + } else if (ret < 0) { + regs->r0 = -ret; + regs->r19 = 1; + } else { + regs->r0 = ret; + regs->r19 = 0; + } + + instrumentation_end(); + syscall_exit_to_user_mode(regs); } diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index ed06367ece57..11768aa292a4 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -625,12 +625,30 @@ static void ipi_flush_tlb_mm(void *x) { struct mm_struct *mm = x; - if (mm == current->active_mm && !asn_locked()) + if (mm == current->mm && !asn_locked()) flush_tlb_current(mm); else flush_tlb_other(mm); } +/* True if a CPU other than this one holds an ASN for MM. */ +static bool +mm_context_elsewhere(struct mm_struct *mm) +{ + int cpu, this_cpu = smp_processor_id(); + + /* Pairs with the barrier the publishing CPU issues before using MM. */ + smp_mb(); + + for_each_online_cpu(cpu) { + if (cpu == this_cpu) + continue; + if (READ_ONCE(mm->context[cpu])) + return true; + } + return false; +} + void flush_tlb_mm(struct mm_struct *mm) { @@ -638,17 +656,14 @@ flush_tlb_mm(struct mm_struct *mm) if (mm == current->active_mm) { flush_tlb_current(mm); - if (atomic_read(&mm->mm_users) <= 1) { - int cpu, this_cpu = smp_processor_id(); - for (cpu = 0; cpu < NR_CPUS; cpu++) { - if (!cpu_online(cpu) || cpu == this_cpu) - continue; - if (mm->context[cpu]) - mm->context[cpu] = 0; - } + if (atomic_read(&mm->mm_users) <= 1 && + !mm_context_elsewhere(mm)) { preempt_enable(); return; } + } else { + /* smp_call_function() does not call back into this CPU. */ + flush_tlb_other(mm); } smp_call_function(ipi_flush_tlb_mm, mm, 1); @@ -669,7 +684,8 @@ ipi_flush_tlb_page(void *x) struct flush_tlb_page_struct *data = x; struct mm_struct * mm = data->mm; - if (mm == current->active_mm && !asn_locked()) + /* A targeted tbi() needs a thread of MM to be current. */ + if (mm == current->mm && !asn_locked()) flush_tlb_current_page(mm, data->vma, data->addr); else flush_tlb_other(mm); @@ -683,19 +699,17 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) preempt_disable(); - if (mm == current->active_mm) { + /* As in ipi_flush_tlb_page(): a targeted tbi() needs MM current. */ + if (mm == current->mm) { flush_tlb_current_page(mm, vma, addr); - if (atomic_read(&mm->mm_users) <= 1) { - int cpu, this_cpu = smp_processor_id(); - for (cpu = 0; cpu < NR_CPUS; cpu++) { - if (!cpu_online(cpu) || cpu == this_cpu) - continue; - if (mm->context[cpu]) - mm->context[cpu] = 0; - } + if (atomic_read(&mm->mm_users) <= 1 && + !mm_context_elsewhere(mm)) { preempt_enable(); return; } + } else { + /* smp_call_function() does not call back into this CPU. */ + flush_tlb_other(mm); } data.vma = vma; @@ -720,7 +734,7 @@ static void ipi_flush_icache_page(void *x) { struct mm_struct *mm = (struct mm_struct *) x; - if (mm == current->active_mm && !asn_locked()) + if (mm == current->mm && !asn_locked()) __load_new_mm_context(mm); else flush_tlb_other(mm); @@ -739,17 +753,14 @@ flush_icache_user_page(struct vm_area_struct *vma, struct page *page, if (mm == current->active_mm) { __load_new_mm_context(mm); - if (atomic_read(&mm->mm_users) <= 1) { - int cpu, this_cpu = smp_processor_id(); - for (cpu = 0; cpu < NR_CPUS; cpu++) { - if (!cpu_online(cpu) || cpu == this_cpu) - continue; - if (mm->context[cpu]) - mm->context[cpu] = 0; - } + if (atomic_read(&mm->mm_users) <= 1 && + !mm_context_elsewhere(mm)) { preempt_enable(); return; } + } else { + /* smp_call_function() does not call back into this CPU. */ + flush_tlb_other(mm); } smp_call_function(ipi_flush_icache_page, mm, 1); diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index 0a2d319bb1c8..f844d8141553 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -533,6 +533,7 @@ monet_init_pci(void) static void __init clipper_init_pci(void) { + tsunami_check_console_data_log(); common_init_pci(); locate_and_init_vga(NULL); } @@ -627,7 +628,7 @@ struct alpha_machine_vector clipper_mv __initmv = { DO_EV6_MMU, DO_DEFAULT_RTC, DO_TSUNAMI_IO, - .machine_check = tsunami_machine_check, + .machine_check = clipper_machine_check, .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, .min_io_address = DEFAULT_IO_BASE, .min_mem_address = DEFAULT_MEM_BASE, diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index cfaef96c4ce8..5882c65b7ef1 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -30,12 +30,6 @@ #include "proto.h" -static __always_inline void alpha_snapshot_usp(struct pt_regs *regs) -{ - if (user_mode(regs)) - regs->usp = rdusp(); -} - void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15) { @@ -186,7 +180,6 @@ do_entArith(unsigned long summary, unsigned long write_mask, { long si_code = FPE_FLTINV; - alpha_snapshot_usp(regs); if (summary & 1) { /* Software-completion summary bit is set, so try to emulate the instruction. If the processor supports @@ -208,7 +201,6 @@ do_entIF(unsigned long type, struct pt_regs *regs) { int signo, code; - alpha_snapshot_usp(regs); if (type == 3) { /* FEN fault */ /* Irritating users can call PAL_clrfen to disable the FPU for the process. The kernel will then trap in diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S index 95704e64b6a6..c44941d3b874 100644 --- a/arch/alpha/kernel/vmlinux.lds.S +++ b/arch/alpha/kernel/vmlinux.lds.S @@ -76,5 +76,16 @@ SECTIONS MODINFO ELF_DETAILS + /* + * gcc emits .eh_frame for Alpha even with + * -fno-asynchronous-unwind-tables, and the kernel has no DWARF + * unwinder to consume it. The relocation sections are likewise + * useless as the kernel image is not relocatable. + */ + /DISCARD/ : { + *(.eh_frame) + *(.rela .rela*) + } + DISCARDS } diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile index 84046e730e6d..f77d4a46f2e6 100644 --- a/arch/alpha/lib/Makefile +++ b/arch/alpha/lib/Makefile @@ -35,6 +35,8 @@ lib-y = __divqu.o __remqu.o __divlu.o __remlu.o \ callback_srm.o srm_puts.o srm_printk.o \ fls.o +obj-$(CONFIG_ARCH_SUPPORTS_INT128) += tishift.o + # The division routines are built from single source, with different defines. AFLAGS___divqu.o = -DDIV AFLAGS___remqu.o = -DREM diff --git a/arch/alpha/lib/tishift.c b/arch/alpha/lib/tishift.c new file mode 100644 index 000000000000..8319907d8150 --- /dev/null +++ b/arch/alpha/lib/tishift.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 128-bit shift helpers. + * + * gcc open-codes a variable 128-bit shift when optimizing for speed, but + * calls these libgcc routines when optimizing for size. + */ + +#include +#include +#include + +union ti { + __int128_t val; + struct { + u64 low; + u64 high; + }; +}; + +noinstr __int128_t __ashlti3(__int128_t a, int shift) +{ + union ti ti = { .val = a }; + + if (!shift) + return ti.val; + if (shift < 64) { + ti.high = (ti.high << shift) | (ti.low >> (64 - shift)); + ti.low <<= shift; + } else { + ti.high = ti.low << (shift - 64); + ti.low = 0; + } + return ti.val; +} +EXPORT_SYMBOL(__ashlti3); + +noinstr __int128_t __ashrti3(__int128_t a, int shift) +{ + union ti ti = { .val = a }; + + if (!shift) + return ti.val; + if (shift < 64) { + ti.low = (ti.low >> shift) | (ti.high << (64 - shift)); + ti.high = (s64)ti.high >> shift; + } else { + ti.low = (s64)ti.high >> (shift - 64); + ti.high = (s64)ti.high >> 63; + } + return ti.val; +} +EXPORT_SYMBOL(__ashrti3); + +noinstr __int128_t __lshrti3(__int128_t a, int shift) +{ + union ti ti = { .val = a }; + + if (!shift) + return ti.val; + if (shift < 64) { + ti.low = (ti.low >> shift) | (ti.high << (64 - shift)); + ti.high >>= shift; + } else { + ti.low = ti.high >> (shift - 64); + ti.high = 0; + } + return ti.val; +} +EXPORT_SYMBOL(__lshrti3); diff --git a/arch/alpha/mm/Makefile b/arch/alpha/mm/Makefile index 2d05664058f6..c022e55f231a 100644 --- a/arch/alpha/mm/Makefile +++ b/arch/alpha/mm/Makefile @@ -4,3 +4,5 @@ # obj-y := init.o fault.o tlbflush.o + +obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index a9816bbc9f34..63a24afa7a15 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #define __EXTERN_INLINE inline @@ -27,8 +28,32 @@ #include #include +#include + extern void die_if_kernel(char *,struct pt_regs *,long, unsigned long *); +int show_unhandled_signals = 1; + +static void +show_signal_msg(struct pt_regs *regs, unsigned long address, int signo, + const char *desc) +{ + const char *level; + + if (!show_unhandled_signals || !unhandled_signal(current, signo)) + return; + if (!printk_ratelimit()) + return; + + /* Losing init is fatal, so shout about it. */ + level = task_pid_nr(current) > 1 ? KERN_INFO : KERN_EMERG; + + printk("%s%s[%d]: %s at %016lx pc %016lx sp %016lx\n", + level, current->comm, task_pid_nr(current), desc, + address, regs->pc, rdusp()); + print_vma_addr(KERN_CONT " in ", regs->pc); + printk(KERN_CONT "\n"); +} /* * Force a new ASN for a task. @@ -45,7 +70,9 @@ __load_new_mm_context(struct mm_struct *next_mm) struct pcb_struct *pcb; mmc = __get_new_mm_context(next_mm, smp_processor_id()); - next_mm->context[smp_processor_id()] = mmc; + WRITE_ONCE(next_mm->context[smp_processor_id()], mmc); + /* Publish the context before this CPU can access the mm. */ + smp_mb(); pcb = ¤t_thread_info()->pcb; pcb->asn = mmc & HARDWARE_ASN_MASK; @@ -89,6 +116,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr, struct mm_struct *mm = current->mm; const struct exception_table_entry *fixup; int si_code = SEGV_MAPERR; + unsigned int lsb; vm_fault_t fault; unsigned int flags = FAULT_FLAG_DEFAULT; @@ -161,6 +189,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr, goto bad_area; else if (fault & VM_FAULT_SIGBUS) goto do_sigbus; + else if (fault & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE)) + goto do_sigbus_mceerr; BUG(); } @@ -217,12 +247,32 @@ do_page_fault(unsigned long address, unsigned long mmcsr, mmap_read_unlock(mm); /* Send a sigbus, regardless of whether we were in kernel or user mode. */ + if (user_mode(regs)) + show_signal_msg(regs, address, SIGBUS, "bus error"); force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *) address); if (!user_mode(regs)) goto no_context; return; + do_sigbus_mceerr: + mmap_read_unlock(mm); + if (!user_mode(regs)) + goto no_context; + /* + * Report the size of the poisoned area, which for a hugetlb fault + * is the size of the huge page that could not be mapped. + */ + lsb = PAGE_SHIFT; + if (fault & VM_FAULT_HWPOISON_LARGE) + lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); + show_signal_msg(regs, address, SIGBUS, "hardware memory error"); + force_sig_mceerr(BUS_MCEERR_AR, (void __user *) address, lsb); + return; + do_sigsegv: + show_signal_msg(regs, address, SIGSEGV, + si_code == SEGV_MAPERR ? "unmapped access" + : "access violation"); force_sig_fault(SIGSEGV, si_code, (void __user *) address); return; diff --git a/arch/alpha/mm/hugetlbpage.c b/arch/alpha/mm/hugetlbpage.c new file mode 100644 index 000000000000..ca36af4667dc --- /dev/null +++ b/arch/alpha/mm/hugetlbpage.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Alpha huge page support, built on the page table entry granularity hint + * described in asm/pgtable.h. All PTEs of a block have to agree in bits + * <15:0>, so every update below rewrites the whole block. + */ + +#include +#include +#include + +#include + +/* + * Every hint size is smaller than PMD_SIZE, so a block always fits inside a + * single last level page table and its entry count is just its page count. + */ +static inline unsigned long num_contig_ptes(unsigned long sz) +{ + return sz >> PAGE_SHIFT; +} + +/* Hint order that maps @sz, or 0 if @sz is not a hint size. */ +static unsigned int gh_order_from_size(unsigned long sz) +{ + unsigned int order; + + for_each_gh_order(order) + if (gh_cont_size(order) == sz) + return order; + + return 0; +} + +pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, + unsigned long addr, unsigned long sz) +{ + unsigned int order = gh_order_from_size(sz); + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + + if (!order) + return NULL; + + pgd = pgd_offset(mm, addr); + p4d = p4d_alloc(mm, pgd, addr); + if (!p4d) + return NULL; + + pud = pud_alloc(mm, p4d, addr); + if (!pud) + return NULL; + + pmd = pmd_alloc(mm, pud, addr); + if (!pmd) + return NULL; + + return pte_alloc_huge(mm, pmd, addr & gh_cont_mask(order)); +} + +pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr, + unsigned long sz) +{ + unsigned int order = gh_order_from_size(sz); + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + + if (!order) + return NULL; + + pgd = pgd_offset(mm, addr); + if (!pgd_present(pgdp_get(pgd))) + return NULL; + + p4d = p4d_offset(pgd, addr); + if (!p4d_present(p4dp_get(p4d))) + return NULL; + + pud = pud_offset(p4d, addr); + if (!pud_present(pudp_get(pud))) + return NULL; + + pmd = pmd_offset(pud, addr); + if (!pmd_present(pmdp_get(pmd))) + return NULL; + + return pte_offset_huge(pmd, addr & gh_cont_mask(order)); +} + +/* + * A block is only as large as its own hint, so the walk can skip to the end of + * the containing last level page table but no further. + */ +unsigned long hugetlb_mask_last_page(struct hstate *h) +{ + return PMD_SIZE - huge_page_size(h); +} + +pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + pte_t orig_pte = ptep_get(ptep); + unsigned long i, ncontig; + + /* Swap, migration and marker entries carry no hint. */ + if (!pte_huge(orig_pte)) + return orig_pte; + + ncontig = gh_pte_num(pte_gh_order(orig_pte)); + + for (i = 0; i < ncontig; i++, ptep++) { + pte_t pte = ptep_get(ptep); + + if (pte_dirty(pte)) + orig_pte = pte_mkdirty(orig_pte); + if (pte_young(pte)) + orig_pte = pte_mkyoung(orig_pte); + } + + return orig_pte; +} + +static pte_t get_clear_contig(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long ncontig) +{ + pte_t pte = ptep_get_and_clear(mm, addr, ptep); + bool present = pte_present(pte); + + while (--ncontig) { + pte_t tmp_pte; + + ptep++; + addr += PAGE_SIZE; + tmp_pte = ptep_get_and_clear(mm, addr, ptep); + if (present) { + if (pte_dirty(tmp_pte)) + pte = pte_mkdirty(pte); + if (pte_young(tmp_pte)) + pte = pte_mkyoung(pte); + } + } + + return pte; +} + +static pte_t get_clear_contig_flush(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long ncontig) +{ + pte_t orig_pte = get_clear_contig(mm, addr, ptep, ncontig); + struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); + + if (!pte_none(orig_pte)) + flush_tlb_range(&vma, addr, addr + ncontig * PAGE_SIZE); + + return orig_pte; +} + +pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags) +{ + unsigned int order = gh_order_from_size(1UL << shift); + + /* + * Not every caller asks for a size Alpha can hint; mm/debug_vm_pgtable.c + * probes with PMD_SHIFT. Leave the entry alone rather than complaining. + */ + if (order) + entry = pte_mkgh(entry, order); + + return entry; +} + +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, + pte_t pte, unsigned long sz) +{ + unsigned long i, ncontig = num_contig_ptes(sz); + + /* The hint requires the block to be naturally aligned in the VA too. */ + VM_WARN_ON_ONCE(addr & (sz - 1)); + + if (!pte_present(pte)) { + /* + * Swap, migration and marker entries hold no frame number, so + * every slot gets the same value. + */ + for (i = 0; i < ncontig; i++, ptep++, addr += PAGE_SIZE) + set_ptes(mm, addr, ptep, pte, 1); + return; + } + + /* + * All PTEs of a block must agree in bits <15:0> (ARM Table 22-3, note + * 2), which rewriting a valid block in place would violate part way + * through. Invalidate it everywhere first, following ARM 11.6.1: the + * invalidate has to assume the hint is zero, so it must cover every + * page of the block. + */ + if (pte_present(ptep_get(ptep))) + get_clear_contig_flush(mm, addr, ptep, ncontig); + + /* set_ptes() advances the frame number for each entry. */ + set_ptes(mm, addr, ptep, pte, ncontig); +} + +pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long sz) +{ + return get_clear_contig(mm, addr, ptep, num_contig_ptes(sz)); +} + +/* + * The block size comes from the hstate rather than from the entry, because + * this is also reached for non-present entries, which carry no hint. + */ +pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep) +{ + unsigned long sz = huge_page_size(hstate_vma(vma)); + + return get_clear_contig_flush(vma->vm_mm, addr, ptep, + num_contig_ptes(sz)); +} + +pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep) +{ + return huge_ptep_clear_flush(vma, addr, ptep); +} + +/* True if any entry of the block differs from @pte outside the frame number. */ +static bool gh_access_flags_changed(pte_t *ptep, pte_t pte, + unsigned long ncontig) +{ + unsigned long i; + + for (i = 0; i < ncontig; i++) + if ((pte_val(ptep_get(ptep + i)) ^ pte_val(pte)) & ~_PFN_MASK) + return true; + + return false; +} + +int huge_ptep_set_access_flags(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep, pte_t pte, int dirty) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long sz = huge_page_size(hstate_vma(vma)); + unsigned long ncontig = num_contig_ptes(sz); + pte_t orig_pte = huge_ptep_get(mm, addr, ptep); + + /* Keep the young and dirty state the block already has. */ + if (pte_dirty(orig_pte)) + pte = pte_mkdirty(pte); + if (pte_young(orig_pte)) + pte = pte_mkyoung(pte); + + /* Breaking an unchanged block only makes concurrent touchers refault. */ + if (!gh_access_flags_changed(ptep, pte, ncontig)) + return 0; + + get_clear_contig_flush(mm, addr, ptep, ncontig); + set_ptes(mm, addr, ptep, pte, ncontig); + + return 1; +} + +void huge_ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, + pte_t *ptep) +{ + pte_t pte = ptep_get(ptep); + unsigned long ncontig = gh_pte_num(pte_gh_order(pte)); + + VM_WARN_ON_ONCE(!pte_huge(pte)); + + /* + * pte_wrprotect() sets _PAGE_FOW, which lives in the bits the whole + * block must agree on, so this takes the break before make path. + */ + pte = pte_wrprotect(get_clear_contig_flush(mm, addr, ptep, ncontig)); + set_ptes(mm, addr, ptep, pte, ncontig); +} + +void huge_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep, + unsigned long sz) +{ + unsigned long i, ncontig = num_contig_ptes(sz); + + for (i = 0; i < ncontig; i++, addr += PAGE_SIZE, ptep++) + pte_clear(mm, addr, ptep); +} + +bool __init arch_hugetlb_valid_size(unsigned long size) +{ + return gh_order_from_size(size) != 0; +} + +static __init int alpha_hugetlbpage_init(void) +{ + unsigned int order; + + for_each_gh_order(order) + hugetlb_add_hstate(gh_cont_shift(order) - PAGE_SHIFT); + + return 0; +} +arch_initcall(alpha_hugetlbpage_init); diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index f4d65a60c869..6c28947d6e2d 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -226,6 +226,11 @@ void __init paging_init(void) memset(absolute_pointer(ZERO_PGE), 0, PAGE_SIZE); } +void __init arch_mm_preinit(void) +{ + early_memtest(memblock_start_of_DRAM(), PFN_PHYS(max_low_pfn)); +} + #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM) void srm_paging_stop (void) diff --git a/arch/alpha/mm/tlbflush.c b/arch/alpha/mm/tlbflush.c index ccbc317b9a34..a2aef90e6234 100644 --- a/arch/alpha/mm/tlbflush.c +++ b/arch/alpha/mm/tlbflush.c @@ -66,7 +66,7 @@ static void ipi_flush_mm_and_page(void *x) struct tlb_mm_and_addr *d = x; /* Part 1: mm context side (Alpha uses ASN/context as a key mechanism). */ - if (d->mm == current->active_mm && !asn_locked()) + if (d->mm == current->mm && !asn_locked()) __load_new_mm_context(d->mm); else flush_tlb_other(d->mm); @@ -90,22 +90,6 @@ void migrate_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) */ preempt_disable(); on_each_cpu(ipi_flush_mm_and_page, &d, 1); - - /* - * mimic flush_tlb_mm()'s mm_users<=1 optimization. - */ - if (atomic_read(&mm->mm_users) <= 1) { - - int cpu, this_cpu; - this_cpu = smp_processor_id(); - - for (cpu = 0; cpu < NR_CPUS; cpu++) { - if (!cpu_online(cpu) || cpu == this_cpu) - continue; - if (READ_ONCE(mm->context[cpu])) - WRITE_ONCE(mm->context[cpu], 0); - } - } preempt_enable(); } diff --git a/scripts/xz_wrap.sh b/scripts/xz_wrap.sh index f19369687030..5af0d9045cbc 100755 --- a/scripts/xz_wrap.sh +++ b/scripts/xz_wrap.sh @@ -10,6 +10,7 @@ # XZ-compressed kernel isn't currently supported on every listed arch. # # Arch Align Notes +# alpha 4 # arm 2/4 ARM and ARM-Thumb2 # arm64 4 # csky 2 @@ -44,6 +45,10 @@ BCJ= # Set the BCJ filter if one is available. # It must match the #ifdef usage in lib/decompress_unxz.c. case $SRCARCH in + alpha) + ALIGN=4 + ;; + arm) if is_enabled CONFIG_THUMB2_KERNEL; then ALIGN=2 diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 0622bc2acad4..79e8eb374cb2 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -136,6 +136,8 @@ struct seccomp_data { # define __NR_seccomp 354 # elif defined(__x86_64__) # define __NR_seccomp 317 +# elif defined(__alpha__) +# define __NR_seccomp 514 # elif defined(__arm__) # define __NR_seccomp 383 # elif defined(__aarch64__) @@ -1754,6 +1756,30 @@ TEST_F(TRACE_poke, getpid_runs_normally) # define ARCH_REGS struct user_regs_struct # define SYSCALL_NUM(_regs) (_regs).orig_eax # define SYSCALL_RET(_regs) (_regs).eax +#elif defined(__alpha__) +/* NT_PRSTATUS contains r0-r30, pc and unique, not struct pt_regs. */ +struct alpha_regs { + unsigned long regs[33]; +}; + +# define ARCH_REGS struct alpha_regs +/* The kernel keeps the mutable syscall number in r1. */ +# define SYSCALL_NUM(_regs) ((_regs).regs[1]) +# define SYSCALL_RET(_regs) ((_regs).regs[0]) +/* + * Alpha returns positive errno in r0 with the r19/a3 error flag set. + * At a syscall-entry stop, r19 is still the fourth syscall argument. + * Callers must also set the syscall number to -1 to skip the syscall + * before replacing r19 with the error flag. + */ +# define SYSCALL_RET_SET(_regs, _val) \ + do { \ + struct alpha_regs *__regs = &(_regs); \ + long __ret = (_val); \ + \ + __regs->regs[0] = __ret < 0 ? -__ret : __ret; \ + __regs->regs[19] = __ret < 0; \ + } while (0) #elif defined(__arm__) # define ARCH_REGS struct pt_regs # define SYSCALL_NUM(_regs) (_regs).ARM_r7