mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
742f62b639
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | ok |
|
||||
| arm: | TODO |
|
||||
| arm64: | ok |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | TODO |
|
||||
| arm: | ok |
|
||||
| arm64: | ok |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | ok |
|
||||
| arm: | ok |
|
||||
| arm64: | ok |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | TODO |
|
||||
| arm: | ok |
|
||||
| arm64: | ok |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | TODO |
|
||||
| arm: | ok |
|
||||
| arm64: | ok |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | ok |
|
||||
| arm: | ok |
|
||||
| arm64: | ok |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
2
arch/alpha/boot/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
vmlinuz
|
||||
|
|
@ -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
3
arch/alpha/boot/compressed/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
vmlinux
|
||||
vmlinux.lds
|
||||
89
arch/alpha/boot/compressed/Makefile
Normal file
89
arch/alpha/boot/compressed/Makefile
Normal 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)
|
||||
36
arch/alpha/boot/compressed/head.S
Normal file
36
arch/alpha/boot/compressed/head.S
Normal 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
|
||||
209
arch/alpha/boot/compressed/misc.c
Normal file
209
arch/alpha/boot/compressed/misc.c
Normal 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);
|
||||
}
|
||||
46
arch/alpha/boot/compressed/trampoline.S
Normal file
46
arch/alpha/boot/compressed/trampoline.S
Normal 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:
|
||||
83
arch/alpha/boot/compressed/vmlinux.lds.S
Normal file
83
arch/alpha/boot/compressed/vmlinux.lds.S
Normal 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.*)
|
||||
}
|
||||
}
|
||||
10
arch/alpha/boot/compressed/vmlinux.scr
Normal file
10
arch/alpha/boot/compressed/vmlinux.scr
Normal 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 = .;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
14
arch/alpha/include/asm/entry-common.h
Normal file
14
arch/alpha/include/asm/entry-common.h
Normal 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
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
43
arch/alpha/include/asm/hugetlb.h
Normal file
43
arch/alpha/include/asm/hugetlb.h
Normal 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 */
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
20
arch/alpha/include/asm/stacktrace.h
Normal file
20
arch/alpha/include/asm/stacktrace.h
Normal 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 */
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
45
arch/alpha/include/uapi/asm/perf_regs.h
Normal file
45
arch/alpha/include/uapi/asm/perf_regs.h
Normal 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 */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
688
arch/alpha/kernel/err_tsunami.c
Normal file
688
arch/alpha/kernel/err_tsunami.c
Normal 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(×tamp);
|
||||
if (packet->type == EL_TYPE__HEADER__HALT_FRAME)
|
||||
printk("%s HALT CODE: %04x\n", err_print_prefix,
|
||||
packet->by_type.err_halt.halt_code);
|
||||
pos += size;
|
||||
remaining -= size + length;
|
||||
while (count--) {
|
||||
if (length < offsetof(struct el_subpacket, by_type))
|
||||
return false;
|
||||
packet = (void *)pos;
|
||||
size = packet->length;
|
||||
if (size < offsetof(struct el_subpacket, by_type) ||
|
||||
size > length || (size & 7) ||
|
||||
packet->class != EL_CLASS__REGATTA_FAMILY)
|
||||
return false;
|
||||
/* This handler checks embedded logout-frame bounds. */
|
||||
if (tsunami_process_subpacket(packet) != (void *)pos + size)
|
||||
return false;
|
||||
pos += size;
|
||||
length -= size;
|
||||
}
|
||||
if (length)
|
||||
return false;
|
||||
}
|
||||
/* Exhaustion without a complete termination packet is incomplete. */
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __init
|
||||
tsunami_console_data_log(int cpu, struct percpu_struct *pcpu)
|
||||
{
|
||||
unsigned char *data = (void *)(IDENT_ADDR | pcpu->console_data_log_pa);
|
||||
size_t length = pcpu->console_data_log_length;
|
||||
size_t dump = length < 1024 ? length : 1024;
|
||||
|
||||
printk("%sConsole data log for CPU %d (%zu bytes):\n",
|
||||
err_print_prefix, cpu, length);
|
||||
if (!(pcpu->console_data_log_pa & 7) &&
|
||||
length <= ~0UL - (unsigned long)data &&
|
||||
tsunami_console_log(data, length)) {
|
||||
/* Acknowledge only after reaching a valid termination packet. */
|
||||
pcpu->console_data_log_pa = 0;
|
||||
return;
|
||||
}
|
||||
printk("%s Incomplete console log; retaining firmware pointer\n",
|
||||
err_print_prefix);
|
||||
/* Byte reads preserve partial headers without rounding beyond length. */
|
||||
if (dump > ~0UL - (unsigned long)data)
|
||||
return;
|
||||
print_hex_dump(err_print_prefix, " ", DUMP_PREFIX_OFFSET, 16, 1,
|
||||
data, dump, false);
|
||||
if (dump < length)
|
||||
printk("%s Raw console log truncated to %zu bytes\n",
|
||||
err_print_prefix, dump);
|
||||
}
|
||||
|
||||
void __init
|
||||
tsunami_check_console_data_log(void)
|
||||
{
|
||||
struct percpu_struct *pcpu;
|
||||
unsigned long cpu;
|
||||
|
||||
for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) {
|
||||
pcpu = (void *)hwrpb + hwrpb->processor_offset +
|
||||
cpu * hwrpb->processor_size;
|
||||
if (pcpu->console_data_log_pa)
|
||||
tsunami_console_data_log(cpu, pcpu);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
64
arch/alpha/kernel/perf_regs.c
Normal file
64
arch/alpha/kernel/perf_regs.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
70
arch/alpha/lib/tishift.c
Normal 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);
|
||||
|
|
@ -4,3 +4,5 @@
|
|||
#
|
||||
|
||||
obj-y := init.o fault.o tlbflush.o
|
||||
|
||||
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
|
||||
|
|
|
|||
|
|
@ -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 = ¤t_thread_info()->pcb;
|
||||
pcb->asn = mmc & HARDWARE_ASN_MASK;
|
||||
|
|
@ -89,6 +116,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
|
|||
struct mm_struct *mm = current->mm;
|
||||
const struct exception_table_entry *fixup;
|
||||
int si_code = SEGV_MAPERR;
|
||||
unsigned int lsb;
|
||||
vm_fault_t fault;
|
||||
unsigned int flags = FAULT_FLAG_DEFAULT;
|
||||
|
||||
|
|
@ -161,6 +189,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
|
|||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
else if (fault & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE))
|
||||
goto do_sigbus_mceerr;
|
||||
BUG();
|
||||
}
|
||||
|
||||
|
|
@ -217,12 +247,32 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
|
|||
mmap_read_unlock(mm);
|
||||
/* Send a sigbus, regardless of whether we were in kernel
|
||||
or user mode. */
|
||||
if (user_mode(regs))
|
||||
show_signal_msg(regs, address, SIGBUS, "bus error");
|
||||
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *) address);
|
||||
if (!user_mode(regs))
|
||||
goto no_context;
|
||||
return;
|
||||
|
||||
do_sigbus_mceerr:
|
||||
mmap_read_unlock(mm);
|
||||
if (!user_mode(regs))
|
||||
goto no_context;
|
||||
/*
|
||||
* Report the size of the poisoned area, which for a hugetlb fault
|
||||
* is the size of the huge page that could not be mapped.
|
||||
*/
|
||||
lsb = PAGE_SHIFT;
|
||||
if (fault & VM_FAULT_HWPOISON_LARGE)
|
||||
lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault));
|
||||
show_signal_msg(regs, address, SIGBUS, "hardware memory error");
|
||||
force_sig_mceerr(BUS_MCEERR_AR, (void __user *) address, lsb);
|
||||
return;
|
||||
|
||||
do_sigsegv:
|
||||
show_signal_msg(regs, address, SIGSEGV,
|
||||
si_code == SEGV_MAPERR ? "unmapped access"
|
||||
: "access violation");
|
||||
force_sig_fault(SIGSEGV, si_code, (void __user *) address);
|
||||
return;
|
||||
|
||||
|
|
|
|||
308
arch/alpha/mm/hugetlbpage.c
Normal file
308
arch/alpha/mm/hugetlbpage.c
Normal 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);
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user