alpha updates for v7.4

- alpha: add bounded Clipper console-log scanning
 - alpha: decode Clipper environmental events
 - alpha: decode and acknowledge Tsunami system machine checks
 - alpha: bound EV6 logout decoding by the processor area
 - alpha: describe the Tsunami system machine check frame
 - alpha: move Tsunami machine check handling to err_tsunami.c
 - alpha: fill in pc for software event samples
 - alpha: add HAVE_PERF_REGS and HAVE_PERF_USER_STACK_DUMP support
 - alpha: use user_stack_pointer() in KSTK_ESP() and dump_elf_thread()
 - alpha: stop storing the user stack pointer in pt_regs
 - alpha: fix stale user_stack_pointer()
 - selftests/seccomp: add Alpha support to seccomp_bpf
 - alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms
 - alpha: do not clear remote MMU contexts in migrate_flush_tlb_page()
 - alpha: invalidate the local context in flush_icache_user_page()
 - alpha: invalidate the local context in flush_tlb_mm()
 - alpha: fix the local TLB invalidate in the UP flush_tlb_page()
 - alpha: fix the local TLB invalidate in flush_tlb_page()
 - alpha: invalidate the local context in flush_tlb_page()
 - alpha: only use a targeted tbi() when the target mm is really current
 - alpha: run check_mmu_context() from finish_arch_post_lock_switch()
 - alpha: load the MMU context when switch_mm() switches the current task
 - alpha: implement hugetlb support
 - alpha: align hugetlb mappings in arch_get_unmapped_area()
 - alpha: define granularity hint PTE bits
 - alpha: describe the PTE read and write enable bits
 - alpha: handle VM_FAULT_HWPOISON in do_page_fault()
 - alpha: add missing page_table_check_pte_clear() to ptep_get_and_clear()
 - alpha: default NR_CPUS to 32 for Wildfire
 - alpha: link platform help text to the alphalinux.org wiki
 - alpha: correct system descriptions in Kconfig help text
 - alpha: replace dead links in Kconfig help text
 - alpha: support the remaining kernel compression formats
 - alpha: add self-extracting compressed kernel boot stub
 - xz: add arch-specific tuning for alpha
 - alpha: select SPARSE_IRQ
 - alpha: select ARCH_WANT_IRQS_OFF_ACTIVATE_MM
 - alpha: select ARCH_WANT_LD_ORPHAN_WARN
 - alpha: discard .eh_frame and the relocation sections
 - alpha: select SYSCTL_EXCEPTION_TRACE
 - alpha: select ARCH_USE_MEMTEST
 - alpha: select HAVE_GUP_FAST
 - alpha: select ARCH_HAS_DEBUG_VM_PGTABLE
 - alpha: add ARCH_SUPPORTS_PAGE_TABLE_CHECK support
 - alpha: add ARCH_HAS_PTE_SPECIAL support
 - alpha: add HAVE_ARCH_THREAD_STRUCT_WHITELIST support
 - alpha: select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
 - alpha: select HAVE_ARCH_COMPILER_H
 - alpha: select ARCH_SUPPORTS_INT128
 - alpha: select ARCH_HAS_FAST_MULTIPLIER
 - alpha: select ARCH_SUPPORTS_ATOMIC_RMW
 - alpha: select EDAC_SUPPORT
 - alpha: select HAVE_DEBUG_KMEMLEAK
 - alpha: select ARCH_HAS_GCOV_PROFILE_ALL
 - alpha: select ARCH_HAS_UBSAN
 - alpha: enter hardirq context before looking up the irq descriptor
 - alpha: enable GENERIC_ENTRY
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQTIw07U9ivnHy+tugnaDr6nZQzE/AUCatZIQgAKCRDaDr6nZQzE
 /AqTAP9RHaiXEn9o6NkcvCfiCLin7UUQmDb0GVBMQCO6elERWQEAoTYaCYWB0cKj
 wsF8DtzdXBKHZt7MKy56uiuESF7zlwk=
 =1yco
 -----END PGP SIGNATURE-----

Merge tag 'alpha-for-v7.4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/lindholm/alpha

Pull alpha updates from Magnus Lindholm:
 "This completes Alpha's conversion to GENERIC_ENTRY and enables a range
  of kernel features through Kconfig updates, including UBSAN, gcov,
  kmemleak, page-table checking, fast GUP and sparse IRQs.

  It includes TLB and MMU context fixes prompted by glibc test-suite
  failures, HugeTLB support for 64KB, 512KB and 4MB pages, and fixes for
  poisoned-page faults and page-table checking.

  Machine-check handling is improved on Tsunami platforms, with bounded
  EV6 logout decoding, Clipper environmental event reporting and safer
  firmware console-log scanning.

  Other changes add self-extracting compressed kernels with support for
  all kernel compression formats, Alpha-specific XZ tuning, perf
  register and user-stack sampling, and Alpha support in the seccomp
  selftests. The series also fixes user stack-pointer reporting and IRQ
  entry ordering, adds 128-bit shift helpers, cleans up linker sections,
  and updates Kconfig help"

* tag 'alpha-for-v7.4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/lindholm/alpha: (57 commits)
  alpha: add bounded Clipper console-log scanning
  alpha: decode Clipper environmental events
  alpha: decode and acknowledge Tsunami system machine checks
  alpha: bound EV6 logout decoding by the processor area
  alpha: describe the Tsunami system machine check frame
  alpha: move Tsunami machine check handling to err_tsunami.c
  alpha: fill in pc for software event samples
  alpha: add HAVE_PERF_REGS and HAVE_PERF_USER_STACK_DUMP support
  alpha: use user_stack_pointer() in KSTK_ESP() and dump_elf_thread()
  alpha: stop storing the user stack pointer in pt_regs
  alpha: fix stale user_stack_pointer()
  selftests/seccomp: add Alpha support to seccomp_bpf
  alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms
  alpha: do not clear remote MMU contexts in migrate_flush_tlb_page()
  alpha: invalidate the local context in flush_icache_user_page()
  alpha: invalidate the local context in flush_tlb_mm()
  alpha: fix the local TLB invalidate in the UP flush_tlb_page()
  alpha: fix the local TLB invalidate in flush_tlb_page()
  alpha: invalidate the local context in flush_tlb_page()
  alpha: only use a targeted tbi() when the target mm is really current
  ...
This commit is contained in:
Linus Torvalds 2026-10-20 14:31:24 -10:00
commit 742f62b639
65 changed files with 2617 additions and 567 deletions

View File

@ -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: <integer>
default : 0 <disable>
Specifies the number of memtest passes to be

View File

@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | ok |
| arm: | TODO |
| arm64: | ok |

View File

@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | TODO |
| arm: | ok |
| arm64: | ok |

View File

@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | ok |
| arm: | ok |
| arm64: | ok |

View File

@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | TODO |
| arm: | ok |
| arm64: | ok |

View File

@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | TODO |
| arm: | ok |
| arm64: | ok |

View File

@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | ok |
| arm: | ok |
| arm64: | ok |

View File

@ -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
<http://www.alphalinux.org/>. In summary:
To find out what type of Alpha system you have, see the system and
motherboard lists at
<https://alphalinux.org/wiki/hardware:systems> and
<https://alphalinux.org/wiki/hardware:motherboards>. 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 <https://alphalinux.org/wiki/hardware:systems:alcor>.
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:
<https://alphalinux.org/wiki/hardware:systems:webbrick>
<https://alphalinux.org/wiki/hardware:systems:goldrush>
<https://alphalinux.org/wiki/hardware:systems:clipper>
<https://alphalinux.org/wiki/hardware:systems:monet>
<https://alphalinux.org/wiki/hardware:motherboards:pc264dp>
<https://alphalinux.org/wiki/hardware:motherboards:up2000>
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 <https://alphalinux.org/wiki/hardware:motherboards:eiger>.
config ALPHA_LX164
bool "LX164"
help
A technical overview of this board is available at
<http://www.unix-ag.org/Linux-Alpha/Architectures/LX164.html>.
AlphaPC164-LX, a 21164A-based single-board computer using the
Pyxis chipset.
See <https://alphalinux.org/wiki/hardware:motherboards:pc164lx>.
config ALPHA_MARVEL
bool "Marvel"
help
AlphaServer ES47 / ES80 / GS1280 based on EV7.
See <https://alphalinux.org/wiki/hardware:systems:marvel>.
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 <https://alphalinux.org/wiki/hardware:systems:miata>.
config ALPHA_MIKASA
bool "Mikasa"
help
AlphaServer 1000-based Alpha systems.
See <https://alphalinux.org/wiki/hardware:systems:mikasa>.
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:
<https://alphalinux.org/wiki/hardware:motherboards:up1000>
<https://alphalinux.org/wiki/hardware:motherboards:up1100>
<https://alphalinux.org/wiki/hardware:motherboards:up1500>
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 <https://alphalinux.org/wiki/hardware:systems:noritake>.
config ALPHA_PC164
bool "PC164"
help
AlphaPC164, a 21164A-based single-board computer using the CIA
chipset.
See <https://alphalinux.org/wiki/hardware:motherboards:pc164>.
config ALPHA_RAWHIDE
bool "Rawhide"
select HAVE_EISA
help
AlphaServer 1200, AlphaServer 4000 and AlphaServer 4100 machines.
See HOWTO at
<http://www.alphalinux.org/docs/rawhide/4100_install.shtml>.
AlphaServer 1200, 4000 and 4100 and Digital Server 5300 and 7300
machines.
See <https://alphalinux.org/wiki/hardware:systems:rawhide>.
config ALPHA_RUFFIAN
bool "Ruffian"
help
Samsung APC164UX. There is a page on known problems and workarounds
at <http://www.alphalinux.org/faq/FAQ-11.html>.
DeskStation RPX164-2, AlphaPC164-UX and AlphaPC164-BX boards.
They have no SRM console and can only boot Linux through MILO.
See <https://alphalinux.org/wiki/hardware:motherboards:pc164ux>.
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 <https://alphalinux.org/wiki/hardware:motherboards:pc164rx>.
config ALPHA_SX164
bool "SX164"
help
AlphaPC164-SX, a 21164PC-based single-board computer using the
Pyxis chipset.
See <https://alphalinux.org/wiki/hardware:motherboards:pc164sx>.
config ALPHA_SABLE
bool "Sable"
@ -217,23 +304,38 @@ config ALPHA_SABLE
help
Digital AlphaServer 2000 and 2100-based systems.
See <https://alphalinux.org/wiki/hardware:systems:sable>.
config ALPHA_SHARK
bool "Shark"
help
AlphaServer DS20L and API NetWorks CS20 systems based on the
Tsunami chipset.
See <https://alphalinux.org/wiki/hardware:systems:shark>.
config ALPHA_TAKARA
bool "Takara"
help
Alpha 11164-based OEM single-board computer.
Alpha 21164-based OEM single-board computer.
See <https://alphalinux.org/wiki/hardware:motherboards:takara>.
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 <https://alphalinux.org/wiki/hardware:systems:titan>.
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 <https://alphalinux.org/wiki/hardware:systems:wildfire>.
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
<http://www.alphalinux.org/>.
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.

View File

@ -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)'

2
arch/alpha/boot/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
vmlinuz

View File

@ -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

3
arch/alpha/boot/compressed/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
vmlinux
vmlinux.lds

View File

@ -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)

View File

@ -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 <linux/init.h>
#include <asm/pal.h>
.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

View File

@ -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 <linux/elf.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/types.h>
#include <asm/hwrpb.h>
#include <asm/page.h>
#include <asm/pal.h>
#include <asm/setup.h>
#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);
}

View File

@ -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:

View File

@ -0,0 +1,83 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <asm-generic/vmlinux.lds.h>
#include <asm/page.h>
#include <asm/setup.h>
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.*)
}
}

View File

@ -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 = .;
}
}

View File

@ -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

View File

@ -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;
};

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ARCH_ALPHA_ENTRY_COMMON_H
#define ARCH_ALPHA_ENTRY_COMMON_H
#include <asm/stacktrace.h> /* For on_thread_stack() */
#include <asm/syscall.h>
#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

View File

@ -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 */

View File

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ALPHA_HUGETLB_H
#define _ALPHA_HUGETLB_H
#include <asm/page.h>
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 <asm-generic/hugetlb.h>
#endif /* _ALPHA_HUGETLB_H */

View File

@ -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)
{

View File

@ -6,6 +6,19 @@
#include <asm/pal.h>
#include <vdso/page.h>
#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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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()

View File

@ -3,7 +3,7 @@
#define _ASMAXP_PTRACE_H
#include <uapi/asm/ptrace.h>
#include <asm/irqflags.h>
#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

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_ALPHA_STACKTRACE_H
#define _ASM_ALPHA_STACKTRACE_H
#include <linux/compiler_attributes.h>
#include <linux/types.h>
#include <asm/current.h>
#include <asm/processor.h>
#include <asm/thread_info.h>
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 */

View File

@ -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 */

View File

@ -8,6 +8,8 @@
#include <linux/types.h>
#include <asm/ptrace.h>
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 */

View File

@ -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<<TIF_SECCOMP)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
/*
* Work to do on syscall entry (in entry.S).
* If you want this to exactly mirror what entry.S checks, keep it aligned
* with the mask used before branching to syscall_trace_enter().
*/
#ifdef CONFIG_AUDITSYSCALL
# define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP \
| _TIF_SYSCALL_TRACEPOINT)
#else
# define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
#endif
/* Work to do on interrupt/exception return. */
#define _TIF_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
_TIF_NOTIFY_RESUME | _TIF_NOTIFY_SIGNAL)
#define _TIF_UPROBE (1 << TIF_UPROBE)
#define TS_UAC_NOPRINT 0x0001 /* ! Preserve the following three */
#define TS_UAC_NOFIX 0x0002 /* ! flags as they match */

View File

@ -87,7 +87,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{
struct mm_struct *mm = vma->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);

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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));
}

View File

@ -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()));
}

View File

@ -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

View File

@ -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)
{

View File

@ -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",

View File

@ -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);

View File

@ -0,0 +1,688 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Machine check handling for Tsunami/Typhoon systems.
* Author: Magnus Lindholm <linmag7@gmail.com>
* Based on Titan error handling by Jeff Wiedemeier (Compaq).
* Register definitions: 21272 Hardware Reference Manual, revision 4.0.
*/
#include <linux/init.h>
#include <linux/printk.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/core_tsunami.h>
#include <asm/smp.h>
#include <asm/err_common.h>
#include <asm/irq_regs.h>
#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(&timestamp);
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);
}
}

View File

@ -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();
}

View File

@ -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)
{

View File

@ -38,6 +38,7 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/uio.h>
#include <linux/hugetlb.h>
#include <linux/vfs.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
@ -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;
}

View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bits.h>
#include <linux/perf_event.h>
#include <linux/sched/task_stack.h>
#include <asm/perf_regs.h>
#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);
}

View File

@ -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

View File

@ -2,6 +2,7 @@
#include <linux/interrupt.h>
#include <linux/screen_info.h>
#include <linux/io.h>
#include <asm/ptrace.h>
/* 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);

View File

@ -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,

View File

@ -27,10 +27,9 @@
#include <linux/uaccess.h>
#include <asm/sigcontext.h>
#include <asm/ucontext.h>
#include <linux/entry-common.h>
#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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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
}

View File

@ -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

70
arch/alpha/lib/tishift.c Normal file
View File

@ -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 <linux/export.h>
#include <linux/types.h>
#include <asm/asm-prototypes.h>
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);

View File

@ -4,3 +4,5 @@
#
obj-y := init.o fault.o tlbflush.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o

View File

@ -8,6 +8,7 @@
#include <linux/sched/signal.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <asm/io.h>
#define __EXTERN_INLINE inline
@ -27,8 +28,32 @@
#include <linux/uaccess.h>
#include <linux/perf_event.h>
#include <asm/pal.h>
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 = &current_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;

308
arch/alpha/mm/hugetlbpage.c Normal file
View File

@ -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 <linux/hugetlb.h>
#include <linux/mm.h>
#include <linux/pgtable.h>
#include <asm/tlbflush.h>
/*
* 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);

View File

@ -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)

View File

@ -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();
}

View File

@ -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

View File

@ -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