mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +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:
|
For systems with a GICv3 interrupt controller to be used in v3 mode:
|
||||||
- If EL3 is present:
|
- 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_SRE_EL3.SRE (bit 0) must be initialised to 0b1.
|
||||||
- ICC_CTLR_EL3.PMHE (bit 6) must be set to the same value across
|
- 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
|
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.
|
- 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
|
The requirements described above for CPU mode, caches, MMUs, architected
|
||||||
timers, coherency and system registers apply to all CPUs. All CPUs must
|
timers, coherency and system registers apply to all CPUs. All CPUs must
|
||||||
enter the kernel in the same exception level. Where the values documented
|
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
|
implementations, and/or with certain kernel configurations, but have no
|
||||||
architected discovery mechanism available to userspace code at EL0. The
|
architected discovery mechanism available to userspace code at EL0. The
|
||||||
kernel exposes the presence of these features to userspace through a set
|
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
|
Userspace software can test for features by acquiring the AT_HWCAP or
|
||||||
AT_HWCAP2 entry of the auxiliary vector, and testing whether the relevant
|
AT_HWCAP2 entry of the auxiliary vector, and testing whether the relevant
|
||||||
@ -284,6 +284,24 @@ HWCAP2_RPRFM
|
|||||||
HWCAP2_SVE2P1
|
HWCAP2_SVE2P1
|
||||||
Functionality implied by ID_AA64ZFR0_EL1.SVEver == 0b0010.
|
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
|
4. Unused AT_HWCAP bits
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
@ -18,14 +18,19 @@ model features for SME is included in Appendix A.
|
|||||||
1. General
|
1. General
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
* PSTATE.SM, PSTATE.ZA, the streaming mode vector length, the ZA
|
* PSTATE.SM, PSTATE.ZA, the streaming mode vector length, the ZA and (when
|
||||||
register state and TPIDR2_EL0 are tracked per thread.
|
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
|
* 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
|
AT_HWCAP2 entry. Presence of this flag implies the presence of the SME
|
||||||
instructions and registers, and the Linux-specific system interfaces
|
instructions and registers, and the Linux-specific system interfaces
|
||||||
described in this document. SME is reported in /proc/cpuinfo as "sme".
|
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
|
* 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
|
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]
|
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_B16F32
|
||||||
HWCAP2_SME_F32F32
|
HWCAP2_SME_F32F32
|
||||||
HWCAP2_SME_FA64
|
HWCAP2_SME_FA64
|
||||||
|
HWCAP2_SME2
|
||||||
|
|
||||||
This list may be extended over time as the SME architecture evolves.
|
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.
|
cpu-feature-registers.txt for details.
|
||||||
|
|
||||||
* Debuggers should restrict themselves to interacting with the target via the
|
* 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
|
NT_ARM_SVE, NT_ARM_SSVE, NT_ARM_ZA and NT_ARM_ZT regsets. The recommended
|
||||||
of detecting support for these regsets is to connect to a target process
|
way of detecting support for these regsets is to connect to a target process
|
||||||
first and then attempt a
|
first and then attempt a
|
||||||
|
|
||||||
ptrace(PTRACE_GETREGSET, pid, NT_ARM_<regset>, &iov).
|
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
|
* 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
|
* On syscall PSTATE.SM will be cleared and the SVE registers will be handled
|
||||||
as per the standard SVE ABI.
|
as per the standard SVE ABI.
|
||||||
|
|
||||||
* Neither the SVE registers nor ZA are used to pass arguments to or receive
|
* None of the SVE registers, ZA or ZTn are used to pass arguments to
|
||||||
results from any syscall.
|
or receive results from any syscall.
|
||||||
|
|
||||||
* On process creation (eg, clone()) the newly created process will have
|
* On process creation (eg, clone()) the newly created process will have
|
||||||
PSTATE.SM cleared.
|
PSTATE.SM cleared.
|
||||||
@ -111,6 +117,9 @@ be zeroed.
|
|||||||
|
|
||||||
* Signal handlers are invoked with streaming mode and ZA disabled.
|
* 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
|
* A new signal frame record za_context encodes the ZA register contents on
|
||||||
signal delivery. [1]
|
signal delivery. [1]
|
||||||
|
|
||||||
@ -134,6 +143,14 @@ be zeroed.
|
|||||||
__reserved[] referencing this space. za_context is then written in the
|
__reserved[] referencing this space. za_context is then written in the
|
||||||
extra space. Refer to [1] for further details about this mechanism.
|
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
|
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
|
the signal frame does not match the current vector length, the signal return
|
||||||
attempt is treated as illegal, resulting in a forced SIGSEGV.
|
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
|
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
|
vector length that will be applied at the next execve() by the calling
|
||||||
thread.
|
thread.
|
||||||
|
|
||||||
* Changing the vector length causes all of ZA, P0..P15, FFR and all bits of
|
* Changing the vector length causes all of ZA, ZTn, P0..P15, FFR and all
|
||||||
Z0..Z31 except for Z0 bits [127:0] .. Z31 bits [127:0] to become
|
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.
|
unspecified, including both streaming and non-streaming SVE state.
|
||||||
Calling PR_SME_SET_VL with vl equal to the thread's current vector
|
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,
|
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.
|
* 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
|
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
|
been read if a PTRACE_GETREGSET of NT_ARM_ZA were executed for each thread
|
||||||
when the coredump was generated.
|
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
|
* 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
|
will contain TPIDR2_EL0 on systems that support SME and will be read as
|
||||||
zero with writes ignored otherwise.
|
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
|
For best system performance it is strongly encouraged for software to enable
|
||||||
ZA only when it is actively being used.
|
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
|
* 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:
|
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:
|
When returning from a signal handler:
|
||||||
|
|
||||||
* If there is no sve_context record in the signal frame, or if the record is
|
* 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.
|
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
|
* 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()
|
Defer the requested vector length change until the next execve()
|
||||||
performed by this thread.
|
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:
|
call immediately after the next execve() (if any) by the thread:
|
||||||
|
|
||||||
prctl(PR_SVE_SET_VL, arg & ~PR_SVE_SET_VL_ONEXEC)
|
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);
|
WARN_ON_ONCE(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool gic_has_relaxed_pmr_sync(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* !__ASSEMBLY__ */
|
#endif /* !__ASSEMBLY__ */
|
||||||
#endif /* !__ASM_ARCH_GICV3_H */
|
#endif /* !__ASM_ARCH_GICV3_H */
|
||||||
|
@ -123,6 +123,8 @@ config ARM64
|
|||||||
select DMA_DIRECT_REMAP
|
select DMA_DIRECT_REMAP
|
||||||
select EDAC_SUPPORT
|
select EDAC_SUPPORT
|
||||||
select FRAME_POINTER
|
select FRAME_POINTER
|
||||||
|
select FUNCTION_ALIGNMENT_4B
|
||||||
|
select FUNCTION_ALIGNMENT_8B if DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||||
select GENERIC_ALLOCATOR
|
select GENERIC_ALLOCATOR
|
||||||
select GENERIC_ARCH_TOPOLOGY
|
select GENERIC_ARCH_TOPOLOGY
|
||||||
select GENERIC_CLOCKEVENTS_BROADCAST
|
select GENERIC_CLOCKEVENTS_BROADCAST
|
||||||
@ -186,6 +188,8 @@ config ARM64
|
|||||||
select HAVE_DYNAMIC_FTRACE
|
select HAVE_DYNAMIC_FTRACE
|
||||||
select HAVE_DYNAMIC_FTRACE_WITH_ARGS \
|
select HAVE_DYNAMIC_FTRACE_WITH_ARGS \
|
||||||
if $(cc-option,-fpatchable-function-entry=2)
|
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 \
|
select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY \
|
||||||
if DYNAMIC_FTRACE_WITH_ARGS
|
if DYNAMIC_FTRACE_WITH_ARGS
|
||||||
select HAVE_EFFICIENT_UNALIGNED_ACCESS
|
select HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||||
@ -1456,10 +1460,23 @@ config XEN
|
|||||||
help
|
help
|
||||||
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM64.
|
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
|
config ARCH_FORCE_MAX_ORDER
|
||||||
int
|
int "Maximum zone order" if ARM64_4K_PAGES || ARM64_16K_PAGES
|
||||||
default "14" if ARM64_64K_PAGES
|
default "14" if ARM64_64K_PAGES
|
||||||
|
range 12 14 if ARM64_16K_PAGES
|
||||||
default "12" if ARM64_16K_PAGES
|
default "12" if ARM64_16K_PAGES
|
||||||
|
range 11 16 if ARM64_4K_PAGES
|
||||||
default "11"
|
default "11"
|
||||||
help
|
help
|
||||||
The kernel memory allocator divides physically contiguous memory
|
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,
|
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.
|
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 :
|
Hence we have :
|
||||||
MAX_ORDER = (PMD_SHIFT - PAGE_SHIFT) + 1 => PAGE_SHIFT - 2
|
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"
|
bool "Use pointer authentication for kernel"
|
||||||
default y
|
default y
|
||||||
depends on ARM64_PTR_AUTH
|
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
|
# Modern compilers insert a .note.gnu.property section note for PAC
|
||||||
# which is only understood by binutils starting with version 2.33.1.
|
# 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)
|
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
|
# GCC 7, 8
|
||||||
def_bool $(cc-option,-msign-return-address=all)
|
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)
|
def_bool $(cc-option,-Wa$(comma)-march=armv8.3-a)
|
||||||
|
|
||||||
config AS_HAS_CFI_NEGATE_RA_STATE
|
config AS_HAS_CFI_NEGATE_RA_STATE
|
||||||
|
@ -187,7 +187,7 @@ config ARCH_MVEBU
|
|||||||
select PINCTRL_ARMADA_CP110
|
select PINCTRL_ARMADA_CP110
|
||||||
select PINCTRL_AC5
|
select PINCTRL_AC5
|
||||||
help
|
help
|
||||||
This enables support for Marvell EBU familly, including:
|
This enables support for Marvell EBU family, including:
|
||||||
- Armada 3700 SoC Family
|
- Armada 3700 SoC Family
|
||||||
- Armada 7K SoC Family
|
- Armada 7K SoC Family
|
||||||
- Armada 8K SoC Family
|
- Armada 8K SoC Family
|
||||||
|
@ -63,50 +63,37 @@ stack_protector_prepare: prepare0
|
|||||||
include/generated/asm-offsets.h))
|
include/generated/asm-offsets.h))
|
||||||
endif
|
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)
|
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
|
else
|
||||||
branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET) := -mbranch-protection=$(PACRET-y)
|
KBUILD_CFLAGS += $(call cc-option,-mbranch-protection=none)
|
||||||
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
|
|
||||||
endif
|
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)
|
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
|
endif
|
||||||
|
|
||||||
ifdef asm-arch
|
ifdef asm-arch
|
||||||
@ -139,7 +126,10 @@ endif
|
|||||||
|
|
||||||
CHECKFLAGS += -D__aarch64__
|
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
|
KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
|
||||||
CC_FLAGS_FTRACE := -fpatchable-function-entry=2
|
CC_FLAGS_FTRACE := -fpatchable-function-entry=2
|
||||||
endif
|
endif
|
||||||
|
@ -190,5 +190,10 @@ static inline void gic_arch_enable_irqs(void)
|
|||||||
asm volatile ("msr daifclr, #3" : : : "memory");
|
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 /* __ASSEMBLY__ */
|
||||||
#endif /* __ASM_ARCH_GICV3_H */
|
#endif /* __ASM_ARCH_GICV3_H */
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include <linux/kasan-checks.h>
|
#include <linux/kasan-checks.h>
|
||||||
|
|
||||||
|
#include <asm/alternative-macros.h>
|
||||||
|
|
||||||
#define __nops(n) ".rept " #n "\nnop\n.endr\n"
|
#define __nops(n) ".rept " #n "\nnop\n.endr\n"
|
||||||
#define nops(n) asm volatile(__nops(n))
|
#define nops(n) asm volatile(__nops(n))
|
||||||
|
|
||||||
@ -41,10 +43,11 @@
|
|||||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||||
#define pmr_sync() \
|
#define pmr_sync() \
|
||||||
do { \
|
do { \
|
||||||
extern struct static_key_false gic_pmr_sync; \
|
asm volatile( \
|
||||||
\
|
ALTERNATIVE_CB("dsb sy", \
|
||||||
if (static_branch_unlikely(&gic_pmr_sync)) \
|
ARM64_HAS_GIC_PRIO_RELAXED_SYNC, \
|
||||||
dsb(sy); \
|
alt_cb_patch_nops) \
|
||||||
|
); \
|
||||||
} while(0)
|
} while(0)
|
||||||
#else
|
#else
|
||||||
#define pmr_sync() do {} while (0)
|
#define pmr_sync() do {} while (0)
|
||||||
|
@ -769,6 +769,12 @@ static __always_inline bool system_supports_sme(void)
|
|||||||
cpus_have_const_cap(ARM64_SME);
|
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)
|
static __always_inline bool system_supports_fa64(void)
|
||||||
{
|
{
|
||||||
return IS_ENABLED(CONFIG_ARM64_SME) &&
|
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)
|
static __always_inline bool system_uses_irq_prio_masking(void)
|
||||||
{
|
{
|
||||||
return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) &&
|
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)
|
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))
|
if (!IS_ENABLED(CONFIG_ARM64_HW_AFDBM))
|
||||||
return false;
|
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,
|
return cpuid_feature_extract_unsigned_field(mmfr1,
|
||||||
ID_AA64MMFR1_EL1_HAFDBS_SHIFT);
|
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_ALIGN SZ_64K
|
||||||
#define EFI_ALLOC_LIMIT ((1UL << 48) - 1)
|
#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
|
* On ARM systems, virtually remapped UEFI runtime services are set up in two
|
||||||
* distinct stages:
|
* distinct stages:
|
||||||
|
@ -177,7 +177,7 @@
|
|||||||
/**
|
/**
|
||||||
* Initialize EL2 registers to sane values. This should be called early on all
|
* 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
|
* 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
|
* if possible later on in the boot process
|
||||||
*
|
*
|
||||||
* Regs: x0, x1 and x2 are clobbered.
|
* Regs: x0, x1 and x2 are clobbered.
|
||||||
|
@ -341,6 +341,7 @@
|
|||||||
#define ESR_ELx_SME_ISS_ILL 1
|
#define ESR_ELx_SME_ISS_ILL 1
|
||||||
#define ESR_ELx_SME_ISS_SM_DISABLED 2
|
#define ESR_ELx_SME_ISS_SM_DISABLED 2
|
||||||
#define ESR_ELx_SME_ISS_ZA_DISABLED 3
|
#define ESR_ELx_SME_ISS_ZA_DISABLED 3
|
||||||
|
#define ESR_ELx_SME_ISS_ZT_DISABLED 4
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
#include <asm/types.h>
|
#include <asm/types.h>
|
||||||
|
@ -61,7 +61,7 @@ extern void fpsimd_kvm_prepare(void);
|
|||||||
struct cpu_fp_state {
|
struct cpu_fp_state {
|
||||||
struct user_fpsimd_state *st;
|
struct user_fpsimd_state *st;
|
||||||
void *sve_state;
|
void *sve_state;
|
||||||
void *za_state;
|
void *sme_state;
|
||||||
u64 *svcr;
|
u64 *svcr;
|
||||||
unsigned int sve_vl;
|
unsigned int sve_vl;
|
||||||
unsigned int sme_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);
|
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_save_state(void *state, u32 *pfpsr, int save_ffr);
|
||||||
extern void sve_load_state(void const *state, u32 const *pfpsr,
|
extern void sve_load_state(void const *state, u32 const *pfpsr,
|
||||||
int restore_ffr);
|
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 unsigned int sve_get_vl(void);
|
||||||
extern void sve_set_vq(unsigned long vq_minus_1);
|
extern void sve_set_vq(unsigned long vq_minus_1);
|
||||||
extern void sme_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 sme_save_state(void *state, int zt);
|
||||||
extern void za_load_state(void const *state);
|
extern void sme_load_state(void const *state, int zt);
|
||||||
|
|
||||||
struct arm64_cpu_capabilities;
|
struct arm64_cpu_capabilities;
|
||||||
extern void sve_kernel_enable(const struct arm64_cpu_capabilities *__unused);
|
extern void sve_kernel_enable(const struct arm64_cpu_capabilities *__unused);
|
||||||
extern void sme_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 void fa64_kernel_enable(const struct arm64_cpu_capabilities *__unused);
|
||||||
|
|
||||||
extern u64 read_zcr_features(void);
|
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
|
* Return how many bytes of memory are required to store the full SME
|
||||||
* specific state (currently just ZA) for task, given task's currently
|
* specific state for task, given task's currently configured vector
|
||||||
* configured vector length.
|
* 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);
|
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
|
#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_set_current_vl(unsigned long arg) { return -EINVAL; }
|
||||||
static inline int sme_get_current_vl(void) { 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -220,6 +220,28 @@
|
|||||||
| ((\offset) & 7)
|
| ((\offset) & 7)
|
||||||
.endm
|
.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 the entire ZA array
|
||||||
* ZERO ZA
|
* ZERO ZA
|
||||||
|
@ -62,20 +62,7 @@ extern unsigned long ftrace_graph_call;
|
|||||||
|
|
||||||
extern void return_to_handler(void);
|
extern void return_to_handler(void);
|
||||||
|
|
||||||
static inline unsigned long ftrace_call_adjust(unsigned long addr)
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
||||||
struct dyn_ftrace;
|
struct dyn_ftrace;
|
||||||
|
@ -31,12 +31,20 @@
|
|||||||
#define COMPAT_HWCAP_VFPD32 (1 << 19)
|
#define COMPAT_HWCAP_VFPD32 (1 << 19)
|
||||||
#define COMPAT_HWCAP_LPAE (1 << 20)
|
#define COMPAT_HWCAP_LPAE (1 << 20)
|
||||||
#define COMPAT_HWCAP_EVTSTRM (1 << 21)
|
#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_AES (1 << 0)
|
||||||
#define COMPAT_HWCAP2_PMULL (1 << 1)
|
#define COMPAT_HWCAP2_PMULL (1 << 1)
|
||||||
#define COMPAT_HWCAP2_SHA1 (1 << 2)
|
#define COMPAT_HWCAP2_SHA1 (1 << 2)
|
||||||
#define COMPAT_HWCAP2_SHA2 (1 << 3)
|
#define COMPAT_HWCAP2_SHA2 (1 << 3)
|
||||||
#define COMPAT_HWCAP2_CRC32 (1 << 4)
|
#define COMPAT_HWCAP2_CRC32 (1 << 4)
|
||||||
|
#define COMPAT_HWCAP2_SB (1 << 5)
|
||||||
|
#define COMPAT_HWCAP2_SSBS (1 << 6)
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
@ -123,6 +131,12 @@
|
|||||||
#define KERNEL_HWCAP_CSSC __khwcap2_feature(CSSC)
|
#define KERNEL_HWCAP_CSSC __khwcap2_feature(CSSC)
|
||||||
#define KERNEL_HWCAP_RPRFM __khwcap2_feature(RPRFM)
|
#define KERNEL_HWCAP_RPRFM __khwcap2_feature(RPRFM)
|
||||||
#define KERNEL_HWCAP_SVE2P1 __khwcap2_feature(SVE2P1)
|
#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
|
* 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(clrex, 0xFFFFF0FF, 0xD503305F)
|
||||||
__AARCH64_INSN_FUNCS(ssbb, 0xFFFFFFFF, 0xD503309F)
|
__AARCH64_INSN_FUNCS(ssbb, 0xFFFFFFFF, 0xD503309F)
|
||||||
__AARCH64_INSN_FUNCS(pssbb, 0xFFFFFFFF, 0xD503349F)
|
__AARCH64_INSN_FUNCS(pssbb, 0xFFFFFFFF, 0xD503349F)
|
||||||
|
__AARCH64_INSN_FUNCS(bti, 0xFFFFFF3F, 0xD503241f)
|
||||||
|
|
||||||
#undef __AARCH64_INSN_FUNCS
|
#undef __AARCH64_INSN_FUNCS
|
||||||
|
|
||||||
|
@ -21,43 +21,77 @@
|
|||||||
* exceptions should be unmasked.
|
* exceptions should be unmasked.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
static __always_inline bool __irqflags_uses_pmr(void)
|
||||||
* CPU interrupt mask handling.
|
|
||||||
*/
|
|
||||||
static inline void arch_local_irq_enable(void)
|
|
||||||
{
|
{
|
||||||
if (system_has_prio_mask_debugging()) {
|
return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) &&
|
||||||
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
|
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);
|
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
asm volatile(ALTERNATIVE(
|
barrier();
|
||||||
"msr daifclr, #3 // arch_local_irq_enable",
|
write_sysreg_s(GIC_PRIO_IRQON, SYS_ICC_PMR_EL1);
|
||||||
__msr_s(SYS_ICC_PMR_EL1, "%0"),
|
|
||||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
|
||||||
:
|
|
||||||
: "r" ((unsigned long) GIC_PRIO_IRQON)
|
|
||||||
: "memory");
|
|
||||||
|
|
||||||
pmr_sync();
|
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)
|
static inline void arch_local_irq_disable(void)
|
||||||
{
|
{
|
||||||
if (system_has_prio_mask_debugging()) {
|
if (__irqflags_uses_pmr()) {
|
||||||
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
|
__pmr_local_irq_disable();
|
||||||
|
} else {
|
||||||
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
|
__daif_local_irq_disable();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
asm volatile(ALTERNATIVE(
|
static __always_inline unsigned long __daif_local_save_flags(void)
|
||||||
"msr daifset, #3 // arch_local_irq_disable",
|
{
|
||||||
__msr_s(SYS_ICC_PMR_EL1, "%0"),
|
return read_sysreg(daif);
|
||||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
}
|
||||||
:
|
|
||||||
: "r" ((unsigned long) GIC_PRIO_IRQOFF)
|
static __always_inline unsigned long __pmr_local_save_flags(void)
|
||||||
: "memory");
|
{
|
||||||
|
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)
|
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(
|
static __always_inline bool __daif_irqs_disabled_flags(unsigned long flags)
|
||||||
"mrs %0, daif",
|
{
|
||||||
__mrs_s("%0", SYS_ICC_PMR_EL1),
|
return flags & PSR_I_BIT;
|
||||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
}
|
||||||
: "=&r" (flags)
|
|
||||||
:
|
static __always_inline bool __pmr_irqs_disabled_flags(unsigned long flags)
|
||||||
: "memory");
|
{
|
||||||
|
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;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int arch_irqs_disabled_flags(unsigned long flags)
|
static __always_inline unsigned long __pmr_local_irq_save(void)
|
||||||
{
|
{
|
||||||
int res;
|
unsigned long flags = __pmr_local_save_flags();
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are too many states with IRQs disabled, just keep the current
|
* There are too many states with IRQs disabled, just keep the current
|
||||||
* state if interrupts are already disabled/masked.
|
* state if interrupts are already disabled/masked.
|
||||||
*/
|
*/
|
||||||
if (!arch_irqs_disabled_flags(flags))
|
if (!__pmr_irqs_disabled_flags(flags))
|
||||||
arch_local_irq_disable();
|
__pmr_local_irq_disable();
|
||||||
|
|
||||||
return flags;
|
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
|
* restore saved IRQ state
|
||||||
*/
|
*/
|
||||||
static inline void arch_local_irq_restore(unsigned long flags)
|
static inline void arch_local_irq_restore(unsigned long flags)
|
||||||
{
|
{
|
||||||
asm volatile(ALTERNATIVE(
|
if (__irqflags_uses_pmr()) {
|
||||||
"msr daif, %0",
|
__pmr_local_irq_restore(flags);
|
||||||
__msr_s(SYS_ICC_PMR_EL1, "%0"),
|
} else {
|
||||||
ARM64_HAS_IRQ_PRIO_MASKING)
|
__daif_local_irq_restore(flags);
|
||||||
:
|
}
|
||||||
: "r" (flags)
|
|
||||||
: "memory");
|
|
||||||
|
|
||||||
pmr_sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __ASM_IRQFLAGS_H */
|
#endif /* __ASM_IRQFLAGS_H */
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
#include <asm/assembler.h>
|
#include <asm/assembler.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define __ALIGN .align 2
|
#define __ALIGN .balign CONFIG_FUNCTION_ALIGNMENT
|
||||||
#define __ALIGN_STR ".align 2"
|
#define __ALIGN_STR ".balign " #CONFIG_FUNCTION_ALIGNMENT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When using in-kernel BTI we need to ensure that PCS-conformant
|
* 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_read(void *addr, u32 *insnp);
|
||||||
int aarch64_insn_write(void *addr, u32 insn);
|
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_nosync(void *addr, u32 insn);
|
||||||
int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt);
|
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);
|
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
|
* 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)
|
* 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 pte)
|
||||||
{
|
{
|
||||||
pte_t old_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),
|
VM_WARN_ONCE(pte_write(old_pte) && !pte_dirty(pte),
|
||||||
"%s: racy dirty state clearing: 0x%016llx -> 0x%016llx",
|
"%s: racy dirty state clearing: 0x%016llx -> 0x%016llx",
|
||||||
__func__, pte_val(old_pte), pte_val(pte));
|
__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,
|
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);
|
mte_sync_tags(old_pte, pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
__check_racy_pte_update(mm, ptep, pte);
|
__check_safe_pte_update(mm, ptep, pte);
|
||||||
|
|
||||||
set_pte(ptep, pte);
|
set_pte(ptep, pte);
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ struct thread_struct {
|
|||||||
enum fp_type fp_type; /* registers FPSIMD or SVE? */
|
enum fp_type fp_type; /* registers FPSIMD or SVE? */
|
||||||
unsigned int fpsimd_cpu;
|
unsigned int fpsimd_cpu;
|
||||||
void *sve_state; /* SVE registers, if any */
|
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[ARM64_VEC_MAX]; /* vector length */
|
||||||
unsigned int vl_onexec[ARM64_VEC_MAX]; /* vl after next exec */
|
unsigned int vl_onexec[ARM64_VEC_MAX]; /* vl after next exec */
|
||||||
unsigned long fault_address; /* fault info */
|
unsigned long fault_address; /* fault info */
|
||||||
|
@ -194,7 +194,7 @@ struct pt_regs {
|
|||||||
u32 unused2;
|
u32 unused2;
|
||||||
#endif
|
#endif
|
||||||
u64 sdei_ttbr1;
|
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 pmr_save;
|
||||||
u64 stackframe[2];
|
u64 stackframe[2];
|
||||||
|
|
||||||
|
@ -10,15 +10,16 @@
|
|||||||
#ifdef CONFIG_SHADOW_CALL_STACK
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||||
scs_sp .req x18
|
scs_sp .req x18
|
||||||
|
|
||||||
.macro scs_load tsk
|
.macro scs_load_current
|
||||||
ldr scs_sp, [\tsk, #TSK_TI_SCS_SP]
|
get_current_task scs_sp
|
||||||
|
ldr scs_sp, [scs_sp, #TSK_TI_SCS_SP]
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro scs_save tsk
|
.macro scs_save tsk
|
||||||
str scs_sp, [\tsk, #TSK_TI_SCS_SP]
|
str scs_sp, [\tsk, #TSK_TI_SCS_SP]
|
||||||
.endm
|
.endm
|
||||||
#else
|
#else
|
||||||
.macro scs_load tsk
|
.macro scs_load_current
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro scs_save tsk
|
.macro scs_save tsk
|
||||||
|
@ -496,6 +496,7 @@
|
|||||||
#define SCTLR_ELx_DSSBS (BIT(44))
|
#define SCTLR_ELx_DSSBS (BIT(44))
|
||||||
#define SCTLR_ELx_ATA (BIT(43))
|
#define SCTLR_ELx_ATA (BIT(43))
|
||||||
|
|
||||||
|
#define SCTLR_ELx_EE_SHIFT 25
|
||||||
#define SCTLR_ELx_ENIA_SHIFT 31
|
#define SCTLR_ELx_ENIA_SHIFT 31
|
||||||
|
|
||||||
#define SCTLR_ELx_ITFSB (BIT(37))
|
#define SCTLR_ELx_ITFSB (BIT(37))
|
||||||
@ -504,7 +505,7 @@
|
|||||||
#define SCTLR_ELx_LSMAOE (BIT(29))
|
#define SCTLR_ELx_LSMAOE (BIT(29))
|
||||||
#define SCTLR_ELx_nTLSMD (BIT(28))
|
#define SCTLR_ELx_nTLSMD (BIT(28))
|
||||||
#define SCTLR_ELx_ENDA (BIT(27))
|
#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_EIS (BIT(22))
|
||||||
#define SCTLR_ELx_IESB (BIT(21))
|
#define SCTLR_ELx_IESB (BIT(21))
|
||||||
#define SCTLR_ELx_TSCXT (BIT(20))
|
#define SCTLR_ELx_TSCXT (BIT(20))
|
||||||
@ -730,8 +731,8 @@
|
|||||||
|
|
||||||
#define ARM64_FEATURE_FIELD_BITS 4
|
#define ARM64_FEATURE_FIELD_BITS 4
|
||||||
|
|
||||||
/* Create a mask for the feature bits of the specified feature. */
|
/* Defined for compatibility only, do not add new users. */
|
||||||
#define ARM64_FEATURE_MASK(x) (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
|
#define ARM64_FEATURE_MASK(x) (x##_MASK)
|
||||||
|
|
||||||
#ifdef __ASSEMBLY__
|
#ifdef __ASSEMBLY__
|
||||||
|
|
||||||
|
@ -96,5 +96,11 @@
|
|||||||
#define HWCAP2_CSSC (1UL << 34)
|
#define HWCAP2_CSSC (1UL << 34)
|
||||||
#define HWCAP2_RPRFM (1UL << 35)
|
#define HWCAP2_RPRFM (1UL << 35)
|
||||||
#define HWCAP2_SVE2P1 (1UL << 36)
|
#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 */
|
#endif /* _UAPI__ASM_HWCAP_H */
|
||||||
|
@ -144,6 +144,14 @@ struct sve_context {
|
|||||||
|
|
||||||
#define SVE_SIG_FLAG_SM 0x1 /* Context describes streaming mode */
|
#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
|
#define ZA_MAGIC 0x54366345
|
||||||
|
|
||||||
struct za_context {
|
struct za_context {
|
||||||
@ -152,6 +160,14 @@ struct za_context {
|
|||||||
__u16 __reserved[3];
|
__u16 __reserved[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define ZT_MAGIC 0x5a544e01
|
||||||
|
|
||||||
|
struct zt_context {
|
||||||
|
struct _aarch64_ctx head;
|
||||||
|
__u16 nregs;
|
||||||
|
__u16 __reserved[3];
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* !__ASSEMBLY__ */
|
#endif /* !__ASSEMBLY__ */
|
||||||
|
|
||||||
#include <asm/sve_context.h>
|
#include <asm/sve_context.h>
|
||||||
@ -304,4 +320,15 @@ struct za_context {
|
|||||||
#define ZA_SIG_CONTEXT_SIZE(vq) \
|
#define ZA_SIG_CONTEXT_SIZE(vq) \
|
||||||
(ZA_SIG_REGS_OFFSET + ZA_SIG_REGS_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 */
|
#endif /* _UAPI__ASM_SIGCONTEXT_H */
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <linux/arm_sdei.h>
|
#include <linux/arm_sdei.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
#include <linux/ftrace.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
@ -193,6 +194,9 @@ int main(void)
|
|||||||
DEFINE(KIMAGE_HEAD, offsetof(struct kimage, head));
|
DEFINE(KIMAGE_HEAD, offsetof(struct kimage, head));
|
||||||
DEFINE(KIMAGE_START, offsetof(struct kimage, start));
|
DEFINE(KIMAGE_START, offsetof(struct kimage, start));
|
||||||
BLANK();
|
BLANK();
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_FUNCTION_TRACER
|
||||||
|
DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func));
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
#include <linux/bsearch.h>
|
#include <linux/bsearch.h>
|
||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
#include <linux/crash_dump.h>
|
#include <linux/crash_dump.h>
|
||||||
|
#include <linux/kstrtox.h>
|
||||||
#include <linux/sort.h>
|
#include <linux/sort.h>
|
||||||
#include <linux/stop_machine.h>
|
#include <linux/stop_machine.h>
|
||||||
#include <linux/sysfs.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[] = {
|
static const struct arm64_ftr_bits ftr_id_aa64smfr0[] = {
|
||||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_FA64_SHIFT, 1, 0),
|
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),
|
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_I16I64_SHIFT, 4, 0),
|
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_I16I64_SHIFT, 4, 0),
|
||||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F64F64_SHIFT, 1, 0),
|
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),
|
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_I8I32_SHIFT, 4, 0),
|
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_I8I32_SHIFT, 4, 0),
|
||||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F16F32_SHIFT, 1, 0),
|
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F16F32_SHIFT, 1, 0),
|
||||||
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_B16F32_SHIFT, 1, 0),
|
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),
|
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
|
||||||
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F32F32_SHIFT, 1, 0),
|
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_F32F32_SHIFT, 1, 0),
|
||||||
ARM64_FTR_END,
|
ARM64_FTR_END,
|
||||||
@ -444,8 +455,8 @@ static const struct arm64_ftr_bits ftr_mvfr0[] = {
|
|||||||
|
|
||||||
static const struct arm64_ftr_bits ftr_mvfr1[] = {
|
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_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_VISIBLE, 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_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_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_SIMDInt_SHIFT, 4, 0),
|
||||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, MVFR1_EL1_SIMDLS_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[] = {
|
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_VISIBLE, 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_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_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_VISIBLE, 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_VISIBLE, 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_DP_SHIFT, 4, 0),
|
||||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_JSCVT_SHIFT, 4, 0),
|
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR6_EL1_JSCVT_SHIFT, 4, 0),
|
||||||
ARM64_FTR_END,
|
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[] = {
|
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_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_PFR2_EL1_CSV3_SHIFT, 4, 0),
|
||||||
ARM64_FTR_END,
|
ARM64_FTR_END,
|
||||||
};
|
};
|
||||||
@ -1795,7 +1806,7 @@ kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
|
|||||||
static int __init parse_kpti(char *str)
|
static int __init parse_kpti(char *str)
|
||||||
{
|
{
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int ret = strtobool(str, &enabled);
|
int ret = kstrtobool(str, &enabled);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -2039,14 +2050,50 @@ static bool enable_pseudo_nmi;
|
|||||||
|
|
||||||
static int __init early_enable_pseudo_nmi(char *p)
|
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);
|
early_param("irqchip.gicv3_pseudo_nmi", early_enable_pseudo_nmi);
|
||||||
|
|
||||||
static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,
|
static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,
|
||||||
int scope)
|
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
|
#endif
|
||||||
|
|
||||||
@ -2142,7 +2189,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.desc = "GIC system register CPU interface",
|
.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,
|
.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
|
||||||
.matches = has_useable_gicv3_cpuif,
|
.matches = has_useable_gicv3_cpuif,
|
||||||
.sys_reg = SYS_ID_AA64PFR0_EL1,
|
.sys_reg = SYS_ID_AA64PFR0_EL1,
|
||||||
@ -2534,14 +2581,17 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
|
|||||||
* Depends on having GICv3
|
* Depends on having GICv3
|
||||||
*/
|
*/
|
||||||
.desc = "IRQ priority masking",
|
.desc = "IRQ priority masking",
|
||||||
.capability = ARM64_HAS_IRQ_PRIO_MASKING,
|
.capability = ARM64_HAS_GIC_PRIO_MASKING,
|
||||||
.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
|
.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
|
||||||
.matches = can_use_gic_priorities,
|
.matches = can_use_gic_priorities,
|
||||||
.sys_reg = SYS_ID_AA64PFR0_EL1,
|
},
|
||||||
.field_pos = ID_AA64PFR0_EL1_GIC_SHIFT,
|
{
|
||||||
.field_width = 4,
|
/*
|
||||||
.sign = FTR_UNSIGNED,
|
* Depends on ARM64_HAS_GIC_PRIO_MASKING
|
||||||
.min_field_value = 1,
|
*/
|
||||||
|
.capability = ARM64_HAS_GIC_PRIO_RELAXED_SYNC,
|
||||||
|
.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
|
||||||
|
.matches = has_gic_prio_relaxed_sync,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_ARM64_E0PD
|
#ifdef CONFIG_ARM64_E0PD
|
||||||
@ -2649,6 +2699,18 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
|
|||||||
.matches = has_cpuid_feature,
|
.matches = has_cpuid_feature,
|
||||||
.cpu_enable = fa64_kernel_enable,
|
.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 */
|
#endif /* CONFIG_ARM64_SME */
|
||||||
{
|
{
|
||||||
.desc = "WFx with timeout",
|
.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_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, 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_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, 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_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),
|
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
|
#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_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_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_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_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_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_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_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),
|
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 */
|
#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 */
|
/* 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_VFP),
|
||||||
HWCAP_CAP(SYS_MVFR0_EL1, MVFR0_EL1_FPDP_SHIFT, 4, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP, COMPAT_HWCAP_VFPv3),
|
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, 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_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_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_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_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
|
#endif
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
@ -119,6 +119,12 @@ static const char *const hwcap_str[] = {
|
|||||||
[KERNEL_HWCAP_CSSC] = "cssc",
|
[KERNEL_HWCAP_CSSC] = "cssc",
|
||||||
[KERNEL_HWCAP_RPRFM] = "rprfm",
|
[KERNEL_HWCAP_RPRFM] = "rprfm",
|
||||||
[KERNEL_HWCAP_SVE2P1] = "sve2p1",
|
[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
|
#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(VFPD32)] = NULL, /* Not possible on arm64 */
|
||||||
[COMPAT_KERNEL_HWCAP(LPAE)] = "lpae",
|
[COMPAT_KERNEL_HWCAP(LPAE)] = "lpae",
|
||||||
[COMPAT_KERNEL_HWCAP(EVTSTRM)] = "evtstrm",
|
[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)
|
#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(SHA1)] = "sha1",
|
||||||
[COMPAT_KERNEL_HWCAP2(SHA2)] = "sha2",
|
[COMPAT_KERNEL_HWCAP2(SHA2)] = "sha2",
|
||||||
[COMPAT_KERNEL_HWCAP2(CRC32)] = "crc32",
|
[COMPAT_KERNEL_HWCAP2(CRC32)] = "crc32",
|
||||||
|
[COMPAT_KERNEL_HWCAP2(SB)] = "sb",
|
||||||
|
[COMPAT_KERNEL_HWCAP2(SSBS)] = "ssbs",
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_COMPAT */
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
||||||
|
@ -100,25 +100,35 @@ SYM_FUNC_START(sme_set_vq)
|
|||||||
SYM_FUNC_END(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
|
* x0 - pointer to buffer for state
|
||||||
|
* x1 - number of ZT registers to save
|
||||||
*/
|
*/
|
||||||
SYM_FUNC_START(za_save_state)
|
SYM_FUNC_START(sme_save_state)
|
||||||
_sme_rdsvl 1, 1 // x1 = VL/8
|
_sme_rdsvl 2, 1 // x2 = VL/8
|
||||||
sme_save_za 0, x1, 12
|
sme_save_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
|
||||||
|
|
||||||
|
cbz x1, 1f
|
||||||
|
_str_zt 0
|
||||||
|
1:
|
||||||
ret
|
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
|
* x0 - pointer to buffer for state
|
||||||
|
* x1 - number of ZT registers to save
|
||||||
*/
|
*/
|
||||||
SYM_FUNC_START(za_load_state)
|
SYM_FUNC_START(sme_load_state)
|
||||||
_sme_rdsvl 1, 1 // x1 = VL/8
|
_sme_rdsvl 2, 1 // x2 = VL/8
|
||||||
sme_load_za 0, x1, 12
|
sme_load_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
|
||||||
|
|
||||||
|
cbz x1, 1f
|
||||||
|
_ldr_zt 0
|
||||||
|
1:
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(za_load_state)
|
SYM_FUNC_END(sme_load_state)
|
||||||
|
|
||||||
#endif /* CONFIG_ARM64_SME */
|
#endif /* CONFIG_ARM64_SME */
|
||||||
|
@ -65,13 +65,35 @@ SYM_CODE_START(ftrace_caller)
|
|||||||
stp x29, x30, [sp, #FREGS_SIZE]
|
stp x29, x30, [sp, #FREGS_SIZE]
|
||||||
add x29, sp, #FREGS_SIZE
|
add x29, sp, #FREGS_SIZE
|
||||||
|
|
||||||
sub x0, x30, #AARCH64_INSN_SIZE // ip (callsite's BL insn)
|
/* Prepare arguments for the the tracer func */
|
||||||
mov x1, x9 // parent_ip (callsite's LR)
|
sub x0, x30, #AARCH64_INSN_SIZE // ip (callsite's BL insn)
|
||||||
ldr_l x2, function_trace_op // op
|
mov x1, x9 // parent_ip (callsite's LR)
|
||||||
mov x3, sp // regs
|
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)
|
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
|
* 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
|
alternative_else_nop_endif
|
||||||
1:
|
1:
|
||||||
|
|
||||||
scs_load tsk
|
scs_load_current
|
||||||
.else
|
.else
|
||||||
add x21, sp, #PT_REGS_SIZE
|
add x21, sp, #PT_REGS_SIZE
|
||||||
get_current_task tsk
|
get_current_task tsk
|
||||||
@ -311,13 +311,16 @@ alternative_else_nop_endif
|
|||||||
.endif
|
.endif
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||||
/* Save pmr */
|
alternative_if_not ARM64_HAS_GIC_PRIO_MASKING
|
||||||
alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
b .Lskip_pmr_save\@
|
||||||
|
alternative_else_nop_endif
|
||||||
|
|
||||||
mrs_s x20, SYS_ICC_PMR_EL1
|
mrs_s x20, SYS_ICC_PMR_EL1
|
||||||
str x20, [sp, #S_PMR_SAVE]
|
str x20, [sp, #S_PMR_SAVE]
|
||||||
mov x20, #GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET
|
mov x20, #GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET
|
||||||
msr_s SYS_ICC_PMR_EL1, x20
|
msr_s SYS_ICC_PMR_EL1, x20
|
||||||
alternative_else_nop_endif
|
|
||||||
|
.Lskip_pmr_save\@:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -336,15 +339,19 @@ alternative_else_nop_endif
|
|||||||
.endif
|
.endif
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||||
/* Restore pmr */
|
alternative_if_not ARM64_HAS_GIC_PRIO_MASKING
|
||||||
alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
b .Lskip_pmr_restore\@
|
||||||
|
alternative_else_nop_endif
|
||||||
|
|
||||||
ldr x20, [sp, #S_PMR_SAVE]
|
ldr x20, [sp, #S_PMR_SAVE]
|
||||||
msr_s SYS_ICC_PMR_EL1, x20
|
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
|
/* Ensure priority change is seen by redistributor */
|
||||||
dsb sy // Ensure priority change is seen by redistributor
|
alternative_if_not ARM64_HAS_GIC_PRIO_RELAXED_SYNC
|
||||||
.L__skip_pmr_sync\@:
|
dsb sy
|
||||||
alternative_else_nop_endif
|
alternative_else_nop_endif
|
||||||
|
|
||||||
|
.Lskip_pmr_restore\@:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
|
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
|
||||||
@ -848,7 +855,7 @@ SYM_FUNC_START(cpu_switch_to)
|
|||||||
msr sp_el0, x1
|
msr sp_el0, x1
|
||||||
ptrauth_keys_install_kernel x1, x8, x9, x10
|
ptrauth_keys_install_kernel x1, x8, x9, x10
|
||||||
scs_save x0
|
scs_save x0
|
||||||
scs_load x1
|
scs_load_current
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(cpu_switch_to)
|
SYM_FUNC_END(cpu_switch_to)
|
||||||
NOKPROBE(cpu_switch_to)
|
NOKPROBE(cpu_switch_to)
|
||||||
@ -876,19 +883,19 @@ NOKPROBE(ret_from_fork)
|
|||||||
*/
|
*/
|
||||||
SYM_FUNC_START(call_on_irq_stack)
|
SYM_FUNC_START(call_on_irq_stack)
|
||||||
#ifdef CONFIG_SHADOW_CALL_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
|
ldr_this_cpu scs_sp, irq_shadow_call_stack_ptr, x17
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Create a frame record to save our LR and SP (implicit in FP) */
|
/* Create a frame record to save our LR and SP (implicit in FP) */
|
||||||
stp x29, x30, [sp, #-16]!
|
stp x29, x30, [sp, #-16]!
|
||||||
mov x29, sp
|
mov x29, sp
|
||||||
|
|
||||||
ldr_this_cpu x16, irq_stack_ptr, x17
|
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 */
|
/* Move to the new stack and call the function there */
|
||||||
mov sp, x16
|
add sp, x16, #IRQ_STACK_SIZE
|
||||||
blr x1
|
blr x1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -897,9 +904,7 @@ SYM_FUNC_START(call_on_irq_stack)
|
|||||||
*/
|
*/
|
||||||
mov sp, x29
|
mov sp, x29
|
||||||
ldp x29, x30, [sp], #16
|
ldp x29, x30, [sp], #16
|
||||||
#ifdef CONFIG_SHADOW_CALL_STACK
|
scs_load_current
|
||||||
ldp scs_sp, xzr, [sp], #16
|
|
||||||
#endif
|
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(call_on_irq_stack)
|
SYM_FUNC_END(call_on_irq_stack)
|
||||||
NOKPROBE(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
|
* TIF_SME controls whether a task can use SME without trapping while
|
||||||
* in userspace, when TIF_SME is set then we must have storage
|
* 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.
|
* 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
|
* 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);
|
write_sysreg_s(current->thread.svcr, SYS_SVCR);
|
||||||
|
|
||||||
if (thread_za_enabled(¤t->thread))
|
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))
|
if (thread_sm_enabled(¤t->thread))
|
||||||
restore_ffr = system_supports_fa64();
|
restore_ffr = system_supports_fa64();
|
||||||
@ -490,7 +491,8 @@ static void fpsimd_save(void)
|
|||||||
*svcr = read_sysreg_s(SYS_SVCR);
|
*svcr = read_sysreg_s(SYS_SVCR);
|
||||||
|
|
||||||
if (*svcr & SVCR_ZA_MASK)
|
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 we are in streaming mode override regular SVE. */
|
||||||
if (*svcr & SVCR_SM_MASK) {
|
if (*svcr & SVCR_SM_MASK) {
|
||||||
@ -1257,30 +1259,30 @@ void fpsimd_release_task(struct task_struct *dead_task)
|
|||||||
#ifdef CONFIG_ARM64_SME
|
#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
|
* 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
|
* here to prevent stale data from showing through: this is done in
|
||||||
* the interest of testability and predictability, the architecture
|
* the interest of testability and predictability, the architecture
|
||||||
* guarantees that when ZA is enabled it will be zeroed.
|
* guarantees that when ZA is enabled it will be zeroed.
|
||||||
*/
|
*/
|
||||||
void sme_alloc(struct task_struct *task)
|
void sme_alloc(struct task_struct *task)
|
||||||
{
|
{
|
||||||
if (task->thread.za_state) {
|
if (task->thread.sme_state) {
|
||||||
memset(task->thread.za_state, 0, za_state_size(task));
|
memset(task->thread.sme_state, 0, sme_state_size(task));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This could potentially be up to 64K. */
|
/* This could potentially be up to 64K. */
|
||||||
task->thread.za_state =
|
task->thread.sme_state =
|
||||||
kzalloc(za_state_size(task), GFP_KERNEL);
|
kzalloc(sme_state_size(task), GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sme_free(struct task_struct *task)
|
static void sme_free(struct task_struct *task)
|
||||||
{
|
{
|
||||||
kfree(task->thread.za_state);
|
kfree(task->thread.sme_state);
|
||||||
task->thread.za_state = NULL;
|
task->thread.sme_state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sme_kernel_enable(const struct arm64_cpu_capabilities *__always_unused p)
|
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();
|
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
|
* This must be called after sme_kernel_enable(), we rely on the
|
||||||
* feature table being sorted to ensure this.
|
* feature table being sorted to ensure this.
|
||||||
@ -1322,7 +1335,6 @@ u64 read_smcr_features(void)
|
|||||||
unsigned int vq_max;
|
unsigned int vq_max;
|
||||||
|
|
||||||
sme_kernel_enable(NULL);
|
sme_kernel_enable(NULL);
|
||||||
sme_smstart_sm();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the maximum possible VL.
|
* Set the maximum possible VL.
|
||||||
@ -1332,11 +1344,9 @@ u64 read_smcr_features(void)
|
|||||||
|
|
||||||
smcr = read_sysreg_s(SYS_SMCR_EL1);
|
smcr = read_sysreg_s(SYS_SMCR_EL1);
|
||||||
smcr &= ~(u64)SMCR_ELx_LEN_MASK; /* Only the LEN field */
|
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 */
|
smcr |= vq_max - 1; /* set LEN field to maximum effective value */
|
||||||
|
|
||||||
sme_smstop_sm();
|
|
||||||
|
|
||||||
return smcr;
|
return smcr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1488,7 +1498,7 @@ void do_sme_acc(unsigned long esr, struct pt_regs *regs)
|
|||||||
|
|
||||||
sve_alloc(current, false);
|
sve_alloc(current, false);
|
||||||
sme_alloc(current);
|
sme_alloc(current);
|
||||||
if (!current->thread.sve_state || !current->thread.za_state) {
|
if (!current->thread.sve_state || !current->thread.sme_state) {
|
||||||
force_sig(SIGKILL);
|
force_sig(SIGKILL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1609,7 +1619,7 @@ static void fpsimd_flush_thread_vl(enum vec_type type)
|
|||||||
void fpsimd_flush_thread(void)
|
void fpsimd_flush_thread(void)
|
||||||
{
|
{
|
||||||
void *sve_state = NULL;
|
void *sve_state = NULL;
|
||||||
void *za_state = NULL;
|
void *sme_state = NULL;
|
||||||
|
|
||||||
if (!system_supports_fpsimd())
|
if (!system_supports_fpsimd())
|
||||||
return;
|
return;
|
||||||
@ -1634,8 +1644,8 @@ void fpsimd_flush_thread(void)
|
|||||||
clear_thread_flag(TIF_SME);
|
clear_thread_flag(TIF_SME);
|
||||||
|
|
||||||
/* Defer kfree() while in atomic context */
|
/* Defer kfree() while in atomic context */
|
||||||
za_state = current->thread.za_state;
|
sme_state = current->thread.sme_state;
|
||||||
current->thread.za_state = NULL;
|
current->thread.sme_state = NULL;
|
||||||
|
|
||||||
fpsimd_flush_thread_vl(ARM64_VEC_SME);
|
fpsimd_flush_thread_vl(ARM64_VEC_SME);
|
||||||
current->thread.svcr = 0;
|
current->thread.svcr = 0;
|
||||||
@ -1645,7 +1655,7 @@ void fpsimd_flush_thread(void)
|
|||||||
|
|
||||||
put_cpu_fpsimd_context();
|
put_cpu_fpsimd_context();
|
||||||
kfree(sve_state);
|
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());
|
WARN_ON(!system_supports_fpsimd());
|
||||||
last->st = ¤t->thread.uw.fpsimd_state;
|
last->st = ¤t->thread.uw.fpsimd_state;
|
||||||
last->sve_state = current->thread.sve_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->sve_vl = task_get_sve_vl(current);
|
||||||
last->sme_vl = task_get_sme_vl(current);
|
last->sme_vl = task_get_sme_vl(current);
|
||||||
last->svcr = ¤t->thread.svcr;
|
last->svcr = ¤t->thread.svcr;
|
||||||
|
@ -60,6 +60,89 @@ int ftrace_regs_query_register_offset(const char *name)
|
|||||||
}
|
}
|
||||||
#endif
|
#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.
|
* Replace a single instruction, which may be a branch or NOP.
|
||||||
* If @validate == true, a replaced instruction is checked against 'old'.
|
* 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;
|
unsigned long pc;
|
||||||
u32 new;
|
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;
|
pc = (unsigned long)ftrace_call;
|
||||||
new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func,
|
new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func,
|
||||||
AARCH64_INSN_BRANCH_LINK);
|
AARCH64_INSN_BRANCH_LINK);
|
||||||
@ -176,6 +266,44 @@ static bool ftrace_find_callable_addr(struct dyn_ftrace *rec,
|
|||||||
return true;
|
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
|
* 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;
|
unsigned long pc = rec->ip;
|
||||||
u32 old, new;
|
u32 old, new;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = ftrace_rec_update_ops(rec);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (!ftrace_find_callable_addr(rec, NULL, &addr))
|
if (!ftrace_find_callable_addr(rec, NULL, &addr))
|
||||||
return -EINVAL;
|
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);
|
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
|
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
||||||
/*
|
/*
|
||||||
* The compiler has inserted two NOPs before the regular function prologue.
|
* 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 | MOV X9, LR | MOV X9, LR |
|
||||||
* | NOP | NOP | BL <entry> |
|
* | 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
|
* 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.
|
* 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;
|
unsigned long pc = rec->ip - AARCH64_INSN_SIZE;
|
||||||
u32 old, new;
|
u32 old, new;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = ftrace_rec_set_nop_ops(rec);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
old = aarch64_insn_gen_nop();
|
old = aarch64_insn_gen_nop();
|
||||||
new = aarch64_insn_gen_move_reg(AARCH64_INSN_REG_9,
|
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;
|
unsigned long pc = rec->ip;
|
||||||
u32 old = 0, new;
|
u32 old = 0, new;
|
||||||
|
int ret;
|
||||||
|
|
||||||
new = aarch64_insn_gen_nop();
|
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
|
* When using mcount, callsites in modules may have been initalized to
|
||||||
* call an arbitrary module PLT (which redirects to the _mcount stub)
|
* call an arbitrary module PLT (which redirects to the _mcount stub)
|
||||||
|
@ -70,13 +70,14 @@
|
|||||||
|
|
||||||
__EFI_PE_HEADER
|
__EFI_PE_HEADER
|
||||||
|
|
||||||
__INIT
|
.section ".idmap.text","awx"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following callee saved general purpose registers are used on the
|
* The following callee saved general purpose registers are used on the
|
||||||
* primary lowlevel boot path:
|
* primary lowlevel boot path:
|
||||||
*
|
*
|
||||||
* Register Scope Purpose
|
* Register Scope Purpose
|
||||||
|
* x19 primary_entry() .. start_kernel() whether we entered with the MMU on
|
||||||
* x20 primary_entry() .. __primary_switch() CPU boot mode
|
* x20 primary_entry() .. __primary_switch() CPU boot mode
|
||||||
* x21 primary_entry() .. start_kernel() FDT pointer passed at boot in x0
|
* x21 primary_entry() .. start_kernel() FDT pointer passed at boot in x0
|
||||||
* x22 create_idmap() .. start_kernel() ID map VA of the DT blob
|
* x22 create_idmap() .. start_kernel() ID map VA of the DT blob
|
||||||
@ -86,10 +87,22 @@
|
|||||||
* x28 create_idmap() callee preserved temp register
|
* x28 create_idmap() callee preserved temp register
|
||||||
*/
|
*/
|
||||||
SYM_CODE_START(primary_entry)
|
SYM_CODE_START(primary_entry)
|
||||||
|
bl record_mmu_state
|
||||||
bl preserve_boot_args
|
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
|
bl init_kernel_el // w0=cpu_boot_mode
|
||||||
mov x20, x0
|
mov x20, x0
|
||||||
bl create_idmap
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following calls CPU setup code, see arch/arm64/mm/proc.S for
|
* 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
|
b __primary_switch
|
||||||
SYM_CODE_END(primary_entry)
|
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
|
* 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 x21, x1, [x0] // x0 .. x3 at kernel entry
|
||||||
stp x2, x3, [x0, #16]
|
stp x2, x3, [x0, #16]
|
||||||
|
|
||||||
|
cbnz x19, 0f // skip cache invalidation if MMU is on
|
||||||
dmb sy // needed before dc ivac with
|
dmb sy // needed before dc ivac with
|
||||||
// MMU off
|
// MMU off
|
||||||
|
|
||||||
add x1, x0, #0x20 // 4 x 8 bytes
|
add x1, x0, #0x20 // 4 x 8 bytes
|
||||||
b dcache_inval_poc // tail call
|
b dcache_inval_poc // tail call
|
||||||
|
0: str_l x19, mmu_enabled_at_boot, x0
|
||||||
|
ret
|
||||||
SYM_CODE_END(preserve_boot_args)
|
SYM_CODE_END(preserve_boot_args)
|
||||||
|
|
||||||
SYM_FUNC_START_LOCAL(clear_page_tables)
|
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
|
* accesses (MMU disabled), invalidate those tables again to
|
||||||
* remove any speculatively loaded cache lines.
|
* remove any speculatively loaded cache lines.
|
||||||
*/
|
*/
|
||||||
|
cbnz x19, 0f // skip cache invalidation if MMU is on
|
||||||
dmb sy
|
dmb sy
|
||||||
|
|
||||||
adrp x0, init_idmap_pg_dir
|
adrp x0, init_idmap_pg_dir
|
||||||
adrp x1, init_idmap_pg_end
|
adrp x1, init_idmap_pg_end
|
||||||
bl dcache_inval_poc
|
bl dcache_inval_poc
|
||||||
ret x28
|
0: ret x28
|
||||||
SYM_FUNC_END(create_idmap)
|
SYM_FUNC_END(create_idmap)
|
||||||
|
|
||||||
SYM_FUNC_START_LOCAL(create_kernel_mapping)
|
SYM_FUNC_START_LOCAL(create_kernel_mapping)
|
||||||
@ -404,7 +455,7 @@ SYM_FUNC_END(create_kernel_mapping)
|
|||||||
stp xzr, xzr, [sp, #S_STACKFRAME]
|
stp xzr, xzr, [sp, #S_STACKFRAME]
|
||||||
add x29, sp, #S_STACKFRAME
|
add x29, sp, #S_STACKFRAME
|
||||||
|
|
||||||
scs_load \tsk
|
scs_load_current
|
||||||
|
|
||||||
adr_l \tmp1, __per_cpu_offset
|
adr_l \tmp1, __per_cpu_offset
|
||||||
ldr w\tmp2, [\tsk, #TSK_TI_CPU]
|
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
|
* 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
|
* booted in EL1 or EL2 respectively, with the top 32 bits containing
|
||||||
* potential context flags. These flags are *not* stored in __boot_cpu_mode.
|
* 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)
|
SYM_FUNC_START(init_kernel_el)
|
||||||
mrs x0, CurrentEL
|
mrs x1, CurrentEL
|
||||||
cmp x0, #CurrentEL_EL2
|
cmp x1, #CurrentEL_EL2
|
||||||
b.eq init_el2
|
b.eq init_el2
|
||||||
|
|
||||||
SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)
|
SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)
|
||||||
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
|
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
|
||||||
|
pre_disable_mmu_workaround
|
||||||
msr sctlr_el1, x0
|
msr sctlr_el1, x0
|
||||||
isb
|
isb
|
||||||
mov_q x0, INIT_PSTATE_EL1
|
mov_q x0, INIT_PSTATE_EL1
|
||||||
@ -506,6 +560,14 @@ SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)
|
|||||||
eret
|
eret
|
||||||
|
|
||||||
SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
|
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
|
mov_q x0, HCR_HOST_NVHE_FLAGS
|
||||||
msr hcr_el2, x0
|
msr hcr_el2, x0
|
||||||
isb
|
isb
|
||||||
@ -529,38 +591,27 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
|
|||||||
cbz x0, 1f
|
cbz x0, 1f
|
||||||
|
|
||||||
/* Set a sane SCTLR_EL1, the VHE way */
|
/* Set a sane SCTLR_EL1, the VHE way */
|
||||||
|
pre_disable_mmu_workaround
|
||||||
msr_s SYS_SCTLR_EL12, x1
|
msr_s SYS_SCTLR_EL12, x1
|
||||||
mov x2, #BOOT_CPU_FLAG_E2H
|
mov x2, #BOOT_CPU_FLAG_E2H
|
||||||
b 2f
|
b 2f
|
||||||
|
|
||||||
1:
|
1:
|
||||||
|
pre_disable_mmu_workaround
|
||||||
msr sctlr_el1, x1
|
msr sctlr_el1, x1
|
||||||
mov x2, xzr
|
mov x2, xzr
|
||||||
2:
|
2:
|
||||||
msr elr_el2, lr
|
|
||||||
mov w0, #BOOT_CPU_MODE_EL2
|
mov w0, #BOOT_CPU_MODE_EL2
|
||||||
orr x0, x0, x2
|
orr x0, x0, x2
|
||||||
eret
|
eret
|
||||||
SYM_FUNC_END(init_kernel_el)
|
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
|
* This provides a "holding pen" for platforms to hold all secondary
|
||||||
* cores are held until we're ready for them to initialise.
|
* cores are held until we're ready for them to initialise.
|
||||||
*/
|
*/
|
||||||
SYM_FUNC_START(secondary_holding_pen)
|
SYM_FUNC_START(secondary_holding_pen)
|
||||||
|
mov x0, xzr
|
||||||
bl init_kernel_el // w0=cpu_boot_mode
|
bl init_kernel_el // w0=cpu_boot_mode
|
||||||
mrs x2, mpidr_el1
|
mrs x2, mpidr_el1
|
||||||
mov_q x1, MPIDR_HWID_BITMASK
|
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.
|
* be used where CPUs are brought online dynamically by the kernel.
|
||||||
*/
|
*/
|
||||||
SYM_FUNC_START(secondary_entry)
|
SYM_FUNC_START(secondary_entry)
|
||||||
|
mov x0, xzr
|
||||||
bl init_kernel_el // w0=cpu_boot_mode
|
bl init_kernel_el // w0=cpu_boot_mode
|
||||||
b secondary_startup
|
b secondary_startup
|
||||||
SYM_FUNC_END(secondary_entry)
|
SYM_FUNC_END(secondary_entry)
|
||||||
@ -587,7 +639,6 @@ SYM_FUNC_START_LOCAL(secondary_startup)
|
|||||||
* Common entry point for secondary CPUs.
|
* Common entry point for secondary CPUs.
|
||||||
*/
|
*/
|
||||||
mov x20, x0 // preserve boot mode
|
mov x20, x0 // preserve boot mode
|
||||||
bl finalise_el2
|
|
||||||
bl __cpu_secondary_check52bitva
|
bl __cpu_secondary_check52bitva
|
||||||
#if VA_BITS > 48
|
#if VA_BITS > 48
|
||||||
ldr_l x0, vabits_actual
|
ldr_l x0, vabits_actual
|
||||||
@ -600,9 +651,14 @@ SYM_FUNC_START_LOCAL(secondary_startup)
|
|||||||
br x8
|
br x8
|
||||||
SYM_FUNC_END(secondary_startup)
|
SYM_FUNC_END(secondary_startup)
|
||||||
|
|
||||||
|
.text
|
||||||
SYM_FUNC_START_LOCAL(__secondary_switched)
|
SYM_FUNC_START_LOCAL(__secondary_switched)
|
||||||
mov x0, x20
|
mov x0, x20
|
||||||
bl set_cpu_boot_mode_flag
|
bl set_cpu_boot_mode_flag
|
||||||
|
|
||||||
|
mov x0, x20
|
||||||
|
bl finalise_el2
|
||||||
|
|
||||||
str_l xzr, __early_cpu_boot_status, x3
|
str_l xzr, __early_cpu_boot_status, x3
|
||||||
adr_l x5, vectors
|
adr_l x5, vectors
|
||||||
msr vbar_el1, x5
|
msr vbar_el1, x5
|
||||||
@ -628,6 +684,19 @@ SYM_FUNC_START_LOCAL(__secondary_too_slow)
|
|||||||
b __secondary_too_slow
|
b __secondary_too_slow
|
||||||
SYM_FUNC_END(__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,
|
* The booting CPU updates the failed status @__early_cpu_boot_status,
|
||||||
* with MMU turned off.
|
* 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.
|
* Checks if the selected granule size is supported by the CPU.
|
||||||
* If it isn't, park the CPU
|
* If it isn't, park the CPU
|
||||||
*/
|
*/
|
||||||
|
.section ".idmap.text","awx"
|
||||||
SYM_FUNC_START(__enable_mmu)
|
SYM_FUNC_START(__enable_mmu)
|
||||||
mrs x3, ID_AA64MMFR0_EL1
|
mrs x3, ID_AA64MMFR0_EL1
|
||||||
ubfx x3, x3, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4
|
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
|
orr x0, x0, SMCR_ELx_FA64_MASK
|
||||||
.Lskip_sme_fa64:
|
.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
|
orr x0, x0, #SMCR_ELx_LEN_MASK // Enable full SME vector
|
||||||
msr_s SYS_SMCR_EL2, x0 // length for EL1.
|
msr_s SYS_SMCR_EL2, x0 // length for EL1.
|
||||||
|
|
||||||
|
@ -131,6 +131,7 @@ static const struct ftr_set_desc smfr0 __initconst = {
|
|||||||
.name = "id_aa64smfr0",
|
.name = "id_aa64smfr0",
|
||||||
.override = &id_aa64smfr0_override,
|
.override = &id_aa64smfr0_override,
|
||||||
.fields = {
|
.fields = {
|
||||||
|
FIELD("smever", ID_AA64SMFR0_EL1_SMEver_SHIFT, NULL),
|
||||||
/* FA64 is a one bit field... :-/ */
|
/* FA64 is a one bit field... :-/ */
|
||||||
{ "fa64", ID_AA64SMFR0_EL1_FA64_SHIFT, 1, },
|
{ "fa64", ID_AA64SMFR0_EL1_FA64_SHIFT, 1, },
|
||||||
{}
|
{}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#error This file should only be included in vmlinux.lds.S
|
#error This file should only be included in vmlinux.lds.S
|
||||||
#endif
|
#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
|
* 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
|
* linked at. The routines below are all implemented in assembler in a
|
||||||
* position independent manner
|
* 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__text = _text);
|
||||||
PROVIDE(__efistub__end = _end);
|
PROVIDE(__efistub__end = _end);
|
||||||
|
PROVIDE(__efistub___inittext_end = __inittext_end);
|
||||||
PROVIDE(__efistub__edata = _edata);
|
PROVIDE(__efistub__edata = _edata);
|
||||||
PROVIDE(__efistub_screen_info = screen_info);
|
PROVIDE(__efistub_screen_info = screen_info);
|
||||||
PROVIDE(__efistub__ctype = _ctype);
|
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_v2_cpuif_trap);
|
||||||
KVM_NVHE_ALIAS(vgic_v3_cpuif_trap);
|
KVM_NVHE_ALIAS(vgic_v3_cpuif_trap);
|
||||||
|
|
||||||
/* Static key checked in pmr_sync(). */
|
|
||||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||||
KVM_NVHE_ALIAS(gic_pmr_sync);
|
|
||||||
/* Static key checked in GIC_PRIO_IRQOFF. */
|
/* Static key checked in GIC_PRIO_IRQOFF. */
|
||||||
KVM_NVHE_ALIAS(gic_nonsecure_priorities);
|
KVM_NVHE_ALIAS(gic_nonsecure_priorities);
|
||||||
#endif
|
#endif
|
||||||
|
@ -130,7 +130,8 @@ struct eh_frame {
|
|||||||
|
|
||||||
static int noinstr scs_handle_fde_frame(const struct eh_frame *frame,
|
static int noinstr scs_handle_fde_frame(const struct eh_frame *frame,
|
||||||
bool fde_has_augmentation_data,
|
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;
|
int size = frame->size - offsetof(struct eh_frame, opcodes) + 4;
|
||||||
u64 loc = (u64)offset_to_ptr(&frame->initial_loc);
|
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;
|
break;
|
||||||
|
|
||||||
case DW_CFA_negate_ra_state:
|
case DW_CFA_negate_ra_state:
|
||||||
scs_patch_loc(loc - 4);
|
if (!dry_run)
|
||||||
|
scs_patch_loc(loc - 4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x40 ... 0x7f:
|
case 0x40 ... 0x7f:
|
||||||
@ -235,9 +237,12 @@ int noinstr scs_patch(const u8 eh_frame[], int size)
|
|||||||
} else {
|
} else {
|
||||||
ret = scs_handle_fde_frame(frame,
|
ret = scs_handle_fde_frame(frame,
|
||||||
fde_has_augmentation_data,
|
fde_has_augmentation_data,
|
||||||
code_alignment_factor);
|
code_alignment_factor,
|
||||||
|
true);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
scs_handle_fde_frame(frame, fde_has_augmentation_data,
|
||||||
|
code_alignment_factor, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
p += sizeof(frame->size) + frame->size;
|
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));
|
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)
|
int __kprobes aarch64_insn_patch_text_nosync(void *addr, u32 insn)
|
||||||
{
|
{
|
||||||
u32 *tp = addr;
|
u32 *tp = addr;
|
||||||
|
@ -387,10 +387,6 @@ int __init arch_populate_kprobe_blacklist(void)
|
|||||||
(unsigned long)__irqentry_text_end);
|
(unsigned long)__irqentry_text_end);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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,
|
ret = kprobe_add_area_blacklist((unsigned long)__hyp_text_start,
|
||||||
(unsigned long)__hyp_text_end);
|
(unsigned long)__hyp_text_end);
|
||||||
if (ret || is_kernel_in_hyp_mode())
|
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
|
* In the unlikely event that we create a new thread with ZA
|
||||||
* enabled we should retain the ZA state so duplicate it here.
|
* enabled we should retain the ZA and ZT state so duplicate
|
||||||
* This may be shortly freed if we exec() or if CLONE_SETTLS
|
* it here. This may be shortly freed if we exec() or if
|
||||||
* but it's simpler to do it here. To avoid confusing the rest
|
* CLONE_SETTLS but it's simpler to do it here. To avoid
|
||||||
* of the code ensure that we have a sve_state allocated
|
* confusing the rest of the code ensure that we have a
|
||||||
* whenever za_state is allocated.
|
* sve_state allocated whenever sme_state is allocated.
|
||||||
*/
|
*/
|
||||||
if (thread_za_enabled(&src->thread)) {
|
if (thread_za_enabled(&src->thread)) {
|
||||||
dst->thread.sve_state = kzalloc(sve_state_size(src),
|
dst->thread.sve_state = kzalloc(sve_state_size(src),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!dst->thread.sve_state)
|
if (!dst->thread.sve_state)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
dst->thread.za_state = kmemdup(src->thread.za_state,
|
|
||||||
za_state_size(src),
|
dst->thread.sme_state = kmemdup(src->thread.sme_state,
|
||||||
GFP_KERNEL);
|
sme_state_size(src),
|
||||||
if (!dst->thread.za_state) {
|
GFP_KERNEL);
|
||||||
|
if (!dst->thread.sme_state) {
|
||||||
kfree(dst->thread.sve_state);
|
kfree(dst->thread.sve_state);
|
||||||
dst->thread.sve_state = NULL;
|
dst->thread.sve_state = NULL;
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dst->thread.za_state = NULL;
|
dst->thread.sme_state = NULL;
|
||||||
clear_tsk_thread_flag(dst, TIF_SME);
|
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];
|
unsigned long tls[2];
|
||||||
|
|
||||||
tls[0] = target->thread.uw.tp_value;
|
tls[0] = target->thread.uw.tp_value;
|
||||||
if (system_supports_sme())
|
if (system_supports_tpidr2())
|
||||||
tls[1] = target->thread.tpidr2_el0;
|
tls[1] = target->thread.tpidr2_el0;
|
||||||
|
|
||||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, tls, 0, count);
|
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;
|
return ret;
|
||||||
|
|
||||||
target->thread.uw.tp_value = tls[0];
|
target->thread.uw.tp_value = tls[0];
|
||||||
if (system_supports_sme())
|
if (system_supports_tpidr2())
|
||||||
target->thread.tpidr2_el0 = tls[1];
|
target->thread.tpidr2_el0 = tls[1];
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1045,7 +1045,7 @@ static int za_get(struct task_struct *target,
|
|||||||
if (thread_za_enabled(&target->thread)) {
|
if (thread_za_enabled(&target->thread)) {
|
||||||
start = end;
|
start = end;
|
||||||
end = ZA_PT_SIZE(vq);
|
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 */
|
/* Zero any trailing padding */
|
||||||
@ -1099,7 +1099,7 @@ static int za_set(struct task_struct *target,
|
|||||||
|
|
||||||
/* Allocate/reinit ZA storage */
|
/* Allocate/reinit ZA storage */
|
||||||
sme_alloc(target);
|
sme_alloc(target);
|
||||||
if (!target->thread.za_state) {
|
if (!target->thread.sme_state) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1124,7 +1124,7 @@ static int za_set(struct task_struct *target,
|
|||||||
start = ZA_PT_ZA_OFFSET;
|
start = ZA_PT_ZA_OFFSET;
|
||||||
end = ZA_PT_SIZE(vq);
|
end = ZA_PT_SIZE(vq);
|
||||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||||
target->thread.za_state,
|
target->thread.sme_state,
|
||||||
start, end);
|
start, end);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
@ -1138,6 +1138,51 @@ out:
|
|||||||
return ret;
|
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 */
|
#endif /* CONFIG_ARM64_SME */
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||||
@ -1360,6 +1405,7 @@ enum aarch64_regset {
|
|||||||
#ifdef CONFIG_ARM64_SVE
|
#ifdef CONFIG_ARM64_SVE
|
||||||
REGSET_SSVE,
|
REGSET_SSVE,
|
||||||
REGSET_ZA,
|
REGSET_ZA,
|
||||||
|
REGSET_ZT,
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||||
REGSET_PAC_MASK,
|
REGSET_PAC_MASK,
|
||||||
@ -1467,6 +1513,14 @@ static const struct user_regset aarch64_regsets[] = {
|
|||||||
.regset_get = za_get,
|
.regset_get = za_get,
|
||||||
.set = za_set,
|
.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
|
#endif
|
||||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||||
[REGSET_PAC_MASK] = {
|
[REGSET_PAC_MASK] = {
|
||||||
|
@ -58,6 +58,7 @@ static int num_standard_resources;
|
|||||||
static struct resource *standard_resources;
|
static struct resource *standard_resources;
|
||||||
|
|
||||||
phys_addr_t __fdt_pointer __initdata;
|
phys_addr_t __fdt_pointer __initdata;
|
||||||
|
u64 mmu_enabled_at_boot __initdata;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Standard memory resources
|
* Standard memory resources
|
||||||
@ -332,8 +333,12 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
|
|||||||
xen_early_init();
|
xen_early_init();
|
||||||
efi_init();
|
efi_init();
|
||||||
|
|
||||||
if (!efi_enabled(EFI_BOOT) && ((u64)_text % MIN_KIMG_ALIGN) != 0)
|
if (!efi_enabled(EFI_BOOT)) {
|
||||||
pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
|
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();
|
arm64_memblock_init();
|
||||||
|
|
||||||
@ -442,3 +447,11 @@ static int __init register_arm64_panic_block(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
device_initcall(register_arm64_panic_block);
|
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 fpsimd_offset;
|
||||||
unsigned long esr_offset;
|
unsigned long esr_offset;
|
||||||
unsigned long sve_offset;
|
unsigned long sve_offset;
|
||||||
|
unsigned long tpidr2_offset;
|
||||||
unsigned long za_offset;
|
unsigned long za_offset;
|
||||||
|
unsigned long zt_offset;
|
||||||
unsigned long extra_offset;
|
unsigned long extra_offset;
|
||||||
unsigned long end_offset;
|
unsigned long end_offset;
|
||||||
};
|
};
|
||||||
@ -220,7 +222,9 @@ static int restore_fpsimd_context(struct fpsimd_context __user *ctx)
|
|||||||
struct user_ctxs {
|
struct user_ctxs {
|
||||||
struct fpsimd_context __user *fpsimd;
|
struct fpsimd_context __user *fpsimd;
|
||||||
struct sve_context __user *sve;
|
struct sve_context __user *sve;
|
||||||
|
struct tpidr2_context __user *tpidr2;
|
||||||
struct za_context __user *za;
|
struct za_context __user *za;
|
||||||
|
struct zt_context __user *zt;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_SVE
|
#ifdef CONFIG_ARM64_SVE
|
||||||
@ -361,6 +365,32 @@ extern int preserve_sve_context(void __user *ctx);
|
|||||||
|
|
||||||
#ifdef CONFIG_ARM64_SME
|
#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)
|
static int preserve_za_context(struct za_context __user *ctx)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@ -389,7 +419,7 @@ static int preserve_za_context(struct za_context __user *ctx)
|
|||||||
* fpsimd_signal_preserve_current_state().
|
* fpsimd_signal_preserve_current_state().
|
||||||
*/
|
*/
|
||||||
err |= __copy_to_user((char __user *)ctx + ZA_SIG_REGS_OFFSET,
|
err |= __copy_to_user((char __user *)ctx + ZA_SIG_REGS_OFFSET,
|
||||||
current->thread.za_state,
|
current->thread.sme_state,
|
||||||
ZA_SIG_REGS_SIZE(vq));
|
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
|
* 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
|
* needed to prevent a racing context switch from writing stale
|
||||||
* registers back over the new data.
|
* 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 */
|
/* From now, fpsimd_thread_switch() won't touch thread.sve_state */
|
||||||
|
|
||||||
sme_alloc(current);
|
sme_alloc(current);
|
||||||
if (!current->thread.za_state) {
|
if (!current->thread.sme_state) {
|
||||||
current->thread.svcr &= ~SVCR_ZA_MASK;
|
current->thread.svcr &= ~SVCR_ZA_MASK;
|
||||||
clear_thread_flag(TIF_SME);
|
clear_thread_flag(TIF_SME);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = __copy_from_user(current->thread.za_state,
|
err = __copy_from_user(current->thread.sme_state,
|
||||||
(char __user const *)user->za +
|
(char __user const *)user->za +
|
||||||
ZA_SIG_REGS_OFFSET,
|
ZA_SIG_REGS_OFFSET,
|
||||||
ZA_SIG_REGS_SIZE(vq));
|
ZA_SIG_REGS_SIZE(vq));
|
||||||
@ -447,11 +477,83 @@ static int restore_za_context(struct user_ctxs *user)
|
|||||||
|
|
||||||
return 0;
|
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 */
|
#else /* ! CONFIG_ARM64_SME */
|
||||||
|
|
||||||
/* Turn any non-optimised out attempts to use these into a link error: */
|
/* 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 preserve_za_context(void __user *ctx);
|
||||||
extern int restore_za_context(struct user_ctxs *user);
|
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 */
|
#endif /* ! CONFIG_ARM64_SME */
|
||||||
|
|
||||||
@ -468,7 +570,9 @@ static int parse_user_sigframe(struct user_ctxs *user,
|
|||||||
|
|
||||||
user->fpsimd = NULL;
|
user->fpsimd = NULL;
|
||||||
user->sve = NULL;
|
user->sve = NULL;
|
||||||
|
user->tpidr2 = NULL;
|
||||||
user->za = NULL;
|
user->za = NULL;
|
||||||
|
user->zt = NULL;
|
||||||
|
|
||||||
if (!IS_ALIGNED((unsigned long)base, 16))
|
if (!IS_ALIGNED((unsigned long)base, 16))
|
||||||
goto invalid;
|
goto invalid;
|
||||||
@ -534,6 +638,19 @@ static int parse_user_sigframe(struct user_ctxs *user,
|
|||||||
user->sve = (struct sve_context __user *)head;
|
user->sve = (struct sve_context __user *)head;
|
||||||
break;
|
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:
|
case ZA_MAGIC:
|
||||||
if (!system_supports_sme())
|
if (!system_supports_sme())
|
||||||
goto invalid;
|
goto invalid;
|
||||||
@ -547,6 +664,19 @@ static int parse_user_sigframe(struct user_ctxs *user,
|
|||||||
user->za = (struct za_context __user *)head;
|
user->za = (struct za_context __user *)head;
|
||||||
break;
|
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:
|
case EXTRA_MAGIC:
|
||||||
if (have_extra_context)
|
if (have_extra_context)
|
||||||
goto invalid;
|
goto invalid;
|
||||||
@ -666,9 +796,15 @@ static int restore_sigframe(struct pt_regs *regs,
|
|||||||
err = restore_fpsimd_context(user.fpsimd);
|
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)
|
if (err == 0 && system_supports_sme() && user.za)
|
||||||
err = restore_za_context(&user);
|
err = restore_za_context(&user);
|
||||||
|
|
||||||
|
if (err == 0 && system_supports_sme2() && user.zt)
|
||||||
|
err = restore_zt_context(&user);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,6 +896,11 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
|
|||||||
else
|
else
|
||||||
vl = task_get_sme_vl(current);
|
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))
|
if (thread_za_enabled(¤t->thread))
|
||||||
vq = sve_vq_from_vl(vl);
|
vq = sve_vq_from_vl(vl);
|
||||||
|
|
||||||
@ -769,6 +910,15 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
|
|||||||
return err;
|
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);
|
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);
|
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 */
|
/* ZA state if present */
|
||||||
if (system_supports_sme() && err == 0 && user->za_offset) {
|
if (system_supports_sme() && err == 0 && user->za_offset) {
|
||||||
struct za_context __user *za_ctx =
|
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);
|
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) {
|
if (err == 0 && user->extra_offset) {
|
||||||
char __user *sfp = (char __user *)user->sigframe;
|
char __user *sfp = (char __user *)user->sigframe;
|
||||||
char __user *userp =
|
char __user *userp =
|
||||||
|
@ -99,8 +99,9 @@ SYM_FUNC_END(__cpu_suspend_enter)
|
|||||||
|
|
||||||
.pushsection ".idmap.text", "awx"
|
.pushsection ".idmap.text", "awx"
|
||||||
SYM_CODE_START(cpu_resume)
|
SYM_CODE_START(cpu_resume)
|
||||||
|
mov x0, xzr
|
||||||
bl init_kernel_el
|
bl init_kernel_el
|
||||||
bl finalise_el2
|
mov x19, x0 // preserve boot mode
|
||||||
#if VA_BITS > 48
|
#if VA_BITS > 48
|
||||||
ldr_l x0, vabits_actual
|
ldr_l x0, vabits_actual
|
||||||
#endif
|
#endif
|
||||||
@ -116,6 +117,9 @@ SYM_CODE_END(cpu_resume)
|
|||||||
.popsection
|
.popsection
|
||||||
|
|
||||||
SYM_FUNC_START(_cpu_resume)
|
SYM_FUNC_START(_cpu_resume)
|
||||||
|
mov x0, x19
|
||||||
|
bl finalise_el2
|
||||||
|
|
||||||
mrs x1, mpidr_el1
|
mrs x1, mpidr_el1
|
||||||
adr_l x8, mpidr_hash // x8 = struct mpidr_hash virt address
|
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
|
* register state to track, if this changes the KVM code will
|
||||||
* need updating.
|
* need updating.
|
||||||
*/
|
*/
|
||||||
if (system_supports_sme() && test_thread_flag(TIF_SME)) {
|
if (system_supports_sme())
|
||||||
u64 svcr = read_sysreg_s(SYS_SVCR);
|
sme_smstop_sm();
|
||||||
|
|
||||||
if (svcr & SVCR_SM_MASK)
|
|
||||||
sme_smstop_sm();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!system_supports_sve())
|
if (!system_supports_sve())
|
||||||
return;
|
return;
|
||||||
|
@ -162,10 +162,8 @@ static void dump_kernel_instr(const char *lvl, struct pt_regs *regs)
|
|||||||
|
|
||||||
if (!bad)
|
if (!bad)
|
||||||
p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
|
p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
|
||||||
else {
|
else
|
||||||
p += sprintf(p, "bad PC value");
|
p += sprintf(p, i == 0 ? "(????????) " : "???????? ");
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printk("%sCode: %s\n", lvl, str);
|
printk("%sCode: %s\n", lvl, str);
|
||||||
|
@ -93,6 +93,7 @@ jiffies = jiffies_64;
|
|||||||
|
|
||||||
#ifdef CONFIG_HIBERNATION
|
#ifdef CONFIG_HIBERNATION
|
||||||
#define HIBERNATE_TEXT \
|
#define HIBERNATE_TEXT \
|
||||||
|
ALIGN_FUNCTION(); \
|
||||||
__hibernate_exit_text_start = .; \
|
__hibernate_exit_text_start = .; \
|
||||||
*(.hibernate_exit.text) \
|
*(.hibernate_exit.text) \
|
||||||
__hibernate_exit_text_end = .;
|
__hibernate_exit_text_end = .;
|
||||||
@ -102,6 +103,7 @@ jiffies = jiffies_64;
|
|||||||
|
|
||||||
#ifdef CONFIG_KEXEC_CORE
|
#ifdef CONFIG_KEXEC_CORE
|
||||||
#define KEXEC_TEXT \
|
#define KEXEC_TEXT \
|
||||||
|
ALIGN_FUNCTION(); \
|
||||||
__relocate_new_kernel_start = .; \
|
__relocate_new_kernel_start = .; \
|
||||||
*(.kexec_relocate.text) \
|
*(.kexec_relocate.text) \
|
||||||
__relocate_new_kernel_end = .;
|
__relocate_new_kernel_end = .;
|
||||||
@ -179,7 +181,6 @@ SECTIONS
|
|||||||
LOCK_TEXT
|
LOCK_TEXT
|
||||||
KPROBES_TEXT
|
KPROBES_TEXT
|
||||||
HYPERVISOR_TEXT
|
HYPERVISOR_TEXT
|
||||||
IDMAP_TEXT
|
|
||||||
*(.gnu.warning)
|
*(.gnu.warning)
|
||||||
. = ALIGN(16);
|
. = ALIGN(16);
|
||||||
*(.got) /* Global offset table */
|
*(.got) /* Global offset table */
|
||||||
@ -206,6 +207,7 @@ SECTIONS
|
|||||||
TRAMP_TEXT
|
TRAMP_TEXT
|
||||||
HIBERNATE_TEXT
|
HIBERNATE_TEXT
|
||||||
KEXEC_TEXT
|
KEXEC_TEXT
|
||||||
|
IDMAP_TEXT
|
||||||
. = ALIGN(PAGE_SIZE);
|
. = ALIGN(PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,6 +357,8 @@ ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K,
|
|||||||
#ifdef CONFIG_HIBERNATION
|
#ifdef CONFIG_HIBERNATION
|
||||||
ASSERT(__hibernate_exit_text_end - __hibernate_exit_text_start <= SZ_4K,
|
ASSERT(__hibernate_exit_text_end - __hibernate_exit_text_start <= SZ_4K,
|
||||||
"Hibernate exit text is bigger than 4 KiB")
|
"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
|
#endif
|
||||||
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
|
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
|
||||||
ASSERT((__entry_tramp_text_end - __entry_tramp_text_start) <= 3*PAGE_SIZE,
|
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,
|
ASSERT(__relocate_new_kernel_end - __relocate_new_kernel_start <= SZ_4K,
|
||||||
"kexec relocation code is bigger than 4 KiB")
|
"kexec relocation code is bigger than 4 KiB")
|
||||||
ASSERT(KEXEC_CONTROL_PAGE_SIZE >= SZ_4K, "KEXEC_CONTROL_PAGE_SIZE is broken")
|
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
|
#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.st = &vcpu->arch.ctxt.fp_regs;
|
||||||
fp_state.sve_state = vcpu->arch.sve_state;
|
fp_state.sve_state = vcpu->arch.sve_state;
|
||||||
fp_state.sve_vl = vcpu->arch.sve_max_vl;
|
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.svcr = &vcpu->arch.svcr;
|
||||||
fp_state.fp_type = &vcpu->arch.fp_type;
|
fp_state.fp_type = &vcpu->arch.fp_type;
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ alternative_else
|
|||||||
dsb sy // Synchronize against in-flight ld/st
|
dsb sy // Synchronize against in-flight ld/st
|
||||||
isb // Prevent an early read of side-effect free ISR
|
isb // Prevent an early read of side-effect free ISR
|
||||||
mrs x2, isr_el1
|
mrs x2, isr_el1
|
||||||
tbnz x2, #8, 2f // ISR_EL1.A
|
tbnz x2, #ISR_EL1_A_SHIFT, 2f
|
||||||
ret
|
ret
|
||||||
nop
|
nop
|
||||||
2:
|
2:
|
||||||
|
@ -56,6 +56,7 @@ SYM_FUNC_START(caches_clean_inval_pou)
|
|||||||
caches_clean_inval_pou_macro
|
caches_clean_inval_pou_macro
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(caches_clean_inval_pou)
|
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)
|
* caches_clean_inval_user_pou(start,end)
|
||||||
|
@ -133,7 +133,7 @@ static phys_addr_t __init early_pgtable_alloc(int shift)
|
|||||||
return phys;
|
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
|
* 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;
|
pteval_t mask = PTE_PXN | PTE_RDONLY | PTE_WRITE | PTE_NG;
|
||||||
|
|
||||||
/* creating or taking down mappings is always safe */
|
/* creating or taking down mappings is always safe */
|
||||||
if (old == 0 || new == 0)
|
if (!pte_valid(__pte(old)) || !pte_valid(__pte(new)))
|
||||||
return true;
|
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 */
|
/* live contiguous mappings may not be manipulated at all */
|
||||||
if ((old | new) & PTE_CONT)
|
if ((old | new) & PTE_CONT)
|
||||||
return false;
|
return false;
|
||||||
|
@ -110,7 +110,6 @@ SYM_FUNC_END(cpu_do_suspend)
|
|||||||
*
|
*
|
||||||
* x0: Address of context pointer
|
* x0: Address of context pointer
|
||||||
*/
|
*/
|
||||||
.pushsection ".idmap.text", "awx"
|
|
||||||
SYM_FUNC_START(cpu_do_resume)
|
SYM_FUNC_START(cpu_do_resume)
|
||||||
ldp x2, x3, [x0]
|
ldp x2, x3, [x0]
|
||||||
ldp x4, x5, [x0, #16]
|
ldp x4, x5, [x0, #16]
|
||||||
@ -166,7 +165,6 @@ alternative_else_nop_endif
|
|||||||
isb
|
isb
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(cpu_do_resume)
|
SYM_FUNC_END(cpu_do_resume)
|
||||||
.popsection
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
.pushsection ".idmap.text", "awx"
|
.pushsection ".idmap.text", "awx"
|
||||||
|
@ -28,7 +28,9 @@ HAS_GENERIC_AUTH
|
|||||||
HAS_GENERIC_AUTH_ARCH_QARMA3
|
HAS_GENERIC_AUTH_ARCH_QARMA3
|
||||||
HAS_GENERIC_AUTH_ARCH_QARMA5
|
HAS_GENERIC_AUTH_ARCH_QARMA5
|
||||||
HAS_GENERIC_AUTH_IMP_DEF
|
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_LDAPR
|
||||||
HAS_LSE_ATOMICS
|
HAS_LSE_ATOMICS
|
||||||
HAS_NO_FPSIMD
|
HAS_NO_FPSIMD
|
||||||
@ -38,7 +40,6 @@ HAS_RAS_EXTN
|
|||||||
HAS_RNG
|
HAS_RNG
|
||||||
HAS_SB
|
HAS_SB
|
||||||
HAS_STAGE2_FWB
|
HAS_STAGE2_FWB
|
||||||
HAS_SYSREG_GIC_CPUIF
|
|
||||||
HAS_TIDCP1
|
HAS_TIDCP1
|
||||||
HAS_TLB_RANGE
|
HAS_TLB_RANGE
|
||||||
HAS_VIRT_HOST_EXTN
|
HAS_VIRT_HOST_EXTN
|
||||||
@ -50,6 +51,7 @@ MTE
|
|||||||
MTE_ASYMM
|
MTE_ASYMM
|
||||||
SME
|
SME
|
||||||
SME_FA64
|
SME_FA64
|
||||||
|
SME2
|
||||||
SPECTRE_V2
|
SPECTRE_V2
|
||||||
SPECTRE_V3A
|
SPECTRE_V3A
|
||||||
SPECTRE_V4
|
SPECTRE_V4
|
||||||
|
@ -689,17 +689,17 @@ EndEnum
|
|||||||
Enum 11:8 FPDP
|
Enum 11:8 FPDP
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
0b0001 VFPv2
|
0b0001 VFPv2
|
||||||
0b0001 VFPv3
|
0b0010 VFPv3
|
||||||
EndEnum
|
EndEnum
|
||||||
Enum 7:4 FPSP
|
Enum 7:4 FPSP
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
0b0001 VFPv2
|
0b0001 VFPv2
|
||||||
0b0001 VFPv3
|
0b0010 VFPv3
|
||||||
EndEnum
|
EndEnum
|
||||||
Enum 3:0 SIMDReg
|
Enum 3:0 SIMDReg
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
0b0001 IMP_16x64
|
0b0001 IMP_16x64
|
||||||
0b0001 IMP_32x64
|
0b0010 IMP_32x64
|
||||||
EndEnum
|
EndEnum
|
||||||
EndSysreg
|
EndSysreg
|
||||||
|
|
||||||
@ -718,7 +718,7 @@ EndEnum
|
|||||||
Enum 23:20 SIMDHP
|
Enum 23:20 SIMDHP
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
0b0001 SIMDHP
|
0b0001 SIMDHP
|
||||||
0b0001 SIMDHP_FLOAT
|
0b0010 SIMDHP_FLOAT
|
||||||
EndEnum
|
EndEnum
|
||||||
Enum 19:16 SIMDSP
|
Enum 19:16 SIMDSP
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
@ -894,6 +894,7 @@ EndEnum
|
|||||||
Enum 27:24 SME
|
Enum 27:24 SME
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
0b0001 IMP
|
0b0001 IMP
|
||||||
|
0b0010 SME2
|
||||||
EndEnum
|
EndEnum
|
||||||
Res0 23:20
|
Res0 23:20
|
||||||
Enum 19:16 MPAM_frac
|
Enum 19:16 MPAM_frac
|
||||||
@ -975,7 +976,9 @@ Enum 63 FA64
|
|||||||
EndEnum
|
EndEnum
|
||||||
Res0 62:60
|
Res0 62:60
|
||||||
Enum 59:56 SMEver
|
Enum 59:56 SMEver
|
||||||
0b0000 IMP
|
0b0000 SME
|
||||||
|
0b0001 SME2
|
||||||
|
0b0010 SME2p1
|
||||||
EndEnum
|
EndEnum
|
||||||
Enum 55:52 I16I64
|
Enum 55:52 I16I64
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
@ -986,7 +989,19 @@ Enum 48 F64F64
|
|||||||
0b0 NI
|
0b0 NI
|
||||||
0b1 IMP
|
0b1 IMP
|
||||||
EndEnum
|
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
|
Enum 39:36 I8I32
|
||||||
0b0000 NI
|
0b0000 NI
|
||||||
0b1111 IMP
|
0b1111 IMP
|
||||||
@ -999,7 +1014,10 @@ Enum 34 B16F32
|
|||||||
0b0 NI
|
0b0 NI
|
||||||
0b1 IMP
|
0b1 IMP
|
||||||
EndEnum
|
EndEnum
|
||||||
Res0 33
|
Enum 33 BI32I32
|
||||||
|
0b0 NI
|
||||||
|
0b1 IMP
|
||||||
|
EndEnum
|
||||||
Enum 32 F32F32
|
Enum 32 F32F32
|
||||||
0b0 NI
|
0b0 NI
|
||||||
0b1 IMP
|
0b1 IMP
|
||||||
@ -1599,7 +1617,8 @@ EndSysreg
|
|||||||
SysregFields SMCR_ELx
|
SysregFields SMCR_ELx
|
||||||
Res0 63:32
|
Res0 63:32
|
||||||
Field 31 FA64
|
Field 31 FA64
|
||||||
Res0 30:9
|
Field 30 EZT0
|
||||||
|
Res0 29:9
|
||||||
Raz 8:4
|
Raz 8:4
|
||||||
Field 3:0 LEN
|
Field 3:0 LEN
|
||||||
EndSysregFields
|
EndSysregFields
|
||||||
@ -1981,3 +2000,18 @@ Field 23:16 LD
|
|||||||
Res0 15:8
|
Res0 15:8
|
||||||
Field 7:0 LR
|
Field 7:0 LR
|
||||||
EndSysreg
|
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
|
# 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
|
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
|
||||||
|
|
||||||
# use acpi.o to put all files here into acpi.o modparam namespace
|
# 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
|
screen_info.o efi-stub-entry.o
|
||||||
|
|
||||||
lib-$(CONFIG_ARM) += arm32-stub.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_X86) += x86-stub.o
|
||||||
lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o
|
lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o
|
||||||
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-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 \
|
STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
|
||||||
--prefix-symbols=__efistub_
|
--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
|
# 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
|
# 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_handle_t image_handle)
|
||||||
{
|
{
|
||||||
efi_status_t status;
|
efi_status_t status;
|
||||||
unsigned long kernel_size, kernel_memsize = 0;
|
unsigned long kernel_size, kernel_codesize, kernel_memsize;
|
||||||
u32 phys_seed = 0;
|
u32 phys_seed = 0;
|
||||||
u64 min_kimg_align = efi_get_kimg_min_align();
|
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);
|
SEGMENT_ALIGN >> 10);
|
||||||
|
|
||||||
kernel_size = _edata - _text;
|
kernel_size = _edata - _text;
|
||||||
|
kernel_codesize = __inittext_end - _text;
|
||||||
kernel_memsize = kernel_size + (_end - _edata);
|
kernel_memsize = kernel_size + (_end - _edata);
|
||||||
*reserve_size = kernel_memsize;
|
*reserve_size = kernel_memsize;
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
|||||||
*/
|
*/
|
||||||
*image_addr = (u64)_text;
|
*image_addr = (u64)_text;
|
||||||
*reserve_size = 0;
|
*reserve_size = 0;
|
||||||
goto clean_image_to_poc;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
|
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;
|
*image_addr = *reserve_addr;
|
||||||
memcpy((void *)*image_addr, _text, kernel_size);
|
memcpy((void *)*image_addr, _text, kernel_size);
|
||||||
|
caches_clean_inval_pou(*image_addr, *image_addr + kernel_codesize);
|
||||||
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");
|
|
||||||
|
|
||||||
return EFI_SUCCESS;
|
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;
|
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,
|
void efi_cache_sync_image(unsigned long image_base,
|
||||||
unsigned long alloc_size,
|
unsigned long alloc_size,
|
||||||
unsigned long code_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,
|
u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
|
||||||
CTR_EL0_DminLine_SHIFT);
|
CTR_EL0_DminLine_SHIFT);
|
||||||
|
|
||||||
do {
|
/* only perform the cache maintenance if needed for I/D coherency */
|
||||||
asm("dc civac, %0" :: "r"(image_base));
|
if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) {
|
||||||
image_base += lsize;
|
do {
|
||||||
alloc_size -= lsize;
|
asm("dc " DCTYPE ", %0" :: "r"(image_base));
|
||||||
} while (alloc_size >= lsize);
|
image_base += lsize;
|
||||||
|
code_size -= lsize;
|
||||||
|
} while (code_size >= lsize);
|
||||||
|
}
|
||||||
|
|
||||||
asm("ic ialluis");
|
asm("ic ialluis");
|
||||||
dsb(ish);
|
dsb(ish);
|
||||||
isb();
|
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);
|
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);
|
DEFINE_STATIC_KEY_FALSE(gic_nonsecure_priorities);
|
||||||
EXPORT_SYMBOL(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++)
|
for (i = 0; i < gic_data.ppi_nr; i++)
|
||||||
refcount_set(&ppi_nmi_refs[i], 0);
|
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",
|
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:
|
* How priority values are used by the GIC depends on two things:
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
static void gic_check_cpu_features(void)
|
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,
|
TAINT_CPU_OUT_OF_SPEC,
|
||||||
"GICv3 system registers enabled, broken firmware!\n");
|
"GICv3 system registers enabled, broken firmware!\n");
|
||||||
}
|
}
|
||||||
|
@ -75,12 +75,6 @@
|
|||||||
# define __assume_aligned(a, ...)
|
# define __assume_aligned(a, ...)
|
||||||
#endif
|
#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.
|
* Note the long name.
|
||||||
*
|
*
|
||||||
|
@ -79,6 +79,33 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
|
|||||||
/* Attributes */
|
/* Attributes */
|
||||||
#include <linux/compiler_attributes.h>
|
#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 */
|
/* Builtins */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -39,6 +39,7 @@ static inline void ftrace_boot_snapshot(void) { }
|
|||||||
|
|
||||||
struct ftrace_ops;
|
struct ftrace_ops;
|
||||||
struct ftrace_regs;
|
struct ftrace_regs;
|
||||||
|
struct dyn_ftrace;
|
||||||
|
|
||||||
#ifdef CONFIG_FUNCTION_TRACER
|
#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,
|
void arch_ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
||||||
struct ftrace_ops *op, struct ftrace_regs *fregs);
|
struct ftrace_ops *op, struct ftrace_regs *fregs);
|
||||||
#endif
|
#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 */
|
#endif /* CONFIG_FUNCTION_TRACER */
|
||||||
|
|
||||||
/* Main tracing buffer and events set up */
|
/* Main tracing buffer and events set up */
|
||||||
@ -391,8 +395,6 @@ struct ftrace_func_entry {
|
|||||||
unsigned long direct; /* for direct lookup only */
|
unsigned long direct; /* for direct lookup only */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dyn_ftrace;
|
|
||||||
|
|
||||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||||
extern int ftrace_direct_func_count;
|
extern int ftrace_direct_func_count;
|
||||||
int register_ftrace_direct(unsigned long ip, unsigned long addr);
|
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.
|
* IPMODIFY - the record allows for the IP address to be changed.
|
||||||
* DISABLED - the record is not ready to be touched yet
|
* DISABLED - the record is not ready to be touched yet
|
||||||
* DIRECT - there is a direct function to call
|
* 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
|
* 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
|
* 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_DISABLED = (1UL << 25),
|
||||||
FTRACE_FL_DIRECT = (1UL << 24),
|
FTRACE_FL_DIRECT = (1UL << 24),
|
||||||
FTRACE_FL_DIRECT_EN = (1UL << 23),
|
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_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1)
|
||||||
|
|
||||||
#define ftrace_rec_count(rec) ((rec)->flags & FTRACE_REF_MAX)
|
#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);
|
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)
|
* ftrace_modify_call - convert from one addr to another (no nop)
|
||||||
* @rec: the call site record (e.g. mcount/fentry)
|
* @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,
|
* what we expect it to be, and then on success of the compare,
|
||||||
* it should write to the location.
|
* 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
|
* The code segment at @rec->ip should be a caller to @old_addr
|
||||||
*
|
*
|
||||||
* Return must be:
|
* 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_PAC_ENABLED_KEYS 0x40a /* arm64 ptr auth enabled keys (prctl()) */
|
||||||
#define NT_ARM_SSVE 0x40b /* ARM Streaming SVE registers */
|
#define NT_ARM_SSVE 0x40b /* ARM Streaming SVE registers */
|
||||||
#define NT_ARM_ZA 0x40c /* ARM SME ZA 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_ARC_V2 0x600 /* ARCv2 accumulator/extra registers */
|
||||||
#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note */
|
#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note */
|
||||||
#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */
|
#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);
|
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();
|
BUG();
|
||||||
|
|
||||||
|
@ -42,6 +42,9 @@ config HAVE_DYNAMIC_FTRACE_WITH_REGS
|
|||||||
config HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
config HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
|
||||||
|
bool
|
||||||
|
|
||||||
config HAVE_DYNAMIC_FTRACE_WITH_ARGS
|
config HAVE_DYNAMIC_FTRACE_WITH_ARGS
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
@ -257,6 +260,10 @@ config DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
|||||||
depends on DYNAMIC_FTRACE_WITH_REGS
|
depends on DYNAMIC_FTRACE_WITH_REGS
|
||||||
depends on HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
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
|
config DYNAMIC_FTRACE_WITH_ARGS
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on DYNAMIC_FTRACE
|
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,
|
void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
||||||
struct ftrace_ops *op, struct ftrace_regs *fregs);
|
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)
|
static inline void ftrace_ops_init(struct ftrace_ops *ops)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||||
@ -1814,6 +1841,18 @@ static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
|
|||||||
* if rec count is zero.
|
* 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++;
|
count++;
|
||||||
|
|
||||||
/* Must match FTRACE_UPDATE_CALLS in ftrace_modify_all_code() */
|
/* 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;
|
struct ftrace_ops *ops = NULL;
|
||||||
|
|
||||||
pr_info("ftrace record flags: %lx\n", rec->flags);
|
pr_info("ftrace record flags: %lx\n", rec->flags);
|
||||||
pr_cont(" (%ld)%s", ftrace_rec_count(rec),
|
pr_cont(" (%ld)%s%s", ftrace_rec_count(rec),
|
||||||
rec->flags & FTRACE_FL_REGS ? " R" : " ");
|
rec->flags & FTRACE_FL_REGS ? " R" : " ",
|
||||||
|
rec->flags & FTRACE_FL_CALL_OPS ? " O" : " ");
|
||||||
if (rec->flags & FTRACE_FL_TRAMP_EN) {
|
if (rec->flags & FTRACE_FL_TRAMP_EN) {
|
||||||
ops = ftrace_find_tramp_ops_any(rec);
|
ops = ftrace_find_tramp_ops_any(rec);
|
||||||
if (ops) {
|
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
|
* want the direct enabled (it will be done via the
|
||||||
* direct helper). But if DIRECT_EN is set, and
|
* direct helper). But if DIRECT_EN is set, and
|
||||||
* the count is not one, we need to clear it.
|
* the count is not one, we need to clear it.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
if (ftrace_rec_count(rec) == 1) {
|
if (ftrace_rec_count(rec) == 1) {
|
||||||
if (!(rec->flags & FTRACE_FL_DIRECT) !=
|
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) {
|
} else if (rec->flags & FTRACE_FL_DIRECT_EN) {
|
||||||
flag |= FTRACE_FL_DIRECT;
|
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 */
|
/* 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;
|
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.
|
* and REGS states. The _EN flags must be disabled though.
|
||||||
*/
|
*/
|
||||||
rec->flags &= ~(FTRACE_FL_ENABLED | FTRACE_FL_TRAMP_EN |
|
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;
|
ftrace_bug_type = FTRACE_BUG_NOP;
|
||||||
@ -2431,6 +2501,25 @@ ftrace_find_tramp_ops_new(struct dyn_ftrace *rec)
|
|||||||
return NULL;
|
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
|
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||||
/* Protected by rcu_tasks for reading, and direct_mutex for writing */
|
/* Protected by rcu_tasks for reading, and direct_mutex for writing */
|
||||||
static struct ftrace_hash *direct_functions = EMPTY_HASH;
|
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) {
|
if (iter->flags & FTRACE_ITER_ENABLED) {
|
||||||
struct ftrace_ops *ops;
|
struct ftrace_ops *ops;
|
||||||
|
|
||||||
seq_printf(m, " (%ld)%s%s%s",
|
seq_printf(m, " (%ld)%s%s%s%s",
|
||||||
ftrace_rec_count(rec),
|
ftrace_rec_count(rec),
|
||||||
rec->flags & FTRACE_FL_REGS ? " R" : " ",
|
rec->flags & FTRACE_FL_REGS ? " R" : " ",
|
||||||
rec->flags & FTRACE_FL_IPMODIFY ? " I" : " ",
|
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) {
|
if (rec->flags & FTRACE_FL_TRAMP_EN) {
|
||||||
ops = ftrace_find_tramp_ops_any(rec);
|
ops = ftrace_find_tramp_ops_any(rec);
|
||||||
if (ops) {
|
if (ops) {
|
||||||
@ -3800,6 +3890,15 @@ static int t_show(struct seq_file *m, void *v)
|
|||||||
} else {
|
} else {
|
||||||
add_trampoline_func(m, NULL, rec);
|
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) {
|
if (rec->flags & FTRACE_FL_DIRECT) {
|
||||||
unsigned long direct;
|
unsigned long direct;
|
||||||
|
|
||||||
|
@ -50,6 +50,78 @@ static void sme_sigill(void)
|
|||||||
asm volatile(".inst 0x04bf5800" : : : "x0");
|
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)
|
static void sve_sigill(void)
|
||||||
{
|
{
|
||||||
/* RDVL x0, #0 */
|
/* RDVL x0, #0 */
|
||||||
@ -158,6 +230,49 @@ static const struct hwcap_data {
|
|||||||
.sigill_fn = sme_sigill,
|
.sigill_fn = sme_sigill,
|
||||||
.sigill_reliable = true,
|
.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",
|
.name = "SVE",
|
||||||
.at_hwcap = AT_HWCAP,
|
.at_hwcap = AT_HWCAP,
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
|
|
||||||
.arch_extension sve
|
.arch_extension sve
|
||||||
|
|
||||||
|
#define ID_AA64SMFR0_EL1_SMEver_SHIFT 56
|
||||||
|
#define ID_AA64SMFR0_EL1_SMEver_WIDTH 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LDR (vector to ZA array):
|
* LDR (vector to ZA array):
|
||||||
* LDR ZA[\nw, #\offset], [X\nxbase, #\offset, MUL VL]
|
* LDR ZA[\nw, #\offset], [X\nxbase, #\offset, MUL VL]
|
||||||
@ -45,6 +48,26 @@
|
|||||||
| ((\offset) & 7)
|
| ((\offset) & 7)
|
||||||
.endm
|
.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
|
.globl do_syscall
|
||||||
do_syscall:
|
do_syscall:
|
||||||
// Store callee saved registers x19-x29 (80 bytes) plus x0 and x1
|
// 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
|
msr S3_3_C4_C2_2, x2
|
||||||
1:
|
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
|
tbz x2, #SVCR_ZA_SHIFT, 1f
|
||||||
mov w12, #0
|
mov w12, #0
|
||||||
ldr x2, =za_in
|
ldr x2, =za_in
|
||||||
@ -73,6 +96,15 @@ do_syscall:
|
|||||||
add x12, x12, #1
|
add x12, x12, #1
|
||||||
cmp x1, x12
|
cmp x1, x12
|
||||||
bne 2b
|
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:
|
1:
|
||||||
|
|
||||||
// Load GPRs x8-x28, and save our SP/FP for later comparison
|
// Load GPRs x8-x28, and save our SP/FP for later comparison
|
||||||
@ -92,8 +124,11 @@ do_syscall:
|
|||||||
str x29, [x2], #8 // FP
|
str x29, [x2], #8 // FP
|
||||||
str x30, [x2], #8 // LR
|
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
|
cbnz x0, 1f
|
||||||
|
ldr x2, =svcr_in
|
||||||
|
tbnz x2, #SVCR_SM_SHIFT, 1f
|
||||||
|
|
||||||
ldr x2, =fpr_in
|
ldr x2, =fpr_in
|
||||||
ldp q0, q1, [x2]
|
ldp q0, q1, [x2]
|
||||||
ldp q2, q3, [x2, #16 * 2]
|
ldp q2, q3, [x2, #16 * 2]
|
||||||
@ -111,10 +146,11 @@ do_syscall:
|
|||||||
ldp q26, q27, [x2, #16 * 26]
|
ldp q26, q27, [x2, #16 * 26]
|
||||||
ldp q28, q29, [x2, #16 * 28]
|
ldp q28, q29, [x2, #16 * 28]
|
||||||
ldp q30, q31, [x2, #16 * 30]
|
ldp q30, q31, [x2, #16 * 30]
|
||||||
|
|
||||||
|
b 2f
|
||||||
1:
|
1:
|
||||||
|
|
||||||
// Load the SVE registers if we're doing SVE/SME
|
// Load the SVE registers if we're doing SVE/SME
|
||||||
cbz x0, 1f
|
|
||||||
|
|
||||||
ldr x2, =z_in
|
ldr x2, =z_in
|
||||||
ldr z0, [x2, #0, MUL VL]
|
ldr z0, [x2, #0, MUL VL]
|
||||||
@ -155,9 +191,9 @@ do_syscall:
|
|||||||
ldr x2, =ffr_in
|
ldr x2, =ffr_in
|
||||||
ldr p0, [x2]
|
ldr p0, [x2]
|
||||||
ldr x2, [x2, #0]
|
ldr x2, [x2, #0]
|
||||||
cbz x2, 2f
|
cbz x2, 1f
|
||||||
wrffr p0.b
|
wrffr p0.b
|
||||||
2:
|
1:
|
||||||
|
|
||||||
ldr x2, =p_in
|
ldr x2, =p_in
|
||||||
ldr p0, [x2, #0, MUL VL]
|
ldr p0, [x2, #0, MUL VL]
|
||||||
@ -176,7 +212,7 @@ do_syscall:
|
|||||||
ldr p13, [x2, #13, MUL VL]
|
ldr p13, [x2, #13, MUL VL]
|
||||||
ldr p14, [x2, #14, MUL VL]
|
ldr p14, [x2, #14, MUL VL]
|
||||||
ldr p15, [x2, #15, MUL VL]
|
ldr p15, [x2, #15, MUL VL]
|
||||||
1:
|
2:
|
||||||
|
|
||||||
// Do the syscall
|
// Do the syscall
|
||||||
svc #0
|
svc #0
|
||||||
@ -235,6 +271,15 @@ do_syscall:
|
|||||||
add x12, x12, #1
|
add x12, x12, #1
|
||||||
cmp x1, x12
|
cmp x1, x12
|
||||||
bne 2b
|
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:
|
1:
|
||||||
|
|
||||||
// Save the SVE state if we have some
|
// Save the SVE state if we have some
|
||||||
|
@ -20,10 +20,13 @@
|
|||||||
|
|
||||||
#include "syscall-abi.h"
|
#include "syscall-abi.h"
|
||||||
|
|
||||||
#define NUM_VL ((SVE_VQ_MAX - SVE_VQ_MIN) + 1)
|
|
||||||
|
|
||||||
static int default_sme_vl;
|
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);
|
extern void do_syscall(int sve_vl, int sme_vl);
|
||||||
|
|
||||||
static void fill_random(void *buf, size_t size)
|
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
|
#define NUM_FPR 32
|
||||||
uint64_t fpr_in[NUM_FPR * 2];
|
uint64_t fpr_in[NUM_FPR * 2];
|
||||||
uint64_t fpr_out[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,
|
static void setup_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||||
uint64_t svcr)
|
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 errors = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!sve_vl) {
|
if (!sve_vl && !(svcr & SVCR_SM_MASK)) {
|
||||||
for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
|
for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
|
||||||
if (fpr_in[i] != fpr_out[i]) {
|
if (fpr_in[i] != fpr_out[i]) {
|
||||||
ksft_print_msg("%s Q%d/%d mismatch %llx != %llx\n",
|
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;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,8 +300,8 @@ static int check_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
|||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t za_in[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
|
uint8_t za_in[ZA_SIG_REGS_SIZE(SVE_VQ_MAX)];
|
||||||
uint8_t za_out[SVE_NUM_PREGS * __SVE_ZREG_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,
|
static void setup_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||||
uint64_t svcr)
|
uint64_t svcr)
|
||||||
@ -311,6 +327,35 @@ static int check_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
|||||||
return errors;
|
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,
|
typedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
||||||
uint64_t svcr);
|
uint64_t svcr);
|
||||||
typedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
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_ffr, check_ffr },
|
||||||
{ setup_svcr, check_svcr },
|
{ setup_svcr, check_svcr },
|
||||||
{ setup_za, check_za },
|
{ setup_za, check_za },
|
||||||
|
{ setup_zt, check_zt },
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool do_test(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
|
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)
|
static void test_one_syscall(struct syscall_cfg *cfg)
|
||||||
{
|
{
|
||||||
int sve_vq, sve_vl;
|
int sve, sme;
|
||||||
int sme_vq, sme_vl;
|
int ret;
|
||||||
|
|
||||||
/* FPSIMD only case */
|
/* FPSIMD only case */
|
||||||
ksft_test_result(do_test(cfg, 0, default_sme_vl, 0),
|
ksft_test_result(do_test(cfg, 0, default_sme_vl, 0),
|
||||||
"%s FPSIMD\n", cfg->name);
|
"%s FPSIMD\n", cfg->name);
|
||||||
|
|
||||||
if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
|
for (sve = 0; sve < sve_vl_count; sve++) {
|
||||||
return;
|
ret = prctl(PR_SVE_SET_VL, sve_vls[sve]);
|
||||||
|
if (ret == -1)
|
||||||
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)
|
|
||||||
ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
|
ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
|
||||||
strerror(errno), errno);
|
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))
|
for (sme = 0; sme < sme_vl_count; sme++) {
|
||||||
sve_vq = sve_vq_from_vl(sve_vl);
|
ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
|
||||||
|
if (ret == -1)
|
||||||
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)
|
|
||||||
ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
|
ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
|
||||||
strerror(errno), errno);
|
strerror(errno), errno);
|
||||||
|
|
||||||
sme_vl &= PR_SME_VL_LEN_MASK;
|
ksft_test_result(do_test(cfg, sve_vls[sve],
|
||||||
|
sme_vls[sme],
|
||||||
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,
|
|
||||||
SVCR_ZA_MASK | SVCR_SM_MASK),
|
SVCR_ZA_MASK | SVCR_SM_MASK),
|
||||||
"%s SVE VL %d/SME VL %d SM+ZA\n",
|
"%s SVE VL %d/SME VL %d SM+ZA\n",
|
||||||
cfg->name, sve_vl, sme_vl);
|
cfg->name, sve_vls[sve],
|
||||||
ksft_test_result(do_test(cfg, sve_vl, sme_vl,
|
sme_vls[sme]);
|
||||||
SVCR_SM_MASK),
|
ksft_test_result(do_test(cfg, sve_vls[sve],
|
||||||
|
sme_vls[sme], SVCR_SM_MASK),
|
||||||
"%s SVE VL %d/SME VL %d SM\n",
|
"%s SVE VL %d/SME VL %d SM\n",
|
||||||
cfg->name, sve_vl, sme_vl);
|
cfg->name, sve_vls[sve],
|
||||||
ksft_test_result(do_test(cfg, sve_vl, sme_vl,
|
sme_vls[sme]);
|
||||||
SVCR_ZA_MASK),
|
ksft_test_result(do_test(cfg, sve_vls[sve],
|
||||||
|
sme_vls[sme], SVCR_ZA_MASK),
|
||||||
"%s SVE VL %d/SME VL %d ZA\n",
|
"%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;
|
unsigned int vq;
|
||||||
int vl_count = 0;
|
|
||||||
int vl;
|
int vl;
|
||||||
|
|
||||||
if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
|
if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enumerate up to SVE_VQ_MAX vector lengths
|
* 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);
|
vl = prctl(PR_SVE_SET_VL, vq * 16);
|
||||||
if (vl == -1)
|
if (vl == -1)
|
||||||
ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
|
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))
|
if (vq != sve_vq_from_vl(vl))
|
||||||
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;
|
unsigned int vq;
|
||||||
int vl_count = 0;
|
|
||||||
int vl;
|
int vl;
|
||||||
|
|
||||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))
|
if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
/* Ensure we configure a SME VL, used to flag if SVCR is set */
|
|
||||||
default_sme_vl = 16;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enumerate up to SVE_VQ_MAX vector lengths
|
* 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);
|
vl = prctl(PR_SME_SET_VL, vq * 16);
|
||||||
if (vl == -1)
|
if (vl == -1)
|
||||||
ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
|
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;
|
vl &= PR_SME_VL_LEN_MASK;
|
||||||
|
|
||||||
|
/* Found lowest VL */
|
||||||
|
if (sve_vq_from_vl(vl) > vq)
|
||||||
|
break;
|
||||||
|
|
||||||
if (vq != sve_vq_from_vl(vl))
|
if (vq != sve_vq_from_vl(vl))
|
||||||
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 main(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int tests = 1; /* FPSIMD */
|
int tests = 1; /* FPSIMD */
|
||||||
|
int sme_ver;
|
||||||
|
|
||||||
srandom(getpid());
|
srandom(getpid());
|
||||||
|
|
||||||
ksft_print_header();
|
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);
|
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)
|
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)
|
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++)
|
for (i = 0; i < ARRAY_SIZE(syscalls); i++)
|
||||||
test_one_syscall(&syscalls[i]);
|
test_one_syscall(&syscalls[i]);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/auxvec.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;
|
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)),
|
static void __do_test(void (*trampoline)(void (*)(void)),
|
||||||
void (*fn)(void),
|
void (*fn)(void),
|
||||||
@ -109,19 +111,11 @@ static void __do_test(void (*trampoline)(void (*)(void)),
|
|||||||
const char *name,
|
const char *name,
|
||||||
int expect_sigill)
|
int expect_sigill)
|
||||||
{
|
{
|
||||||
if (skip_all) {
|
/*
|
||||||
test_skipped++;
|
* Branch Target exceptions should only happen for BTI
|
||||||
putstr("ok ");
|
* binaries running on a system with BTI:
|
||||||
putnum(test_num);
|
*/
|
||||||
putstr(" ");
|
if (!BTI || !have_bti)
|
||||||
puttestname(name, trampoline_name);
|
|
||||||
putstr(" # SKIP\n");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Branch Target exceptions should only happen in BTI binaries: */
|
|
||||||
if (!BTI)
|
|
||||||
expect_sigill = 0;
|
expect_sigill = 0;
|
||||||
|
|
||||||
sigill_expected = expect_sigill;
|
sigill_expected = expect_sigill;
|
||||||
@ -199,9 +193,10 @@ void start(int *argcp)
|
|||||||
putstr("# HWCAP2_BTI present\n");
|
putstr("# HWCAP2_BTI present\n");
|
||||||
if (!(hwcap & HWCAP_PACA))
|
if (!(hwcap & HWCAP_PACA))
|
||||||
putstr("# Bad hardware? Expect problems.\n");
|
putstr("# Bad hardware? Expect problems.\n");
|
||||||
|
have_bti = true;
|
||||||
} else {
|
} else {
|
||||||
putstr("# HWCAP2_BTI not present\n");
|
putstr("# HWCAP2_BTI not present\n");
|
||||||
skip_all = 1;
|
have_bti = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
putstr("# Test binary");
|
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-fork
|
||||||
za-ptrace
|
za-ptrace
|
||||||
za-test
|
za-test
|
||||||
|
zt-ptrace
|
||||||
|
zt-test
|
||||||
|
@ -14,6 +14,8 @@ TEST_GEN_PROGS_EXTENDED := fp-pidbench fpsimd-test \
|
|||||||
sve-test \
|
sve-test \
|
||||||
ssve-test \
|
ssve-test \
|
||||||
za-test \
|
za-test \
|
||||||
|
zt-ptrace \
|
||||||
|
zt-test \
|
||||||
vlset
|
vlset
|
||||||
TEST_PROGS_EXTENDED := fpsimd-stress sve-stress ssve-stress za-stress
|
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-ptrace: za-ptrace.c
|
||||||
$(OUTPUT)/za-test: za-test.S $(OUTPUT)/asm-utils.o
|
$(OUTPUT)/za-test: za-test.S $(OUTPUT)/asm-utils.o
|
||||||
$(CC) -nostdlib $^ -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
|
include ../../lib.mk
|
||||||
|
@ -57,7 +57,7 @@ endfunction
|
|||||||
// Utility macro to print a literal string
|
// Utility macro to print a literal string
|
||||||
// Clobbers x0-x4,x8
|
// Clobbers x0-x4,x8
|
||||||
.macro puts string
|
.macro puts string
|
||||||
.pushsection .rodata.str1.1, "aMS", 1
|
.pushsection .rodata.str1.1, "aMS", @progbits, 1
|
||||||
.L__puts_literal\@: .string "\string"
|
.L__puts_literal\@: .string "\string"
|
||||||
.popsection
|
.popsection
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
// Main program entry point
|
// Main program entry point
|
||||||
.globl _start
|
.globl _start
|
||||||
function _start
|
function _start
|
||||||
_start:
|
|
||||||
puts "Iterations per test: "
|
puts "Iterations per test: "
|
||||||
mov x20, #10000
|
mov x20, #10000
|
||||||
lsl x20, x20, #8
|
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);
|
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)
|
static void probe_vls(int vls[], int *vl_count, int set_vl)
|
||||||
{
|
{
|
||||||
unsigned int vq;
|
unsigned int vq;
|
||||||
@ -377,7 +390,7 @@ static void probe_vls(int vls[], int *vl_count, int set_vl)
|
|||||||
|
|
||||||
*vl_count = 0;
|
*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);
|
vl = prctl(set_vl, vq * 16);
|
||||||
if (vl == -1)
|
if (vl == -1)
|
||||||
ksft_exit_fail_msg("SET_VL failed: %s (%d)\n",
|
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;
|
vl &= PR_SVE_VL_LEN_MASK;
|
||||||
|
|
||||||
|
if (*vl_count && (vl == vls[*vl_count - 1]))
|
||||||
|
break;
|
||||||
|
|
||||||
vq = sve_vq_from_vl(vl);
|
vq = sve_vq_from_vl(vl);
|
||||||
|
|
||||||
vls[*vl_count] = vl;
|
vls[*vl_count] = vl;
|
||||||
@ -426,6 +442,7 @@ int main(int argc, char **argv)
|
|||||||
bool all_children_started = false;
|
bool all_children_started = false;
|
||||||
int seen_children;
|
int seen_children;
|
||||||
int sve_vls[MAX_VLS], sme_vls[MAX_VLS];
|
int sve_vls[MAX_VLS], sme_vls[MAX_VLS];
|
||||||
|
bool have_sme2;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) {
|
||||||
@ -458,6 +475,13 @@ int main(int argc, char **argv)
|
|||||||
sme_vl_count = 0;
|
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 */
|
/* Force context switching if we only have FPSIMD */
|
||||||
if (!sve_vl_count && !sme_vl_count)
|
if (!sve_vl_count && !sme_vl_count)
|
||||||
fpsimd_per_cpu = 2;
|
fpsimd_per_cpu = 2;
|
||||||
@ -468,8 +492,9 @@ int main(int argc, char **argv)
|
|||||||
ksft_print_header();
|
ksft_print_header();
|
||||||
ksft_set_plan(tests);
|
ksft_set_plan(tests);
|
||||||
|
|
||||||
ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs\n",
|
ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs, SME2 %s\n",
|
||||||
cpus, sve_vl_count, sme_vl_count);
|
cpus, sve_vl_count, sme_vl_count,
|
||||||
|
have_sme2 ? "present" : "absent");
|
||||||
|
|
||||||
if (timeout > 0)
|
if (timeout > 0)
|
||||||
ksft_print_msg("Will run for %ds\n", timeout);
|
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_ssve(&children[num_children++], sme_vls[j], i);
|
||||||
start_za(&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
|
// Main program entry point
|
||||||
.globl _start
|
.globl _start
|
||||||
function _start
|
function _start
|
||||||
_start:
|
|
||||||
mov x23, #0 // signal count
|
mov x23, #0 // signal count
|
||||||
|
|
||||||
mov w0, #SIGINT
|
mov w0, #SIGINT
|
||||||
|
@ -48,4 +48,24 @@
|
|||||||
| ((\offset) & 7)
|
| ((\offset) & 7)
|
||||||
.endm
|
.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
|
#endif
|
||||||
|
@ -30,6 +30,16 @@
|
|||||||
#define NT_ARM_SSVE 0x40b
|
#define NT_ARM_SSVE 0x40b
|
||||||
#endif
|
#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 {
|
struct vec_type {
|
||||||
const char *name;
|
const char *name;
|
||||||
unsigned long hwcap_type;
|
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 FLAG_TESTS 2
|
||||||
#define FPSIMD_TESTS 2
|
#define FPSIMD_TESTS 2
|
||||||
|
|
||||||
@ -689,7 +699,7 @@ static int do_parent(pid_t child)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Step through every possible VQ */
|
/* 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);
|
vl = sve_vl_from_vq(vq);
|
||||||
|
|
||||||
/* First, try to set this vector length */
|
/* First, try to set this vector length */
|
||||||
|
@ -378,7 +378,6 @@ endfunction
|
|||||||
// Main program entry point
|
// Main program entry point
|
||||||
.globl _start
|
.globl _start
|
||||||
function _start
|
function _start
|
||||||
_start:
|
|
||||||
mov x23, #0 // Irritation signal count
|
mov x23, #0 // Irritation signal count
|
||||||
|
|
||||||
mov w0, #SIGINT
|
mov w0, #SIGINT
|
||||||
|
@ -25,7 +25,17 @@
|
|||||||
#define NT_ARM_ZA 0x40c
|
#define NT_ARM_ZA 0x40c
|
||||||
#endif
|
#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)
|
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);
|
ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
|
||||||
|
|
||||||
/* Step through every possible VQ */
|
/* 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);
|
vl = sve_vl_from_vq(vq);
|
||||||
|
|
||||||
/* First, try to set this vector length */
|
/* First, try to set this vector length */
|
||||||
|
@ -231,7 +231,6 @@ endfunction
|
|||||||
// Main program entry point
|
// Main program entry point
|
||||||
.globl _start
|
.globl _start
|
||||||
function _start
|
function _start
|
||||||
_start:
|
|
||||||
mov x23, #0 // signal count
|
mov x23, #0 // signal count
|
||||||
|
|
||||||
mov w0, #SIGINT
|
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
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
# Copyright (C) 2020 ARM Limited
|
# 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
|
CFLAGS += -std=gnu99 -I. -pthread
|
||||||
LDFLAGS += -pthread
|
LDFLAGS += -pthread
|
||||||
SRCS := $(filter-out mte_common_util.c,$(wildcard *.c))
|
SRCS := $(filter-out mte_common_util.c,$(wildcard *.c))
|
||||||
PROGS := $(patsubst %.c,%,$(SRCS))
|
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
|
#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)
|
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)
|
ifeq ($(mte_cc_support),1)
|
||||||
# Generated binaries to be installed by top KSFT script
|
# Generated binaries to be installed by top KSFT script
|
||||||
TEST_GEN_PROGS := $(PROGS)
|
TEST_GEN_PROGS := $(PROGS)
|
||||||
|
|
||||||
# Get Kernel headers installed and use them.
|
|
||||||
else
|
else
|
||||||
$(warning compiler "$(CC)" does not support the ARMv8.5 MTE extension.)
|
$(warning compiler "$(CC)" does not support the ARMv8.5 MTE extension.)
|
||||||
$(warning test program "mte" will not be created.)
|
$(warning test program "mte" will not be created.)
|
||||||
|
@ -4,5 +4,7 @@ fake_sigreturn_*
|
|||||||
sme_*
|
sme_*
|
||||||
ssve_*
|
ssve_*
|
||||||
sve_*
|
sve_*
|
||||||
|
tpidr2_siginfo
|
||||||
za_*
|
za_*
|
||||||
|
zt_*
|
||||||
!*.[ch]
|
!*.[ch]
|
||||||
|
@ -22,6 +22,10 @@ $(TEST_GEN_PROGS): $(PROGS)
|
|||||||
|
|
||||||
# Common test-unit targets to build common-layout test-cases executables
|
# Common test-unit targets to build common-layout test-cases executables
|
||||||
# Needs secondary expansion to properly include the testcase c-file in pre-reqs
|
# 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:
|
.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
|
$(PROGS): $$@.c ${COMMON_SOURCES} ${COMMON_HEADERS}
|
||||||
$(CC) $(CFLAGS) $^ -o $@
|
$(CC) $(CFLAGS) ${@}.c ${COMMON_SOURCES} -o $@
|
||||||
|
@ -12,12 +12,10 @@
|
|||||||
#include "test_signals.h"
|
#include "test_signals.h"
|
||||||
#include "test_signals_utils.h"
|
#include "test_signals_utils.h"
|
||||||
|
|
||||||
struct tdescr *current;
|
struct tdescr *current = &tde;
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
current = &tde;
|
|
||||||
|
|
||||||
ksft_print_msg("%s :: %s\n", current->name, current->descr);
|
ksft_print_msg("%s :: %s\n", current->name, current->descr);
|
||||||
if (test_setup(current) && test_init(current)) {
|
if (test_setup(current) && test_init(current)) {
|
||||||
test_run(current);
|
test_run(current);
|
||||||
|
@ -34,6 +34,7 @@ enum {
|
|||||||
FSVE_BIT,
|
FSVE_BIT,
|
||||||
FSME_BIT,
|
FSME_BIT,
|
||||||
FSME_FA64_BIT,
|
FSME_FA64_BIT,
|
||||||
|
FSME2_BIT,
|
||||||
FMAX_END
|
FMAX_END
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ enum {
|
|||||||
#define FEAT_SVE (1UL << FSVE_BIT)
|
#define FEAT_SVE (1UL << FSVE_BIT)
|
||||||
#define FEAT_SME (1UL << FSME_BIT)
|
#define FEAT_SME (1UL << FSME_BIT)
|
||||||
#define FEAT_SME_FA64 (1UL << FSME_FA64_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.
|
* A descriptor used to describe and configure a test case.
|
||||||
|
@ -29,6 +29,7 @@ static char const *const feats_names[FMAX_END] = {
|
|||||||
" SVE ",
|
" SVE ",
|
||||||
" SME ",
|
" SME ",
|
||||||
" FA64 ",
|
" FA64 ",
|
||||||
|
" SME2 ",
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_FEATS_SZ 128
|
#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
|
* in the copy, this was previously validated in
|
||||||
* ASSERT_GOOD_CONTEXT().
|
* ASSERT_GOOD_CONTEXT().
|
||||||
*/
|
*/
|
||||||
to_copy = offset + sizeof(struct extra_context) + 16 +
|
to_copy = __builtin_offsetof(ucontext_t,
|
||||||
extra->size;
|
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]);
|
copied_extra = (struct extra_context *)&(td->live_uc->uc_mcontext.__reserved[offset]);
|
||||||
} else {
|
} else {
|
||||||
copied_extra = NULL;
|
copied_extra = NULL;
|
||||||
@ -323,6 +326,8 @@ int test_init(struct tdescr *td)
|
|||||||
td->feats_supported |= FEAT_SME;
|
td->feats_supported |= FEAT_SME;
|
||||||
if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
|
if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
|
||||||
td->feats_supported |= FEAT_SME_FA64;
|
td->feats_supported |= FEAT_SME_FA64;
|
||||||
|
if (getauxval(AT_HWCAP2) & HWCAP2_SME2)
|
||||||
|
td->feats_supported |= FEAT_SME2;
|
||||||
if (feats_ok(td)) {
|
if (feats_ok(td)) {
|
||||||
if (td->feats_required & td->feats_supported)
|
if (td->feats_required & td->feats_supported)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
@ -34,6 +34,10 @@ static bool sme_get_vls(struct tdescr *td)
|
|||||||
|
|
||||||
vl &= PR_SME_VL_LEN_MASK;
|
vl &= PR_SME_VL_LEN_MASK;
|
||||||
|
|
||||||
|
/* Did we find the lowest supported VL? */
|
||||||
|
if (vq < sve_vq_from_vl(vl))
|
||||||
|
break;
|
||||||
|
|
||||||
/* Skip missing VLs */
|
/* Skip missing VLs */
|
||||||
vq = sve_vq_from_vl(vl);
|
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;
|
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() */
|
/* The actual size validation is done in get_current_context() */
|
||||||
fprintf(stderr, "Got expected size %u and VL %d\n",
|
fprintf(stderr, "Got expected size %u and VL %d\n",
|
||||||
head->size, ssve->vl);
|
head->size, ssve->vl);
|
||||||
@ -116,12 +125,7 @@ static int sme_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
|
|||||||
struct tdescr tde = {
|
struct tdescr tde = {
|
||||||
.name = "Streaming SVE registers",
|
.name = "Streaming SVE registers",
|
||||||
.descr = "Check that we get the right Streaming SVE registers reported",
|
.descr = "Check that we get the right Streaming SVE registers reported",
|
||||||
/*
|
.feats_required = FEAT_SME,
|
||||||
* 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,
|
|
||||||
.timeout = 3,
|
.timeout = 3,
|
||||||
.init = sme_get_vls,
|
.init = sme_get_vls,
|
||||||
.run = sme_regs,
|
.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;
|
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 validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
||||||
{
|
{
|
||||||
bool terminated = false;
|
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 extra_context *extra = NULL;
|
||||||
struct sve_context *sve = NULL;
|
struct sve_context *sve = NULL;
|
||||||
struct za_context *za = NULL;
|
struct za_context *za = NULL;
|
||||||
|
struct zt_context *zt = NULL;
|
||||||
struct _aarch64_ctx *head =
|
struct _aarch64_ctx *head =
|
||||||
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
|
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
|
||||||
void *extra_data = NULL;
|
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))
|
if (head->size != sizeof(struct esr_context))
|
||||||
*err = "Bad size for esr_context";
|
*err = "Bad size for esr_context";
|
||||||
break;
|
break;
|
||||||
|
case TPIDR2_MAGIC:
|
||||||
|
if (head->size != sizeof(struct tpidr2_context))
|
||||||
|
*err = "Bad size for tpidr2_context";
|
||||||
|
break;
|
||||||
case SVE_MAGIC:
|
case SVE_MAGIC:
|
||||||
if (flags & SVE_CTX)
|
if (flags & SVE_CTX)
|
||||||
*err = "Multiple SVE_MAGIC";
|
*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;
|
za = (struct za_context *)head;
|
||||||
new_flags |= ZA_CTX;
|
new_flags |= ZA_CTX;
|
||||||
break;
|
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:
|
case EXTRA_MAGIC:
|
||||||
if (flags & EXTRA_CTX)
|
if (flags & EXTRA_CTX)
|
||||||
*err = "Multiple EXTRA_MAGIC";
|
*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 (new_flags & ZA_CTX)
|
||||||
if (!validate_za_context(za, err))
|
if (!validate_za_context(za, err))
|
||||||
return false;
|
return false;
|
||||||
|
if (new_flags & ZT_CTX)
|
||||||
|
if (!validate_zt_context(zt, err))
|
||||||
|
return false;
|
||||||
|
|
||||||
flags |= new_flags;
|
flags |= new_flags;
|
||||||
|
|
||||||
@ -245,6 +280,11 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (terminated && (flags & ZT_CTX) && !(flags & ZA_CTX)) {
|
||||||
|
*err = "ZT context but no ZA context";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#define SVE_CTX (1 << 1)
|
#define SVE_CTX (1 << 1)
|
||||||
#define ZA_CTX (1 << 2)
|
#define ZA_CTX (1 << 2)
|
||||||
#define EXTRA_CTX (1 << 3)
|
#define EXTRA_CTX (1 << 3)
|
||||||
|
#define ZT_CTX (1 << 4)
|
||||||
|
|
||||||
#define KSFT_BAD_MAGIC 0xdeadbeef
|
#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