mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
Merge branches 'for-next/sysreg', 'for-next/sme', 'for-next/kselftest', 'for-next/misc', 'for-next/sme2', 'for-next/tpidr2', 'for-next/scs', 'for-next/compat-hwcap', 'for-next/ftrace', 'for-next/efi-boot-mmu-on', 'for-next/ptrauth' and 'for-next/pseudo-nmi', remote-tracking branch 'arm64/for-next/perf' into for-next/core
* arm64/for-next/perf: perf: arm_spe: Print the version of SPE detected perf: arm_spe: Add support for SPEv1.2 inverted event filtering perf: Add perf_event_attr::config3 drivers/perf: fsl_imx8_ddr_perf: Remove set-but-not-used variable perf: arm_spe: Support new SPEv1.2/v8.7 'not taken' event perf: arm_spe: Use new PMSIDR_EL1 register enums perf: arm_spe: Drop BIT() and use FIELD_GET/PREP accessors arm64/sysreg: Convert SPE registers to automatic generation arm64: Drop SYS_ from SPE register defines perf: arm_spe: Use feature numbering for PMSEVFR_EL1 defines perf/marvell: Add ACPI support to TAD uncore driver perf/marvell: Add ACPI support to DDR uncore driver perf/arm-cmn: Reset DTM_PMU_CONFIG at probe drivers/perf: hisi: Extract initialization of "cpa_pmu->pmu" drivers/perf: hisi: Simplify the parameters of hisi_pmu_init() drivers/perf: hisi: Advertise the PERF_PMU_CAP_NO_EXCLUDE capability * for-next/sysreg: : arm64 sysreg and cpufeature fixes/updates KVM: arm64: Use symbolic definition for ISR_EL1.A arm64/sysreg: Add definition of ISR_EL1 arm64/sysreg: Add definition for ICC_NMIAR1_EL1 arm64/cpufeature: Remove 4 bit assumption in ARM64_FEATURE_MASK() arm64/sysreg: Fix errors in 32 bit enumeration values arm64/cpufeature: Fix field sign for DIT hwcap detection * for-next/sme: : SME-related updates arm64/sme: Optimise SME exit on syscall entry arm64/sme: Don't use streaming mode to probe the maximum SME VL arm64/ptrace: Use system_supports_tpidr2() to check for TPIDR2 support * for-next/kselftest: (23 commits) : arm64 kselftest fixes and improvements kselftest/arm64: Don't require FA64 for streaming SVE+ZA tests kselftest/arm64: Copy whole EXTRA context kselftest/arm64: Fix enumeration of systems without 128 bit SME for SSVE+ZA kselftest/arm64: Fix enumeration of systems without 128 bit SME kselftest/arm64: Don't require FA64 for streaming SVE tests kselftest/arm64: Limit the maximum VL we try to set via ptrace kselftest/arm64: Correct buffer size for SME ZA storage kselftest/arm64: Remove the local NUM_VL definition kselftest/arm64: Verify simultaneous SSVE and ZA context generation kselftest/arm64: Verify that SSVE signal context has SVE_SIG_FLAG_SM set kselftest/arm64: Remove spurious comment from MTE test Makefile kselftest/arm64: Support build of MTE tests with clang kselftest/arm64: Initialise current at build time in signal tests kselftest/arm64: Don't pass headers to the compiler as source kselftest/arm64: Remove redundant _start labels from FP tests kselftest/arm64: Fix .pushsection for strings in FP tests kselftest/arm64: Run BTI selftests on systems without BTI kselftest/arm64: Fix test numbering when skipping tests kselftest/arm64: Skip non-power of 2 SVE vector lengths in fp-stress kselftest/arm64: Only enumerate power of two VLs in syscall-abi ... * for-next/misc: : Miscellaneous arm64 updates arm64/mm: Intercept pfn changes in set_pte_at() Documentation: arm64: correct spelling arm64: traps: attempt to dump all instructions arm64: Apply dynamic shadow call stack patching in two passes arm64: el2_setup.h: fix spelling typo in comments arm64: Kconfig: fix spelling arm64: cpufeature: Use kstrtobool() instead of strtobool() arm64: Avoid repeated AA64MMFR1_EL1 register read on pagefault path arm64: make ARCH_FORCE_MAX_ORDER selectable * for-next/sme2: (23 commits) : Support for arm64 SME 2 and 2.1 arm64/sme: Fix __finalise_el2 SMEver check kselftest/arm64: Remove redundant _start labels from zt-test kselftest/arm64: Add coverage of SME 2 and 2.1 hwcaps kselftest/arm64: Add coverage of the ZT ptrace regset kselftest/arm64: Add SME2 coverage to syscall-abi kselftest/arm64: Add test coverage for ZT register signal frames kselftest/arm64: Teach the generic signal context validation about ZT kselftest/arm64: Enumerate SME2 in the signal test utility code kselftest/arm64: Cover ZT in the FP stress test kselftest/arm64: Add a stress test program for ZT0 arm64/sme: Add hwcaps for SME 2 and 2.1 features arm64/sme: Implement ZT0 ptrace support arm64/sme: Implement signal handling for ZT arm64/sme: Implement context switching for ZT0 arm64/sme: Provide storage for ZT0 arm64/sme: Add basic enumeration for SME2 arm64/sme: Enable host kernel to access ZT0 arm64/sme: Manually encode ZT0 load and store instructions arm64/esr: Document ISS for ZT0 being disabled arm64/sme: Document SME 2 and SME 2.1 ABI ... * for-next/tpidr2: : Include TPIDR2 in the signal context kselftest/arm64: Add test case for TPIDR2 signal frame records kselftest/arm64: Add TPIDR2 to the set of known signal context records arm64/signal: Include TPIDR2 in the signal context arm64/sme: Document ABI for TPIDR2 signal information * for-next/scs: : arm64: harden shadow call stack pointer handling arm64: Stash shadow stack pointer in the task struct on interrupt arm64: Always load shadow stack pointer directly from the task struct * for-next/compat-hwcap: : arm64: Expose compat ARMv8 AArch32 features (HWCAPs) arm64: Add compat hwcap SSBS arm64: Add compat hwcap SB arm64: Add compat hwcap I8MM arm64: Add compat hwcap ASIMDBF16 arm64: Add compat hwcap ASIMDFHM arm64: Add compat hwcap ASIMDDP arm64: Add compat hwcap FPHP and ASIMDHP * for-next/ftrace: : Add arm64 support for DYNAMICE_FTRACE_WITH_CALL_OPS arm64: avoid executing padding bytes during kexec / hibernation arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS arm64: ftrace: Update stale comment arm64: patching: Add aarch64_insn_write_literal_u64() arm64: insn: Add helpers for BTI arm64: Extend support for CONFIG_FUNCTION_ALIGNMENT ACPI: Don't build ACPICA with '-Os' Compiler attributes: GCC cold function alignment workarounds ftrace: Add DYNAMIC_FTRACE_WITH_CALL_OPS * for-next/efi-boot-mmu-on: : Permit arm64 EFI boot with MMU and caches on arm64: kprobes: Drop ID map text from kprobes blacklist arm64: head: Switch endianness before populating the ID map efi: arm64: enter with MMU and caches enabled arm64: head: Clean the ID map and the HYP text to the PoC if needed arm64: head: avoid cache invalidation when entering with the MMU on arm64: head: record the MMU state at primary entry arm64: kernel: move identity map out of .text mapping arm64: head: Move all finalise_el2 calls to after __enable_mmu * for-next/ptrauth: : arm64 pointer authentication cleanup arm64: pauth: don't sign leaf functions arm64: unify asm-arch manipulation * for-next/pseudo-nmi: : Pseudo-NMI code generation optimisations arm64: irqflags: use alternative branches for pseudo-NMI logic arm64: add ARM64_HAS_GIC_PRIO_RELAXED_SYNC cpucap arm64: make ARM64_HAS_GIC_PRIO_MASKING depend on ARM64_HAS_GIC_CPUIF_SYSREGS arm64: rename ARM64_HAS_IRQ_PRIO_MASKING to ARM64_HAS_GIC_PRIO_MASKING arm64: rename ARM64_HAS_SYSREG_GIC_CPUIF to ARM64_HAS_GIC_CPUIF_SYSREGS
This commit is contained in:
parent
e8a709dc2a
1abf363d08
b2482807fb
2c4192c0a7
004fc58f91
9442d05bba
8ced928019
59b37fe52f
4f2c9bf16a
dc4824faa2
a088cf8eee
c68cf5285e
a5f61cc636
commit
156010ed9c
@ -223,7 +223,7 @@ Before jumping into the kernel, the following conditions must be met:
|
||||
For systems with a GICv3 interrupt controller to be used in v3 mode:
|
||||
- If EL3 is present:
|
||||
|
||||
- ICC_SRE_EL3.Enable (bit 3) must be initialiased to 0b1.
|
||||
- ICC_SRE_EL3.Enable (bit 3) must be initialised to 0b1.
|
||||
- ICC_SRE_EL3.SRE (bit 0) must be initialised to 0b1.
|
||||
- ICC_CTLR_EL3.PMHE (bit 6) must be set to the same value across
|
||||
all CPUs the kernel is executing on, and must stay constant
|
||||
@ -369,6 +369,16 @@ Before jumping into the kernel, the following conditions must be met:
|
||||
|
||||
- HCR_EL2.ATA (bit 56) must be initialised to 0b1.
|
||||
|
||||
For CPUs with the Scalable Matrix Extension version 2 (FEAT_SME2):
|
||||
|
||||
- If EL3 is present:
|
||||
|
||||
- SMCR_EL3.EZT0 (bit 30) must be initialised to 0b1.
|
||||
|
||||
- If the kernel is entered at EL1 and EL2 is present:
|
||||
|
||||
- SMCR_EL2.EZT0 (bit 30) must be initialised to 0b1.
|
||||
|
||||
The requirements described above for CPU mode, caches, MMUs, architected
|
||||
timers, coherency and system registers apply to all CPUs. All CPUs must
|
||||
enter the kernel in the same exception level. Where the values documented
|
||||
|
@ -14,7 +14,7 @@ Some hardware or software features are only available on some CPU
|
||||
implementations, and/or with certain kernel configurations, but have no
|
||||
architected discovery mechanism available to userspace code at EL0. The
|
||||
kernel exposes the presence of these features to userspace through a set
|
||||
of flags called hwcaps, exposed in the auxilliary vector.
|
||||
of flags called hwcaps, exposed in the auxiliary vector.
|
||||
|
||||
Userspace software can test for features by acquiring the AT_HWCAP or
|
||||
AT_HWCAP2 entry of the auxiliary vector, and testing whether the relevant
|
||||
@ -284,6 +284,24 @@ HWCAP2_RPRFM
|
||||
HWCAP2_SVE2P1
|
||||
Functionality implied by ID_AA64ZFR0_EL1.SVEver == 0b0010.
|
||||
|
||||
HWCAP2_SME2
|
||||
Functionality implied by ID_AA64SMFR0_EL1.SMEver == 0b0001.
|
||||
|
||||
HWCAP2_SME2P1
|
||||
Functionality implied by ID_AA64SMFR0_EL1.SMEver == 0b0010.
|
||||
|
||||
HWCAP2_SMEI16I32
|
||||
Functionality implied by ID_AA64SMFR0_EL1.I16I32 == 0b0101
|
||||
|
||||
HWCAP2_SMEBI32I32
|
||||
Functionality implied by ID_AA64SMFR0_EL1.BI32I32 == 0b1
|
||||
|
||||
HWCAP2_SMEB16B16
|
||||
Functionality implied by ID_AA64SMFR0_EL1.B16B16 == 0b1
|
||||
|
||||
HWCAP2_SMEF16F16
|
||||
Functionality implied by ID_AA64SMFR0_EL1.F16F16 == 0b1
|
||||
|
||||
4. Unused AT_HWCAP bits
|
||||
-----------------------
|
||||
|
||||
|
@ -18,14 +18,19 @@ model features for SME is included in Appendix A.
|
||||
1. General
|
||||
-----------
|
||||
|
||||
* PSTATE.SM, PSTATE.ZA, the streaming mode vector length, the ZA
|
||||
register state and TPIDR2_EL0 are tracked per thread.
|
||||
* PSTATE.SM, PSTATE.ZA, the streaming mode vector length, the ZA and (when
|
||||
present) ZTn register state and TPIDR2_EL0 are tracked per thread.
|
||||
|
||||
* The presence of SME is reported to userspace via HWCAP2_SME in the aux vector
|
||||
AT_HWCAP2 entry. Presence of this flag implies the presence of the SME
|
||||
instructions and registers, and the Linux-specific system interfaces
|
||||
described in this document. SME is reported in /proc/cpuinfo as "sme".
|
||||
|
||||
* The presence of SME2 is reported to userspace via HWCAP2_SME2 in the
|
||||
aux vector AT_HWCAP2 entry. Presence of this flag implies the presence of
|
||||
the SME2 instructions and ZT0, and the Linux-specific system interfaces
|
||||
described in this document. SME2 is reported in /proc/cpuinfo as "sme2".
|
||||
|
||||
* Support for the execution of SME instructions in userspace can also be
|
||||
detected by reading the CPU ID register ID_AA64PFR1_EL1 using an MRS
|
||||
instruction, and checking that the value of the SME field is nonzero. [3]
|
||||
@ -44,6 +49,7 @@ model features for SME is included in Appendix A.
|
||||
HWCAP2_SME_B16F32
|
||||
HWCAP2_SME_F32F32
|
||||
HWCAP2_SME_FA64
|
||||
HWCAP2_SME2
|
||||
|
||||
This list may be extended over time as the SME architecture evolves.
|
||||
|
||||
@ -52,8 +58,8 @@ model features for SME is included in Appendix A.
|
||||
cpu-feature-registers.txt for details.
|
||||
|
||||
* Debuggers should restrict themselves to interacting with the target via the
|
||||
NT_ARM_SVE, NT_ARM_SSVE and NT_ARM_ZA regsets. The recommended way
|
||||
of detecting support for these regsets is to connect to a target process
|
||||
NT_ARM_SVE, NT_ARM_SSVE, NT_ARM_ZA and NT_ARM_ZT regsets. The recommended
|
||||
way of detecting support for these regsets is to connect to a target process
|
||||
first and then attempt a
|
||||
|
||||
ptrace(PTRACE_GETREGSET, pid, NT_ARM_<regset>, &iov).
|
||||
@ -89,13 +95,13 @@ be zeroed.
|
||||
-------------------------
|
||||
|
||||
* On syscall PSTATE.ZA is preserved, if PSTATE.ZA==1 then the contents of the
|
||||
ZA matrix are preserved.
|
||||
ZA matrix and ZTn (if present) are preserved.
|
||||
|
||||
* On syscall PSTATE.SM will be cleared and the SVE registers will be handled
|
||||
as per the standard SVE ABI.
|
||||
|
||||
* Neither the SVE registers nor ZA are used to pass arguments to or receive
|
||||
results from any syscall.
|
||||
* None of the SVE registers, ZA or ZTn are used to pass arguments to
|
||||
or receive results from any syscall.
|
||||
|
||||
* On process creation (eg, clone()) the newly created process will have
|
||||
PSTATE.SM cleared.
|
||||
@ -111,6 +117,9 @@ be zeroed.
|
||||
|
||||
* Signal handlers are invoked with streaming mode and ZA disabled.
|
||||
|
||||
* A new signal frame record TPIDR2_MAGIC is added formatted as a struct
|
||||
tpidr2_context to allow access to TPIDR2_EL0 from signal handlers.
|
||||
|
||||
* A new signal frame record za_context encodes the ZA register contents on
|
||||
signal delivery. [1]
|
||||
|
||||
@ -134,6 +143,14 @@ be zeroed.
|
||||
__reserved[] referencing this space. za_context is then written in the
|
||||
extra space. Refer to [1] for further details about this mechanism.
|
||||
|
||||
* If ZTn is supported and PSTATE.ZA==1 then a signal frame record for ZTn will
|
||||
be generated.
|
||||
|
||||
* The signal record for ZTn has magic ZT_MAGIC (0x5a544e01) and consists of a
|
||||
standard signal frame header followed by a struct zt_context specifying
|
||||
the number of ZTn registers supported by the system, then zt_context.nregs
|
||||
blocks of 64 bytes of data per register.
|
||||
|
||||
|
||||
5. Signal return
|
||||
-----------------
|
||||
@ -151,6 +168,9 @@ When returning from a signal handler:
|
||||
the signal frame does not match the current vector length, the signal return
|
||||
attempt is treated as illegal, resulting in a forced SIGSEGV.
|
||||
|
||||
* If ZTn is not supported or PSTATE.ZA==0 then it is illegal to have a
|
||||
signal frame record for ZTn, resulting in a forced SIGSEGV.
|
||||
|
||||
|
||||
6. prctl extensions
|
||||
--------------------
|
||||
@ -214,8 +234,8 @@ prctl(PR_SME_SET_VL, unsigned long arg)
|
||||
vector length that will be applied at the next execve() by the calling
|
||||
thread.
|
||||
|
||||
* Changing the vector length causes all of ZA, P0..P15, FFR and all bits of
|
||||
Z0..Z31 except for Z0 bits [127:0] .. Z31 bits [127:0] to become
|
||||
* Changing the vector length causes all of ZA, ZTn, P0..P15, FFR and all
|
||||
bits of Z0..Z31 except for Z0 bits [127:0] .. Z31 bits [127:0] to become
|
||||
unspecified, including both streaming and non-streaming SVE state.
|
||||
Calling PR_SME_SET_VL with vl equal to the thread's current vector
|
||||
length, or calling PR_SME_SET_VL with the PR_SVE_SET_VL_ONEXEC flag,
|
||||
@ -317,6 +337,15 @@ The regset data starts with struct user_za_header, containing:
|
||||
|
||||
* The effect of writing a partial, incomplete payload is unspecified.
|
||||
|
||||
* A new regset NT_ARM_ZT is defined for access to ZTn state via
|
||||
PTRACE_GETREGSET and PTRACE_SETREGSET.
|
||||
|
||||
* The NT_ARM_ZT regset consists of a single 512 bit register.
|
||||
|
||||
* When PSTATE.ZA==0 reads of NT_ARM_ZT will report all bits of ZTn as 0.
|
||||
|
||||
* Writes to NT_ARM_ZT will set PSTATE.ZA to 1.
|
||||
|
||||
|
||||
8. ELF coredump extensions
|
||||
---------------------------
|
||||
@ -331,6 +360,11 @@ The regset data starts with struct user_za_header, containing:
|
||||
been read if a PTRACE_GETREGSET of NT_ARM_ZA were executed for each thread
|
||||
when the coredump was generated.
|
||||
|
||||
* A NT_ARM_ZT note will be added to each coredump for each thread of the
|
||||
dumped process. The contents will be equivalent to the data that would have
|
||||
been read if a PTRACE_GETREGSET of NT_ARM_ZT were executed for each thread
|
||||
when the coredump was generated.
|
||||
|
||||
* The NT_ARM_TLS note will be extended to two registers, the second register
|
||||
will contain TPIDR2_EL0 on systems that support SME and will be read as
|
||||
zero with writes ignored otherwise.
|
||||
@ -406,6 +440,9 @@ In A64 state, SME adds the following:
|
||||
For best system performance it is strongly encouraged for software to enable
|
||||
ZA only when it is actively being used.
|
||||
|
||||
* A new ZT0 register is introduced when SME2 is present. This is a 512 bit
|
||||
register which is accessible when PSTATE.ZA is set, as ZA itself is.
|
||||
|
||||
* Two new 1 bit fields in PSTATE which may be controlled via the SMSTART and
|
||||
SMSTOP instructions or by access to the SVCR system register:
|
||||
|
||||
|
@ -175,7 +175,7 @@ the SVE instruction set architecture.
|
||||
When returning from a signal handler:
|
||||
|
||||
* If there is no sve_context record in the signal frame, or if the record is
|
||||
present but contains no register data as desribed in the previous section,
|
||||
present but contains no register data as described in the previous section,
|
||||
then the SVE registers/bits become non-live and take unspecified values.
|
||||
|
||||
* If sve_context is present in the signal frame and contains full register
|
||||
@ -223,7 +223,7 @@ prctl(PR_SVE_SET_VL, unsigned long arg)
|
||||
Defer the requested vector length change until the next execve()
|
||||
performed by this thread.
|
||||
|
||||
The effect is equivalent to implicit exceution of the following
|
||||
The effect is equivalent to implicit execution of the following
|
||||
call immediately after the next execve() (if any) by the thread:
|
||||
|
||||
prctl(PR_SVE_SET_VL, arg & ~PR_SVE_SET_VL_ONEXEC)
|
||||
|
@ -252,5 +252,10 @@ static inline void gic_arch_enable_irqs(void)
|
||||
WARN_ON_ONCE(true);
|
||||
}
|
||||
|
||||
static inline bool gic_has_relaxed_pmr_sync(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
#endif /* !__ASM_ARCH_GICV3_H */
|
||||
|
@ -123,6 +123,8 @@ config ARM64
|
||||
select DMA_DIRECT_REMAP
|
||||
select EDAC_SUPPORT
|
||||
select FRAME_POINTER
|
||||
select FUNCTION_ALIGNMENT_4B
|
||||
select FUNCTION_ALIGNMENT_8B if DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
select GENERIC_ALLOCATOR
|
||||
select GENERIC_ARCH_TOPOLOGY
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST
|
||||
@ -186,6 +188,8 @@ config ARM64
|
||||
select HAVE_DYNAMIC_FTRACE
|
||||
select HAVE_DYNAMIC_FTRACE_WITH_ARGS \
|
||||
if $(cc-option,-fpatchable-function-entry=2)
|
||||
select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS \
|
||||
if (DYNAMIC_FTRACE_WITH_ARGS && !CFI_CLANG)
|
||||
select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY \
|
||||
if DYNAMIC_FTRACE_WITH_ARGS
|
||||
select HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
@ -1456,10 +1460,23 @@ config XEN
|
||||
help
|
||||
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM64.
|
||||
|
||||
# include/linux/mmzone.h requires the following to be true:
|
||||
#
|
||||
# MAX_ORDER - 1 + PAGE_SHIFT <= SECTION_SIZE_BITS
|
||||
#
|
||||
# so the maximum value of MAX_ORDER is SECTION_SIZE_BITS + 1 - PAGE_SHIFT:
|
||||
#
|
||||
# | SECTION_SIZE_BITS | PAGE_SHIFT | max MAX_ORDER | default MAX_ORDER |
|
||||
# ----+-------------------+--------------+-----------------+--------------------+
|
||||
# 4K | 27 | 12 | 16 | 11 |
|
||||
# 16K | 27 | 14 | 14 | 12 |
|
||||
# 64K | 29 | 16 | 14 | 14 |
|
||||
config ARCH_FORCE_MAX_ORDER
|
||||
int
|
||||
int "Maximum zone order" if ARM64_4K_PAGES || ARM64_16K_PAGES
|
||||
default "14" if ARM64_64K_PAGES
|
||||
range 12 14 if ARM64_16K_PAGES
|
||||
default "12" if ARM64_16K_PAGES
|
||||
range 11 16 if ARM64_4K_PAGES
|
||||
default "11"
|
||||
help
|
||||
The kernel memory allocator divides physically contiguous memory
|
||||
@ -1472,7 +1489,7 @@ config ARCH_FORCE_MAX_ORDER
|
||||
This config option is actually maximum order plus one. For example,
|
||||
a value of 11 means that the largest free memory block is 2^10 pages.
|
||||
|
||||
We make sure that we can allocate upto a HugePage size for each configuration.
|
||||
We make sure that we can allocate up to a HugePage size for each configuration.
|
||||
Hence we have :
|
||||
MAX_ORDER = (PMD_SHIFT - PAGE_SHIFT) + 1 => PAGE_SHIFT - 2
|
||||
|
||||
@ -1818,7 +1835,7 @@ config ARM64_PTR_AUTH_KERNEL
|
||||
bool "Use pointer authentication for kernel"
|
||||
default y
|
||||
depends on ARM64_PTR_AUTH
|
||||
depends on (CC_HAS_SIGN_RETURN_ADDRESS || CC_HAS_BRANCH_PROT_PAC_RET) && AS_HAS_PAC
|
||||
depends on (CC_HAS_SIGN_RETURN_ADDRESS || CC_HAS_BRANCH_PROT_PAC_RET) && AS_HAS_ARMV8_3
|
||||
# Modern compilers insert a .note.gnu.property section note for PAC
|
||||
# which is only understood by binutils starting with version 2.33.1.
|
||||
depends on LD_IS_LLD || LD_VERSION >= 23301 || (CC_IS_GCC && GCC_VERSION < 90100)
|
||||
@ -1843,7 +1860,7 @@ config CC_HAS_SIGN_RETURN_ADDRESS
|
||||
# GCC 7, 8
|
||||
def_bool $(cc-option,-msign-return-address=all)
|
||||
|
||||
config AS_HAS_PAC
|
||||
config AS_HAS_ARMV8_3
|
||||
def_bool $(cc-option,-Wa$(comma)-march=armv8.3-a)
|
||||
|
||||
config AS_HAS_CFI_NEGATE_RA_STATE
|
||||
|
@ -187,7 +187,7 @@ config ARCH_MVEBU
|
||||
select PINCTRL_ARMADA_CP110
|
||||
select PINCTRL_AC5
|
||||
help
|
||||
This enables support for Marvell EBU familly, including:
|
||||
This enables support for Marvell EBU family, including:
|
||||
- Armada 3700 SoC Family
|
||||
- Armada 7K SoC Family
|
||||
- Armada 8K SoC Family
|
||||
|
@ -63,50 +63,37 @@ stack_protector_prepare: prepare0
|
||||
include/generated/asm-offsets.h))
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_AS_HAS_ARMV8_2), y)
|
||||
# make sure to pass the newest target architecture to -march.
|
||||
asm-arch := armv8.2-a
|
||||
endif
|
||||
|
||||
# Ensure that if the compiler supports branch protection we default it
|
||||
# off, this will be overridden if we are using branch protection.
|
||||
branch-prot-flags-y += $(call cc-option,-mbranch-protection=none)
|
||||
|
||||
ifeq ($(CONFIG_ARM64_PTR_AUTH_KERNEL),y)
|
||||
branch-prot-flags-$(CONFIG_CC_HAS_SIGN_RETURN_ADDRESS) := -msign-return-address=all
|
||||
# We enable additional protection for leaf functions as there is some
|
||||
# narrow potential for ROP protection benefits and no substantial
|
||||
# performance impact has been observed.
|
||||
PACRET-y := pac-ret+leaf
|
||||
|
||||
# Using a shadow call stack in leaf functions is too costly, so avoid PAC there
|
||||
# as well when we may be patching PAC into SCS
|
||||
PACRET-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) := pac-ret
|
||||
|
||||
ifeq ($(CONFIG_ARM64_BTI_KERNEL),y)
|
||||
branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI) := -mbranch-protection=$(PACRET-y)+bti
|
||||
KBUILD_CFLAGS += -mbranch-protection=pac-ret+bti
|
||||
else ifeq ($(CONFIG_ARM64_PTR_AUTH_KERNEL),y)
|
||||
ifeq ($(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET),y)
|
||||
KBUILD_CFLAGS += -mbranch-protection=pac-ret
|
||||
else
|
||||
KBUILD_CFLAGS += -msign-return-address=non-leaf
|
||||
endif
|
||||
else
|
||||
branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET) := -mbranch-protection=$(PACRET-y)
|
||||
endif
|
||||
# -march=armv8.3-a enables the non-nops instructions for PAC, to avoid the
|
||||
# compiler to generate them and consequently to break the single image contract
|
||||
# we pass it only to the assembler. This option is utilized only in case of non
|
||||
# integrated assemblers.
|
||||
ifeq ($(CONFIG_AS_HAS_PAC), y)
|
||||
asm-arch := armv8.3-a
|
||||
endif
|
||||
endif
|
||||
|
||||
KBUILD_CFLAGS += $(branch-prot-flags-y)
|
||||
|
||||
ifeq ($(CONFIG_AS_HAS_ARMV8_4), y)
|
||||
# make sure to pass the newest target architecture to -march.
|
||||
asm-arch := armv8.4-a
|
||||
KBUILD_CFLAGS += $(call cc-option,-mbranch-protection=none)
|
||||
endif
|
||||
|
||||
# Tell the assembler to support instructions from the latest target
|
||||
# architecture.
|
||||
#
|
||||
# For non-integrated assemblers we'll pass this on the command line, and for
|
||||
# integrated assemblers we'll define ARM64_ASM_ARCH and ARM64_ASM_PREAMBLE for
|
||||
# inline usage.
|
||||
#
|
||||
# We cannot pass the same arch flag to the compiler as this would allow it to
|
||||
# freely generate instructions which are not supported by earlier architecture
|
||||
# versions, which would prevent a single kernel image from working on earlier
|
||||
# hardware.
|
||||
ifeq ($(CONFIG_AS_HAS_ARMV8_5), y)
|
||||
# make sure to pass the newest target architecture to -march.
|
||||
asm-arch := armv8.5-a
|
||||
asm-arch := armv8.5-a
|
||||
else ifeq ($(CONFIG_AS_HAS_ARMV8_4), y)
|
||||
asm-arch := armv8.4-a
|
||||
else ifeq ($(CONFIG_AS_HAS_ARMV8_3), y)
|
||||
asm-arch := armv8.3-a
|
||||
else ifeq ($(CONFIG_AS_HAS_ARMV8_2), y)
|
||||
asm-arch := armv8.2-a
|
||||
endif
|
||||
|
||||
ifdef asm-arch
|
||||
@ -139,7 +126,10 @@ endif
|
||||
|
||||
CHECKFLAGS += -D__aarch64__
|
||||
|
||||
ifeq ($(CONFIG_DYNAMIC_FTRACE_WITH_ARGS),y)
|
||||
ifeq ($(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS),y)
|
||||
KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
|
||||
CC_FLAGS_FTRACE := -fpatchable-function-entry=4,2
|
||||
else ifeq ($(CONFIG_DYNAMIC_FTRACE_WITH_ARGS),y)
|
||||
KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
|
||||
CC_FLAGS_FTRACE := -fpatchable-function-entry=2
|
||||
endif
|
||||
|
@ -190,5 +190,10 @@ static inline void gic_arch_enable_irqs(void)
|
||||
asm volatile ("msr daifclr, #3" : : : "memory");
|
||||
}
|
||||
|
||||
static inline bool gic_has_relaxed_pmr_sync(void)
|
||||
{
|
||||
return cpus_have_cap(ARM64_HAS_GIC_PRIO_RELAXED_SYNC);
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASM_ARCH_GICV3_H */
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include <linux/kasan-checks.h>
|
||||
|
||||
#include <asm/alternative-macros.h>
|
||||
|
||||
#define __nops(n) ".rept " #n "\nnop\n.endr\n"
|
||||
#define nops(n) asm volatile(__nops(n))
|
||||
|
||||
@ -41,10 +43,11 @@
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
#define pmr_sync() \
|
||||
do { \
|
||||
extern struct static_key_false gic_pmr_sync; \
|
||||
\
|
||||
if (static_branch_unlikely(&gic_pmr_sync)) \
|
||||
dsb(sy); \
|
||||
asm volatile( \
|
||||
ALTERNATIVE_CB("dsb sy", \
|
||||
ARM64_HAS_GIC_PRIO_RELAXED_SYNC, \
|
||||
alt_cb_patch_nops) \
|
||||
); \
|
||||
} while(0)
|
||||
#else
|
||||
#define pmr_sync() do {} while (0)
|
||||
|
@ -769,6 +769,12 @@ static __always_inline bool system_supports_sme(void)
|
||||
cpus_have_const_cap(ARM64_SME);
|
||||
}
|
||||
|
||||
static __always_inline bool system_supports_sme2(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_ARM64_SME) &&
|
||||
cpus_have_const_cap(ARM64_SME2);
|
||||
}
|
||||
|
||||
static __always_inline bool system_supports_fa64(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_ARM64_SME) &&
|
||||
@ -806,7 +812,7 @@ static inline bool system_has_full_ptr_auth(void)
|
||||
static __always_inline bool system_uses_irq_prio_masking(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) &&
|
||||
cpus_have_const_cap(ARM64_HAS_IRQ_PRIO_MASKING);
|
||||
cpus_have_const_cap(ARM64_HAS_GIC_PRIO_MASKING);
|
||||
}
|
||||
|
||||
static inline bool system_supports_mte(void)
|
||||
@ -864,7 +870,11 @@ static inline bool cpu_has_hw_af(void)
|
||||
if (!IS_ENABLED(CONFIG_ARM64_HW_AFDBM))
|
||||
return false;
|
||||
|
||||
mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
|
||||
/*
|
||||
* Use cached version to avoid emulated msr operation on KVM
|
||||
* guests.
|
||||
*/
|
||||
mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
||||
return cpuid_feature_extract_unsigned_field(mmfr1,
|
||||
ID_AA64MMFR1_EL1_HAFDBS_SHIFT);
|
||||
}
|
||||
|
@ -105,6 +105,8 @@ static inline unsigned long efi_get_kimg_min_align(void)
|
||||
#define EFI_ALLOC_ALIGN SZ_64K
|
||||
#define EFI_ALLOC_LIMIT ((1UL << 48) - 1)
|
||||
|
||||
extern unsigned long primary_entry_offset(void);
|
||||
|
||||
/*
|
||||
* On ARM systems, virtually remapped UEFI runtime services are set up in two
|
||||
* distinct stages:
|
||||
|
@ -177,7 +177,7 @@
|
||||
/**
|
||||
* Initialize EL2 registers to sane values. This should be called early on all
|
||||
* cores that were booted in EL2. Note that everything gets initialised as
|
||||
* if VHE was not evailable. The kernel context will be upgraded to VHE
|
||||
* if VHE was not available. The kernel context will be upgraded to VHE
|
||||
* if possible later on in the boot process
|
||||
*
|
||||
* Regs: x0, x1 and x2 are clobbered.
|
||||
|
@ -341,6 +341,7 @@
|
||||
#define ESR_ELx_SME_ISS_ILL 1
|
||||
#define ESR_ELx_SME_ISS_SM_DISABLED 2
|
||||
#define ESR_ELx_SME_ISS_ZA_DISABLED 3
|
||||
#define ESR_ELx_SME_ISS_ZT_DISABLED 4
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <asm/types.h>
|
||||
|
@ -61,7 +61,7 @@ extern void fpsimd_kvm_prepare(void);
|
||||
struct cpu_fp_state {
|
||||
struct user_fpsimd_state *st;
|
||||
void *sve_state;
|
||||
void *za_state;
|
||||
void *sme_state;
|
||||
u64 *svcr;
|
||||
unsigned int sve_vl;
|
||||
unsigned int sme_vl;
|
||||
@ -105,6 +105,13 @@ static inline void *sve_pffr(struct thread_struct *thread)
|
||||
return (char *)thread->sve_state + sve_ffr_offset(vl);
|
||||
}
|
||||
|
||||
static inline void *thread_zt_state(struct thread_struct *thread)
|
||||
{
|
||||
/* The ZT register state is stored immediately after the ZA state */
|
||||
unsigned int sme_vq = sve_vq_from_vl(thread_get_sme_vl(thread));
|
||||
return thread->sme_state + ZA_SIG_REGS_SIZE(sme_vq);
|
||||
}
|
||||
|
||||
extern void sve_save_state(void *state, u32 *pfpsr, int save_ffr);
|
||||
extern void sve_load_state(void const *state, u32 const *pfpsr,
|
||||
int restore_ffr);
|
||||
@ -112,12 +119,13 @@ extern void sve_flush_live(bool flush_ffr, unsigned long vq_minus_1);
|
||||
extern unsigned int sve_get_vl(void);
|
||||
extern void sve_set_vq(unsigned long vq_minus_1);
|
||||
extern void sme_set_vq(unsigned long vq_minus_1);
|
||||
extern void za_save_state(void *state);
|
||||
extern void za_load_state(void const *state);
|
||||
extern void sme_save_state(void *state, int zt);
|
||||
extern void sme_load_state(void const *state, int zt);
|
||||
|
||||
struct arm64_cpu_capabilities;
|
||||
extern void sve_kernel_enable(const struct arm64_cpu_capabilities *__unused);
|
||||
extern void sme_kernel_enable(const struct arm64_cpu_capabilities *__unused);
|
||||
extern void sme2_kernel_enable(const struct arm64_cpu_capabilities *__unused);
|
||||
extern void fa64_kernel_enable(const struct arm64_cpu_capabilities *__unused);
|
||||
|
||||
extern u64 read_zcr_features(void);
|
||||
@ -355,14 +363,20 @@ extern int sme_get_current_vl(void);
|
||||
|
||||
/*
|
||||
* Return how many bytes of memory are required to store the full SME
|
||||
* specific state (currently just ZA) for task, given task's currently
|
||||
* configured vector length.
|
||||
* specific state for task, given task's currently configured vector
|
||||
* length.
|
||||
*/
|
||||
static inline size_t za_state_size(struct task_struct const *task)
|
||||
static inline size_t sme_state_size(struct task_struct const *task)
|
||||
{
|
||||
unsigned int vl = task_get_sme_vl(task);
|
||||
size_t size;
|
||||
|
||||
return ZA_SIG_REGS_SIZE(sve_vq_from_vl(vl));
|
||||
size = ZA_SIG_REGS_SIZE(sve_vq_from_vl(vl));
|
||||
|
||||
if (system_supports_sme2())
|
||||
size += ZT_SIG_REG_SIZE;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#else
|
||||
@ -382,7 +396,7 @@ static inline int sme_max_virtualisable_vl(void) { return 0; }
|
||||
static inline int sme_set_current_vl(unsigned long arg) { return -EINVAL; }
|
||||
static inline int sme_get_current_vl(void) { return -EINVAL; }
|
||||
|
||||
static inline size_t za_state_size(struct task_struct const *task)
|
||||
static inline size_t sme_state_size(struct task_struct const *task)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -220,6 +220,28 @@
|
||||
| ((\offset) & 7)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* LDR (ZT0)
|
||||
*
|
||||
* LDR ZT0, nx
|
||||
*/
|
||||
.macro _ldr_zt nx
|
||||
_check_general_reg \nx
|
||||
.inst 0xe11f8000 \
|
||||
| (\nx << 5)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* STR (ZT0)
|
||||
*
|
||||
* STR ZT0, nx
|
||||
*/
|
||||
.macro _str_zt nx
|
||||
_check_general_reg \nx
|
||||
.inst 0xe13f8000 \
|
||||
| (\nx << 5)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Zero the entire ZA array
|
||||
* ZERO ZA
|
||||
|
@ -62,20 +62,7 @@ extern unsigned long ftrace_graph_call;
|
||||
|
||||
extern void return_to_handler(void);
|
||||
|
||||
static inline unsigned long ftrace_call_adjust(unsigned long addr)
|
||||
{
|
||||
/*
|
||||
* Adjust addr to point at the BL in the callsite.
|
||||
* See ftrace_init_nop() for the callsite sequence.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_ARGS))
|
||||
return addr + AARCH64_INSN_SIZE;
|
||||
/*
|
||||
* addr is the address of the mcount call instruction.
|
||||
* recordmcount does the necessary offset calculation.
|
||||
*/
|
||||
return addr;
|
||||
}
|
||||
unsigned long ftrace_call_adjust(unsigned long addr);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
||||
struct dyn_ftrace;
|
||||
|
@ -31,12 +31,20 @@
|
||||
#define COMPAT_HWCAP_VFPD32 (1 << 19)
|
||||
#define COMPAT_HWCAP_LPAE (1 << 20)
|
||||
#define COMPAT_HWCAP_EVTSTRM (1 << 21)
|
||||
#define COMPAT_HWCAP_FPHP (1 << 22)
|
||||
#define COMPAT_HWCAP_ASIMDHP (1 << 23)
|
||||
#define COMPAT_HWCAP_ASIMDDP (1 << 24)
|
||||
#define COMPAT_HWCAP_ASIMDFHM (1 << 25)
|
||||
#define COMPAT_HWCAP_ASIMDBF16 (1 << 26)
|
||||
#define COMPAT_HWCAP_I8MM (1 << 27)
|
||||
|
||||
#define COMPAT_HWCAP2_AES (1 << 0)
|
||||
#define COMPAT_HWCAP2_PMULL (1 << 1)
|
||||
#define COMPAT_HWCAP2_SHA1 (1 << 2)
|
||||
#define COMPAT_HWCAP2_SHA2 (1 << 3)
|
||||
#define COMPAT_HWCAP2_CRC32 (1 << 4)
|
||||
#define COMPAT_HWCAP2_SB (1 << 5)
|
||||
#define COMPAT_HWCAP2_SSBS (1 << 6)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <linux/log2.h>
|
||||
@ -123,6 +131,12 @@
|
||||
#define KERNEL_HWCAP_CSSC __khwcap2_feature(CSSC)
|
||||
#define KERNEL_HWCAP_RPRFM __khwcap2_feature(RPRFM)
|
||||
#define KERNEL_HWCAP_SVE2P1 __khwcap2_feature(SVE2P1)
|
||||
#define KERNEL_HWCAP_SME2 __khwcap2_feature(SME2)
|
||||
#define KERNEL_HWCAP_SME2P1 __khwcap2_feature(SME2P1)
|
||||
#define KERNEL_HWCAP_SME_I16I32 __khwcap2_feature(SME_I16I32)
|
||||
#define KERNEL_HWCAP_SME_BI32I32 __khwcap2_feature(SME_BI32I32)
|
||||
#define KERNEL_HWCAP_SME_B16B16 __khwcap2_feature(SME_B16B16)
|
||||
#define KERNEL_HWCAP_SME_F16F16 __khwcap2_feature(SME_F16F16)
|
||||
|
||||
/*
|
||||
* This yields a mask that user programs can use to figure out what
|
||||
|
@ -420,6 +420,7 @@ __AARCH64_INSN_FUNCS(sb, 0xFFFFFFFF, 0xD50330FF)
|
||||
__AARCH64_INSN_FUNCS(clrex, 0xFFFFF0FF, 0xD503305F)
|
||||
__AARCH64_INSN_FUNCS(ssbb, 0xFFFFFFFF, 0xD503309F)
|
||||
__AARCH64_INSN_FUNCS(pssbb, 0xFFFFFFFF, 0xD503349F)
|
||||
__AARCH64_INSN_FUNCS(bti, 0xFFFFFF3F, 0xD503241f)
|
||||
|
||||
#undef __AARCH64_INSN_FUNCS
|
||||
|
||||
|
@ -21,43 +21,77 @@
|
||||
* exceptions should be unmasked.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CPU interrupt mask handling.
|
||||
*/
|
||||
static inline void arch_local_irq_enable(void)
|
||||
static __always_inline bool __irqflags_uses_pmr(void)
|
||||
{
|
||||
if (system_has_prio_mask_debugging()) {
|
||||
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
|
||||
return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) &&
|
||||
alternative_has_feature_unlikely(ARM64_HAS_GIC_PRIO_MASKING);
|
||||
}
|
||||
|
||||
static __always_inline void __daif_local_irq_enable(void)
|
||||
{
|
||||
barrier();
|
||||
asm volatile("msr daifclr, #3");
|
||||
barrier();
|
||||
}
|
||||
|
||||
static __always_inline void __pmr_local_irq_enable(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARM64_DEBUG_PRIORITY_MASKING)) {
|
||||
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
|
||||
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
|
||||
}
|
||||
|
||||
asm volatile(ALTERNATIVE(
|
||||
"msr daifclr, #3 // arch_local_irq_enable",
|
||||
__msr_s(SYS_ICC_PMR_EL1, "%0"),
|
||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
||||
:
|
||||
: "r" ((unsigned long) GIC_PRIO_IRQON)
|
||||
: "memory");
|
||||
|
||||
barrier();
|
||||
write_sysreg_s(GIC_PRIO_IRQON, SYS_ICC_PMR_EL1);
|
||||
pmr_sync();
|
||||
barrier();
|
||||
}
|
||||
|
||||
static inline void arch_local_irq_enable(void)
|
||||
{
|
||||
if (__irqflags_uses_pmr()) {
|
||||
__pmr_local_irq_enable();
|
||||
} else {
|
||||
__daif_local_irq_enable();
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline void __daif_local_irq_disable(void)
|
||||
{
|
||||
barrier();
|
||||
asm volatile("msr daifset, #3");
|
||||
barrier();
|
||||
}
|
||||
|
||||
static __always_inline void __pmr_local_irq_disable(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARM64_DEBUG_PRIORITY_MASKING)) {
|
||||
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
|
||||
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
|
||||
}
|
||||
|
||||
barrier();
|
||||
write_sysreg_s(GIC_PRIO_IRQOFF, SYS_ICC_PMR_EL1);
|
||||
barrier();
|
||||
}
|
||||
|
||||
static inline void arch_local_irq_disable(void)
|
||||
{
|
||||
if (system_has_prio_mask_debugging()) {
|
||||
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
|
||||
|
||||
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
|
||||
if (__irqflags_uses_pmr()) {
|
||||
__pmr_local_irq_disable();
|
||||
} else {
|
||||
__daif_local_irq_disable();
|
||||
}
|
||||
}
|
||||
|
||||
asm volatile(ALTERNATIVE(
|
||||
"msr daifset, #3 // arch_local_irq_disable",
|
||||
__msr_s(SYS_ICC_PMR_EL1, "%0"),
|
||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
||||
:
|
||||
: "r" ((unsigned long) GIC_PRIO_IRQOFF)
|
||||
: "memory");
|
||||
static __always_inline unsigned long __daif_local_save_flags(void)
|
||||
{
|
||||
return read_sysreg(daif);
|
||||
}
|
||||
|
||||
static __always_inline unsigned long __pmr_local_save_flags(void)
|
||||
{
|
||||
return read_sysreg_s(SYS_ICC_PMR_EL1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -65,69 +99,108 @@ static inline void arch_local_irq_disable(void)
|
||||
*/
|
||||
static inline unsigned long arch_local_save_flags(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
if (__irqflags_uses_pmr()) {
|
||||
return __pmr_local_save_flags();
|
||||
} else {
|
||||
return __daif_local_save_flags();
|
||||
}
|
||||
}
|
||||
|
||||
asm volatile(ALTERNATIVE(
|
||||
"mrs %0, daif",
|
||||
__mrs_s("%0", SYS_ICC_PMR_EL1),
|
||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
||||
: "=&r" (flags)
|
||||
:
|
||||
: "memory");
|
||||
static __always_inline bool __daif_irqs_disabled_flags(unsigned long flags)
|
||||
{
|
||||
return flags & PSR_I_BIT;
|
||||
}
|
||||
|
||||
static __always_inline bool __pmr_irqs_disabled_flags(unsigned long flags)
|
||||
{
|
||||
return flags != GIC_PRIO_IRQON;
|
||||
}
|
||||
|
||||
static inline bool arch_irqs_disabled_flags(unsigned long flags)
|
||||
{
|
||||
if (__irqflags_uses_pmr()) {
|
||||
return __pmr_irqs_disabled_flags(flags);
|
||||
} else {
|
||||
return __daif_irqs_disabled_flags(flags);
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline bool __daif_irqs_disabled(void)
|
||||
{
|
||||
return __daif_irqs_disabled_flags(__daif_local_save_flags());
|
||||
}
|
||||
|
||||
static __always_inline bool __pmr_irqs_disabled(void)
|
||||
{
|
||||
return __pmr_irqs_disabled_flags(__pmr_local_save_flags());
|
||||
}
|
||||
|
||||
static inline bool arch_irqs_disabled(void)
|
||||
{
|
||||
if (__irqflags_uses_pmr()) {
|
||||
return __pmr_irqs_disabled();
|
||||
} else {
|
||||
return __daif_irqs_disabled();
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline unsigned long __daif_local_irq_save(void)
|
||||
{
|
||||
unsigned long flags = __daif_local_save_flags();
|
||||
|
||||
__daif_local_irq_disable();
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline int arch_irqs_disabled_flags(unsigned long flags)
|
||||
static __always_inline unsigned long __pmr_local_irq_save(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
asm volatile(ALTERNATIVE(
|
||||
"and %w0, %w1, #" __stringify(PSR_I_BIT),
|
||||
"eor %w0, %w1, #" __stringify(GIC_PRIO_IRQON),
|
||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
||||
: "=&r" (res)
|
||||
: "r" ((int) flags)
|
||||
: "memory");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline int arch_irqs_disabled(void)
|
||||
{
|
||||
return arch_irqs_disabled_flags(arch_local_save_flags());
|
||||
}
|
||||
|
||||
static inline unsigned long arch_local_irq_save(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
flags = arch_local_save_flags();
|
||||
unsigned long flags = __pmr_local_save_flags();
|
||||
|
||||
/*
|
||||
* There are too many states with IRQs disabled, just keep the current
|
||||
* state if interrupts are already disabled/masked.
|
||||
*/
|
||||
if (!arch_irqs_disabled_flags(flags))
|
||||
arch_local_irq_disable();
|
||||
if (!__pmr_irqs_disabled_flags(flags))
|
||||
__pmr_local_irq_disable();
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline unsigned long arch_local_irq_save(void)
|
||||
{
|
||||
if (__irqflags_uses_pmr()) {
|
||||
return __pmr_local_irq_save();
|
||||
} else {
|
||||
return __daif_local_irq_save();
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline void __daif_local_irq_restore(unsigned long flags)
|
||||
{
|
||||
barrier();
|
||||
write_sysreg(flags, daif);
|
||||
barrier();
|
||||
}
|
||||
|
||||
static __always_inline void __pmr_local_irq_restore(unsigned long flags)
|
||||
{
|
||||
barrier();
|
||||
write_sysreg_s(flags, SYS_ICC_PMR_EL1);
|
||||
pmr_sync();
|
||||
barrier();
|
||||
}
|
||||
|
||||
/*
|
||||
* restore saved IRQ state
|
||||
*/
|
||||
static inline void arch_local_irq_restore(unsigned long flags)
|
||||
{
|
||||
asm volatile(ALTERNATIVE(
|
||||
"msr daif, %0",
|
||||
__msr_s(SYS_ICC_PMR_EL1, "%0"),
|
||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
||||
:
|
||||
: "r" (flags)
|
||||
: "memory");
|
||||
|
||||
pmr_sync();
|
||||
if (__irqflags_uses_pmr()) {
|
||||
__pmr_local_irq_restore(flags);
|
||||
} else {
|
||||
__daif_local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __ASM_IRQFLAGS_H */
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include <asm/assembler.h>
|
||||
#endif
|
||||
|
||||
#define __ALIGN .align 2
|
||||
#define __ALIGN_STR ".align 2"
|
||||
#define __ALIGN .balign CONFIG_FUNCTION_ALIGNMENT
|
||||
#define __ALIGN_STR ".balign " #CONFIG_FUNCTION_ALIGNMENT
|
||||
|
||||
/*
|
||||
* When using in-kernel BTI we need to ensure that PCS-conformant
|
||||
|
@ -7,6 +7,8 @@
|
||||
int aarch64_insn_read(void *addr, u32 *insnp);
|
||||
int aarch64_insn_write(void *addr, u32 insn);
|
||||
|
||||
int aarch64_insn_write_literal_u64(void *addr, u64 val);
|
||||
|
||||
int aarch64_insn_patch_text_nosync(void *addr, u32 insn);
|
||||
int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt);
|
||||
|
||||
|
@ -275,6 +275,7 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
|
||||
}
|
||||
|
||||
extern void __sync_icache_dcache(pte_t pteval);
|
||||
bool pgattr_change_is_safe(u64 old, u64 new);
|
||||
|
||||
/*
|
||||
* PTE bits configuration in the presence of hardware Dirty Bit Management
|
||||
@ -292,7 +293,7 @@ extern void __sync_icache_dcache(pte_t pteval);
|
||||
* PTE_DIRTY || (PTE_WRITE && !PTE_RDONLY)
|
||||
*/
|
||||
|
||||
static inline void __check_racy_pte_update(struct mm_struct *mm, pte_t *ptep,
|
||||
static inline void __check_safe_pte_update(struct mm_struct *mm, pte_t *ptep,
|
||||
pte_t pte)
|
||||
{
|
||||
pte_t old_pte;
|
||||
@ -318,6 +319,9 @@ static inline void __check_racy_pte_update(struct mm_struct *mm, pte_t *ptep,
|
||||
VM_WARN_ONCE(pte_write(old_pte) && !pte_dirty(pte),
|
||||
"%s: racy dirty state clearing: 0x%016llx -> 0x%016llx",
|
||||
__func__, pte_val(old_pte), pte_val(pte));
|
||||
VM_WARN_ONCE(!pgattr_change_is_safe(pte_val(old_pte), pte_val(pte)),
|
||||
"%s: unsafe attribute change: 0x%016llx -> 0x%016llx",
|
||||
__func__, pte_val(old_pte), pte_val(pte));
|
||||
}
|
||||
|
||||
static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
|
||||
@ -346,7 +350,7 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
|
||||
mte_sync_tags(old_pte, pte);
|
||||
}
|
||||
|
||||
__check_racy_pte_update(mm, ptep, pte);
|
||||
__check_safe_pte_update(mm, ptep, pte);
|
||||
|
||||
set_pte(ptep, pte);
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ struct thread_struct {
|
||||
enum fp_type fp_type; /* registers FPSIMD or SVE? */
|
||||
unsigned int fpsimd_cpu;
|
||||
void *sve_state; /* SVE registers, if any */
|
||||
void *za_state; /* ZA register, if any */
|
||||
void *sme_state; /* ZA and ZT state, if any */
|
||||
unsigned int vl[ARM64_VEC_MAX]; /* vector length */
|
||||
unsigned int vl_onexec[ARM64_VEC_MAX]; /* vl after next exec */
|
||||
unsigned long fault_address; /* fault info */
|
||||
|
@ -194,7 +194,7 @@ struct pt_regs {
|
||||
u32 unused2;
|
||||
#endif
|
||||
u64 sdei_ttbr1;
|
||||
/* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */
|
||||
/* Only valid when ARM64_HAS_GIC_PRIO_MASKING is enabled. */
|
||||
u64 pmr_save;
|
||||
u64 stackframe[2];
|
||||
|
||||
|
@ -10,15 +10,16 @@
|
||||
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||
scs_sp .req x18
|
||||
|
||||
.macro scs_load tsk
|
||||
ldr scs_sp, [\tsk, #TSK_TI_SCS_SP]
|
||||
.macro scs_load_current
|
||||
get_current_task scs_sp
|
||||
ldr scs_sp, [scs_sp, #TSK_TI_SCS_SP]
|
||||
.endm
|
||||
|
||||
.macro scs_save tsk
|
||||
str scs_sp, [\tsk, #TSK_TI_SCS_SP]
|
||||
.endm
|
||||
#else
|
||||
.macro scs_load tsk
|
||||
.macro scs_load_current
|
||||
.endm
|
||||
|
||||
.macro scs_save tsk
|
||||
|
@ -496,6 +496,7 @@
|
||||
#define SCTLR_ELx_DSSBS (BIT(44))
|
||||
#define SCTLR_ELx_ATA (BIT(43))
|
||||
|
||||
#define SCTLR_ELx_EE_SHIFT 25
|
||||
#define SCTLR_ELx_ENIA_SHIFT 31
|
||||
|
||||
#define SCTLR_ELx_ITFSB (BIT(37))
|
||||
@ -504,7 +505,7 @@
|
||||
#define SCTLR_ELx_LSMAOE (BIT(29))
|
||||
#define SCTLR_ELx_nTLSMD (BIT(28))
|
||||
#define SCTLR_ELx_ENDA (BIT(27))
|
||||
#define SCTLR_ELx_EE (BIT(25))
|
||||
#define SCTLR_ELx_EE (BIT(SCTLR_ELx_EE_SHIFT))
|
||||
#define SCTLR_ELx_EIS (BIT(22))
|
||||
#define SCTLR_ELx_IESB (BIT(21))
|
||||
#define SCTLR_ELx_TSCXT (BIT(20))
|
||||
@ -730,8 +731,8 @@
|
||||
|
||||
#define ARM64_FEATURE_FIELD_BITS 4
|
||||
|
||||
/* Create a mask for the feature bits of the specified feature. */
|
||||
#define ARM64_FEATURE_MASK(x) (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
|
||||
/* Defined for compatibility only, do not add new users. */
|
||||
#define ARM64_FEATURE_MASK(x) (x##_MASK)
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
|
@ -96,5 +96,11 @@
|
||||
#define HWCAP2_CSSC (1UL << 34)
|
||||
#define HWCAP2_RPRFM (1UL << 35)
|
||||
#define HWCAP2_SVE2P1 (1UL << 36)
|
||||
#define HWCAP2_SME2 (1UL << 37)
|
||||
#define HWCAP2_SME2P1 (1UL << 38)
|
||||
#define HWCAP2_SME_I16I32 (1UL << 39)
|
||||
#define HWCAP2_SME_BI32I32 (1UL << 40)
|
||||
#define HWCAP2_SME_B16B16 (1UL << 41)
|
||||
#define HWCAP2_SME_F16F16 (1UL << 42)
|
||||
|
||||
#endif /* _UAPI__ASM_HWCAP_H */
|
||||
|
@ -144,6 +144,14 @@ struct sve_context {
|
||||
|
||||
#define SVE_SIG_FLAG_SM 0x1 /* Context describes streaming mode */
|
||||
|
||||
/* TPIDR2_EL0 context */
|
||||
#define TPIDR2_MAGIC 0x54504902
|
||||
|
||||
struct tpidr2_context {
|
||||
struct _aarch64_ctx head;
|
||||
__u64 tpidr2;
|
||||
};
|
||||
|
||||
#define ZA_MAGIC 0x54366345
|
||||
|
||||
struct za_context {
|
||||
@ -152,6 +160,14 @@ struct za_context {
|
||||
__u16 __reserved[3];
|
||||
};
|
||||
|
||||
#define ZT_MAGIC 0x5a544e01
|
||||
|
||||
struct zt_context {
|
||||
struct _aarch64_ctx head;
|
||||
__u16 nregs;
|
||||
__u16 __reserved[3];
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#include <asm/sve_context.h>
|
||||
@ -304,4 +320,15 @@ struct za_context {
|
||||
#define ZA_SIG_CONTEXT_SIZE(vq) \
|
||||
(ZA_SIG_REGS_OFFSET + ZA_SIG_REGS_SIZE(vq))
|
||||
|
||||
#define ZT_SIG_REG_SIZE 512
|
||||
|
||||
#define ZT_SIG_REG_BYTES (ZT_SIG_REG_SIZE / 8)
|
||||
|
||||
#define ZT_SIG_REGS_OFFSET sizeof(struct zt_context)
|
||||
|
||||
#define ZT_SIG_REGS_SIZE(n) (ZT_SIG_REG_BYTES * n)
|
||||
|
||||
#define ZT_SIG_CONTEXT_SIZE(n) \
|
||||
(sizeof(struct zt_context) + ZT_SIG_REGS_SIZE(n))
|
||||
|
||||
#endif /* _UAPI__ASM_SIGCONTEXT_H */
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <linux/arm_sdei.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@ -193,6 +194,9 @@ int main(void)
|
||||
DEFINE(KIMAGE_HEAD, offsetof(struct kimage, head));
|
||||
DEFINE(KIMAGE_START, offsetof(struct kimage, start));
|
||||
BLANK();
|
||||
#endif
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/stop_machine.h>
|
||||
#include <linux/sysfs.h>
|
||||
@ -282,16 +283,26 @@ static const struct arm64_ftr_bits ftr_id_aa64zfr0[] = {
|
||||
static const struct arm64_ftr_bits ftr_id_aa64smfr0[] = {
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_FA64_SHIFT, 1, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_SMEver_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_I16I64_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F64F64_SHIFT, 1, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_I16I32_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_B16B16_SHIFT, 1, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F16F16_SHIFT, 1, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_I8I32_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F16F32_SHIFT, 1, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_B16F32_SHIFT, 1, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_BI32I32_SHIFT, 1, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F32F32_SHIFT, 1, 0),
|
||||
ARM64_FTR_END,
|
||||
@ -444,8 +455,8 @@ static const struct arm64_ftr_bits ftr_mvfr0[] = {
|
||||
|
||||
static const struct arm64_ftr_bits ftr_mvfr1[] = {
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_SIMDFMAC_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_FPHP_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_SIMDHP_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_FPHP_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_SIMDHP_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_SIMDSP_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_SIMDInt_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_SIMDLS_SHIFT, 4, 0),
|
||||
@ -529,12 +540,12 @@ static const struct arm64_ftr_bits ftr_id_mmfr5[] = {
|
||||
};
|
||||
|
||||
static const struct arm64_ftr_bits ftr_id_isar6[] = {
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_I8MM_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_BF16_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_I8MM_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_BF16_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_SPECRES_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_SB_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_FHM_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_DP_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_SB_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_FHM_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_DP_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_JSCVT_SHIFT, 4, 0),
|
||||
ARM64_FTR_END,
|
||||
};
|
||||
@ -562,7 +573,7 @@ static const struct arm64_ftr_bits ftr_id_pfr1[] = {
|
||||
};
|
||||
|
||||
static const struct arm64_ftr_bits ftr_id_pfr2[] = {
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_PFR2_EL1_SSBS_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_PFR2_EL1_SSBS_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_PFR2_EL1_CSV3_SHIFT, 4, 0),
|
||||
ARM64_FTR_END,
|
||||
};
|
||||
@ -1795,7 +1806,7 @@ kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
|
||||
static int __init parse_kpti(char *str)
|
||||
{
|
||||
bool enabled;
|
||||
int ret = strtobool(str, &enabled);
|
||||
int ret = kstrtobool(str, &enabled);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -2039,14 +2050,50 @@ static bool enable_pseudo_nmi;
|
||||
|
||||
static int __init early_enable_pseudo_nmi(char *p)
|
||||
{
|
||||
return strtobool(p, &enable_pseudo_nmi);
|
||||
return kstrtobool(p, &enable_pseudo_nmi);
|
||||
}
|
||||
early_param("irqchip.gicv3_pseudo_nmi", early_enable_pseudo_nmi);
|
||||
|
||||
static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,
|
||||
int scope)
|
||||
{
|
||||
return enable_pseudo_nmi && has_useable_gicv3_cpuif(entry, scope);
|
||||
/*
|
||||
* ARM64_HAS_GIC_CPUIF_SYSREGS has a lower index, and is a boot CPU
|
||||
* feature, so will be detected earlier.
|
||||
*/
|
||||
BUILD_BUG_ON(ARM64_HAS_GIC_PRIO_MASKING <= ARM64_HAS_GIC_CPUIF_SYSREGS);
|
||||
if (!cpus_have_cap(ARM64_HAS_GIC_CPUIF_SYSREGS))
|
||||
return false;
|
||||
|
||||
return enable_pseudo_nmi;
|
||||
}
|
||||
|
||||
static bool has_gic_prio_relaxed_sync(const struct arm64_cpu_capabilities *entry,
|
||||
int scope)
|
||||
{
|
||||
/*
|
||||
* If we're not using priority masking then we won't be poking PMR_EL1,
|
||||
* and there's no need to relax synchronization of writes to it, and
|
||||
* ICC_CTLR_EL1 might not be accessible and we must avoid reads from
|
||||
* that.
|
||||
*
|
||||
* ARM64_HAS_GIC_PRIO_MASKING has a lower index, and is a boot CPU
|
||||
* feature, so will be detected earlier.
|
||||
*/
|
||||
BUILD_BUG_ON(ARM64_HAS_GIC_PRIO_RELAXED_SYNC <= ARM64_HAS_GIC_PRIO_MASKING);
|
||||
if (!cpus_have_cap(ARM64_HAS_GIC_PRIO_MASKING))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* When Priority Mask Hint Enable (PMHE) == 0b0, PMR is not used as a
|
||||
* hint for interrupt distribution, a DSB is not necessary when
|
||||
* unmasking IRQs via PMR, and we can relax the barrier to a NOP.
|
||||
*
|
||||
* Linux itself doesn't use 1:N distribution, so has no need to
|
||||
* set PMHE. The only reason to have it set is if EL3 requires it
|
||||
* (and we can't change it).
|
||||
*/
|
||||
return (gic_read_ctlr() & ICC_CTLR_EL1_PMHE_MASK) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2142,7 +2189,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
|
||||
},
|
||||
{
|
||||
.desc = "GIC system register CPU interface",
|
||||
.capability = ARM64_HAS_SYSREG_GIC_CPUIF,
|
||||
.capability = ARM64_HAS_GIC_CPUIF_SYSREGS,
|
||||
.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
|
||||
.matches = has_useable_gicv3_cpuif,
|
||||
.sys_reg = SYS_ID_AA64PFR0_EL1,
|
||||
@ -2534,14 +2581,17 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
|
||||
* Depends on having GICv3
|
||||
*/
|
||||
.desc = "IRQ priority masking",
|
||||
.capability = ARM64_HAS_IRQ_PRIO_MASKING,
|
||||
.capability = ARM64_HAS_GIC_PRIO_MASKING,
|
||||
.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
|
||||
.matches = can_use_gic_priorities,
|
||||
.sys_reg = SYS_ID_AA64PFR0_EL1,
|
||||
.field_pos = ID_AA64PFR0_EL1_GIC_SHIFT,
|
||||
.field_width = 4,
|
||||
.sign = FTR_UNSIGNED,
|
||||
.min_field_value = 1,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Depends on ARM64_HAS_GIC_PRIO_MASKING
|
||||
*/
|
||||
.capability = ARM64_HAS_GIC_PRIO_RELAXED_SYNC,
|
||||
.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
|
||||
.matches = has_gic_prio_relaxed_sync,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_E0PD
|
||||
@ -2649,6 +2699,18 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
|
||||
.matches = has_cpuid_feature,
|
||||
.cpu_enable = fa64_kernel_enable,
|
||||
},
|
||||
{
|
||||
.desc = "SME2",
|
||||
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
|
||||
.capability = ARM64_SME2,
|
||||
.sys_reg = SYS_ID_AA64PFR1_EL1,
|
||||
.sign = FTR_UNSIGNED,
|
||||
.field_pos = ID_AA64PFR1_EL1_SME_SHIFT,
|
||||
.field_width = ID_AA64PFR1_EL1_SME_WIDTH,
|
||||
.min_field_value = ID_AA64PFR1_EL1_SME_SME2,
|
||||
.matches = has_cpuid_feature,
|
||||
.cpu_enable = sme2_kernel_enable,
|
||||
},
|
||||
#endif /* CONFIG_ARM64_SME */
|
||||
{
|
||||
.desc = "WFx with timeout",
|
||||
@ -2777,7 +2839,7 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
|
||||
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_EL1_FP_SHIFT, 4, FTR_SIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_FPHP),
|
||||
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_EL1_AdvSIMD_SHIFT, 4, FTR_SIGNED, 0, CAP_HWCAP, KERNEL_HWCAP_ASIMD),
|
||||
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_EL1_AdvSIMD_SHIFT, 4, FTR_SIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ASIMDHP),
|
||||
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_EL1_DIT_SHIFT, 4, FTR_SIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_DIT),
|
||||
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_EL1_DIT_SHIFT, 4, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_DIT),
|
||||
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_EL1_DPB_SHIFT, 4, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_DCPOP),
|
||||
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_EL1_DPB_SHIFT, 4, FTR_UNSIGNED, 2, CAP_HWCAP, KERNEL_HWCAP_DCPODP),
|
||||
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_EL1_JSCVT_SHIFT, 4, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_JSCVT),
|
||||
@ -2827,11 +2889,17 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
|
||||
#ifdef CONFIG_ARM64_SME
|
||||
HWCAP_CAP(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_EL1_SME_SHIFT, 4, FTR_UNSIGNED, ID_AA64PFR1_EL1_SME_IMP, CAP_HWCAP, KERNEL_HWCAP_SME),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_FA64_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_FA64_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_FA64),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_SMEver_SHIFT, 4, FTR_UNSIGNED, ID_AA64SMFR0_EL1_SMEver_SME2p1, CAP_HWCAP, KERNEL_HWCAP_SME2P1),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_SMEver_SHIFT, 4, FTR_UNSIGNED, ID_AA64SMFR0_EL1_SMEver_SME2, CAP_HWCAP, KERNEL_HWCAP_SME2),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_I16I64_SHIFT, 4, FTR_UNSIGNED, ID_AA64SMFR0_EL1_I16I64_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I16I64),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_F64F64_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_F64F64_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F64F64),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_I16I32_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_I16I32_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I16I32),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_B16B16_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_B16B16_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_B16B16),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_F16F16_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_F16F16_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F16F16),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_I8I32_SHIFT, 4, FTR_UNSIGNED, ID_AA64SMFR0_EL1_I8I32_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_I8I32),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_F16F32_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_F16F32_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F16F32),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_B16F32_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_B16F32_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_B16F32),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_BI32I32_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_BI32I32_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_BI32I32),
|
||||
HWCAP_CAP(SYS_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_F32F32_SHIFT, 1, FTR_UNSIGNED, ID_AA64SMFR0_EL1_F32F32_IMP, CAP_HWCAP, KERNEL_HWCAP_SME_F32F32),
|
||||
#endif /* CONFIG_ARM64_SME */
|
||||
{},
|
||||
@ -2866,11 +2934,19 @@ static const struct arm64_cpu_capabilities compat_elf_hwcaps[] = {
|
||||
/* Arm v8 mandates MVFR0.FPDP == {0, 2}. So, piggy back on this for the presence of VFP support */
|
||||
HWCAP_CAP(SYS_MVFR0_EL1, MVFR0_EL1_FPDP_SHIFT, 4, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP, COMPAT_HWCAP_VFP),
|
||||
HWCAP_CAP(SYS_MVFR0_EL1, MVFR0_EL1_FPDP_SHIFT, 4, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP, COMPAT_HWCAP_VFPv3),
|
||||
HWCAP_CAP(SYS_MVFR1_EL1, MVFR1_EL1_FPHP_SHIFT, 4, FTR_UNSIGNED, 3, CAP_COMPAT_HWCAP, COMPAT_HWCAP_FPHP),
|
||||
HWCAP_CAP(SYS_MVFR1_EL1, MVFR1_EL1_SIMDHP_SHIFT, 4, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP, COMPAT_HWCAP_ASIMDHP),
|
||||
HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_EL1_AES_SHIFT, 4, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_PMULL),
|
||||
HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_EL1_AES_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_AES),
|
||||
HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_EL1_SHA1_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SHA1),
|
||||
HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_EL1_SHA2_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SHA2),
|
||||
HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_EL1_CRC32_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_CRC32),
|
||||
HWCAP_CAP(SYS_ID_ISAR6_EL1, ID_ISAR6_EL1_DP_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP, COMPAT_HWCAP_ASIMDDP),
|
||||
HWCAP_CAP(SYS_ID_ISAR6_EL1, ID_ISAR6_EL1_FHM_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP, COMPAT_HWCAP_ASIMDFHM),
|
||||
HWCAP_CAP(SYS_ID_ISAR6_EL1, ID_ISAR6_EL1_SB_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SB),
|
||||
HWCAP_CAP(SYS_ID_ISAR6_EL1, ID_ISAR6_EL1_BF16_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP, COMPAT_HWCAP_ASIMDBF16),
|
||||
HWCAP_CAP(SYS_ID_ISAR6_EL1, ID_ISAR6_EL1_I8MM_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP, COMPAT_HWCAP_I8MM),
|
||||
HWCAP_CAP(SYS_ID_PFR2_EL1, ID_PFR2_EL1_SSBS_SHIFT, 4, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SSBS),
|
||||
#endif
|
||||
{},
|
||||
};
|
||||
|
@ -119,6 +119,12 @@ static const char *const hwcap_str[] = {
|
||||
[KERNEL_HWCAP_CSSC] = "cssc",
|
||||
[KERNEL_HWCAP_RPRFM] = "rprfm",
|
||||
[KERNEL_HWCAP_SVE2P1] = "sve2p1",
|
||||
[KERNEL_HWCAP_SME2] = "sme2",
|
||||
[KERNEL_HWCAP_SME2P1] = "sme2p1",
|
||||
[KERNEL_HWCAP_SME_I16I32] = "smei16i32",
|
||||
[KERNEL_HWCAP_SME_BI32I32] = "smebi32i32",
|
||||
[KERNEL_HWCAP_SME_B16B16] = "smeb16b16",
|
||||
[KERNEL_HWCAP_SME_F16F16] = "smef16f16",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
@ -146,6 +152,12 @@ static const char *const compat_hwcap_str[] = {
|
||||
[COMPAT_KERNEL_HWCAP(VFPD32)] = NULL, /* Not possible on arm64 */
|
||||
[COMPAT_KERNEL_HWCAP(LPAE)] = "lpae",
|
||||
[COMPAT_KERNEL_HWCAP(EVTSTRM)] = "evtstrm",
|
||||
[COMPAT_KERNEL_HWCAP(FPHP)] = "fphp",
|
||||
[COMPAT_KERNEL_HWCAP(ASIMDHP)] = "asimdhp",
|
||||
[COMPAT_KERNEL_HWCAP(ASIMDDP)] = "asimddp",
|
||||
[COMPAT_KERNEL_HWCAP(ASIMDFHM)] = "asimdfhm",
|
||||
[COMPAT_KERNEL_HWCAP(ASIMDBF16)] = "asimdbf16",
|
||||
[COMPAT_KERNEL_HWCAP(I8MM)] = "i8mm",
|
||||
};
|
||||
|
||||
#define COMPAT_KERNEL_HWCAP2(x) const_ilog2(COMPAT_HWCAP2_ ## x)
|
||||
@ -155,6 +167,8 @@ static const char *const compat_hwcap2_str[] = {
|
||||
[COMPAT_KERNEL_HWCAP2(SHA1)] = "sha1",
|
||||
[COMPAT_KERNEL_HWCAP2(SHA2)] = "sha2",
|
||||
[COMPAT_KERNEL_HWCAP2(CRC32)] = "crc32",
|
||||
[COMPAT_KERNEL_HWCAP2(SB)] = "sb",
|
||||
[COMPAT_KERNEL_HWCAP2(SSBS)] = "ssbs",
|
||||
};
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
|
@ -100,25 +100,35 @@ SYM_FUNC_START(sme_set_vq)
|
||||
SYM_FUNC_END(sme_set_vq)
|
||||
|
||||
/*
|
||||
* Save the SME state
|
||||
* Save the ZA and ZT state
|
||||
*
|
||||
* x0 - pointer to buffer for state
|
||||
* x1 - number of ZT registers to save
|
||||
*/
|
||||
SYM_FUNC_START(za_save_state)
|
||||
_sme_rdsvl 1, 1 // x1 = VL/8
|
||||
sme_save_za 0, x1, 12
|
||||
SYM_FUNC_START(sme_save_state)
|
||||
_sme_rdsvl 2, 1 // x2 = VL/8
|
||||
sme_save_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
|
||||
|
||||
cbz x1, 1f
|
||||
_str_zt 0
|
||||
1:
|
||||
ret
|
||||
SYM_FUNC_END(za_save_state)
|
||||
SYM_FUNC_END(sme_save_state)
|
||||
|
||||
/*
|
||||
* Load the SME state
|
||||
* Load the ZA and ZT state
|
||||
*
|
||||
* x0 - pointer to buffer for state
|
||||
* x1 - number of ZT registers to save
|
||||
*/
|
||||
SYM_FUNC_START(za_load_state)
|
||||
_sme_rdsvl 1, 1 // x1 = VL/8
|
||||
sme_load_za 0, x1, 12
|
||||
SYM_FUNC_START(sme_load_state)
|
||||
_sme_rdsvl 2, 1 // x2 = VL/8
|
||||
sme_load_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
|
||||
|
||||
cbz x1, 1f
|
||||
_ldr_zt 0
|
||||
1:
|
||||
ret
|
||||
SYM_FUNC_END(za_load_state)
|
||||
SYM_FUNC_END(sme_load_state)
|
||||
|
||||
#endif /* CONFIG_ARM64_SME */
|
||||
|
@ -65,13 +65,35 @@ SYM_CODE_START(ftrace_caller)
|
||||
stp x29, x30, [sp, #FREGS_SIZE]
|
||||
add x29, sp, #FREGS_SIZE
|
||||
|
||||
sub x0, x30, #AARCH64_INSN_SIZE // ip (callsite's BL insn)
|
||||
mov x1, x9 // parent_ip (callsite's LR)
|
||||
ldr_l x2, function_trace_op // op
|
||||
mov x3, sp // regs
|
||||
/* Prepare arguments for the the tracer func */
|
||||
sub x0, x30, #AARCH64_INSN_SIZE // ip (callsite's BL insn)
|
||||
mov x1, x9 // parent_ip (callsite's LR)
|
||||
mov x3, sp // regs
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
/*
|
||||
* The literal pointer to the ops is at an 8-byte aligned boundary
|
||||
* which is either 12 or 16 bytes before the BL instruction in the call
|
||||
* site. See ftrace_call_adjust() for details.
|
||||
*
|
||||
* Therefore here the LR points at `literal + 16` or `literal + 20`,
|
||||
* and we can find the address of the literal in either case by
|
||||
* aligning to an 8-byte boundary and subtracting 16. We do the
|
||||
* alignment first as this allows us to fold the subtraction into the
|
||||
* LDR.
|
||||
*/
|
||||
bic x2, x30, 0x7
|
||||
ldr x2, [x2, #-16] // op
|
||||
|
||||
ldr x4, [x2, #FTRACE_OPS_FUNC] // op->func
|
||||
blr x4 // op->func(ip, parent_ip, op, regs)
|
||||
|
||||
#else
|
||||
ldr_l x2, function_trace_op // op
|
||||
|
||||
SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
|
||||
bl ftrace_stub
|
||||
bl ftrace_stub // func(ip, parent_ip, op, regs)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* At the callsite x0-x8 and x19-x30 were live. Any C code will have preserved
|
||||
|
@ -275,7 +275,7 @@ alternative_if ARM64_HAS_ADDRESS_AUTH
|
||||
alternative_else_nop_endif
|
||||
1:
|
||||
|
||||
scs_load tsk
|
||||
scs_load_current
|
||||
.else
|
||||
add x21, sp, #PT_REGS_SIZE
|
||||
get_current_task tsk
|
||||
@ -311,13 +311,16 @@ alternative_else_nop_endif
|
||||
.endif
|
||||
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
/* Save pmr */
|
||||
alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
||||
alternative_if_not ARM64_HAS_GIC_PRIO_MASKING
|
||||
b .Lskip_pmr_save\@
|
||||
alternative_else_nop_endif
|
||||
|
||||
mrs_s x20, SYS_ICC_PMR_EL1
|
||||
str x20, [sp, #S_PMR_SAVE]
|
||||
mov x20, #GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET
|
||||
msr_s SYS_ICC_PMR_EL1, x20
|
||||
alternative_else_nop_endif
|
||||
|
||||
.Lskip_pmr_save\@:
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -336,15 +339,19 @@ alternative_else_nop_endif
|
||||
.endif
|
||||
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
/* Restore pmr */
|
||||
alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
||||
alternative_if_not ARM64_HAS_GIC_PRIO_MASKING
|
||||
b .Lskip_pmr_restore\@
|
||||
alternative_else_nop_endif
|
||||
|
||||
ldr x20, [sp, #S_PMR_SAVE]
|
||||
msr_s SYS_ICC_PMR_EL1, x20
|
||||
mrs_s x21, SYS_ICC_CTLR_EL1
|
||||
tbz x21, #6, .L__skip_pmr_sync\@ // Check for ICC_CTLR_EL1.PMHE
|
||||
dsb sy // Ensure priority change is seen by redistributor
|
||||
.L__skip_pmr_sync\@:
|
||||
|
||||
/* Ensure priority change is seen by redistributor */
|
||||
alternative_if_not ARM64_HAS_GIC_PRIO_RELAXED_SYNC
|
||||
dsb sy
|
||||
alternative_else_nop_endif
|
||||
|
||||
.Lskip_pmr_restore\@:
|
||||
#endif
|
||||
|
||||
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
|
||||
@ -848,7 +855,7 @@ SYM_FUNC_START(cpu_switch_to)
|
||||
msr sp_el0, x1
|
||||
ptrauth_keys_install_kernel x1, x8, x9, x10
|
||||
scs_save x0
|
||||
scs_load x1
|
||||
scs_load_current
|
||||
ret
|
||||
SYM_FUNC_END(cpu_switch_to)
|
||||
NOKPROBE(cpu_switch_to)
|
||||
@ -876,19 +883,19 @@ NOKPROBE(ret_from_fork)
|
||||
*/
|
||||
SYM_FUNC_START(call_on_irq_stack)
|
||||
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||
stp scs_sp, xzr, [sp, #-16]!
|
||||
get_current_task x16
|
||||
scs_save x16
|
||||
ldr_this_cpu scs_sp, irq_shadow_call_stack_ptr, x17
|
||||
#endif
|
||||
|
||||
/* Create a frame record to save our LR and SP (implicit in FP) */
|
||||
stp x29, x30, [sp, #-16]!
|
||||
mov x29, sp
|
||||
|
||||
ldr_this_cpu x16, irq_stack_ptr, x17
|
||||
mov x15, #IRQ_STACK_SIZE
|
||||
add x16, x16, x15
|
||||
|
||||
/* Move to the new stack and call the function there */
|
||||
mov sp, x16
|
||||
add sp, x16, #IRQ_STACK_SIZE
|
||||
blr x1
|
||||
|
||||
/*
|
||||
@ -897,9 +904,7 @@ SYM_FUNC_START(call_on_irq_stack)
|
||||
*/
|
||||
mov sp, x29
|
||||
ldp x29, x30, [sp], #16
|
||||
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||
ldp scs_sp, xzr, [sp], #16
|
||||
#endif
|
||||
scs_load_current
|
||||
ret
|
||||
SYM_FUNC_END(call_on_irq_stack)
|
||||
NOKPROBE(call_on_irq_stack)
|
||||
|
@ -299,7 +299,7 @@ void task_set_vl_onexec(struct task_struct *task, enum vec_type type,
|
||||
/*
|
||||
* TIF_SME controls whether a task can use SME without trapping while
|
||||
* in userspace, when TIF_SME is set then we must have storage
|
||||
* alocated in sve_state and za_state to store the contents of both ZA
|
||||
* alocated in sve_state and sme_state to store the contents of both ZA
|
||||
* and the SVE registers for both streaming and non-streaming modes.
|
||||
*
|
||||
* If both SVCR.ZA and SVCR.SM are disabled then at any point we
|
||||
@ -429,7 +429,8 @@ static void task_fpsimd_load(void)
|
||||
write_sysreg_s(current->thread.svcr, SYS_SVCR);
|
||||
|
||||
if (thread_za_enabled(¤t->thread))
|
||||
za_load_state(current->thread.za_state);
|
||||
sme_load_state(current->thread.sme_state,
|
||||
system_supports_sme2());
|
||||
|
||||
if (thread_sm_enabled(¤t->thread))
|
||||
restore_ffr = system_supports_fa64();
|
||||
@ -490,7 +491,8 @@ static void fpsimd_save(void)
|
||||
*svcr = read_sysreg_s(SYS_SVCR);
|
||||
|
||||
if (*svcr & SVCR_ZA_MASK)
|
||||
za_save_state(last->za_state);
|
||||
sme_save_state(last->sme_state,
|
||||
system_supports_sme2());
|
||||
|
||||
/* If we are in streaming mode override regular SVE. */
|
||||
if (*svcr & SVCR_SM_MASK) {
|
||||
@ -1257,30 +1259,30 @@ void fpsimd_release_task(struct task_struct *dead_task)
|
||||
#ifdef CONFIG_ARM64_SME
|
||||
|
||||
/*
|
||||
* Ensure that task->thread.za_state is allocated and sufficiently large.
|
||||
* Ensure that task->thread.sme_state is allocated and sufficiently large.
|
||||
*
|
||||
* This function should be used only in preparation for replacing
|
||||
* task->thread.za_state with new data. The memory is always zeroed
|
||||
* task->thread.sme_state with new data. The memory is always zeroed
|
||||
* here to prevent stale data from showing through: this is done in
|
||||
* the interest of testability and predictability, the architecture
|
||||
* guarantees that when ZA is enabled it will be zeroed.
|
||||
*/
|
||||
void sme_alloc(struct task_struct *task)
|
||||
{
|
||||
if (task->thread.za_state) {
|
||||
memset(task->thread.za_state, 0, za_state_size(task));
|
||||
if (task->thread.sme_state) {
|
||||
memset(task->thread.sme_state, 0, sme_state_size(task));
|
||||
return;
|
||||
}
|
||||
|
||||
/* This could potentially be up to 64K. */
|
||||
task->thread.za_state =
|
||||
kzalloc(za_state_size(task), GFP_KERNEL);
|
||||
task->thread.sme_state =
|
||||
kzalloc(sme_state_size(task), GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void sme_free(struct task_struct *task)
|
||||
{
|
||||
kfree(task->thread.za_state);
|
||||
task->thread.za_state = NULL;
|
||||
kfree(task->thread.sme_state);
|
||||
task->thread.sme_state = NULL;
|
||||
}
|
||||
|
||||
void sme_kernel_enable(const struct arm64_cpu_capabilities *__always_unused p)
|
||||
@ -1298,6 +1300,17 @@ void sme_kernel_enable(const struct arm64_cpu_capabilities *__always_unused p)
|
||||
isb();
|
||||
}
|
||||
|
||||
/*
|
||||
* This must be called after sme_kernel_enable(), we rely on the
|
||||
* feature table being sorted to ensure this.
|
||||
*/
|
||||
void sme2_kernel_enable(const struct arm64_cpu_capabilities *__always_unused p)
|
||||
{
|
||||
/* Allow use of ZT0 */
|
||||
write_sysreg_s(read_sysreg_s(SYS_SMCR_EL1) | SMCR_ELx_EZT0_MASK,
|
||||
SYS_SMCR_EL1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This must be called after sme_kernel_enable(), we rely on the
|
||||
* feature table being sorted to ensure this.
|
||||
@ -1322,7 +1335,6 @@ u64 read_smcr_features(void)
|
||||
unsigned int vq_max;
|
||||
|
||||
sme_kernel_enable(NULL);
|
||||
sme_smstart_sm();
|
||||
|
||||
/*
|
||||
* Set the maximum possible VL.
|
||||
@ -1332,11 +1344,9 @@ u64 read_smcr_features(void)
|
||||
|
||||
smcr = read_sysreg_s(SYS_SMCR_EL1);
|
||||
smcr &= ~(u64)SMCR_ELx_LEN_MASK; /* Only the LEN field */
|
||||
vq_max = sve_vq_from_vl(sve_get_vl());
|
||||
vq_max = sve_vq_from_vl(sme_get_vl());
|
||||
smcr |= vq_max - 1; /* set LEN field to maximum effective value */
|
||||
|
||||
sme_smstop_sm();
|
||||
|
||||
return smcr;
|
||||
}
|
||||
|
||||
@ -1488,7 +1498,7 @@ void do_sme_acc(unsigned long esr, struct pt_regs *regs)
|
||||
|
||||
sve_alloc(current, false);
|
||||
sme_alloc(current);
|
||||
if (!current->thread.sve_state || !current->thread.za_state) {
|
||||
if (!current->thread.sve_state || !current->thread.sme_state) {
|
||||
force_sig(SIGKILL);
|
||||
return;
|
||||
}
|
||||
@ -1609,7 +1619,7 @@ static void fpsimd_flush_thread_vl(enum vec_type type)
|
||||
void fpsimd_flush_thread(void)
|
||||
{
|
||||
void *sve_state = NULL;
|
||||
void *za_state = NULL;
|
||||
void *sme_state = NULL;
|
||||
|
||||
if (!system_supports_fpsimd())
|
||||
return;
|
||||
@ -1634,8 +1644,8 @@ void fpsimd_flush_thread(void)
|
||||
clear_thread_flag(TIF_SME);
|
||||
|
||||
/* Defer kfree() while in atomic context */
|
||||
za_state = current->thread.za_state;
|
||||
current->thread.za_state = NULL;
|
||||
sme_state = current->thread.sme_state;
|
||||
current->thread.sme_state = NULL;
|
||||
|
||||
fpsimd_flush_thread_vl(ARM64_VEC_SME);
|
||||
current->thread.svcr = 0;
|
||||
@ -1645,7 +1655,7 @@ void fpsimd_flush_thread(void)
|
||||
|
||||
put_cpu_fpsimd_context();
|
||||
kfree(sve_state);
|
||||
kfree(za_state);
|
||||
kfree(sme_state);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1711,7 +1721,7 @@ static void fpsimd_bind_task_to_cpu(void)
|
||||
WARN_ON(!system_supports_fpsimd());
|
||||
last->st = ¤t->thread.uw.fpsimd_state;
|
||||
last->sve_state = current->thread.sve_state;
|
||||
last->za_state = current->thread.za_state;
|
||||
last->sme_state = current->thread.sme_state;
|
||||
last->sve_vl = task_get_sve_vl(current);
|
||||
last->sme_vl = task_get_sme_vl(current);
|
||||
last->svcr = ¤t->thread.svcr;
|
||||
|
@ -60,6 +60,89 @@ int ftrace_regs_query_register_offset(const char *name)
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned long ftrace_call_adjust(unsigned long addr)
|
||||
{
|
||||
/*
|
||||
* When using mcount, addr is the address of the mcount call
|
||||
* instruction, and no adjustment is necessary.
|
||||
*/
|
||||
if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_ARGS))
|
||||
return addr;
|
||||
|
||||
/*
|
||||
* When using patchable-function-entry without pre-function NOPS, addr
|
||||
* is the address of the first NOP after the function entry point.
|
||||
*
|
||||
* The compiler has either generated:
|
||||
*
|
||||
* addr+00: func: NOP // To be patched to MOV X9, LR
|
||||
* addr+04: NOP // To be patched to BL <caller>
|
||||
*
|
||||
* Or:
|
||||
*
|
||||
* addr-04: BTI C
|
||||
* addr+00: func: NOP // To be patched to MOV X9, LR
|
||||
* addr+04: NOP // To be patched to BL <caller>
|
||||
*
|
||||
* We must adjust addr to the address of the NOP which will be patched
|
||||
* to `BL <caller>`, which is at `addr + 4` bytes in either case.
|
||||
*
|
||||
*/
|
||||
if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
|
||||
return addr + AARCH64_INSN_SIZE;
|
||||
|
||||
/*
|
||||
* When using patchable-function-entry with pre-function NOPs, addr is
|
||||
* the address of the first pre-function NOP.
|
||||
*
|
||||
* Starting from an 8-byte aligned base, the compiler has either
|
||||
* generated:
|
||||
*
|
||||
* addr+00: NOP // Literal (first 32 bits)
|
||||
* addr+04: NOP // Literal (last 32 bits)
|
||||
* addr+08: func: NOP // To be patched to MOV X9, LR
|
||||
* addr+12: NOP // To be patched to BL <caller>
|
||||
*
|
||||
* Or:
|
||||
*
|
||||
* addr+00: NOP // Literal (first 32 bits)
|
||||
* addr+04: NOP // Literal (last 32 bits)
|
||||
* addr+08: func: BTI C
|
||||
* addr+12: NOP // To be patched to MOV X9, LR
|
||||
* addr+16: NOP // To be patched to BL <caller>
|
||||
*
|
||||
* We must adjust addr to the address of the NOP which will be patched
|
||||
* to `BL <caller>`, which is at either addr+12 or addr+16 depending on
|
||||
* whether there is a BTI.
|
||||
*/
|
||||
|
||||
if (!IS_ALIGNED(addr, sizeof(unsigned long))) {
|
||||
WARN_RATELIMIT(1, "Misaligned patch-site %pS\n",
|
||||
(void *)(addr + 8));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Skip the NOPs placed before the function entry point */
|
||||
addr += 2 * AARCH64_INSN_SIZE;
|
||||
|
||||
/* Skip any BTI */
|
||||
if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) {
|
||||
u32 insn = le32_to_cpu(*(__le32 *)addr);
|
||||
|
||||
if (aarch64_insn_is_bti(insn)) {
|
||||
addr += AARCH64_INSN_SIZE;
|
||||
} else if (insn != aarch64_insn_gen_nop()) {
|
||||
WARN_RATELIMIT(1, "unexpected insn in patch-site %pS: 0x%08x\n",
|
||||
(void *)addr, insn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip the first NOP after function entry */
|
||||
addr += AARCH64_INSN_SIZE;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace a single instruction, which may be a branch or NOP.
|
||||
* If @validate == true, a replaced instruction is checked against 'old'.
|
||||
@ -98,6 +181,13 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
|
||||
unsigned long pc;
|
||||
u32 new;
|
||||
|
||||
/*
|
||||
* When using CALL_OPS, the function to call is associated with the
|
||||
* call site, and we don't have a global function pointer to update.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
|
||||
return 0;
|
||||
|
||||
pc = (unsigned long)ftrace_call;
|
||||
new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func,
|
||||
AARCH64_INSN_BRANCH_LINK);
|
||||
@ -176,6 +266,44 @@ static bool ftrace_find_callable_addr(struct dyn_ftrace *rec,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
static const struct ftrace_ops *arm64_rec_get_ops(struct dyn_ftrace *rec)
|
||||
{
|
||||
const struct ftrace_ops *ops = NULL;
|
||||
|
||||
if (rec->flags & FTRACE_FL_CALL_OPS_EN) {
|
||||
ops = ftrace_find_unique_ops(rec);
|
||||
WARN_ON_ONCE(!ops);
|
||||
}
|
||||
|
||||
if (!ops)
|
||||
ops = &ftrace_list_ops;
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
static int ftrace_rec_set_ops(const struct dyn_ftrace *rec,
|
||||
const struct ftrace_ops *ops)
|
||||
{
|
||||
unsigned long literal = ALIGN_DOWN(rec->ip - 12, 8);
|
||||
return aarch64_insn_write_literal_u64((void *)literal,
|
||||
(unsigned long)ops);
|
||||
}
|
||||
|
||||
static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec)
|
||||
{
|
||||
return ftrace_rec_set_ops(rec, &ftrace_nop_ops);
|
||||
}
|
||||
|
||||
static int ftrace_rec_update_ops(struct dyn_ftrace *rec)
|
||||
{
|
||||
return ftrace_rec_set_ops(rec, arm64_rec_get_ops(rec));
|
||||
}
|
||||
#else
|
||||
static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec) { return 0; }
|
||||
static int ftrace_rec_update_ops(struct dyn_ftrace *rec) { return 0; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Turn on the call to ftrace_caller() in instrumented function
|
||||
*/
|
||||
@ -183,6 +311,11 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
{
|
||||
unsigned long pc = rec->ip;
|
||||
u32 old, new;
|
||||
int ret;
|
||||
|
||||
ret = ftrace_rec_update_ops(rec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ftrace_find_callable_addr(rec, NULL, &addr))
|
||||
return -EINVAL;
|
||||
@ -193,6 +326,19 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
return ftrace_modify_code(pc, old, new, true);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
||||
unsigned long addr)
|
||||
{
|
||||
if (WARN_ON_ONCE(old_addr != (unsigned long)ftrace_caller))
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(addr != (unsigned long)ftrace_caller))
|
||||
return -EINVAL;
|
||||
|
||||
return ftrace_rec_update_ops(rec);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
||||
/*
|
||||
* The compiler has inserted two NOPs before the regular function prologue.
|
||||
@ -209,7 +355,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
* | NOP | MOV X9, LR | MOV X9, LR |
|
||||
* | NOP | NOP | BL <entry> |
|
||||
*
|
||||
* The LR value will be recovered by ftrace_regs_entry, and restored into LR
|
||||
* The LR value will be recovered by ftrace_caller, and restored into LR
|
||||
* before returning to the regular function prologue. When a function is not
|
||||
* being traced, the MOV is not harmful given x9 is not live per the AAPCS.
|
||||
*
|
||||
@ -220,6 +366,11 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
|
||||
{
|
||||
unsigned long pc = rec->ip - AARCH64_INSN_SIZE;
|
||||
u32 old, new;
|
||||
int ret;
|
||||
|
||||
ret = ftrace_rec_set_nop_ops(rec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
old = aarch64_insn_gen_nop();
|
||||
new = aarch64_insn_gen_move_reg(AARCH64_INSN_REG_9,
|
||||
@ -237,9 +388,14 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
|
||||
{
|
||||
unsigned long pc = rec->ip;
|
||||
u32 old = 0, new;
|
||||
int ret;
|
||||
|
||||
new = aarch64_insn_gen_nop();
|
||||
|
||||
ret = ftrace_rec_set_nop_ops(rec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* When using mcount, callsites in modules may have been initalized to
|
||||
* call an arbitrary module PLT (which redirects to the _mcount stub)
|
||||
|
@ -70,13 +70,14 @@
|
||||
|
||||
__EFI_PE_HEADER
|
||||
|
||||
__INIT
|
||||
.section ".idmap.text","awx"
|
||||
|
||||
/*
|
||||
* The following callee saved general purpose registers are used on the
|
||||
* primary lowlevel boot path:
|
||||
*
|
||||
* Register Scope Purpose
|
||||
* x19 primary_entry() .. start_kernel() whether we entered with the MMU on
|
||||
* x20 primary_entry() .. __primary_switch() CPU boot mode
|
||||
* x21 primary_entry() .. start_kernel() FDT pointer passed at boot in x0
|
||||
* x22 create_idmap() .. start_kernel() ID map VA of the DT blob
|
||||
@ -86,10 +87,22 @@
|
||||
* x28 create_idmap() callee preserved temp register
|
||||
*/
|
||||
SYM_CODE_START(primary_entry)
|
||||
bl record_mmu_state
|
||||
bl preserve_boot_args
|
||||
bl create_idmap
|
||||
|
||||
/*
|
||||
* If we entered with the MMU and caches on, clean the ID mapped part
|
||||
* of the primary boot code to the PoC so we can safely execute it with
|
||||
* the MMU off.
|
||||
*/
|
||||
cbz x19, 0f
|
||||
adrp x0, __idmap_text_start
|
||||
adr_l x1, __idmap_text_end
|
||||
bl dcache_clean_poc
|
||||
0: mov x0, x19
|
||||
bl init_kernel_el // w0=cpu_boot_mode
|
||||
mov x20, x0
|
||||
bl create_idmap
|
||||
|
||||
/*
|
||||
* The following calls CPU setup code, see arch/arm64/mm/proc.S for
|
||||
@ -109,6 +122,40 @@ SYM_CODE_START(primary_entry)
|
||||
b __primary_switch
|
||||
SYM_CODE_END(primary_entry)
|
||||
|
||||
__INIT
|
||||
SYM_CODE_START_LOCAL(record_mmu_state)
|
||||
mrs x19, CurrentEL
|
||||
cmp x19, #CurrentEL_EL2
|
||||
mrs x19, sctlr_el1
|
||||
b.ne 0f
|
||||
mrs x19, sctlr_el2
|
||||
0:
|
||||
CPU_LE( tbnz x19, #SCTLR_ELx_EE_SHIFT, 1f )
|
||||
CPU_BE( tbz x19, #SCTLR_ELx_EE_SHIFT, 1f )
|
||||
tst x19, #SCTLR_ELx_C // Z := (C == 0)
|
||||
and x19, x19, #SCTLR_ELx_M // isolate M bit
|
||||
csel x19, xzr, x19, eq // clear x19 if Z
|
||||
ret
|
||||
|
||||
/*
|
||||
* Set the correct endianness early so all memory accesses issued
|
||||
* before init_kernel_el() occur in the correct byte order. Note that
|
||||
* this means the MMU must be disabled, or the active ID map will end
|
||||
* up getting interpreted with the wrong byte order.
|
||||
*/
|
||||
1: eor x19, x19, #SCTLR_ELx_EE
|
||||
bic x19, x19, #SCTLR_ELx_M
|
||||
b.ne 2f
|
||||
pre_disable_mmu_workaround
|
||||
msr sctlr_el2, x19
|
||||
b 3f
|
||||
pre_disable_mmu_workaround
|
||||
2: msr sctlr_el1, x19
|
||||
3: isb
|
||||
mov x19, xzr
|
||||
ret
|
||||
SYM_CODE_END(record_mmu_state)
|
||||
|
||||
/*
|
||||
* Preserve the arguments passed by the bootloader in x0 .. x3
|
||||
*/
|
||||
@ -119,11 +166,14 @@ SYM_CODE_START_LOCAL(preserve_boot_args)
|
||||
stp x21, x1, [x0] // x0 .. x3 at kernel entry
|
||||
stp x2, x3, [x0, #16]
|
||||
|
||||
cbnz x19, 0f // skip cache invalidation if MMU is on
|
||||
dmb sy // needed before dc ivac with
|
||||
// MMU off
|
||||
|
||||
add x1, x0, #0x20 // 4 x 8 bytes
|
||||
b dcache_inval_poc // tail call
|
||||
0: str_l x19, mmu_enabled_at_boot, x0
|
||||
ret
|
||||
SYM_CODE_END(preserve_boot_args)
|
||||
|
||||
SYM_FUNC_START_LOCAL(clear_page_tables)
|
||||
@ -360,12 +410,13 @@ SYM_FUNC_START_LOCAL(create_idmap)
|
||||
* accesses (MMU disabled), invalidate those tables again to
|
||||
* remove any speculatively loaded cache lines.
|
||||
*/
|
||||
cbnz x19, 0f // skip cache invalidation if MMU is on
|
||||
dmb sy
|
||||
|
||||
adrp x0, init_idmap_pg_dir
|
||||
adrp x1, init_idmap_pg_end
|
||||
bl dcache_inval_poc
|
||||
ret x28
|
||||
0: ret x28
|
||||
SYM_FUNC_END(create_idmap)
|
||||
|
||||
SYM_FUNC_START_LOCAL(create_kernel_mapping)
|
||||
@ -404,7 +455,7 @@ SYM_FUNC_END(create_kernel_mapping)
|
||||
stp xzr, xzr, [sp, #S_STACKFRAME]
|
||||
add x29, sp, #S_STACKFRAME
|
||||
|
||||
scs_load \tsk
|
||||
scs_load_current
|
||||
|
||||
adr_l \tmp1, __per_cpu_offset
|
||||
ldr w\tmp2, [\tsk, #TSK_TI_CPU]
|
||||
@ -489,14 +540,17 @@ SYM_FUNC_END(__primary_switched)
|
||||
* Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in x0 if
|
||||
* booted in EL1 or EL2 respectively, with the top 32 bits containing
|
||||
* potential context flags. These flags are *not* stored in __boot_cpu_mode.
|
||||
*
|
||||
* x0: whether we are being called from the primary boot path with the MMU on
|
||||
*/
|
||||
SYM_FUNC_START(init_kernel_el)
|
||||
mrs x0, CurrentEL
|
||||
cmp x0, #CurrentEL_EL2
|
||||
mrs x1, CurrentEL
|
||||
cmp x1, #CurrentEL_EL2
|
||||
b.eq init_el2
|
||||
|
||||
SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)
|
||||
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
|
||||
pre_disable_mmu_workaround
|
||||
msr sctlr_el1, x0
|
||||
isb
|
||||
mov_q x0, INIT_PSTATE_EL1
|
||||
@ -506,6 +560,14 @@ SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)
|
||||
eret
|
||||
|
||||
SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
|
||||
msr elr_el2, lr
|
||||
|
||||
// clean all HYP code to the PoC if we booted at EL2 with the MMU on
|
||||
cbz x0, 0f
|
||||
adrp x0, __hyp_idmap_text_start
|
||||
adr_l x1, __hyp_text_end
|
||||
bl dcache_clean_poc
|
||||
0:
|
||||
mov_q x0, HCR_HOST_NVHE_FLAGS
|
||||
msr hcr_el2, x0
|
||||
isb
|
||||
@ -529,38 +591,27 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
|
||||
cbz x0, 1f
|
||||
|
||||
/* Set a sane SCTLR_EL1, the VHE way */
|
||||
pre_disable_mmu_workaround
|
||||
msr_s SYS_SCTLR_EL12, x1
|
||||
mov x2, #BOOT_CPU_FLAG_E2H
|
||||
b 2f
|
||||
|
||||
1:
|
||||
pre_disable_mmu_workaround
|
||||
msr sctlr_el1, x1
|
||||
mov x2, xzr
|
||||
2:
|
||||
msr elr_el2, lr
|
||||
mov w0, #BOOT_CPU_MODE_EL2
|
||||
orr x0, x0, x2
|
||||
eret
|
||||
SYM_FUNC_END(init_kernel_el)
|
||||
|
||||
/*
|
||||
* Sets the __boot_cpu_mode flag depending on the CPU boot mode passed
|
||||
* in w0. See arch/arm64/include/asm/virt.h for more info.
|
||||
*/
|
||||
SYM_FUNC_START_LOCAL(set_cpu_boot_mode_flag)
|
||||
adr_l x1, __boot_cpu_mode
|
||||
cmp w0, #BOOT_CPU_MODE_EL2
|
||||
b.ne 1f
|
||||
add x1, x1, #4
|
||||
1: str w0, [x1] // Save CPU boot mode
|
||||
ret
|
||||
SYM_FUNC_END(set_cpu_boot_mode_flag)
|
||||
|
||||
/*
|
||||
* This provides a "holding pen" for platforms to hold all secondary
|
||||
* cores are held until we're ready for them to initialise.
|
||||
*/
|
||||
SYM_FUNC_START(secondary_holding_pen)
|
||||
mov x0, xzr
|
||||
bl init_kernel_el // w0=cpu_boot_mode
|
||||
mrs x2, mpidr_el1
|
||||
mov_q x1, MPIDR_HWID_BITMASK
|
||||
@ -578,6 +629,7 @@ SYM_FUNC_END(secondary_holding_pen)
|
||||
* be used where CPUs are brought online dynamically by the kernel.
|
||||
*/
|
||||
SYM_FUNC_START(secondary_entry)
|
||||
mov x0, xzr
|
||||
bl init_kernel_el // w0=cpu_boot_mode
|
||||
b secondary_startup
|
||||
SYM_FUNC_END(secondary_entry)
|
||||
@ -587,7 +639,6 @@ SYM_FUNC_START_LOCAL(secondary_startup)
|
||||
* Common entry point for secondary CPUs.
|
||||
*/
|
||||
mov x20, x0 // preserve boot mode
|
||||
bl finalise_el2
|
||||
bl __cpu_secondary_check52bitva
|
||||
#if VA_BITS > 48
|
||||
ldr_l x0, vabits_actual
|
||||
@ -600,9 +651,14 @@ SYM_FUNC_START_LOCAL(secondary_startup)
|
||||
br x8
|
||||
SYM_FUNC_END(secondary_startup)
|
||||
|
||||
.text
|
||||
SYM_FUNC_START_LOCAL(__secondary_switched)
|
||||
mov x0, x20
|
||||
bl set_cpu_boot_mode_flag
|
||||
|
||||
mov x0, x20
|
||||
bl finalise_el2
|
||||
|
||||
str_l xzr, __early_cpu_boot_status, x3
|
||||
adr_l x5, vectors
|
||||
msr vbar_el1, x5
|
||||
@ -628,6 +684,19 @@ SYM_FUNC_START_LOCAL(__secondary_too_slow)
|
||||
b __secondary_too_slow
|
||||
SYM_FUNC_END(__secondary_too_slow)
|
||||
|
||||
/*
|
||||
* Sets the __boot_cpu_mode flag depending on the CPU boot mode passed
|
||||
* in w0. See arch/arm64/include/asm/virt.h for more info.
|
||||
*/
|
||||
SYM_FUNC_START_LOCAL(set_cpu_boot_mode_flag)
|
||||
adr_l x1, __boot_cpu_mode
|
||||
cmp w0, #BOOT_CPU_MODE_EL2
|
||||
b.ne 1f
|
||||
add x1, x1, #4
|
||||
1: str w0, [x1] // Save CPU boot mode
|
||||
ret
|
||||
SYM_FUNC_END(set_cpu_boot_mode_flag)
|
||||
|
||||
/*
|
||||
* The booting CPU updates the failed status @__early_cpu_boot_status,
|
||||
* with MMU turned off.
|
||||
@ -659,6 +728,7 @@ SYM_FUNC_END(__secondary_too_slow)
|
||||
* Checks if the selected granule size is supported by the CPU.
|
||||
* If it isn't, park the CPU
|
||||
*/
|
||||
.section ".idmap.text","awx"
|
||||
SYM_FUNC_START(__enable_mmu)
|
||||
mrs x3, ID_AA64MMFR0_EL1
|
||||
ubfx x3, x3, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4
|
||||
|
@ -132,6 +132,13 @@ SYM_CODE_START_LOCAL(__finalise_el2)
|
||||
orr x0, x0, SMCR_ELx_FA64_MASK
|
||||
.Lskip_sme_fa64:
|
||||
|
||||
// ZT0 available?
|
||||
mrs_s x1, SYS_ID_AA64SMFR0_EL1
|
||||
__check_override id_aa64smfr0 ID_AA64SMFR0_EL1_SMEver_SHIFT 4 .Linit_sme_zt0 .Lskip_sme_zt0
|
||||
.Linit_sme_zt0:
|
||||
orr x0, x0, SMCR_ELx_EZT0_MASK
|
||||
.Lskip_sme_zt0:
|
||||
|
||||
orr x0, x0, #SMCR_ELx_LEN_MASK // Enable full SME vector
|
||||
msr_s SYS_SMCR_EL2, x0 // length for EL1.
|
||||
|
||||
|
@ -131,6 +131,7 @@ static const struct ftr_set_desc smfr0 __initconst = {
|
||||
.name = "id_aa64smfr0",
|
||||
.override = &id_aa64smfr0_override,
|
||||
.fields = {
|
||||
FIELD("smever", ID_AA64SMFR0_EL1_SMEver_SHIFT, NULL),
|
||||
/* FA64 is a one bit field... :-/ */
|
||||
{ "fa64", ID_AA64SMFR0_EL1_FA64_SHIFT, 1, },
|
||||
{}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#error This file should only be included in vmlinux.lds.S
|
||||
#endif
|
||||
|
||||
PROVIDE(__efistub_primary_entry_offset = primary_entry - _text);
|
||||
PROVIDE(__efistub_primary_entry = primary_entry);
|
||||
|
||||
/*
|
||||
* The EFI stub has its own symbol namespace prefixed by __efistub_, to
|
||||
@ -21,10 +21,11 @@ PROVIDE(__efistub_primary_entry_offset = primary_entry - _text);
|
||||
* linked at. The routines below are all implemented in assembler in a
|
||||
* position independent manner
|
||||
*/
|
||||
PROVIDE(__efistub_dcache_clean_poc = __pi_dcache_clean_poc);
|
||||
PROVIDE(__efistub_caches_clean_inval_pou = __pi_caches_clean_inval_pou);
|
||||
|
||||
PROVIDE(__efistub__text = _text);
|
||||
PROVIDE(__efistub__end = _end);
|
||||
PROVIDE(__efistub___inittext_end = __inittext_end);
|
||||
PROVIDE(__efistub__edata = _edata);
|
||||
PROVIDE(__efistub_screen_info = screen_info);
|
||||
PROVIDE(__efistub__ctype = _ctype);
|
||||
@ -67,9 +68,7 @@ KVM_NVHE_ALIAS(__hyp_stub_vectors);
|
||||
KVM_NVHE_ALIAS(vgic_v2_cpuif_trap);
|
||||
KVM_NVHE_ALIAS(vgic_v3_cpuif_trap);
|
||||
|
||||
/* Static key checked in pmr_sync(). */
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
KVM_NVHE_ALIAS(gic_pmr_sync);
|
||||
/* Static key checked in GIC_PRIO_IRQOFF. */
|
||||
KVM_NVHE_ALIAS(gic_nonsecure_priorities);
|
||||
#endif
|
||||
|
@ -130,7 +130,8 @@ struct eh_frame {
|
||||
|
||||
static int noinstr scs_handle_fde_frame(const struct eh_frame *frame,
|
||||
bool fde_has_augmentation_data,
|
||||
int code_alignment_factor)
|
||||
int code_alignment_factor,
|
||||
bool dry_run)
|
||||
{
|
||||
int size = frame->size - offsetof(struct eh_frame, opcodes) + 4;
|
||||
u64 loc = (u64)offset_to_ptr(&frame->initial_loc);
|
||||
@ -184,7 +185,8 @@ static int noinstr scs_handle_fde_frame(const struct eh_frame *frame,
|
||||
break;
|
||||
|
||||
case DW_CFA_negate_ra_state:
|
||||
scs_patch_loc(loc - 4);
|
||||
if (!dry_run)
|
||||
scs_patch_loc(loc - 4);
|
||||
break;
|
||||
|
||||
case 0x40 ... 0x7f:
|
||||
@ -235,9 +237,12 @@ int noinstr scs_patch(const u8 eh_frame[], int size)
|
||||
} else {
|
||||
ret = scs_handle_fde_frame(frame,
|
||||
fde_has_augmentation_data,
|
||||
code_alignment_factor);
|
||||
code_alignment_factor,
|
||||
true);
|
||||
if (ret)
|
||||
return ret;
|
||||
scs_handle_fde_frame(frame, fde_has_augmentation_data,
|
||||
code_alignment_factor, false);
|
||||
}
|
||||
|
||||
p += sizeof(frame->size) + frame->size;
|
||||
|
@ -88,6 +88,23 @@ int __kprobes aarch64_insn_write(void *addr, u32 insn)
|
||||
return __aarch64_insn_write(addr, cpu_to_le32(insn));
|
||||
}
|
||||
|
||||
noinstr int aarch64_insn_write_literal_u64(void *addr, u64 val)
|
||||
{
|
||||
u64 *waddr;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
raw_spin_lock_irqsave(&patch_lock, flags);
|
||||
waddr = patch_map(addr, FIX_TEXT_POKE0);
|
||||
|
||||
ret = copy_to_kernel_nofault(waddr, &val, sizeof(val));
|
||||
|
||||
patch_unmap(FIX_TEXT_POKE0);
|
||||
raw_spin_unlock_irqrestore(&patch_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __kprobes aarch64_insn_patch_text_nosync(void *addr, u32 insn)
|
||||
{
|
||||
u32 *tp = addr;
|
||||
|
@ -387,10 +387,6 @@ int __init arch_populate_kprobe_blacklist(void)
|
||||
(unsigned long)__irqentry_text_end);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = kprobe_add_area_blacklist((unsigned long)__idmap_text_start,
|
||||
(unsigned long)__idmap_text_end);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = kprobe_add_area_blacklist((unsigned long)__hyp_text_start,
|
||||
(unsigned long)__hyp_text_end);
|
||||
if (ret || is_kernel_in_hyp_mode())
|
||||
|
@ -307,27 +307,28 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
|
||||
|
||||
/*
|
||||
* In the unlikely event that we create a new thread with ZA
|
||||
* enabled we should retain the ZA state so duplicate it here.
|
||||
* This may be shortly freed if we exec() or if CLONE_SETTLS
|
||||
* but it's simpler to do it here. To avoid confusing the rest
|
||||
* of the code ensure that we have a sve_state allocated
|
||||
* whenever za_state is allocated.
|
||||
* enabled we should retain the ZA and ZT state so duplicate
|
||||
* it here. This may be shortly freed if we exec() or if
|
||||
* CLONE_SETTLS but it's simpler to do it here. To avoid
|
||||
* confusing the rest of the code ensure that we have a
|
||||
* sve_state allocated whenever sme_state is allocated.
|
||||
*/
|
||||
if (thread_za_enabled(&src->thread)) {
|
||||
dst->thread.sve_state = kzalloc(sve_state_size(src),
|
||||
GFP_KERNEL);
|
||||
if (!dst->thread.sve_state)
|
||||
return -ENOMEM;
|
||||
dst->thread.za_state = kmemdup(src->thread.za_state,
|
||||
za_state_size(src),
|
||||
GFP_KERNEL);
|
||||
if (!dst->thread.za_state) {
|
||||
|
||||
dst->thread.sme_state = kmemdup(src->thread.sme_state,
|
||||
sme_state_size(src),
|
||||
GFP_KERNEL);
|
||||
if (!dst->thread.sme_state) {
|
||||
kfree(dst->thread.sve_state);
|
||||
dst->thread.sve_state = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
dst->thread.za_state = NULL;
|
||||
dst->thread.sme_state = NULL;
|
||||
clear_tsk_thread_flag(dst, TIF_SME);
|
||||
}
|
||||
|
||||
|
@ -683,7 +683,7 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset,
|
||||
unsigned long tls[2];
|
||||
|
||||
tls[0] = target->thread.uw.tp_value;
|
||||
if (system_supports_sme())
|
||||
if (system_supports_tpidr2())
|
||||
tls[1] = target->thread.tpidr2_el0;
|
||||
|
||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, tls, 0, count);
|
||||
@ -691,7 +691,7 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset,
|
||||
return ret;
|
||||
|
||||
target->thread.uw.tp_value = tls[0];
|
||||
if (system_supports_sme())
|
||||
if (system_supports_tpidr2())
|
||||
target->thread.tpidr2_el0 = tls[1];
|
||||
|
||||
return ret;
|
||||
@ -1045,7 +1045,7 @@ static int za_get(struct task_struct *target,
|
||||
if (thread_za_enabled(&target->thread)) {
|
||||
start = end;
|
||||
end = ZA_PT_SIZE(vq);
|
||||
membuf_write(&to, target->thread.za_state, end - start);
|
||||
membuf_write(&to, target->thread.sme_state, end - start);
|
||||
}
|
||||
|
||||
/* Zero any trailing padding */
|
||||
@ -1099,7 +1099,7 @@ static int za_set(struct task_struct *target,
|
||||
|
||||
/* Allocate/reinit ZA storage */
|
||||
sme_alloc(target);
|
||||
if (!target->thread.za_state) {
|
||||
if (!target->thread.sme_state) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
@ -1124,7 +1124,7 @@ static int za_set(struct task_struct *target,
|
||||
start = ZA_PT_ZA_OFFSET;
|
||||
end = ZA_PT_SIZE(vq);
|
||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||
target->thread.za_state,
|
||||
target->thread.sme_state,
|
||||
start, end);
|
||||
if (ret)
|
||||
goto out;
|
||||
@ -1138,6 +1138,51 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zt_get(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
struct membuf to)
|
||||
{
|
||||
if (!system_supports_sme2())
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If PSTATE.ZA is not set then ZT will be zeroed when it is
|
||||
* enabled so report the current register value as zero.
|
||||
*/
|
||||
if (thread_za_enabled(&target->thread))
|
||||
membuf_write(&to, thread_zt_state(&target->thread),
|
||||
ZT_SIG_REG_BYTES);
|
||||
else
|
||||
membuf_zero(&to, ZT_SIG_REG_BYTES);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zt_set(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
const void *kbuf, const void __user *ubuf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!system_supports_sme2())
|
||||
return -EINVAL;
|
||||
|
||||
if (!thread_za_enabled(&target->thread)) {
|
||||
sme_alloc(target);
|
||||
if (!target->thread.sme_state)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||
thread_zt_state(&target->thread),
|
||||
0, ZT_SIG_REG_BYTES);
|
||||
if (ret == 0)
|
||||
target->thread.svcr |= SVCR_ZA_MASK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ARM64_SME */
|
||||
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
@ -1360,6 +1405,7 @@ enum aarch64_regset {
|
||||
#ifdef CONFIG_ARM64_SVE
|
||||
REGSET_SSVE,
|
||||
REGSET_ZA,
|
||||
REGSET_ZT,
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
REGSET_PAC_MASK,
|
||||
@ -1467,6 +1513,14 @@ static const struct user_regset aarch64_regsets[] = {
|
||||
.regset_get = za_get,
|
||||
.set = za_set,
|
||||
},
|
||||
[REGSET_ZT] = { /* SME ZT */
|
||||
.core_note_type = NT_ARM_ZT,
|
||||
.n = 1,
|
||||
.size = ZT_SIG_REG_BYTES,
|
||||
.align = sizeof(u64),
|
||||
.regset_get = zt_get,
|
||||
.set = zt_set,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
[REGSET_PAC_MASK] = {
|
||||
|
@ -58,6 +58,7 @@ static int num_standard_resources;
|
||||
static struct resource *standard_resources;
|
||||
|
||||
phys_addr_t __fdt_pointer __initdata;
|
||||
u64 mmu_enabled_at_boot __initdata;
|
||||
|
||||
/*
|
||||
* Standard memory resources
|
||||
@ -332,8 +333,12 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
|
||||
xen_early_init();
|
||||
efi_init();
|
||||
|
||||
if (!efi_enabled(EFI_BOOT) && ((u64)_text % MIN_KIMG_ALIGN) != 0)
|
||||
pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
|
||||
if (!efi_enabled(EFI_BOOT)) {
|
||||
if ((u64)_text % MIN_KIMG_ALIGN)
|
||||
pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
|
||||
WARN_TAINT(mmu_enabled_at_boot, TAINT_FIRMWARE_WORKAROUND,
|
||||
FW_BUG "Booted with MMU enabled!");
|
||||
}
|
||||
|
||||
arm64_memblock_init();
|
||||
|
||||
@ -442,3 +447,11 @@ static int __init register_arm64_panic_block(void)
|
||||
return 0;
|
||||
}
|
||||
device_initcall(register_arm64_panic_block);
|
||||
|
||||
static int __init check_mmu_enabled_at_boot(void)
|
||||
{
|
||||
if (!efi_enabled(EFI_BOOT) && mmu_enabled_at_boot)
|
||||
panic("Non-EFI boot detected with MMU and caches enabled");
|
||||
return 0;
|
||||
}
|
||||
device_initcall_sync(check_mmu_enabled_at_boot);
|
||||
|
@ -56,7 +56,9 @@ struct rt_sigframe_user_layout {
|
||||
unsigned long fpsimd_offset;
|
||||
unsigned long esr_offset;
|
||||
unsigned long sve_offset;
|
||||
unsigned long tpidr2_offset;
|
||||
unsigned long za_offset;
|
||||
unsigned long zt_offset;
|
||||
unsigned long extra_offset;
|
||||
unsigned long end_offset;
|
||||
};
|
||||
@ -220,7 +222,9 @@ static int restore_fpsimd_context(struct fpsimd_context __user *ctx)
|
||||
struct user_ctxs {
|
||||
struct fpsimd_context __user *fpsimd;
|
||||
struct sve_context __user *sve;
|
||||
struct tpidr2_context __user *tpidr2;
|
||||
struct za_context __user *za;
|
||||
struct zt_context __user *zt;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARM64_SVE
|
||||
@ -361,6 +365,32 @@ extern int preserve_sve_context(void __user *ctx);
|
||||
|
||||
#ifdef CONFIG_ARM64_SME
|
||||
|
||||
static int preserve_tpidr2_context(struct tpidr2_context __user *ctx)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
current->thread.tpidr2_el0 = read_sysreg_s(SYS_TPIDR2_EL0);
|
||||
|
||||
__put_user_error(TPIDR2_MAGIC, &ctx->head.magic, err);
|
||||
__put_user_error(sizeof(*ctx), &ctx->head.size, err);
|
||||
__put_user_error(current->thread.tpidr2_el0, &ctx->tpidr2, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int restore_tpidr2_context(struct user_ctxs *user)
|
||||
{
|
||||
u64 tpidr2_el0;
|
||||
int err = 0;
|
||||
|
||||
/* Magic and size were validated deciding to call this function */
|
||||
__get_user_error(tpidr2_el0, &user->tpidr2->tpidr2, err);
|
||||
if (!err)
|
||||
current->thread.tpidr2_el0 = tpidr2_el0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int preserve_za_context(struct za_context __user *ctx)
|
||||
{
|
||||
int err = 0;
|
||||
@ -389,7 +419,7 @@ static int preserve_za_context(struct za_context __user *ctx)
|
||||
* fpsimd_signal_preserve_current_state().
|
||||
*/
|
||||
err |= __copy_to_user((char __user *)ctx + ZA_SIG_REGS_OFFSET,
|
||||
current->thread.za_state,
|
||||
current->thread.sme_state,
|
||||
ZA_SIG_REGS_SIZE(vq));
|
||||
}
|
||||
|
||||
@ -420,7 +450,7 @@ static int restore_za_context(struct user_ctxs *user)
|
||||
|
||||
/*
|
||||
* Careful: we are about __copy_from_user() directly into
|
||||
* thread.za_state with preemption enabled, so protection is
|
||||
* thread.sme_state with preemption enabled, so protection is
|
||||
* needed to prevent a racing context switch from writing stale
|
||||
* registers back over the new data.
|
||||
*/
|
||||
@ -429,13 +459,13 @@ static int restore_za_context(struct user_ctxs *user)
|
||||
/* From now, fpsimd_thread_switch() won't touch thread.sve_state */
|
||||
|
||||
sme_alloc(current);
|
||||
if (!current->thread.za_state) {
|
||||
if (!current->thread.sme_state) {
|
||||
current->thread.svcr &= ~SVCR_ZA_MASK;
|
||||
clear_thread_flag(TIF_SME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = __copy_from_user(current->thread.za_state,
|
||||
err = __copy_from_user(current->thread.sme_state,
|
||||
(char __user const *)user->za +
|
||||
ZA_SIG_REGS_OFFSET,
|
||||
ZA_SIG_REGS_SIZE(vq));
|
||||
@ -447,11 +477,83 @@ static int restore_za_context(struct user_ctxs *user)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int preserve_zt_context(struct zt_context __user *ctx)
|
||||
{
|
||||
int err = 0;
|
||||
u16 reserved[ARRAY_SIZE(ctx->__reserved)];
|
||||
|
||||
if (WARN_ON(!thread_za_enabled(¤t->thread)))
|
||||
return -EINVAL;
|
||||
|
||||
memset(reserved, 0, sizeof(reserved));
|
||||
|
||||
__put_user_error(ZT_MAGIC, &ctx->head.magic, err);
|
||||
__put_user_error(round_up(ZT_SIG_CONTEXT_SIZE(1), 16),
|
||||
&ctx->head.size, err);
|
||||
__put_user_error(1, &ctx->nregs, err);
|
||||
BUILD_BUG_ON(sizeof(ctx->__reserved) != sizeof(reserved));
|
||||
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
|
||||
|
||||
/*
|
||||
* This assumes that the ZT state has already been saved to
|
||||
* the task struct by calling the function
|
||||
* fpsimd_signal_preserve_current_state().
|
||||
*/
|
||||
err |= __copy_to_user((char __user *)ctx + ZT_SIG_REGS_OFFSET,
|
||||
thread_zt_state(¤t->thread),
|
||||
ZT_SIG_REGS_SIZE(1));
|
||||
|
||||
return err ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int restore_zt_context(struct user_ctxs *user)
|
||||
{
|
||||
int err;
|
||||
struct zt_context zt;
|
||||
|
||||
/* ZA must be restored first for this check to be valid */
|
||||
if (!thread_za_enabled(¤t->thread))
|
||||
return -EINVAL;
|
||||
|
||||
if (__copy_from_user(&zt, user->zt, sizeof(zt)))
|
||||
return -EFAULT;
|
||||
|
||||
if (zt.nregs != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (zt.head.size != ZT_SIG_CONTEXT_SIZE(zt.nregs))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Careful: we are about __copy_from_user() directly into
|
||||
* thread.zt_state with preemption enabled, so protection is
|
||||
* needed to prevent a racing context switch from writing stale
|
||||
* registers back over the new data.
|
||||
*/
|
||||
|
||||
fpsimd_flush_task_state(current);
|
||||
/* From now, fpsimd_thread_switch() won't touch ZT in thread state */
|
||||
|
||||
err = __copy_from_user(thread_zt_state(¤t->thread),
|
||||
(char __user const *)user->zt +
|
||||
ZT_SIG_REGS_OFFSET,
|
||||
ZT_SIG_REGS_SIZE(1));
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* ! CONFIG_ARM64_SME */
|
||||
|
||||
/* Turn any non-optimised out attempts to use these into a link error: */
|
||||
extern int preserve_tpidr2_context(void __user *ctx);
|
||||
extern int restore_tpidr2_context(struct user_ctxs *user);
|
||||
extern int preserve_za_context(void __user *ctx);
|
||||
extern int restore_za_context(struct user_ctxs *user);
|
||||
extern int preserve_zt_context(void __user *ctx);
|
||||
extern int restore_zt_context(struct user_ctxs *user);
|
||||
|
||||
#endif /* ! CONFIG_ARM64_SME */
|
||||
|
||||
@ -468,7 +570,9 @@ static int parse_user_sigframe(struct user_ctxs *user,
|
||||
|
||||
user->fpsimd = NULL;
|
||||
user->sve = NULL;
|
||||
user->tpidr2 = NULL;
|
||||
user->za = NULL;
|
||||
user->zt = NULL;
|
||||
|
||||
if (!IS_ALIGNED((unsigned long)base, 16))
|
||||
goto invalid;
|
||||
@ -534,6 +638,19 @@ static int parse_user_sigframe(struct user_ctxs *user,
|
||||
user->sve = (struct sve_context __user *)head;
|
||||
break;
|
||||
|
||||
case TPIDR2_MAGIC:
|
||||
if (!system_supports_sme())
|
||||
goto invalid;
|
||||
|
||||
if (user->tpidr2)
|
||||
goto invalid;
|
||||
|
||||
if (size != sizeof(*user->tpidr2))
|
||||
goto invalid;
|
||||
|
||||
user->tpidr2 = (struct tpidr2_context __user *)head;
|
||||
break;
|
||||
|
||||
case ZA_MAGIC:
|
||||
if (!system_supports_sme())
|
||||
goto invalid;
|
||||
@ -547,6 +664,19 @@ static int parse_user_sigframe(struct user_ctxs *user,
|
||||
user->za = (struct za_context __user *)head;
|
||||
break;
|
||||
|
||||
case ZT_MAGIC:
|
||||
if (!system_supports_sme2())
|
||||
goto invalid;
|
||||
|
||||
if (user->zt)
|
||||
goto invalid;
|
||||
|
||||
if (size < sizeof(*user->zt))
|
||||
goto invalid;
|
||||
|
||||
user->zt = (struct zt_context __user *)head;
|
||||
break;
|
||||
|
||||
case EXTRA_MAGIC:
|
||||
if (have_extra_context)
|
||||
goto invalid;
|
||||
@ -666,9 +796,15 @@ static int restore_sigframe(struct pt_regs *regs,
|
||||
err = restore_fpsimd_context(user.fpsimd);
|
||||
}
|
||||
|
||||
if (err == 0 && system_supports_sme() && user.tpidr2)
|
||||
err = restore_tpidr2_context(&user);
|
||||
|
||||
if (err == 0 && system_supports_sme() && user.za)
|
||||
err = restore_za_context(&user);
|
||||
|
||||
if (err == 0 && system_supports_sme2() && user.zt)
|
||||
err = restore_zt_context(&user);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -760,6 +896,11 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
|
||||
else
|
||||
vl = task_get_sme_vl(current);
|
||||
|
||||
err = sigframe_alloc(user, &user->tpidr2_offset,
|
||||
sizeof(struct tpidr2_context));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (thread_za_enabled(¤t->thread))
|
||||
vq = sve_vq_from_vl(vl);
|
||||
|
||||
@ -769,6 +910,15 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (system_supports_sme2()) {
|
||||
if (add_all || thread_za_enabled(¤t->thread)) {
|
||||
err = sigframe_alloc(user, &user->zt_offset,
|
||||
ZT_SIG_CONTEXT_SIZE(1));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return sigframe_alloc_end(user);
|
||||
}
|
||||
|
||||
@ -817,6 +967,13 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
|
||||
err |= preserve_sve_context(sve_ctx);
|
||||
}
|
||||
|
||||
/* TPIDR2 if supported */
|
||||
if (system_supports_sme() && err == 0) {
|
||||
struct tpidr2_context __user *tpidr2_ctx =
|
||||
apply_user_offset(user, user->tpidr2_offset);
|
||||
err |= preserve_tpidr2_context(tpidr2_ctx);
|
||||
}
|
||||
|
||||
/* ZA state if present */
|
||||
if (system_supports_sme() && err == 0 && user->za_offset) {
|
||||
struct za_context __user *za_ctx =
|
||||
@ -824,6 +981,13 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
|
||||
err |= preserve_za_context(za_ctx);
|
||||
}
|
||||
|
||||
/* ZT state if present */
|
||||
if (system_supports_sme2() && err == 0 && user->zt_offset) {
|
||||
struct zt_context __user *zt_ctx =
|
||||
apply_user_offset(user, user->zt_offset);
|
||||
err |= preserve_zt_context(zt_ctx);
|
||||
}
|
||||
|
||||
if (err == 0 && user->extra_offset) {
|
||||
char __user *sfp = (char __user *)user->sigframe;
|
||||
char __user *userp =
|
||||
|
@ -99,8 +99,9 @@ SYM_FUNC_END(__cpu_suspend_enter)
|
||||
|
||||
.pushsection ".idmap.text", "awx"
|
||||
SYM_CODE_START(cpu_resume)
|
||||
mov x0, xzr
|
||||
bl init_kernel_el
|
||||
bl finalise_el2
|
||||
mov x19, x0 // preserve boot mode
|
||||
#if VA_BITS > 48
|
||||
ldr_l x0, vabits_actual
|
||||
#endif
|
||||
@ -116,6 +117,9 @@ SYM_CODE_END(cpu_resume)
|
||||
.popsection
|
||||
|
||||
SYM_FUNC_START(_cpu_resume)
|
||||
mov x0, x19
|
||||
bl finalise_el2
|
||||
|
||||
mrs x1, mpidr_el1
|
||||
adr_l x8, mpidr_hash // x8 = struct mpidr_hash virt address
|
||||
|
||||
|
@ -173,12 +173,8 @@ static inline void fp_user_discard(void)
|
||||
* register state to track, if this changes the KVM code will
|
||||
* need updating.
|
||||
*/
|
||||
if (system_supports_sme() && test_thread_flag(TIF_SME)) {
|
||||
u64 svcr = read_sysreg_s(SYS_SVCR);
|
||||
|
||||
if (svcr & SVCR_SM_MASK)
|
||||
sme_smstop_sm();
|
||||
}
|
||||
if (system_supports_sme())
|
||||
sme_smstop_sm();
|
||||
|
||||
if (!system_supports_sve())
|
||||
return;
|
||||
|
@ -162,10 +162,8 @@ static void dump_kernel_instr(const char *lvl, struct pt_regs *regs)
|
||||
|
||||
if (!bad)
|
||||
p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
|
||||
else {
|
||||
p += sprintf(p, "bad PC value");
|
||||
break;
|
||||
}
|
||||
else
|
||||
p += sprintf(p, i == 0 ? "(????????) " : "???????? ");
|
||||
}
|
||||
|
||||
printk("%sCode: %s\n", lvl, str);
|
||||
|
@ -93,6 +93,7 @@ jiffies = jiffies_64;
|
||||
|
||||
#ifdef CONFIG_HIBERNATION
|
||||
#define HIBERNATE_TEXT \
|
||||
ALIGN_FUNCTION(); \
|
||||
__hibernate_exit_text_start = .; \
|
||||
*(.hibernate_exit.text) \
|
||||
__hibernate_exit_text_end = .;
|
||||
@ -102,6 +103,7 @@ jiffies = jiffies_64;
|
||||
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
#define KEXEC_TEXT \
|
||||
ALIGN_FUNCTION(); \
|
||||
__relocate_new_kernel_start = .; \
|
||||
*(.kexec_relocate.text) \
|
||||
__relocate_new_kernel_end = .;
|
||||
@ -179,7 +181,6 @@ SECTIONS
|
||||
LOCK_TEXT
|
||||
KPROBES_TEXT
|
||||
HYPERVISOR_TEXT
|
||||
IDMAP_TEXT
|
||||
*(.gnu.warning)
|
||||
. = ALIGN(16);
|
||||
*(.got) /* Global offset table */
|
||||
@ -206,6 +207,7 @@ SECTIONS
|
||||
TRAMP_TEXT
|
||||
HIBERNATE_TEXT
|
||||
KEXEC_TEXT
|
||||
IDMAP_TEXT
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
}
|
||||
|
||||
@ -355,6 +357,8 @@ ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K,
|
||||
#ifdef CONFIG_HIBERNATION
|
||||
ASSERT(__hibernate_exit_text_end - __hibernate_exit_text_start <= SZ_4K,
|
||||
"Hibernate exit text is bigger than 4 KiB")
|
||||
ASSERT(__hibernate_exit_text_start == swsusp_arch_suspend_exit,
|
||||
"Hibernate exit text does not start with swsusp_arch_suspend_exit")
|
||||
#endif
|
||||
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
|
||||
ASSERT((__entry_tramp_text_end - __entry_tramp_text_start) <= 3*PAGE_SIZE,
|
||||
@ -381,4 +385,6 @@ ASSERT(swapper_pg_dir - tramp_pg_dir == TRAMP_SWAPPER_OFFSET,
|
||||
ASSERT(__relocate_new_kernel_end - __relocate_new_kernel_start <= SZ_4K,
|
||||
"kexec relocation code is bigger than 4 KiB")
|
||||
ASSERT(KEXEC_CONTROL_PAGE_SIZE >= SZ_4K, "KEXEC_CONTROL_PAGE_SIZE is broken")
|
||||
ASSERT(__relocate_new_kernel_start == arm64_relocate_new_kernel,
|
||||
"kexec control page does not start with arm64_relocate_new_kernel")
|
||||
#endif
|
||||
|
@ -143,7 +143,7 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)
|
||||
fp_state.st = &vcpu->arch.ctxt.fp_regs;
|
||||
fp_state.sve_state = vcpu->arch.sve_state;
|
||||
fp_state.sve_vl = vcpu->arch.sve_max_vl;
|
||||
fp_state.za_state = NULL;
|
||||
fp_state.sme_state = NULL;
|
||||
fp_state.svcr = &vcpu->arch.svcr;
|
||||
fp_state.fp_type = &vcpu->arch.fp_type;
|
||||
|
||||
|
@ -171,7 +171,7 @@ alternative_else
|
||||
dsb sy // Synchronize against in-flight ld/st
|
||||
isb // Prevent an early read of side-effect free ISR
|
||||
mrs x2, isr_el1
|
||||
tbnz x2, #8, 2f // ISR_EL1.A
|
||||
tbnz x2, #ISR_EL1_A_SHIFT, 2f
|
||||
ret
|
||||
nop
|
||||
2:
|
||||
|
@ -56,6 +56,7 @@ SYM_FUNC_START(caches_clean_inval_pou)
|
||||
caches_clean_inval_pou_macro
|
||||
ret
|
||||
SYM_FUNC_END(caches_clean_inval_pou)
|
||||
SYM_FUNC_ALIAS(__pi_caches_clean_inval_pou, caches_clean_inval_pou)
|
||||
|
||||
/*
|
||||
* caches_clean_inval_user_pou(start,end)
|
||||
|
@ -133,7 +133,7 @@ static phys_addr_t __init early_pgtable_alloc(int shift)
|
||||
return phys;
|
||||
}
|
||||
|
||||
static bool pgattr_change_is_safe(u64 old, u64 new)
|
||||
bool pgattr_change_is_safe(u64 old, u64 new)
|
||||
{
|
||||
/*
|
||||
* The following mapping attributes may be updated in live
|
||||
@ -142,9 +142,13 @@ static bool pgattr_change_is_safe(u64 old, u64 new)
|
||||
pteval_t mask = PTE_PXN | PTE_RDONLY | PTE_WRITE | PTE_NG;
|
||||
|
||||
/* creating or taking down mappings is always safe */
|
||||
if (old == 0 || new == 0)
|
||||
if (!pte_valid(__pte(old)) || !pte_valid(__pte(new)))
|
||||
return true;
|
||||
|
||||
/* A live entry's pfn should not change */
|
||||
if (pte_pfn(__pte(old)) != pte_pfn(__pte(new)))
|
||||
return false;
|
||||
|
||||
/* live contiguous mappings may not be manipulated at all */
|
||||
if ((old | new) & PTE_CONT)
|
||||
return false;
|
||||
|
@ -110,7 +110,6 @@ SYM_FUNC_END(cpu_do_suspend)
|
||||
*
|
||||
* x0: Address of context pointer
|
||||
*/
|
||||
.pushsection ".idmap.text", "awx"
|
||||
SYM_FUNC_START(cpu_do_resume)
|
||||
ldp x2, x3, [x0]
|
||||
ldp x4, x5, [x0, #16]
|
||||
@ -166,7 +165,6 @@ alternative_else_nop_endif
|
||||
isb
|
||||
ret
|
||||
SYM_FUNC_END(cpu_do_resume)
|
||||
.popsection
|
||||
#endif
|
||||
|
||||
.pushsection ".idmap.text", "awx"
|
||||
|
@ -28,7 +28,9 @@ HAS_GENERIC_AUTH
|
||||
HAS_GENERIC_AUTH_ARCH_QARMA3
|
||||
HAS_GENERIC_AUTH_ARCH_QARMA5
|
||||
HAS_GENERIC_AUTH_IMP_DEF
|
||||
HAS_IRQ_PRIO_MASKING
|
||||
HAS_GIC_CPUIF_SYSREGS
|
||||
HAS_GIC_PRIO_MASKING
|
||||
HAS_GIC_PRIO_RELAXED_SYNC
|
||||
HAS_LDAPR
|
||||
HAS_LSE_ATOMICS
|
||||
HAS_NO_FPSIMD
|
||||
@ -38,7 +40,6 @@ HAS_RAS_EXTN
|
||||
HAS_RNG
|
||||
HAS_SB
|
||||
HAS_STAGE2_FWB
|
||||
HAS_SYSREG_GIC_CPUIF
|
||||
HAS_TIDCP1
|
||||
HAS_TLB_RANGE
|
||||
HAS_VIRT_HOST_EXTN
|
||||
@ -50,6 +51,7 @@ MTE
|
||||
MTE_ASYMM
|
||||
SME
|
||||
SME_FA64
|
||||
SME2
|
||||
SPECTRE_V2
|
||||
SPECTRE_V3A
|
||||
SPECTRE_V4
|
||||
|
@ -689,17 +689,17 @@ EndEnum
|
||||
Enum 11:8 FPDP
|
||||
0b0000 NI
|
||||
0b0001 VFPv2
|
||||
0b0001 VFPv3
|
||||
0b0010 VFPv3
|
||||
EndEnum
|
||||
Enum 7:4 FPSP
|
||||
0b0000 NI
|
||||
0b0001 VFPv2
|
||||
0b0001 VFPv3
|
||||
0b0010 VFPv3
|
||||
EndEnum
|
||||
Enum 3:0 SIMDReg
|
||||
0b0000 NI
|
||||
0b0001 IMP_16x64
|
||||
0b0001 IMP_32x64
|
||||
0b0010 IMP_32x64
|
||||
EndEnum
|
||||
EndSysreg
|
||||
|
||||
@ -718,7 +718,7 @@ EndEnum
|
||||
Enum 23:20 SIMDHP
|
||||
0b0000 NI
|
||||
0b0001 SIMDHP
|
||||
0b0001 SIMDHP_FLOAT
|
||||
0b0010 SIMDHP_FLOAT
|
||||
EndEnum
|
||||
Enum 19:16 SIMDSP
|
||||
0b0000 NI
|
||||
@ -894,6 +894,7 @@ EndEnum
|
||||
Enum 27:24 SME
|
||||
0b0000 NI
|
||||
0b0001 IMP
|
||||
0b0010 SME2
|
||||
EndEnum
|
||||
Res0 23:20
|
||||
Enum 19:16 MPAM_frac
|
||||
@ -975,7 +976,9 @@ Enum 63 FA64
|
||||
EndEnum
|
||||
Res0 62:60
|
||||
Enum 59:56 SMEver
|
||||
0b0000 IMP
|
||||
0b0000 SME
|
||||
0b0001 SME2
|
||||
0b0010 SME2p1
|
||||
EndEnum
|
||||
Enum 55:52 I16I64
|
||||
0b0000 NI
|
||||
@ -986,7 +989,19 @@ Enum 48 F64F64
|
||||
0b0 NI
|
||||
0b1 IMP
|
||||
EndEnum
|
||||
Res0 47:40
|
||||
Enum 47:44 I16I32
|
||||
0b0000 NI
|
||||
0b0101 IMP
|
||||
EndEnum
|
||||
Enum 43 B16B16
|
||||
0b0 NI
|
||||
0b1 IMP
|
||||
EndEnum
|
||||
Enum 42 F16F16
|
||||
0b0 NI
|
||||
0b1 IMP
|
||||
EndEnum
|
||||
Res0 41:40
|
||||
Enum 39:36 I8I32
|
||||
0b0000 NI
|
||||
0b1111 IMP
|
||||
@ -999,7 +1014,10 @@ Enum 34 B16F32
|
||||
0b0 NI
|
||||
0b1 IMP
|
||||
EndEnum
|
||||
Res0 33
|
||||
Enum 33 BI32I32
|
||||
0b0 NI
|
||||
0b1 IMP
|
||||
EndEnum
|
||||
Enum 32 F32F32
|
||||
0b0 NI
|
||||
0b1 IMP
|
||||
@ -1599,7 +1617,8 @@ EndSysreg
|
||||
SysregFields SMCR_ELx
|
||||
Res0 63:32
|
||||
Field 31 FA64
|
||||
Res0 30:9
|
||||
Field 30 EZT0
|
||||
Res0 29:9
|
||||
Raz 8:4
|
||||
Field 3:0 LEN
|
||||
EndSysregFields
|
||||
@ -1981,3 +2000,18 @@ Field 23:16 LD
|
||||
Res0 15:8
|
||||
Field 7:0 LR
|
||||
EndSysreg
|
||||
|
||||
Sysreg ISR_EL1 3 0 12 1 0
|
||||
Res0 63:11
|
||||
Field 10 IS
|
||||
Field 9 FS
|
||||
Field 8 A
|
||||
Field 7 I
|
||||
Field 6 F
|
||||
Res0 5:0
|
||||
EndSysreg
|
||||
|
||||
Sysreg ICC_NMIAR1_EL1 3 0 12 9 5
|
||||
Res0 63:24
|
||||
Field 23:0 INTID
|
||||
EndSysreg
|
||||
|
@ -3,7 +3,7 @@
|
||||
# Makefile for ACPICA Core interpreter
|
||||
#
|
||||
|
||||
ccflags-y := -Os -D_LINUX -DBUILDING_ACPICA
|
||||
ccflags-y := -D_LINUX -DBUILDING_ACPICA
|
||||
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
|
||||
|
||||
# use acpi.o to put all files here into acpi.o modparam namespace
|
||||
|
@ -87,7 +87,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \
|
||||
screen_info.o efi-stub-entry.o
|
||||
|
||||
lib-$(CONFIG_ARM) += arm32-stub.o
|
||||
lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o arm64-entry.o smbios.o
|
||||
lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o smbios.o
|
||||
lib-$(CONFIG_X86) += x86-stub.o
|
||||
lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o
|
||||
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
|
||||
@ -141,7 +141,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
|
||||
#
|
||||
STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
|
||||
--prefix-symbols=__efistub_
|
||||
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS64
|
||||
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS
|
||||
|
||||
# For RISC-V, we don't need anything special other than arm64. Keep all the
|
||||
# symbols in .init section and make sure that no absolute symbols references
|
||||
|
@ -1,67 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* EFI entry point.
|
||||
*
|
||||
* Copyright (C) 2013, 2014 Red Hat, Inc.
|
||||
* Author: Mark Salter <msalter@redhat.com>
|
||||
*/
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
/*
|
||||
* The entrypoint of a arm64 bare metal image is at offset #0 of the
|
||||
* image, so this is a reasonable default for primary_entry_offset.
|
||||
* Only when the EFI stub is integrated into the core kernel, it is not
|
||||
* guaranteed that the PE/COFF header has been copied to memory too, so
|
||||
* in this case, primary_entry_offset should be overridden by the
|
||||
* linker and point to primary_entry() directly.
|
||||
*/
|
||||
.weak primary_entry_offset
|
||||
|
||||
SYM_CODE_START(efi_enter_kernel)
|
||||
/*
|
||||
* efi_pe_entry() will have copied the kernel image if necessary and we
|
||||
* end up here with device tree address in x1 and the kernel entry
|
||||
* point stored in x0. Save those values in registers which are
|
||||
* callee preserved.
|
||||
*/
|
||||
ldr w2, =primary_entry_offset
|
||||
add x19, x0, x2 // relocated Image entrypoint
|
||||
|
||||
mov x0, x1 // DTB address
|
||||
mov x1, xzr
|
||||
mov x2, xzr
|
||||
mov x3, xzr
|
||||
|
||||
/*
|
||||
* Clean the remainder of this routine to the PoC
|
||||
* so that we can safely disable the MMU and caches.
|
||||
*/
|
||||
adr x4, 1f
|
||||
dc civac, x4
|
||||
dsb sy
|
||||
|
||||
/* Turn off Dcache and MMU */
|
||||
mrs x4, CurrentEL
|
||||
cmp x4, #CurrentEL_EL2
|
||||
mrs x4, sctlr_el1
|
||||
b.ne 0f
|
||||
mrs x4, sctlr_el2
|
||||
0: bic x4, x4, #SCTLR_ELx_M
|
||||
bic x4, x4, #SCTLR_ELx_C
|
||||
b.eq 1f
|
||||
b 2f
|
||||
|
||||
.balign 32
|
||||
1: pre_disable_mmu_workaround
|
||||
msr sctlr_el2, x4
|
||||
isb
|
||||
br x19 // jump to kernel entrypoint
|
||||
|
||||
2: pre_disable_mmu_workaround
|
||||
msr sctlr_el1, x4
|
||||
isb
|
||||
br x19 // jump to kernel entrypoint
|
||||
|
||||
.org 1b + 32
|
||||
SYM_CODE_END(efi_enter_kernel)
|
@ -58,7 +58,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
efi_handle_t image_handle)
|
||||
{
|
||||
efi_status_t status;
|
||||
unsigned long kernel_size, kernel_memsize = 0;
|
||||
unsigned long kernel_size, kernel_codesize, kernel_memsize;
|
||||
u32 phys_seed = 0;
|
||||
u64 min_kimg_align = efi_get_kimg_min_align();
|
||||
|
||||
@ -93,6 +93,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
SEGMENT_ALIGN >> 10);
|
||||
|
||||
kernel_size = _edata - _text;
|
||||
kernel_codesize = __inittext_end - _text;
|
||||
kernel_memsize = kernel_size + (_end - _edata);
|
||||
*reserve_size = kernel_memsize;
|
||||
|
||||
@ -121,7 +122,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
*/
|
||||
*image_addr = (u64)_text;
|
||||
*reserve_size = 0;
|
||||
goto clean_image_to_poc;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
|
||||
@ -137,14 +138,21 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
|
||||
*image_addr = *reserve_addr;
|
||||
memcpy((void *)*image_addr, _text, kernel_size);
|
||||
|
||||
clean_image_to_poc:
|
||||
/*
|
||||
* Clean the copied Image to the PoC, and ensure it is not shadowed by
|
||||
* stale icache entries from before relocation.
|
||||
*/
|
||||
dcache_clean_poc(*image_addr, *image_addr + kernel_size);
|
||||
asm("ic ialluis");
|
||||
caches_clean_inval_pou(*image_addr, *image_addr + kernel_codesize);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
asmlinkage void primary_entry(void);
|
||||
|
||||
unsigned long primary_entry_offset(void)
|
||||
{
|
||||
/*
|
||||
* When built as part of the kernel, the EFI stub cannot branch to the
|
||||
* kernel proper via the image header, as the PE/COFF header is
|
||||
* strictly not part of the in-memory presentation of the image, only
|
||||
* of the file representation. So instead, we need to jump to the
|
||||
* actual entrypoint in the .text region of the image.
|
||||
*/
|
||||
return (char *)primary_entry - _text;
|
||||
}
|
||||
|
@ -56,6 +56,12 @@ efi_status_t check_platform_features(void)
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
|
||||
#define DCTYPE "civac"
|
||||
#else
|
||||
#define DCTYPE "cvau"
|
||||
#endif
|
||||
|
||||
void efi_cache_sync_image(unsigned long image_base,
|
||||
unsigned long alloc_size,
|
||||
unsigned long code_size)
|
||||
@ -64,13 +70,38 @@ void efi_cache_sync_image(unsigned long image_base,
|
||||
u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
|
||||
CTR_EL0_DminLine_SHIFT);
|
||||
|
||||
do {
|
||||
asm("dc civac, %0" :: "r"(image_base));
|
||||
image_base += lsize;
|
||||
alloc_size -= lsize;
|
||||
} while (alloc_size >= lsize);
|
||||
/* only perform the cache maintenance if needed for I/D coherency */
|
||||
if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) {
|
||||
do {
|
||||
asm("dc " DCTYPE ", %0" :: "r"(image_base));
|
||||
image_base += lsize;
|
||||
code_size -= lsize;
|
||||
} while (code_size >= lsize);
|
||||
}
|
||||
|
||||
asm("ic ialluis");
|
||||
dsb(ish);
|
||||
isb();
|
||||
}
|
||||
|
||||
unsigned long __weak primary_entry_offset(void)
|
||||
{
|
||||
/*
|
||||
* By default, we can invoke the kernel via the branch instruction in
|
||||
* the image header, so offset #0. This will be overridden by the EFI
|
||||
* stub build that is linked into the core kernel, as in that case, the
|
||||
* image header may not have been loaded into memory, or may be mapped
|
||||
* with non-executable permissions.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __noreturn efi_enter_kernel(unsigned long entrypoint,
|
||||
unsigned long fdt_addr,
|
||||
unsigned long fdt_size)
|
||||
{
|
||||
void (* __noreturn enter_kernel)(u64, u64, u64, u64);
|
||||
|
||||
enter_kernel = (void *)entrypoint + primary_entry_offset();
|
||||
enter_kernel(fdt_addr, 0, 0, 0);
|
||||
}
|
||||
|
@ -89,15 +89,6 @@ static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
|
||||
*/
|
||||
static DEFINE_STATIC_KEY_FALSE(supports_pseudo_nmis);
|
||||
|
||||
/*
|
||||
* Global static key controlling whether an update to PMR allowing more
|
||||
* interrupts requires to be propagated to the redistributor (DSB SY).
|
||||
* And this needs to be exported for modules to be able to enable
|
||||
* interrupts...
|
||||
*/
|
||||
DEFINE_STATIC_KEY_FALSE(gic_pmr_sync);
|
||||
EXPORT_SYMBOL(gic_pmr_sync);
|
||||
|
||||
DEFINE_STATIC_KEY_FALSE(gic_nonsecure_priorities);
|
||||
EXPORT_SYMBOL(gic_nonsecure_priorities);
|
||||
|
||||
@ -1768,16 +1759,8 @@ static void gic_enable_nmi_support(void)
|
||||
for (i = 0; i < gic_data.ppi_nr; i++)
|
||||
refcount_set(&ppi_nmi_refs[i], 0);
|
||||
|
||||
/*
|
||||
* Linux itself doesn't use 1:N distribution, so has no need to
|
||||
* set PMHE. The only reason to have it set is if EL3 requires it
|
||||
* (and we can't change it).
|
||||
*/
|
||||
if (gic_read_ctlr() & ICC_CTLR_EL1_PMHE_MASK)
|
||||
static_branch_enable(&gic_pmr_sync);
|
||||
|
||||
pr_info("Pseudo-NMIs enabled using %s ICC_PMR_EL1 synchronisation\n",
|
||||
static_branch_unlikely(&gic_pmr_sync) ? "forced" : "relaxed");
|
||||
gic_has_relaxed_pmr_sync() ? "relaxed" : "forced");
|
||||
|
||||
/*
|
||||
* How priority values are used by the GIC depends on two things:
|
||||
|
@ -54,7 +54,7 @@
|
||||
|
||||
static void gic_check_cpu_features(void)
|
||||
{
|
||||
WARN_TAINT_ONCE(this_cpu_has_cap(ARM64_HAS_SYSREG_GIC_CPUIF),
|
||||
WARN_TAINT_ONCE(this_cpu_has_cap(ARM64_HAS_GIC_CPUIF_SYSREGS),
|
||||
TAINT_CPU_OUT_OF_SPEC,
|
||||
"GICv3 system registers enabled, broken firmware!\n");
|
||||
}
|
||||
|
@ -75,12 +75,6 @@
|
||||
# define __assume_aligned(a, ...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-cold-function-attribute
|
||||
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Label-Attributes.html#index-cold-label-attribute
|
||||
*/
|
||||
#define __cold __attribute__((__cold__))
|
||||
|
||||
/*
|
||||
* Note the long name.
|
||||
*
|
||||
|
@ -79,6 +79,33 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
|
||||
/* Attributes */
|
||||
#include <linux/compiler_attributes.h>
|
||||
|
||||
#if CONFIG_FUNCTION_ALIGNMENT > 0
|
||||
#define __function_aligned __aligned(CONFIG_FUNCTION_ALIGNMENT)
|
||||
#else
|
||||
#define __function_aligned
|
||||
#endif
|
||||
|
||||
/*
|
||||
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-cold-function-attribute
|
||||
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Label-Attributes.html#index-cold-label-attribute
|
||||
*
|
||||
* When -falign-functions=N is in use, we must avoid the cold attribute as
|
||||
* contemporary versions of GCC drop the alignment for cold functions. Worse,
|
||||
* GCC can implicitly mark callees of cold functions as cold themselves, so
|
||||
* it's not sufficient to add __function_aligned here as that will not ensure
|
||||
* that callees are correctly aligned.
|
||||
*
|
||||
* See:
|
||||
*
|
||||
* https://lore.kernel.org/lkml/Y77%2FqVgvaJidFpYt@FVFF77S0Q05N
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88345#c9
|
||||
*/
|
||||
#if !defined(CONFIG_CC_IS_GCC) || (CONFIG_FUNCTION_ALIGNMENT == 0)
|
||||
#define __cold __attribute__((__cold__))
|
||||
#else
|
||||
#define __cold
|
||||
#endif
|
||||
|
||||
/* Builtins */
|
||||
|
||||
/*
|
||||
|
@ -39,6 +39,7 @@ static inline void ftrace_boot_snapshot(void) { }
|
||||
|
||||
struct ftrace_ops;
|
||||
struct ftrace_regs;
|
||||
struct dyn_ftrace;
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
/*
|
||||
@ -57,6 +58,9 @@ void arch_ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip);
|
||||
void arch_ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct ftrace_regs *fregs);
|
||||
#endif
|
||||
extern const struct ftrace_ops ftrace_nop_ops;
|
||||
extern const struct ftrace_ops ftrace_list_ops;
|
||||
struct ftrace_ops *ftrace_find_unique_ops(struct dyn_ftrace *rec);
|
||||
#endif /* CONFIG_FUNCTION_TRACER */
|
||||
|
||||
/* Main tracing buffer and events set up */
|
||||
@ -391,8 +395,6 @@ struct ftrace_func_entry {
|
||||
unsigned long direct; /* for direct lookup only */
|
||||
};
|
||||
|
||||
struct dyn_ftrace;
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||
extern int ftrace_direct_func_count;
|
||||
int register_ftrace_direct(unsigned long ip, unsigned long addr);
|
||||
@ -563,6 +565,8 @@ bool is_ftrace_trampoline(unsigned long addr);
|
||||
* IPMODIFY - the record allows for the IP address to be changed.
|
||||
* DISABLED - the record is not ready to be touched yet
|
||||
* DIRECT - there is a direct function to call
|
||||
* CALL_OPS - the record can use callsite-specific ops
|
||||
* CALL_OPS_EN - the function is set up to use callsite-specific ops
|
||||
*
|
||||
* When a new ftrace_ops is registered and wants a function to save
|
||||
* pt_regs, the rec->flags REGS is set. When the function has been
|
||||
@ -580,9 +584,11 @@ enum {
|
||||
FTRACE_FL_DISABLED = (1UL << 25),
|
||||
FTRACE_FL_DIRECT = (1UL << 24),
|
||||
FTRACE_FL_DIRECT_EN = (1UL << 23),
|
||||
FTRACE_FL_CALL_OPS = (1UL << 22),
|
||||
FTRACE_FL_CALL_OPS_EN = (1UL << 21),
|
||||
};
|
||||
|
||||
#define FTRACE_REF_MAX_SHIFT 23
|
||||
#define FTRACE_REF_MAX_SHIFT 21
|
||||
#define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1)
|
||||
|
||||
#define ftrace_rec_count(rec) ((rec)->flags & FTRACE_REF_MAX)
|
||||
@ -820,7 +826,8 @@ static inline int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
|
||||
*/
|
||||
extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
|
||||
#if defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS) || \
|
||||
defined(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS)
|
||||
/**
|
||||
* ftrace_modify_call - convert from one addr to another (no nop)
|
||||
* @rec: the call site record (e.g. mcount/fentry)
|
||||
@ -833,6 +840,9 @@ extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr);
|
||||
* what we expect it to be, and then on success of the compare,
|
||||
* it should write to the location.
|
||||
*
|
||||
* When using call ops, this is called when the associated ops change, even
|
||||
* when (addr == old_addr).
|
||||
*
|
||||
* The code segment at @rec->ip should be a caller to @old_addr
|
||||
*
|
||||
* Return must be:
|
||||
|
@ -434,6 +434,7 @@ typedef struct elf64_shdr {
|
||||
#define NT_ARM_PAC_ENABLED_KEYS 0x40a /* arm64 ptr auth enabled keys (prctl()) */
|
||||
#define NT_ARM_SSVE 0x40b /* ARM Streaming SVE registers */
|
||||
#define NT_ARM_ZA 0x40c /* ARM SME ZA registers */
|
||||
#define NT_ARM_ZT 0x40d /* ARM SME ZT registers */
|
||||
#define NT_ARC_V2 0x600 /* ARCv2 accumulator/extra registers */
|
||||
#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note */
|
||||
#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */
|
||||
|
@ -1898,7 +1898,14 @@ bool thread_group_exited(struct pid *pid)
|
||||
}
|
||||
EXPORT_SYMBOL(thread_group_exited);
|
||||
|
||||
__weak void abort(void)
|
||||
/*
|
||||
* This needs to be __function_aligned as GCC implicitly makes any
|
||||
* implementation of abort() cold and drops alignment specified by
|
||||
* -falign-functions=N.
|
||||
*
|
||||
* See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88345#c11
|
||||
*/
|
||||
__weak __function_aligned void abort(void)
|
||||
{
|
||||
BUG();
|
||||
|
||||
|
@ -42,6 +42,9 @@ config HAVE_DYNAMIC_FTRACE_WITH_REGS
|
||||
config HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||
bool
|
||||
|
||||
config HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
bool
|
||||
|
||||
config HAVE_DYNAMIC_FTRACE_WITH_ARGS
|
||||
bool
|
||||
help
|
||||
@ -257,6 +260,10 @@ config DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||
depends on DYNAMIC_FTRACE_WITH_REGS
|
||||
depends on HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||
|
||||
config DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
def_bool y
|
||||
depends on HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
|
||||
config DYNAMIC_FTRACE_WITH_ARGS
|
||||
def_bool y
|
||||
depends on DYNAMIC_FTRACE
|
||||
|
@ -125,6 +125,33 @@ struct ftrace_ops global_ops;
|
||||
void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct ftrace_regs *fregs);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||
/*
|
||||
* Stub used to invoke the list ops without requiring a separate trampoline.
|
||||
*/
|
||||
const struct ftrace_ops ftrace_list_ops = {
|
||||
.func = ftrace_ops_list_func,
|
||||
.flags = FTRACE_OPS_FL_STUB,
|
||||
};
|
||||
|
||||
static void ftrace_ops_nop_func(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op,
|
||||
struct ftrace_regs *fregs)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/*
|
||||
* Stub used when a call site is disabled. May be called transiently by threads
|
||||
* which have made it into ftrace_caller but haven't yet recovered the ops at
|
||||
* the point the call site is disabled.
|
||||
*/
|
||||
const struct ftrace_ops ftrace_nop_ops = {
|
||||
.func = ftrace_ops_nop_func,
|
||||
.flags = FTRACE_OPS_FL_STUB,
|
||||
};
|
||||
#endif
|
||||
|
||||
static inline void ftrace_ops_init(struct ftrace_ops *ops)
|
||||
{
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
@ -1814,6 +1841,18 @@ static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
|
||||
* if rec count is zero.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* If the rec has a single associated ops, and ops->func can be
|
||||
* called directly, allow the call site to call via the ops.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS) &&
|
||||
ftrace_rec_count(rec) == 1 &&
|
||||
ftrace_ops_get_func(ops) == ops->func)
|
||||
rec->flags |= FTRACE_FL_CALL_OPS;
|
||||
else
|
||||
rec->flags &= ~FTRACE_FL_CALL_OPS;
|
||||
|
||||
count++;
|
||||
|
||||
/* Must match FTRACE_UPDATE_CALLS in ftrace_modify_all_code() */
|
||||
@ -2108,8 +2147,9 @@ void ftrace_bug(int failed, struct dyn_ftrace *rec)
|
||||
struct ftrace_ops *ops = NULL;
|
||||
|
||||
pr_info("ftrace record flags: %lx\n", rec->flags);
|
||||
pr_cont(" (%ld)%s", ftrace_rec_count(rec),
|
||||
rec->flags & FTRACE_FL_REGS ? " R" : " ");
|
||||
pr_cont(" (%ld)%s%s", ftrace_rec_count(rec),
|
||||
rec->flags & FTRACE_FL_REGS ? " R" : " ",
|
||||
rec->flags & FTRACE_FL_CALL_OPS ? " O" : " ");
|
||||
if (rec->flags & FTRACE_FL_TRAMP_EN) {
|
||||
ops = ftrace_find_tramp_ops_any(rec);
|
||||
if (ops) {
|
||||
@ -2177,6 +2217,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, bool enable, bool update)
|
||||
* want the direct enabled (it will be done via the
|
||||
* direct helper). But if DIRECT_EN is set, and
|
||||
* the count is not one, we need to clear it.
|
||||
*
|
||||
*/
|
||||
if (ftrace_rec_count(rec) == 1) {
|
||||
if (!(rec->flags & FTRACE_FL_DIRECT) !=
|
||||
@ -2185,6 +2226,19 @@ static int ftrace_check_record(struct dyn_ftrace *rec, bool enable, bool update)
|
||||
} else if (rec->flags & FTRACE_FL_DIRECT_EN) {
|
||||
flag |= FTRACE_FL_DIRECT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ops calls are special, as count matters.
|
||||
* As with direct calls, they must only be enabled when count
|
||||
* is one, otherwise they'll be handled via the list ops.
|
||||
*/
|
||||
if (ftrace_rec_count(rec) == 1) {
|
||||
if (!(rec->flags & FTRACE_FL_CALL_OPS) !=
|
||||
!(rec->flags & FTRACE_FL_CALL_OPS_EN))
|
||||
flag |= FTRACE_FL_CALL_OPS;
|
||||
} else if (rec->flags & FTRACE_FL_CALL_OPS_EN) {
|
||||
flag |= FTRACE_FL_CALL_OPS;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the state of this record hasn't changed, then do nothing */
|
||||
@ -2229,6 +2283,21 @@ static int ftrace_check_record(struct dyn_ftrace *rec, bool enable, bool update)
|
||||
rec->flags &= ~FTRACE_FL_DIRECT_EN;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & FTRACE_FL_CALL_OPS) {
|
||||
if (ftrace_rec_count(rec) == 1) {
|
||||
if (rec->flags & FTRACE_FL_CALL_OPS)
|
||||
rec->flags |= FTRACE_FL_CALL_OPS_EN;
|
||||
else
|
||||
rec->flags &= ~FTRACE_FL_CALL_OPS_EN;
|
||||
} else {
|
||||
/*
|
||||
* Can only call directly if there's
|
||||
* only one set of associated ops.
|
||||
*/
|
||||
rec->flags &= ~FTRACE_FL_CALL_OPS_EN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2258,7 +2327,8 @@ static int ftrace_check_record(struct dyn_ftrace *rec, bool enable, bool update)
|
||||
* and REGS states. The _EN flags must be disabled though.
|
||||
*/
|
||||
rec->flags &= ~(FTRACE_FL_ENABLED | FTRACE_FL_TRAMP_EN |
|
||||
FTRACE_FL_REGS_EN | FTRACE_FL_DIRECT_EN);
|
||||
FTRACE_FL_REGS_EN | FTRACE_FL_DIRECT_EN |
|
||||
FTRACE_FL_CALL_OPS_EN);
|
||||
}
|
||||
|
||||
ftrace_bug_type = FTRACE_BUG_NOP;
|
||||
@ -2431,6 +2501,25 @@ ftrace_find_tramp_ops_new(struct dyn_ftrace *rec)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ftrace_ops *
|
||||
ftrace_find_unique_ops(struct dyn_ftrace *rec)
|
||||
{
|
||||
struct ftrace_ops *op, *found = NULL;
|
||||
unsigned long ip = rec->ip;
|
||||
|
||||
do_for_each_ftrace_op(op, ftrace_ops_list) {
|
||||
|
||||
if (hash_contains_ip(ip, op->func_hash)) {
|
||||
if (found)
|
||||
return NULL;
|
||||
found = op;
|
||||
}
|
||||
|
||||
} while_for_each_ftrace_op(op);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||
/* Protected by rcu_tasks for reading, and direct_mutex for writing */
|
||||
static struct ftrace_hash *direct_functions = EMPTY_HASH;
|
||||
@ -3780,11 +3869,12 @@ static int t_show(struct seq_file *m, void *v)
|
||||
if (iter->flags & FTRACE_ITER_ENABLED) {
|
||||
struct ftrace_ops *ops;
|
||||
|
||||
seq_printf(m, " (%ld)%s%s%s",
|
||||
seq_printf(m, " (%ld)%s%s%s%s",
|
||||
ftrace_rec_count(rec),
|
||||
rec->flags & FTRACE_FL_REGS ? " R" : " ",
|
||||
rec->flags & FTRACE_FL_IPMODIFY ? " I" : " ",
|
||||
rec->flags & FTRACE_FL_DIRECT ? " D" : " ");
|
||||
rec->flags & FTRACE_FL_DIRECT ? " D" : " ",
|
||||
rec->flags & FTRACE_FL_CALL_OPS ? " O" : " ");
|
||||
if (rec->flags & FTRACE_FL_TRAMP_EN) {
|
||||
ops = ftrace_find_tramp_ops_any(rec);
|
||||
if (ops) {
|
||||
@ -3800,6 +3890,15 @@ static int t_show(struct seq_file *m, void *v)
|
||||
} else {
|
||||
add_trampoline_func(m, NULL, rec);
|
||||
}
|
||||
if (rec->flags & FTRACE_FL_CALL_OPS_EN) {
|
||||
ops = ftrace_find_unique_ops(rec);
|
||||
if (ops) {
|
||||
seq_printf(m, "\tops: %pS (%pS)",
|
||||
ops, ops->func);
|
||||
} else {
|
||||
seq_puts(m, "\tops: ERROR!");
|
||||
}
|
||||
}
|
||||
if (rec->flags & FTRACE_FL_DIRECT) {
|
||||
unsigned long direct;
|
||||
|
||||
|
@ -50,6 +50,78 @@ static void sme_sigill(void)
|
||||
asm volatile(".inst 0x04bf5800" : : : "x0");
|
||||
}
|
||||
|
||||
static void sme2_sigill(void)
|
||||
{
|
||||
/* SMSTART ZA */
|
||||
asm volatile("msr S0_3_C4_C5_3, xzr" : : : );
|
||||
|
||||
/* ZERO ZT0 */
|
||||
asm volatile(".inst 0xc0480001" : : : );
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void sme2p1_sigill(void)
|
||||
{
|
||||
/* SMSTART SM */
|
||||
asm volatile("msr S0_3_C4_C3_3, xzr" : : : );
|
||||
|
||||
/* BFCLAMP { Z0.H - Z1.H }, Z0.H, Z0.H */
|
||||
asm volatile(".inst 0xc120C000" : : : );
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void smei16i32_sigill(void)
|
||||
{
|
||||
/* SMSTART */
|
||||
asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
|
||||
|
||||
/* SMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
|
||||
asm volatile(".inst 0xa0800000" : : : );
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void smebi32i32_sigill(void)
|
||||
{
|
||||
/* SMSTART */
|
||||
asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
|
||||
|
||||
/* BMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
|
||||
asm volatile(".inst 0x80800008" : : : );
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void smeb16b16_sigill(void)
|
||||
{
|
||||
/* SMSTART */
|
||||
asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
|
||||
|
||||
/* BFADD ZA.H[W0, 0], {Z0.H-Z1.H} */
|
||||
asm volatile(".inst 0xC1E41C00" : : : );
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void smef16f16_sigill(void)
|
||||
{
|
||||
/* SMSTART */
|
||||
asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
|
||||
|
||||
/* FADD ZA.H[W0, 0], { Z0.H-Z1.H } */
|
||||
asm volatile(".inst 0xc1a41C00" : : : );
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void sve_sigill(void)
|
||||
{
|
||||
/* RDVL x0, #0 */
|
||||
@ -158,6 +230,49 @@ static const struct hwcap_data {
|
||||
.sigill_fn = sme_sigill,
|
||||
.sigill_reliable = true,
|
||||
},
|
||||
{
|
||||
.name = "SME2",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
.hwcap_bit = HWCAP2_SME2,
|
||||
.cpuinfo = "sme2",
|
||||
.sigill_fn = sme2_sigill,
|
||||
.sigill_reliable = true,
|
||||
},
|
||||
{
|
||||
.name = "SME 2.1",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
.hwcap_bit = HWCAP2_SME2P1,
|
||||
.cpuinfo = "sme2p1",
|
||||
.sigill_fn = sme2p1_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME I16I32",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
.hwcap_bit = HWCAP2_SME_I16I32,
|
||||
.cpuinfo = "smei16i32",
|
||||
.sigill_fn = smei16i32_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME BI32I32",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
.hwcap_bit = HWCAP2_SME_BI32I32,
|
||||
.cpuinfo = "smebi32i32",
|
||||
.sigill_fn = smebi32i32_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME B16B16",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
.hwcap_bit = HWCAP2_SME_B16B16,
|
||||
.cpuinfo = "smeb16b16",
|
||||
.sigill_fn = smeb16b16_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME F16F16",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
.hwcap_bit = HWCAP2_SME_F16F16,
|
||||
.cpuinfo = "smef16f16",
|
||||
.sigill_fn = smef16f16_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SVE",
|
||||
.at_hwcap = AT_HWCAP,
|
||||
|
@ -23,6 +23,9 @@
|
||||
|
||||
.arch_extension sve
|
||||
|
||||
#define ID_AA64SMFR0_EL1_SMEver_SHIFT 56
|
||||
#define ID_AA64SMFR0_EL1_SMEver_WIDTH 4
|
||||
|
||||
/*
|
||||
* LDR (vector to ZA array):
|
||||
* LDR ZA[\nw, #\offset], [X\nxbase, #\offset, MUL VL]
|
||||
@ -45,6 +48,26 @@
|
||||
| ((\offset) & 7)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* LDR (ZT0)
|
||||
*
|
||||
* LDR ZT0, nx
|
||||
*/
|
||||
.macro _ldr_zt nx
|
||||
.inst 0xe11f8000 \
|
||||
| (((\nx) & 0x1f) << 5)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* STR (ZT0)
|
||||
*
|
||||
* STR ZT0, nx
|
||||
*/
|
||||
.macro _str_zt nx
|
||||
.inst 0xe13f8000 \
|
||||
| (((\nx) & 0x1f) << 5)
|
||||
.endm
|
||||
|
||||
.globl do_syscall
|
||||
do_syscall:
|
||||
// Store callee saved registers x19-x29 (80 bytes) plus x0 and x1
|
||||
@ -64,7 +87,7 @@ do_syscall:
|
||||
msr S3_3_C4_C2_2, x2
|
||||
1:
|
||||
|
||||
// Load ZA if it's enabled - uses x12 as scratch due to SME LDR
|
||||
// Load ZA and ZT0 if enabled - uses x12 as scratch due to SME LDR
|
||||
tbz x2, #SVCR_ZA_SHIFT, 1f
|
||||
mov w12, #0
|
||||
ldr x2, =za_in
|
||||
@ -73,6 +96,15 @@ do_syscall:
|
||||
add x12, x12, #1
|
||||
cmp x1, x12
|
||||
bne 2b
|
||||
|
||||
// ZT0
|
||||
mrs x2, S3_0_C0_C4_5 // ID_AA64SMFR0_EL1
|
||||
ubfx x2, x2, #ID_AA64SMFR0_EL1_SMEver_SHIFT, \
|
||||
#ID_AA64SMFR0_EL1_SMEver_WIDTH
|
||||
cbz x2, 1f
|
||||
adrp x2, zt_in
|
||||
add x2, x2, :lo12:zt_in
|
||||
_ldr_zt 2
|
||||
1:
|
||||
|
||||
// Load GPRs x8-x28, and save our SP/FP for later comparison
|
||||
@ -92,8 +124,11 @@ do_syscall:
|
||||
str x29, [x2], #8 // FP
|
||||
str x30, [x2], #8 // LR
|
||||
|
||||
// Load FPRs if we're not doing SVE
|
||||
// Load FPRs if we're not doing neither SVE nor streaming SVE
|
||||
cbnz x0, 1f
|
||||
ldr x2, =svcr_in
|
||||
tbnz x2, #SVCR_SM_SHIFT, 1f
|
||||
|
||||
ldr x2, =fpr_in
|
||||
ldp q0, q1, [x2]
|
||||
ldp q2, q3, [x2, #16 * 2]
|
||||
@ -111,10 +146,11 @@ do_syscall:
|
||||
ldp q26, q27, [x2, #16 * 26]
|
||||
ldp q28, q29, [x2, #16 * 28]
|
||||
ldp q30, q31, [x2, #16 * 30]
|
||||
|
||||
b 2f
|
||||
1:
|
||||
|
||||
// Load the SVE registers if we're doing SVE/SME
|
||||
cbz x0, 1f
|
||||
|
||||
ldr x2, =z_in
|
||||
ldr z0, [x2, #0, MUL VL]
|
||||
@ -155,9 +191,9 @@ do_syscall:
|
||||
ldr x2, =ffr_in
|
||||
ldr p0, [x2]
|
||||
ldr x2, [x2, #0]
|
||||
cbz x2, 2f
|
||||
cbz x2, 1f
|
||||
wrffr p0.b
|
||||
2:
|
||||
1:
|
||||
|
||||
ldr x2, =p_in
|
||||
ldr p0, [x2, #0, MUL VL]
|
||||
@ -176,7 +212,7 @@ do_syscall:
|
||||
ldr p13, [x2, #13, MUL VL]
|
||||
ldr p14, [x2, #14, MUL VL]
|
||||
ldr p15, [x2, #15, MUL VL]
|
||||
1:
|
||||
2:
|
||||
|
||||
// Do the syscall
|
||||
svc #0
|
||||
@ -235,6 +271,15 @@ do_syscall:
|
||||
add x12, x12, #1
|
||||
cmp x1, x12
|
||||
bne 2b
|
||||
|
||||
// ZT0
|
||||
mrs x2, S3_0_C0_C4_5 // ID_AA64SMFR0_EL1
|
||||
ubfx x2, x2, #ID_AA64SMFR0_EL1_SMEver_SHIFT, \
|
||||
#ID_AA64SMFR0_EL1_SMEver_WIDTH
|
||||
cbz x2, 1f
|
||||
adrp x2, zt_out
|
||||
add x2, x2, :lo12:zt_out
|
||||
_str_zt 2
|
||||
1:
|
||||
|
||||
// Save the SVE state if we have some
|
||||
|
@ -20,10 +20,13 @@
|
||||
|
||||
#include "syscall-abi.h"
|
||||
|
||||
#define NUM_VL ((SVE_VQ_MAX - SVE_VQ_MIN) + 1)
|
||||
|
||||
static int default_sme_vl;
|
||||
|
||||
static int sve_vl_count;
|
||||
static unsigned int sve_vls[SVE_VQ_MAX];
|
||||
static int sme_vl_count;
|
||||
static unsigned int sme_vls[SVE_VQ_MAX];
|
||||
|
||||
extern void do_syscall(int sve_vl, int sme_vl);
|
||||
|
||||
static void fill_random(void *buf, size_t size)
|
||||
@ -83,6 +86,7 @@ static int check_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl, uint64_t s
|
||||
#define NUM_FPR 32
|
||||
uint64_t fpr_in[NUM_FPR * 2];
|
||||
uint64_t fpr_out[NUM_FPR * 2];
|
||||
uint64_t fpr_zero[NUM_FPR * 2];
|
||||
|
||||
static void setup_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
uint64_t svcr)
|
||||
@ -97,7 +101,7 @@ static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
int errors = 0;
|
||||
int i;
|
||||
|
||||
if (!sve_vl) {
|
||||
if (!sve_vl && !(svcr & SVCR_SM_MASK)) {
|
||||
for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
|
||||
if (fpr_in[i] != fpr_out[i]) {
|
||||
ksft_print_msg("%s Q%d/%d mismatch %llx != %llx\n",
|
||||
@ -109,6 +113,18 @@ static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In streaming mode the whole register set should be cleared
|
||||
* by the transition out of streaming mode.
|
||||
*/
|
||||
if (svcr & SVCR_SM_MASK) {
|
||||
if (memcmp(fpr_zero, fpr_out, sizeof(fpr_out)) != 0) {
|
||||
ksft_print_msg("%s FPSIMD registers non-zero exiting SM\n",
|
||||
cfg->name);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
@ -284,8 +300,8 @@ static int check_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
return errors;
|
||||
}
|
||||
|
||||
uint8_t za_in[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
|
||||
uint8_t za_out[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
|
||||
uint8_t za_in[ZA_SIG_REGS_SIZE(SVE_VQ_MAX)];
|
||||
uint8_t za_out[ZA_SIG_REGS_SIZE(SVE_VQ_MAX)];
|
||||
|
||||
static void setup_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
uint64_t svcr)
|
||||
@ -311,6 +327,35 @@ static int check_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
return errors;
|
||||
}
|
||||
|
||||
uint8_t zt_in[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));
|
||||
uint8_t zt_out[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));
|
||||
|
||||
static void setup_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
uint64_t svcr)
|
||||
{
|
||||
fill_random(zt_in, sizeof(zt_in));
|
||||
memset(zt_out, 0, sizeof(zt_out));
|
||||
}
|
||||
|
||||
static int check_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
uint64_t svcr)
|
||||
{
|
||||
int errors = 0;
|
||||
|
||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2))
|
||||
return 0;
|
||||
|
||||
if (!(svcr & SVCR_ZA_MASK))
|
||||
return 0;
|
||||
|
||||
if (memcmp(zt_in, zt_out, sizeof(zt_in)) != 0) {
|
||||
ksft_print_msg("SME VL %d ZT does not match\n", sme_vl);
|
||||
errors++;
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
typedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
uint64_t svcr);
|
||||
typedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
@ -334,6 +379,7 @@ static struct {
|
||||
{ setup_ffr, check_ffr },
|
||||
{ setup_svcr, check_svcr },
|
||||
{ setup_za, check_za },
|
||||
{ setup_zt, check_zt },
|
||||
};
|
||||
|
||||
static bool do_test(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
@ -355,73 +401,78 @@ static bool do_test(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||
|
||||
static void test_one_syscall(struct syscall_cfg *cfg)
|
||||
{
|
||||
int sve_vq, sve_vl;
|
||||
int sme_vq, sme_vl;
|
||||
int sve, sme;
|
||||
int ret;
|
||||
|
||||
/* FPSIMD only case */
|
||||
ksft_test_result(do_test(cfg, 0, default_sme_vl, 0),
|
||||
"%s FPSIMD\n", cfg->name);
|
||||
|
||||
if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
|
||||
return;
|
||||
|
||||
for (sve_vq = SVE_VQ_MAX; sve_vq > 0; --sve_vq) {
|
||||
sve_vl = prctl(PR_SVE_SET_VL, sve_vq * 16);
|
||||
if (sve_vl == -1)
|
||||
for (sve = 0; sve < sve_vl_count; sve++) {
|
||||
ret = prctl(PR_SVE_SET_VL, sve_vls[sve]);
|
||||
if (ret == -1)
|
||||
ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
|
||||
sve_vl &= PR_SVE_VL_LEN_MASK;
|
||||
ksft_test_result(do_test(cfg, sve_vls[sve], default_sme_vl, 0),
|
||||
"%s SVE VL %d\n", cfg->name, sve_vls[sve]);
|
||||
|
||||
if (sve_vq != sve_vq_from_vl(sve_vl))
|
||||
sve_vq = sve_vq_from_vl(sve_vl);
|
||||
|
||||
ksft_test_result(do_test(cfg, sve_vl, default_sme_vl, 0),
|
||||
"%s SVE VL %d\n", cfg->name, sve_vl);
|
||||
|
||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))
|
||||
continue;
|
||||
|
||||
for (sme_vq = SVE_VQ_MAX; sme_vq > 0; --sme_vq) {
|
||||
sme_vl = prctl(PR_SME_SET_VL, sme_vq * 16);
|
||||
if (sme_vl == -1)
|
||||
for (sme = 0; sme < sme_vl_count; sme++) {
|
||||
ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
|
||||
if (ret == -1)
|
||||
ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
|
||||
sme_vl &= PR_SME_VL_LEN_MASK;
|
||||
|
||||
if (sme_vq != sve_vq_from_vl(sme_vl))
|
||||
sme_vq = sve_vq_from_vl(sme_vl);
|
||||
|
||||
ksft_test_result(do_test(cfg, sve_vl, sme_vl,
|
||||
ksft_test_result(do_test(cfg, sve_vls[sve],
|
||||
sme_vls[sme],
|
||||
SVCR_ZA_MASK | SVCR_SM_MASK),
|
||||
"%s SVE VL %d/SME VL %d SM+ZA\n",
|
||||
cfg->name, sve_vl, sme_vl);
|
||||
ksft_test_result(do_test(cfg, sve_vl, sme_vl,
|
||||
SVCR_SM_MASK),
|
||||
cfg->name, sve_vls[sve],
|
||||
sme_vls[sme]);
|
||||
ksft_test_result(do_test(cfg, sve_vls[sve],
|
||||
sme_vls[sme], SVCR_SM_MASK),
|
||||
"%s SVE VL %d/SME VL %d SM\n",
|
||||
cfg->name, sve_vl, sme_vl);
|
||||
ksft_test_result(do_test(cfg, sve_vl, sme_vl,
|
||||
SVCR_ZA_MASK),
|
||||
cfg->name, sve_vls[sve],
|
||||
sme_vls[sme]);
|
||||
ksft_test_result(do_test(cfg, sve_vls[sve],
|
||||
sme_vls[sme], SVCR_ZA_MASK),
|
||||
"%s SVE VL %d/SME VL %d ZA\n",
|
||||
cfg->name, sve_vl, sme_vl);
|
||||
cfg->name, sve_vls[sve],
|
||||
sme_vls[sme]);
|
||||
}
|
||||
}
|
||||
|
||||
for (sme = 0; sme < sme_vl_count; sme++) {
|
||||
ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
|
||||
if (ret == -1)
|
||||
ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
|
||||
ksft_test_result(do_test(cfg, 0, sme_vls[sme],
|
||||
SVCR_ZA_MASK | SVCR_SM_MASK),
|
||||
"%s SME VL %d SM+ZA\n",
|
||||
cfg->name, sme_vls[sme]);
|
||||
ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_SM_MASK),
|
||||
"%s SME VL %d SM\n",
|
||||
cfg->name, sme_vls[sme]);
|
||||
ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_ZA_MASK),
|
||||
"%s SME VL %d ZA\n",
|
||||
cfg->name, sme_vls[sme]);
|
||||
}
|
||||
}
|
||||
|
||||
int sve_count_vls(void)
|
||||
void sve_count_vls(void)
|
||||
{
|
||||
unsigned int vq;
|
||||
int vl_count = 0;
|
||||
int vl;
|
||||
|
||||
if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
/*
|
||||
* Enumerate up to SVE_VQ_MAX vector lengths
|
||||
*/
|
||||
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
|
||||
for (vq = SVE_VQ_MAX; vq > 0; vq /= 2) {
|
||||
vl = prctl(PR_SVE_SET_VL, vq * 16);
|
||||
if (vl == -1)
|
||||
ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
|
||||
@ -432,28 +483,22 @@ int sve_count_vls(void)
|
||||
if (vq != sve_vq_from_vl(vl))
|
||||
vq = sve_vq_from_vl(vl);
|
||||
|
||||
vl_count++;
|
||||
sve_vls[sve_vl_count++] = vl;
|
||||
}
|
||||
|
||||
return vl_count;
|
||||
}
|
||||
|
||||
int sme_count_vls(void)
|
||||
void sme_count_vls(void)
|
||||
{
|
||||
unsigned int vq;
|
||||
int vl_count = 0;
|
||||
int vl;
|
||||
|
||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))
|
||||
return 0;
|
||||
|
||||
/* Ensure we configure a SME VL, used to flag if SVCR is set */
|
||||
default_sme_vl = 16;
|
||||
return;
|
||||
|
||||
/*
|
||||
* Enumerate up to SVE_VQ_MAX vector lengths
|
||||
*/
|
||||
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
|
||||
for (vq = SVE_VQ_MAX; vq > 0; vq /= 2) {
|
||||
vl = prctl(PR_SME_SET_VL, vq * 16);
|
||||
if (vl == -1)
|
||||
ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
|
||||
@ -461,31 +506,47 @@ int sme_count_vls(void)
|
||||
|
||||
vl &= PR_SME_VL_LEN_MASK;
|
||||
|
||||
/* Found lowest VL */
|
||||
if (sve_vq_from_vl(vl) > vq)
|
||||
break;
|
||||
|
||||
if (vq != sve_vq_from_vl(vl))
|
||||
vq = sve_vq_from_vl(vl);
|
||||
|
||||
vl_count++;
|
||||
sme_vls[sme_vl_count++] = vl;
|
||||
}
|
||||
|
||||
return vl_count;
|
||||
/* Ensure we configure a SME VL, used to flag if SVCR is set */
|
||||
default_sme_vl = sme_vls[0];
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
int tests = 1; /* FPSIMD */
|
||||
int sme_ver;
|
||||
|
||||
srandom(getpid());
|
||||
|
||||
ksft_print_header();
|
||||
tests += sve_count_vls();
|
||||
tests += (sve_count_vls() * sme_count_vls()) * 3;
|
||||
|
||||
sve_count_vls();
|
||||
sme_count_vls();
|
||||
|
||||
tests += sve_vl_count;
|
||||
tests += sme_vl_count * 3;
|
||||
tests += (sve_vl_count * sme_vl_count) * 3;
|
||||
ksft_set_plan(ARRAY_SIZE(syscalls) * tests);
|
||||
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_SME2)
|
||||
sme_ver = 2;
|
||||
else
|
||||
sme_ver = 1;
|
||||
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
|
||||
ksft_print_msg("SME with FA64\n");
|
||||
ksft_print_msg("SME%d with FA64\n", sme_ver);
|
||||
else if (getauxval(AT_HWCAP2) & HWCAP2_SME)
|
||||
ksft_print_msg("SME without FA64\n");
|
||||
ksft_print_msg("SME%d without FA64\n", sme_ver);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(syscalls); i++)
|
||||
test_one_syscall(&syscalls[i]);
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/auxvec.h>
|
||||
@ -101,7 +102,8 @@ static void handler(int n, siginfo_t *si __always_unused,
|
||||
uc->uc_mcontext.pstate &= ~PSR_BTYPE_MASK;
|
||||
}
|
||||
|
||||
static int skip_all;
|
||||
/* Does the system have BTI? */
|
||||
static bool have_bti;
|
||||
|
||||
static void __do_test(void (*trampoline)(void (*)(void)),
|
||||
void (*fn)(void),
|
||||
@ -109,19 +111,11 @@ static void __do_test(void (*trampoline)(void (*)(void)),
|
||||
const char *name,
|
||||
int expect_sigill)
|
||||
{
|
||||
if (skip_all) {
|
||||
test_skipped++;
|
||||
putstr("ok ");
|
||||
putnum(test_num);
|
||||
putstr(" ");
|
||||
puttestname(name, trampoline_name);
|
||||
putstr(" # SKIP\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Branch Target exceptions should only happen in BTI binaries: */
|
||||
if (!BTI)
|
||||
/*
|
||||
* Branch Target exceptions should only happen for BTI
|
||||
* binaries running on a system with BTI:
|
||||
*/
|
||||
if (!BTI || !have_bti)
|
||||
expect_sigill = 0;
|
||||
|
||||
sigill_expected = expect_sigill;
|
||||
@ -199,9 +193,10 @@ void start(int *argcp)
|
||||
putstr("# HWCAP2_BTI present\n");
|
||||
if (!(hwcap & HWCAP_PACA))
|
||||
putstr("# Bad hardware? Expect problems.\n");
|
||||
have_bti = true;
|
||||
} else {
|
||||
putstr("# HWCAP2_BTI not present\n");
|
||||
skip_all = 1;
|
||||
have_bti = false;
|
||||
}
|
||||
|
||||
putstr("# Test binary");
|
||||
|
2
tools/testing/selftests/arm64/fp/.gitignore
vendored
2
tools/testing/selftests/arm64/fp/.gitignore
vendored
@ -12,3 +12,5 @@ vlset
|
||||
za-fork
|
||||
za-ptrace
|
||||
za-test
|
||||
zt-ptrace
|
||||
zt-test
|
||||
|
@ -14,6 +14,8 @@ TEST_GEN_PROGS_EXTENDED := fp-pidbench fpsimd-test \
|
||||
sve-test \
|
||||
ssve-test \
|
||||
za-test \
|
||||
zt-ptrace \
|
||||
zt-test \
|
||||
vlset
|
||||
TEST_PROGS_EXTENDED := fpsimd-stress sve-stress ssve-stress za-stress
|
||||
|
||||
@ -41,5 +43,8 @@ $(OUTPUT)/za-fork: za-fork.c $(OUTPUT)/za-fork-asm.o
|
||||
$(OUTPUT)/za-ptrace: za-ptrace.c
|
||||
$(OUTPUT)/za-test: za-test.S $(OUTPUT)/asm-utils.o
|
||||
$(CC) -nostdlib $^ -o $@
|
||||
$(OUTPUT)/zt-ptrace: zt-ptrace.c
|
||||
$(OUTPUT)/zt-test: zt-test.S $(OUTPUT)/asm-utils.o
|
||||
$(CC) -nostdlib $^ -o $@
|
||||
|
||||
include ../../lib.mk
|
||||
|
@ -57,7 +57,7 @@ endfunction
|
||||
// Utility macro to print a literal string
|
||||
// Clobbers x0-x4,x8
|
||||
.macro puts string
|
||||
.pushsection .rodata.str1.1, "aMS", 1
|
||||
.pushsection .rodata.str1.1, "aMS", @progbits, 1
|
||||
.L__puts_literal\@: .string "\string"
|
||||
.popsection
|
||||
|
||||
|
@ -31,7 +31,6 @@
|
||||
// Main program entry point
|
||||
.globl _start
|
||||
function _start
|
||||
_start:
|
||||
puts "Iterations per test: "
|
||||
mov x20, #10000
|
||||
lsl x20, x20, #8
|
||||
|
@ -370,6 +370,19 @@ static void start_za(struct child_data *child, int vl, int cpu)
|
||||
ksft_print_msg("Started %s\n", child->name);
|
||||
}
|
||||
|
||||
static void start_zt(struct child_data *child, int cpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = asprintf(&child->name, "ZT-%d", cpu);
|
||||
if (ret == -1)
|
||||
ksft_exit_fail_msg("asprintf() failed\n");
|
||||
|
||||
child_start(child, "./zt-test");
|
||||
|
||||
ksft_print_msg("Started %s\n", child->name);
|
||||
}
|
||||
|
||||
static void probe_vls(int vls[], int *vl_count, int set_vl)
|
||||
{
|
||||
unsigned int vq;
|
||||
@ -377,7 +390,7 @@ static void probe_vls(int vls[], int *vl_count, int set_vl)
|
||||
|
||||
*vl_count = 0;
|
||||
|
||||
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
|
||||
for (vq = SVE_VQ_MAX; vq > 0; vq /= 2) {
|
||||
vl = prctl(set_vl, vq * 16);
|
||||
if (vl == -1)
|
||||
ksft_exit_fail_msg("SET_VL failed: %s (%d)\n",
|
||||
@ -385,6 +398,9 @@ static void probe_vls(int vls[], int *vl_count, int set_vl)
|
||||
|
||||
vl &= PR_SVE_VL_LEN_MASK;
|
||||
|
||||
if (*vl_count && (vl == vls[*vl_count - 1]))
|
||||
break;
|
||||
|
||||
vq = sve_vq_from_vl(vl);
|
||||
|
||||
vls[*vl_count] = vl;
|
||||
@ -426,6 +442,7 @@ int main(int argc, char **argv)
|
||||
bool all_children_started = false;
|
||||
int seen_children;
|
||||
int sve_vls[MAX_VLS], sme_vls[MAX_VLS];
|
||||
bool have_sme2;
|
||||
struct sigaction sa;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) {
|
||||
@ -458,6 +475,13 @@ int main(int argc, char **argv)
|
||||
sme_vl_count = 0;
|
||||
}
|
||||
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_SME2) {
|
||||
tests += cpus;
|
||||
have_sme2 = true;
|
||||
} else {
|
||||
have_sme2 = false;
|
||||
}
|
||||
|
||||
/* Force context switching if we only have FPSIMD */
|
||||
if (!sve_vl_count && !sme_vl_count)
|
||||
fpsimd_per_cpu = 2;
|
||||
@ -468,8 +492,9 @@ int main(int argc, char **argv)
|
||||
ksft_print_header();
|
||||
ksft_set_plan(tests);
|
||||
|
||||
ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs\n",
|
||||
cpus, sve_vl_count, sme_vl_count);
|
||||
ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs, SME2 %s\n",
|
||||
cpus, sve_vl_count, sme_vl_count,
|
||||
have_sme2 ? "present" : "absent");
|
||||
|
||||
if (timeout > 0)
|
||||
ksft_print_msg("Will run for %ds\n", timeout);
|
||||
@ -527,6 +552,9 @@ int main(int argc, char **argv)
|
||||
start_ssve(&children[num_children++], sme_vls[j], i);
|
||||
start_za(&children[num_children++], sme_vls[j], i);
|
||||
}
|
||||
|
||||
if (have_sme2)
|
||||
start_zt(&children[num_children++], i);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -215,7 +215,6 @@ endfunction
|
||||
// Main program entry point
|
||||
.globl _start
|
||||
function _start
|
||||
_start:
|
||||
mov x23, #0 // signal count
|
||||
|
||||
mov w0, #SIGINT
|
||||
|
@ -48,4 +48,24 @@
|
||||
| ((\offset) & 7)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* LDR (ZT0)
|
||||
*
|
||||
* LDR ZT0, nx
|
||||
*/
|
||||
.macro _ldr_zt nx
|
||||
.inst 0xe11f8000 \
|
||||
| (((\nx) & 0x1f) << 5)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* STR (ZT0)
|
||||
*
|
||||
* STR ZT0, nx
|
||||
*/
|
||||
.macro _str_zt nx
|
||||
.inst 0xe13f8000 \
|
||||
| (((\nx) & 0x1f) << 5)
|
||||
.endm
|
||||
|
||||
#endif
|
||||
|
@ -30,6 +30,16 @@
|
||||
#define NT_ARM_SSVE 0x40b
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The architecture defines the maximum VQ as 16 but for extensibility
|
||||
* the kernel specifies the SVE_VQ_MAX as 512 resulting in us running
|
||||
* a *lot* more tests than are useful if we use it. Until the
|
||||
* architecture is extended let's limit our coverage to what is
|
||||
* currently allowed, plus one extra to ensure we cover constraining
|
||||
* the VL as expected.
|
||||
*/
|
||||
#define TEST_VQ_MAX 17
|
||||
|
||||
struct vec_type {
|
||||
const char *name;
|
||||
unsigned long hwcap_type;
|
||||
@ -55,7 +65,7 @@ static const struct vec_type vec_types[] = {
|
||||
},
|
||||
};
|
||||
|
||||
#define VL_TESTS (((SVE_VQ_MAX - SVE_VQ_MIN) + 1) * 4)
|
||||
#define VL_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 4)
|
||||
#define FLAG_TESTS 2
|
||||
#define FPSIMD_TESTS 2
|
||||
|
||||
@ -689,7 +699,7 @@ static int do_parent(pid_t child)
|
||||
}
|
||||
|
||||
/* Step through every possible VQ */
|
||||
for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {
|
||||
for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {
|
||||
vl = sve_vl_from_vq(vq);
|
||||
|
||||
/* First, try to set this vector length */
|
||||
|
@ -378,7 +378,6 @@ endfunction
|
||||
// Main program entry point
|
||||
.globl _start
|
||||
function _start
|
||||
_start:
|
||||
mov x23, #0 // Irritation signal count
|
||||
|
||||
mov w0, #SIGINT
|
||||
|
@ -25,7 +25,17 @@
|
||||
#define NT_ARM_ZA 0x40c
|
||||
#endif
|
||||
|
||||
#define EXPECTED_TESTS (((SVE_VQ_MAX - SVE_VQ_MIN) + 1) * 3)
|
||||
/*
|
||||
* The architecture defines the maximum VQ as 16 but for extensibility
|
||||
* the kernel specifies the SVE_VQ_MAX as 512 resulting in us running
|
||||
* a *lot* more tests than are useful if we use it. Until the
|
||||
* architecture is extended let's limit our coverage to what is
|
||||
* currently allowed, plus one extra to ensure we cover constraining
|
||||
* the VL as expected.
|
||||
*/
|
||||
#define TEST_VQ_MAX 17
|
||||
|
||||
#define EXPECTED_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 3)
|
||||
|
||||
static void fill_buf(char *buf, size_t size)
|
||||
{
|
||||
@ -301,7 +311,7 @@ static int do_parent(pid_t child)
|
||||
ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
|
||||
|
||||
/* Step through every possible VQ */
|
||||
for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {
|
||||
for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {
|
||||
vl = sve_vl_from_vq(vq);
|
||||
|
||||
/* First, try to set this vector length */
|
||||
|
@ -231,7 +231,6 @@ endfunction
|
||||
// Main program entry point
|
||||
.globl _start
|
||||
function _start
|
||||
_start:
|
||||
mov x23, #0 // signal count
|
||||
|
||||
mov w0, #SIGINT
|
||||
|
365
tools/testing/selftests/arm64/fp/zt-ptrace.c
Normal file
365
tools/testing/selftests/arm64/fp/zt-ptrace.c
Normal file
@ -0,0 +1,365 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021 ARM Limited.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <asm/sigcontext.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#include "../../kselftest.h"
|
||||
|
||||
/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
|
||||
#ifndef NT_ARM_ZA
|
||||
#define NT_ARM_ZA 0x40c
|
||||
#endif
|
||||
#ifndef NT_ARM_ZT
|
||||
#define NT_ARM_ZT 0x40d
|
||||
#endif
|
||||
|
||||
#define EXPECTED_TESTS 3
|
||||
|
||||
static int sme_vl;
|
||||
|
||||
static void fill_buf(char *buf, size_t size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
buf[i] = random();
|
||||
}
|
||||
|
||||
static int do_child(void)
|
||||
{
|
||||
if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
|
||||
ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
|
||||
|
||||
if (raise(SIGSTOP))
|
||||
ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static struct user_za_header *get_za(pid_t pid, void **buf, size_t *size)
|
||||
{
|
||||
struct user_za_header *za;
|
||||
void *p;
|
||||
size_t sz = sizeof(*za);
|
||||
struct iovec iov;
|
||||
|
||||
while (1) {
|
||||
if (*size < sz) {
|
||||
p = realloc(*buf, sz);
|
||||
if (!p) {
|
||||
errno = ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
*buf = p;
|
||||
*size = sz;
|
||||
}
|
||||
|
||||
iov.iov_base = *buf;
|
||||
iov.iov_len = sz;
|
||||
if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZA, &iov))
|
||||
goto error;
|
||||
|
||||
za = *buf;
|
||||
if (za->size <= sz)
|
||||
break;
|
||||
|
||||
sz = za->size;
|
||||
}
|
||||
|
||||
return za;
|
||||
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int set_za(pid_t pid, const struct user_za_header *za)
|
||||
{
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_base = (void *)za;
|
||||
iov.iov_len = za->size;
|
||||
return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZA, &iov);
|
||||
}
|
||||
|
||||
static int get_zt(pid_t pid, char zt[ZT_SIG_REG_BYTES])
|
||||
{
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_base = zt;
|
||||
iov.iov_len = ZT_SIG_REG_BYTES;
|
||||
return ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZT, &iov);
|
||||
}
|
||||
|
||||
|
||||
static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES])
|
||||
{
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_base = (void *)zt;
|
||||
iov.iov_len = ZT_SIG_REG_BYTES;
|
||||
return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZT, &iov);
|
||||
}
|
||||
|
||||
/* Reading with ZA disabled returns all zeros */
|
||||
static void ptrace_za_disabled_read_zt(pid_t child)
|
||||
{
|
||||
struct user_za_header za;
|
||||
char zt[ZT_SIG_REG_BYTES];
|
||||
int ret, i;
|
||||
bool fail = false;
|
||||
|
||||
/* Disable PSTATE.ZA using the ZA interface */
|
||||
memset(&za, 0, sizeof(za));
|
||||
za.vl = sme_vl;
|
||||
za.size = sizeof(za);
|
||||
|
||||
ret = set_za(child, &za);
|
||||
if (ret != 0) {
|
||||
ksft_print_msg("Failed to disable ZA\n");
|
||||
fail = true;
|
||||
}
|
||||
|
||||
/* Read back ZT */
|
||||
ret = get_zt(child, zt);
|
||||
if (ret != 0) {
|
||||
ksft_print_msg("Failed to read ZT\n");
|
||||
fail = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zt); i++) {
|
||||
if (zt[i]) {
|
||||
ksft_print_msg("zt[%d]: 0x%x != 0\n", i, zt[i]);
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
|
||||
ksft_test_result(!fail, "ptrace_za_disabled_read_zt\n");
|
||||
}
|
||||
|
||||
/* Writing then reading ZT should return the data written */
|
||||
static void ptrace_set_get_zt(pid_t child)
|
||||
{
|
||||
char zt_in[ZT_SIG_REG_BYTES];
|
||||
char zt_out[ZT_SIG_REG_BYTES];
|
||||
int ret, i;
|
||||
bool fail = false;
|
||||
|
||||
fill_buf(zt_in, sizeof(zt_in));
|
||||
|
||||
ret = set_zt(child, zt_in);
|
||||
if (ret != 0) {
|
||||
ksft_print_msg("Failed to set ZT\n");
|
||||
fail = true;
|
||||
}
|
||||
|
||||
ret = get_zt(child, zt_out);
|
||||
if (ret != 0) {
|
||||
ksft_print_msg("Failed to read ZT\n");
|
||||
fail = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zt_in); i++) {
|
||||
if (zt_in[i] != zt_out[i]) {
|
||||
ksft_print_msg("zt[%d]: 0x%x != 0x%x\n", i,
|
||||
zt_in[i], zt_out[i]);
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
|
||||
ksft_test_result(!fail, "ptrace_set_get_zt\n");
|
||||
}
|
||||
|
||||
/* Writing ZT should set PSTATE.ZA */
|
||||
static void ptrace_enable_za_via_zt(pid_t child)
|
||||
{
|
||||
struct user_za_header za_in;
|
||||
struct user_za_header *za_out;
|
||||
char zt[ZT_SIG_REG_BYTES];
|
||||
char *za_data;
|
||||
size_t za_out_size;
|
||||
int ret, i, vq;
|
||||
bool fail = false;
|
||||
|
||||
/* Disable PSTATE.ZA using the ZA interface */
|
||||
memset(&za_in, 0, sizeof(za_in));
|
||||
za_in.vl = sme_vl;
|
||||
za_in.size = sizeof(za_in);
|
||||
|
||||
ret = set_za(child, &za_in);
|
||||
if (ret != 0) {
|
||||
ksft_print_msg("Failed to disable ZA\n");
|
||||
fail = true;
|
||||
}
|
||||
|
||||
/* Write ZT */
|
||||
fill_buf(zt, sizeof(zt));
|
||||
ret = set_zt(child, zt);
|
||||
if (ret != 0) {
|
||||
ksft_print_msg("Failed to set ZT\n");
|
||||
fail = true;
|
||||
}
|
||||
|
||||
/* Read back ZA and check for register data */
|
||||
za_out = NULL;
|
||||
za_out_size = 0;
|
||||
if (get_za(child, (void **)&za_out, &za_out_size)) {
|
||||
/* Should have an unchanged VL */
|
||||
if (za_out->vl != sme_vl) {
|
||||
ksft_print_msg("VL changed from %d to %d\n",
|
||||
sme_vl, za_out->vl);
|
||||
fail = true;
|
||||
}
|
||||
vq = __sve_vq_from_vl(za_out->vl);
|
||||
za_data = (char *)za_out + ZA_PT_ZA_OFFSET;
|
||||
|
||||
/* Should have register data */
|
||||
if (za_out->size < ZA_PT_SIZE(vq)) {
|
||||
ksft_print_msg("ZA data less than expected: %u < %u\n",
|
||||
za_out->size, ZA_PT_SIZE(vq));
|
||||
fail = true;
|
||||
vq = 0;
|
||||
}
|
||||
|
||||
/* That register data should be non-zero */
|
||||
for (i = 0; i < ZA_PT_ZA_SIZE(vq); i++) {
|
||||
if (za_data[i]) {
|
||||
ksft_print_msg("ZA byte %d is %x\n",
|
||||
i, za_data[i]);
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ksft_print_msg("Failed to read ZA\n");
|
||||
fail = true;
|
||||
}
|
||||
|
||||
ksft_test_result(!fail, "ptrace_enable_za_via_zt\n");
|
||||
}
|
||||
|
||||
static int do_parent(pid_t child)
|
||||
{
|
||||
int ret = EXIT_FAILURE;
|
||||
pid_t pid;
|
||||
int status;
|
||||
siginfo_t si;
|
||||
|
||||
/* Attach to the child */
|
||||
while (1) {
|
||||
int sig;
|
||||
|
||||
pid = wait(&status);
|
||||
if (pid == -1) {
|
||||
perror("wait");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* This should never happen but it's hard to flag in
|
||||
* the framework.
|
||||
*/
|
||||
if (pid != child)
|
||||
continue;
|
||||
|
||||
if (WIFEXITED(status) || WIFSIGNALED(status))
|
||||
ksft_exit_fail_msg("Child died unexpectedly\n");
|
||||
|
||||
if (!WIFSTOPPED(status))
|
||||
goto error;
|
||||
|
||||
sig = WSTOPSIG(status);
|
||||
|
||||
if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
|
||||
if (errno == ESRCH)
|
||||
goto disappeared;
|
||||
|
||||
if (errno == EINVAL) {
|
||||
sig = 0; /* bust group-stop */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sig == SIGSTOP && si.si_code == SI_TKILL &&
|
||||
si.si_pid == pid)
|
||||
break;
|
||||
|
||||
cont:
|
||||
if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
|
||||
if (errno == ESRCH)
|
||||
goto disappeared;
|
||||
|
||||
ksft_test_result_fail("PTRACE_CONT: %s\n",
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
|
||||
|
||||
ptrace_za_disabled_read_zt(child);
|
||||
ptrace_set_get_zt(child);
|
||||
ptrace_enable_za_via_zt(child);
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
kill(child, SIGKILL);
|
||||
|
||||
disappeared:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
pid_t child;
|
||||
|
||||
srandom(getpid());
|
||||
|
||||
ksft_print_header();
|
||||
|
||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2)) {
|
||||
ksft_set_plan(1);
|
||||
ksft_exit_skip("SME2 not available\n");
|
||||
}
|
||||
|
||||
/* We need a valid SME VL to enable/disable ZA */
|
||||
sme_vl = prctl(PR_SME_GET_VL);
|
||||
if (sme_vl == -1) {
|
||||
ksft_set_plan(1);
|
||||
ksft_exit_skip("Failed to read SME VL: %d (%s)\n",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
|
||||
ksft_set_plan(EXPECTED_TESTS);
|
||||
|
||||
child = fork();
|
||||
if (!child)
|
||||
return do_child();
|
||||
|
||||
if (do_parent(child))
|
||||
ret = EXIT_FAILURE;
|
||||
|
||||
ksft_print_cnts();
|
||||
|
||||
return ret;
|
||||
}
|
316
tools/testing/selftests/arm64/fp/zt-test.S
Normal file
316
tools/testing/selftests/arm64/fp/zt-test.S
Normal file
@ -0,0 +1,316 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright (C) 2021-2 ARM Limited.
|
||||
// Original author: Mark Brown <broonie@kernel.org>
|
||||
//
|
||||
// Scalable Matrix Extension ZT context switch test
|
||||
// Repeatedly writes unique test patterns into ZT0
|
||||
// and reads them back to verify integrity.
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include "assembler.h"
|
||||
#include "asm-offsets.h"
|
||||
#include "sme-inst.h"
|
||||
|
||||
.arch_extension sve
|
||||
|
||||
#define ZT_SZ 512
|
||||
#define ZT_B (ZT_SZ / 8)
|
||||
|
||||
// Declare some storage space to shadow ZT register contents and a
|
||||
// scratch buffer.
|
||||
.pushsection .text
|
||||
.data
|
||||
.align 4
|
||||
ztref:
|
||||
.space ZT_B
|
||||
scratch:
|
||||
.space ZT_B
|
||||
.popsection
|
||||
|
||||
|
||||
// Generate a test pattern for storage in ZT
|
||||
// x0: pid
|
||||
// x1: generation
|
||||
|
||||
// These values are used to construct a 32-bit pattern that is repeated in the
|
||||
// scratch buffer as many times as will fit:
|
||||
// bits 31:24 generation number (increments once per test_loop)
|
||||
// bits 23: 8 pid
|
||||
// bits 7: 0 32-bit lane index
|
||||
|
||||
function pattern
|
||||
mov w3, wzr
|
||||
bfi w3, w0, #8, #16 // PID
|
||||
bfi w3, w1, #24, #8 // Generation
|
||||
|
||||
ldr x0, =scratch
|
||||
mov w1, #ZT_B / 4
|
||||
|
||||
0: str w3, [x0], #4
|
||||
add w3, w3, #1 // Lane
|
||||
subs w1, w1, #1
|
||||
b.ne 0b
|
||||
|
||||
ret
|
||||
endfunction
|
||||
|
||||
// Set up test pattern in a ZT horizontal vector
|
||||
// x0: pid
|
||||
// x1: generation
|
||||
function setup_zt
|
||||
mov x4, x30
|
||||
|
||||
bl pattern // Get pattern in scratch buffer
|
||||
ldr x0, =ztref
|
||||
ldr x1, =scratch
|
||||
mov x2, #ZT_B
|
||||
bl memcpy
|
||||
|
||||
ldr x0, =ztref
|
||||
_ldr_zt 0 // load zt0 from pointer x0
|
||||
|
||||
ret x4
|
||||
endfunction
|
||||
|
||||
// Trivial memory compare: compare x2 bytes starting at address x0 with
|
||||
// bytes starting at address x1.
|
||||
// Returns only if all bytes match; otherwise, the program is aborted.
|
||||
// Clobbers x0-x5.
|
||||
function memcmp
|
||||
cbz x2, 2f
|
||||
|
||||
stp x0, x1, [sp, #-0x20]!
|
||||
str x2, [sp, #0x10]
|
||||
|
||||
mov x5, #0
|
||||
0: ldrb w3, [x0, x5]
|
||||
ldrb w4, [x1, x5]
|
||||
add x5, x5, #1
|
||||
cmp w3, w4
|
||||
b.ne 1f
|
||||
subs x2, x2, #1
|
||||
b.ne 0b
|
||||
|
||||
1: ldr x2, [sp, #0x10]
|
||||
ldp x0, x1, [sp], #0x20
|
||||
b.ne barf
|
||||
|
||||
2: ret
|
||||
endfunction
|
||||
|
||||
// Verify that a ZT vector matches its shadow in memory, else abort
|
||||
// Clobbers x0-x3
|
||||
function check_zt
|
||||
mov x3, x30
|
||||
|
||||
ldr x0, =scratch // Poison scratch
|
||||
mov x1, #ZT_B
|
||||
bl memfill_ae
|
||||
|
||||
ldr x0, =scratch
|
||||
_str_zt 0
|
||||
|
||||
ldr x0, =ztref
|
||||
ldr x1, =scratch
|
||||
mov x2, #ZT_B
|
||||
mov x30, x3
|
||||
b memcmp
|
||||
endfunction
|
||||
|
||||
// Any SME register modified here can cause corruption in the main
|
||||
// thread -- but *only* the locations modified here.
|
||||
function irritator_handler
|
||||
// Increment the irritation signal count (x23):
|
||||
ldr x0, [x2, #ucontext_regs + 8 * 23]
|
||||
add x0, x0, #1
|
||||
str x0, [x2, #ucontext_regs + 8 * 23]
|
||||
|
||||
// Corrupt some random ZT data
|
||||
#if 0
|
||||
adr x0, .text + (irritator_handler - .text) / 16 * 16
|
||||
movi v0.8b, #1
|
||||
movi v9.16b, #2
|
||||
movi v31.8b, #3
|
||||
#endif
|
||||
|
||||
ret
|
||||
endfunction
|
||||
|
||||
function tickle_handler
|
||||
// Increment the signal count (x23):
|
||||
ldr x0, [x2, #ucontext_regs + 8 * 23]
|
||||
add x0, x0, #1
|
||||
str x0, [x2, #ucontext_regs + 8 * 23]
|
||||
|
||||
ret
|
||||
endfunction
|
||||
|
||||
function terminate_handler
|
||||
mov w21, w0
|
||||
mov x20, x2
|
||||
|
||||
puts "Terminated by signal "
|
||||
mov w0, w21
|
||||
bl putdec
|
||||
puts ", no error, iterations="
|
||||
ldr x0, [x20, #ucontext_regs + 8 * 22]
|
||||
bl putdec
|
||||
puts ", signals="
|
||||
ldr x0, [x20, #ucontext_regs + 8 * 23]
|
||||
bl putdecn
|
||||
|
||||
mov x0, #0
|
||||
mov x8, #__NR_exit
|
||||
svc #0
|
||||
endfunction
|
||||
|
||||
// w0: signal number
|
||||
// x1: sa_action
|
||||
// w2: sa_flags
|
||||
// Clobbers x0-x6,x8
|
||||
function setsignal
|
||||
str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
|
||||
|
||||
mov w4, w0
|
||||
mov x5, x1
|
||||
mov w6, w2
|
||||
|
||||
add x0, sp, #16
|
||||
mov x1, #sa_sz
|
||||
bl memclr
|
||||
|
||||
mov w0, w4
|
||||
add x1, sp, #16
|
||||
str w6, [x1, #sa_flags]
|
||||
str x5, [x1, #sa_handler]
|
||||
mov x2, #0
|
||||
mov x3, #sa_mask_sz
|
||||
mov x8, #__NR_rt_sigaction
|
||||
svc #0
|
||||
|
||||
cbz w0, 1f
|
||||
|
||||
puts "sigaction failure\n"
|
||||
b .Labort
|
||||
|
||||
1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
|
||||
ret
|
||||
endfunction
|
||||
|
||||
// Main program entry point
|
||||
.globl _start
|
||||
function _start
|
||||
mov x23, #0 // signal count
|
||||
|
||||
mov w0, #SIGINT
|
||||
adr x1, terminate_handler
|
||||
mov w2, #SA_SIGINFO
|
||||
bl setsignal
|
||||
|
||||
mov w0, #SIGTERM
|
||||
adr x1, terminate_handler
|
||||
mov w2, #SA_SIGINFO
|
||||
bl setsignal
|
||||
|
||||
mov w0, #SIGUSR1
|
||||
adr x1, irritator_handler
|
||||
mov w2, #SA_SIGINFO
|
||||
orr w2, w2, #SA_NODEFER
|
||||
bl setsignal
|
||||
|
||||
mov w0, #SIGUSR2
|
||||
adr x1, tickle_handler
|
||||
mov w2, #SA_SIGINFO
|
||||
orr w2, w2, #SA_NODEFER
|
||||
bl setsignal
|
||||
|
||||
smstart_za
|
||||
|
||||
// Obtain our PID, to ensure test pattern uniqueness between processes
|
||||
mov x8, #__NR_getpid
|
||||
svc #0
|
||||
mov x20, x0
|
||||
|
||||
puts "PID:\t"
|
||||
mov x0, x20
|
||||
bl putdecn
|
||||
|
||||
mov x22, #0 // generation number, increments per iteration
|
||||
.Ltest_loop:
|
||||
mov x0, x20
|
||||
mov x1, x22
|
||||
bl setup_zt
|
||||
|
||||
mov x8, #__NR_sched_yield // Encourage preemption
|
||||
svc #0
|
||||
|
||||
mrs x0, S3_3_C4_C2_2 // SVCR should have ZA=1,SM=0
|
||||
and x1, x0, #3
|
||||
cmp x1, #2
|
||||
b.ne svcr_barf
|
||||
|
||||
bl check_zt
|
||||
|
||||
add x22, x22, #1 // Everything still working
|
||||
b .Ltest_loop
|
||||
|
||||
.Labort:
|
||||
mov x0, #0
|
||||
mov x1, #SIGABRT
|
||||
mov x8, #__NR_kill
|
||||
svc #0
|
||||
endfunction
|
||||
|
||||
function barf
|
||||
// fpsimd.c acitivty log dump hack
|
||||
// ldr w0, =0xdeadc0de
|
||||
// mov w8, #__NR_exit
|
||||
// svc #0
|
||||
// end hack
|
||||
smstop
|
||||
mov x10, x0 // expected data
|
||||
mov x11, x1 // actual data
|
||||
mov x12, x2 // data size
|
||||
|
||||
puts "Mismatch: PID="
|
||||
mov x0, x20
|
||||
bl putdec
|
||||
puts ", iteration="
|
||||
mov x0, x22
|
||||
bl putdec
|
||||
puts "\tExpected ["
|
||||
mov x0, x10
|
||||
mov x1, x12
|
||||
bl dumphex
|
||||
puts "]\n\tGot ["
|
||||
mov x0, x11
|
||||
mov x1, x12
|
||||
bl dumphex
|
||||
puts "]\n"
|
||||
|
||||
mov x8, #__NR_getpid
|
||||
svc #0
|
||||
// fpsimd.c acitivty log dump hack
|
||||
// ldr w0, =0xdeadc0de
|
||||
// mov w8, #__NR_exit
|
||||
// svc #0
|
||||
// ^ end of hack
|
||||
mov x1, #SIGABRT
|
||||
mov x8, #__NR_kill
|
||||
svc #0
|
||||
// mov x8, #__NR_exit
|
||||
// mov x1, #1
|
||||
// svc #0
|
||||
endfunction
|
||||
|
||||
function svcr_barf
|
||||
mov x10, x0
|
||||
|
||||
puts "Bad SVCR: "
|
||||
mov x0, x10
|
||||
bl putdecn
|
||||
|
||||
mov x8, #__NR_exit
|
||||
mov x1, #1
|
||||
svc #0
|
||||
endfunction
|
@ -1,24 +1,33 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (C) 2020 ARM Limited
|
||||
|
||||
# preserve CC value from top level Makefile
|
||||
ifeq ($(CC),cc)
|
||||
CC := $(CROSS_COMPILE)gcc
|
||||
endif
|
||||
|
||||
CFLAGS += -std=gnu99 -I. -pthread
|
||||
LDFLAGS += -pthread
|
||||
SRCS := $(filter-out mte_common_util.c,$(wildcard *.c))
|
||||
PROGS := $(patsubst %.c,%,$(SRCS))
|
||||
|
||||
ifeq ($(LLVM),)
|
||||
# For GCC check that the toolchain has MTE support.
|
||||
|
||||
# preserve CC value from top level Makefile
|
||||
ifeq ($(CC),cc)
|
||||
CC := $(CROSS_COMPILE)gcc
|
||||
endif
|
||||
|
||||
#check if the compiler works well
|
||||
mte_cc_support := $(shell if ($(CC) $(CFLAGS) -march=armv8.5-a+memtag -E -x c /dev/null -o /dev/null 2>&1) then echo "1"; fi)
|
||||
|
||||
else
|
||||
|
||||
# All supported clang versions also support MTE.
|
||||
mte_cc_support := 1
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(mte_cc_support),1)
|
||||
# Generated binaries to be installed by top KSFT script
|
||||
TEST_GEN_PROGS := $(PROGS)
|
||||
|
||||
# Get Kernel headers installed and use them.
|
||||
else
|
||||
$(warning compiler "$(CC)" does not support the ARMv8.5 MTE extension.)
|
||||
$(warning test program "mte" will not be created.)
|
||||
|
@ -4,5 +4,7 @@ fake_sigreturn_*
|
||||
sme_*
|
||||
ssve_*
|
||||
sve_*
|
||||
tpidr2_siginfo
|
||||
za_*
|
||||
zt_*
|
||||
!*.[ch]
|
||||
|
@ -22,6 +22,10 @@ $(TEST_GEN_PROGS): $(PROGS)
|
||||
|
||||
# Common test-unit targets to build common-layout test-cases executables
|
||||
# Needs secondary expansion to properly include the testcase c-file in pre-reqs
|
||||
COMMON_SOURCES := test_signals.c test_signals_utils.c testcases/testcases.c \
|
||||
signals.S
|
||||
COMMON_HEADERS := test_signals.h test_signals_utils.h testcases/testcases.h
|
||||
|
||||
.SECONDEXPANSION:
|
||||
$(PROGS): test_signals.c test_signals_utils.c testcases/testcases.c signals.S $$@.c test_signals.h test_signals_utils.h testcases/testcases.h
|
||||
$(CC) $(CFLAGS) $^ -o $@
|
||||
$(PROGS): $$@.c ${COMMON_SOURCES} ${COMMON_HEADERS}
|
||||
$(CC) $(CFLAGS) ${@}.c ${COMMON_SOURCES} -o $@
|
||||
|
@ -12,12 +12,10 @@
|
||||
#include "test_signals.h"
|
||||
#include "test_signals_utils.h"
|
||||
|
||||
struct tdescr *current;
|
||||
struct tdescr *current = &tde;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
current = &tde;
|
||||
|
||||
ksft_print_msg("%s :: %s\n", current->name, current->descr);
|
||||
if (test_setup(current) && test_init(current)) {
|
||||
test_run(current);
|
||||
|
@ -34,6 +34,7 @@ enum {
|
||||
FSVE_BIT,
|
||||
FSME_BIT,
|
||||
FSME_FA64_BIT,
|
||||
FSME2_BIT,
|
||||
FMAX_END
|
||||
};
|
||||
|
||||
@ -41,6 +42,7 @@ enum {
|
||||
#define FEAT_SVE (1UL << FSVE_BIT)
|
||||
#define FEAT_SME (1UL << FSME_BIT)
|
||||
#define FEAT_SME_FA64 (1UL << FSME_FA64_BIT)
|
||||
#define FEAT_SME2 (1UL << FSME2_BIT)
|
||||
|
||||
/*
|
||||
* A descriptor used to describe and configure a test case.
|
||||
|
@ -29,6 +29,7 @@ static char const *const feats_names[FMAX_END] = {
|
||||
" SVE ",
|
||||
" SME ",
|
||||
" FA64 ",
|
||||
" SME2 ",
|
||||
};
|
||||
|
||||
#define MAX_FEATS_SZ 128
|
||||
@ -192,8 +193,10 @@ static bool handle_signal_copyctx(struct tdescr *td,
|
||||
* in the copy, this was previously validated in
|
||||
* ASSERT_GOOD_CONTEXT().
|
||||
*/
|
||||
to_copy = offset + sizeof(struct extra_context) + 16 +
|
||||
extra->size;
|
||||
to_copy = __builtin_offsetof(ucontext_t,
|
||||
uc_mcontext.__reserved);
|
||||
to_copy += offset + sizeof(struct extra_context) + 16;
|
||||
to_copy += extra->size;
|
||||
copied_extra = (struct extra_context *)&(td->live_uc->uc_mcontext.__reserved[offset]);
|
||||
} else {
|
||||
copied_extra = NULL;
|
||||
@ -323,6 +326,8 @@ int test_init(struct tdescr *td)
|
||||
td->feats_supported |= FEAT_SME;
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
|
||||
td->feats_supported |= FEAT_SME_FA64;
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_SME2)
|
||||
td->feats_supported |= FEAT_SME2;
|
||||
if (feats_ok(td)) {
|
||||
if (td->feats_required & td->feats_supported)
|
||||
fprintf(stderr,
|
||||
|
@ -34,6 +34,10 @@ static bool sme_get_vls(struct tdescr *td)
|
||||
|
||||
vl &= PR_SME_VL_LEN_MASK;
|
||||
|
||||
/* Did we find the lowest supported VL? */
|
||||
if (vq < sve_vq_from_vl(vl))
|
||||
break;
|
||||
|
||||
/* Skip missing VLs */
|
||||
vq = sve_vq_from_vl(vl);
|
||||
|
||||
@ -92,6 +96,11 @@ static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(ssve->flags & SVE_SIG_FLAG_SM)) {
|
||||
fprintf(stderr, "SVE_SIG_FLAG_SM not set in SVE record\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The actual size validation is done in get_current_context() */
|
||||
fprintf(stderr, "Got expected size %u and VL %d\n",
|
||||
head->size, ssve->vl);
|
||||
@ -116,12 +125,7 @@ static int sme_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
|
||||
struct tdescr tde = {
|
||||
.name = "Streaming SVE registers",
|
||||
.descr = "Check that we get the right Streaming SVE registers reported",
|
||||
/*
|
||||
* We shouldn't require FA64 but things like memset() used in the
|
||||
* helpers might use unsupported instructions so for now disable
|
||||
* the test unless we've got the full instruction set.
|
||||
*/
|
||||
.feats_required = FEAT_SME | FEAT_SME_FA64,
|
||||
.feats_required = FEAT_SME,
|
||||
.timeout = 3,
|
||||
.init = sme_get_vls,
|
||||
.run = sme_regs,
|
||||
|
161
tools/testing/selftests/arm64/signal/testcases/ssve_za_regs.c
Normal file
161
tools/testing/selftests/arm64/signal/testcases/ssve_za_regs.c
Normal file
@ -0,0 +1,161 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2021 ARM Limited
|
||||
*
|
||||
* Verify that both the streaming SVE and ZA register context in
|
||||
* signal frames is set up as expected when enabled simultaneously.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <ucontext.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "test_signals_utils.h"
|
||||
#include "testcases.h"
|
||||
|
||||
static union {
|
||||
ucontext_t uc;
|
||||
char buf[1024 * 128];
|
||||
} context;
|
||||
static unsigned int vls[SVE_VQ_MAX];
|
||||
unsigned int nvls = 0;
|
||||
|
||||
static bool sme_get_vls(struct tdescr *td)
|
||||
{
|
||||
int vq, vl;
|
||||
|
||||
/*
|
||||
* Enumerate up to SVE_VQ_MAX vector lengths
|
||||
*/
|
||||
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
|
||||
vl = prctl(PR_SME_SET_VL, vq * 16);
|
||||
if (vl == -1)
|
||||
return false;
|
||||
|
||||
vl &= PR_SME_VL_LEN_MASK;
|
||||
|
||||
/* Did we find the lowest supported VL? */
|
||||
if (vq < sve_vq_from_vl(vl))
|
||||
break;
|
||||
|
||||
/* Skip missing VLs */
|
||||
vq = sve_vq_from_vl(vl);
|
||||
|
||||
vls[nvls++] = vl;
|
||||
}
|
||||
|
||||
/* We need at least one VL */
|
||||
if (nvls < 1) {
|
||||
fprintf(stderr, "Only %d VL supported\n", nvls);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setup_regs(void)
|
||||
{
|
||||
/* smstart sm; real data is TODO */
|
||||
asm volatile(".inst 0xd503437f" : : : );
|
||||
|
||||
/* smstart za; real data is TODO */
|
||||
asm volatile(".inst 0xd503457f" : : : );
|
||||
}
|
||||
|
||||
static char zeros[ZA_SIG_REGS_SIZE(SVE_VQ_MAX)];
|
||||
|
||||
static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
|
||||
unsigned int vl)
|
||||
{
|
||||
size_t offset;
|
||||
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
|
||||
struct _aarch64_ctx *regs;
|
||||
struct sve_context *ssve;
|
||||
struct za_context *za;
|
||||
int ret;
|
||||
|
||||
fprintf(stderr, "Testing VL %d\n", vl);
|
||||
|
||||
ret = prctl(PR_SME_SET_VL, vl);
|
||||
if (ret != vl) {
|
||||
fprintf(stderr, "Failed to set VL, got %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a signal context which should have the SVE and ZA
|
||||
* frames in it.
|
||||
*/
|
||||
setup_regs();
|
||||
if (!get_current_context(td, &context.uc, sizeof(context)))
|
||||
return 1;
|
||||
|
||||
regs = get_header(head, SVE_MAGIC, GET_BUF_RESV_SIZE(context),
|
||||
&offset);
|
||||
if (!regs) {
|
||||
fprintf(stderr, "No SVE context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssve = (struct sve_context *)regs;
|
||||
if (ssve->vl != vl) {
|
||||
fprintf(stderr, "Got SSVE VL %d, expected %d\n", ssve->vl, vl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(ssve->flags & SVE_SIG_FLAG_SM)) {
|
||||
fprintf(stderr, "SVE_SIG_FLAG_SM not set in SVE record\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Got expected SSVE size %u and VL %d\n",
|
||||
regs->size, ssve->vl);
|
||||
|
||||
regs = get_header(head, ZA_MAGIC, GET_BUF_RESV_SIZE(context),
|
||||
&offset);
|
||||
if (!regs) {
|
||||
fprintf(stderr, "No ZA context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
za = (struct za_context *)regs;
|
||||
if (za->vl != vl) {
|
||||
fprintf(stderr, "Got ZA VL %d, expected %d\n", za->vl, vl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Got expected ZA size %u and VL %d\n",
|
||||
regs->size, za->vl);
|
||||
|
||||
/* We didn't load any data into ZA so it should be all zeros */
|
||||
if (memcmp(zeros, (char *)za + ZA_SIG_REGS_OFFSET,
|
||||
ZA_SIG_REGS_SIZE(sve_vq_from_vl(za->vl))) != 0) {
|
||||
fprintf(stderr, "ZA data invalid\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sme_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nvls; i++) {
|
||||
if (do_one_sme_vl(td, si, uc, vls[i]))
|
||||
return 1;
|
||||
}
|
||||
|
||||
td->pass = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tdescr tde = {
|
||||
.name = "Streaming SVE registers",
|
||||
.descr = "Check that we get the right Streaming SVE registers reported",
|
||||
.feats_required = FEAT_SME,
|
||||
.timeout = 3,
|
||||
.init = sme_get_vls,
|
||||
.run = sme_regs,
|
||||
};
|
@ -108,6 +108,26 @@ bool validate_za_context(struct za_context *za, char **err)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validate_zt_context(struct zt_context *zt, char **err)
|
||||
{
|
||||
if (!zt || !err)
|
||||
return false;
|
||||
|
||||
/* If the context is present there should be at least one register */
|
||||
if (zt->nregs == 0) {
|
||||
*err = "no registers";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Size should agree with the number of registers */
|
||||
if (zt->head.size != ZT_SIG_CONTEXT_SIZE(zt->nregs)) {
|
||||
*err = "register count does not match size";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
||||
{
|
||||
bool terminated = false;
|
||||
@ -117,6 +137,7 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
||||
struct extra_context *extra = NULL;
|
||||
struct sve_context *sve = NULL;
|
||||
struct za_context *za = NULL;
|
||||
struct zt_context *zt = NULL;
|
||||
struct _aarch64_ctx *head =
|
||||
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
|
||||
void *extra_data = NULL;
|
||||
@ -163,6 +184,10 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
||||
if (head->size != sizeof(struct esr_context))
|
||||
*err = "Bad size for esr_context";
|
||||
break;
|
||||
case TPIDR2_MAGIC:
|
||||
if (head->size != sizeof(struct tpidr2_context))
|
||||
*err = "Bad size for tpidr2_context";
|
||||
break;
|
||||
case SVE_MAGIC:
|
||||
if (flags & SVE_CTX)
|
||||
*err = "Multiple SVE_MAGIC";
|
||||
@ -177,6 +202,13 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
||||
za = (struct za_context *)head;
|
||||
new_flags |= ZA_CTX;
|
||||
break;
|
||||
case ZT_MAGIC:
|
||||
if (flags & ZT_CTX)
|
||||
*err = "Multiple ZT_MAGIC";
|
||||
/* Size is validated in validate_za_context() */
|
||||
zt = (struct zt_context *)head;
|
||||
new_flags |= ZT_CTX;
|
||||
break;
|
||||
case EXTRA_MAGIC:
|
||||
if (flags & EXTRA_CTX)
|
||||
*err = "Multiple EXTRA_MAGIC";
|
||||
@ -234,6 +266,9 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
||||
if (new_flags & ZA_CTX)
|
||||
if (!validate_za_context(za, err))
|
||||
return false;
|
||||
if (new_flags & ZT_CTX)
|
||||
if (!validate_zt_context(zt, err))
|
||||
return false;
|
||||
|
||||
flags |= new_flags;
|
||||
|
||||
@ -245,6 +280,11 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (terminated && (flags & ZT_CTX) && !(flags & ZA_CTX)) {
|
||||
*err = "ZT context but no ZA context";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#define SVE_CTX (1 << 1)
|
||||
#define ZA_CTX (1 << 2)
|
||||
#define EXTRA_CTX (1 << 3)
|
||||
#define ZT_CTX (1 << 4)
|
||||
|
||||
#define KSFT_BAD_MAGIC 0xdeadbeef
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user