arm64 updates for 5.2

Mostly just incremental improvements here:
 
 - Introduce AT_HWCAP2 for advertising CPU features to userspace
 
 - Expose SVE2 availability to userspace
 
 - Support for "data cache clean to point of deep persistence" (DC PODP)
 
 - Honour "mitigations=off" on the cmdline and advertise status via sysfs
 
 - CPU timer erratum workaround (Neoverse-N1 #1188873)
 
 - Introduce perf PMU driver for the SMMUv3 performance counters
 
 - Add config option to disable the kuser helpers page for AArch32 tasks
 
 - Futex modifications to ensure liveness under contention
 
 - Rework debug exception handling to seperate kernel and user handlers
 
 - Non-critical fixes and cleanup
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEPxTL6PPUbjXGY88ct6xw3ITBYzQFAlzMFGgACgkQt6xw3ITB
 YzTicAf/TX1h1+ecbx4WJAa4qeiOCPoNpG9efldQumqJhKL44MR5bkhuShna5mwE
 ptm5qUXkZCxLTjzssZKnbdbgwa3t+emW8Of3D91IfI9akiZbMoDx5FGgcNbqjazb
 RLrhOFHwgontA38yppZN+DrL+sXbvif/CVELdHahkEx6KepSGaS2lmPXRmz/W56v
 4yIRy/zxc3Dhjgfm3wKh72nBwoZdLiIc4mchd5pthNlR9E2idrYkQegG1C+gA00r
 o8uZRVOWgoh7H+QJE+xLUc8PaNCg8xqRRXOuZYg9GOz6hh7zSWhm+f1nRz9S2tIR
 gIgsCHNqoO2I3E1uJpAQXDGtt2kFhA==
 =ulpJ
 -----END PGP SIGNATURE-----

Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux

Pull arm64 updates from Will Deacon:
 "Mostly just incremental improvements here:

   - Introduce AT_HWCAP2 for advertising CPU features to userspace

   - Expose SVE2 availability to userspace

   - Support for "data cache clean to point of deep persistence" (DC PODP)

   - Honour "mitigations=off" on the cmdline and advertise status via
     sysfs

   - CPU timer erratum workaround (Neoverse-N1 #1188873)

   - Introduce perf PMU driver for the SMMUv3 performance counters

   - Add config option to disable the kuser helpers page for AArch32 tasks

   - Futex modifications to ensure liveness under contention

   - Rework debug exception handling to seperate kernel and user
     handlers

   - Non-critical fixes and cleanup"

* tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: (92 commits)
  Documentation: Add ARM64 to kernel-parameters.rst
  arm64/speculation: Support 'mitigations=' cmdline option
  arm64: ssbs: Don't treat CPUs with SSBS as unaffected by SSB
  arm64: enable generic CPU vulnerabilites support
  arm64: add sysfs vulnerability show for speculative store bypass
  arm64: Fix size of __early_cpu_boot_status
  clocksource/arm_arch_timer: Use arch_timer_read_counter to access stable counters
  clocksource/arm_arch_timer: Remove use of workaround static key
  clocksource/arm_arch_timer: Drop use of static key in arch_timer_reg_read_stable
  clocksource/arm_arch_timer: Direcly assign set_next_event workaround
  arm64: Use arch_timer_read_counter instead of arch_counter_get_cntvct
  watchdog/sbsa: Use arch_timer_read_counter instead of arch_counter_get_cntvct
  ARM: vdso: Remove dependency with the arch_timer driver internals
  arm64: Apply ARM64_ERRATUM_1188873 to Neoverse-N1
  arm64: Add part number for Neoverse N1
  arm64: Make ARM64_ERRATUM_1188873 depend on COMPAT
  arm64: Restrict ARM64_ERRATUM_1188873 mitigation to AArch32
  arm64: mm: Remove pte_unmap_nested()
  arm64: Fix compiler warning from pte_unmap() with -Wunused-but-set-variable
  arm64: compat: Reduce address limit for 64K pages
  ...
This commit is contained in:
Linus Torvalds 2019-05-06 17:54:22 -07:00
commit c620f7bd0b
93 changed files with 2541 additions and 837 deletions

View File

@ -88,6 +88,7 @@ parameter is applicable::
APIC APIC support is enabled. APIC APIC support is enabled.
APM Advanced Power Management support is enabled. APM Advanced Power Management support is enabled.
ARM ARM architecture is enabled. ARM ARM architecture is enabled.
ARM64 ARM64 architecture is enabled.
AX25 Appropriate AX.25 support is enabled. AX25 Appropriate AX.25 support is enabled.
CLK Common clock infrastructure is enabled. CLK Common clock infrastructure is enabled.
CMA Contiguous Memory Area support is enabled. CMA Contiguous Memory Area support is enabled.

View File

@ -2548,8 +2548,8 @@
http://repo.or.cz/w/linux-2.6/mini2440.git http://repo.or.cz/w/linux-2.6/mini2440.git
mitigations= mitigations=
[X86,PPC,S390] Control optional mitigations for CPU [X86,PPC,S390,ARM64] Control optional mitigations for
vulnerabilities. This is a set of curated, CPU vulnerabilities. This is a set of curated,
arch-independent options, each of which is an arch-independent options, each of which is an
aggregation of existing arch-specific options. aggregation of existing arch-specific options.
@ -2558,11 +2558,13 @@
improves system performance, but it may also improves system performance, but it may also
expose users to several CPU vulnerabilities. expose users to several CPU vulnerabilities.
Equivalent to: nopti [X86,PPC] Equivalent to: nopti [X86,PPC]
kpti=0 [ARM64]
nospectre_v1 [PPC] nospectre_v1 [PPC]
nobp=0 [S390] nobp=0 [S390]
nospectre_v2 [X86,PPC,S390] nospectre_v2 [X86,PPC,S390,ARM64]
spectre_v2_user=off [X86] spectre_v2_user=off [X86]
spec_store_bypass_disable=off [X86,PPC] spec_store_bypass_disable=off [X86,PPC]
ssbd=force-off [ARM64]
l1tf=off [X86] l1tf=off [X86]
auto (default) auto (default)
@ -2908,10 +2910,10 @@
check bypass). With this option data leaks are possible check bypass). With this option data leaks are possible
in the system. in the system.
nospectre_v2 [X86,PPC_FSL_BOOK3E] Disable all mitigations for the Spectre variant 2 nospectre_v2 [X86,PPC_FSL_BOOK3E,ARM64] Disable all mitigations for
(indirect branch prediction) vulnerability. System may the Spectre variant 2 (indirect branch prediction)
allow data leaks with this option, which is equivalent vulnerability. System may allow data leaks with this
to spectre_v2=off. option.
nospec_store_bypass_disable nospec_store_bypass_disable
[HW] Disable all mitigations for the Speculative Store Bypass vulnerability [HW] Disable all mitigations for the Speculative Store Bypass vulnerability

View File

@ -209,6 +209,22 @@ infrastructure:
| AT | [35-32] | y | | AT | [35-32] | y |
x--------------------------------------------------x x--------------------------------------------------x
6) ID_AA64ZFR0_EL1 - SVE feature ID register 0
x--------------------------------------------------x
| Name | bits | visible |
|--------------------------------------------------|
| SM4 | [43-40] | y |
|--------------------------------------------------|
| SHA3 | [35-32] | y |
|--------------------------------------------------|
| BitPerm | [19-16] | y |
|--------------------------------------------------|
| AES | [7-4] | y |
|--------------------------------------------------|
| SVEVer | [3-0] | y |
x--------------------------------------------------x
Appendix I: Example Appendix I: Example
--------------------------- ---------------------------

View File

@ -13,9 +13,9 @@ 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 auxilliary vector.
Userspace software can test for features by acquiring the AT_HWCAP entry Userspace software can test for features by acquiring the AT_HWCAP or
of the auxilliary vector, and testing whether the relevant flags are AT_HWCAP2 entry of the auxiliary vector, and testing whether the relevant
set, e.g. flags are set, e.g.
bool floating_point_is_present(void) bool floating_point_is_present(void)
{ {
@ -135,6 +135,10 @@ HWCAP_DCPOP
Functionality implied by ID_AA64ISAR1_EL1.DPB == 0b0001. Functionality implied by ID_AA64ISAR1_EL1.DPB == 0b0001.
HWCAP2_DCPODP
Functionality implied by ID_AA64ISAR1_EL1.DPB == 0b0010.
HWCAP_SHA3 HWCAP_SHA3
Functionality implied by ID_AA64ISAR0_EL1.SHA3 == 0b0001. Functionality implied by ID_AA64ISAR0_EL1.SHA3 == 0b0001.
@ -159,6 +163,30 @@ HWCAP_SVE
Functionality implied by ID_AA64PFR0_EL1.SVE == 0b0001. Functionality implied by ID_AA64PFR0_EL1.SVE == 0b0001.
HWCAP2_SVE2
Functionality implied by ID_AA64ZFR0_EL1.SVEVer == 0b0001.
HWCAP2_SVEAES
Functionality implied by ID_AA64ZFR0_EL1.AES == 0b0001.
HWCAP2_SVEPMULL
Functionality implied by ID_AA64ZFR0_EL1.AES == 0b0010.
HWCAP2_SVEBITPERM
Functionality implied by ID_AA64ZFR0_EL1.BitPerm == 0b0001.
HWCAP2_SVESHA3
Functionality implied by ID_AA64ZFR0_EL1.SHA3 == 0b0001.
HWCAP2_SVESM4
Functionality implied by ID_AA64ZFR0_EL1.SM4 == 0b0001.
HWCAP_ASIMDFHM HWCAP_ASIMDFHM
Functionality implied by ID_AA64ISAR0_EL1.FHM == 0b0001. Functionality implied by ID_AA64ISAR0_EL1.FHM == 0b0001.
@ -194,3 +222,10 @@ HWCAP_PACG
Functionality implied by ID_AA64ISAR1_EL1.GPA == 0b0001 or Functionality implied by ID_AA64ISAR1_EL1.GPA == 0b0001 or
ID_AA64ISAR1_EL1.GPI == 0b0001, as described by ID_AA64ISAR1_EL1.GPI == 0b0001, as described by
Documentation/arm64/pointer-authentication.txt. Documentation/arm64/pointer-authentication.txt.
4. Unused AT_HWCAP bits
-----------------------
For interoperation with userspace, the kernel guarantees that bits 62
and 63 of AT_HWCAP will always be returned as 0.

View File

@ -61,6 +61,7 @@ stable kernels.
| ARM | Cortex-A76 | #1188873 | ARM64_ERRATUM_1188873 | | ARM | Cortex-A76 | #1188873 | ARM64_ERRATUM_1188873 |
| ARM | Cortex-A76 | #1165522 | ARM64_ERRATUM_1165522 | | ARM | Cortex-A76 | #1165522 | ARM64_ERRATUM_1165522 |
| ARM | Cortex-A76 | #1286807 | ARM64_ERRATUM_1286807 | | ARM | Cortex-A76 | #1286807 | ARM64_ERRATUM_1286807 |
| ARM | Neoverse-N1 | #1188873 | ARM64_ERRATUM_1188873 |
| ARM | MMU-500 | #841119,#826419 | N/A | | ARM | MMU-500 | #841119,#826419 | N/A |
| | | | | | | | | |
| Cavium | ThunderX ITS | #22375, #24313 | CAVIUM_ERRATUM_22375 | | Cavium | ThunderX ITS | #22375, #24313 | CAVIUM_ERRATUM_22375 |
@ -77,6 +78,7 @@ stable kernels.
| Hisilicon | Hip0{5,6,7} | #161010101 | HISILICON_ERRATUM_161010101 | | Hisilicon | Hip0{5,6,7} | #161010101 | HISILICON_ERRATUM_161010101 |
| Hisilicon | Hip0{6,7} | #161010701 | N/A | | Hisilicon | Hip0{6,7} | #161010701 | N/A |
| Hisilicon | Hip07 | #161600802 | HISILICON_ERRATUM_161600802 | | Hisilicon | Hip07 | #161600802 | HISILICON_ERRATUM_161600802 |
| Hisilicon | Hip08 SMMU PMCG | #162001800 | N/A |
| | | | | | | | | |
| Qualcomm Tech. | Kryo/Falkor v1 | E1003 | QCOM_FALKOR_ERRATUM_1003 | | Qualcomm Tech. | Kryo/Falkor v1 | E1003 | QCOM_FALKOR_ERRATUM_1003 |
| Qualcomm Tech. | Falkor v1 | E1009 | QCOM_FALKOR_ERRATUM_1009 | | Qualcomm Tech. | Falkor v1 | E1009 | QCOM_FALKOR_ERRATUM_1009 |

View File

@ -34,6 +34,23 @@ model features for SVE is included in Appendix A.
following sections: software that needs to verify that those interfaces are following sections: software that needs to verify that those interfaces are
present must check for HWCAP_SVE instead. present must check for HWCAP_SVE instead.
* On hardware that supports the SVE2 extensions, HWCAP2_SVE2 will also
be reported in the AT_HWCAP2 aux vector entry. In addition to this,
optional extensions to SVE2 may be reported by the presence of:
HWCAP2_SVE2
HWCAP2_SVEAES
HWCAP2_SVEPMULL
HWCAP2_SVEBITPERM
HWCAP2_SVESHA3
HWCAP2_SVESM4
This list may be extended over time as the SVE architecture evolves.
These extensions are also reported via the CPU ID register ID_AA64ZFR0_EL1,
which userspace can read using an MRS instruction. See elf_hwcaps.txt and
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 regset. The recommended way of detecting support for this regset NT_ARM_SVE regset. The recommended way of detecting support for this regset
is to connect to a target process first and then attempt a is to connect to a target process first and then attempt a

View File

@ -218,5 +218,4 @@ All other architectures should build just fine too - but they won't have
the new syscalls yet. the new syscalls yet.
Architectures need to implement the new futex_atomic_cmpxchg_inatomic() Architectures need to implement the new futex_atomic_cmpxchg_inatomic()
inline function before writing up the syscalls (that function returns inline function before writing up the syscalls.
-ENOSYS right now).

View File

@ -11,6 +11,10 @@
#include <clocksource/arm_arch_timer.h> #include <clocksource/arm_arch_timer.h>
#ifdef CONFIG_ARM_ARCH_TIMER #ifdef CONFIG_ARM_ARCH_TIMER
/* 32bit ARM doesn't know anything about timer errata... */
#define has_erratum_handler(h) (false)
#define erratum_handler(h) (arch_timer_##h)
int arch_timer_arch_init(void); int arch_timer_arch_init(void);
/* /*
@ -79,7 +83,7 @@ static inline u32 arch_timer_get_cntfrq(void)
return val; return val;
} }
static inline u64 arch_counter_get_cntpct(void) static inline u64 __arch_counter_get_cntpct(void)
{ {
u64 cval; u64 cval;
@ -88,7 +92,12 @@ static inline u64 arch_counter_get_cntpct(void)
return cval; return cval;
} }
static inline u64 arch_counter_get_cntvct(void) static inline u64 __arch_counter_get_cntpct_stable(void)
{
return __arch_counter_get_cntpct();
}
static inline u64 __arch_counter_get_cntvct(void)
{ {
u64 cval; u64 cval;
@ -97,6 +106,11 @@ static inline u64 arch_counter_get_cntvct(void)
return cval; return cval;
} }
static inline u64 __arch_counter_get_cntvct_stable(void)
{
return __arch_counter_get_cntvct();
}
static inline u32 arch_timer_get_cntkctl(void) static inline u32 arch_timer_get_cntkctl(void)
{ {
u32 cntkctl; u32 cntkctl;

View File

@ -68,6 +68,8 @@
#define BPIALL __ACCESS_CP15(c7, 0, c5, 6) #define BPIALL __ACCESS_CP15(c7, 0, c5, 6)
#define ICIALLU __ACCESS_CP15(c7, 0, c5, 0) #define ICIALLU __ACCESS_CP15(c7, 0, c5, 0)
#define CNTVCT __ACCESS_CP15_64(1, c14)
extern unsigned long cr_alignment; /* defined in entry-armv.S */ extern unsigned long cr_alignment; /* defined in entry-armv.S */
static inline unsigned long get_cr(void) static inline unsigned long get_cr(void)

View File

@ -32,14 +32,14 @@
#define stage2_pgd_present(kvm, pgd) pgd_present(pgd) #define stage2_pgd_present(kvm, pgd) pgd_present(pgd)
#define stage2_pgd_populate(kvm, pgd, pud) pgd_populate(NULL, pgd, pud) #define stage2_pgd_populate(kvm, pgd, pud) pgd_populate(NULL, pgd, pud)
#define stage2_pud_offset(kvm, pgd, address) pud_offset(pgd, address) #define stage2_pud_offset(kvm, pgd, address) pud_offset(pgd, address)
#define stage2_pud_free(kvm, pud) pud_free(NULL, pud) #define stage2_pud_free(kvm, pud) do { } while (0)
#define stage2_pud_none(kvm, pud) pud_none(pud) #define stage2_pud_none(kvm, pud) pud_none(pud)
#define stage2_pud_clear(kvm, pud) pud_clear(pud) #define stage2_pud_clear(kvm, pud) pud_clear(pud)
#define stage2_pud_present(kvm, pud) pud_present(pud) #define stage2_pud_present(kvm, pud) pud_present(pud)
#define stage2_pud_populate(kvm, pud, pmd) pud_populate(NULL, pud, pmd) #define stage2_pud_populate(kvm, pud, pmd) pud_populate(NULL, pud, pmd)
#define stage2_pmd_offset(kvm, pud, address) pmd_offset(pud, address) #define stage2_pmd_offset(kvm, pud, address) pmd_offset(pud, address)
#define stage2_pmd_free(kvm, pmd) pmd_free(NULL, pmd) #define stage2_pmd_free(kvm, pmd) free_page((unsigned long)pmd)
#define stage2_pud_huge(kvm, pud) pud_huge(pud) #define stage2_pud_huge(kvm, pud) pud_huge(pud)

View File

@ -18,9 +18,9 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/time.h> #include <linux/time.h>
#include <asm/arch_timer.h>
#include <asm/barrier.h> #include <asm/barrier.h>
#include <asm/bug.h> #include <asm/bug.h>
#include <asm/cp15.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <asm/vdso_datapage.h> #include <asm/vdso_datapage.h>
@ -123,7 +123,8 @@ static notrace u64 get_ns(struct vdso_data *vdata)
u64 cycle_now; u64 cycle_now;
u64 nsec; u64 nsec;
cycle_now = arch_counter_get_cntvct(); isb();
cycle_now = read_sysreg(CNTVCT);
cycle_delta = (cycle_now - vdata->cs_cycle_last) & vdata->cs_mask; cycle_delta = (cycle_now - vdata->cs_cycle_last) & vdata->cs_mask;

View File

@ -90,6 +90,7 @@ config ARM64
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select GENERIC_CLOCKEVENTS_BROADCAST select GENERIC_CLOCKEVENTS_BROADCAST
select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_AUTOPROBE
select GENERIC_CPU_VULNERABILITIES
select GENERIC_EARLY_IOREMAP select GENERIC_EARLY_IOREMAP
select GENERIC_IDLE_POLL_SETUP select GENERIC_IDLE_POLL_SETUP
select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_MULTI_HANDLER
@ -148,6 +149,7 @@ config ARM64
select HAVE_PERF_REGS select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP select HAVE_PERF_USER_STACK_DUMP
select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_FUNCTION_ARG_ACCESS_API
select HAVE_RCU_TABLE_FREE select HAVE_RCU_TABLE_FREE
select HAVE_RSEQ select HAVE_RSEQ
select HAVE_STACKPROTECTOR select HAVE_STACKPROTECTOR
@ -293,7 +295,7 @@ menu "Kernel Features"
menu "ARM errata workarounds via the alternatives framework" menu "ARM errata workarounds via the alternatives framework"
config ARM64_WORKAROUND_CLEAN_CACHE config ARM64_WORKAROUND_CLEAN_CACHE
def_bool n bool
config ARM64_ERRATUM_826319 config ARM64_ERRATUM_826319
bool "Cortex-A53: 826319: System might deadlock if a write cannot complete until read data is accepted" bool "Cortex-A53: 826319: System might deadlock if a write cannot complete until read data is accepted"
@ -460,26 +462,28 @@ config ARM64_ERRATUM_1024718
bool "Cortex-A55: 1024718: Update of DBM/AP bits without break before make might result in incorrect update" bool "Cortex-A55: 1024718: Update of DBM/AP bits without break before make might result in incorrect update"
default y default y
help help
This option adds work around for Arm Cortex-A55 Erratum 1024718. This option adds a workaround for ARM Cortex-A55 Erratum 1024718.
Affected Cortex-A55 cores (r0p0, r0p1, r1p0) could cause incorrect Affected Cortex-A55 cores (r0p0, r0p1, r1p0) could cause incorrect
update of the hardware dirty bit when the DBM/AP bits are updated update of the hardware dirty bit when the DBM/AP bits are updated
without a break-before-make. The work around is to disable the usage without a break-before-make. The workaround is to disable the usage
of hardware DBM locally on the affected cores. CPUs not affected by of hardware DBM locally on the affected cores. CPUs not affected by
erratum will continue to use the feature. this erratum will continue to use the feature.
If unsure, say Y. If unsure, say Y.
config ARM64_ERRATUM_1188873 config ARM64_ERRATUM_1188873
bool "Cortex-A76: MRC read following MRRC read of specific Generic Timer in AArch32 might give incorrect result" bool "Cortex-A76/Neoverse-N1: MRC read following MRRC read of specific Generic Timer in AArch32 might give incorrect result"
default y default y
depends on COMPAT
select ARM_ARCH_TIMER_OOL_WORKAROUND select ARM_ARCH_TIMER_OOL_WORKAROUND
help help
This option adds work arounds for ARM Cortex-A76 erratum 1188873 This option adds a workaround for ARM Cortex-A76/Neoverse-N1
erratum 1188873.
Affected Cortex-A76 cores (r0p0, r1p0, r2p0) could cause Affected Cortex-A76/Neoverse-N1 cores (r0p0, r1p0, r2p0) could
register corruption when accessing the timer registers from cause register corruption when accessing the timer registers
AArch32 userspace. from AArch32 userspace.
If unsure, say Y. If unsure, say Y.
@ -487,7 +491,7 @@ config ARM64_ERRATUM_1165522
bool "Cortex-A76: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation" bool "Cortex-A76: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation"
default y default y
help help
This option adds work arounds for ARM Cortex-A76 erratum 1165522 This option adds a workaround for ARM Cortex-A76 erratum 1165522.
Affected Cortex-A76 cores (r0p0, r1p0, r2p0) could end-up with Affected Cortex-A76 cores (r0p0, r1p0, r2p0) could end-up with
corrupted TLBs by speculating an AT instruction during a guest corrupted TLBs by speculating an AT instruction during a guest
@ -500,7 +504,7 @@ config ARM64_ERRATUM_1286807
default y default y
select ARM64_WORKAROUND_REPEAT_TLBI select ARM64_WORKAROUND_REPEAT_TLBI
help help
This option adds workaround for ARM Cortex-A76 erratum 1286807 This option adds a workaround for ARM Cortex-A76 erratum 1286807.
On the affected Cortex-A76 cores (r0p0 to r3p0), if a virtual On the affected Cortex-A76 cores (r0p0 to r3p0), if a virtual
address for a cacheable mapping of a location is being address for a cacheable mapping of a location is being
@ -517,10 +521,10 @@ config CAVIUM_ERRATUM_22375
bool "Cavium erratum 22375, 24313" bool "Cavium erratum 22375, 24313"
default y default y
help help
Enable workaround for erratum 22375, 24313. Enable workaround for errata 22375 and 24313.
This implements two gicv3-its errata workarounds for ThunderX. Both This implements two gicv3-its errata workarounds for ThunderX. Both
with small impact affecting only ITS table allocation. with a small impact affecting only ITS table allocation.
erratum 22375: only alloc 8MB table size erratum 22375: only alloc 8MB table size
erratum 24313: ignore memory access type erratum 24313: ignore memory access type
@ -584,9 +588,6 @@ config QCOM_FALKOR_ERRATUM_1003
config ARM64_WORKAROUND_REPEAT_TLBI config ARM64_WORKAROUND_REPEAT_TLBI
bool bool
help
Enable the repeat TLBI workaround for Falkor erratum 1009 and
Cortex-A76 erratum 1286807.
config QCOM_FALKOR_ERRATUM_1009 config QCOM_FALKOR_ERRATUM_1009
bool "Falkor E1009: Prematurely complete a DSB after a TLBI" bool "Falkor E1009: Prematurely complete a DSB after a TLBI"
@ -622,7 +623,7 @@ config HISILICON_ERRATUM_161600802
bool "Hip07 161600802: Erroneous redistributor VLPI base" bool "Hip07 161600802: Erroneous redistributor VLPI base"
default y default y
help help
The HiSilicon Hip07 SoC usees the wrong redistributor base The HiSilicon Hip07 SoC uses the wrong redistributor base
when issued ITS commands such as VMOVP and VMAPP, and requires when issued ITS commands such as VMOVP and VMAPP, and requires
a 128kB offset to be applied to the target address in this commands. a 128kB offset to be applied to the target address in this commands.
@ -642,7 +643,7 @@ config FUJITSU_ERRATUM_010001
bool "Fujitsu-A64FX erratum E#010001: Undefined fault may occur wrongly" bool "Fujitsu-A64FX erratum E#010001: Undefined fault may occur wrongly"
default y default y
help help
This option adds workaround for Fujitsu-A64FX erratum E#010001. This option adds a workaround for Fujitsu-A64FX erratum E#010001.
On some variants of the Fujitsu-A64FX cores ver(1.0, 1.1), memory On some variants of the Fujitsu-A64FX cores ver(1.0, 1.1), memory
accesses may cause undefined fault (Data abort, DFSC=0b111111). accesses may cause undefined fault (Data abort, DFSC=0b111111).
This fault occurs under a specific hardware condition when a This fault occurs under a specific hardware condition when a
@ -653,7 +654,7 @@ config FUJITSU_ERRATUM_010001
case-4 TTBR1_EL2 with TCR_EL2.NFD1 == 1. case-4 TTBR1_EL2 with TCR_EL2.NFD1 == 1.
The workaround is to ensure these bits are clear in TCR_ELx. The workaround is to ensure these bits are clear in TCR_ELx.
The workaround only affect the Fujitsu-A64FX. The workaround only affects the Fujitsu-A64FX.
If unsure, say Y. If unsure, say Y.
@ -885,6 +886,9 @@ config ARCH_WANT_HUGE_PMD_SHARE
config ARCH_HAS_CACHE_LINE_SIZE config ARCH_HAS_CACHE_LINE_SIZE
def_bool y def_bool y
config ARCH_ENABLE_SPLIT_PMD_PTLOCK
def_bool y if PGTABLE_LEVELS > 2
config SECCOMP config SECCOMP
bool "Enable seccomp to safely compute untrusted bytecode" bool "Enable seccomp to safely compute untrusted bytecode"
---help--- ---help---
@ -1074,9 +1078,65 @@ config RODATA_FULL_DEFAULT_ENABLED
This requires the linear region to be mapped down to pages, This requires the linear region to be mapped down to pages,
which may adversely affect performance in some cases. which may adversely affect performance in some cases.
config ARM64_SW_TTBR0_PAN
bool "Emulate Privileged Access Never using TTBR0_EL1 switching"
help
Enabling this option prevents the kernel from accessing
user-space memory directly by pointing TTBR0_EL1 to a reserved
zeroed area and reserved ASID. The user access routines
restore the valid TTBR0_EL1 temporarily.
menuconfig COMPAT
bool "Kernel support for 32-bit EL0"
depends on ARM64_4K_PAGES || EXPERT
select COMPAT_BINFMT_ELF if BINFMT_ELF
select HAVE_UID16
select OLD_SIGSUSPEND3
select COMPAT_OLD_SIGACTION
help
This option enables support for a 32-bit EL0 running under a 64-bit
kernel at EL1. AArch32-specific components such as system calls,
the user helper functions, VFP support and the ptrace interface are
handled appropriately by the kernel.
If you use a page size other than 4KB (i.e, 16KB or 64KB), please be aware
that you will only be able to execute AArch32 binaries that were compiled
with page size aligned segments.
If you want to execute 32-bit userspace applications, say Y.
if COMPAT
config KUSER_HELPERS
bool "Enable kuser helpers page for 32 bit applications"
default y
help
Warning: disabling this option may break 32-bit user programs.
Provide kuser helpers to compat tasks. The kernel provides
helper code to userspace in read only form at a fixed location
to allow userspace to be independent of the CPU type fitted to
the system. This permits binaries to be run on ARMv4 through
to ARMv8 without modification.
See Documentation/arm/kernel_user_helpers.txt for details.
However, the fixed address nature of these helpers can be used
by ROP (return orientated programming) authors when creating
exploits.
If all of the binaries and libraries which run on your platform
are built specifically for your platform, and make no use of
these helpers, then you can turn this option off to hinder
such exploits. However, in that case, if a binary or library
relying on those helpers is run, it will not function correctly.
Say N here only if you are absolutely certain that you do not
need these helpers; otherwise, the safe option is to say Y.
menuconfig ARMV8_DEPRECATED menuconfig ARMV8_DEPRECATED
bool "Emulate deprecated/obsolete ARMv8 instructions" bool "Emulate deprecated/obsolete ARMv8 instructions"
depends on COMPAT
depends on SYSCTL depends on SYSCTL
help help
Legacy software support may require certain instructions Legacy software support may require certain instructions
@ -1142,13 +1202,7 @@ config SETEND_EMULATION
If unsure, say Y If unsure, say Y
endif endif
config ARM64_SW_TTBR0_PAN endif
bool "Emulate Privileged Access Never using TTBR0_EL1 switching"
help
Enabling this option prevents the kernel from accessing
user-space memory directly by pointing TTBR0_EL1 to a reserved
zeroed area and reserved ASID. The user access routines
restore the valid TTBR0_EL1 temporarily.
menu "ARMv8.1 architectural features" menu "ARMv8.1 architectural features"
@ -1314,6 +1368,9 @@ config ARM64_SVE
To enable use of this extension on CPUs that implement it, say Y. To enable use of this extension on CPUs that implement it, say Y.
On CPUs that support the SVE2 extensions, this option will enable
those too.
Note that for architectural reasons, firmware _must_ implement SVE Note that for architectural reasons, firmware _must_ implement SVE
support when running on SVE capable hardware. The required support support when running on SVE capable hardware. The required support
is present in: is present in:
@ -1347,7 +1404,7 @@ config ARM64_PSEUDO_NMI
help help
Adds support for mimicking Non-Maskable Interrupts through the use of Adds support for mimicking Non-Maskable Interrupts through the use of
GIC interrupt priority. This support requires version 3 or later of GIC interrupt priority. This support requires version 3 or later of
Arm GIC. ARM GIC.
This high priority configuration for interrupts needs to be This high priority configuration for interrupts needs to be
explicitly enabled by setting the kernel parameter explicitly enabled by setting the kernel parameter
@ -1471,25 +1528,6 @@ config DMI
endmenu endmenu
config COMPAT
bool "Kernel support for 32-bit EL0"
depends on ARM64_4K_PAGES || EXPERT
select COMPAT_BINFMT_ELF if BINFMT_ELF
select HAVE_UID16
select OLD_SIGSUSPEND3
select COMPAT_OLD_SIGACTION
help
This option enables support for a 32-bit EL0 running under a 64-bit
kernel at EL1. AArch32-specific components such as system calls,
the user helper functions, VFP support and the ptrace interface are
handled appropriately by the kernel.
If you use a page size other than 4KB (i.e, 16KB or 64KB), please be aware
that you will only be able to execute AArch32 binaries that were compiled
with page size aligned segments.
If you want to execute 32-bit userspace applications, say Y.
config SYSVIPC_COMPAT config SYSVIPC_COMPAT
def_bool y def_bool y
depends on COMPAT && SYSVIPC depends on COMPAT && SYSVIPC

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2018 MediaTek Inc. * Copyright (C) 2018 MediaTek Inc.
* Author: Zhiyong Tao <zhiyong.tao@mediatek.com> * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>

View File

@ -372,7 +372,7 @@ static struct aead_alg ccm_aes_alg = {
static int __init aes_mod_init(void) static int __init aes_mod_init(void)
{ {
if (!(elf_hwcap & HWCAP_AES)) if (!cpu_have_named_feature(AES))
return -ENODEV; return -ENODEV;
return crypto_register_aead(&ccm_aes_alg); return crypto_register_aead(&ccm_aes_alg);
} }

View File

@ -440,7 +440,7 @@ static int __init aes_init(void)
int err; int err;
int i; int i;
if (!(elf_hwcap & HWCAP_ASIMD)) if (!cpu_have_named_feature(ASIMD))
return -ENODEV; return -ENODEV;
err = crypto_register_skciphers(aes_algs, ARRAY_SIZE(aes_algs)); err = crypto_register_skciphers(aes_algs, ARRAY_SIZE(aes_algs));

View File

@ -173,7 +173,7 @@ static struct skcipher_alg algs[] = {
static int __init chacha_simd_mod_init(void) static int __init chacha_simd_mod_init(void)
{ {
if (!(elf_hwcap & HWCAP_ASIMD)) if (!cpu_have_named_feature(ASIMD))
return -ENODEV; return -ENODEV;
return crypto_register_skciphers(algs, ARRAY_SIZE(algs)); return crypto_register_skciphers(algs, ARRAY_SIZE(algs));

View File

@ -101,7 +101,7 @@ static struct shash_alg crc_t10dif_alg[] = {{
static int __init crc_t10dif_mod_init(void) static int __init crc_t10dif_mod_init(void)
{ {
if (elf_hwcap & HWCAP_PMULL) if (cpu_have_named_feature(PMULL))
return crypto_register_shashes(crc_t10dif_alg, return crypto_register_shashes(crc_t10dif_alg,
ARRAY_SIZE(crc_t10dif_alg)); ARRAY_SIZE(crc_t10dif_alg));
else else
@ -111,7 +111,7 @@ static int __init crc_t10dif_mod_init(void)
static void __exit crc_t10dif_mod_exit(void) static void __exit crc_t10dif_mod_exit(void)
{ {
if (elf_hwcap & HWCAP_PMULL) if (cpu_have_named_feature(PMULL))
crypto_unregister_shashes(crc_t10dif_alg, crypto_unregister_shashes(crc_t10dif_alg,
ARRAY_SIZE(crc_t10dif_alg)); ARRAY_SIZE(crc_t10dif_alg));
else else

View File

@ -704,10 +704,10 @@ static int __init ghash_ce_mod_init(void)
{ {
int ret; int ret;
if (!(elf_hwcap & HWCAP_ASIMD)) if (!cpu_have_named_feature(ASIMD))
return -ENODEV; return -ENODEV;
if (elf_hwcap & HWCAP_PMULL) if (cpu_have_named_feature(PMULL))
ret = crypto_register_shashes(ghash_alg, ret = crypto_register_shashes(ghash_alg,
ARRAY_SIZE(ghash_alg)); ARRAY_SIZE(ghash_alg));
else else
@ -717,7 +717,7 @@ static int __init ghash_ce_mod_init(void)
if (ret) if (ret)
return ret; return ret;
if (elf_hwcap & HWCAP_PMULL) { if (cpu_have_named_feature(PMULL)) {
ret = crypto_register_aead(&gcm_aes_alg); ret = crypto_register_aead(&gcm_aes_alg);
if (ret) if (ret)
crypto_unregister_shashes(ghash_alg, crypto_unregister_shashes(ghash_alg,
@ -728,7 +728,7 @@ static int __init ghash_ce_mod_init(void)
static void __exit ghash_ce_mod_exit(void) static void __exit ghash_ce_mod_exit(void)
{ {
if (elf_hwcap & HWCAP_PMULL) if (cpu_have_named_feature(PMULL))
crypto_unregister_shashes(ghash_alg, ARRAY_SIZE(ghash_alg)); crypto_unregister_shashes(ghash_alg, ARRAY_SIZE(ghash_alg));
else else
crypto_unregister_shash(ghash_alg); crypto_unregister_shash(ghash_alg);

View File

@ -56,7 +56,7 @@ static struct shash_alg nhpoly1305_alg = {
static int __init nhpoly1305_mod_init(void) static int __init nhpoly1305_mod_init(void)
{ {
if (!(elf_hwcap & HWCAP_ASIMD)) if (!cpu_have_named_feature(ASIMD))
return -ENODEV; return -ENODEV;
return crypto_register_shash(&nhpoly1305_alg); return crypto_register_shash(&nhpoly1305_alg);

View File

@ -173,7 +173,7 @@ static int __init sha256_mod_init(void)
if (ret) if (ret)
return ret; return ret;
if (elf_hwcap & HWCAP_ASIMD) { if (cpu_have_named_feature(ASIMD)) {
ret = crypto_register_shashes(neon_algs, ARRAY_SIZE(neon_algs)); ret = crypto_register_shashes(neon_algs, ARRAY_SIZE(neon_algs));
if (ret) if (ret)
crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
@ -183,7 +183,7 @@ static int __init sha256_mod_init(void)
static void __exit sha256_mod_fini(void) static void __exit sha256_mod_fini(void)
{ {
if (elf_hwcap & HWCAP_ASIMD) if (cpu_have_named_feature(ASIMD))
crypto_unregister_shashes(neon_algs, ARRAY_SIZE(neon_algs)); crypto_unregister_shashes(neon_algs, ARRAY_SIZE(neon_algs));
crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
} }

View File

@ -31,11 +31,23 @@
#include <clocksource/arm_arch_timer.h> #include <clocksource/arm_arch_timer.h>
#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND) #if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND)
extern struct static_key_false arch_timer_read_ool_enabled; #define has_erratum_handler(h) \
#define needs_unstable_timer_counter_workaround() \ ({ \
static_branch_unlikely(&arch_timer_read_ool_enabled) const struct arch_timer_erratum_workaround *__wa; \
__wa = __this_cpu_read(timer_unstable_counter_workaround); \
(__wa && __wa->h); \
})
#define erratum_handler(h) \
({ \
const struct arch_timer_erratum_workaround *__wa; \
__wa = __this_cpu_read(timer_unstable_counter_workaround); \
(__wa && __wa->h) ? __wa->h : arch_timer_##h; \
})
#else #else
#define needs_unstable_timer_counter_workaround() false #define has_erratum_handler(h) false
#define erratum_handler(h) (arch_timer_##h)
#endif #endif
enum arch_timer_erratum_match_type { enum arch_timer_erratum_match_type {
@ -61,23 +73,37 @@ struct arch_timer_erratum_workaround {
DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *, DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *,
timer_unstable_counter_workaround); timer_unstable_counter_workaround);
/* inline sysreg accessors that make erratum_handler() work */
static inline notrace u32 arch_timer_read_cntp_tval_el0(void)
{
return read_sysreg(cntp_tval_el0);
}
static inline notrace u32 arch_timer_read_cntv_tval_el0(void)
{
return read_sysreg(cntv_tval_el0);
}
static inline notrace u64 arch_timer_read_cntpct_el0(void)
{
return read_sysreg(cntpct_el0);
}
static inline notrace u64 arch_timer_read_cntvct_el0(void)
{
return read_sysreg(cntvct_el0);
}
#define arch_timer_reg_read_stable(reg) \ #define arch_timer_reg_read_stable(reg) \
({ \ ({ \
u64 _val; \ u64 _val; \
if (needs_unstable_timer_counter_workaround()) { \ \
const struct arch_timer_erratum_workaround *wa; \
preempt_disable_notrace(); \ preempt_disable_notrace(); \
wa = __this_cpu_read(timer_unstable_counter_workaround); \ _val = erratum_handler(read_ ## reg)(); \
if (wa && wa->read_##reg) \
_val = wa->read_##reg(); \
else \
_val = read_sysreg(reg); \
preempt_enable_notrace(); \ preempt_enable_notrace(); \
} else { \ \
_val = read_sysreg(reg); \ _val; \
} \ })
_val; \
})
/* /*
* These register accessors are marked inline so the compiler can * These register accessors are marked inline so the compiler can
@ -148,18 +174,67 @@ static inline void arch_timer_set_cntkctl(u32 cntkctl)
isb(); isb();
} }
static inline u64 arch_counter_get_cntpct(void) /*
* Ensure that reads of the counter are treated the same as memory reads
* for the purposes of ordering by subsequent memory barriers.
*
* This insanity brought to you by speculative system register reads,
* out-of-order memory accesses, sequence locks and Thomas Gleixner.
*
* http://lists.infradead.org/pipermail/linux-arm-kernel/2019-February/631195.html
*/
#define arch_counter_enforce_ordering(val) do { \
u64 tmp, _val = (val); \
\
asm volatile( \
" eor %0, %1, %1\n" \
" add %0, sp, %0\n" \
" ldr xzr, [%0]" \
: "=r" (tmp) : "r" (_val)); \
} while (0)
static inline u64 __arch_counter_get_cntpct_stable(void)
{ {
u64 cnt;
isb(); isb();
return arch_timer_reg_read_stable(cntpct_el0); cnt = arch_timer_reg_read_stable(cntpct_el0);
arch_counter_enforce_ordering(cnt);
return cnt;
} }
static inline u64 arch_counter_get_cntvct(void) static inline u64 __arch_counter_get_cntpct(void)
{ {
u64 cnt;
isb(); isb();
return arch_timer_reg_read_stable(cntvct_el0); cnt = read_sysreg(cntpct_el0);
arch_counter_enforce_ordering(cnt);
return cnt;
} }
static inline u64 __arch_counter_get_cntvct_stable(void)
{
u64 cnt;
isb();
cnt = arch_timer_reg_read_stable(cntvct_el0);
arch_counter_enforce_ordering(cnt);
return cnt;
}
static inline u64 __arch_counter_get_cntvct(void)
{
u64 cnt;
isb();
cnt = read_sysreg(cntvct_el0);
arch_counter_enforce_ordering(cnt);
return cnt;
}
#undef arch_counter_enforce_ordering
static inline int arch_timer_arch_init(void) static inline int arch_timer_arch_init(void)
{ {
return 0; return 0;

View File

@ -407,10 +407,14 @@ alternative_endif
.ifc \op, cvap .ifc \op, cvap
sys 3, c7, c12, 1, \kaddr // dc cvap sys 3, c7, c12, 1, \kaddr // dc cvap
.else .else
.ifc \op, cvadp
sys 3, c7, c13, 1, \kaddr // dc cvadp
.else
dc \op, \kaddr dc \op, \kaddr
.endif .endif
.endif .endif
.endif .endif
.endif
add \kaddr, \kaddr, \tmp1 add \kaddr, \kaddr, \tmp1
cmp \kaddr, \size cmp \kaddr, \size
b.lo 9998b b.lo 9998b
@ -442,8 +446,8 @@ USER(\label, ic ivau, \tmp2) // invalidate I line PoU
* reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present * reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present
*/ */
.macro reset_pmuserenr_el0, tmpreg .macro reset_pmuserenr_el0, tmpreg
mrs \tmpreg, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer mrs \tmpreg, id_aa64dfr0_el1
sbfx \tmpreg, \tmpreg, #8, #4 sbfx \tmpreg, \tmpreg, #ID_AA64DFR0_PMUVER_SHIFT, #4
cmp \tmpreg, #1 // Skip if no PMU present cmp \tmpreg, #1 // Skip if no PMU present
b.lt 9000f b.lt 9000f
msr pmuserenr_el0, xzr // Disable PMU access from EL0 msr pmuserenr_el0, xzr // Disable PMU access from EL0

View File

@ -20,6 +20,8 @@
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/kasan-checks.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))
@ -72,31 +74,33 @@ static inline unsigned long array_index_mask_nospec(unsigned long idx,
#define __smp_store_release(p, v) \ #define __smp_store_release(p, v) \
do { \ do { \
typeof(p) __p = (p); \
union { typeof(*p) __val; char __c[1]; } __u = \ union { typeof(*p) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(*p)) (v) }; \ { .__val = (__force typeof(*p)) (v) }; \
compiletime_assert_atomic_type(*p); \ compiletime_assert_atomic_type(*p); \
kasan_check_write(__p, sizeof(*p)); \
switch (sizeof(*p)) { \ switch (sizeof(*p)) { \
case 1: \ case 1: \
asm volatile ("stlrb %w1, %0" \ asm volatile ("stlrb %w1, %0" \
: "=Q" (*p) \ : "=Q" (*__p) \
: "r" (*(__u8 *)__u.__c) \ : "r" (*(__u8 *)__u.__c) \
: "memory"); \ : "memory"); \
break; \ break; \
case 2: \ case 2: \
asm volatile ("stlrh %w1, %0" \ asm volatile ("stlrh %w1, %0" \
: "=Q" (*p) \ : "=Q" (*__p) \
: "r" (*(__u16 *)__u.__c) \ : "r" (*(__u16 *)__u.__c) \
: "memory"); \ : "memory"); \
break; \ break; \
case 4: \ case 4: \
asm volatile ("stlr %w1, %0" \ asm volatile ("stlr %w1, %0" \
: "=Q" (*p) \ : "=Q" (*__p) \
: "r" (*(__u32 *)__u.__c) \ : "r" (*(__u32 *)__u.__c) \
: "memory"); \ : "memory"); \
break; \ break; \
case 8: \ case 8: \
asm volatile ("stlr %1, %0" \ asm volatile ("stlr %1, %0" \
: "=Q" (*p) \ : "=Q" (*__p) \
: "r" (*(__u64 *)__u.__c) \ : "r" (*(__u64 *)__u.__c) \
: "memory"); \ : "memory"); \
break; \ break; \
@ -106,27 +110,29 @@ do { \
#define __smp_load_acquire(p) \ #define __smp_load_acquire(p) \
({ \ ({ \
union { typeof(*p) __val; char __c[1]; } __u; \ union { typeof(*p) __val; char __c[1]; } __u; \
typeof(p) __p = (p); \
compiletime_assert_atomic_type(*p); \ compiletime_assert_atomic_type(*p); \
kasan_check_read(__p, sizeof(*p)); \
switch (sizeof(*p)) { \ switch (sizeof(*p)) { \
case 1: \ case 1: \
asm volatile ("ldarb %w0, %1" \ asm volatile ("ldarb %w0, %1" \
: "=r" (*(__u8 *)__u.__c) \ : "=r" (*(__u8 *)__u.__c) \
: "Q" (*p) : "memory"); \ : "Q" (*__p) : "memory"); \
break; \ break; \
case 2: \ case 2: \
asm volatile ("ldarh %w0, %1" \ asm volatile ("ldarh %w0, %1" \
: "=r" (*(__u16 *)__u.__c) \ : "=r" (*(__u16 *)__u.__c) \
: "Q" (*p) : "memory"); \ : "Q" (*__p) : "memory"); \
break; \ break; \
case 4: \ case 4: \
asm volatile ("ldar %w0, %1" \ asm volatile ("ldar %w0, %1" \
: "=r" (*(__u32 *)__u.__c) \ : "=r" (*(__u32 *)__u.__c) \
: "Q" (*p) : "memory"); \ : "Q" (*__p) : "memory"); \
break; \ break; \
case 8: \ case 8: \
asm volatile ("ldar %0, %1" \ asm volatile ("ldar %0, %1" \
: "=r" (*(__u64 *)__u.__c) \ : "=r" (*(__u64 *)__u.__c) \
: "Q" (*p) : "memory"); \ : "Q" (*__p) : "memory"); \
break; \ break; \
} \ } \
__u.__val; \ __u.__val; \

View File

@ -11,6 +11,8 @@
/* /*
* #imm16 values used for BRK instruction generation * #imm16 values used for BRK instruction generation
* 0x004: for installing kprobes
* 0x005: for installing uprobes
* Allowed values for kgdb are 0x400 - 0x7ff * Allowed values for kgdb are 0x400 - 0x7ff
* 0x100: for triggering a fault on purpose (reserved) * 0x100: for triggering a fault on purpose (reserved)
* 0x400: for dynamic BRK instruction * 0x400: for dynamic BRK instruction
@ -18,10 +20,13 @@
* 0x800: kernel-mode BUG() and WARN() traps * 0x800: kernel-mode BUG() and WARN() traps
* 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff) * 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff)
*/ */
#define KPROBES_BRK_IMM 0x004
#define UPROBES_BRK_IMM 0x005
#define FAULT_BRK_IMM 0x100 #define FAULT_BRK_IMM 0x100
#define KGDB_DYN_DBG_BRK_IMM 0x400 #define KGDB_DYN_DBG_BRK_IMM 0x400
#define KGDB_COMPILED_DBG_BRK_IMM 0x401 #define KGDB_COMPILED_DBG_BRK_IMM 0x401
#define BUG_BRK_IMM 0x800 #define BUG_BRK_IMM 0x800
#define KASAN_BRK_IMM 0x900 #define KASAN_BRK_IMM 0x900
#define KASAN_BRK_MASK 0x0ff
#endif #endif

View File

@ -61,7 +61,8 @@
#define ARM64_HAS_GENERIC_AUTH_ARCH 40 #define ARM64_HAS_GENERIC_AUTH_ARCH 40
#define ARM64_HAS_GENERIC_AUTH_IMP_DEF 41 #define ARM64_HAS_GENERIC_AUTH_IMP_DEF 41
#define ARM64_HAS_IRQ_PRIO_MASKING 42 #define ARM64_HAS_IRQ_PRIO_MASKING 42
#define ARM64_HAS_DCPODP 43
#define ARM64_NCAPS 43 #define ARM64_NCAPS 44
#endif /* __ASM_CPUCAPS_H */ #endif /* __ASM_CPUCAPS_H */

View File

@ -14,15 +14,8 @@
#include <asm/hwcap.h> #include <asm/hwcap.h>
#include <asm/sysreg.h> #include <asm/sysreg.h>
/* #define MAX_CPU_FEATURES 64
* In the arm64 world (as in the ARM world), elf_hwcap is used both internally #define cpu_feature(x) KERNEL_HWCAP_ ## x
* in the kernel and for user space to keep track of which optional features
* are supported by the current system. So let's map feature 'x' to HWCAP_x.
* Note that HWCAP_x constants are bit fields so we need to take the log.
*/
#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))
#define cpu_feature(x) ilog2(HWCAP_ ## x)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
@ -399,11 +392,13 @@ extern DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE);
for_each_set_bit(cap, cpu_hwcaps, ARM64_NCAPS) for_each_set_bit(cap, cpu_hwcaps, ARM64_NCAPS)
bool this_cpu_has_cap(unsigned int cap); bool this_cpu_has_cap(unsigned int cap);
void cpu_set_feature(unsigned int num);
bool cpu_have_feature(unsigned int num);
unsigned long cpu_get_elf_hwcap(void);
unsigned long cpu_get_elf_hwcap2(void);
static inline bool cpu_have_feature(unsigned int num) #define cpu_set_named_feature(name) cpu_set_feature(cpu_feature(name))
{ #define cpu_have_named_feature(name) cpu_have_feature(cpu_feature(name))
return elf_hwcap & (1UL << num);
}
/* System capability check for constant caps */ /* System capability check for constant caps */
static inline bool __cpus_have_const_cap(int num) static inline bool __cpus_have_const_cap(int num)
@ -638,11 +633,7 @@ static inline int arm64_get_ssbd_state(void)
#endif #endif
} }
#ifdef CONFIG_ARM64_SSBD
void arm64_set_ssbd_mitigation(bool state); void arm64_set_ssbd_mitigation(bool state);
#else
static inline void arm64_set_ssbd_mitigation(bool state) {}
#endif
extern int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt); extern int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt);

View File

@ -89,6 +89,7 @@
#define ARM_CPU_PART_CORTEX_A35 0xD04 #define ARM_CPU_PART_CORTEX_A35 0xD04
#define ARM_CPU_PART_CORTEX_A55 0xD05 #define ARM_CPU_PART_CORTEX_A55 0xD05
#define ARM_CPU_PART_CORTEX_A76 0xD0B #define ARM_CPU_PART_CORTEX_A76 0xD0B
#define ARM_CPU_PART_NEOVERSE_N1 0xD0C
#define APM_CPU_PART_POTENZA 0x000 #define APM_CPU_PART_POTENZA 0x000
@ -118,6 +119,7 @@
#define MIDR_CORTEX_A35 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A35) #define MIDR_CORTEX_A35 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A35)
#define MIDR_CORTEX_A55 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A55) #define MIDR_CORTEX_A55 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A55)
#define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) #define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76)
#define MIDR_NEOVERSE_N1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N1)
#define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX)
#define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX)
#define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX)

View File

@ -65,12 +65,9 @@
#define CACHE_FLUSH_IS_SAFE 1 #define CACHE_FLUSH_IS_SAFE 1
/* kprobes BRK opcodes with ESR encoding */ /* kprobes BRK opcodes with ESR encoding */
#define BRK64_ESR_MASK 0xFFFF #define BRK64_OPCODE_KPROBES (AARCH64_BREAK_MON | (KPROBES_BRK_IMM << 5))
#define BRK64_ESR_KPROBES 0x0004
#define BRK64_OPCODE_KPROBES (AARCH64_BREAK_MON | (BRK64_ESR_KPROBES << 5))
/* uprobes BRK opcodes with ESR encoding */ /* uprobes BRK opcodes with ESR encoding */
#define BRK64_ESR_UPROBES 0x0005 #define BRK64_OPCODE_UPROBES (AARCH64_BREAK_MON | (UPROBES_BRK_IMM << 5))
#define BRK64_OPCODE_UPROBES (AARCH64_BREAK_MON | (BRK64_ESR_UPROBES << 5))
/* AArch32 */ /* AArch32 */
#define DBG_ESR_EVT_BKPT 0x4 #define DBG_ESR_EVT_BKPT 0x4
@ -94,18 +91,24 @@ struct step_hook {
int (*fn)(struct pt_regs *regs, unsigned int esr); int (*fn)(struct pt_regs *regs, unsigned int esr);
}; };
void register_step_hook(struct step_hook *hook); void register_user_step_hook(struct step_hook *hook);
void unregister_step_hook(struct step_hook *hook); void unregister_user_step_hook(struct step_hook *hook);
void register_kernel_step_hook(struct step_hook *hook);
void unregister_kernel_step_hook(struct step_hook *hook);
struct break_hook { struct break_hook {
struct list_head node; struct list_head node;
u32 esr_val;
u32 esr_mask;
int (*fn)(struct pt_regs *regs, unsigned int esr); int (*fn)(struct pt_regs *regs, unsigned int esr);
u16 imm;
u16 mask; /* These bits are ignored when comparing with imm */
}; };
void register_break_hook(struct break_hook *hook); void register_user_break_hook(struct break_hook *hook);
void unregister_break_hook(struct break_hook *hook); void unregister_user_break_hook(struct break_hook *hook);
void register_kernel_break_hook(struct break_hook *hook);
void unregister_kernel_break_hook(struct break_hook *hook);
u8 debug_monitors_arch(void); u8 debug_monitors_arch(void);

View File

@ -214,10 +214,10 @@ typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG];
set_thread_flag(TIF_32BIT); \ set_thread_flag(TIF_32BIT); \
}) })
#define COMPAT_ARCH_DLINFO #define COMPAT_ARCH_DLINFO
extern int aarch32_setup_vectors_page(struct linux_binprm *bprm, extern int aarch32_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp); int uses_interp);
#define compat_arch_setup_additional_pages \ #define compat_arch_setup_additional_pages \
aarch32_setup_vectors_page aarch32_setup_additional_pages
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */

View File

@ -156,9 +156,7 @@
ESR_ELx_WFx_ISS_WFI) ESR_ELx_WFx_ISS_WFI)
/* BRK instruction trap from AArch64 state */ /* BRK instruction trap from AArch64 state */
#define ESR_ELx_VAL_BRK64(imm) \ #define ESR_ELx_BRK64_ISS_COMMENT_MASK 0xffff
((ESR_ELx_EC_BRK64 << ESR_ELx_EC_SHIFT) | ESR_ELx_IL | \
((imm) & 0xffff))
/* ISS field definitions for System instruction traps */ /* ISS field definitions for System instruction traps */
#define ESR_ELx_SYS64_ISS_RES0_SHIFT 22 #define ESR_ELx_SYS64_ISS_RES0_SHIFT 22
@ -198,9 +196,10 @@
/* /*
* User space cache operations have the following sysreg encoding * User space cache operations have the following sysreg encoding
* in System instructions. * in System instructions.
* op0=1, op1=3, op2=1, crn=7, crm={ 5, 10, 11, 12, 14 }, WRITE (L=0) * op0=1, op1=3, op2=1, crn=7, crm={ 5, 10, 11, 12, 13, 14 }, WRITE (L=0)
*/ */
#define ESR_ELx_SYS64_ISS_CRM_DC_CIVAC 14 #define ESR_ELx_SYS64_ISS_CRM_DC_CIVAC 14
#define ESR_ELx_SYS64_ISS_CRM_DC_CVADP 13
#define ESR_ELx_SYS64_ISS_CRM_DC_CVAP 12 #define ESR_ELx_SYS64_ISS_CRM_DC_CVAP 12
#define ESR_ELx_SYS64_ISS_CRM_DC_CVAU 11 #define ESR_ELx_SYS64_ISS_CRM_DC_CVAU 11
#define ESR_ELx_SYS64_ISS_CRM_DC_CVAC 10 #define ESR_ELx_SYS64_ISS_CRM_DC_CVAC 10

View File

@ -23,26 +23,34 @@
#include <asm/errno.h> #include <asm/errno.h>
#define FUTEX_MAX_LOOPS 128 /* What's the largest number you can think of? */
#define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \ #define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \
do { \ do { \
unsigned int loops = FUTEX_MAX_LOOPS; \
\
uaccess_enable(); \ uaccess_enable(); \
asm volatile( \ asm volatile( \
" prfm pstl1strm, %2\n" \ " prfm pstl1strm, %2\n" \
"1: ldxr %w1, %2\n" \ "1: ldxr %w1, %2\n" \
insn "\n" \ insn "\n" \
"2: stlxr %w0, %w3, %2\n" \ "2: stlxr %w0, %w3, %2\n" \
" cbnz %w0, 1b\n" \ " cbz %w0, 3f\n" \
" dmb ish\n" \ " sub %w4, %w4, %w0\n" \
" cbnz %w4, 1b\n" \
" mov %w0, %w7\n" \
"3:\n" \ "3:\n" \
" dmb ish\n" \
" .pushsection .fixup,\"ax\"\n" \ " .pushsection .fixup,\"ax\"\n" \
" .align 2\n" \ " .align 2\n" \
"4: mov %w0, %w5\n" \ "4: mov %w0, %w6\n" \
" b 3b\n" \ " b 3b\n" \
" .popsection\n" \ " .popsection\n" \
_ASM_EXTABLE(1b, 4b) \ _ASM_EXTABLE(1b, 4b) \
_ASM_EXTABLE(2b, 4b) \ _ASM_EXTABLE(2b, 4b) \
: "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \ : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp), \
: "r" (oparg), "Ir" (-EFAULT) \ "+r" (loops) \
: "r" (oparg), "Ir" (-EFAULT), "Ir" (-EAGAIN) \
: "memory"); \ : "memory"); \
uaccess_disable(); \ uaccess_disable(); \
} while (0) } while (0)
@ -57,23 +65,23 @@ arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr)
switch (op) { switch (op) {
case FUTEX_OP_SET: case FUTEX_OP_SET:
__futex_atomic_op("mov %w3, %w4", __futex_atomic_op("mov %w3, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
case FUTEX_OP_ADD: case FUTEX_OP_ADD:
__futex_atomic_op("add %w3, %w1, %w4", __futex_atomic_op("add %w3, %w1, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
case FUTEX_OP_OR: case FUTEX_OP_OR:
__futex_atomic_op("orr %w3, %w1, %w4", __futex_atomic_op("orr %w3, %w1, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
case FUTEX_OP_ANDN: case FUTEX_OP_ANDN:
__futex_atomic_op("and %w3, %w1, %w4", __futex_atomic_op("and %w3, %w1, %w5",
ret, oldval, uaddr, tmp, ~oparg); ret, oldval, uaddr, tmp, ~oparg);
break; break;
case FUTEX_OP_XOR: case FUTEX_OP_XOR:
__futex_atomic_op("eor %w3, %w1, %w4", __futex_atomic_op("eor %w3, %w1, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
default: default:
@ -93,6 +101,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr,
u32 oldval, u32 newval) u32 oldval, u32 newval)
{ {
int ret = 0; int ret = 0;
unsigned int loops = FUTEX_MAX_LOOPS;
u32 val, tmp; u32 val, tmp;
u32 __user *uaddr; u32 __user *uaddr;
@ -104,24 +113,30 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr,
asm volatile("// futex_atomic_cmpxchg_inatomic\n" asm volatile("// futex_atomic_cmpxchg_inatomic\n"
" prfm pstl1strm, %2\n" " prfm pstl1strm, %2\n"
"1: ldxr %w1, %2\n" "1: ldxr %w1, %2\n"
" sub %w3, %w1, %w4\n" " sub %w3, %w1, %w5\n"
" cbnz %w3, 3f\n" " cbnz %w3, 4f\n"
"2: stlxr %w3, %w5, %2\n" "2: stlxr %w3, %w6, %2\n"
" cbnz %w3, 1b\n" " cbz %w3, 3f\n"
" dmb ish\n" " sub %w4, %w4, %w3\n"
" cbnz %w4, 1b\n"
" mov %w0, %w8\n"
"3:\n" "3:\n"
" dmb ish\n"
"4:\n"
" .pushsection .fixup,\"ax\"\n" " .pushsection .fixup,\"ax\"\n"
"4: mov %w0, %w6\n" "5: mov %w0, %w7\n"
" b 3b\n" " b 4b\n"
" .popsection\n" " .popsection\n"
_ASM_EXTABLE(1b, 4b) _ASM_EXTABLE(1b, 5b)
_ASM_EXTABLE(2b, 4b) _ASM_EXTABLE(2b, 5b)
: "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp) : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp), "+r" (loops)
: "r" (oldval), "r" (newval), "Ir" (-EFAULT) : "r" (oldval), "r" (newval), "Ir" (-EFAULT), "Ir" (-EAGAIN)
: "memory"); : "memory");
uaccess_disable(); uaccess_disable();
*uval = val; if (!ret)
*uval = val;
return ret; return ret;
} }

View File

@ -17,6 +17,7 @@
#define __ASM_HWCAP_H #define __ASM_HWCAP_H
#include <uapi/asm/hwcap.h> #include <uapi/asm/hwcap.h>
#include <asm/cpufeature.h>
#define COMPAT_HWCAP_HALF (1 << 1) #define COMPAT_HWCAP_HALF (1 << 1)
#define COMPAT_HWCAP_THUMB (1 << 2) #define COMPAT_HWCAP_THUMB (1 << 2)
@ -40,11 +41,67 @@
#define COMPAT_HWCAP2_CRC32 (1 << 4) #define COMPAT_HWCAP2_CRC32 (1 << 4)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/log2.h>
/*
* For userspace we represent hwcaps as a collection of HWCAP{,2}_x bitfields
* as described in uapi/asm/hwcap.h. For the kernel we represent hwcaps as
* natural numbers (in a single range of size MAX_CPU_FEATURES) defined here
* with prefix KERNEL_HWCAP_ mapped to their HWCAP{,2}_x counterpart.
*
* Hwcaps should be set and tested within the kernel via the
* cpu_{set,have}_named_feature(feature) where feature is the unique suffix
* of KERNEL_HWCAP_{feature}.
*/
#define __khwcap_feature(x) const_ilog2(HWCAP_ ## x)
#define KERNEL_HWCAP_FP __khwcap_feature(FP)
#define KERNEL_HWCAP_ASIMD __khwcap_feature(ASIMD)
#define KERNEL_HWCAP_EVTSTRM __khwcap_feature(EVTSTRM)
#define KERNEL_HWCAP_AES __khwcap_feature(AES)
#define KERNEL_HWCAP_PMULL __khwcap_feature(PMULL)
#define KERNEL_HWCAP_SHA1 __khwcap_feature(SHA1)
#define KERNEL_HWCAP_SHA2 __khwcap_feature(SHA2)
#define KERNEL_HWCAP_CRC32 __khwcap_feature(CRC32)
#define KERNEL_HWCAP_ATOMICS __khwcap_feature(ATOMICS)
#define KERNEL_HWCAP_FPHP __khwcap_feature(FPHP)
#define KERNEL_HWCAP_ASIMDHP __khwcap_feature(ASIMDHP)
#define KERNEL_HWCAP_CPUID __khwcap_feature(CPUID)
#define KERNEL_HWCAP_ASIMDRDM __khwcap_feature(ASIMDRDM)
#define KERNEL_HWCAP_JSCVT __khwcap_feature(JSCVT)
#define KERNEL_HWCAP_FCMA __khwcap_feature(FCMA)
#define KERNEL_HWCAP_LRCPC __khwcap_feature(LRCPC)
#define KERNEL_HWCAP_DCPOP __khwcap_feature(DCPOP)
#define KERNEL_HWCAP_SHA3 __khwcap_feature(SHA3)
#define KERNEL_HWCAP_SM3 __khwcap_feature(SM3)
#define KERNEL_HWCAP_SM4 __khwcap_feature(SM4)
#define KERNEL_HWCAP_ASIMDDP __khwcap_feature(ASIMDDP)
#define KERNEL_HWCAP_SHA512 __khwcap_feature(SHA512)
#define KERNEL_HWCAP_SVE __khwcap_feature(SVE)
#define KERNEL_HWCAP_ASIMDFHM __khwcap_feature(ASIMDFHM)
#define KERNEL_HWCAP_DIT __khwcap_feature(DIT)
#define KERNEL_HWCAP_USCAT __khwcap_feature(USCAT)
#define KERNEL_HWCAP_ILRCPC __khwcap_feature(ILRCPC)
#define KERNEL_HWCAP_FLAGM __khwcap_feature(FLAGM)
#define KERNEL_HWCAP_SSBS __khwcap_feature(SSBS)
#define KERNEL_HWCAP_SB __khwcap_feature(SB)
#define KERNEL_HWCAP_PACA __khwcap_feature(PACA)
#define KERNEL_HWCAP_PACG __khwcap_feature(PACG)
#define __khwcap2_feature(x) (const_ilog2(HWCAP2_ ## x) + 32)
#define KERNEL_HWCAP_DCPODP __khwcap2_feature(DCPODP)
#define KERNEL_HWCAP_SVE2 __khwcap2_feature(SVE2)
#define KERNEL_HWCAP_SVEAES __khwcap2_feature(SVEAES)
#define KERNEL_HWCAP_SVEPMULL __khwcap2_feature(SVEPMULL)
#define KERNEL_HWCAP_SVEBITPERM __khwcap2_feature(SVEBITPERM)
#define KERNEL_HWCAP_SVESHA3 __khwcap2_feature(SVESHA3)
#define KERNEL_HWCAP_SVESM4 __khwcap2_feature(SVESM4)
/* /*
* 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
* instruction set this cpu supports. * instruction set this cpu supports.
*/ */
#define ELF_HWCAP (elf_hwcap) #define ELF_HWCAP cpu_get_elf_hwcap()
#define ELF_HWCAP2 cpu_get_elf_hwcap2()
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#define COMPAT_ELF_HWCAP (compat_elf_hwcap) #define COMPAT_ELF_HWCAP (compat_elf_hwcap)
@ -60,6 +117,5 @@ enum {
#endif #endif
}; };
extern unsigned long elf_hwcap;
#endif #endif
#endif #endif

View File

@ -43,7 +43,7 @@ static inline void arch_local_irq_enable(void)
asm volatile(ALTERNATIVE( asm volatile(ALTERNATIVE(
"msr daifclr, #2 // arch_local_irq_enable\n" "msr daifclr, #2 // arch_local_irq_enable\n"
"nop", "nop",
"msr_s " __stringify(SYS_ICC_PMR_EL1) ",%0\n" __msr_s(SYS_ICC_PMR_EL1, "%0")
"dsb sy", "dsb sy",
ARM64_HAS_IRQ_PRIO_MASKING) ARM64_HAS_IRQ_PRIO_MASKING)
: :
@ -55,7 +55,7 @@ static inline void arch_local_irq_disable(void)
{ {
asm volatile(ALTERNATIVE( asm volatile(ALTERNATIVE(
"msr daifset, #2 // arch_local_irq_disable", "msr daifset, #2 // arch_local_irq_disable",
"msr_s " __stringify(SYS_ICC_PMR_EL1) ", %0", __msr_s(SYS_ICC_PMR_EL1, "%0"),
ARM64_HAS_IRQ_PRIO_MASKING) ARM64_HAS_IRQ_PRIO_MASKING)
: :
: "r" ((unsigned long) GIC_PRIO_IRQOFF) : "r" ((unsigned long) GIC_PRIO_IRQOFF)
@ -86,7 +86,7 @@ static inline unsigned long arch_local_save_flags(void)
"mov %0, %1\n" "mov %0, %1\n"
"nop\n" "nop\n"
"nop", "nop",
"mrs_s %0, " __stringify(SYS_ICC_PMR_EL1) "\n" __mrs_s("%0", SYS_ICC_PMR_EL1)
"ands %1, %1, " __stringify(PSR_I_BIT) "\n" "ands %1, %1, " __stringify(PSR_I_BIT) "\n"
"csel %0, %0, %2, eq", "csel %0, %0, %2, eq",
ARM64_HAS_IRQ_PRIO_MASKING) ARM64_HAS_IRQ_PRIO_MASKING)
@ -116,7 +116,7 @@ static inline void arch_local_irq_restore(unsigned long flags)
asm volatile(ALTERNATIVE( asm volatile(ALTERNATIVE(
"msr daif, %0\n" "msr daif, %0\n"
"nop", "nop",
"msr_s " __stringify(SYS_ICC_PMR_EL1) ", %0\n" __msr_s(SYS_ICC_PMR_EL1, "%0")
"dsb sy", "dsb sy",
ARM64_HAS_IRQ_PRIO_MASKING) ARM64_HAS_IRQ_PRIO_MASKING)
: "+r" (flags) : "+r" (flags)

View File

@ -54,8 +54,6 @@ void arch_remove_kprobe(struct kprobe *);
int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr); int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);
int kprobe_exceptions_notify(struct notifier_block *self, int kprobe_exceptions_notify(struct notifier_block *self,
unsigned long val, void *data); unsigned long val, void *data);
int kprobe_breakpoint_handler(struct pt_regs *regs, unsigned int esr);
int kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr);
void kretprobe_trampoline(void); void kretprobe_trampoline(void);
void __kprobes *trampoline_probe_handler(struct pt_regs *regs); void __kprobes *trampoline_probe_handler(struct pt_regs *regs);

View File

@ -30,7 +30,7 @@
({ \ ({ \
u64 reg; \ u64 reg; \
asm volatile(ALTERNATIVE("mrs %0, " __stringify(r##nvh),\ asm volatile(ALTERNATIVE("mrs %0, " __stringify(r##nvh),\
"mrs_s %0, " __stringify(r##vh),\ __mrs_s("%0", r##vh), \
ARM64_HAS_VIRT_HOST_EXTN) \ ARM64_HAS_VIRT_HOST_EXTN) \
: "=r" (reg)); \ : "=r" (reg)); \
reg; \ reg; \
@ -40,7 +40,7 @@
do { \ do { \
u64 __val = (u64)(v); \ u64 __val = (u64)(v); \
asm volatile(ALTERNATIVE("msr " __stringify(r##nvh) ", %x0",\ asm volatile(ALTERNATIVE("msr " __stringify(r##nvh) ", %x0",\
"msr_s " __stringify(r##vh) ", %x0",\ __msr_s(r##vh, "%x0"), \
ARM64_HAS_VIRT_HOST_EXTN) \ ARM64_HAS_VIRT_HOST_EXTN) \
: : "rZ" (__val)); \ : : "rZ" (__val)); \
} while (0) } while (0)

View File

@ -302,7 +302,7 @@ static inline void *phys_to_virt(phys_addr_t x)
*/ */
#define ARCH_PFN_OFFSET ((unsigned long)PHYS_PFN_OFFSET) #define ARCH_PFN_OFFSET ((unsigned long)PHYS_PFN_OFFSET)
#ifndef CONFIG_SPARSEMEM_VMEMMAP #if !defined(CONFIG_SPARSEMEM_VMEMMAP) || defined(CONFIG_DEBUG_VIRTUAL)
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
#define _virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) #define _virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
#else #else

View File

@ -33,12 +33,22 @@
static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
{ {
return (pmd_t *)__get_free_page(PGALLOC_GFP); struct page *page;
page = alloc_page(PGALLOC_GFP);
if (!page)
return NULL;
if (!pgtable_pmd_page_ctor(page)) {
__free_page(page);
return NULL;
}
return page_address(page);
} }
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmdp) static inline void pmd_free(struct mm_struct *mm, pmd_t *pmdp)
{ {
BUG_ON((unsigned long)pmdp & (PAGE_SIZE-1)); BUG_ON((unsigned long)pmdp & (PAGE_SIZE-1));
pgtable_pmd_page_dtor(virt_to_page(pmdp));
free_page((unsigned long)pmdp); free_page((unsigned long)pmdp);
} }

View File

@ -478,6 +478,8 @@ static inline phys_addr_t pmd_page_paddr(pmd_t pmd)
return __pmd_to_phys(pmd); return __pmd_to_phys(pmd);
} }
static inline void pte_unmap(pte_t *pte) { }
/* Find an entry in the third-level page table. */ /* Find an entry in the third-level page table. */
#define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) #define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
@ -485,9 +487,6 @@ static inline phys_addr_t pmd_page_paddr(pmd_t pmd)
#define pte_offset_kernel(dir,addr) ((pte_t *)__va(pte_offset_phys((dir), (addr)))) #define pte_offset_kernel(dir,addr) ((pte_t *)__va(pte_offset_phys((dir), (addr))))
#define pte_offset_map(dir,addr) pte_offset_kernel((dir), (addr)) #define pte_offset_map(dir,addr) pte_offset_kernel((dir), (addr))
#define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir), (addr))
#define pte_unmap(pte) do { } while (0)
#define pte_unmap_nested(pte) do { } while (0)
#define pte_set_fixmap(addr) ((pte_t *)set_fixmap_offset(FIX_PTE, addr)) #define pte_set_fixmap(addr) ((pte_t *)set_fixmap_offset(FIX_PTE, addr))
#define pte_set_fixmap_offset(pmd, addr) pte_set_fixmap(pte_offset_phys(pmd, addr)) #define pte_set_fixmap_offset(pmd, addr) pte_set_fixmap(pte_offset_phys(pmd, addr))

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 /* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_POINTER_AUTH_H #ifndef __ASM_POINTER_AUTH_H
#define __ASM_POINTER_AUTH_H #define __ASM_POINTER_AUTH_H

View File

@ -57,7 +57,15 @@
#define TASK_SIZE_64 (UL(1) << vabits_user) #define TASK_SIZE_64 (UL(1) << vabits_user)
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#if defined(CONFIG_ARM64_64K_PAGES) && defined(CONFIG_KUSER_HELPERS)
/*
* With CONFIG_ARM64_64K_PAGES enabled, the last page is occupied
* by the compat vectors page.
*/
#define TASK_SIZE_32 UL(0x100000000) #define TASK_SIZE_32 UL(0x100000000)
#else
#define TASK_SIZE_32 (UL(0x100000000) - PAGE_SIZE)
#endif /* CONFIG_ARM64_64K_PAGES */
#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \ #define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \
TASK_SIZE_32 : TASK_SIZE_64) TASK_SIZE_32 : TASK_SIZE_64)
#define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \ #define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \

View File

@ -305,6 +305,28 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
return regs->regs[0]; return regs->regs[0];
} }
/**
* regs_get_kernel_argument() - get Nth function argument in kernel
* @regs: pt_regs of that context
* @n: function argument number (start from 0)
*
* regs_get_argument() returns @n th argument of the function call.
*
* Note that this chooses the most likely register mapping. In very rare
* cases this may not return correct data, for example, if one of the
* function parameters is 16 bytes or bigger. In such cases, we cannot
* get access the parameter correctly and the register assignment of
* subsequent parameters will be shifted.
*/
static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs,
unsigned int n)
{
#define NR_REG_ARGUMENTS 8
if (n < NR_REG_ARGUMENTS)
return pt_regs_read_reg(regs, n);
return 0;
}
/* We must avoid circular header include via sched.h */ /* We must avoid circular header include via sched.h */
struct task_struct; struct task_struct;
int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task); int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task);

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 /* SPDX-License-Identifier: GPL-2.0 */
// Copyright (C) 2017 Arm Ltd. // Copyright (C) 2017 Arm Ltd.
#ifndef __ASM_SDEI_H #ifndef __ASM_SDEI_H
#define __ASM_SDEI_H #define __ASM_SDEI_H

View File

@ -20,8 +20,6 @@
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#include <linux/compat.h> #include <linux/compat.h>
#define AARCH32_KERN_SIGRET_CODE_OFFSET 0x500
int compat_setup_frame(int usig, struct ksignal *ksig, sigset_t *set, int compat_setup_frame(int usig, struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs); struct pt_regs *regs);
int compat_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, int compat_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,

View File

@ -119,7 +119,7 @@ static inline pud_t *stage2_pud_offset(struct kvm *kvm,
static inline void stage2_pud_free(struct kvm *kvm, pud_t *pud) static inline void stage2_pud_free(struct kvm *kvm, pud_t *pud)
{ {
if (kvm_stage2_has_pud(kvm)) if (kvm_stage2_has_pud(kvm))
pud_free(NULL, pud); free_page((unsigned long)pud);
} }
static inline bool stage2_pud_table_empty(struct kvm *kvm, pud_t *pudp) static inline bool stage2_pud_table_empty(struct kvm *kvm, pud_t *pudp)
@ -192,7 +192,7 @@ static inline pmd_t *stage2_pmd_offset(struct kvm *kvm,
static inline void stage2_pmd_free(struct kvm *kvm, pmd_t *pmd) static inline void stage2_pmd_free(struct kvm *kvm, pmd_t *pmd)
{ {
if (kvm_stage2_has_pmd(kvm)) if (kvm_stage2_has_pmd(kvm))
pmd_free(NULL, pmd); free_page((unsigned long)pmd);
} }
static inline bool stage2_pud_huge(struct kvm *kvm, pud_t pud) static inline bool stage2_pud_huge(struct kvm *kvm, pud_t pud)

View File

@ -606,6 +606,20 @@
#define ID_AA64PFR1_SSBS_PSTATE_ONLY 1 #define ID_AA64PFR1_SSBS_PSTATE_ONLY 1
#define ID_AA64PFR1_SSBS_PSTATE_INSNS 2 #define ID_AA64PFR1_SSBS_PSTATE_INSNS 2
/* id_aa64zfr0 */
#define ID_AA64ZFR0_SM4_SHIFT 40
#define ID_AA64ZFR0_SHA3_SHIFT 32
#define ID_AA64ZFR0_BITPERM_SHIFT 16
#define ID_AA64ZFR0_AES_SHIFT 4
#define ID_AA64ZFR0_SVEVER_SHIFT 0
#define ID_AA64ZFR0_SM4 0x1
#define ID_AA64ZFR0_SHA3 0x1
#define ID_AA64ZFR0_BITPERM 0x1
#define ID_AA64ZFR0_AES 0x1
#define ID_AA64ZFR0_AES_PMULL 0x2
#define ID_AA64ZFR0_SVEVER_SVE2 0x1
/* id_aa64mmfr0 */ /* id_aa64mmfr0 */
#define ID_AA64MMFR0_TGRAN4_SHIFT 28 #define ID_AA64MMFR0_TGRAN4_SHIFT 28
#define ID_AA64MMFR0_TGRAN64_SHIFT 24 #define ID_AA64MMFR0_TGRAN64_SHIFT 24
@ -746,20 +760,39 @@
#include <linux/build_bug.h> #include <linux/build_bug.h>
#include <linux/types.h> #include <linux/types.h>
asm( #define __DEFINE_MRS_MSR_S_REGNUM \
" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" " .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" \
" .equ .L__reg_num_x\\num, \\num\n" " .equ .L__reg_num_x\\num, \\num\n" \
" .endr\n" " .endr\n" \
" .equ .L__reg_num_xzr, 31\n" " .equ .L__reg_num_xzr, 31\n"
"\n"
" .macro mrs_s, rt, sreg\n" #define DEFINE_MRS_S \
__emit_inst(0xd5200000|(\\sreg)|(.L__reg_num_\\rt)) __DEFINE_MRS_MSR_S_REGNUM \
" .macro mrs_s, rt, sreg\n" \
__emit_inst(0xd5200000|(\\sreg)|(.L__reg_num_\\rt)) \
" .endm\n" " .endm\n"
"\n"
" .macro msr_s, sreg, rt\n" #define DEFINE_MSR_S \
__emit_inst(0xd5000000|(\\sreg)|(.L__reg_num_\\rt)) __DEFINE_MRS_MSR_S_REGNUM \
" .macro msr_s, sreg, rt\n" \
__emit_inst(0xd5000000|(\\sreg)|(.L__reg_num_\\rt)) \
" .endm\n" " .endm\n"
);
#define UNDEFINE_MRS_S \
" .purgem mrs_s\n"
#define UNDEFINE_MSR_S \
" .purgem msr_s\n"
#define __mrs_s(v, r) \
DEFINE_MRS_S \
" mrs_s " v ", " __stringify(r) "\n" \
UNDEFINE_MRS_S
#define __msr_s(r, v) \
DEFINE_MSR_S \
" msr_s " __stringify(r) ", " v "\n" \
UNDEFINE_MSR_S
/* /*
* Unlike read_cpuid, calls to read_sysreg are never expected to be * Unlike read_cpuid, calls to read_sysreg are never expected to be
@ -787,13 +820,13 @@ asm(
*/ */
#define read_sysreg_s(r) ({ \ #define read_sysreg_s(r) ({ \
u64 __val; \ u64 __val; \
asm volatile("mrs_s %0, " __stringify(r) : "=r" (__val)); \ asm volatile(__mrs_s("%0", r) : "=r" (__val)); \
__val; \ __val; \
}) })
#define write_sysreg_s(v, r) do { \ #define write_sysreg_s(v, r) do { \
u64 __val = (u64)(v); \ u64 __val = (u64)(v); \
asm volatile("msr_s " __stringify(r) ", %x0" : : "rZ" (__val)); \ asm volatile(__msr_s(r, "%x0") : : "rZ" (__val)); \
} while (0) } while (0)
/* /*

View File

@ -41,7 +41,6 @@ void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
int sig, int code, const char *name); int sig, int code, const char *name);
struct mm_struct; struct mm_struct;
extern void show_pte(unsigned long addr);
extern void __show_regs(struct pt_regs *); extern void __show_regs(struct pt_regs *);
extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);

View File

@ -63,7 +63,10 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
unsigned long addr) unsigned long addr)
{ {
tlb_remove_table(tlb, virt_to_page(pmdp)); struct page *page = virt_to_page(pmdp);
pgtable_pmd_page_dtor(page);
tlb_remove_table(tlb, page);
} }
#endif #endif

View File

@ -38,6 +38,7 @@ struct vdso_data {
__u32 tz_minuteswest; /* Whacky timezone stuff */ __u32 tz_minuteswest; /* Whacky timezone stuff */
__u32 tz_dsttime; __u32 tz_dsttime;
__u32 use_syscall; __u32 use_syscall;
__u32 hrtimer_res;
}; };
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 /* SPDX-License-Identifier: GPL-2.0 */
// Copyright (C) 2017 Arm Ltd. // Copyright (C) 2017 Arm Ltd.
#ifndef __ASM_VMAP_STACK_H #ifndef __ASM_VMAP_STACK_H
#define __ASM_VMAP_STACK_H #define __ASM_VMAP_STACK_H

View File

@ -18,7 +18,7 @@
#define _UAPI__ASM_HWCAP_H #define _UAPI__ASM_HWCAP_H
/* /*
* HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP * HWCAP flags - for AT_HWCAP
*/ */
#define HWCAP_FP (1 << 0) #define HWCAP_FP (1 << 0)
#define HWCAP_ASIMD (1 << 1) #define HWCAP_ASIMD (1 << 1)
@ -53,4 +53,15 @@
#define HWCAP_PACA (1 << 30) #define HWCAP_PACA (1 << 30)
#define HWCAP_PACG (1UL << 31) #define HWCAP_PACG (1UL << 31)
/*
* HWCAP2 flags - for AT_HWCAP2
*/
#define HWCAP2_DCPODP (1 << 0)
#define HWCAP2_SVE2 (1 << 1)
#define HWCAP2_SVEAES (1 << 2)
#define HWCAP2_SVEPMULL (1 << 3)
#define HWCAP2_SVEBITPERM (1 << 4)
#define HWCAP2_SVESHA3 (1 << 5)
#define HWCAP2_SVESM4 (1 << 6)
#endif /* _UAPI__ASM_HWCAP_H */ #endif /* _UAPI__ASM_HWCAP_H */

View File

@ -7,9 +7,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET)
AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_armv8_deprecated.o := -I$(src) CFLAGS_armv8_deprecated.o := -I$(src)
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_insn.o = -pg CFLAGS_REMOVE_insn.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_return_address.o = -pg CFLAGS_REMOVE_return_address.o = $(CC_FLAGS_FTRACE)
# Object file lists. # Object file lists.
obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
@ -27,8 +27,9 @@ OBJCOPYFLAGS := --prefix-symbols=__efistub_
$(obj)/%.stub.o: $(obj)/%.o FORCE $(obj)/%.stub.o: $(obj)/%.o FORCE
$(call if_changed,objcopy) $(call if_changed,objcopy)
obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ obj-$(CONFIG_COMPAT) += sys32.o signal32.o \
sys_compat.o sigreturn32.o sys_compat.o
obj-$(CONFIG_KUSER_HELPERS) += kuser32.o
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o

View File

@ -94,7 +94,7 @@ int main(void)
DEFINE(CLOCK_REALTIME, CLOCK_REALTIME); DEFINE(CLOCK_REALTIME, CLOCK_REALTIME);
DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC); DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC);
DEFINE(CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_RAW); DEFINE(CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_RAW);
DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); DEFINE(CLOCK_REALTIME_RES, offsetof(struct vdso_data, hrtimer_res));
DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE); DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE);
DEFINE(CLOCK_MONOTONIC_COARSE,CLOCK_MONOTONIC_COARSE); DEFINE(CLOCK_MONOTONIC_COARSE,CLOCK_MONOTONIC_COARSE);
DEFINE(CLOCK_COARSE_RES, LOW_RES_NSEC); DEFINE(CLOCK_COARSE_RES, LOW_RES_NSEC);

View File

@ -19,6 +19,7 @@
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/psci.h> #include <linux/psci.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/cpu.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/cpufeature.h> #include <asm/cpufeature.h>
@ -109,7 +110,6 @@ cpu_enable_trap_ctr_access(const struct arm64_cpu_capabilities *__unused)
atomic_t arm64_el2_vector_last_slot = ATOMIC_INIT(-1); atomic_t arm64_el2_vector_last_slot = ATOMIC_INIT(-1);
#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
@ -131,9 +131,9 @@ static void __copy_hyp_vect_bpi(int slot, const char *hyp_vecs_start,
__flush_icache_range((uintptr_t)dst, (uintptr_t)dst + SZ_2K); __flush_icache_range((uintptr_t)dst, (uintptr_t)dst + SZ_2K);
} }
static void __install_bp_hardening_cb(bp_hardening_cb_t fn, static void install_bp_hardening_cb(bp_hardening_cb_t fn,
const char *hyp_vecs_start, const char *hyp_vecs_start,
const char *hyp_vecs_end) const char *hyp_vecs_end)
{ {
static DEFINE_RAW_SPINLOCK(bp_lock); static DEFINE_RAW_SPINLOCK(bp_lock);
int cpu, slot = -1; int cpu, slot = -1;
@ -169,7 +169,7 @@ static void __install_bp_hardening_cb(bp_hardening_cb_t fn,
#define __smccc_workaround_1_smc_start NULL #define __smccc_workaround_1_smc_start NULL
#define __smccc_workaround_1_smc_end NULL #define __smccc_workaround_1_smc_end NULL
static void __install_bp_hardening_cb(bp_hardening_cb_t fn, static void install_bp_hardening_cb(bp_hardening_cb_t fn,
const char *hyp_vecs_start, const char *hyp_vecs_start,
const char *hyp_vecs_end) const char *hyp_vecs_end)
{ {
@ -177,23 +177,6 @@ static void __install_bp_hardening_cb(bp_hardening_cb_t fn,
} }
#endif /* CONFIG_KVM_INDIRECT_VECTORS */ #endif /* CONFIG_KVM_INDIRECT_VECTORS */
static void install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry,
bp_hardening_cb_t fn,
const char *hyp_vecs_start,
const char *hyp_vecs_end)
{
u64 pfr0;
if (!entry->matches(entry, SCOPE_LOCAL_CPU))
return;
pfr0 = read_cpuid(ID_AA64PFR0_EL1);
if (cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_CSV2_SHIFT))
return;
__install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end);
}
#include <uapi/linux/psci.h> #include <uapi/linux/psci.h>
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/psci.h> #include <linux/psci.h>
@ -220,60 +203,83 @@ static void qcom_link_stack_sanitization(void)
: "=&r" (tmp)); : "=&r" (tmp));
} }
static void static bool __nospectre_v2;
enable_smccc_arch_workaround_1(const struct arm64_cpu_capabilities *entry) static int __init parse_nospectre_v2(char *str)
{
__nospectre_v2 = true;
return 0;
}
early_param("nospectre_v2", parse_nospectre_v2);
/*
* -1: No workaround
* 0: No workaround required
* 1: Workaround installed
*/
static int detect_harden_bp_fw(void)
{ {
bp_hardening_cb_t cb; bp_hardening_cb_t cb;
void *smccc_start, *smccc_end; void *smccc_start, *smccc_end;
struct arm_smccc_res res; struct arm_smccc_res res;
u32 midr = read_cpuid_id(); u32 midr = read_cpuid_id();
if (!entry->matches(entry, SCOPE_LOCAL_CPU))
return;
if (psci_ops.smccc_version == SMCCC_VERSION_1_0) if (psci_ops.smccc_version == SMCCC_VERSION_1_0)
return; return -1;
switch (psci_ops.conduit) { switch (psci_ops.conduit) {
case PSCI_CONDUIT_HVC: case PSCI_CONDUIT_HVC:
arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
ARM_SMCCC_ARCH_WORKAROUND_1, &res); ARM_SMCCC_ARCH_WORKAROUND_1, &res);
if ((int)res.a0 < 0) switch ((int)res.a0) {
return; case 1:
cb = call_hvc_arch_workaround_1; /* Firmware says we're just fine */
/* This is a guest, no need to patch KVM vectors */ return 0;
smccc_start = NULL; case 0:
smccc_end = NULL; cb = call_hvc_arch_workaround_1;
/* This is a guest, no need to patch KVM vectors */
smccc_start = NULL;
smccc_end = NULL;
break;
default:
return -1;
}
break; break;
case PSCI_CONDUIT_SMC: case PSCI_CONDUIT_SMC:
arm_smccc_1_1_smc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, arm_smccc_1_1_smc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
ARM_SMCCC_ARCH_WORKAROUND_1, &res); ARM_SMCCC_ARCH_WORKAROUND_1, &res);
if ((int)res.a0 < 0) switch ((int)res.a0) {
return; case 1:
cb = call_smc_arch_workaround_1; /* Firmware says we're just fine */
smccc_start = __smccc_workaround_1_smc_start; return 0;
smccc_end = __smccc_workaround_1_smc_end; case 0:
cb = call_smc_arch_workaround_1;
smccc_start = __smccc_workaround_1_smc_start;
smccc_end = __smccc_workaround_1_smc_end;
break;
default:
return -1;
}
break; break;
default: default:
return; return -1;
} }
if (((midr & MIDR_CPU_MODEL_MASK) == MIDR_QCOM_FALKOR) || if (((midr & MIDR_CPU_MODEL_MASK) == MIDR_QCOM_FALKOR) ||
((midr & MIDR_CPU_MODEL_MASK) == MIDR_QCOM_FALKOR_V1)) ((midr & MIDR_CPU_MODEL_MASK) == MIDR_QCOM_FALKOR_V1))
cb = qcom_link_stack_sanitization; cb = qcom_link_stack_sanitization;
install_bp_hardening_cb(entry, cb, smccc_start, smccc_end); if (IS_ENABLED(CONFIG_HARDEN_BRANCH_PREDICTOR))
install_bp_hardening_cb(cb, smccc_start, smccc_end);
return; return 1;
} }
#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */
#ifdef CONFIG_ARM64_SSBD
DEFINE_PER_CPU_READ_MOSTLY(u64, arm64_ssbd_callback_required); DEFINE_PER_CPU_READ_MOSTLY(u64, arm64_ssbd_callback_required);
int ssbd_state __read_mostly = ARM64_SSBD_KERNEL; int ssbd_state __read_mostly = ARM64_SSBD_KERNEL;
static bool __ssb_safe = true;
static const struct ssbd_options { static const struct ssbd_options {
const char *str; const char *str;
@ -343,6 +349,11 @@ void __init arm64_enable_wa2_handling(struct alt_instr *alt,
void arm64_set_ssbd_mitigation(bool state) void arm64_set_ssbd_mitigation(bool state)
{ {
if (!IS_ENABLED(CONFIG_ARM64_SSBD)) {
pr_info_once("SSBD disabled by kernel configuration\n");
return;
}
if (this_cpu_has_cap(ARM64_SSBS)) { if (this_cpu_has_cap(ARM64_SSBS)) {
if (state) if (state)
asm volatile(SET_PSTATE_SSBS(0)); asm volatile(SET_PSTATE_SSBS(0));
@ -372,16 +383,28 @@ static bool has_ssbd_mitigation(const struct arm64_cpu_capabilities *entry,
struct arm_smccc_res res; struct arm_smccc_res res;
bool required = true; bool required = true;
s32 val; s32 val;
bool this_cpu_safe = false;
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible()); WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
if (cpu_mitigations_off())
ssbd_state = ARM64_SSBD_FORCE_DISABLE;
/* delay setting __ssb_safe until we get a firmware response */
if (is_midr_in_range_list(read_cpuid_id(), entry->midr_range_list))
this_cpu_safe = true;
if (this_cpu_has_cap(ARM64_SSBS)) { if (this_cpu_has_cap(ARM64_SSBS)) {
if (!this_cpu_safe)
__ssb_safe = false;
required = false; required = false;
goto out_printmsg; goto out_printmsg;
} }
if (psci_ops.smccc_version == SMCCC_VERSION_1_0) { if (psci_ops.smccc_version == SMCCC_VERSION_1_0) {
ssbd_state = ARM64_SSBD_UNKNOWN; ssbd_state = ARM64_SSBD_UNKNOWN;
if (!this_cpu_safe)
__ssb_safe = false;
return false; return false;
} }
@ -398,6 +421,8 @@ static bool has_ssbd_mitigation(const struct arm64_cpu_capabilities *entry,
default: default:
ssbd_state = ARM64_SSBD_UNKNOWN; ssbd_state = ARM64_SSBD_UNKNOWN;
if (!this_cpu_safe)
__ssb_safe = false;
return false; return false;
} }
@ -406,14 +431,18 @@ static bool has_ssbd_mitigation(const struct arm64_cpu_capabilities *entry,
switch (val) { switch (val) {
case SMCCC_RET_NOT_SUPPORTED: case SMCCC_RET_NOT_SUPPORTED:
ssbd_state = ARM64_SSBD_UNKNOWN; ssbd_state = ARM64_SSBD_UNKNOWN;
if (!this_cpu_safe)
__ssb_safe = false;
return false; return false;
/* machines with mixed mitigation requirements must not return this */
case SMCCC_RET_NOT_REQUIRED: case SMCCC_RET_NOT_REQUIRED:
pr_info_once("%s mitigation not required\n", entry->desc); pr_info_once("%s mitigation not required\n", entry->desc);
ssbd_state = ARM64_SSBD_MITIGATED; ssbd_state = ARM64_SSBD_MITIGATED;
return false; return false;
case SMCCC_RET_SUCCESS: case SMCCC_RET_SUCCESS:
__ssb_safe = false;
required = true; required = true;
break; break;
@ -423,6 +452,8 @@ static bool has_ssbd_mitigation(const struct arm64_cpu_capabilities *entry,
default: default:
WARN_ON(1); WARN_ON(1);
if (!this_cpu_safe)
__ssb_safe = false;
return false; return false;
} }
@ -462,7 +493,14 @@ static bool has_ssbd_mitigation(const struct arm64_cpu_capabilities *entry,
return required; return required;
} }
#endif /* CONFIG_ARM64_SSBD */
/* known invulnerable cores */
static const struct midr_range arm64_ssb_cpus[] = {
MIDR_ALL_VERSIONS(MIDR_CORTEX_A35),
MIDR_ALL_VERSIONS(MIDR_CORTEX_A53),
MIDR_ALL_VERSIONS(MIDR_CORTEX_A55),
{},
};
static void __maybe_unused static void __maybe_unused
cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused) cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused)
@ -507,26 +545,67 @@ cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused)
.type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM, \ .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM, \
CAP_MIDR_RANGE_LIST(midr_list) CAP_MIDR_RANGE_LIST(midr_list)
#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR /* Track overall mitigation state. We are only mitigated if all cores are ok */
static bool __hardenbp_enab = true;
static bool __spectrev2_safe = true;
/* /*
* List of CPUs where we need to issue a psci call to * List of CPUs that do not need any Spectre-v2 mitigation at all.
* harden the branch predictor.
*/ */
static const struct midr_range arm64_bp_harden_smccc_cpus[] = { static const struct midr_range spectre_v2_safe_list[] = {
MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), MIDR_ALL_VERSIONS(MIDR_CORTEX_A35),
MIDR_ALL_VERSIONS(MIDR_CORTEX_A72), MIDR_ALL_VERSIONS(MIDR_CORTEX_A53),
MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), MIDR_ALL_VERSIONS(MIDR_CORTEX_A55),
MIDR_ALL_VERSIONS(MIDR_CORTEX_A75), { /* sentinel */ }
MIDR_ALL_VERSIONS(MIDR_BRCM_VULCAN),
MIDR_ALL_VERSIONS(MIDR_CAVIUM_THUNDERX2),
MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR_V1),
MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR),
MIDR_ALL_VERSIONS(MIDR_NVIDIA_DENVER),
{},
}; };
#endif /*
* Track overall bp hardening for all heterogeneous cores in the machine.
* We are only considered "safe" if all booted cores are known safe.
*/
static bool __maybe_unused
check_branch_predictor(const struct arm64_cpu_capabilities *entry, int scope)
{
int need_wa;
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
/* If the CPU has CSV2 set, we're safe */
if (cpuid_feature_extract_unsigned_field(read_cpuid(ID_AA64PFR0_EL1),
ID_AA64PFR0_CSV2_SHIFT))
return false;
/* Alternatively, we have a list of unaffected CPUs */
if (is_midr_in_range_list(read_cpuid_id(), spectre_v2_safe_list))
return false;
/* Fallback to firmware detection */
need_wa = detect_harden_bp_fw();
if (!need_wa)
return false;
__spectrev2_safe = false;
if (!IS_ENABLED(CONFIG_HARDEN_BRANCH_PREDICTOR)) {
pr_warn_once("spectrev2 mitigation disabled by kernel configuration\n");
__hardenbp_enab = false;
return false;
}
/* forced off */
if (__nospectre_v2 || cpu_mitigations_off()) {
pr_info_once("spectrev2 mitigation disabled by command line option\n");
__hardenbp_enab = false;
return false;
}
if (need_wa < 0) {
pr_warn_once("ARM_SMCCC_ARCH_WORKAROUND_1 missing from firmware\n");
__hardenbp_enab = false;
}
return (need_wa > 0);
}
#ifdef CONFIG_HARDEN_EL2_VECTORS #ifdef CONFIG_HARDEN_EL2_VECTORS
@ -603,6 +682,16 @@ static const struct midr_range workaround_clean_cache[] = {
}; };
#endif #endif
#ifdef CONFIG_ARM64_ERRATUM_1188873
static const struct midr_range erratum_1188873_list[] = {
/* Cortex-A76 r0p0 to r2p0 */
MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 2, 0),
/* Neoverse-N1 r0p0 to r2p0 */
MIDR_RANGE(MIDR_NEOVERSE_N1, 0, 0, 2, 0),
{},
};
#endif
const struct arm64_cpu_capabilities arm64_errata[] = { const struct arm64_cpu_capabilities arm64_errata[] = {
#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE #ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
{ {
@ -701,13 +790,11 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
ERRATA_MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), ERRATA_MIDR_ALL_VERSIONS(MIDR_CORTEX_A73),
}, },
#endif #endif
#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR
{ {
.capability = ARM64_HARDEN_BRANCH_PREDICTOR, .capability = ARM64_HARDEN_BRANCH_PREDICTOR,
.cpu_enable = enable_smccc_arch_workaround_1, .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
ERRATA_MIDR_RANGE_LIST(arm64_bp_harden_smccc_cpus), .matches = check_branch_predictor,
}, },
#endif
#ifdef CONFIG_HARDEN_EL2_VECTORS #ifdef CONFIG_HARDEN_EL2_VECTORS
{ {
.desc = "EL2 vector hardening", .desc = "EL2 vector hardening",
@ -715,20 +802,18 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
ERRATA_MIDR_RANGE_LIST(arm64_harden_el2_vectors), ERRATA_MIDR_RANGE_LIST(arm64_harden_el2_vectors),
}, },
#endif #endif
#ifdef CONFIG_ARM64_SSBD
{ {
.desc = "Speculative Store Bypass Disable", .desc = "Speculative Store Bypass Disable",
.capability = ARM64_SSBD, .capability = ARM64_SSBD,
.type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM, .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
.matches = has_ssbd_mitigation, .matches = has_ssbd_mitigation,
.midr_range_list = arm64_ssb_cpus,
}, },
#endif
#ifdef CONFIG_ARM64_ERRATUM_1188873 #ifdef CONFIG_ARM64_ERRATUM_1188873
{ {
/* Cortex-A76 r0p0 to r2p0 */
.desc = "ARM erratum 1188873", .desc = "ARM erratum 1188873",
.capability = ARM64_WORKAROUND_1188873, .capability = ARM64_WORKAROUND_1188873,
ERRATA_MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 2, 0), ERRATA_MIDR_RANGE_LIST(erratum_1188873_list),
}, },
#endif #endif
#ifdef CONFIG_ARM64_ERRATUM_1165522 #ifdef CONFIG_ARM64_ERRATUM_1165522
@ -742,3 +827,38 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
{ {
} }
}; };
ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "Mitigation: __user pointer sanitization\n");
}
ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr,
char *buf)
{
if (__spectrev2_safe)
return sprintf(buf, "Not affected\n");
if (__hardenbp_enab)
return sprintf(buf, "Mitigation: Branch predictor hardening\n");
return sprintf(buf, "Vulnerable\n");
}
ssize_t cpu_show_spec_store_bypass(struct device *dev,
struct device_attribute *attr, char *buf)
{
if (__ssb_safe)
return sprintf(buf, "Not affected\n");
switch (ssbd_state) {
case ARM64_SSBD_KERNEL:
case ARM64_SSBD_FORCE_ENABLE:
if (IS_ENABLED(CONFIG_ARM64_SSBD))
return sprintf(buf,
"Mitigation: Speculative Store Bypass disabled via prctl\n");
}
return sprintf(buf, "Vulnerable\n");
}

View File

@ -85,6 +85,7 @@ static const char *__init cpu_read_enable_method(int cpu)
pr_err("%pOF: missing enable-method property\n", pr_err("%pOF: missing enable-method property\n",
dn); dn);
} }
of_node_put(dn);
} else { } else {
enable_method = acpi_get_enable_method(cpu); enable_method = acpi_get_enable_method(cpu);
if (!enable_method) { if (!enable_method) {

View File

@ -25,6 +25,7 @@
#include <linux/stop_machine.h> #include <linux/stop_machine.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/cpu.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/cpufeature.h> #include <asm/cpufeature.h>
#include <asm/cpu_ops.h> #include <asm/cpu_ops.h>
@ -35,8 +36,8 @@
#include <asm/traps.h> #include <asm/traps.h>
#include <asm/virt.h> #include <asm/virt.h>
unsigned long elf_hwcap __read_mostly; /* Kernel representation of AT_HWCAP and AT_HWCAP2 */
EXPORT_SYMBOL_GPL(elf_hwcap); static unsigned long elf_hwcap __read_mostly;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#define COMPAT_ELF_HWCAP_DEFAULT \ #define COMPAT_ELF_HWCAP_DEFAULT \
@ -184,6 +185,15 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr1[] = {
ARM64_FTR_END, ARM64_FTR_END,
}; };
static const struct arm64_ftr_bits ftr_id_aa64zfr0[] = {
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ZFR0_SM4_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ZFR0_SHA3_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ZFR0_BITPERM_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ZFR0_AES_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ZFR0_SVEVER_SHIFT, 4, 0),
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = { static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = {
S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_TGRAN4_SHIFT, 4, ID_AA64MMFR0_TGRAN4_NI), S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_TGRAN4_SHIFT, 4, ID_AA64MMFR0_TGRAN4_NI),
S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_TGRAN64_SHIFT, 4, ID_AA64MMFR0_TGRAN64_NI), S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_TGRAN64_SHIFT, 4, ID_AA64MMFR0_TGRAN64_NI),
@ -392,7 +402,7 @@ static const struct __ftr_reg_entry {
/* Op1 = 0, CRn = 0, CRm = 4 */ /* Op1 = 0, CRn = 0, CRm = 4 */
ARM64_FTR_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0), ARM64_FTR_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0),
ARM64_FTR_REG(SYS_ID_AA64PFR1_EL1, ftr_id_aa64pfr1), ARM64_FTR_REG(SYS_ID_AA64PFR1_EL1, ftr_id_aa64pfr1),
ARM64_FTR_REG(SYS_ID_AA64ZFR0_EL1, ftr_raz), ARM64_FTR_REG(SYS_ID_AA64ZFR0_EL1, ftr_id_aa64zfr0),
/* Op1 = 0, CRn = 0, CRm = 5 */ /* Op1 = 0, CRn = 0, CRm = 5 */
ARM64_FTR_REG(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0), ARM64_FTR_REG(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0),
@ -947,7 +957,7 @@ has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
return has_cpuid_feature(entry, scope); return has_cpuid_feature(entry, scope);
} }
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 static bool __meltdown_safe = true;
static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */ static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */
static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
@ -966,7 +976,17 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), MIDR_ALL_VERSIONS(MIDR_HISI_TSV110),
{ /* sentinel */ } { /* sentinel */ }
}; };
char const *str = "command line option"; char const *str = "kpti command line option";
bool meltdown_safe;
meltdown_safe = is_midr_in_range_list(read_cpuid_id(), kpti_safe_list);
/* Defer to CPU feature registers */
if (has_cpuid_feature(entry, scope))
meltdown_safe = true;
if (!meltdown_safe)
__meltdown_safe = false;
/* /*
* For reasons that aren't entirely clear, enabling KPTI on Cavium * For reasons that aren't entirely clear, enabling KPTI on Cavium
@ -978,6 +998,24 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
__kpti_forced = -1; __kpti_forced = -1;
} }
/* Useful for KASLR robustness */
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_offset() > 0) {
if (!__kpti_forced) {
str = "KASLR";
__kpti_forced = 1;
}
}
if (cpu_mitigations_off() && !__kpti_forced) {
str = "mitigations=off";
__kpti_forced = -1;
}
if (!IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0)) {
pr_info_once("kernel page table isolation disabled by kernel configuration\n");
return false;
}
/* Forced? */ /* Forced? */
if (__kpti_forced) { if (__kpti_forced) {
pr_info_once("kernel page table isolation forced %s by %s\n", pr_info_once("kernel page table isolation forced %s by %s\n",
@ -985,18 +1023,10 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
return __kpti_forced > 0; return __kpti_forced > 0;
} }
/* Useful for KASLR robustness */ return !meltdown_safe;
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE))
return kaslr_offset() > 0;
/* Don't force KPTI for CPUs that are not vulnerable */
if (is_midr_in_range_list(read_cpuid_id(), kpti_safe_list))
return false;
/* Defer to CPU feature registers */
return !has_cpuid_feature(entry, scope);
} }
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
static void static void
kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused) kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
{ {
@ -1026,6 +1056,12 @@ kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
return; return;
} }
#else
static void
kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
{
}
#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */
static int __init parse_kpti(char *str) static int __init parse_kpti(char *str)
{ {
@ -1039,7 +1075,6 @@ static int __init parse_kpti(char *str)
return 0; return 0;
} }
early_param("kpti", parse_kpti); early_param("kpti", parse_kpti);
#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */
#ifdef CONFIG_ARM64_HW_AFDBM #ifdef CONFIG_ARM64_HW_AFDBM
static inline void __cpu_enable_hw_dbm(void) static inline void __cpu_enable_hw_dbm(void)
@ -1306,7 +1341,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.field_pos = ID_AA64PFR0_EL0_SHIFT, .field_pos = ID_AA64PFR0_EL0_SHIFT,
.min_field_value = ID_AA64PFR0_EL0_32BIT_64BIT, .min_field_value = ID_AA64PFR0_EL0_32BIT_64BIT,
}, },
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
{ {
.desc = "Kernel page table isolation (KPTI)", .desc = "Kernel page table isolation (KPTI)",
.capability = ARM64_UNMAP_KERNEL_AT_EL0, .capability = ARM64_UNMAP_KERNEL_AT_EL0,
@ -1322,7 +1356,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.matches = unmap_kernel_at_el0, .matches = unmap_kernel_at_el0,
.cpu_enable = kpti_install_ng_mappings, .cpu_enable = kpti_install_ng_mappings,
}, },
#endif
{ {
/* FP/SIMD is not implemented */ /* FP/SIMD is not implemented */
.capability = ARM64_HAS_NO_FPSIMD, .capability = ARM64_HAS_NO_FPSIMD,
@ -1340,6 +1373,16 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.field_pos = ID_AA64ISAR1_DPB_SHIFT, .field_pos = ID_AA64ISAR1_DPB_SHIFT,
.min_field_value = 1, .min_field_value = 1,
}, },
{
.desc = "Data cache clean to Point of Deep Persistence",
.capability = ARM64_HAS_DCPODP,
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
.matches = has_cpuid_feature,
.sys_reg = SYS_ID_AA64ISAR1_EL1,
.sign = FTR_UNSIGNED,
.field_pos = ID_AA64ISAR1_DPB_SHIFT,
.min_field_value = 2,
},
#endif #endif
#ifdef CONFIG_ARM64_SVE #ifdef CONFIG_ARM64_SVE
{ {
@ -1571,39 +1614,46 @@ static const struct arm64_cpu_capabilities ptr_auth_hwcap_gen_matches[] = {
#endif #endif
static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_AES_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, HWCAP_PMULL), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_AES_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, KERNEL_HWCAP_PMULL),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_AES_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_AES), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_AES_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_AES),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA1_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_SHA1), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA1_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_SHA1),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA2_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_SHA2), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA2_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_SHA2),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA2_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, HWCAP_SHA512), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA2_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, KERNEL_HWCAP_SHA512),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_CRC32_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_CRC32), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_CRC32_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_CRC32),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_ATOMICS_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, HWCAP_ATOMICS), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_ATOMICS_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, KERNEL_HWCAP_ATOMICS),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_RDM_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_ASIMDRDM), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_RDM_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ASIMDRDM),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA3_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_SHA3), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA3_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_SHA3),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SM3_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_SM3), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SM3_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_SM3),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SM4_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_SM4), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SM4_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_SM4),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_DP_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_ASIMDDP), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_DP_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ASIMDDP),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_FHM_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_ASIMDFHM), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_FHM_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ASIMDFHM),
HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_TS_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_FLAGM), HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_TS_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_FLAGM),
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_FP_SHIFT, FTR_SIGNED, 0, CAP_HWCAP, HWCAP_FP), HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_FP_SHIFT, FTR_SIGNED, 0, CAP_HWCAP, KERNEL_HWCAP_FP),
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_FP_SHIFT, FTR_SIGNED, 1, CAP_HWCAP, HWCAP_FPHP), HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_FP_SHIFT, FTR_SIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_FPHP),
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_ASIMD_SHIFT, FTR_SIGNED, 0, CAP_HWCAP, HWCAP_ASIMD), HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_ASIMD_SHIFT, FTR_SIGNED, 0, CAP_HWCAP, KERNEL_HWCAP_ASIMD),
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_ASIMD_SHIFT, FTR_SIGNED, 1, CAP_HWCAP, HWCAP_ASIMDHP), HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_ASIMD_SHIFT, FTR_SIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ASIMDHP),
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_DIT_SHIFT, FTR_SIGNED, 1, CAP_HWCAP, HWCAP_DIT), HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_DIT_SHIFT, FTR_SIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_DIT),
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_DPB_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_DCPOP), HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_DPB_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_DCPOP),
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_JSCVT_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_JSCVT), HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_DPB_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, KERNEL_HWCAP_DCPODP),
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_FCMA_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_FCMA), HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_JSCVT_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_JSCVT),
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_LRCPC_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_LRCPC), HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_FCMA_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_FCMA),
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_LRCPC_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, HWCAP_ILRCPC), HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_LRCPC_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_LRCPC),
HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_SB_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_SB), HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_LRCPC_SHIFT, FTR_UNSIGNED, 2, CAP_HWCAP, KERNEL_HWCAP_ILRCPC),
HWCAP_CAP(SYS_ID_AA64MMFR2_EL1, ID_AA64MMFR2_AT_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, HWCAP_USCAT), HWCAP_CAP(SYS_ID_AA64ISAR1_EL1, ID_AA64ISAR1_SB_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_SB),
HWCAP_CAP(SYS_ID_AA64MMFR2_EL1, ID_AA64MMFR2_AT_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_USCAT),
#ifdef CONFIG_ARM64_SVE #ifdef CONFIG_ARM64_SVE
HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_SVE_SHIFT, FTR_UNSIGNED, ID_AA64PFR0_SVE, CAP_HWCAP, HWCAP_SVE), HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_SVE_SHIFT, FTR_UNSIGNED, ID_AA64PFR0_SVE, CAP_HWCAP, KERNEL_HWCAP_SVE),
HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_SVEVER_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_SVEVER_SVE2, CAP_HWCAP, KERNEL_HWCAP_SVE2),
HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_AES_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_AES, CAP_HWCAP, KERNEL_HWCAP_SVEAES),
HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_AES_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_AES_PMULL, CAP_HWCAP, KERNEL_HWCAP_SVEPMULL),
HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_BITPERM_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_BITPERM, CAP_HWCAP, KERNEL_HWCAP_SVEBITPERM),
HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_SHA3_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_SHA3, CAP_HWCAP, KERNEL_HWCAP_SVESHA3),
HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_SM4_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_SM4, CAP_HWCAP, KERNEL_HWCAP_SVESM4),
#endif #endif
HWCAP_CAP(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_SSBS_SHIFT, FTR_UNSIGNED, ID_AA64PFR1_SSBS_PSTATE_INSNS, CAP_HWCAP, HWCAP_SSBS), HWCAP_CAP(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_SSBS_SHIFT, FTR_UNSIGNED, ID_AA64PFR1_SSBS_PSTATE_INSNS, CAP_HWCAP, KERNEL_HWCAP_SSBS),
#ifdef CONFIG_ARM64_PTR_AUTH #ifdef CONFIG_ARM64_PTR_AUTH
HWCAP_MULTI_CAP(ptr_auth_hwcap_addr_matches, CAP_HWCAP, HWCAP_PACA), HWCAP_MULTI_CAP(ptr_auth_hwcap_addr_matches, CAP_HWCAP, KERNEL_HWCAP_PACA),
HWCAP_MULTI_CAP(ptr_auth_hwcap_gen_matches, CAP_HWCAP, HWCAP_PACG), HWCAP_MULTI_CAP(ptr_auth_hwcap_gen_matches, CAP_HWCAP, KERNEL_HWCAP_PACG),
#endif #endif
{}, {},
}; };
@ -1623,7 +1673,7 @@ static void __init cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap)
{ {
switch (cap->hwcap_type) { switch (cap->hwcap_type) {
case CAP_HWCAP: case CAP_HWCAP:
elf_hwcap |= cap->hwcap; cpu_set_feature(cap->hwcap);
break; break;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
case CAP_COMPAT_HWCAP: case CAP_COMPAT_HWCAP:
@ -1646,7 +1696,7 @@ static bool cpus_have_elf_hwcap(const struct arm64_cpu_capabilities *cap)
switch (cap->hwcap_type) { switch (cap->hwcap_type) {
case CAP_HWCAP: case CAP_HWCAP:
rc = (elf_hwcap & cap->hwcap) != 0; rc = cpu_have_feature(cap->hwcap);
break; break;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
case CAP_COMPAT_HWCAP: case CAP_COMPAT_HWCAP:
@ -1667,7 +1717,7 @@ static bool cpus_have_elf_hwcap(const struct arm64_cpu_capabilities *cap)
static void __init setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps) static void __init setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)
{ {
/* We support emulation of accesses to CPU ID feature registers */ /* We support emulation of accesses to CPU ID feature registers */
elf_hwcap |= HWCAP_CPUID; cpu_set_named_feature(CPUID);
for (; hwcaps->matches; hwcaps++) for (; hwcaps->matches; hwcaps++)
if (hwcaps->matches(hwcaps, cpucap_default_scope(hwcaps))) if (hwcaps->matches(hwcaps, cpucap_default_scope(hwcaps)))
cap_set_elf_hwcap(hwcaps); cap_set_elf_hwcap(hwcaps);
@ -1947,6 +1997,35 @@ bool this_cpu_has_cap(unsigned int n)
return false; return false;
} }
void cpu_set_feature(unsigned int num)
{
WARN_ON(num >= MAX_CPU_FEATURES);
elf_hwcap |= BIT(num);
}
EXPORT_SYMBOL_GPL(cpu_set_feature);
bool cpu_have_feature(unsigned int num)
{
WARN_ON(num >= MAX_CPU_FEATURES);
return elf_hwcap & BIT(num);
}
EXPORT_SYMBOL_GPL(cpu_have_feature);
unsigned long cpu_get_elf_hwcap(void)
{
/*
* We currently only populate the first 32 bits of AT_HWCAP. Please
* note that for userspace compatibility we guarantee that bits 62
* and 63 will always be returned as 0.
*/
return lower_32_bits(elf_hwcap);
}
unsigned long cpu_get_elf_hwcap2(void)
{
return upper_32_bits(elf_hwcap);
}
static void __init setup_system_capabilities(void) static void __init setup_system_capabilities(void)
{ {
/* /*
@ -2101,3 +2180,15 @@ static int __init enable_mrs_emulation(void)
} }
core_initcall(enable_mrs_emulation); core_initcall(enable_mrs_emulation);
ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
char *buf)
{
if (__meltdown_safe)
return sprintf(buf, "Not affected\n");
if (arm64_kernel_unmapped_at_el0())
return sprintf(buf, "Mitigation: PTI\n");
return sprintf(buf, "Vulnerable\n");
}

View File

@ -85,6 +85,13 @@ static const char *const hwcap_str[] = {
"sb", "sb",
"paca", "paca",
"pacg", "pacg",
"dcpodp",
"sve2",
"sveaes",
"svepmull",
"svebitperm",
"svesha3",
"svesm4",
NULL NULL
}; };
@ -167,7 +174,7 @@ static int c_show(struct seq_file *m, void *v)
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */
} else { } else {
for (j = 0; hwcap_str[j]; j++) for (j = 0; hwcap_str[j]; j++)
if (elf_hwcap & (1 << j)) if (cpu_have_feature(j))
seq_printf(m, " %s", hwcap_str[j]); seq_printf(m, " %s", hwcap_str[j]);
} }
seq_puts(m, "\n"); seq_puts(m, "\n");

View File

@ -135,6 +135,7 @@ NOKPROBE_SYMBOL(disable_debug_monitors);
*/ */
static int clear_os_lock(unsigned int cpu) static int clear_os_lock(unsigned int cpu)
{ {
write_sysreg(0, osdlr_el1);
write_sysreg(0, oslar_el1); write_sysreg(0, oslar_el1);
isb(); isb();
return 0; return 0;
@ -163,25 +164,46 @@ static void clear_regs_spsr_ss(struct pt_regs *regs)
} }
NOKPROBE_SYMBOL(clear_regs_spsr_ss); NOKPROBE_SYMBOL(clear_regs_spsr_ss);
/* EL1 Single Step Handler hooks */ static DEFINE_SPINLOCK(debug_hook_lock);
static LIST_HEAD(step_hook); static LIST_HEAD(user_step_hook);
static DEFINE_SPINLOCK(step_hook_lock); static LIST_HEAD(kernel_step_hook);
void register_step_hook(struct step_hook *hook) static void register_debug_hook(struct list_head *node, struct list_head *list)
{ {
spin_lock(&step_hook_lock); spin_lock(&debug_hook_lock);
list_add_rcu(&hook->node, &step_hook); list_add_rcu(node, list);
spin_unlock(&step_hook_lock); spin_unlock(&debug_hook_lock);
} }
void unregister_step_hook(struct step_hook *hook) static void unregister_debug_hook(struct list_head *node)
{ {
spin_lock(&step_hook_lock); spin_lock(&debug_hook_lock);
list_del_rcu(&hook->node); list_del_rcu(node);
spin_unlock(&step_hook_lock); spin_unlock(&debug_hook_lock);
synchronize_rcu(); synchronize_rcu();
} }
void register_user_step_hook(struct step_hook *hook)
{
register_debug_hook(&hook->node, &user_step_hook);
}
void unregister_user_step_hook(struct step_hook *hook)
{
unregister_debug_hook(&hook->node);
}
void register_kernel_step_hook(struct step_hook *hook)
{
register_debug_hook(&hook->node, &kernel_step_hook);
}
void unregister_kernel_step_hook(struct step_hook *hook)
{
unregister_debug_hook(&hook->node);
}
/* /*
* Call registered single step handlers * Call registered single step handlers
* There is no Syndrome info to check for determining the handler. * There is no Syndrome info to check for determining the handler.
@ -191,11 +213,14 @@ void unregister_step_hook(struct step_hook *hook)
static int call_step_hook(struct pt_regs *regs, unsigned int esr) static int call_step_hook(struct pt_regs *regs, unsigned int esr)
{ {
struct step_hook *hook; struct step_hook *hook;
struct list_head *list;
int retval = DBG_HOOK_ERROR; int retval = DBG_HOOK_ERROR;
list = user_mode(regs) ? &user_step_hook : &kernel_step_hook;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(hook, &step_hook, node) { list_for_each_entry_rcu(hook, list, node) {
retval = hook->fn(regs, esr); retval = hook->fn(regs, esr);
if (retval == DBG_HOOK_HANDLED) if (retval == DBG_HOOK_HANDLED)
break; break;
@ -222,7 +247,7 @@ static void send_user_sigtrap(int si_code)
"User debug trap"); "User debug trap");
} }
static int single_step_handler(unsigned long addr, unsigned int esr, static int single_step_handler(unsigned long unused, unsigned int esr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
bool handler_found = false; bool handler_found = false;
@ -234,10 +259,6 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
if (!reinstall_suspended_bps(regs)) if (!reinstall_suspended_bps(regs))
return 0; return 0;
#ifdef CONFIG_KPROBES
if (kprobe_single_step_handler(regs, esr) == DBG_HOOK_HANDLED)
handler_found = true;
#endif
if (!handler_found && call_step_hook(regs, esr) == DBG_HOOK_HANDLED) if (!handler_found && call_step_hook(regs, esr) == DBG_HOOK_HANDLED)
handler_found = true; handler_found = true;
@ -264,61 +285,59 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
} }
NOKPROBE_SYMBOL(single_step_handler); NOKPROBE_SYMBOL(single_step_handler);
/* static LIST_HEAD(user_break_hook);
* Breakpoint handler is re-entrant as another breakpoint can static LIST_HEAD(kernel_break_hook);
* hit within breakpoint handler, especically in kprobes.
* Use reader/writer locks instead of plain spinlock.
*/
static LIST_HEAD(break_hook);
static DEFINE_SPINLOCK(break_hook_lock);
void register_break_hook(struct break_hook *hook) void register_user_break_hook(struct break_hook *hook)
{ {
spin_lock(&break_hook_lock); register_debug_hook(&hook->node, &user_break_hook);
list_add_rcu(&hook->node, &break_hook);
spin_unlock(&break_hook_lock);
} }
void unregister_break_hook(struct break_hook *hook) void unregister_user_break_hook(struct break_hook *hook)
{ {
spin_lock(&break_hook_lock); unregister_debug_hook(&hook->node);
list_del_rcu(&hook->node); }
spin_unlock(&break_hook_lock);
synchronize_rcu(); void register_kernel_break_hook(struct break_hook *hook)
{
register_debug_hook(&hook->node, &kernel_break_hook);
}
void unregister_kernel_break_hook(struct break_hook *hook)
{
unregister_debug_hook(&hook->node);
} }
static int call_break_hook(struct pt_regs *regs, unsigned int esr) static int call_break_hook(struct pt_regs *regs, unsigned int esr)
{ {
struct break_hook *hook; struct break_hook *hook;
struct list_head *list;
int (*fn)(struct pt_regs *regs, unsigned int esr) = NULL; int (*fn)(struct pt_regs *regs, unsigned int esr) = NULL;
list = user_mode(regs) ? &user_break_hook : &kernel_break_hook;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(hook, &break_hook, node) list_for_each_entry_rcu(hook, list, node) {
if ((esr & hook->esr_mask) == hook->esr_val) unsigned int comment = esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
if ((comment & ~hook->mask) == hook->imm)
fn = hook->fn; fn = hook->fn;
}
rcu_read_unlock(); rcu_read_unlock();
return fn ? fn(regs, esr) : DBG_HOOK_ERROR; return fn ? fn(regs, esr) : DBG_HOOK_ERROR;
} }
NOKPROBE_SYMBOL(call_break_hook); NOKPROBE_SYMBOL(call_break_hook);
static int brk_handler(unsigned long addr, unsigned int esr, static int brk_handler(unsigned long unused, unsigned int esr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
bool handler_found = false; if (call_break_hook(regs, esr) == DBG_HOOK_HANDLED)
return 0;
#ifdef CONFIG_KPROBES if (user_mode(regs)) {
if ((esr & BRK64_ESR_MASK) == BRK64_ESR_KPROBES) {
if (kprobe_breakpoint_handler(regs, esr) == DBG_HOOK_HANDLED)
handler_found = true;
}
#endif
if (!handler_found && call_break_hook(regs, esr) == DBG_HOOK_HANDLED)
handler_found = true;
if (!handler_found && user_mode(regs)) {
send_user_sigtrap(TRAP_BRKPT); send_user_sigtrap(TRAP_BRKPT);
} else if (!handler_found) { } else {
pr_warn("Unexpected kernel BRK exception at EL1\n"); pr_warn("Unexpected kernel BRK exception at EL1\n");
return -EFAULT; return -EFAULT;
} }

View File

@ -336,6 +336,21 @@ alternative_if ARM64_WORKAROUND_845719
alternative_else_nop_endif alternative_else_nop_endif
#endif #endif
3: 3:
#ifdef CONFIG_ARM64_ERRATUM_1188873
alternative_if_not ARM64_WORKAROUND_1188873
b 4f
alternative_else_nop_endif
/*
* if (x22.mode32 == cntkctl_el1.el0vcten)
* cntkctl_el1.el0vcten = ~cntkctl_el1.el0vcten
*/
mrs x1, cntkctl_el1
eon x0, x1, x22, lsr #3
tbz x0, #1, 4f
eor x1, x1, #2 // ARCH_TIMER_USR_VCT_ACCESS_EN
msr cntkctl_el1, x1
4:
#endif
apply_ssbd 0, x0, x1 apply_ssbd 0, x0, x1
.endif .endif
@ -362,11 +377,11 @@ alternative_else_nop_endif
.if \el == 0 .if \el == 0
alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0 alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0
bne 4f bne 5f
msr far_el1, x30 msr far_el1, x30
tramp_alias x30, tramp_exit_native tramp_alias x30, tramp_exit_native
br x30 br x30
4: 5:
tramp_alias x30, tramp_exit_compat tramp_alias x30, tramp_exit_compat
br x30 br x30
#endif #endif

View File

@ -1258,14 +1258,14 @@ static inline void fpsimd_hotplug_init(void) { }
*/ */
static int __init fpsimd_init(void) static int __init fpsimd_init(void)
{ {
if (elf_hwcap & HWCAP_FP) { if (cpu_have_named_feature(FP)) {
fpsimd_pm_init(); fpsimd_pm_init();
fpsimd_hotplug_init(); fpsimd_hotplug_init();
} else { } else {
pr_notice("Floating-point is not implemented\n"); pr_notice("Floating-point is not implemented\n");
} }
if (!(elf_hwcap & HWCAP_ASIMD)) if (!cpu_have_named_feature(ASIMD))
pr_notice("Advanced SIMD is not implemented\n"); pr_notice("Advanced SIMD is not implemented\n");
return sve_sysctl_init(); return sve_sysctl_init();

View File

@ -505,7 +505,7 @@ ENTRY(el2_setup)
* kernel is intended to run at EL2. * kernel is intended to run at EL2.
*/ */
mrs x2, id_aa64mmfr1_el1 mrs x2, id_aa64mmfr1_el1
ubfx x2, x2, #8, #4 ubfx x2, x2, #ID_AA64MMFR1_VHE_SHIFT, #4
#else #else
mov x2, xzr mov x2, xzr
#endif #endif
@ -538,7 +538,7 @@ set_hcr:
#ifdef CONFIG_ARM_GIC_V3 #ifdef CONFIG_ARM_GIC_V3
/* GICv3 system register access */ /* GICv3 system register access */
mrs x0, id_aa64pfr0_el1 mrs x0, id_aa64pfr0_el1
ubfx x0, x0, #24, #4 ubfx x0, x0, #ID_AA64PFR0_GIC_SHIFT, #4
cbz x0, 3f cbz x0, 3f
mrs_s x0, SYS_ICC_SRE_EL2 mrs_s x0, SYS_ICC_SRE_EL2
@ -564,8 +564,8 @@ set_hcr:
#endif #endif
/* EL2 debug */ /* EL2 debug */
mrs x1, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer mrs x1, id_aa64dfr0_el1
sbfx x0, x1, #8, #4 sbfx x0, x1, #ID_AA64DFR0_PMUVER_SHIFT, #4
cmp x0, #1 cmp x0, #1
b.lt 4f // Skip if no PMU present b.lt 4f // Skip if no PMU present
mrs x0, pmcr_el0 // Disable debug access traps mrs x0, pmcr_el0 // Disable debug access traps
@ -574,7 +574,7 @@ set_hcr:
csel x3, xzr, x0, lt // all PMU counters from EL1 csel x3, xzr, x0, lt // all PMU counters from EL1
/* Statistical profiling */ /* Statistical profiling */
ubfx x0, x1, #32, #4 // Check ID_AA64DFR0_EL1 PMSVer ubfx x0, x1, #ID_AA64DFR0_PMSVER_SHIFT, #4
cbz x0, 7f // Skip if SPE not present cbz x0, 7f // Skip if SPE not present
cbnz x2, 6f // VHE? cbnz x2, 6f // VHE?
mrs_s x4, SYS_PMBIDR_EL1 // If SPE available at EL2, mrs_s x4, SYS_PMBIDR_EL1 // If SPE available at EL2,
@ -684,7 +684,7 @@ ENTRY(__boot_cpu_mode)
* with MMU turned off. * with MMU turned off.
*/ */
ENTRY(__early_cpu_boot_status) ENTRY(__early_cpu_boot_status)
.long 0 .quad 0
.popsection .popsection

View File

@ -244,9 +244,6 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
static int kgdb_brk_fn(struct pt_regs *regs, unsigned int esr) static int kgdb_brk_fn(struct pt_regs *regs, unsigned int esr)
{ {
if (user_mode(regs))
return DBG_HOOK_ERROR;
kgdb_handle_exception(1, SIGTRAP, 0, regs); kgdb_handle_exception(1, SIGTRAP, 0, regs);
return DBG_HOOK_HANDLED; return DBG_HOOK_HANDLED;
} }
@ -254,9 +251,6 @@ NOKPROBE_SYMBOL(kgdb_brk_fn)
static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr) static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr)
{ {
if (user_mode(regs))
return DBG_HOOK_ERROR;
compiled_break = 1; compiled_break = 1;
kgdb_handle_exception(1, SIGTRAP, 0, regs); kgdb_handle_exception(1, SIGTRAP, 0, regs);
@ -266,7 +260,7 @@ NOKPROBE_SYMBOL(kgdb_compiled_brk_fn);
static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr) static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr)
{ {
if (user_mode(regs) || !kgdb_single_step) if (!kgdb_single_step)
return DBG_HOOK_ERROR; return DBG_HOOK_ERROR;
kgdb_handle_exception(1, SIGTRAP, 0, regs); kgdb_handle_exception(1, SIGTRAP, 0, regs);
@ -275,15 +269,13 @@ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr)
NOKPROBE_SYMBOL(kgdb_step_brk_fn); NOKPROBE_SYMBOL(kgdb_step_brk_fn);
static struct break_hook kgdb_brkpt_hook = { static struct break_hook kgdb_brkpt_hook = {
.esr_mask = 0xffffffff, .fn = kgdb_brk_fn,
.esr_val = (u32)ESR_ELx_VAL_BRK64(KGDB_DYN_DBG_BRK_IMM), .imm = KGDB_DYN_DBG_BRK_IMM,
.fn = kgdb_brk_fn
}; };
static struct break_hook kgdb_compiled_brkpt_hook = { static struct break_hook kgdb_compiled_brkpt_hook = {
.esr_mask = 0xffffffff, .fn = kgdb_compiled_brk_fn,
.esr_val = (u32)ESR_ELx_VAL_BRK64(KGDB_COMPILED_DBG_BRK_IMM), .imm = KGDB_COMPILED_DBG_BRK_IMM,
.fn = kgdb_compiled_brk_fn
}; };
static struct step_hook kgdb_step_hook = { static struct step_hook kgdb_step_hook = {
@ -332,9 +324,9 @@ int kgdb_arch_init(void)
if (ret != 0) if (ret != 0)
return ret; return ret;
register_break_hook(&kgdb_brkpt_hook); register_kernel_break_hook(&kgdb_brkpt_hook);
register_break_hook(&kgdb_compiled_brkpt_hook); register_kernel_break_hook(&kgdb_compiled_brkpt_hook);
register_step_hook(&kgdb_step_hook); register_kernel_step_hook(&kgdb_step_hook);
return 0; return 0;
} }
@ -345,9 +337,9 @@ int kgdb_arch_init(void)
*/ */
void kgdb_arch_exit(void) void kgdb_arch_exit(void)
{ {
unregister_break_hook(&kgdb_brkpt_hook); unregister_kernel_break_hook(&kgdb_brkpt_hook);
unregister_break_hook(&kgdb_compiled_brkpt_hook); unregister_kernel_break_hook(&kgdb_compiled_brkpt_hook);
unregister_step_hook(&kgdb_step_hook); unregister_kernel_step_hook(&kgdb_step_hook);
unregister_die_notifier(&kgdb_notifier); unregister_die_notifier(&kgdb_notifier);
} }

View File

@ -1,29 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Low-level user helpers placed in the vectors page for AArch32. * AArch32 user helpers.
* Based on the kuser helpers in arch/arm/kernel/entry-armv.S. * Based on the kuser helpers in arch/arm/kernel/entry-armv.S.
* *
* Copyright (C) 2005-2011 Nicolas Pitre <nico@fluxnic.net> * Copyright (C) 2005-2011 Nicolas Pitre <nico@fluxnic.net>
* Copyright (C) 2012 ARM Ltd. * Copyright (C) 2012-2018 ARM Ltd.
* *
* This program is free software; you can redistribute it and/or modify * The kuser helpers below are mapped at a fixed address by
* it under the terms of the GNU General Public License version 2 as * aarch32_setup_additional_pages() and are provided for compatibility
* published by the Free Software Foundation. * reasons with 32 bit (aarch32) applications that need them.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* AArch32 user helpers.
*
* Each segment is 32-byte aligned and will be moved to the top of the high
* vector page. New segments (if ever needed) must be added in front of
* existing ones. This mechanism should be used only for things that are
* really small and justified, and not be abused freely.
* *
* See Documentation/arm/kernel_user_helpers.txt for formal definitions. * See Documentation/arm/kernel_user_helpers.txt for formal definitions.
*/ */
@ -77,42 +62,3 @@ __kuser_helper_version: // 0xffff0ffc
.word ((__kuser_helper_end - __kuser_helper_start) >> 5) .word ((__kuser_helper_end - __kuser_helper_start) >> 5)
.globl __kuser_helper_end .globl __kuser_helper_end
__kuser_helper_end: __kuser_helper_end:
/*
* AArch32 sigreturn code
*
* For ARM syscalls, the syscall number has to be loaded into r7.
* We do not support an OABI userspace.
*
* For Thumb syscalls, we also pass the syscall number via r7. We therefore
* need two 16-bit instructions.
*/
.globl __aarch32_sigret_code_start
__aarch32_sigret_code_start:
/*
* ARM Code
*/
.byte __NR_compat_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_sigreturn
.byte __NR_compat_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_sigreturn
/*
* Thumb code
*/
.byte __NR_compat_sigreturn, 0x27 // svc #__NR_compat_sigreturn
.byte __NR_compat_sigreturn, 0xdf // mov r7, #__NR_compat_sigreturn
/*
* ARM code
*/
.byte __NR_compat_rt_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_rt_sigreturn
.byte __NR_compat_rt_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_rt_sigreturn
/*
* Thumb code
*/
.byte __NR_compat_rt_sigreturn, 0x27 // svc #__NR_compat_rt_sigreturn
.byte __NR_compat_rt_sigreturn, 0xdf // mov r7, #__NR_compat_rt_sigreturn
.globl __aarch32_sigret_code_end
__aarch32_sigret_code_end:

View File

@ -431,7 +431,7 @@ static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
return val; return val;
} }
static inline u64 armv8pmu_read_counter(struct perf_event *event) static u64 armv8pmu_read_counter(struct perf_event *event)
{ {
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
@ -468,7 +468,7 @@ static inline void armv8pmu_write_hw_counter(struct perf_event *event,
} }
} }
static inline void armv8pmu_write_counter(struct perf_event *event, u64 value) static void armv8pmu_write_counter(struct perf_event *event, u64 value)
{ {
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;

View File

@ -439,15 +439,12 @@ kprobe_ss_hit(struct kprobe_ctlblk *kcb, unsigned long addr)
return DBG_HOOK_ERROR; return DBG_HOOK_ERROR;
} }
int __kprobes static int __kprobes
kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr) kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr)
{ {
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
int retval; int retval;
if (user_mode(regs))
return DBG_HOOK_ERROR;
/* return error if this is not our step */ /* return error if this is not our step */
retval = kprobe_ss_hit(kcb, instruction_pointer(regs)); retval = kprobe_ss_hit(kcb, instruction_pointer(regs));
@ -461,16 +458,22 @@ kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr)
return retval; return retval;
} }
int __kprobes static struct step_hook kprobes_step_hook = {
.fn = kprobe_single_step_handler,
};
static int __kprobes
kprobe_breakpoint_handler(struct pt_regs *regs, unsigned int esr) kprobe_breakpoint_handler(struct pt_regs *regs, unsigned int esr)
{ {
if (user_mode(regs))
return DBG_HOOK_ERROR;
kprobe_handler(regs); kprobe_handler(regs);
return DBG_HOOK_HANDLED; return DBG_HOOK_HANDLED;
} }
static struct break_hook kprobes_break_hook = {
.imm = KPROBES_BRK_IMM,
.fn = kprobe_breakpoint_handler,
};
/* /*
* Provide a blacklist of symbols identifying ranges which cannot be kprobed. * Provide a blacklist of symbols identifying ranges which cannot be kprobed.
* This blacklist is exposed to userspace via debugfs (kprobes/blacklist). * This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
@ -599,5 +602,8 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)
int __init arch_init_kprobes(void) int __init arch_init_kprobes(void)
{ {
register_kernel_break_hook(&kprobes_break_hook);
register_kernel_step_hook(&kprobes_step_hook);
return 0; return 0;
} }

View File

@ -171,7 +171,7 @@ int arch_uprobe_exception_notify(struct notifier_block *self,
static int uprobe_breakpoint_handler(struct pt_regs *regs, static int uprobe_breakpoint_handler(struct pt_regs *regs,
unsigned int esr) unsigned int esr)
{ {
if (user_mode(regs) && uprobe_pre_sstep_notifier(regs)) if (uprobe_pre_sstep_notifier(regs))
return DBG_HOOK_HANDLED; return DBG_HOOK_HANDLED;
return DBG_HOOK_ERROR; return DBG_HOOK_ERROR;
@ -182,21 +182,16 @@ static int uprobe_single_step_handler(struct pt_regs *regs,
{ {
struct uprobe_task *utask = current->utask; struct uprobe_task *utask = current->utask;
if (user_mode(regs)) { WARN_ON(utask && (instruction_pointer(regs) != utask->xol_vaddr + 4));
WARN_ON(utask && if (uprobe_post_sstep_notifier(regs))
(instruction_pointer(regs) != utask->xol_vaddr + 4)); return DBG_HOOK_HANDLED;
if (uprobe_post_sstep_notifier(regs))
return DBG_HOOK_HANDLED;
}
return DBG_HOOK_ERROR; return DBG_HOOK_ERROR;
} }
/* uprobe breakpoint handler hook */ /* uprobe breakpoint handler hook */
static struct break_hook uprobes_break_hook = { static struct break_hook uprobes_break_hook = {
.esr_mask = BRK64_ESR_MASK, .imm = UPROBES_BRK_IMM,
.esr_val = BRK64_ESR_UPROBES,
.fn = uprobe_breakpoint_handler, .fn = uprobe_breakpoint_handler,
}; };
@ -207,8 +202,8 @@ static struct step_hook uprobes_step_hook = {
static int __init arch_init_uprobes(void) static int __init arch_init_uprobes(void)
{ {
register_break_hook(&uprobes_break_hook); register_user_break_hook(&uprobes_break_hook);
register_step_hook(&uprobes_step_hook); register_user_step_hook(&uprobes_step_hook);
return 0; return 0;
} }

View File

@ -403,8 +403,7 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka,
if (ka->sa.sa_flags & SA_SIGINFO) if (ka->sa.sa_flags & SA_SIGINFO)
idx += 3; idx += 3;
retcode = AARCH32_VECTORS_BASE + retcode = (unsigned long)current->mm->context.vdso +
AARCH32_KERN_SIGRET_CODE_OFFSET +
(idx << 2) + thumb; (idx << 2) + thumb;
} }

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* AArch32 sigreturn code.
* Based on the kuser helpers in arch/arm/kernel/entry-armv.S.
*
* Copyright (C) 2005-2011 Nicolas Pitre <nico@fluxnic.net>
* Copyright (C) 2012-2018 ARM Ltd.
*
* For ARM syscalls, the syscall number has to be loaded into r7.
* We do not support an OABI userspace.
*
* For Thumb syscalls, we also pass the syscall number via r7. We therefore
* need two 16-bit instructions.
*/
#include <asm/unistd.h>
.globl __aarch32_sigret_code_start
__aarch32_sigret_code_start:
/*
* ARM Code
*/
.byte __NR_compat_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_sigreturn
.byte __NR_compat_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_sigreturn
/*
* Thumb code
*/
.byte __NR_compat_sigreturn, 0x27 // svc #__NR_compat_sigreturn
.byte __NR_compat_sigreturn, 0xdf // mov r7, #__NR_compat_sigreturn
/*
* ARM code
*/
.byte __NR_compat_rt_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_rt_sigreturn
.byte __NR_compat_rt_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_rt_sigreturn
/*
* Thumb code
*/
.byte __NR_compat_rt_sigreturn, 0x27 // svc #__NR_compat_rt_sigreturn
.byte __NR_compat_rt_sigreturn, 0xdf // mov r7, #__NR_compat_rt_sigreturn
.globl __aarch32_sigret_code_end
__aarch32_sigret_code_end:

View File

@ -31,7 +31,7 @@
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len, SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
unsigned long, prot, unsigned long, flags, unsigned long, prot, unsigned long, flags,
unsigned long, fd, off_t, off) unsigned long, fd, unsigned long, off)
{ {
if (offset_in_page(off) != 0) if (offset_in_page(off) != 0)
return -EINVAL; return -EINVAL;

View File

@ -462,6 +462,9 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
case ESR_ELx_SYS64_ISS_CRM_DC_CVAC: /* DC CVAC, gets promoted */ case ESR_ELx_SYS64_ISS_CRM_DC_CVAC: /* DC CVAC, gets promoted */
__user_cache_maint("dc civac", address, ret); __user_cache_maint("dc civac", address, ret);
break; break;
case ESR_ELx_SYS64_ISS_CRM_DC_CVADP: /* DC CVADP */
__user_cache_maint("sys 3, c7, c13, 1", address, ret);
break;
case ESR_ELx_SYS64_ISS_CRM_DC_CVAP: /* DC CVAP */ case ESR_ELx_SYS64_ISS_CRM_DC_CVAP: /* DC CVAP */
__user_cache_maint("sys 3, c7, c12, 1", address, ret); __user_cache_maint("sys 3, c7, c12, 1", address, ret);
break; break;
@ -496,7 +499,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
{ {
int rt = ESR_ELx_SYS64_ISS_RT(esr); int rt = ESR_ELx_SYS64_ISS_RT(esr);
pt_regs_write_reg(regs, rt, arch_counter_get_cntvct()); pt_regs_write_reg(regs, rt, arch_timer_read_counter());
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
} }
@ -668,7 +671,7 @@ static void compat_cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
{ {
int rt = (esr & ESR_ELx_CP15_64_ISS_RT_MASK) >> ESR_ELx_CP15_64_ISS_RT_SHIFT; int rt = (esr & ESR_ELx_CP15_64_ISS_RT_MASK) >> ESR_ELx_CP15_64_ISS_RT_SHIFT;
int rt2 = (esr & ESR_ELx_CP15_64_ISS_RT2_MASK) >> ESR_ELx_CP15_64_ISS_RT2_SHIFT; int rt2 = (esr & ESR_ELx_CP15_64_ISS_RT2_MASK) >> ESR_ELx_CP15_64_ISS_RT2_SHIFT;
u64 val = arch_counter_get_cntvct(); u64 val = arch_timer_read_counter();
pt_regs_write_reg(regs, rt, lower_32_bits(val)); pt_regs_write_reg(regs, rt, lower_32_bits(val));
pt_regs_write_reg(regs, rt2, upper_32_bits(val)); pt_regs_write_reg(regs, rt2, upper_32_bits(val));
@ -950,9 +953,6 @@ int is_valid_bugaddr(unsigned long addr)
static int bug_handler(struct pt_regs *regs, unsigned int esr) static int bug_handler(struct pt_regs *regs, unsigned int esr)
{ {
if (user_mode(regs))
return DBG_HOOK_ERROR;
switch (report_bug(regs->pc, regs)) { switch (report_bug(regs->pc, regs)) {
case BUG_TRAP_TYPE_BUG: case BUG_TRAP_TYPE_BUG:
die("Oops - BUG", regs, 0); die("Oops - BUG", regs, 0);
@ -972,9 +972,8 @@ static int bug_handler(struct pt_regs *regs, unsigned int esr)
} }
static struct break_hook bug_break_hook = { static struct break_hook bug_break_hook = {
.esr_val = 0xf2000000 | BUG_BRK_IMM,
.esr_mask = 0xffffffff,
.fn = bug_handler, .fn = bug_handler,
.imm = BUG_BRK_IMM,
}; };
#ifdef CONFIG_KASAN_SW_TAGS #ifdef CONFIG_KASAN_SW_TAGS
@ -992,9 +991,6 @@ static int kasan_handler(struct pt_regs *regs, unsigned int esr)
u64 addr = regs->regs[0]; u64 addr = regs->regs[0];
u64 pc = regs->pc; u64 pc = regs->pc;
if (user_mode(regs))
return DBG_HOOK_ERROR;
kasan_report(addr, size, write, pc); kasan_report(addr, size, write, pc);
/* /*
@ -1019,13 +1015,10 @@ static int kasan_handler(struct pt_regs *regs, unsigned int esr)
return DBG_HOOK_HANDLED; return DBG_HOOK_HANDLED;
} }
#define KASAN_ESR_VAL (0xf2000000 | KASAN_BRK_IMM)
#define KASAN_ESR_MASK 0xffffff00
static struct break_hook kasan_break_hook = { static struct break_hook kasan_break_hook = {
.esr_val = KASAN_ESR_VAL, .fn = kasan_handler,
.esr_mask = KASAN_ESR_MASK, .imm = KASAN_BRK_IMM,
.fn = kasan_handler, .mask = KASAN_BRK_MASK,
}; };
#endif #endif
@ -1037,7 +1030,9 @@ int __init early_brk64(unsigned long addr, unsigned int esr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
#ifdef CONFIG_KASAN_SW_TAGS #ifdef CONFIG_KASAN_SW_TAGS
if ((esr & KASAN_ESR_MASK) == KASAN_ESR_VAL) unsigned int comment = esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
if ((comment & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
return kasan_handler(regs, esr) != DBG_HOOK_HANDLED; return kasan_handler(regs, esr) != DBG_HOOK_HANDLED;
#endif #endif
return bug_handler(regs, esr) != DBG_HOOK_HANDLED; return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
@ -1046,8 +1041,8 @@ int __init early_brk64(unsigned long addr, unsigned int esr,
/* This registration must happen early, before debug_traps_init(). */ /* This registration must happen early, before debug_traps_init(). */
void __init trap_init(void) void __init trap_init(void)
{ {
register_break_hook(&bug_break_hook); register_kernel_break_hook(&bug_break_hook);
#ifdef CONFIG_KASAN_SW_TAGS #ifdef CONFIG_KASAN_SW_TAGS
register_break_hook(&kasan_break_hook); register_kernel_break_hook(&kasan_break_hook);
#endif #endif
} }

View File

@ -1,5 +1,5 @@
/* /*
* VDSO implementation for AArch64 and vector page setup for AArch32. * VDSO implementations.
* *
* Copyright (C) 2012 ARM Limited * Copyright (C) 2012 ARM Limited
* *
@ -53,60 +53,128 @@ struct vdso_data *vdso_data = &vdso_data_store.data;
/* /*
* Create and map the vectors page for AArch32 tasks. * Create and map the vectors page for AArch32 tasks.
*/ */
static struct page *vectors_page[1] __ro_after_init; #define C_VECTORS 0
#define C_SIGPAGE 1
#define C_PAGES (C_SIGPAGE + 1)
static struct page *aarch32_vdso_pages[C_PAGES] __ro_after_init;
static const struct vm_special_mapping aarch32_vdso_spec[C_PAGES] = {
{
.name = "[vectors]", /* ABI */
.pages = &aarch32_vdso_pages[C_VECTORS],
},
{
.name = "[sigpage]", /* ABI */
.pages = &aarch32_vdso_pages[C_SIGPAGE],
},
};
static int __init alloc_vectors_page(void) static int aarch32_alloc_kuser_vdso_page(void)
{ {
extern char __kuser_helper_start[], __kuser_helper_end[]; extern char __kuser_helper_start[], __kuser_helper_end[];
extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];
int kuser_sz = __kuser_helper_end - __kuser_helper_start; int kuser_sz = __kuser_helper_end - __kuser_helper_start;
int sigret_sz = __aarch32_sigret_code_end - __aarch32_sigret_code_start; unsigned long vdso_page;
unsigned long vpage;
vpage = get_zeroed_page(GFP_ATOMIC); if (!IS_ENABLED(CONFIG_KUSER_HELPERS))
return 0;
if (!vpage) vdso_page = get_zeroed_page(GFP_ATOMIC);
if (!vdso_page)
return -ENOMEM; return -ENOMEM;
/* kuser helpers */ memcpy((void *)(vdso_page + 0x1000 - kuser_sz), __kuser_helper_start,
memcpy((void *)vpage + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
kuser_sz); aarch32_vdso_pages[C_VECTORS] = virt_to_page(vdso_page);
flush_dcache_page(aarch32_vdso_pages[C_VECTORS]);
/* sigreturn code */
memcpy((void *)vpage + AARCH32_KERN_SIGRET_CODE_OFFSET,
__aarch32_sigret_code_start, sigret_sz);
flush_icache_range(vpage, vpage + PAGE_SIZE);
vectors_page[0] = virt_to_page(vpage);
return 0; return 0;
} }
arch_initcall(alloc_vectors_page);
int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp) static int __init aarch32_alloc_vdso_pages(void)
{
extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];
int sigret_sz = __aarch32_sigret_code_end - __aarch32_sigret_code_start;
unsigned long sigpage;
int ret;
sigpage = get_zeroed_page(GFP_ATOMIC);
if (!sigpage)
return -ENOMEM;
memcpy((void *)sigpage, __aarch32_sigret_code_start, sigret_sz);
aarch32_vdso_pages[C_SIGPAGE] = virt_to_page(sigpage);
flush_dcache_page(aarch32_vdso_pages[C_SIGPAGE]);
ret = aarch32_alloc_kuser_vdso_page();
if (ret)
free_page(sigpage);
return ret;
}
arch_initcall(aarch32_alloc_vdso_pages);
static int aarch32_kuser_helpers_setup(struct mm_struct *mm)
{
void *ret;
if (!IS_ENABLED(CONFIG_KUSER_HELPERS))
return 0;
/*
* Avoid VM_MAYWRITE for compatibility with arch/arm/, where it's
* not safe to CoW the page containing the CPU exception vectors.
*/
ret = _install_special_mapping(mm, AARCH32_VECTORS_BASE, PAGE_SIZE,
VM_READ | VM_EXEC |
VM_MAYREAD | VM_MAYEXEC,
&aarch32_vdso_spec[C_VECTORS]);
return PTR_ERR_OR_ZERO(ret);
}
static int aarch32_sigreturn_setup(struct mm_struct *mm)
{
unsigned long addr;
void *ret;
addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
if (IS_ERR_VALUE(addr)) {
ret = ERR_PTR(addr);
goto out;
}
/*
* VM_MAYWRITE is required to allow gdb to Copy-on-Write and
* set breakpoints.
*/
ret = _install_special_mapping(mm, addr, PAGE_SIZE,
VM_READ | VM_EXEC | VM_MAYREAD |
VM_MAYWRITE | VM_MAYEXEC,
&aarch32_vdso_spec[C_SIGPAGE]);
if (IS_ERR(ret))
goto out;
mm->context.vdso = (void *)addr;
out:
return PTR_ERR_OR_ZERO(ret);
}
int aarch32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
unsigned long addr = AARCH32_VECTORS_BASE; int ret;
static const struct vm_special_mapping spec = {
.name = "[vectors]",
.pages = vectors_page,
};
void *ret;
if (down_write_killable(&mm->mmap_sem)) if (down_write_killable(&mm->mmap_sem))
return -EINTR; return -EINTR;
current->mm->context.vdso = (void *)addr;
/* Map vectors page at the high address. */ ret = aarch32_kuser_helpers_setup(mm);
ret = _install_special_mapping(mm, addr, PAGE_SIZE, if (ret)
VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC, goto out;
&spec);
ret = aarch32_sigreturn_setup(mm);
out:
up_write(&mm->mmap_sem); up_write(&mm->mmap_sem);
return ret;
return PTR_ERR_OR_ZERO(ret);
} }
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */
@ -146,8 +214,6 @@ static int __init vdso_init(void)
} }
vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT;
pr_info("vdso: %ld pages (%ld code @ %p, %ld data @ %p)\n",
vdso_pages + 1, vdso_pages, vdso_start, 1L, vdso_data);
/* Allocate the vDSO pagelist, plus a page for the data. */ /* Allocate the vDSO pagelist, plus a page for the data. */
vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *), vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *),
@ -232,6 +298,9 @@ void update_vsyscall(struct timekeeper *tk)
vdso_data->wtm_clock_sec = tk->wall_to_monotonic.tv_sec; vdso_data->wtm_clock_sec = tk->wall_to_monotonic.tv_sec;
vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec; vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec;
/* Read without the seqlock held by clock_getres() */
WRITE_ONCE(vdso_data->hrtimer_res, hrtimer_resolution);
if (!use_syscall) { if (!use_syscall) {
/* tkr_mono.cycle_last == tkr_raw.cycle_last */ /* tkr_mono.cycle_last == tkr_raw.cycle_last */
vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last; vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last;

View File

@ -12,17 +12,12 @@ obj-vdso := gettimeofday.o note.o sigreturn.o
targets := $(obj-vdso) vdso.so vdso.so.dbg targets := $(obj-vdso) vdso.so vdso.so.dbg
obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
ccflags-y := -shared -fno-common -fno-builtin ldflags-y := -shared -nostdlib -soname=linux-vdso.so.1 \
ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \ $(call ld-option, --hash-style=sysv) -n -T
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
# Disable gcov profiling for VDSO code # Disable gcov profiling for VDSO code
GCOV_PROFILE := n GCOV_PROFILE := n
# Workaround for bare-metal (ELF) toolchains that neglect to pass -shared
# down to collect2, resulting in silent corruption of the vDSO image.
ccflags-y += -Wl,-shared
obj-y += vdso.o obj-y += vdso.o
extra-y += vdso.lds extra-y += vdso.lds
CPPFLAGS_vdso.lds += -P -C -U$(ARCH) CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
@ -31,8 +26,8 @@ CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
$(obj)/vdso.o : $(obj)/vdso.so $(obj)/vdso.o : $(obj)/vdso.so
# Link rule for the .so file, .lds has to be first # Link rule for the .so file, .lds has to be first
$(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) $(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE
$(call if_changed,vdsold) $(call if_changed,ld)
# Strip rule for the .so file # Strip rule for the .so file
$(obj)/%.so: OBJCOPYFLAGS := -S $(obj)/%.so: OBJCOPYFLAGS := -S
@ -42,9 +37,7 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE
# Generate VDSO offsets using helper script # Generate VDSO offsets using helper script
gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh
quiet_cmd_vdsosym = VDSOSYM $@ quiet_cmd_vdsosym = VDSOSYM $@
define cmd_vdsosym cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
$(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
endef
include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE
$(call if_changed,vdsosym) $(call if_changed,vdsosym)
@ -54,8 +47,6 @@ $(obj-vdso): %.o: %.S FORCE
$(call if_changed_dep,vdsoas) $(call if_changed_dep,vdsoas)
# Actual build commands # Actual build commands
quiet_cmd_vdsold = VDSOL $@
cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@
quiet_cmd_vdsoas = VDSOA $@ quiet_cmd_vdsoas = VDSOA $@
cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $< cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<

View File

@ -73,6 +73,13 @@ x_tmp .req x8
movn x_tmp, #0xff00, lsl #48 movn x_tmp, #0xff00, lsl #48
and \res, x_tmp, \res and \res, x_tmp, \res
mul \res, \res, \mult mul \res, \res, \mult
/*
* Fake address dependency from the value computed from the counter
* register to subsequent data page accesses so that the sequence
* locking also orders the read of the counter.
*/
and x_tmp, \res, xzr
add vdso_data, vdso_data, x_tmp
.endm .endm
/* /*
@ -147,12 +154,12 @@ ENTRY(__kernel_gettimeofday)
/* w11 = cs_mono_mult, w12 = cs_shift */ /* w11 = cs_mono_mult, w12 = cs_shift */
ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
seqcnt_check fail=1b
get_nsec_per_sec res=x9 get_nsec_per_sec res=x9
lsl x9, x9, x12 lsl x9, x9, x12
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
seqcnt_check fail=1b
get_ts_realtime res_sec=x10, res_nsec=x11, \ get_ts_realtime res_sec=x10, res_nsec=x11, \
clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
@ -211,13 +218,13 @@ realtime:
/* w11 = cs_mono_mult, w12 = cs_shift */ /* w11 = cs_mono_mult, w12 = cs_shift */
ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
seqcnt_check fail=realtime
/* All computations are done with left-shifted nsecs. */ /* All computations are done with left-shifted nsecs. */
get_nsec_per_sec res=x9 get_nsec_per_sec res=x9
lsl x9, x9, x12 lsl x9, x9, x12
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
seqcnt_check fail=realtime
get_ts_realtime res_sec=x10, res_nsec=x11, \ get_ts_realtime res_sec=x10, res_nsec=x11, \
clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
clock_gettime_return, shift=1 clock_gettime_return, shift=1
@ -231,7 +238,6 @@ monotonic:
ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
ldp x3, x4, [vdso_data, #VDSO_WTM_CLK_SEC] ldp x3, x4, [vdso_data, #VDSO_WTM_CLK_SEC]
seqcnt_check fail=monotonic
/* All computations are done with left-shifted nsecs. */ /* All computations are done with left-shifted nsecs. */
lsl x4, x4, x12 lsl x4, x4, x12
@ -239,6 +245,7 @@ monotonic:
lsl x9, x9, x12 lsl x9, x9, x12
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
seqcnt_check fail=monotonic
get_ts_realtime res_sec=x10, res_nsec=x11, \ get_ts_realtime res_sec=x10, res_nsec=x11, \
clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
@ -253,13 +260,13 @@ monotonic_raw:
/* w11 = cs_raw_mult, w12 = cs_shift */ /* w11 = cs_raw_mult, w12 = cs_shift */
ldp w12, w11, [vdso_data, #VDSO_CS_SHIFT] ldp w12, w11, [vdso_data, #VDSO_CS_SHIFT]
ldp x13, x14, [vdso_data, #VDSO_RAW_TIME_SEC] ldp x13, x14, [vdso_data, #VDSO_RAW_TIME_SEC]
seqcnt_check fail=monotonic_raw
/* All computations are done with left-shifted nsecs. */ /* All computations are done with left-shifted nsecs. */
get_nsec_per_sec res=x9 get_nsec_per_sec res=x9
lsl x9, x9, x12 lsl x9, x9, x12
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
seqcnt_check fail=monotonic_raw
get_ts_clock_raw res_sec=x10, res_nsec=x11, \ get_ts_clock_raw res_sec=x10, res_nsec=x11, \
clock_nsec=x15, nsec_to_sec=x9 clock_nsec=x15, nsec_to_sec=x9
@ -301,13 +308,14 @@ ENTRY(__kernel_clock_getres)
ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne
b.ne 1f b.ne 1f
ldr x2, 5f adr vdso_data, _vdso_data
ldr w2, [vdso_data, #CLOCK_REALTIME_RES]
b 2f b 2f
1: 1:
cmp w0, #CLOCK_REALTIME_COARSE cmp w0, #CLOCK_REALTIME_COARSE
ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
b.ne 4f b.ne 4f
ldr x2, 6f ldr x2, 5f
2: 2:
cbz x1, 3f cbz x1, 3f
stp xzr, x2, [x1] stp xzr, x2, [x1]
@ -321,8 +329,6 @@ ENTRY(__kernel_clock_getres)
svc #0 svc #0
ret ret
5: 5:
.quad CLOCK_REALTIME_RES
6:
.quad CLOCK_COARSE_RES .quad CLOCK_COARSE_RES
.cfi_endproc .cfi_endproc
ENDPROC(__kernel_clock_getres) ENDPROC(__kernel_clock_getres)

View File

@ -24,7 +24,7 @@ CFLAGS_atomic_ll_sc.o := -ffixed-x1 -ffixed-x2 \
-fcall-saved-x10 -fcall-saved-x11 -fcall-saved-x12 \ -fcall-saved-x10 -fcall-saved-x11 -fcall-saved-x12 \
-fcall-saved-x13 -fcall-saved-x14 -fcall-saved-x15 \ -fcall-saved-x13 -fcall-saved-x14 -fcall-saved-x15 \
-fcall-saved-x18 -fomit-frame-pointer -fcall-saved-x18 -fomit-frame-pointer
CFLAGS_REMOVE_atomic_ll_sc.o := -pg CFLAGS_REMOVE_atomic_ll_sc.o := $(CC_FLAGS_FTRACE)
GCOV_PROFILE_atomic_ll_sc.o := n GCOV_PROFILE_atomic_ll_sc.o := n
KASAN_SANITIZE_atomic_ll_sc.o := n KASAN_SANITIZE_atomic_ll_sc.o := n
KCOV_INSTRUMENT_atomic_ll_sc.o := n KCOV_INSTRUMENT_atomic_ll_sc.o := n

View File

@ -148,7 +148,7 @@ static inline bool is_ttbr1_addr(unsigned long addr)
/* /*
* Dump out the page tables associated with 'addr' in the currently active mm. * Dump out the page tables associated with 'addr' in the currently active mm.
*/ */
void show_pte(unsigned long addr) static void show_pte(unsigned long addr)
{ {
struct mm_struct *mm; struct mm_struct *mm;
pgd_t *pgdp; pgd_t *pgdp;
@ -810,13 +810,12 @@ void __init hook_debug_fault_code(int nr,
debug_fault_info[nr].name = name; debug_fault_info[nr].name = name;
} }
asmlinkage int __exception do_debug_exception(unsigned long addr_if_watchpoint, asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
unsigned int esr, unsigned int esr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
const struct fault_info *inf = esr_to_debug_fault_info(esr); const struct fault_info *inf = esr_to_debug_fault_info(esr);
unsigned long pc = instruction_pointer(regs); unsigned long pc = instruction_pointer(regs);
int rv;
/* /*
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were * Tell lockdep we disabled irqs in entry.S. Do nothing if they were
@ -828,17 +827,12 @@ asmlinkage int __exception do_debug_exception(unsigned long addr_if_watchpoint,
if (user_mode(regs) && !is_ttbr0_addr(pc)) if (user_mode(regs) && !is_ttbr0_addr(pc))
arm64_apply_bp_hardening(); arm64_apply_bp_hardening();
if (!inf->fn(addr_if_watchpoint, esr, regs)) { if (inf->fn(addr_if_watchpoint, esr, regs)) {
rv = 1;
} else {
arm64_notify_die(inf->name, regs, arm64_notify_die(inf->name, regs,
inf->sig, inf->code, (void __user *)pc, esr); inf->sig, inf->code, (void __user *)pc, esr);
rv = 0;
} }
if (interrupts_enabled(regs)) if (interrupts_enabled(regs))
trace_hardirqs_on(); trace_hardirqs_on();
return rv;
} }
NOKPROBE_SYMBOL(do_debug_exception); NOKPROBE_SYMBOL(do_debug_exception);

View File

@ -377,7 +377,7 @@ void __init arm64_memblock_init(void)
base + size > memblock_start_of_DRAM() + base + size > memblock_start_of_DRAM() +
linear_region_size, linear_region_size,
"initrd not fully accessible via the linear mapping -- please check your bootloader ...\n")) { "initrd not fully accessible via the linear mapping -- please check your bootloader ...\n")) {
initrd_start = 0; phys_initrd_size = 0;
} else { } else {
memblock_remove(base, size); /* clear MEMBLOCK_ flags */ memblock_remove(base, size); /* clear MEMBLOCK_ flags */
memblock_add(base, size); memblock_add(base, size);
@ -440,6 +440,7 @@ void __init bootmem_init(void)
early_memtest(min << PAGE_SHIFT, max << PAGE_SHIFT); early_memtest(min << PAGE_SHIFT, max << PAGE_SHIFT);
max_pfn = max_low_pfn = max; max_pfn = max_low_pfn = max;
min_low_pfn = min;
arm64_numa_init(); arm64_numa_init();
/* /*
@ -535,7 +536,7 @@ void __init mem_init(void)
else else
swiotlb_force = SWIOTLB_NO_FORCE; swiotlb_force = SWIOTLB_NO_FORCE;
set_max_mapnr(pfn_to_page(max_pfn) - mem_map); set_max_mapnr(max_pfn - PHYS_PFN_OFFSET);
#ifndef CONFIG_SPARSEMEM_VMEMMAP #ifndef CONFIG_SPARSEMEM_VMEMMAP
free_unused_memmap(); free_unused_memmap();

View File

@ -97,7 +97,7 @@ pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
} }
EXPORT_SYMBOL(phys_mem_access_prot); EXPORT_SYMBOL(phys_mem_access_prot);
static phys_addr_t __init early_pgtable_alloc(void) static phys_addr_t __init early_pgtable_alloc(int shift)
{ {
phys_addr_t phys; phys_addr_t phys;
void *ptr; void *ptr;
@ -174,7 +174,7 @@ static void init_pte(pmd_t *pmdp, unsigned long addr, unsigned long end,
static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr, static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
unsigned long end, phys_addr_t phys, unsigned long end, phys_addr_t phys,
pgprot_t prot, pgprot_t prot,
phys_addr_t (*pgtable_alloc)(void), phys_addr_t (*pgtable_alloc)(int),
int flags) int flags)
{ {
unsigned long next; unsigned long next;
@ -184,7 +184,7 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
if (pmd_none(pmd)) { if (pmd_none(pmd)) {
phys_addr_t pte_phys; phys_addr_t pte_phys;
BUG_ON(!pgtable_alloc); BUG_ON(!pgtable_alloc);
pte_phys = pgtable_alloc(); pte_phys = pgtable_alloc(PAGE_SHIFT);
__pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE); __pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE);
pmd = READ_ONCE(*pmdp); pmd = READ_ONCE(*pmdp);
} }
@ -208,7 +208,7 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
static void init_pmd(pud_t *pudp, unsigned long addr, unsigned long end, static void init_pmd(pud_t *pudp, unsigned long addr, unsigned long end,
phys_addr_t phys, pgprot_t prot, phys_addr_t phys, pgprot_t prot,
phys_addr_t (*pgtable_alloc)(void), int flags) phys_addr_t (*pgtable_alloc)(int), int flags)
{ {
unsigned long next; unsigned long next;
pmd_t *pmdp; pmd_t *pmdp;
@ -246,7 +246,7 @@ static void init_pmd(pud_t *pudp, unsigned long addr, unsigned long end,
static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr, static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
unsigned long end, phys_addr_t phys, unsigned long end, phys_addr_t phys,
pgprot_t prot, pgprot_t prot,
phys_addr_t (*pgtable_alloc)(void), int flags) phys_addr_t (*pgtable_alloc)(int), int flags)
{ {
unsigned long next; unsigned long next;
pud_t pud = READ_ONCE(*pudp); pud_t pud = READ_ONCE(*pudp);
@ -258,7 +258,7 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
if (pud_none(pud)) { if (pud_none(pud)) {
phys_addr_t pmd_phys; phys_addr_t pmd_phys;
BUG_ON(!pgtable_alloc); BUG_ON(!pgtable_alloc);
pmd_phys = pgtable_alloc(); pmd_phys = pgtable_alloc(PMD_SHIFT);
__pud_populate(pudp, pmd_phys, PUD_TYPE_TABLE); __pud_populate(pudp, pmd_phys, PUD_TYPE_TABLE);
pud = READ_ONCE(*pudp); pud = READ_ONCE(*pudp);
} }
@ -294,7 +294,7 @@ static inline bool use_1G_block(unsigned long addr, unsigned long next,
static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end, static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
phys_addr_t phys, pgprot_t prot, phys_addr_t phys, pgprot_t prot,
phys_addr_t (*pgtable_alloc)(void), phys_addr_t (*pgtable_alloc)(int),
int flags) int flags)
{ {
unsigned long next; unsigned long next;
@ -304,7 +304,7 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
if (pgd_none(pgd)) { if (pgd_none(pgd)) {
phys_addr_t pud_phys; phys_addr_t pud_phys;
BUG_ON(!pgtable_alloc); BUG_ON(!pgtable_alloc);
pud_phys = pgtable_alloc(); pud_phys = pgtable_alloc(PUD_SHIFT);
__pgd_populate(pgdp, pud_phys, PUD_TYPE_TABLE); __pgd_populate(pgdp, pud_phys, PUD_TYPE_TABLE);
pgd = READ_ONCE(*pgdp); pgd = READ_ONCE(*pgdp);
} }
@ -345,7 +345,7 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys, static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
unsigned long virt, phys_addr_t size, unsigned long virt, phys_addr_t size,
pgprot_t prot, pgprot_t prot,
phys_addr_t (*pgtable_alloc)(void), phys_addr_t (*pgtable_alloc)(int),
int flags) int flags)
{ {
unsigned long addr, length, end, next; unsigned long addr, length, end, next;
@ -371,17 +371,36 @@ static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
} while (pgdp++, addr = next, addr != end); } while (pgdp++, addr = next, addr != end);
} }
static phys_addr_t pgd_pgtable_alloc(void) static phys_addr_t __pgd_pgtable_alloc(int shift)
{ {
void *ptr = (void *)__get_free_page(PGALLOC_GFP); void *ptr = (void *)__get_free_page(PGALLOC_GFP);
if (!ptr || !pgtable_page_ctor(virt_to_page(ptr))) BUG_ON(!ptr);
BUG();
/* Ensure the zeroed page is visible to the page table walker */ /* Ensure the zeroed page is visible to the page table walker */
dsb(ishst); dsb(ishst);
return __pa(ptr); return __pa(ptr);
} }
static phys_addr_t pgd_pgtable_alloc(int shift)
{
phys_addr_t pa = __pgd_pgtable_alloc(shift);
/*
* Call proper page table ctor in case later we need to
* call core mm functions like apply_to_page_range() on
* this pre-allocated page table.
*
* We don't select ARCH_ENABLE_SPLIT_PMD_PTLOCK if pmd is
* folded, and if so pgtable_pmd_page_ctor() becomes nop.
*/
if (shift == PAGE_SHIFT)
BUG_ON(!pgtable_page_ctor(phys_to_page(pa)));
else if (shift == PMD_SHIFT)
BUG_ON(!pgtable_pmd_page_ctor(phys_to_page(pa)));
return pa;
}
/* /*
* This function can only be used to modify existing table entries, * This function can only be used to modify existing table entries,
* without allocating new levels of table. Note that this permits the * without allocating new levels of table. Note that this permits the
@ -583,7 +602,7 @@ static int __init map_entry_trampoline(void)
/* Map only the text into the trampoline page table */ /* Map only the text into the trampoline page table */
memset(tramp_pg_dir, 0, PGD_SIZE); memset(tramp_pg_dir, 0, PGD_SIZE);
__create_pgd_mapping(tramp_pg_dir, pa_start, TRAMP_VALIAS, PAGE_SIZE, __create_pgd_mapping(tramp_pg_dir, pa_start, TRAMP_VALIAS, PAGE_SIZE,
prot, pgd_pgtable_alloc, 0); prot, __pgd_pgtable_alloc, 0);
/* Map both the text and data into the kernel page table */ /* Map both the text and data into the kernel page table */
__set_fixmap(FIX_ENTRY_TRAMP_TEXT, pa_start, prot); __set_fixmap(FIX_ENTRY_TRAMP_TEXT, pa_start, prot);
@ -1055,7 +1074,7 @@ int arch_add_memory(int nid, u64 start, u64 size, struct vmem_altmap *altmap,
flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS; flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
__create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start), __create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start),
size, PAGE_KERNEL, pgd_pgtable_alloc, flags); size, PAGE_KERNEL, __pgd_pgtable_alloc, flags);
return __add_pages(nid, start >> PAGE_SHIFT, size >> PAGE_SHIFT, return __add_pages(nid, start >> PAGE_SHIFT, size >> PAGE_SHIFT,
altmap, want_memblock); altmap, want_memblock);

View File

@ -124,7 +124,7 @@ static void __init setup_node_to_cpumask_map(void)
} }
/* /*
* Set the cpu to node and mem mapping * Set the cpu to node and mem mapping
*/ */
void numa_store_cpu_info(unsigned int cpu) void numa_store_cpu_info(unsigned int cpu)
{ {
@ -200,7 +200,7 @@ void __init setup_per_cpu_areas(void)
#endif #endif
/** /**
* numa_add_memblk - Set node id to memblk * numa_add_memblk() - Set node id to memblk
* @nid: NUMA node ID of the new memblk * @nid: NUMA node ID of the new memblk
* @start: Start address of the new memblk * @start: Start address of the new memblk
* @end: End address of the new memblk * @end: End address of the new memblk
@ -223,7 +223,7 @@ int __init numa_add_memblk(int nid, u64 start, u64 end)
return ret; return ret;
} }
/** /*
* Initialize NODE_DATA for a node on the local memory * Initialize NODE_DATA for a node on the local memory
*/ */
static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn) static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
@ -257,7 +257,7 @@ static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
} }
/** /*
* numa_free_distance * numa_free_distance
* *
* The current table is freed. * The current table is freed.
@ -277,10 +277,8 @@ void __init numa_free_distance(void)
numa_distance = NULL; numa_distance = NULL;
} }
/** /*
*
* Create a new NUMA distance table. * Create a new NUMA distance table.
*
*/ */
static int __init numa_alloc_distance(void) static int __init numa_alloc_distance(void)
{ {
@ -311,7 +309,7 @@ static int __init numa_alloc_distance(void)
} }
/** /**
* numa_set_distance - Set inter node NUMA distance from node to node. * numa_set_distance() - Set inter node NUMA distance from node to node.
* @from: the 'from' node to set distance * @from: the 'from' node to set distance
* @to: the 'to' node to set distance * @to: the 'to' node to set distance
* @distance: NUMA distance * @distance: NUMA distance
@ -321,7 +319,6 @@ static int __init numa_alloc_distance(void)
* *
* If @from or @to is higher than the highest known node or lower than zero * If @from or @to is higher than the highest known node or lower than zero
* or @distance doesn't make sense, the call is ignored. * or @distance doesn't make sense, the call is ignored.
*
*/ */
void __init numa_set_distance(int from, int to, int distance) void __init numa_set_distance(int from, int to, int distance)
{ {
@ -347,7 +344,7 @@ void __init numa_set_distance(int from, int to, int distance)
numa_distance[from * numa_distance_cnt + to] = distance; numa_distance[from * numa_distance_cnt + to] = distance;
} }
/** /*
* Return NUMA distance @from to @to * Return NUMA distance @from to @to
*/ */
int __node_distance(int from, int to) int __node_distance(int from, int to)
@ -422,13 +419,15 @@ static int __init numa_init(int (*init_func)(void))
} }
/** /**
* dummy_numa_init - Fallback dummy NUMA init * dummy_numa_init() - Fallback dummy NUMA init
* *
* Used if there's no underlying NUMA architecture, NUMA initialization * Used if there's no underlying NUMA architecture, NUMA initialization
* fails, or NUMA is disabled on the command line. * fails, or NUMA is disabled on the command line.
* *
* Must online at least one node (node 0) and add memory blocks that cover all * Must online at least one node (node 0) and add memory blocks that cover all
* allowed memory. It is unlikely that this function fails. * allowed memory. It is unlikely that this function fails.
*
* Return: 0 on success, -errno on failure.
*/ */
static int __init dummy_numa_init(void) static int __init dummy_numa_init(void)
{ {
@ -454,9 +453,9 @@ static int __init dummy_numa_init(void)
} }
/** /**
* arm64_numa_init - Initialize NUMA * arm64_numa_init() - Initialize NUMA
* *
* Try each configured NUMA initialization method until one succeeds. The * Try each configured NUMA initialization method until one succeeds. The
* last fallback is dummy single node config encomapssing whole memory. * last fallback is dummy single node config encomapssing whole memory.
*/ */
void __init arm64_numa_init(void) void __init arm64_numa_init(void)

View File

@ -65,24 +65,25 @@ ENTRY(cpu_do_suspend)
mrs x2, tpidr_el0 mrs x2, tpidr_el0
mrs x3, tpidrro_el0 mrs x3, tpidrro_el0
mrs x4, contextidr_el1 mrs x4, contextidr_el1
mrs x5, cpacr_el1 mrs x5, osdlr_el1
mrs x6, tcr_el1 mrs x6, cpacr_el1
mrs x7, vbar_el1 mrs x7, tcr_el1
mrs x8, mdscr_el1 mrs x8, vbar_el1
mrs x9, oslsr_el1 mrs x9, mdscr_el1
mrs x10, sctlr_el1 mrs x10, oslsr_el1
mrs x11, sctlr_el1
alternative_if_not ARM64_HAS_VIRT_HOST_EXTN alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
mrs x11, tpidr_el1 mrs x12, tpidr_el1
alternative_else alternative_else
mrs x11, tpidr_el2 mrs x12, tpidr_el2
alternative_endif alternative_endif
mrs x12, sp_el0 mrs x13, sp_el0
stp x2, x3, [x0] stp x2, x3, [x0]
stp x4, xzr, [x0, #16] stp x4, x5, [x0, #16]
stp x5, x6, [x0, #32] stp x6, x7, [x0, #32]
stp x7, x8, [x0, #48] stp x8, x9, [x0, #48]
stp x9, x10, [x0, #64] stp x10, x11, [x0, #64]
stp x11, x12, [x0, #80] stp x12, x13, [x0, #80]
ret ret
ENDPROC(cpu_do_suspend) ENDPROC(cpu_do_suspend)
@ -105,8 +106,8 @@ ENTRY(cpu_do_resume)
msr cpacr_el1, x6 msr cpacr_el1, x6
/* Don't change t0sz here, mask those bits when restoring */ /* Don't change t0sz here, mask those bits when restoring */
mrs x5, tcr_el1 mrs x7, tcr_el1
bfi x8, x5, TCR_T0SZ_OFFSET, TCR_TxSZ_WIDTH bfi x8, x7, TCR_T0SZ_OFFSET, TCR_TxSZ_WIDTH
msr tcr_el1, x8 msr tcr_el1, x8
msr vbar_el1, x9 msr vbar_el1, x9
@ -130,6 +131,7 @@ alternative_endif
/* /*
* Restore oslsr_el1 by writing oslar_el1 * Restore oslsr_el1 by writing oslar_el1
*/ */
msr osdlr_el1, x5
ubfx x11, x11, #1, #1 ubfx x11, x11, #1, #1
msr oslar_el1, x11 msr oslar_el1, x11
reset_pmuserenr_el0 x0 // Disable PMU access from EL0 reset_pmuserenr_el0 x0 // Disable PMU access from EL0

View File

@ -356,7 +356,8 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX ||
node->type == ACPI_IORT_NODE_SMMU_V3) { node->type == ACPI_IORT_NODE_SMMU_V3 ||
node->type == ACPI_IORT_NODE_PMCG) {
*id_out = map->output_base; *id_out = map->output_base;
return parent; return parent;
} }
@ -394,6 +395,8 @@ static int iort_get_id_mapping_index(struct acpi_iort_node *node)
} }
return smmu->id_mapping_index; return smmu->id_mapping_index;
case ACPI_IORT_NODE_PMCG:
return 0;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -1218,32 +1221,47 @@ static void __init arm_smmu_v3_init_resources(struct resource *res,
} }
} }
static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) static void __init arm_smmu_v3_dma_configure(struct device *dev,
struct acpi_iort_node *node)
{ {
struct acpi_iort_smmu_v3 *smmu; struct acpi_iort_smmu_v3 *smmu;
enum dev_dma_attr attr;
/* Retrieve SMMUv3 specific data */ /* Retrieve SMMUv3 specific data */
smmu = (struct acpi_iort_smmu_v3 *)node->node_data; smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; attr = (smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) ?
DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
/* We expect the dma masks to be equivalent for all SMMUv3 set-ups */
dev->dma_mask = &dev->coherent_dma_mask;
/* Configure DMA for the page table walker */
acpi_dma_configure(dev, attr);
} }
#if defined(CONFIG_ACPI_NUMA) #if defined(CONFIG_ACPI_NUMA)
/* /*
* set numa proximity domain for smmuv3 device * set numa proximity domain for smmuv3 device
*/ */
static void __init arm_smmu_v3_set_proximity(struct device *dev, static int __init arm_smmu_v3_set_proximity(struct device *dev,
struct acpi_iort_node *node) struct acpi_iort_node *node)
{ {
struct acpi_iort_smmu_v3 *smmu; struct acpi_iort_smmu_v3 *smmu;
smmu = (struct acpi_iort_smmu_v3 *)node->node_data; smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) {
set_dev_node(dev, acpi_map_pxm_to_node(smmu->pxm)); int node = acpi_map_pxm_to_node(smmu->pxm);
if (node != NUMA_NO_NODE && !node_online(node))
return -EINVAL;
set_dev_node(dev, node);
pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n",
smmu->base_address, smmu->base_address,
smmu->pxm); smmu->pxm);
} }
return 0;
} }
#else #else
#define arm_smmu_v3_set_proximity NULL #define arm_smmu_v3_set_proximity NULL
@ -1301,30 +1319,96 @@ static void __init arm_smmu_init_resources(struct resource *res,
} }
} }
static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) static void __init arm_smmu_dma_configure(struct device *dev,
struct acpi_iort_node *node)
{ {
struct acpi_iort_smmu *smmu; struct acpi_iort_smmu *smmu;
enum dev_dma_attr attr;
/* Retrieve SMMU specific data */ /* Retrieve SMMU specific data */
smmu = (struct acpi_iort_smmu *)node->node_data; smmu = (struct acpi_iort_smmu *)node->node_data;
return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; attr = (smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK) ?
DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
/* We expect the dma masks to be equivalent for SMMU set-ups */
dev->dma_mask = &dev->coherent_dma_mask;
/* Configure DMA for the page table walker */
acpi_dma_configure(dev, attr);
}
static int __init arm_smmu_v3_pmcg_count_resources(struct acpi_iort_node *node)
{
struct acpi_iort_pmcg *pmcg;
/* Retrieve PMCG specific data */
pmcg = (struct acpi_iort_pmcg *)node->node_data;
/*
* There are always 2 memory resources.
* If the overflow_gsiv is present then add that for a total of 3.
*/
return pmcg->overflow_gsiv ? 3 : 2;
}
static void __init arm_smmu_v3_pmcg_init_resources(struct resource *res,
struct acpi_iort_node *node)
{
struct acpi_iort_pmcg *pmcg;
/* Retrieve PMCG specific data */
pmcg = (struct acpi_iort_pmcg *)node->node_data;
res[0].start = pmcg->page0_base_address;
res[0].end = pmcg->page0_base_address + SZ_4K - 1;
res[0].flags = IORESOURCE_MEM;
res[1].start = pmcg->page1_base_address;
res[1].end = pmcg->page1_base_address + SZ_4K - 1;
res[1].flags = IORESOURCE_MEM;
if (pmcg->overflow_gsiv)
acpi_iort_register_irq(pmcg->overflow_gsiv, "overflow",
ACPI_EDGE_SENSITIVE, &res[2]);
}
static struct acpi_platform_list pmcg_plat_info[] __initdata = {
/* HiSilicon Hip08 Platform */
{"HISI ", "HIP08 ", 0, ACPI_SIG_IORT, greater_than_or_equal,
"Erratum #162001800", IORT_SMMU_V3_PMCG_HISI_HIP08},
{ }
};
static int __init arm_smmu_v3_pmcg_add_platdata(struct platform_device *pdev)
{
u32 model;
int idx;
idx = acpi_match_platform_list(pmcg_plat_info);
if (idx >= 0)
model = pmcg_plat_info[idx].data;
else
model = IORT_SMMU_V3_PMCG_GENERIC;
return platform_device_add_data(pdev, &model, sizeof(model));
} }
struct iort_dev_config { struct iort_dev_config {
const char *name; const char *name;
int (*dev_init)(struct acpi_iort_node *node); int (*dev_init)(struct acpi_iort_node *node);
bool (*dev_is_coherent)(struct acpi_iort_node *node); void (*dev_dma_configure)(struct device *dev,
struct acpi_iort_node *node);
int (*dev_count_resources)(struct acpi_iort_node *node); int (*dev_count_resources)(struct acpi_iort_node *node);
void (*dev_init_resources)(struct resource *res, void (*dev_init_resources)(struct resource *res,
struct acpi_iort_node *node); struct acpi_iort_node *node);
void (*dev_set_proximity)(struct device *dev, int (*dev_set_proximity)(struct device *dev,
struct acpi_iort_node *node); struct acpi_iort_node *node);
int (*dev_add_platdata)(struct platform_device *pdev);
}; };
static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = {
.name = "arm-smmu-v3", .name = "arm-smmu-v3",
.dev_is_coherent = arm_smmu_v3_is_coherent, .dev_dma_configure = arm_smmu_v3_dma_configure,
.dev_count_resources = arm_smmu_v3_count_resources, .dev_count_resources = arm_smmu_v3_count_resources,
.dev_init_resources = arm_smmu_v3_init_resources, .dev_init_resources = arm_smmu_v3_init_resources,
.dev_set_proximity = arm_smmu_v3_set_proximity, .dev_set_proximity = arm_smmu_v3_set_proximity,
@ -1332,9 +1416,16 @@ static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = {
static const struct iort_dev_config iort_arm_smmu_cfg __initconst = { static const struct iort_dev_config iort_arm_smmu_cfg __initconst = {
.name = "arm-smmu", .name = "arm-smmu",
.dev_is_coherent = arm_smmu_is_coherent, .dev_dma_configure = arm_smmu_dma_configure,
.dev_count_resources = arm_smmu_count_resources, .dev_count_resources = arm_smmu_count_resources,
.dev_init_resources = arm_smmu_init_resources .dev_init_resources = arm_smmu_init_resources,
};
static const struct iort_dev_config iort_arm_smmu_v3_pmcg_cfg __initconst = {
.name = "arm-smmu-v3-pmcg",
.dev_count_resources = arm_smmu_v3_pmcg_count_resources,
.dev_init_resources = arm_smmu_v3_pmcg_init_resources,
.dev_add_platdata = arm_smmu_v3_pmcg_add_platdata,
}; };
static __init const struct iort_dev_config *iort_get_dev_cfg( static __init const struct iort_dev_config *iort_get_dev_cfg(
@ -1345,6 +1436,8 @@ static __init const struct iort_dev_config *iort_get_dev_cfg(
return &iort_arm_smmu_v3_cfg; return &iort_arm_smmu_v3_cfg;
case ACPI_IORT_NODE_SMMU: case ACPI_IORT_NODE_SMMU:
return &iort_arm_smmu_cfg; return &iort_arm_smmu_cfg;
case ACPI_IORT_NODE_PMCG:
return &iort_arm_smmu_v3_pmcg_cfg;
default: default:
return NULL; return NULL;
} }
@ -1362,15 +1455,17 @@ static int __init iort_add_platform_device(struct acpi_iort_node *node,
struct fwnode_handle *fwnode; struct fwnode_handle *fwnode;
struct platform_device *pdev; struct platform_device *pdev;
struct resource *r; struct resource *r;
enum dev_dma_attr attr;
int ret, count; int ret, count;
pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
if (!pdev) if (!pdev)
return -ENOMEM; return -ENOMEM;
if (ops->dev_set_proximity) if (ops->dev_set_proximity) {
ops->dev_set_proximity(&pdev->dev, node); ret = ops->dev_set_proximity(&pdev->dev, node);
if (ret)
goto dev_put;
}
count = ops->dev_count_resources(node); count = ops->dev_count_resources(node);
@ -1393,19 +1488,19 @@ static int __init iort_add_platform_device(struct acpi_iort_node *node,
goto dev_put; goto dev_put;
/* /*
* Add a copy of IORT node pointer to platform_data to * Platform devices based on PMCG nodes uses platform_data to
* be used to retrieve IORT data information. * pass the hardware model info to the driver. For others, add
* a copy of IORT node pointer to platform_data to be used to
* retrieve IORT data information.
*/ */
ret = platform_device_add_data(pdev, &node, sizeof(node)); if (ops->dev_add_platdata)
ret = ops->dev_add_platdata(pdev);
else
ret = platform_device_add_data(pdev, &node, sizeof(node));
if (ret) if (ret)
goto dev_put; goto dev_put;
/*
* We expect the dma masks to be equivalent for
* all SMMUs set-ups
*/
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
fwnode = iort_get_fwnode(node); fwnode = iort_get_fwnode(node);
if (!fwnode) { if (!fwnode) {
@ -1415,11 +1510,8 @@ static int __init iort_add_platform_device(struct acpi_iort_node *node,
pdev->dev.fwnode = fwnode; pdev->dev.fwnode = fwnode;
attr = ops->dev_is_coherent && ops->dev_is_coherent(node) ? if (ops->dev_dma_configure)
DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; ops->dev_dma_configure(&pdev->dev, node);
/* Configure DMA for the page table walker */
acpi_dma_configure(&pdev->dev, attr);
iort_set_device_domain(&pdev->dev, node); iort_set_device_domain(&pdev->dev, node);

View File

@ -149,6 +149,26 @@ u32 arch_timer_reg_read(int access, enum arch_timer_reg reg,
return val; return val;
} }
static u64 arch_counter_get_cntpct_stable(void)
{
return __arch_counter_get_cntpct_stable();
}
static u64 arch_counter_get_cntpct(void)
{
return __arch_counter_get_cntpct();
}
static u64 arch_counter_get_cntvct_stable(void)
{
return __arch_counter_get_cntvct_stable();
}
static u64 arch_counter_get_cntvct(void)
{
return __arch_counter_get_cntvct();
}
/* /*
* Default to cp15 based access because arm64 uses this function for * Default to cp15 based access because arm64 uses this function for
* sched_clock() before DT is probed and the cp15 method is guaranteed * sched_clock() before DT is probed and the cp15 method is guaranteed
@ -316,13 +336,6 @@ static u64 notrace arm64_858921_read_cntvct_el0(void)
} }
#endif #endif
#ifdef CONFIG_ARM64_ERRATUM_1188873
static u64 notrace arm64_1188873_read_cntvct_el0(void)
{
return read_sysreg(cntvct_el0);
}
#endif
#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1 #ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1
/* /*
* The low bits of the counter registers are indeterminate while bit 10 or * The low bits of the counter registers are indeterminate while bit 10 or
@ -369,8 +382,7 @@ static u32 notrace sun50i_a64_read_cntv_tval_el0(void)
DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround); DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround);
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled); static atomic_t timer_unstable_counter_workaround_in_use = ATOMIC_INIT(0);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
static void erratum_set_next_event_tval_generic(const int access, unsigned long evt, static void erratum_set_next_event_tval_generic(const int access, unsigned long evt,
struct clock_event_device *clk) struct clock_event_device *clk)
@ -454,14 +466,6 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = {
.read_cntvct_el0 = arm64_858921_read_cntvct_el0, .read_cntvct_el0 = arm64_858921_read_cntvct_el0,
}, },
#endif #endif
#ifdef CONFIG_ARM64_ERRATUM_1188873
{
.match_type = ate_match_local_cap_id,
.id = (void *)ARM64_WORKAROUND_1188873,
.desc = "ARM erratum 1188873",
.read_cntvct_el0 = arm64_1188873_read_cntvct_el0,
},
#endif
#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1 #ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1
{ {
.match_type = ate_match_dt, .match_type = ate_match_dt,
@ -549,11 +553,8 @@ void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa
per_cpu(timer_unstable_counter_workaround, i) = wa; per_cpu(timer_unstable_counter_workaround, i) = wa;
} }
/* if (wa->read_cntvct_el0 || wa->read_cntpct_el0)
* Use the locked version, as we're called from the CPU atomic_set(&timer_unstable_counter_workaround_in_use, 1);
* hotplug framework. Otherwise, we end-up in deadlock-land.
*/
static_branch_enable_cpuslocked(&arch_timer_read_ool_enabled);
/* /*
* Don't use the vdso fastpath if errata require using the * Don't use the vdso fastpath if errata require using the
@ -570,7 +571,7 @@ void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa
static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type type, static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type type,
void *arg) void *arg)
{ {
const struct arch_timer_erratum_workaround *wa; const struct arch_timer_erratum_workaround *wa, *__wa;
ate_match_fn_t match_fn = NULL; ate_match_fn_t match_fn = NULL;
bool local = false; bool local = false;
@ -594,53 +595,32 @@ static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type t
if (!wa) if (!wa)
return; return;
if (needs_unstable_timer_counter_workaround()) { __wa = __this_cpu_read(timer_unstable_counter_workaround);
const struct arch_timer_erratum_workaround *__wa; if (__wa && wa != __wa)
__wa = __this_cpu_read(timer_unstable_counter_workaround); pr_warn("Can't enable workaround for %s (clashes with %s\n)",
if (__wa && wa != __wa) wa->desc, __wa->desc);
pr_warn("Can't enable workaround for %s (clashes with %s\n)",
wa->desc, __wa->desc);
if (__wa) if (__wa)
return; return;
}
arch_timer_enable_workaround(wa, local); arch_timer_enable_workaround(wa, local);
pr_info("Enabling %s workaround for %s\n", pr_info("Enabling %s workaround for %s\n",
local ? "local" : "global", wa->desc); local ? "local" : "global", wa->desc);
} }
#define erratum_handler(fn, r, ...) \
({ \
bool __val; \
if (needs_unstable_timer_counter_workaround()) { \
const struct arch_timer_erratum_workaround *__wa; \
__wa = __this_cpu_read(timer_unstable_counter_workaround); \
if (__wa && __wa->fn) { \
r = __wa->fn(__VA_ARGS__); \
__val = true; \
} else { \
__val = false; \
} \
} else { \
__val = false; \
} \
__val; \
})
static bool arch_timer_this_cpu_has_cntvct_wa(void) static bool arch_timer_this_cpu_has_cntvct_wa(void)
{ {
const struct arch_timer_erratum_workaround *wa; return has_erratum_handler(read_cntvct_el0);
}
wa = __this_cpu_read(timer_unstable_counter_workaround); static bool arch_timer_counter_has_wa(void)
return wa && wa->read_cntvct_el0; {
return atomic_read(&timer_unstable_counter_workaround_in_use);
} }
#else #else
#define arch_timer_check_ool_workaround(t,a) do { } while(0) #define arch_timer_check_ool_workaround(t,a) do { } while(0)
#define erratum_set_next_event_tval_virt(...) ({BUG(); 0;})
#define erratum_set_next_event_tval_phys(...) ({BUG(); 0;})
#define erratum_handler(fn, r, ...) ({false;})
#define arch_timer_this_cpu_has_cntvct_wa() ({false;}) #define arch_timer_this_cpu_has_cntvct_wa() ({false;})
#define arch_timer_counter_has_wa() ({false;})
#endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */ #endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */
static __always_inline irqreturn_t timer_handler(const int access, static __always_inline irqreturn_t timer_handler(const int access,
@ -733,11 +713,6 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
static int arch_timer_set_next_event_virt(unsigned long evt, static int arch_timer_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk) struct clock_event_device *clk)
{ {
int ret;
if (erratum_handler(set_next_event_virt, ret, evt, clk))
return ret;
set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk); set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
return 0; return 0;
} }
@ -745,11 +720,6 @@ static int arch_timer_set_next_event_virt(unsigned long evt,
static int arch_timer_set_next_event_phys(unsigned long evt, static int arch_timer_set_next_event_phys(unsigned long evt,
struct clock_event_device *clk) struct clock_event_device *clk)
{ {
int ret;
if (erratum_handler(set_next_event_phys, ret, evt, clk))
return ret;
set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk); set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
return 0; return 0;
} }
@ -774,6 +744,10 @@ static void __arch_timer_setup(unsigned type,
clk->features = CLOCK_EVT_FEAT_ONESHOT; clk->features = CLOCK_EVT_FEAT_ONESHOT;
if (type == ARCH_TIMER_TYPE_CP15) { if (type == ARCH_TIMER_TYPE_CP15) {
typeof(clk->set_next_event) sne;
arch_timer_check_ool_workaround(ate_match_local_cap_id, NULL);
if (arch_timer_c3stop) if (arch_timer_c3stop)
clk->features |= CLOCK_EVT_FEAT_C3STOP; clk->features |= CLOCK_EVT_FEAT_C3STOP;
clk->name = "arch_sys_timer"; clk->name = "arch_sys_timer";
@ -784,20 +758,20 @@ static void __arch_timer_setup(unsigned type,
case ARCH_TIMER_VIRT_PPI: case ARCH_TIMER_VIRT_PPI:
clk->set_state_shutdown = arch_timer_shutdown_virt; clk->set_state_shutdown = arch_timer_shutdown_virt;
clk->set_state_oneshot_stopped = arch_timer_shutdown_virt; clk->set_state_oneshot_stopped = arch_timer_shutdown_virt;
clk->set_next_event = arch_timer_set_next_event_virt; sne = erratum_handler(set_next_event_virt);
break; break;
case ARCH_TIMER_PHYS_SECURE_PPI: case ARCH_TIMER_PHYS_SECURE_PPI:
case ARCH_TIMER_PHYS_NONSECURE_PPI: case ARCH_TIMER_PHYS_NONSECURE_PPI:
case ARCH_TIMER_HYP_PPI: case ARCH_TIMER_HYP_PPI:
clk->set_state_shutdown = arch_timer_shutdown_phys; clk->set_state_shutdown = arch_timer_shutdown_phys;
clk->set_state_oneshot_stopped = arch_timer_shutdown_phys; clk->set_state_oneshot_stopped = arch_timer_shutdown_phys;
clk->set_next_event = arch_timer_set_next_event_phys; sne = erratum_handler(set_next_event_phys);
break; break;
default: default:
BUG(); BUG();
} }
arch_timer_check_ool_workaround(ate_match_local_cap_id, NULL); clk->set_next_event = sne;
} else { } else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ; clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer"; clk->name = "arch_mem_timer";
@ -830,7 +804,11 @@ static void arch_timer_evtstrm_enable(int divider)
cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT) cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
| ARCH_TIMER_VIRT_EVT_EN; | ARCH_TIMER_VIRT_EVT_EN;
arch_timer_set_cntkctl(cntkctl); arch_timer_set_cntkctl(cntkctl);
#ifdef CONFIG_ARM64
cpu_set_named_feature(EVTSTRM);
#else
elf_hwcap |= HWCAP_EVTSTRM; elf_hwcap |= HWCAP_EVTSTRM;
#endif
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM;
#endif #endif
@ -995,12 +973,22 @@ static void __init arch_counter_register(unsigned type)
/* Register the CP15 based counter if we have one */ /* Register the CP15 based counter if we have one */
if (type & ARCH_TIMER_TYPE_CP15) { if (type & ARCH_TIMER_TYPE_CP15) {
if ((IS_ENABLED(CONFIG_ARM64) && !is_hyp_mode_available()) || u64 (*rd)(void);
arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
if ((IS_ENABLED(CONFIG_ARM64) && !is_hyp_mode_available()) ||
arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) {
if (arch_timer_counter_has_wa())
rd = arch_counter_get_cntvct_stable;
else
rd = arch_counter_get_cntvct;
} else {
if (arch_timer_counter_has_wa())
rd = arch_counter_get_cntpct_stable;
else
rd = arch_counter_get_cntpct;
}
arch_timer_read_counter = rd;
clocksource_counter.archdata.vdso_direct = vdso_default; clocksource_counter.archdata.vdso_direct = vdso_default;
} else { } else {
arch_timer_read_counter = arch_counter_get_cntvct_mem; arch_timer_read_counter = arch_counter_get_cntvct_mem;
@ -1052,7 +1040,11 @@ static int arch_timer_cpu_pm_notify(struct notifier_block *self,
} else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) { } else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) {
arch_timer_set_cntkctl(__this_cpu_read(saved_cntkctl)); arch_timer_set_cntkctl(__this_cpu_read(saved_cntkctl));
#ifdef CONFIG_ARM64
if (cpu_have_named_feature(EVTSTRM))
#else
if (elf_hwcap & HWCAP_EVTSTRM) if (elf_hwcap & HWCAP_EVTSTRM)
#endif
cpumask_set_cpu(smp_processor_id(), &evtstrm_available); cpumask_set_cpu(smp_processor_id(), &evtstrm_available);
} }
return NOTIFY_OK; return NOTIFY_OK;

View File

@ -165,6 +165,7 @@ static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0,
return err; return err;
} }
NOKPROBE_SYMBOL(invoke_sdei_fn);
static struct sdei_event *sdei_event_find(u32 event_num) static struct sdei_event *sdei_event_find(u32 event_num)
{ {
@ -879,6 +880,7 @@ static void sdei_smccc_smc(unsigned long function_id,
{ {
arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
} }
NOKPROBE_SYMBOL(sdei_smccc_smc);
static void sdei_smccc_hvc(unsigned long function_id, static void sdei_smccc_hvc(unsigned long function_id,
unsigned long arg0, unsigned long arg1, unsigned long arg0, unsigned long arg1,
@ -887,6 +889,7 @@ static void sdei_smccc_hvc(unsigned long function_id,
{ {
arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
} }
NOKPROBE_SYMBOL(sdei_smccc_hvc);
int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb, int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
sdei_event_callback *critical_cb) sdei_event_callback *critical_cb)

View File

@ -16,9 +16,9 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \
# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly
# disable the stackleak plugin # disable the stackleak plugin
cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS)) -fpie \ cflags-$(CONFIG_ARM64) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
$(DISABLE_STACKLEAK_PLUGIN) -fpie $(DISABLE_STACKLEAK_PLUGIN)
cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
-fno-builtin -fpic \ -fno-builtin -fpic \
$(call cc-option,-mno-single-pic-base) $(call cc-option,-mno-single-pic-base)

View File

@ -52,6 +52,15 @@ config ARM_PMU_ACPI
depends on ARM_PMU && ACPI depends on ARM_PMU && ACPI
def_bool y def_bool y
config ARM_SMMU_V3_PMU
tristate "ARM SMMUv3 Performance Monitors Extension"
depends on ARM64 && ACPI && ARM_SMMU_V3
help
Provides support for the ARM SMMUv3 Performance Monitor Counter
Groups (PMCG), which provide monitoring of transactions passing
through the SMMU and allow the resulting information to be filtered
based on the Stream ID of the corresponding master.
config ARM_DSU_PMU config ARM_DSU_PMU
tristate "ARM DynamIQ Shared Unit (DSU) PMU" tristate "ARM DynamIQ Shared Unit (DSU) PMU"
depends on ARM64 depends on ARM64

View File

@ -4,6 +4,7 @@ obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_ARM_DSU_PMU) += arm_dsu_pmu.o obj-$(CONFIG_ARM_DSU_PMU) += arm_dsu_pmu.o
obj-$(CONFIG_ARM_PMU) += arm_pmu.o arm_pmu_platform.o obj-$(CONFIG_ARM_PMU) += arm_pmu.o arm_pmu_platform.o
obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o
obj-$(CONFIG_ARM_SMMU_V3_PMU) += arm_smmuv3_pmu.o
obj-$(CONFIG_HISI_PMU) += hisilicon/ obj-$(CONFIG_HISI_PMU) += hisilicon/
obj-$(CONFIG_QCOM_L2_PMU) += qcom_l2_pmu.o obj-$(CONFIG_QCOM_L2_PMU) += qcom_l2_pmu.o
obj-$(CONFIG_QCOM_L3_PMU) += qcom_l3_pmu.o obj-$(CONFIG_QCOM_L3_PMU) += qcom_l3_pmu.o

View File

@ -1684,21 +1684,24 @@ static int cci_pmu_probe(struct platform_device *pdev)
raw_spin_lock_init(&cci_pmu->hw_events.pmu_lock); raw_spin_lock_init(&cci_pmu->hw_events.pmu_lock);
mutex_init(&cci_pmu->reserve_mutex); mutex_init(&cci_pmu->reserve_mutex);
atomic_set(&cci_pmu->active_events, 0); atomic_set(&cci_pmu->active_events, 0);
cci_pmu->cpu = get_cpu();
ret = cci_pmu_init(cci_pmu, pdev);
if (ret) {
put_cpu();
return ret;
}
cci_pmu->cpu = raw_smp_processor_id();
g_cci_pmu = cci_pmu;
cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE, cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE,
"perf/arm/cci:online", NULL, "perf/arm/cci:online", NULL,
cci_pmu_offline_cpu); cci_pmu_offline_cpu);
put_cpu();
g_cci_pmu = cci_pmu; ret = cci_pmu_init(cci_pmu, pdev);
if (ret)
goto error_pmu_init;
pr_info("ARM %s PMU driver probed", cci_pmu->model->name); pr_info("ARM %s PMU driver probed", cci_pmu->model->name);
return 0; return 0;
error_pmu_init:
cpuhp_remove_state(CPUHP_AP_PERF_ARM_CCI_ONLINE);
g_cci_pmu = NULL;
return ret;
} }
static int cci_pmu_remove(struct platform_device *pdev) static int cci_pmu_remove(struct platform_device *pdev)

View File

@ -167,7 +167,7 @@ struct arm_ccn_dt {
struct hrtimer hrtimer; struct hrtimer hrtimer;
cpumask_t cpu; unsigned int cpu;
struct hlist_node node; struct hlist_node node;
struct pmu pmu; struct pmu pmu;
@ -559,7 +559,7 @@ static ssize_t arm_ccn_pmu_cpumask_show(struct device *dev,
{ {
struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev)); struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
return cpumap_print_to_pagebuf(true, buf, &ccn->dt.cpu); return cpumap_print_to_pagebuf(true, buf, cpumask_of(ccn->dt.cpu));
} }
static struct device_attribute arm_ccn_pmu_cpumask_attr = static struct device_attribute arm_ccn_pmu_cpumask_attr =
@ -759,7 +759,7 @@ static int arm_ccn_pmu_event_init(struct perf_event *event)
* mitigate this, we enforce CPU assignment to one, selected * mitigate this, we enforce CPU assignment to one, selected
* processor (the one described in the "cpumask" attribute). * processor (the one described in the "cpumask" attribute).
*/ */
event->cpu = cpumask_first(&ccn->dt.cpu); event->cpu = ccn->dt.cpu;
node_xp = CCN_CONFIG_NODE(event->attr.config); node_xp = CCN_CONFIG_NODE(event->attr.config);
type = CCN_CONFIG_TYPE(event->attr.config); type = CCN_CONFIG_TYPE(event->attr.config);
@ -1215,15 +1215,15 @@ static int arm_ccn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt); struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt);
unsigned int target; unsigned int target;
if (!cpumask_test_and_clear_cpu(cpu, &dt->cpu)) if (cpu != dt->cpu)
return 0; return 0;
target = cpumask_any_but(cpu_online_mask, cpu); target = cpumask_any_but(cpu_online_mask, cpu);
if (target >= nr_cpu_ids) if (target >= nr_cpu_ids)
return 0; return 0;
perf_pmu_migrate_context(&dt->pmu, cpu, target); perf_pmu_migrate_context(&dt->pmu, cpu, target);
cpumask_set_cpu(target, &dt->cpu); dt->cpu = target;
if (ccn->irq) if (ccn->irq)
WARN_ON(irq_set_affinity_hint(ccn->irq, &dt->cpu) != 0); WARN_ON(irq_set_affinity_hint(ccn->irq, cpumask_of(dt->cpu)));
return 0; return 0;
} }
@ -1299,29 +1299,30 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn)
} }
/* Pick one CPU which we will use to collect data from CCN... */ /* Pick one CPU which we will use to collect data from CCN... */
cpumask_set_cpu(get_cpu(), &ccn->dt.cpu); ccn->dt.cpu = raw_smp_processor_id();
/* Also make sure that the overflow interrupt is handled by this CPU */ /* Also make sure that the overflow interrupt is handled by this CPU */
if (ccn->irq) { if (ccn->irq) {
err = irq_set_affinity_hint(ccn->irq, &ccn->dt.cpu); err = irq_set_affinity_hint(ccn->irq, cpumask_of(ccn->dt.cpu));
if (err) { if (err) {
dev_err(ccn->dev, "Failed to set interrupt affinity!\n"); dev_err(ccn->dev, "Failed to set interrupt affinity!\n");
goto error_set_affinity; goto error_set_affinity;
} }
} }
cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
&ccn->dt.node);
err = perf_pmu_register(&ccn->dt.pmu, name, -1); err = perf_pmu_register(&ccn->dt.pmu, name, -1);
if (err) if (err)
goto error_pmu_register; goto error_pmu_register;
cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
&ccn->dt.node);
put_cpu();
return 0; return 0;
error_pmu_register: error_pmu_register:
cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
&ccn->dt.node);
error_set_affinity: error_set_affinity:
put_cpu();
error_choose_name: error_choose_name:
ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id); ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id);
for (i = 0; i < ccn->num_xps; i++) for (i = 0; i < ccn->num_xps; i++)

View File

@ -0,0 +1,865 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This driver adds support for perf events to use the Performance
* Monitor Counter Groups (PMCG) associated with an SMMUv3 node
* to monitor that node.
*
* SMMUv3 PMCG devices are named as smmuv3_pmcg_<phys_addr_page> where
* <phys_addr_page> is the physical page address of the SMMU PMCG wrapped
* to 4K boundary. For example, the PMCG at 0xff88840000 is named
* smmuv3_pmcg_ff88840
*
* Filtering by stream id is done by specifying filtering parameters
* with the event. options are:
* filter_enable - 0 = no filtering, 1 = filtering enabled
* filter_span - 0 = exact match, 1 = pattern match
* filter_stream_id - pattern to filter against
*
* To match a partial StreamID where the X most-significant bits must match
* but the Y least-significant bits might differ, STREAMID is programmed
* with a value that contains:
* STREAMID[Y - 1] == 0.
* STREAMID[Y - 2:0] == 1 (where Y > 1).
* The remainder of implemented bits of STREAMID (X bits, from bit Y upwards)
* contain a value to match from the corresponding bits of event StreamID.
*
* Example: perf stat -e smmuv3_pmcg_ff88840/transaction,filter_enable=1,
* filter_span=1,filter_stream_id=0x42/ -a netperf
* Applies filter pattern 0x42 to transaction events, which means events
* matching stream ids 0x42 and 0x43 are counted. Further filtering
* information is available in the SMMU documentation.
*
* SMMU events are not attributable to a CPU, so task mode and sampling
* are not supported.
*/
#include <linux/acpi.h>
#include <linux/acpi_iort.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cpuhotplug.h>
#include <linux/cpumask.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/msi.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
#include <linux/smp.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#define SMMU_PMCG_EVCNTR0 0x0
#define SMMU_PMCG_EVCNTR(n, stride) (SMMU_PMCG_EVCNTR0 + (n) * (stride))
#define SMMU_PMCG_EVTYPER0 0x400
#define SMMU_PMCG_EVTYPER(n) (SMMU_PMCG_EVTYPER0 + (n) * 4)
#define SMMU_PMCG_SID_SPAN_SHIFT 29
#define SMMU_PMCG_SMR0 0xA00
#define SMMU_PMCG_SMR(n) (SMMU_PMCG_SMR0 + (n) * 4)
#define SMMU_PMCG_CNTENSET0 0xC00
#define SMMU_PMCG_CNTENCLR0 0xC20
#define SMMU_PMCG_INTENSET0 0xC40
#define SMMU_PMCG_INTENCLR0 0xC60
#define SMMU_PMCG_OVSCLR0 0xC80
#define SMMU_PMCG_OVSSET0 0xCC0
#define SMMU_PMCG_CFGR 0xE00
#define SMMU_PMCG_CFGR_SID_FILTER_TYPE BIT(23)
#define SMMU_PMCG_CFGR_MSI BIT(21)
#define SMMU_PMCG_CFGR_RELOC_CTRS BIT(20)
#define SMMU_PMCG_CFGR_SIZE GENMASK(13, 8)
#define SMMU_PMCG_CFGR_NCTR GENMASK(5, 0)
#define SMMU_PMCG_CR 0xE04
#define SMMU_PMCG_CR_ENABLE BIT(0)
#define SMMU_PMCG_CEID0 0xE20
#define SMMU_PMCG_CEID1 0xE28
#define SMMU_PMCG_IRQ_CTRL 0xE50
#define SMMU_PMCG_IRQ_CTRL_IRQEN BIT(0)
#define SMMU_PMCG_IRQ_CFG0 0xE58
#define SMMU_PMCG_IRQ_CFG1 0xE60
#define SMMU_PMCG_IRQ_CFG2 0xE64
/* MSI config fields */
#define MSI_CFG0_ADDR_MASK GENMASK_ULL(51, 2)
#define MSI_CFG2_MEMATTR_DEVICE_nGnRE 0x1
#define SMMU_PMCG_DEFAULT_FILTER_SPAN 1
#define SMMU_PMCG_DEFAULT_FILTER_SID GENMASK(31, 0)
#define SMMU_PMCG_MAX_COUNTERS 64
#define SMMU_PMCG_ARCH_MAX_EVENTS 128
#define SMMU_PMCG_PA_SHIFT 12
#define SMMU_PMCG_EVCNTR_RDONLY BIT(0)
static int cpuhp_state_num;
struct smmu_pmu {
struct hlist_node node;
struct perf_event *events[SMMU_PMCG_MAX_COUNTERS];
DECLARE_BITMAP(used_counters, SMMU_PMCG_MAX_COUNTERS);
DECLARE_BITMAP(supported_events, SMMU_PMCG_ARCH_MAX_EVENTS);
unsigned int irq;
unsigned int on_cpu;
struct pmu pmu;
unsigned int num_counters;
struct device *dev;
void __iomem *reg_base;
void __iomem *reloc_base;
u64 counter_mask;
u32 options;
bool global_filter;
u32 global_filter_span;
u32 global_filter_sid;
};
#define to_smmu_pmu(p) (container_of(p, struct smmu_pmu, pmu))
#define SMMU_PMU_EVENT_ATTR_EXTRACTOR(_name, _config, _start, _end) \
static inline u32 get_##_name(struct perf_event *event) \
{ \
return FIELD_GET(GENMASK_ULL(_end, _start), \
event->attr._config); \
} \
SMMU_PMU_EVENT_ATTR_EXTRACTOR(event, config, 0, 15);
SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_stream_id, config1, 0, 31);
SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_span, config1, 32, 32);
SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_enable, config1, 33, 33);
static inline void smmu_pmu_enable(struct pmu *pmu)
{
struct smmu_pmu *smmu_pmu = to_smmu_pmu(pmu);
writel(SMMU_PMCG_IRQ_CTRL_IRQEN,
smmu_pmu->reg_base + SMMU_PMCG_IRQ_CTRL);
writel(SMMU_PMCG_CR_ENABLE, smmu_pmu->reg_base + SMMU_PMCG_CR);
}
static inline void smmu_pmu_disable(struct pmu *pmu)
{
struct smmu_pmu *smmu_pmu = to_smmu_pmu(pmu);
writel(0, smmu_pmu->reg_base + SMMU_PMCG_CR);
writel(0, smmu_pmu->reg_base + SMMU_PMCG_IRQ_CTRL);
}
static inline void smmu_pmu_counter_set_value(struct smmu_pmu *smmu_pmu,
u32 idx, u64 value)
{
if (smmu_pmu->counter_mask & BIT(32))
writeq(value, smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 8));
else
writel(value, smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 4));
}
static inline u64 smmu_pmu_counter_get_value(struct smmu_pmu *smmu_pmu, u32 idx)
{
u64 value;
if (smmu_pmu->counter_mask & BIT(32))
value = readq(smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 8));
else
value = readl(smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 4));
return value;
}
static inline void smmu_pmu_counter_enable(struct smmu_pmu *smmu_pmu, u32 idx)
{
writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_CNTENSET0);
}
static inline void smmu_pmu_counter_disable(struct smmu_pmu *smmu_pmu, u32 idx)
{
writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_CNTENCLR0);
}
static inline void smmu_pmu_interrupt_enable(struct smmu_pmu *smmu_pmu, u32 idx)
{
writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_INTENSET0);
}
static inline void smmu_pmu_interrupt_disable(struct smmu_pmu *smmu_pmu,
u32 idx)
{
writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_INTENCLR0);
}
static inline void smmu_pmu_set_evtyper(struct smmu_pmu *smmu_pmu, u32 idx,
u32 val)
{
writel(val, smmu_pmu->reg_base + SMMU_PMCG_EVTYPER(idx));
}
static inline void smmu_pmu_set_smr(struct smmu_pmu *smmu_pmu, u32 idx, u32 val)
{
writel(val, smmu_pmu->reg_base + SMMU_PMCG_SMR(idx));
}
static void smmu_pmu_event_update(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
u64 delta, prev, now;
u32 idx = hwc->idx;
do {
prev = local64_read(&hwc->prev_count);
now = smmu_pmu_counter_get_value(smmu_pmu, idx);
} while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
/* handle overflow. */
delta = now - prev;
delta &= smmu_pmu->counter_mask;
local64_add(delta, &event->count);
}
static void smmu_pmu_set_period(struct smmu_pmu *smmu_pmu,
struct hw_perf_event *hwc)
{
u32 idx = hwc->idx;
u64 new;
if (smmu_pmu->options & SMMU_PMCG_EVCNTR_RDONLY) {
/*
* On platforms that require this quirk, if the counter starts
* at < half_counter value and wraps, the current logic of
* handling the overflow may not work. It is expected that,
* those platforms will have full 64 counter bits implemented
* so that such a possibility is remote(eg: HiSilicon HIP08).
*/
new = smmu_pmu_counter_get_value(smmu_pmu, idx);
} else {
/*
* We limit the max period to half the max counter value
* of the counter size, so that even in the case of extreme
* interrupt latency the counter will (hopefully) not wrap
* past its initial value.
*/
new = smmu_pmu->counter_mask >> 1;
smmu_pmu_counter_set_value(smmu_pmu, idx, new);
}
local64_set(&hwc->prev_count, new);
}
static void smmu_pmu_set_event_filter(struct perf_event *event,
int idx, u32 span, u32 sid)
{
struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
u32 evtyper;
evtyper = get_event(event) | span << SMMU_PMCG_SID_SPAN_SHIFT;
smmu_pmu_set_evtyper(smmu_pmu, idx, evtyper);
smmu_pmu_set_smr(smmu_pmu, idx, sid);
}
static int smmu_pmu_apply_event_filter(struct smmu_pmu *smmu_pmu,
struct perf_event *event, int idx)
{
u32 span, sid;
unsigned int num_ctrs = smmu_pmu->num_counters;
bool filter_en = !!get_filter_enable(event);
span = filter_en ? get_filter_span(event) :
SMMU_PMCG_DEFAULT_FILTER_SPAN;
sid = filter_en ? get_filter_stream_id(event) :
SMMU_PMCG_DEFAULT_FILTER_SID;
/* Support individual filter settings */
if (!smmu_pmu->global_filter) {
smmu_pmu_set_event_filter(event, idx, span, sid);
return 0;
}
/* Requested settings same as current global settings*/
if (span == smmu_pmu->global_filter_span &&
sid == smmu_pmu->global_filter_sid)
return 0;
if (!bitmap_empty(smmu_pmu->used_counters, num_ctrs))
return -EAGAIN;
smmu_pmu_set_event_filter(event, 0, span, sid);
smmu_pmu->global_filter_span = span;
smmu_pmu->global_filter_sid = sid;
return 0;
}
static int smmu_pmu_get_event_idx(struct smmu_pmu *smmu_pmu,
struct perf_event *event)
{
int idx, err;
unsigned int num_ctrs = smmu_pmu->num_counters;
idx = find_first_zero_bit(smmu_pmu->used_counters, num_ctrs);
if (idx == num_ctrs)
/* The counters are all in use. */
return -EAGAIN;
err = smmu_pmu_apply_event_filter(smmu_pmu, event, idx);
if (err)
return err;
set_bit(idx, smmu_pmu->used_counters);
return idx;
}
/*
* Implementation of abstract pmu functionality required by
* the core perf events code.
*/
static int smmu_pmu_event_init(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
struct device *dev = smmu_pmu->dev;
struct perf_event *sibling;
u16 event_id;
if (event->attr.type != event->pmu->type)
return -ENOENT;
if (hwc->sample_period) {
dev_dbg(dev, "Sampling not supported\n");
return -EOPNOTSUPP;
}
if (event->cpu < 0) {
dev_dbg(dev, "Per-task mode not supported\n");
return -EOPNOTSUPP;
}
/* Verify specified event is supported on this PMU */
event_id = get_event(event);
if (event_id < SMMU_PMCG_ARCH_MAX_EVENTS &&
(!test_bit(event_id, smmu_pmu->supported_events))) {
dev_dbg(dev, "Invalid event %d for this PMU\n", event_id);
return -EINVAL;
}
/* Don't allow groups with mixed PMUs, except for s/w events */
if (event->group_leader->pmu != event->pmu &&
!is_software_event(event->group_leader)) {
dev_dbg(dev, "Can't create mixed PMU group\n");
return -EINVAL;
}
for_each_sibling_event(sibling, event->group_leader) {
if (sibling->pmu != event->pmu &&
!is_software_event(sibling)) {
dev_dbg(dev, "Can't create mixed PMU group\n");
return -EINVAL;
}
}
hwc->idx = -1;
/*
* Ensure all events are on the same cpu so all events are in the
* same cpu context, to avoid races on pmu_enable etc.
*/
event->cpu = smmu_pmu->on_cpu;
return 0;
}
static void smmu_pmu_event_start(struct perf_event *event, int flags)
{
struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
hwc->state = 0;
smmu_pmu_set_period(smmu_pmu, hwc);
smmu_pmu_counter_enable(smmu_pmu, idx);
}
static void smmu_pmu_event_stop(struct perf_event *event, int flags)
{
struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
if (hwc->state & PERF_HES_STOPPED)
return;
smmu_pmu_counter_disable(smmu_pmu, idx);
/* As the counter gets updated on _start, ignore PERF_EF_UPDATE */
smmu_pmu_event_update(event);
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
}
static int smmu_pmu_event_add(struct perf_event *event, int flags)
{
struct hw_perf_event *hwc = &event->hw;
int idx;
struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
idx = smmu_pmu_get_event_idx(smmu_pmu, event);
if (idx < 0)
return idx;
hwc->idx = idx;
hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
smmu_pmu->events[idx] = event;
local64_set(&hwc->prev_count, 0);
smmu_pmu_interrupt_enable(smmu_pmu, idx);
if (flags & PERF_EF_START)
smmu_pmu_event_start(event, flags);
/* Propagate changes to the userspace mapping. */
perf_event_update_userpage(event);
return 0;
}
static void smmu_pmu_event_del(struct perf_event *event, int flags)
{
struct hw_perf_event *hwc = &event->hw;
struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
int idx = hwc->idx;
smmu_pmu_event_stop(event, flags | PERF_EF_UPDATE);
smmu_pmu_interrupt_disable(smmu_pmu, idx);
smmu_pmu->events[idx] = NULL;
clear_bit(idx, smmu_pmu->used_counters);
perf_event_update_userpage(event);
}
static void smmu_pmu_event_read(struct perf_event *event)
{
smmu_pmu_event_update(event);
}
/* cpumask */
static ssize_t smmu_pmu_cpumask_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev));
return cpumap_print_to_pagebuf(true, buf, cpumask_of(smmu_pmu->on_cpu));
}
static struct device_attribute smmu_pmu_cpumask_attr =
__ATTR(cpumask, 0444, smmu_pmu_cpumask_show, NULL);
static struct attribute *smmu_pmu_cpumask_attrs[] = {
&smmu_pmu_cpumask_attr.attr,
NULL
};
static struct attribute_group smmu_pmu_cpumask_group = {
.attrs = smmu_pmu_cpumask_attrs,
};
/* Events */
static ssize_t smmu_pmu_event_show(struct device *dev,
struct device_attribute *attr, char *page)
{
struct perf_pmu_events_attr *pmu_attr;
pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
}
#define SMMU_EVENT_ATTR(name, config) \
PMU_EVENT_ATTR(name, smmu_event_attr_##name, \
config, smmu_pmu_event_show)
SMMU_EVENT_ATTR(cycles, 0);
SMMU_EVENT_ATTR(transaction, 1);
SMMU_EVENT_ATTR(tlb_miss, 2);
SMMU_EVENT_ATTR(config_cache_miss, 3);
SMMU_EVENT_ATTR(trans_table_walk_access, 4);
SMMU_EVENT_ATTR(config_struct_access, 5);
SMMU_EVENT_ATTR(pcie_ats_trans_rq, 6);
SMMU_EVENT_ATTR(pcie_ats_trans_passed, 7);
static struct attribute *smmu_pmu_events[] = {
&smmu_event_attr_cycles.attr.attr,
&smmu_event_attr_transaction.attr.attr,
&smmu_event_attr_tlb_miss.attr.attr,
&smmu_event_attr_config_cache_miss.attr.attr,
&smmu_event_attr_trans_table_walk_access.attr.attr,
&smmu_event_attr_config_struct_access.attr.attr,
&smmu_event_attr_pcie_ats_trans_rq.attr.attr,
&smmu_event_attr_pcie_ats_trans_passed.attr.attr,
NULL
};
static umode_t smmu_pmu_event_is_visible(struct kobject *kobj,
struct attribute *attr, int unused)
{
struct device *dev = kobj_to_dev(kobj);
struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev));
struct perf_pmu_events_attr *pmu_attr;
pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr.attr);
if (test_bit(pmu_attr->id, smmu_pmu->supported_events))
return attr->mode;
return 0;
}
static struct attribute_group smmu_pmu_events_group = {
.name = "events",
.attrs = smmu_pmu_events,
.is_visible = smmu_pmu_event_is_visible,
};
/* Formats */
PMU_FORMAT_ATTR(event, "config:0-15");
PMU_FORMAT_ATTR(filter_stream_id, "config1:0-31");
PMU_FORMAT_ATTR(filter_span, "config1:32");
PMU_FORMAT_ATTR(filter_enable, "config1:33");
static struct attribute *smmu_pmu_formats[] = {
&format_attr_event.attr,
&format_attr_filter_stream_id.attr,
&format_attr_filter_span.attr,
&format_attr_filter_enable.attr,
NULL
};
static struct attribute_group smmu_pmu_format_group = {
.name = "format",
.attrs = smmu_pmu_formats,
};
static const struct attribute_group *smmu_pmu_attr_grps[] = {
&smmu_pmu_cpumask_group,
&smmu_pmu_events_group,
&smmu_pmu_format_group,
NULL
};
/*
* Generic device handlers
*/
static int smmu_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
{
struct smmu_pmu *smmu_pmu;
unsigned int target;
smmu_pmu = hlist_entry_safe(node, struct smmu_pmu, node);
if (cpu != smmu_pmu->on_cpu)
return 0;
target = cpumask_any_but(cpu_online_mask, cpu);
if (target >= nr_cpu_ids)
return 0;
perf_pmu_migrate_context(&smmu_pmu->pmu, cpu, target);
smmu_pmu->on_cpu = target;
WARN_ON(irq_set_affinity_hint(smmu_pmu->irq, cpumask_of(target)));
return 0;
}
static irqreturn_t smmu_pmu_handle_irq(int irq_num, void *data)
{
struct smmu_pmu *smmu_pmu = data;
u64 ovsr;
unsigned int idx;
ovsr = readq(smmu_pmu->reloc_base + SMMU_PMCG_OVSSET0);
if (!ovsr)
return IRQ_NONE;
writeq(ovsr, smmu_pmu->reloc_base + SMMU_PMCG_OVSCLR0);
for_each_set_bit(idx, (unsigned long *)&ovsr, smmu_pmu->num_counters) {
struct perf_event *event = smmu_pmu->events[idx];
struct hw_perf_event *hwc;
if (WARN_ON_ONCE(!event))
continue;
smmu_pmu_event_update(event);
hwc = &event->hw;
smmu_pmu_set_period(smmu_pmu, hwc);
}
return IRQ_HANDLED;
}
static void smmu_pmu_free_msis(void *data)
{
struct device *dev = data;
platform_msi_domain_free_irqs(dev);
}
static void smmu_pmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
{
phys_addr_t doorbell;
struct device *dev = msi_desc_to_dev(desc);
struct smmu_pmu *pmu = dev_get_drvdata(dev);
doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
doorbell &= MSI_CFG0_ADDR_MASK;
writeq_relaxed(doorbell, pmu->reg_base + SMMU_PMCG_IRQ_CFG0);
writel_relaxed(msg->data, pmu->reg_base + SMMU_PMCG_IRQ_CFG1);
writel_relaxed(MSI_CFG2_MEMATTR_DEVICE_nGnRE,
pmu->reg_base + SMMU_PMCG_IRQ_CFG2);
}
static void smmu_pmu_setup_msi(struct smmu_pmu *pmu)
{
struct msi_desc *desc;
struct device *dev = pmu->dev;
int ret;
/* Clear MSI address reg */
writeq_relaxed(0, pmu->reg_base + SMMU_PMCG_IRQ_CFG0);
/* MSI supported or not */
if (!(readl(pmu->reg_base + SMMU_PMCG_CFGR) & SMMU_PMCG_CFGR_MSI))
return;
ret = platform_msi_domain_alloc_irqs(dev, 1, smmu_pmu_write_msi_msg);
if (ret) {
dev_warn(dev, "failed to allocate MSIs\n");
return;
}
desc = first_msi_entry(dev);
if (desc)
pmu->irq = desc->irq;
/* Add callback to free MSIs on teardown */
devm_add_action(dev, smmu_pmu_free_msis, dev);
}
static int smmu_pmu_setup_irq(struct smmu_pmu *pmu)
{
unsigned long flags = IRQF_NOBALANCING | IRQF_SHARED | IRQF_NO_THREAD;
int irq, ret = -ENXIO;
smmu_pmu_setup_msi(pmu);
irq = pmu->irq;
if (irq)
ret = devm_request_irq(pmu->dev, irq, smmu_pmu_handle_irq,
flags, "smmuv3-pmu", pmu);
return ret;
}
static void smmu_pmu_reset(struct smmu_pmu *smmu_pmu)
{
u64 counter_present_mask = GENMASK_ULL(smmu_pmu->num_counters - 1, 0);
smmu_pmu_disable(&smmu_pmu->pmu);
/* Disable counter and interrupt */
writeq_relaxed(counter_present_mask,
smmu_pmu->reg_base + SMMU_PMCG_CNTENCLR0);
writeq_relaxed(counter_present_mask,
smmu_pmu->reg_base + SMMU_PMCG_INTENCLR0);
writeq_relaxed(counter_present_mask,
smmu_pmu->reloc_base + SMMU_PMCG_OVSCLR0);
}
static void smmu_pmu_get_acpi_options(struct smmu_pmu *smmu_pmu)
{
u32 model;
model = *(u32 *)dev_get_platdata(smmu_pmu->dev);
switch (model) {
case IORT_SMMU_V3_PMCG_HISI_HIP08:
/* HiSilicon Erratum 162001800 */
smmu_pmu->options |= SMMU_PMCG_EVCNTR_RDONLY;
break;
}
dev_notice(smmu_pmu->dev, "option mask 0x%x\n", smmu_pmu->options);
}
static int smmu_pmu_probe(struct platform_device *pdev)
{
struct smmu_pmu *smmu_pmu;
struct resource *res_0, *res_1;
u32 cfgr, reg_size;
u64 ceid_64[2];
int irq, err;
char *name;
struct device *dev = &pdev->dev;
smmu_pmu = devm_kzalloc(dev, sizeof(*smmu_pmu), GFP_KERNEL);
if (!smmu_pmu)
return -ENOMEM;
smmu_pmu->dev = dev;
platform_set_drvdata(pdev, smmu_pmu);
smmu_pmu->pmu = (struct pmu) {
.task_ctx_nr = perf_invalid_context,
.pmu_enable = smmu_pmu_enable,
.pmu_disable = smmu_pmu_disable,
.event_init = smmu_pmu_event_init,
.add = smmu_pmu_event_add,
.del = smmu_pmu_event_del,
.start = smmu_pmu_event_start,
.stop = smmu_pmu_event_stop,
.read = smmu_pmu_event_read,
.attr_groups = smmu_pmu_attr_grps,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
};
res_0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
smmu_pmu->reg_base = devm_ioremap_resource(dev, res_0);
if (IS_ERR(smmu_pmu->reg_base))
return PTR_ERR(smmu_pmu->reg_base);
cfgr = readl_relaxed(smmu_pmu->reg_base + SMMU_PMCG_CFGR);
/* Determine if page 1 is present */
if (cfgr & SMMU_PMCG_CFGR_RELOC_CTRS) {
res_1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
smmu_pmu->reloc_base = devm_ioremap_resource(dev, res_1);
if (IS_ERR(smmu_pmu->reloc_base))
return PTR_ERR(smmu_pmu->reloc_base);
} else {
smmu_pmu->reloc_base = smmu_pmu->reg_base;
}
irq = platform_get_irq(pdev, 0);
if (irq > 0)
smmu_pmu->irq = irq;
ceid_64[0] = readq_relaxed(smmu_pmu->reg_base + SMMU_PMCG_CEID0);
ceid_64[1] = readq_relaxed(smmu_pmu->reg_base + SMMU_PMCG_CEID1);
bitmap_from_arr32(smmu_pmu->supported_events, (u32 *)ceid_64,
SMMU_PMCG_ARCH_MAX_EVENTS);
smmu_pmu->num_counters = FIELD_GET(SMMU_PMCG_CFGR_NCTR, cfgr) + 1;
smmu_pmu->global_filter = !!(cfgr & SMMU_PMCG_CFGR_SID_FILTER_TYPE);
reg_size = FIELD_GET(SMMU_PMCG_CFGR_SIZE, cfgr);
smmu_pmu->counter_mask = GENMASK_ULL(reg_size, 0);
smmu_pmu_reset(smmu_pmu);
err = smmu_pmu_setup_irq(smmu_pmu);
if (err) {
dev_err(dev, "Setup irq failed, PMU @%pa\n", &res_0->start);
return err;
}
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "smmuv3_pmcg_%llx",
(res_0->start) >> SMMU_PMCG_PA_SHIFT);
if (!name) {
dev_err(dev, "Create name failed, PMU @%pa\n", &res_0->start);
return -EINVAL;
}
smmu_pmu_get_acpi_options(smmu_pmu);
/* Pick one CPU to be the preferred one to use */
smmu_pmu->on_cpu = raw_smp_processor_id();
WARN_ON(irq_set_affinity_hint(smmu_pmu->irq,
cpumask_of(smmu_pmu->on_cpu)));
err = cpuhp_state_add_instance_nocalls(cpuhp_state_num,
&smmu_pmu->node);
if (err) {
dev_err(dev, "Error %d registering hotplug, PMU @%pa\n",
err, &res_0->start);
goto out_cpuhp_err;
}
err = perf_pmu_register(&smmu_pmu->pmu, name, -1);
if (err) {
dev_err(dev, "Error %d registering PMU @%pa\n",
err, &res_0->start);
goto out_unregister;
}
dev_info(dev, "Registered PMU @ %pa using %d counters with %s filter settings\n",
&res_0->start, smmu_pmu->num_counters,
smmu_pmu->global_filter ? "Global(Counter0)" :
"Individual");
return 0;
out_unregister:
cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &smmu_pmu->node);
out_cpuhp_err:
put_cpu();
return err;
}
static int smmu_pmu_remove(struct platform_device *pdev)
{
struct smmu_pmu *smmu_pmu = platform_get_drvdata(pdev);
perf_pmu_unregister(&smmu_pmu->pmu);
cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &smmu_pmu->node);
return 0;
}
static void smmu_pmu_shutdown(struct platform_device *pdev)
{
struct smmu_pmu *smmu_pmu = platform_get_drvdata(pdev);
smmu_pmu_disable(&smmu_pmu->pmu);
}
static struct platform_driver smmu_pmu_driver = {
.driver = {
.name = "arm-smmu-v3-pmcg",
},
.probe = smmu_pmu_probe,
.remove = smmu_pmu_remove,
.shutdown = smmu_pmu_shutdown,
};
static int __init arm_smmu_pmu_init(void)
{
cpuhp_state_num = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
"perf/arm/pmcg:online",
NULL,
smmu_pmu_offline_cpu);
if (cpuhp_state_num < 0)
return cpuhp_state_num;
return platform_driver_register(&smmu_pmu_driver);
}
module_init(arm_smmu_pmu_init);
static void __exit arm_smmu_pmu_exit(void)
{
platform_driver_unregister(&smmu_pmu_driver);
cpuhp_remove_multi_state(cpuhp_state_num);
}
module_exit(arm_smmu_pmu_exit);
MODULE_DESCRIPTION("PMU driver for ARM SMMUv3 Performance Monitors Extension");
MODULE_AUTHOR("Neil Leeder <nleeder@codeaurora.org>");
MODULE_AUTHOR("Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>");
MODULE_LICENSE("GPL v2");

View File

@ -161,7 +161,7 @@ static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd)
timeleft += readl(gwdt->control_base + SBSA_GWDT_WOR); timeleft += readl(gwdt->control_base + SBSA_GWDT_WOR);
timeleft += lo_hi_readq(gwdt->control_base + SBSA_GWDT_WCV) - timeleft += lo_hi_readq(gwdt->control_base + SBSA_GWDT_WCV) -
arch_counter_get_cntvct(); arch_timer_read_counter();
do_div(timeleft, gwdt->clk); do_div(timeleft, gwdt->clk);

View File

@ -23,7 +23,9 @@
* *
* Return: * Return:
* 0 - On success * 0 - On success
* <0 - On error * -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
* -ENOSYS - Operation not supported
*/ */
static inline int static inline int
arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
@ -85,7 +87,9 @@ arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
* *
* Return: * Return:
* 0 - On success * 0 - On success
* <0 - On error * -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
* -ENOSYS - Function not implemented (only if !HAVE_FUTEX_CMPXCHG)
*/ */
static inline int static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,

View File

@ -26,6 +26,14 @@
#define IORT_IRQ_MASK(irq) (irq & 0xffffffffULL) #define IORT_IRQ_MASK(irq) (irq & 0xffffffffULL)
#define IORT_IRQ_TRIGGER_MASK(irq) ((irq >> 32) & 0xffffffffULL) #define IORT_IRQ_TRIGGER_MASK(irq) ((irq >> 32) & 0xffffffffULL)
/*
* PMCG model identifiers for use in smmu pmu driver. Please note
* that this is purely for the use of software and has nothing to
* do with hardware or with IORT specification.
*/
#define IORT_SMMU_V3_PMCG_GENERIC 0x00000000 /* Generic SMMUv3 PMCG */
#define IORT_SMMU_V3_PMCG_HISI_HIP08 0x00000001 /* HiSilicon HIP08 PMCG */
int iort_register_domain_token(int trans_id, phys_addr_t base, int iort_register_domain_token(int trans_id, phys_addr_t base,
struct fwnode_handle *fw_node); struct fwnode_handle *fw_node);
void iort_deregister_domain_token(int trans_id); void iort_deregister_domain_token(int trans_id);

View File

@ -1311,13 +1311,15 @@ static int lookup_pi_state(u32 __user *uaddr, u32 uval,
static int lock_pi_update_atomic(u32 __user *uaddr, u32 uval, u32 newval) static int lock_pi_update_atomic(u32 __user *uaddr, u32 uval, u32 newval)
{ {
int err;
u32 uninitialized_var(curval); u32 uninitialized_var(curval);
if (unlikely(should_fail_futex(true))) if (unlikely(should_fail_futex(true)))
return -EFAULT; return -EFAULT;
if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))) err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval);
return -EFAULT; if (unlikely(err))
return err;
/* If user space value changed, let the caller retry */ /* If user space value changed, let the caller retry */
return curval != uval ? -EAGAIN : 0; return curval != uval ? -EAGAIN : 0;
@ -1502,10 +1504,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_
if (unlikely(should_fail_futex(true))) if (unlikely(should_fail_futex(true)))
ret = -EFAULT; ret = -EFAULT;
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) { ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval);
ret = -EFAULT; if (!ret && (curval != uval)) {
} else if (curval != uval) {
/* /*
* If a unconditional UNLOCK_PI operation (user space did not * If a unconditional UNLOCK_PI operation (user space did not
* try the TID->0 transition) raced with a waiter setting the * try the TID->0 transition) raced with a waiter setting the
@ -1700,32 +1700,32 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2,
double_lock_hb(hb1, hb2); double_lock_hb(hb1, hb2);
op_ret = futex_atomic_op_inuser(op, uaddr2); op_ret = futex_atomic_op_inuser(op, uaddr2);
if (unlikely(op_ret < 0)) { if (unlikely(op_ret < 0)) {
double_unlock_hb(hb1, hb2); double_unlock_hb(hb1, hb2);
#ifndef CONFIG_MMU if (!IS_ENABLED(CONFIG_MMU) ||
/* unlikely(op_ret != -EFAULT && op_ret != -EAGAIN)) {
* we don't get EFAULT from MMU faults if we don't have an MMU, /*
* but we might get them from range checking * we don't get EFAULT from MMU faults if we don't have
*/ * an MMU, but we might get them from range checking
ret = op_ret; */
goto out_put_keys;
#endif
if (unlikely(op_ret != -EFAULT)) {
ret = op_ret; ret = op_ret;
goto out_put_keys; goto out_put_keys;
} }
ret = fault_in_user_writeable(uaddr2); if (op_ret == -EFAULT) {
if (ret) ret = fault_in_user_writeable(uaddr2);
goto out_put_keys; if (ret)
goto out_put_keys;
}
if (!(flags & FLAGS_SHARED)) if (!(flags & FLAGS_SHARED)) {
cond_resched();
goto retry_private; goto retry_private;
}
put_futex_key(&key2); put_futex_key(&key2);
put_futex_key(&key1); put_futex_key(&key1);
cond_resched();
goto retry; goto retry;
} }
@ -2350,7 +2350,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
u32 uval, uninitialized_var(curval), newval; u32 uval, uninitialized_var(curval), newval;
struct task_struct *oldowner, *newowner; struct task_struct *oldowner, *newowner;
u32 newtid; u32 newtid;
int ret; int ret, err = 0;
lockdep_assert_held(q->lock_ptr); lockdep_assert_held(q->lock_ptr);
@ -2421,14 +2421,17 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
if (!pi_state->owner) if (!pi_state->owner)
newtid |= FUTEX_OWNER_DIED; newtid |= FUTEX_OWNER_DIED;
if (get_futex_value_locked(&uval, uaddr)) err = get_futex_value_locked(&uval, uaddr);
goto handle_fault; if (err)
goto handle_err;
for (;;) { for (;;) {
newval = (uval & FUTEX_OWNER_DIED) | newtid; newval = (uval & FUTEX_OWNER_DIED) | newtid;
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval);
goto handle_fault; if (err)
goto handle_err;
if (curval == uval) if (curval == uval)
break; break;
uval = curval; uval = curval;
@ -2456,23 +2459,37 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
return 0; return 0;
/* /*
* To handle the page fault we need to drop the locks here. That gives * In order to reschedule or handle a page fault, we need to drop the
* the other task (either the highest priority waiter itself or the * locks here. In the case of a fault, this gives the other task
* task which stole the rtmutex) the chance to try the fixup of the * (either the highest priority waiter itself or the task which stole
* pi_state. So once we are back from handling the fault we need to * the rtmutex) the chance to try the fixup of the pi_state. So once we
* check the pi_state after reacquiring the locks and before trying to * are back from handling the fault we need to check the pi_state after
* do another fixup. When the fixup has been done already we simply * reacquiring the locks and before trying to do another fixup. When
* return. * the fixup has been done already we simply return.
* *
* Note: we hold both hb->lock and pi_mutex->wait_lock. We can safely * Note: we hold both hb->lock and pi_mutex->wait_lock. We can safely
* drop hb->lock since the caller owns the hb -> futex_q relation. * drop hb->lock since the caller owns the hb -> futex_q relation.
* Dropping the pi_mutex->wait_lock requires the state revalidate. * Dropping the pi_mutex->wait_lock requires the state revalidate.
*/ */
handle_fault: handle_err:
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock); raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
spin_unlock(q->lock_ptr); spin_unlock(q->lock_ptr);
ret = fault_in_user_writeable(uaddr); switch (err) {
case -EFAULT:
ret = fault_in_user_writeable(uaddr);
break;
case -EAGAIN:
cond_resched();
ret = 0;
break;
default:
WARN_ON_ONCE(1);
ret = err;
break;
}
spin_lock(q->lock_ptr); spin_lock(q->lock_ptr);
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock); raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
@ -3041,10 +3058,8 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
* A unconditional UNLOCK_PI op raced against a waiter * A unconditional UNLOCK_PI op raced against a waiter
* setting the FUTEX_WAITERS bit. Try again. * setting the FUTEX_WAITERS bit. Try again.
*/ */
if (ret == -EAGAIN) { if (ret == -EAGAIN)
put_futex_key(&key); goto pi_retry;
goto retry;
}
/* /*
* wake_futex_pi has detected invalid state. Tell user * wake_futex_pi has detected invalid state. Tell user
* space. * space.
@ -3059,9 +3074,19 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
* preserve the WAITERS bit not the OWNER_DIED one. We are the * preserve the WAITERS bit not the OWNER_DIED one. We are the
* owner. * owner.
*/ */
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, 0)) { if ((ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, 0))) {
spin_unlock(&hb->lock); spin_unlock(&hb->lock);
goto pi_faulted; switch (ret) {
case -EFAULT:
goto pi_faulted;
case -EAGAIN:
goto pi_retry;
default:
WARN_ON_ONCE(1);
goto out_putkey;
}
} }
/* /*
@ -3075,6 +3100,11 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
put_futex_key(&key); put_futex_key(&key);
return ret; return ret;
pi_retry:
put_futex_key(&key);
cond_resched();
goto retry;
pi_faulted: pi_faulted:
put_futex_key(&key); put_futex_key(&key);
@ -3435,6 +3465,7 @@ SYSCALL_DEFINE3(get_robust_list, int, pid,
static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi) static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi)
{ {
u32 uval, uninitialized_var(nval), mval; u32 uval, uninitialized_var(nval), mval;
int err;
/* Futex address must be 32bit aligned */ /* Futex address must be 32bit aligned */
if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0) if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0)
@ -3444,42 +3475,57 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p
if (get_user(uval, uaddr)) if (get_user(uval, uaddr))
return -1; return -1;
if ((uval & FUTEX_TID_MASK) == task_pid_vnr(curr)) { if ((uval & FUTEX_TID_MASK) != task_pid_vnr(curr))
/* return 0;
* Ok, this dying thread is truly holding a futex
* of interest. Set the OWNER_DIED bit atomically /*
* via cmpxchg, and if the value had FUTEX_WAITERS * Ok, this dying thread is truly holding a futex
* set, wake up a waiter (if any). (We have to do a * of interest. Set the OWNER_DIED bit atomically
* futex_wake() even if OWNER_DIED is already set - * via cmpxchg, and if the value had FUTEX_WAITERS
* to handle the rare but possible case of recursive * set, wake up a waiter (if any). (We have to do a
* thread-death.) The rest of the cleanup is done in * futex_wake() even if OWNER_DIED is already set -
* userspace. * to handle the rare but possible case of recursive
*/ * thread-death.) The rest of the cleanup is done in
mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; * userspace.
/* */
* We are not holding a lock here, but we want to have mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
* the pagefault_disable/enable() protection because
* we want to handle the fault gracefully. If the /*
* access fails we try to fault in the futex with R/W * We are not holding a lock here, but we want to have
* verification via get_user_pages. get_user() above * the pagefault_disable/enable() protection because
* does not guarantee R/W access. If that fails we * we want to handle the fault gracefully. If the
* give up and leave the futex locked. * access fails we try to fault in the futex with R/W
*/ * verification via get_user_pages. get_user() above
if (cmpxchg_futex_value_locked(&nval, uaddr, uval, mval)) { * does not guarantee R/W access. If that fails we
* give up and leave the futex locked.
*/
if ((err = cmpxchg_futex_value_locked(&nval, uaddr, uval, mval))) {
switch (err) {
case -EFAULT:
if (fault_in_user_writeable(uaddr)) if (fault_in_user_writeable(uaddr))
return -1; return -1;
goto retry; goto retry;
}
if (nval != uval) case -EAGAIN:
cond_resched();
goto retry; goto retry;
/* default:
* Wake robust non-PI futexes here. The wakeup of WARN_ON_ONCE(1);
* PI futexes happens in exit_pi_state(): return err;
*/ }
if (!pi && (uval & FUTEX_WAITERS))
futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY);
} }
if (nval != uval)
goto retry;
/*
* Wake robust non-PI futexes here. The wakeup of
* PI futexes happens in exit_pi_state():
*/
if (!pi && (uval & FUTEX_WAITERS))
futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY);
return 0; return 0;
} }

View File

@ -6,10 +6,10 @@ UBSAN_SANITIZE_generic_report.o := n
UBSAN_SANITIZE_tags.o := n UBSAN_SANITIZE_tags.o := n
KCOV_INSTRUMENT := n KCOV_INSTRUMENT := n
CFLAGS_REMOVE_common.o = -pg CFLAGS_REMOVE_common.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_generic.o = -pg CFLAGS_REMOVE_generic.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_generic_report.o = -pg CFLAGS_REMOVE_generic_report.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_tags.o = -pg CFLAGS_REMOVE_tags.o = $(CC_FLAGS_FTRACE)
# Function splitter causes unnecessary splits in __asan_load1/__asan_store1 # Function splitter causes unnecessary splits in __asan_load1/__asan_store1
# see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533 # see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533

View File

@ -189,7 +189,7 @@ static void clear_stage2_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr
VM_BUG_ON(pmd_thp_or_huge(*pmd)); VM_BUG_ON(pmd_thp_or_huge(*pmd));
pmd_clear(pmd); pmd_clear(pmd);
kvm_tlb_flush_vmid_ipa(kvm, addr); kvm_tlb_flush_vmid_ipa(kvm, addr);
pte_free_kernel(NULL, pte_table); free_page((unsigned long)pte_table);
put_page(virt_to_page(pmd)); put_page(virt_to_page(pmd));
} }