Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8c3aced75 | |||
| 69622a5cbe | |||
| c414efdf39 | |||
| f47e3431b1 | |||
| 744ff5f0dc | |||
| d5cceea6dd | |||
| e990be06ac | |||
| 4b90064175 | |||
| 990e47782e | |||
| bbf934bc6b | |||
| 6223c1f703 | |||
| f2ccfbe471 | |||
| 93aff32d53 | |||
| 9113175ffd | |||
| 46c98de7cb | |||
| ce8556cca6 | |||
| 2ca715a123 | |||
| 0ae4fd9024 | |||
| 4805211684 | |||
| 35cf328313 | |||
| 031e646616 | |||
| fcaef4e27f | |||
| a8ac113d44 | |||
| 529bea3741 | |||
| c5b169d4c6 | |||
| f6be6903fc | |||
| 3b39dc7463 | |||
| abf914eefa | |||
| 573864e657 | |||
| 862b9e18a0 | |||
| 9fc384dd53 | |||
| 43b0b90df5 | |||
| 330e5973bb | |||
| 4d3579f7f9 | |||
| 3dde264329 | |||
| ee82c13f6c | |||
| 3ba71a6ffc | |||
| e41eed5736 | |||
| b16ff629f3 | |||
| 3131918d53 | |||
| 5ab84cf0c2 | |||
| 516ed9db64 | |||
| 6120bb2867 | |||
| 2754f7c6ec | |||
| 7f29a7c250 | |||
| d3cc70af8e | |||
| 89f5f75827 | |||
| 00cfc2f506 | |||
| 62de9c5ed2 | |||
| b8b75ff14f | |||
| d8360e9279 | |||
| 82abbe0ea7 | |||
| 347ff08ab7 | |||
| 758b51878d | |||
| fe30a30f59 | |||
| 8b2f15b4f2 | |||
| 651f562081 | |||
| cc1e665999 | |||
| 3c68ad96f2 | |||
| dc622f564b | |||
| 8fc95fe3fe | |||
| 8e5e7a0988 | |||
| f11565bed1 | |||
| cd1fb4bb18 | |||
| d86126ab74 | |||
| 452695e55b | |||
| 6cfd9bd016 | |||
| 1f2bbb423b | |||
| 43cb51f8ef | |||
| f1b4a0b93e | |||
| a1a4f40bee | |||
| 3d2db4f84f | |||
| 7774cb314c | |||
| fbe359706d | |||
| 0122cebdc4 | |||
| ae3b9410d7 | |||
| ccd135cdd9 | |||
| eabc9764eb | |||
| f70abc9107 | |||
| 5b44df38ed | |||
| 20d82bac62 | |||
| f2444b280d | |||
| dcf82399d6 | |||
| c3155990e2 | |||
| b4be123b58 | |||
| 6886c1b815 | |||
| 49e0975a05 | |||
| 24d9fd272e | |||
| 74f1c7a26b | |||
| 6249d3232f | |||
| 2883a1f89b | |||
| aa5de56185 | |||
| 71ef4580dc | |||
| 89d8157a39 | |||
| 1655bd148c | |||
| 9870e75571 | |||
| dcd1b1099b | |||
| adb3336455 | |||
| 0c5e625935 | |||
| 056dc0fa4c | |||
| db279f7193 | |||
| 1e56c506b3 | |||
| 45db516a6a | |||
| 16fe09de54 | |||
| f8a0aeefc2 | |||
| b13b271933 |
@@ -476,6 +476,7 @@ What: /sys/devices/system/cpu/vulnerabilities
|
||||
/sys/devices/system/cpu/vulnerabilities/spectre_v1
|
||||
/sys/devices/system/cpu/vulnerabilities/spectre_v2
|
||||
/sys/devices/system/cpu/vulnerabilities/spec_store_bypass
|
||||
/sys/devices/system/cpu/vulnerabilities/l1tf
|
||||
Date: January 2018
|
||||
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
|
||||
Description: Information about CPU vulnerabilities
|
||||
@@ -487,3 +488,26 @@ Description: Information about CPU vulnerabilities
|
||||
"Not affected" CPU is not affected by the vulnerability
|
||||
"Vulnerable" CPU is affected and no mitigation in effect
|
||||
"Mitigation: $M" CPU is affected and mitigation $M is in effect
|
||||
|
||||
Details about the l1tf file can be found in
|
||||
Documentation/admin-guide/l1tf.rst
|
||||
|
||||
What: /sys/devices/system/cpu/smt
|
||||
/sys/devices/system/cpu/smt/active
|
||||
/sys/devices/system/cpu/smt/control
|
||||
Date: June 2018
|
||||
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
|
||||
Description: Control Symetric Multi Threading (SMT)
|
||||
|
||||
active: Tells whether SMT is active (enabled and siblings online)
|
||||
|
||||
control: Read/write interface to control SMT. Possible
|
||||
values:
|
||||
|
||||
"on" SMT is enabled
|
||||
"off" SMT is disabled
|
||||
"forceoff" SMT is force disabled. Cannot be changed.
|
||||
"notsupported" SMT is not supported by the CPU
|
||||
|
||||
If control status is "forceoff" or "notsupported" writes
|
||||
are rejected.
|
||||
|
||||
@@ -17,6 +17,15 @@ etc.
|
||||
kernel-parameters
|
||||
devices
|
||||
|
||||
This section describes CPU vulnerabilities and provides an overview of the
|
||||
possible mitigations along with guidance for selecting mitigations if they
|
||||
are configurable at compile, boot or run time.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
l1tf
|
||||
|
||||
Here is a set of documents aimed at users who are trying to track down
|
||||
problems and bugs in particular.
|
||||
|
||||
|
||||
@@ -1967,10 +1967,84 @@
|
||||
(virtualized real and unpaged mode) on capable
|
||||
Intel chips. Default is 1 (enabled)
|
||||
|
||||
kvm-intel.vmentry_l1d_flush=[KVM,Intel] Mitigation for L1 Terminal Fault
|
||||
CVE-2018-3620.
|
||||
|
||||
Valid arguments: never, cond, always
|
||||
|
||||
always: L1D cache flush on every VMENTER.
|
||||
cond: Flush L1D on VMENTER only when the code between
|
||||
VMEXIT and VMENTER can leak host memory.
|
||||
never: Disables the mitigation
|
||||
|
||||
Default is cond (do L1 cache flush in specific instances)
|
||||
|
||||
kvm-intel.vpid= [KVM,Intel] Disable Virtual Processor Identification
|
||||
feature (tagged TLBs) on capable Intel chips.
|
||||
Default is 1 (enabled)
|
||||
|
||||
l1tf= [X86] Control mitigation of the L1TF vulnerability on
|
||||
affected CPUs
|
||||
|
||||
The kernel PTE inversion protection is unconditionally
|
||||
enabled and cannot be disabled.
|
||||
|
||||
full
|
||||
Provides all available mitigations for the
|
||||
L1TF vulnerability. Disables SMT and
|
||||
enables all mitigations in the
|
||||
hypervisors, i.e. unconditional L1D flush.
|
||||
|
||||
SMT control and L1D flush control via the
|
||||
sysfs interface is still possible after
|
||||
boot. Hypervisors will issue a warning
|
||||
when the first VM is started in a
|
||||
potentially insecure configuration,
|
||||
i.e. SMT enabled or L1D flush disabled.
|
||||
|
||||
full,force
|
||||
Same as 'full', but disables SMT and L1D
|
||||
flush runtime control. Implies the
|
||||
'nosmt=force' command line option.
|
||||
(i.e. sysfs control of SMT is disabled.)
|
||||
|
||||
flush
|
||||
Leaves SMT enabled and enables the default
|
||||
hypervisor mitigation, i.e. conditional
|
||||
L1D flush.
|
||||
|
||||
SMT control and L1D flush control via the
|
||||
sysfs interface is still possible after
|
||||
boot. Hypervisors will issue a warning
|
||||
when the first VM is started in a
|
||||
potentially insecure configuration,
|
||||
i.e. SMT enabled or L1D flush disabled.
|
||||
|
||||
flush,nosmt
|
||||
|
||||
Disables SMT and enables the default
|
||||
hypervisor mitigation.
|
||||
|
||||
SMT control and L1D flush control via the
|
||||
sysfs interface is still possible after
|
||||
boot. Hypervisors will issue a warning
|
||||
when the first VM is started in a
|
||||
potentially insecure configuration,
|
||||
i.e. SMT enabled or L1D flush disabled.
|
||||
|
||||
flush,nowarn
|
||||
Same as 'flush', but hypervisors will not
|
||||
warn when a VM is started in a potentially
|
||||
insecure configuration.
|
||||
|
||||
off
|
||||
Disables hypervisor mitigations and doesn't
|
||||
emit any warnings.
|
||||
|
||||
Default is 'flush'.
|
||||
|
||||
For details see: Documentation/admin-guide/l1tf.rst
|
||||
|
||||
l2cr= [PPC]
|
||||
|
||||
l3cr= [PPC]
|
||||
@@ -2687,6 +2761,10 @@
|
||||
nosmt [KNL,S390] Disable symmetric multithreading (SMT).
|
||||
Equivalent to smt=1.
|
||||
|
||||
[KNL,x86] Disable symmetric multithreading (SMT).
|
||||
nosmt=force: Force disable SMT, cannot be undone
|
||||
via the sysfs control file.
|
||||
|
||||
nospectre_v2 [X86] Disable all mitigations for the Spectre variant 2
|
||||
(indirect branch prediction) vulnerability. System may
|
||||
allow data leaks with this option, which is equivalent
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
L1TF - L1 Terminal Fault
|
||||
========================
|
||||
|
||||
L1 Terminal Fault is a hardware vulnerability which allows unprivileged
|
||||
speculative access to data which is available in the Level 1 Data Cache
|
||||
when the page table entry controlling the virtual address, which is used
|
||||
for the access, has the Present bit cleared or other reserved bits set.
|
||||
|
||||
Affected processors
|
||||
-------------------
|
||||
|
||||
This vulnerability affects a wide range of Intel processors. The
|
||||
vulnerability is not present on:
|
||||
|
||||
- Processors from AMD, Centaur and other non Intel vendors
|
||||
|
||||
- Older processor models, where the CPU family is < 6
|
||||
|
||||
- A range of Intel ATOM processors (Cedarview, Cloverview, Lincroft,
|
||||
Penwell, Pineview, Silvermont, Airmont, Merrifield)
|
||||
|
||||
- The Intel XEON PHI family
|
||||
|
||||
- Intel processors which have the ARCH_CAP_RDCL_NO bit set in the
|
||||
IA32_ARCH_CAPABILITIES MSR. If the bit is set the CPU is not affected
|
||||
by the Meltdown vulnerability either. These CPUs should become
|
||||
available by end of 2018.
|
||||
|
||||
Whether a processor is affected or not can be read out from the L1TF
|
||||
vulnerability file in sysfs. See :ref:`l1tf_sys_info`.
|
||||
|
||||
Related CVEs
|
||||
------------
|
||||
|
||||
The following CVE entries are related to the L1TF vulnerability:
|
||||
|
||||
============= ================= ==============================
|
||||
CVE-2018-3615 L1 Terminal Fault SGX related aspects
|
||||
CVE-2018-3620 L1 Terminal Fault OS, SMM related aspects
|
||||
CVE-2018-3646 L1 Terminal Fault Virtualization related aspects
|
||||
============= ================= ==============================
|
||||
|
||||
Problem
|
||||
-------
|
||||
|
||||
If an instruction accesses a virtual address for which the relevant page
|
||||
table entry (PTE) has the Present bit cleared or other reserved bits set,
|
||||
then speculative execution ignores the invalid PTE and loads the referenced
|
||||
data if it is present in the Level 1 Data Cache, as if the page referenced
|
||||
by the address bits in the PTE was still present and accessible.
|
||||
|
||||
While this is a purely speculative mechanism and the instruction will raise
|
||||
a page fault when it is retired eventually, the pure act of loading the
|
||||
data and making it available to other speculative instructions opens up the
|
||||
opportunity for side channel attacks to unprivileged malicious code,
|
||||
similar to the Meltdown attack.
|
||||
|
||||
While Meltdown breaks the user space to kernel space protection, L1TF
|
||||
allows to attack any physical memory address in the system and the attack
|
||||
works across all protection domains. It allows an attack of SGX and also
|
||||
works from inside virtual machines because the speculation bypasses the
|
||||
extended page table (EPT) protection mechanism.
|
||||
|
||||
|
||||
Attack scenarios
|
||||
----------------
|
||||
|
||||
1. Malicious user space
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Operating Systems store arbitrary information in the address bits of a
|
||||
PTE which is marked non present. This allows a malicious user space
|
||||
application to attack the physical memory to which these PTEs resolve.
|
||||
In some cases user-space can maliciously influence the information
|
||||
encoded in the address bits of the PTE, thus making attacks more
|
||||
deterministic and more practical.
|
||||
|
||||
The Linux kernel contains a mitigation for this attack vector, PTE
|
||||
inversion, which is permanently enabled and has no performance
|
||||
impact. The kernel ensures that the address bits of PTEs, which are not
|
||||
marked present, never point to cacheable physical memory space.
|
||||
|
||||
A system with an up to date kernel is protected against attacks from
|
||||
malicious user space applications.
|
||||
|
||||
2. Malicious guest in a virtual machine
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The fact that L1TF breaks all domain protections allows malicious guest
|
||||
OSes, which can control the PTEs directly, and malicious guest user
|
||||
space applications, which run on an unprotected guest kernel lacking the
|
||||
PTE inversion mitigation for L1TF, to attack physical host memory.
|
||||
|
||||
A special aspect of L1TF in the context of virtualization is symmetric
|
||||
multi threading (SMT). The Intel implementation of SMT is called
|
||||
HyperThreading. The fact that Hyperthreads on the affected processors
|
||||
share the L1 Data Cache (L1D) is important for this. As the flaw allows
|
||||
only to attack data which is present in L1D, a malicious guest running
|
||||
on one Hyperthread can attack the data which is brought into the L1D by
|
||||
the context which runs on the sibling Hyperthread of the same physical
|
||||
core. This context can be host OS, host user space or a different guest.
|
||||
|
||||
If the processor does not support Extended Page Tables, the attack is
|
||||
only possible, when the hypervisor does not sanitize the content of the
|
||||
effective (shadow) page tables.
|
||||
|
||||
While solutions exist to mitigate these attack vectors fully, these
|
||||
mitigations are not enabled by default in the Linux kernel because they
|
||||
can affect performance significantly. The kernel provides several
|
||||
mechanisms which can be utilized to address the problem depending on the
|
||||
deployment scenario. The mitigations, their protection scope and impact
|
||||
are described in the next sections.
|
||||
|
||||
The default mitigations and the rationale for choosing them are explained
|
||||
at the end of this document. See :ref:`default_mitigations`.
|
||||
|
||||
.. _l1tf_sys_info:
|
||||
|
||||
L1TF system information
|
||||
-----------------------
|
||||
|
||||
The Linux kernel provides a sysfs interface to enumerate the current L1TF
|
||||
status of the system: whether the system is vulnerable, and which
|
||||
mitigations are active. The relevant sysfs file is:
|
||||
|
||||
/sys/devices/system/cpu/vulnerabilities/l1tf
|
||||
|
||||
The possible values in this file are:
|
||||
|
||||
=========================== ===============================
|
||||
'Not affected' The processor is not vulnerable
|
||||
'Mitigation: PTE Inversion' The host protection is active
|
||||
=========================== ===============================
|
||||
|
||||
If KVM/VMX is enabled and the processor is vulnerable then the following
|
||||
information is appended to the 'Mitigation: PTE Inversion' part:
|
||||
|
||||
- SMT status:
|
||||
|
||||
===================== ================
|
||||
'VMX: SMT vulnerable' SMT is enabled
|
||||
'VMX: SMT disabled' SMT is disabled
|
||||
===================== ================
|
||||
|
||||
- L1D Flush mode:
|
||||
|
||||
================================ ====================================
|
||||
'L1D vulnerable' L1D flushing is disabled
|
||||
|
||||
'L1D conditional cache flushes' L1D flush is conditionally enabled
|
||||
|
||||
'L1D cache flushes' L1D flush is unconditionally enabled
|
||||
================================ ====================================
|
||||
|
||||
The resulting grade of protection is discussed in the following sections.
|
||||
|
||||
|
||||
Host mitigation mechanism
|
||||
-------------------------
|
||||
|
||||
The kernel is unconditionally protected against L1TF attacks from malicious
|
||||
user space running on the host.
|
||||
|
||||
|
||||
Guest mitigation mechanisms
|
||||
---------------------------
|
||||
|
||||
.. _l1d_flush:
|
||||
|
||||
1. L1D flush on VMENTER
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To make sure that a guest cannot attack data which is present in the L1D
|
||||
the hypervisor flushes the L1D before entering the guest.
|
||||
|
||||
Flushing the L1D evicts not only the data which should not be accessed
|
||||
by a potentially malicious guest, it also flushes the guest
|
||||
data. Flushing the L1D has a performance impact as the processor has to
|
||||
bring the flushed guest data back into the L1D. Depending on the
|
||||
frequency of VMEXIT/VMENTER and the type of computations in the guest
|
||||
performance degradation in the range of 1% to 50% has been observed. For
|
||||
scenarios where guest VMEXIT/VMENTER are rare the performance impact is
|
||||
minimal. Virtio and mechanisms like posted interrupts are designed to
|
||||
confine the VMEXITs to a bare minimum, but specific configurations and
|
||||
application scenarios might still suffer from a high VMEXIT rate.
|
||||
|
||||
The kernel provides two L1D flush modes:
|
||||
- conditional ('cond')
|
||||
- unconditional ('always')
|
||||
|
||||
The conditional mode avoids L1D flushing after VMEXITs which execute
|
||||
only audited code paths before the corresponding VMENTER. These code
|
||||
paths have been verified that they cannot expose secrets or other
|
||||
interesting data to an attacker, but they can leak information about the
|
||||
address space layout of the hypervisor.
|
||||
|
||||
Unconditional mode flushes L1D on all VMENTER invocations and provides
|
||||
maximum protection. It has a higher overhead than the conditional
|
||||
mode. The overhead cannot be quantified correctly as it depends on the
|
||||
workload scenario and the resulting number of VMEXITs.
|
||||
|
||||
The general recommendation is to enable L1D flush on VMENTER. The kernel
|
||||
defaults to conditional mode on affected processors.
|
||||
|
||||
**Note**, that L1D flush does not prevent the SMT problem because the
|
||||
sibling thread will also bring back its data into the L1D which makes it
|
||||
attackable again.
|
||||
|
||||
L1D flush can be controlled by the administrator via the kernel command
|
||||
line and sysfs control files. See :ref:`mitigation_control_command_line`
|
||||
and :ref:`mitigation_control_kvm`.
|
||||
|
||||
.. _guest_confinement:
|
||||
|
||||
2. Guest VCPU confinement to dedicated physical cores
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To address the SMT problem, it is possible to make a guest or a group of
|
||||
guests affine to one or more physical cores. The proper mechanism for
|
||||
that is to utilize exclusive cpusets to ensure that no other guest or
|
||||
host tasks can run on these cores.
|
||||
|
||||
If only a single guest or related guests run on sibling SMT threads on
|
||||
the same physical core then they can only attack their own memory and
|
||||
restricted parts of the host memory.
|
||||
|
||||
Host memory is attackable, when one of the sibling SMT threads runs in
|
||||
host OS (hypervisor) context and the other in guest context. The amount
|
||||
of valuable information from the host OS context depends on the context
|
||||
which the host OS executes, i.e. interrupts, soft interrupts and kernel
|
||||
threads. The amount of valuable data from these contexts cannot be
|
||||
declared as non-interesting for an attacker without deep inspection of
|
||||
the code.
|
||||
|
||||
**Note**, that assigning guests to a fixed set of physical cores affects
|
||||
the ability of the scheduler to do load balancing and might have
|
||||
negative effects on CPU utilization depending on the hosting
|
||||
scenario. Disabling SMT might be a viable alternative for particular
|
||||
scenarios.
|
||||
|
||||
For further information about confining guests to a single or to a group
|
||||
of cores consult the cpusets documentation:
|
||||
|
||||
https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt
|
||||
|
||||
.. _interrupt_isolation:
|
||||
|
||||
3. Interrupt affinity
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Interrupts can be made affine to logical CPUs. This is not universally
|
||||
true because there are types of interrupts which are truly per CPU
|
||||
interrupts, e.g. the local timer interrupt. Aside of that multi queue
|
||||
devices affine their interrupts to single CPUs or groups of CPUs per
|
||||
queue without allowing the administrator to control the affinities.
|
||||
|
||||
Moving the interrupts, which can be affinity controlled, away from CPUs
|
||||
which run untrusted guests, reduces the attack vector space.
|
||||
|
||||
Whether the interrupts with are affine to CPUs, which run untrusted
|
||||
guests, provide interesting data for an attacker depends on the system
|
||||
configuration and the scenarios which run on the system. While for some
|
||||
of the interrupts it can be assumed that they won't expose interesting
|
||||
information beyond exposing hints about the host OS memory layout, there
|
||||
is no way to make general assumptions.
|
||||
|
||||
Interrupt affinity can be controlled by the administrator via the
|
||||
/proc/irq/$NR/smp_affinity[_list] files. Limited documentation is
|
||||
available at:
|
||||
|
||||
https://www.kernel.org/doc/Documentation/IRQ-affinity.txt
|
||||
|
||||
.. _smt_control:
|
||||
|
||||
4. SMT control
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
To prevent the SMT issues of L1TF it might be necessary to disable SMT
|
||||
completely. Disabling SMT can have a significant performance impact, but
|
||||
the impact depends on the hosting scenario and the type of workloads.
|
||||
The impact of disabling SMT needs also to be weighted against the impact
|
||||
of other mitigation solutions like confining guests to dedicated cores.
|
||||
|
||||
The kernel provides a sysfs interface to retrieve the status of SMT and
|
||||
to control it. It also provides a kernel command line interface to
|
||||
control SMT.
|
||||
|
||||
The kernel command line interface consists of the following options:
|
||||
|
||||
=========== ==========================================================
|
||||
nosmt Affects the bring up of the secondary CPUs during boot. The
|
||||
kernel tries to bring all present CPUs online during the
|
||||
boot process. "nosmt" makes sure that from each physical
|
||||
core only one - the so called primary (hyper) thread is
|
||||
activated. Due to a design flaw of Intel processors related
|
||||
to Machine Check Exceptions the non primary siblings have
|
||||
to be brought up at least partially and are then shut down
|
||||
again. "nosmt" can be undone via the sysfs interface.
|
||||
|
||||
nosmt=force Has the same effect as "nosmt" but it does not allow to
|
||||
undo the SMT disable via the sysfs interface.
|
||||
=========== ==========================================================
|
||||
|
||||
The sysfs interface provides two files:
|
||||
|
||||
- /sys/devices/system/cpu/smt/control
|
||||
- /sys/devices/system/cpu/smt/active
|
||||
|
||||
/sys/devices/system/cpu/smt/control:
|
||||
|
||||
This file allows to read out the SMT control state and provides the
|
||||
ability to disable or (re)enable SMT. The possible states are:
|
||||
|
||||
============== ===================================================
|
||||
on SMT is supported by the CPU and enabled. All
|
||||
logical CPUs can be onlined and offlined without
|
||||
restrictions.
|
||||
|
||||
off SMT is supported by the CPU and disabled. Only
|
||||
the so called primary SMT threads can be onlined
|
||||
and offlined without restrictions. An attempt to
|
||||
online a non-primary sibling is rejected
|
||||
|
||||
forceoff Same as 'off' but the state cannot be controlled.
|
||||
Attempts to write to the control file are rejected.
|
||||
|
||||
notsupported The processor does not support SMT. It's therefore
|
||||
not affected by the SMT implications of L1TF.
|
||||
Attempts to write to the control file are rejected.
|
||||
============== ===================================================
|
||||
|
||||
The possible states which can be written into this file to control SMT
|
||||
state are:
|
||||
|
||||
- on
|
||||
- off
|
||||
- forceoff
|
||||
|
||||
/sys/devices/system/cpu/smt/active:
|
||||
|
||||
This file reports whether SMT is enabled and active, i.e. if on any
|
||||
physical core two or more sibling threads are online.
|
||||
|
||||
SMT control is also possible at boot time via the l1tf kernel command
|
||||
line parameter in combination with L1D flush control. See
|
||||
:ref:`mitigation_control_command_line`.
|
||||
|
||||
5. Disabling EPT
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Disabling EPT for virtual machines provides full mitigation for L1TF even
|
||||
with SMT enabled, because the effective page tables for guests are
|
||||
managed and sanitized by the hypervisor. Though disabling EPT has a
|
||||
significant performance impact especially when the Meltdown mitigation
|
||||
KPTI is enabled.
|
||||
|
||||
EPT can be disabled in the hypervisor via the 'kvm-intel.ept' parameter.
|
||||
|
||||
There is ongoing research and development for new mitigation mechanisms to
|
||||
address the performance impact of disabling SMT or EPT.
|
||||
|
||||
.. _mitigation_control_command_line:
|
||||
|
||||
Mitigation control on the kernel command line
|
||||
---------------------------------------------
|
||||
|
||||
The kernel command line allows to control the L1TF mitigations at boot
|
||||
time with the option "l1tf=". The valid arguments for this option are:
|
||||
|
||||
============ =============================================================
|
||||
full Provides all available mitigations for the L1TF
|
||||
vulnerability. Disables SMT and enables all mitigations in
|
||||
the hypervisors, i.e. unconditional L1D flushing
|
||||
|
||||
SMT control and L1D flush control via the sysfs interface
|
||||
is still possible after boot. Hypervisors will issue a
|
||||
warning when the first VM is started in a potentially
|
||||
insecure configuration, i.e. SMT enabled or L1D flush
|
||||
disabled.
|
||||
|
||||
full,force Same as 'full', but disables SMT and L1D flush runtime
|
||||
control. Implies the 'nosmt=force' command line option.
|
||||
(i.e. sysfs control of SMT is disabled.)
|
||||
|
||||
flush Leaves SMT enabled and enables the default hypervisor
|
||||
mitigation, i.e. conditional L1D flushing
|
||||
|
||||
SMT control and L1D flush control via the sysfs interface
|
||||
is still possible after boot. Hypervisors will issue a
|
||||
warning when the first VM is started in a potentially
|
||||
insecure configuration, i.e. SMT enabled or L1D flush
|
||||
disabled.
|
||||
|
||||
flush,nosmt Disables SMT and enables the default hypervisor mitigation,
|
||||
i.e. conditional L1D flushing.
|
||||
|
||||
SMT control and L1D flush control via the sysfs interface
|
||||
is still possible after boot. Hypervisors will issue a
|
||||
warning when the first VM is started in a potentially
|
||||
insecure configuration, i.e. SMT enabled or L1D flush
|
||||
disabled.
|
||||
|
||||
flush,nowarn Same as 'flush', but hypervisors will not warn when a VM is
|
||||
started in a potentially insecure configuration.
|
||||
|
||||
off Disables hypervisor mitigations and doesn't emit any
|
||||
warnings.
|
||||
============ =============================================================
|
||||
|
||||
The default is 'flush'. For details about L1D flushing see :ref:`l1d_flush`.
|
||||
|
||||
|
||||
.. _mitigation_control_kvm:
|
||||
|
||||
Mitigation control for KVM - module parameter
|
||||
-------------------------------------------------------------
|
||||
|
||||
The KVM hypervisor mitigation mechanism, flushing the L1D cache when
|
||||
entering a guest, can be controlled with a module parameter.
|
||||
|
||||
The option/parameter is "kvm-intel.vmentry_l1d_flush=". It takes the
|
||||
following arguments:
|
||||
|
||||
============ ==============================================================
|
||||
always L1D cache flush on every VMENTER.
|
||||
|
||||
cond Flush L1D on VMENTER only when the code between VMEXIT and
|
||||
VMENTER can leak host memory which is considered
|
||||
interesting for an attacker. This still can leak host memory
|
||||
which allows e.g. to determine the hosts address space layout.
|
||||
|
||||
never Disables the mitigation
|
||||
============ ==============================================================
|
||||
|
||||
The parameter can be provided on the kernel command line, as a module
|
||||
parameter when loading the modules and at runtime modified via the sysfs
|
||||
file:
|
||||
|
||||
/sys/module/kvm_intel/parameters/vmentry_l1d_flush
|
||||
|
||||
The default is 'cond'. If 'l1tf=full,force' is given on the kernel command
|
||||
line, then 'always' is enforced and the kvm-intel.vmentry_l1d_flush
|
||||
module parameter is ignored and writes to the sysfs file are rejected.
|
||||
|
||||
|
||||
Mitigation selection guide
|
||||
--------------------------
|
||||
|
||||
1. No virtualization in use
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The system is protected by the kernel unconditionally and no further
|
||||
action is required.
|
||||
|
||||
2. Virtualization with trusted guests
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the guest comes from a trusted source and the guest OS kernel is
|
||||
guaranteed to have the L1TF mitigations in place the system is fully
|
||||
protected against L1TF and no further action is required.
|
||||
|
||||
To avoid the overhead of the default L1D flushing on VMENTER the
|
||||
administrator can disable the flushing via the kernel command line and
|
||||
sysfs control files. See :ref:`mitigation_control_command_line` and
|
||||
:ref:`mitigation_control_kvm`.
|
||||
|
||||
|
||||
3. Virtualization with untrusted guests
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
3.1. SMT not supported or disabled
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
If SMT is not supported by the processor or disabled in the BIOS or by
|
||||
the kernel, it's only required to enforce L1D flushing on VMENTER.
|
||||
|
||||
Conditional L1D flushing is the default behaviour and can be tuned. See
|
||||
:ref:`mitigation_control_command_line` and :ref:`mitigation_control_kvm`.
|
||||
|
||||
3.2. EPT not supported or disabled
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
If EPT is not supported by the processor or disabled in the hypervisor,
|
||||
the system is fully protected. SMT can stay enabled and L1D flushing on
|
||||
VMENTER is not required.
|
||||
|
||||
EPT can be disabled in the hypervisor via the 'kvm-intel.ept' parameter.
|
||||
|
||||
3.3. SMT and EPT supported and active
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
If SMT and EPT are supported and active then various degrees of
|
||||
mitigations can be employed:
|
||||
|
||||
- L1D flushing on VMENTER:
|
||||
|
||||
L1D flushing on VMENTER is the minimal protection requirement, but it
|
||||
is only potent in combination with other mitigation methods.
|
||||
|
||||
Conditional L1D flushing is the default behaviour and can be tuned. See
|
||||
:ref:`mitigation_control_command_line` and :ref:`mitigation_control_kvm`.
|
||||
|
||||
- Guest confinement:
|
||||
|
||||
Confinement of guests to a single or a group of physical cores which
|
||||
are not running any other processes, can reduce the attack surface
|
||||
significantly, but interrupts, soft interrupts and kernel threads can
|
||||
still expose valuable data to a potential attacker. See
|
||||
:ref:`guest_confinement`.
|
||||
|
||||
- Interrupt isolation:
|
||||
|
||||
Isolating the guest CPUs from interrupts can reduce the attack surface
|
||||
further, but still allows a malicious guest to explore a limited amount
|
||||
of host physical memory. This can at least be used to gain knowledge
|
||||
about the host address space layout. The interrupts which have a fixed
|
||||
affinity to the CPUs which run the untrusted guests can depending on
|
||||
the scenario still trigger soft interrupts and schedule kernel threads
|
||||
which might expose valuable information. See
|
||||
:ref:`interrupt_isolation`.
|
||||
|
||||
The above three mitigation methods combined can provide protection to a
|
||||
certain degree, but the risk of the remaining attack surface has to be
|
||||
carefully analyzed. For full protection the following methods are
|
||||
available:
|
||||
|
||||
- Disabling SMT:
|
||||
|
||||
Disabling SMT and enforcing the L1D flushing provides the maximum
|
||||
amount of protection. This mitigation is not depending on any of the
|
||||
above mitigation methods.
|
||||
|
||||
SMT control and L1D flushing can be tuned by the command line
|
||||
parameters 'nosmt', 'l1tf', 'kvm-intel.vmentry_l1d_flush' and at run
|
||||
time with the matching sysfs control files. See :ref:`smt_control`,
|
||||
:ref:`mitigation_control_command_line` and
|
||||
:ref:`mitigation_control_kvm`.
|
||||
|
||||
- Disabling EPT:
|
||||
|
||||
Disabling EPT provides the maximum amount of protection as well. It is
|
||||
not depending on any of the above mitigation methods. SMT can stay
|
||||
enabled and L1D flushing is not required, but the performance impact is
|
||||
significant.
|
||||
|
||||
EPT can be disabled in the hypervisor via the 'kvm-intel.ept'
|
||||
parameter.
|
||||
|
||||
3.4. Nested virtual machines
|
||||
""""""""""""""""""""""""""""
|
||||
|
||||
When nested virtualization is in use, three operating systems are involved:
|
||||
the bare metal hypervisor, the nested hypervisor and the nested virtual
|
||||
machine. VMENTER operations from the nested hypervisor into the nested
|
||||
guest will always be processed by the bare metal hypervisor. If KVM is the
|
||||
bare metal hypervisor it wiil:
|
||||
|
||||
- Flush the L1D cache on every switch from the nested hypervisor to the
|
||||
nested virtual machine, so that the nested hypervisor's secrets are not
|
||||
exposed to the nested virtual machine;
|
||||
|
||||
- Flush the L1D cache on every switch from the nested virtual machine to
|
||||
the nested hypervisor; this is a complex operation, and flushing the L1D
|
||||
cache avoids that the bare metal hypervisor's secrets are exposed to the
|
||||
nested virtual machine;
|
||||
|
||||
- Instruct the nested hypervisor to not perform any L1D cache flush. This
|
||||
is an optimization to avoid double L1D flushing.
|
||||
|
||||
|
||||
.. _default_mitigations:
|
||||
|
||||
Default mitigations
|
||||
-------------------
|
||||
|
||||
The kernel default mitigations for vulnerable processors are:
|
||||
|
||||
- PTE inversion to protect against malicious user space. This is done
|
||||
unconditionally and cannot be controlled.
|
||||
|
||||
- L1D conditional flushing on VMENTER when EPT is enabled for
|
||||
a guest.
|
||||
|
||||
The kernel does not by default enforce the disabling of SMT, which leaves
|
||||
SMT systems vulnerable when running untrusted guests with EPT enabled.
|
||||
|
||||
The rationale for this choice is:
|
||||
|
||||
- Force disabling SMT can break existing setups, especially with
|
||||
unattended updates.
|
||||
|
||||
- If regular users run untrusted guests on their machine, then L1TF is
|
||||
just an add on to other malware which might be embedded in an untrusted
|
||||
guest, e.g. spam-bots or attacks on the local network.
|
||||
|
||||
There is no technical way to prevent a user from running untrusted code
|
||||
on their machines blindly.
|
||||
|
||||
- It's technically extremely unlikely and from today's knowledge even
|
||||
impossible that L1TF can be exploited via the most popular attack
|
||||
mechanisms like JavaScript because these mechanisms have no way to
|
||||
control PTEs. If this would be possible and not other mitigation would
|
||||
be possible, then the default might be different.
|
||||
|
||||
- The administrators of cloud and hosting setups have to carefully
|
||||
analyze the risk for their scenarios and make the appropriate
|
||||
mitigation choices, which might even vary across their deployed
|
||||
machines and also result in other changes of their overall setup.
|
||||
There is no way for the kernel to provide a sensible default for this
|
||||
kind of scenarios.
|
||||
@@ -35,7 +35,7 @@ binutils 2.20 ld -v
|
||||
flex 2.5.35 flex --version
|
||||
bison 2.0 bison --version
|
||||
util-linux 2.10o fdformat --version
|
||||
module-init-tools 0.9.10 depmod -V
|
||||
kmod 13 depmod -V
|
||||
e2fsprogs 1.41.4 e2fsck -V
|
||||
jfsutils 1.1.3 fsck.jfs -V
|
||||
reiserfsprogs 3.6.3 reiserfsck -V
|
||||
@@ -156,12 +156,6 @@ is not build with ``CONFIG_KALLSYMS`` and you have no way to rebuild and
|
||||
reproduce the Oops with that option, then you can still decode that Oops
|
||||
with ksymoops.
|
||||
|
||||
Module-Init-Tools
|
||||
-----------------
|
||||
|
||||
A new module loader is now in the kernel that requires ``module-init-tools``
|
||||
to use. It is backward compatible with the 2.4.x series kernels.
|
||||
|
||||
Mkinitrd
|
||||
--------
|
||||
|
||||
@@ -371,16 +365,17 @@ Util-linux
|
||||
|
||||
- <https://www.kernel.org/pub/linux/utils/util-linux/>
|
||||
|
||||
Kmod
|
||||
----
|
||||
|
||||
- <https://www.kernel.org/pub/linux/utils/kernel/kmod/>
|
||||
- <https://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git>
|
||||
|
||||
Ksymoops
|
||||
--------
|
||||
|
||||
- <https://www.kernel.org/pub/linux/utils/kernel/ksymoops/v2.4/>
|
||||
|
||||
Module-Init-Tools
|
||||
-----------------
|
||||
|
||||
- <https://www.kernel.org/pub/linux/utils/kernel/module-init-tools/>
|
||||
|
||||
Mkinitrd
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
VERSION = 4
|
||||
PATCHLEVEL = 18
|
||||
SUBLEVEL = 0
|
||||
SUBLEVEL = 2
|
||||
EXTRAVERSION =
|
||||
NAME = Merciless Moray
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ config KEXEC_CORE
|
||||
config HAVE_IMA_KEXEC
|
||||
bool
|
||||
|
||||
config HOTPLUG_SMT
|
||||
bool
|
||||
|
||||
config OPROFILE
|
||||
tristate "OProfile system profiling"
|
||||
depends on PROFILING
|
||||
|
||||
+2
-2
@@ -977,12 +977,12 @@ int pmd_clear_huge(pmd_t *pmdp)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pud_free_pmd_page(pud_t *pud)
|
||||
int pud_free_pmd_page(pud_t *pud, unsigned long addr)
|
||||
{
|
||||
return pud_none(*pud);
|
||||
}
|
||||
|
||||
int pmd_free_pte_page(pmd_t *pmd)
|
||||
int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
|
||||
{
|
||||
return pmd_none(*pmd);
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ config X86
|
||||
select HAVE_SYSCALL_TRACEPOINTS
|
||||
select HAVE_UNSTABLE_SCHED_CLOCK
|
||||
select HAVE_USER_RETURN_NOTIFIER
|
||||
select HOTPLUG_SMT if SMP
|
||||
select IRQ_FORCED_THREADING
|
||||
select NEED_SG_DMA_LENGTH
|
||||
select PCI_LOCKLESS_CONFIG
|
||||
|
||||
@@ -265,7 +265,7 @@ ENTRY(sha256_mb_mgr_get_comp_job_avx2)
|
||||
vpinsrd $1, _args_digest+1*32(state, idx, 4), %xmm0, %xmm0
|
||||
vpinsrd $2, _args_digest+2*32(state, idx, 4), %xmm0, %xmm0
|
||||
vpinsrd $3, _args_digest+3*32(state, idx, 4), %xmm0, %xmm0
|
||||
vmovd _args_digest(state , idx, 4) , %xmm0
|
||||
vmovd _args_digest+4*32(state, idx, 4), %xmm1
|
||||
vpinsrd $1, _args_digest+5*32(state, idx, 4), %xmm1, %xmm1
|
||||
vpinsrd $2, _args_digest+6*32(state, idx, 4), %xmm1, %xmm1
|
||||
vpinsrd $3, _args_digest+7*32(state, idx, 4), %xmm1, %xmm1
|
||||
|
||||
@@ -95,6 +95,11 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
|
||||
} else {
|
||||
for_each_cpu(cpu, cpus) {
|
||||
vcpu = hv_cpu_number_to_vp_number(cpu);
|
||||
if (vcpu == VP_INVAL) {
|
||||
local_irq_restore(flags);
|
||||
goto do_native;
|
||||
}
|
||||
|
||||
if (vcpu >= 64)
|
||||
goto do_native;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/mpspec.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/hardirq.h>
|
||||
|
||||
#define ARCH_APICTIMER_STOPS_ON_C3 1
|
||||
|
||||
@@ -502,12 +503,19 @@ extern int default_check_phys_apicid_present(int phys_apicid);
|
||||
|
||||
#endif /* CONFIG_X86_LOCAL_APIC */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
bool apic_id_is_primary_thread(unsigned int id);
|
||||
#else
|
||||
static inline bool apic_id_is_primary_thread(unsigned int id) { return false; }
|
||||
#endif
|
||||
|
||||
extern void irq_enter(void);
|
||||
extern void irq_exit(void);
|
||||
|
||||
static inline void entering_irq(void)
|
||||
{
|
||||
irq_enter();
|
||||
kvm_set_cpu_l1tf_flush_l1d();
|
||||
}
|
||||
|
||||
static inline void entering_ack_irq(void)
|
||||
@@ -520,6 +528,7 @@ static inline void ipi_entering_ack_irq(void)
|
||||
{
|
||||
irq_enter();
|
||||
ack_APIC_irq();
|
||||
kvm_set_cpu_l1tf_flush_l1d();
|
||||
}
|
||||
|
||||
static inline void exiting_irq(void)
|
||||
|
||||
@@ -219,6 +219,7 @@
|
||||
#define X86_FEATURE_IBPB ( 7*32+26) /* Indirect Branch Prediction Barrier */
|
||||
#define X86_FEATURE_STIBP ( 7*32+27) /* Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_ZEN ( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */
|
||||
#define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */
|
||||
|
||||
/* Virtualization flags: Linux defined, word 8 */
|
||||
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
|
||||
@@ -341,6 +342,7 @@
|
||||
#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
|
||||
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
|
||||
#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_FLUSH_L1D (18*32+28) /* Flush L1D cache */
|
||||
#define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */
|
||||
#define X86_FEATURE_SPEC_CTRL_SSBD (18*32+31) /* "" Speculative Store Bypass Disable */
|
||||
|
||||
@@ -373,5 +375,6 @@
|
||||
#define X86_BUG_SPECTRE_V1 X86_BUG(15) /* CPU is affected by Spectre variant 1 attack with conditional branches */
|
||||
#define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */
|
||||
#define X86_BUG_SPEC_STORE_BYPASS X86_BUG(17) /* CPU is affected by speculative store bypass attack */
|
||||
#define X86_BUG_L1TF X86_BUG(18) /* CPU is affected by L1 Terminal Fault */
|
||||
|
||||
#endif /* _ASM_X86_CPUFEATURES_H */
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
static __always_inline __init void *dmi_alloc(unsigned len)
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#define _ASM_X86_HARDIRQ_H
|
||||
|
||||
#include <linux/threads.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned int __softirq_pending;
|
||||
u16 __softirq_pending;
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
u8 kvm_cpu_l1tf_flush_l1d;
|
||||
#endif
|
||||
unsigned int __nmi_count; /* arch dependent */
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
unsigned int apic_timer_irqs; /* arch dependent */
|
||||
@@ -58,4 +60,24 @@ extern u64 arch_irq_stat_cpu(unsigned int cpu);
|
||||
extern u64 arch_irq_stat(void);
|
||||
#define arch_irq_stat arch_irq_stat
|
||||
|
||||
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
static inline void kvm_set_cpu_l1tf_flush_l1d(void)
|
||||
{
|
||||
__this_cpu_write(irq_stat.kvm_cpu_l1tf_flush_l1d, 1);
|
||||
}
|
||||
|
||||
static inline void kvm_clear_cpu_l1tf_flush_l1d(void)
|
||||
{
|
||||
__this_cpu_write(irq_stat.kvm_cpu_l1tf_flush_l1d, 0);
|
||||
}
|
||||
|
||||
static inline bool kvm_get_cpu_l1tf_flush_l1d(void)
|
||||
{
|
||||
return __this_cpu_read(irq_stat.kvm_cpu_l1tf_flush_l1d);
|
||||
}
|
||||
#else /* !IS_ENABLED(CONFIG_KVM_INTEL) */
|
||||
static inline void kvm_set_cpu_l1tf_flush_l1d(void) { }
|
||||
#endif /* IS_ENABLED(CONFIG_KVM_INTEL) */
|
||||
|
||||
#endif /* _ASM_X86_HARDIRQ_H */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define _ASM_X86_I8259_H
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
extern unsigned int cached_irq_mask;
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
* Interrupt control:
|
||||
*/
|
||||
|
||||
/* Declaration required for gcc < 4.9 to prevent -Werror=missing-prototypes */
|
||||
extern inline unsigned long native_save_fl(void);
|
||||
extern inline unsigned long native_save_fl(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_para.h>
|
||||
@@ -713,6 +714,9 @@ struct kvm_vcpu_arch {
|
||||
|
||||
/* be preempted when it's in kernel-mode(cpl=0) */
|
||||
bool preempted_in_kernel;
|
||||
|
||||
/* Flush the L1 Data cache for L1TF mitigation on VMENTER */
|
||||
bool l1tf_flush_l1d;
|
||||
};
|
||||
|
||||
struct kvm_lpage_info {
|
||||
@@ -881,6 +885,7 @@ struct kvm_vcpu_stat {
|
||||
u64 signal_exits;
|
||||
u64 irq_window_exits;
|
||||
u64 nmi_window_exits;
|
||||
u64 l1d_flush;
|
||||
u64 halt_exits;
|
||||
u64 halt_successful_poll;
|
||||
u64 halt_attempted_poll;
|
||||
@@ -1413,6 +1418,7 @@ int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
|
||||
void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event);
|
||||
void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu);
|
||||
|
||||
u64 kvm_get_arch_capabilities(void);
|
||||
void kvm_define_shared_msr(unsigned index, u32 msr);
|
||||
int kvm_set_shared_msr(unsigned index, u64 val, u64 mask);
|
||||
|
||||
|
||||
@@ -70,12 +70,19 @@
|
||||
#define MSR_IA32_ARCH_CAPABILITIES 0x0000010a
|
||||
#define ARCH_CAP_RDCL_NO (1 << 0) /* Not susceptible to Meltdown */
|
||||
#define ARCH_CAP_IBRS_ALL (1 << 1) /* Enhanced IBRS support */
|
||||
#define ARCH_CAP_SKIP_VMENTRY_L1DFLUSH (1 << 3) /* Skip L1D flush on vmentry */
|
||||
#define ARCH_CAP_SSB_NO (1 << 4) /*
|
||||
* Not susceptible to Speculative Store Bypass
|
||||
* attack, so no Speculative Store Bypass
|
||||
* control required.
|
||||
*/
|
||||
|
||||
#define MSR_IA32_FLUSH_CMD 0x0000010b
|
||||
#define L1D_FLUSH (1 << 0) /*
|
||||
* Writeback and invalidate the
|
||||
* L1 data cache.
|
||||
*/
|
||||
|
||||
#define MSR_IA32_BBL_CR_CTL 0x00000119
|
||||
#define MSR_IA32_BBL_CR_CTL3 0x0000011e
|
||||
|
||||
|
||||
@@ -29,8 +29,13 @@
|
||||
#define N_EXCEPTION_STACKS 1
|
||||
|
||||
#ifdef CONFIG_X86_PAE
|
||||
/* 44=32+12, the limit we can fit into an unsigned long pfn */
|
||||
#define __PHYSICAL_MASK_SHIFT 44
|
||||
/*
|
||||
* This is beyond the 44 bit limit imposed by the 32bit long pfns,
|
||||
* but we need the full mask to make sure inverted PROT_NONE
|
||||
* entries have all the host bits set in a guest.
|
||||
* The real limit is still 44 bits.
|
||||
*/
|
||||
#define __PHYSICAL_MASK_SHIFT 52
|
||||
#define __VIRTUAL_MASK_SHIFT 32
|
||||
|
||||
#else /* !CONFIG_X86_PAE */
|
||||
|
||||
@@ -95,4 +95,21 @@ static inline unsigned long pte_bitop(unsigned long value, unsigned int rightshi
|
||||
#define __pte_to_swp_entry(pte) ((swp_entry_t) { (pte).pte_low })
|
||||
#define __swp_entry_to_pte(x) ((pte_t) { .pte = (x).val })
|
||||
|
||||
/* No inverted PFNs on 2 level page tables */
|
||||
|
||||
static inline u64 protnone_mask(u64 val)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u64 flip_protnone_guard(u64 oldval, u64 val, u64 mask)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline bool __pte_needs_invert(u64 val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* _ASM_X86_PGTABLE_2LEVEL_H */
|
||||
|
||||
@@ -241,12 +241,43 @@ static inline pud_t native_pudp_get_and_clear(pud_t *pudp)
|
||||
#endif
|
||||
|
||||
/* Encode and de-code a swap entry */
|
||||
#define SWP_TYPE_BITS 5
|
||||
|
||||
#define SWP_OFFSET_FIRST_BIT (_PAGE_BIT_PROTNONE + 1)
|
||||
|
||||
/* We always extract/encode the offset by shifting it all the way up, and then down again */
|
||||
#define SWP_OFFSET_SHIFT (SWP_OFFSET_FIRST_BIT + SWP_TYPE_BITS)
|
||||
|
||||
#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > 5)
|
||||
#define __swp_type(x) (((x).val) & 0x1f)
|
||||
#define __swp_offset(x) ((x).val >> 5)
|
||||
#define __swp_entry(type, offset) ((swp_entry_t){(type) | (offset) << 5})
|
||||
#define __pte_to_swp_entry(pte) ((swp_entry_t){ (pte).pte_high })
|
||||
#define __swp_entry_to_pte(x) ((pte_t){ { .pte_high = (x).val } })
|
||||
|
||||
/*
|
||||
* Normally, __swp_entry() converts from arch-independent swp_entry_t to
|
||||
* arch-dependent swp_entry_t, and __swp_entry_to_pte() just stores the result
|
||||
* to pte. But here we have 32bit swp_entry_t and 64bit pte, and need to use the
|
||||
* whole 64 bits. Thus, we shift the "real" arch-dependent conversion to
|
||||
* __swp_entry_to_pte() through the following helper macro based on 64bit
|
||||
* __swp_entry().
|
||||
*/
|
||||
#define __swp_pteval_entry(type, offset) ((pteval_t) { \
|
||||
(~(pteval_t)(offset) << SWP_OFFSET_SHIFT >> SWP_TYPE_BITS) \
|
||||
| ((pteval_t)(type) << (64 - SWP_TYPE_BITS)) })
|
||||
|
||||
#define __swp_entry_to_pte(x) ((pte_t){ .pte = \
|
||||
__swp_pteval_entry(__swp_type(x), __swp_offset(x)) })
|
||||
/*
|
||||
* Analogically, __pte_to_swp_entry() doesn't just extract the arch-dependent
|
||||
* swp_entry_t, but also has to convert it from 64bit to the 32bit
|
||||
* intermediate representation, using the following macros based on 64bit
|
||||
* __swp_type() and __swp_offset().
|
||||
*/
|
||||
#define __pteval_swp_type(x) ((unsigned long)((x).pte >> (64 - SWP_TYPE_BITS)))
|
||||
#define __pteval_swp_offset(x) ((unsigned long)(~((x).pte) << SWP_TYPE_BITS >> SWP_OFFSET_SHIFT))
|
||||
|
||||
#define __pte_to_swp_entry(pte) (__swp_entry(__pteval_swp_type(pte), \
|
||||
__pteval_swp_offset(pte)))
|
||||
|
||||
#define gup_get_pte gup_get_pte
|
||||
/*
|
||||
@@ -295,4 +326,6 @@ static inline pte_t gup_get_pte(pte_t *ptep)
|
||||
return pte;
|
||||
}
|
||||
|
||||
#include <asm/pgtable-invert.h>
|
||||
|
||||
#endif /* _ASM_X86_PGTABLE_3LEVEL_H */
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_PGTABLE_INVERT_H
|
||||
#define _ASM_PGTABLE_INVERT_H 1
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
static inline bool __pte_needs_invert(u64 val)
|
||||
{
|
||||
return !(val & _PAGE_PRESENT);
|
||||
}
|
||||
|
||||
/* Get a mask to xor with the page table entry to get the correct pfn. */
|
||||
static inline u64 protnone_mask(u64 val)
|
||||
{
|
||||
return __pte_needs_invert(val) ? ~0ull : 0;
|
||||
}
|
||||
|
||||
static inline u64 flip_protnone_guard(u64 oldval, u64 val, u64 mask)
|
||||
{
|
||||
/*
|
||||
* When a PTE transitions from NONE to !NONE or vice-versa
|
||||
* invert the PFN part to stop speculation.
|
||||
* pte_pfn undoes this when needed.
|
||||
*/
|
||||
if (__pte_needs_invert(oldval) != __pte_needs_invert(val))
|
||||
val = (val & ~mask) | (~val & mask);
|
||||
return val;
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif
|
||||
@@ -185,19 +185,29 @@ static inline int pte_special(pte_t pte)
|
||||
return pte_flags(pte) & _PAGE_SPECIAL;
|
||||
}
|
||||
|
||||
/* Entries that were set to PROT_NONE are inverted */
|
||||
|
||||
static inline u64 protnone_mask(u64 val);
|
||||
|
||||
static inline unsigned long pte_pfn(pte_t pte)
|
||||
{
|
||||
return (pte_val(pte) & PTE_PFN_MASK) >> PAGE_SHIFT;
|
||||
phys_addr_t pfn = pte_val(pte);
|
||||
pfn ^= protnone_mask(pfn);
|
||||
return (pfn & PTE_PFN_MASK) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static inline unsigned long pmd_pfn(pmd_t pmd)
|
||||
{
|
||||
return (pmd_val(pmd) & pmd_pfn_mask(pmd)) >> PAGE_SHIFT;
|
||||
phys_addr_t pfn = pmd_val(pmd);
|
||||
pfn ^= protnone_mask(pfn);
|
||||
return (pfn & pmd_pfn_mask(pmd)) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static inline unsigned long pud_pfn(pud_t pud)
|
||||
{
|
||||
return (pud_val(pud) & pud_pfn_mask(pud)) >> PAGE_SHIFT;
|
||||
phys_addr_t pfn = pud_val(pud);
|
||||
pfn ^= protnone_mask(pfn);
|
||||
return (pfn & pud_pfn_mask(pud)) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static inline unsigned long p4d_pfn(p4d_t p4d)
|
||||
@@ -400,11 +410,6 @@ static inline pmd_t pmd_mkwrite(pmd_t pmd)
|
||||
return pmd_set_flags(pmd, _PAGE_RW);
|
||||
}
|
||||
|
||||
static inline pmd_t pmd_mknotpresent(pmd_t pmd)
|
||||
{
|
||||
return pmd_clear_flags(pmd, _PAGE_PRESENT | _PAGE_PROTNONE);
|
||||
}
|
||||
|
||||
static inline pud_t pud_set_flags(pud_t pud, pudval_t set)
|
||||
{
|
||||
pudval_t v = native_pud_val(pud);
|
||||
@@ -459,11 +464,6 @@ static inline pud_t pud_mkwrite(pud_t pud)
|
||||
return pud_set_flags(pud, _PAGE_RW);
|
||||
}
|
||||
|
||||
static inline pud_t pud_mknotpresent(pud_t pud)
|
||||
{
|
||||
return pud_clear_flags(pud, _PAGE_PRESENT | _PAGE_PROTNONE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_ARCH_SOFT_DIRTY
|
||||
static inline int pte_soft_dirty(pte_t pte)
|
||||
{
|
||||
@@ -545,25 +545,45 @@ static inline pgprotval_t check_pgprot(pgprot_t pgprot)
|
||||
|
||||
static inline pte_t pfn_pte(unsigned long page_nr, pgprot_t pgprot)
|
||||
{
|
||||
return __pte(((phys_addr_t)page_nr << PAGE_SHIFT) |
|
||||
check_pgprot(pgprot));
|
||||
phys_addr_t pfn = (phys_addr_t)page_nr << PAGE_SHIFT;
|
||||
pfn ^= protnone_mask(pgprot_val(pgprot));
|
||||
pfn &= PTE_PFN_MASK;
|
||||
return __pte(pfn | check_pgprot(pgprot));
|
||||
}
|
||||
|
||||
static inline pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot)
|
||||
{
|
||||
return __pmd(((phys_addr_t)page_nr << PAGE_SHIFT) |
|
||||
check_pgprot(pgprot));
|
||||
phys_addr_t pfn = (phys_addr_t)page_nr << PAGE_SHIFT;
|
||||
pfn ^= protnone_mask(pgprot_val(pgprot));
|
||||
pfn &= PHYSICAL_PMD_PAGE_MASK;
|
||||
return __pmd(pfn | check_pgprot(pgprot));
|
||||
}
|
||||
|
||||
static inline pud_t pfn_pud(unsigned long page_nr, pgprot_t pgprot)
|
||||
{
|
||||
return __pud(((phys_addr_t)page_nr << PAGE_SHIFT) |
|
||||
check_pgprot(pgprot));
|
||||
phys_addr_t pfn = (phys_addr_t)page_nr << PAGE_SHIFT;
|
||||
pfn ^= protnone_mask(pgprot_val(pgprot));
|
||||
pfn &= PHYSICAL_PUD_PAGE_MASK;
|
||||
return __pud(pfn | check_pgprot(pgprot));
|
||||
}
|
||||
|
||||
static inline pmd_t pmd_mknotpresent(pmd_t pmd)
|
||||
{
|
||||
return pfn_pmd(pmd_pfn(pmd),
|
||||
__pgprot(pmd_flags(pmd) & ~(_PAGE_PRESENT|_PAGE_PROTNONE)));
|
||||
}
|
||||
|
||||
static inline pud_t pud_mknotpresent(pud_t pud)
|
||||
{
|
||||
return pfn_pud(pud_pfn(pud),
|
||||
__pgprot(pud_flags(pud) & ~(_PAGE_PRESENT|_PAGE_PROTNONE)));
|
||||
}
|
||||
|
||||
static inline u64 flip_protnone_guard(u64 oldval, u64 val, u64 mask);
|
||||
|
||||
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
||||
{
|
||||
pteval_t val = pte_val(pte);
|
||||
pteval_t val = pte_val(pte), oldval = val;
|
||||
|
||||
/*
|
||||
* Chop off the NX bit (if present), and add the NX portion of
|
||||
@@ -571,17 +591,17 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
||||
*/
|
||||
val &= _PAGE_CHG_MASK;
|
||||
val |= check_pgprot(newprot) & ~_PAGE_CHG_MASK;
|
||||
|
||||
val = flip_protnone_guard(oldval, val, PTE_PFN_MASK);
|
||||
return __pte(val);
|
||||
}
|
||||
|
||||
static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
|
||||
{
|
||||
pmdval_t val = pmd_val(pmd);
|
||||
pmdval_t val = pmd_val(pmd), oldval = val;
|
||||
|
||||
val &= _HPAGE_CHG_MASK;
|
||||
val |= check_pgprot(newprot) & ~_HPAGE_CHG_MASK;
|
||||
|
||||
val = flip_protnone_guard(oldval, val, PHYSICAL_PMD_PAGE_MASK);
|
||||
return __pmd(val);
|
||||
}
|
||||
|
||||
@@ -1320,6 +1340,14 @@ static inline bool pud_access_permitted(pud_t pud, bool write)
|
||||
return __pte_access_permitted(pud_val(pud), write);
|
||||
}
|
||||
|
||||
#define __HAVE_ARCH_PFN_MODIFY_ALLOWED 1
|
||||
extern bool pfn_modify_allowed(unsigned long pfn, pgprot_t prot);
|
||||
|
||||
static inline bool arch_has_pfn_modify_check(void)
|
||||
{
|
||||
return boot_cpu_has_bug(X86_BUG_L1TF);
|
||||
}
|
||||
|
||||
#include <asm-generic/pgtable.h>
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ static inline int pgd_large(pgd_t pgd) { return 0; }
|
||||
*
|
||||
* | ... | 11| 10| 9|8|7|6|5| 4| 3|2| 1|0| <- bit number
|
||||
* | ... |SW3|SW2|SW1|G|L|D|A|CD|WT|U| W|P| <- bit names
|
||||
* | OFFSET (14->63) | TYPE (9-13) |0|0|X|X| X| X|X|SD|0| <- swp entry
|
||||
* | TYPE (59-63) | ~OFFSET (9-58) |0|0|X|X| X| X|X|SD|0| <- swp entry
|
||||
*
|
||||
* G (8) is aliased and used as a PROT_NONE indicator for
|
||||
* !present ptes. We need to start storing swap entries above
|
||||
@@ -286,20 +286,34 @@ static inline int pgd_large(pgd_t pgd) { return 0; }
|
||||
*
|
||||
* Bit 7 in swp entry should be 0 because pmd_present checks not only P,
|
||||
* but also L and G.
|
||||
*
|
||||
* The offset is inverted by a binary not operation to make the high
|
||||
* physical bits set.
|
||||
*/
|
||||
#define SWP_TYPE_FIRST_BIT (_PAGE_BIT_PROTNONE + 1)
|
||||
#define SWP_TYPE_BITS 5
|
||||
/* Place the offset above the type: */
|
||||
#define SWP_OFFSET_FIRST_BIT (SWP_TYPE_FIRST_BIT + SWP_TYPE_BITS)
|
||||
#define SWP_TYPE_BITS 5
|
||||
|
||||
#define SWP_OFFSET_FIRST_BIT (_PAGE_BIT_PROTNONE + 1)
|
||||
|
||||
/* We always extract/encode the offset by shifting it all the way up, and then down again */
|
||||
#define SWP_OFFSET_SHIFT (SWP_OFFSET_FIRST_BIT+SWP_TYPE_BITS)
|
||||
|
||||
#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > SWP_TYPE_BITS)
|
||||
|
||||
#define __swp_type(x) (((x).val >> (SWP_TYPE_FIRST_BIT)) \
|
||||
& ((1U << SWP_TYPE_BITS) - 1))
|
||||
#define __swp_offset(x) ((x).val >> SWP_OFFSET_FIRST_BIT)
|
||||
#define __swp_entry(type, offset) ((swp_entry_t) { \
|
||||
((type) << (SWP_TYPE_FIRST_BIT)) \
|
||||
| ((offset) << SWP_OFFSET_FIRST_BIT) })
|
||||
/* Extract the high bits for type */
|
||||
#define __swp_type(x) ((x).val >> (64 - SWP_TYPE_BITS))
|
||||
|
||||
/* Shift up (to get rid of type), then down to get value */
|
||||
#define __swp_offset(x) (~(x).val << SWP_TYPE_BITS >> SWP_OFFSET_SHIFT)
|
||||
|
||||
/*
|
||||
* Shift the offset up "too far" by TYPE bits, then down again
|
||||
* The offset is inverted by a binary not operation to make the high
|
||||
* physical bits set.
|
||||
*/
|
||||
#define __swp_entry(type, offset) ((swp_entry_t) { \
|
||||
(~(unsigned long)(offset) << SWP_OFFSET_SHIFT >> SWP_TYPE_BITS) \
|
||||
| ((unsigned long)(type) << (64-SWP_TYPE_BITS)) })
|
||||
|
||||
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val((pte)) })
|
||||
#define __pmd_to_swp_entry(pmd) ((swp_entry_t) { pmd_val((pmd)) })
|
||||
#define __swp_entry_to_pte(x) ((pte_t) { .pte = (x).val })
|
||||
@@ -343,5 +357,7 @@ static inline bool gup_fast_permitted(unsigned long start, int nr_pages,
|
||||
return true;
|
||||
}
|
||||
|
||||
#include <asm/pgtable-invert.h>
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
#endif /* _ASM_X86_PGTABLE_64_H */
|
||||
|
||||
@@ -181,6 +181,11 @@ extern const struct seq_operations cpuinfo_op;
|
||||
|
||||
extern void cpu_detect(struct cpuinfo_x86 *c);
|
||||
|
||||
static inline unsigned long l1tf_pfn_limit(void)
|
||||
{
|
||||
return BIT(boot_cpu_data.x86_phys_bits - 1 - PAGE_SHIFT) - 1;
|
||||
}
|
||||
|
||||
extern void early_cpu_init(void);
|
||||
extern void identify_boot_cpu(void);
|
||||
extern void identify_secondary_cpu(struct cpuinfo_x86 *);
|
||||
@@ -977,4 +982,16 @@ bool xen_set_default_idle(void);
|
||||
void stop_this_cpu(void *dummy);
|
||||
void df_debug(struct pt_regs *regs, long error_code);
|
||||
void microcode_check(void);
|
||||
|
||||
enum l1tf_mitigations {
|
||||
L1TF_MITIGATION_OFF,
|
||||
L1TF_MITIGATION_FLUSH_NOWARN,
|
||||
L1TF_MITIGATION_FLUSH,
|
||||
L1TF_MITIGATION_FLUSH_NOSMT,
|
||||
L1TF_MITIGATION_FULL,
|
||||
L1TF_MITIGATION_FULL_FORCE
|
||||
};
|
||||
|
||||
extern enum l1tf_mitigations l1tf_mitigation;
|
||||
|
||||
#endif /* _ASM_X86_PROCESSOR_H */
|
||||
|
||||
@@ -123,13 +123,17 @@ static inline int topology_max_smt_threads(void)
|
||||
}
|
||||
|
||||
int topology_update_package_map(unsigned int apicid, unsigned int cpu);
|
||||
extern int topology_phys_to_logical_pkg(unsigned int pkg);
|
||||
int topology_phys_to_logical_pkg(unsigned int pkg);
|
||||
bool topology_is_primary_thread(unsigned int cpu);
|
||||
bool topology_smt_supported(void);
|
||||
#else
|
||||
#define topology_max_packages() (1)
|
||||
static inline int
|
||||
topology_update_package_map(unsigned int apicid, unsigned int cpu) { return 0; }
|
||||
static inline int topology_phys_to_logical_pkg(unsigned int pkg) { return 0; }
|
||||
static inline int topology_max_smt_threads(void) { return 1; }
|
||||
static inline bool topology_is_primary_thread(unsigned int cpu) { return true; }
|
||||
static inline bool topology_smt_supported(void) { return false; }
|
||||
#endif
|
||||
|
||||
static inline void arch_fix_phys_package_id(int num, u32 slot)
|
||||
|
||||
@@ -576,4 +576,15 @@ enum vm_instruction_error_number {
|
||||
VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID = 28,
|
||||
};
|
||||
|
||||
enum vmx_l1d_flush_state {
|
||||
VMENTER_L1D_FLUSH_AUTO,
|
||||
VMENTER_L1D_FLUSH_NEVER,
|
||||
VMENTER_L1D_FLUSH_COND,
|
||||
VMENTER_L1D_FLUSH_ALWAYS,
|
||||
VMENTER_L1D_FLUSH_EPT_DISABLED,
|
||||
VMENTER_L1D_FLUSH_NOT_REQUIRED,
|
||||
};
|
||||
|
||||
extern enum vmx_l1d_flush_state l1tf_vmx_mitigation;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include <asm/hypervisor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
unsigned int num_processors;
|
||||
|
||||
@@ -2192,6 +2193,23 @@ static int cpuid_to_apicid[] = {
|
||||
[0 ... NR_CPUS - 1] = -1,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/**
|
||||
* apic_id_is_primary_thread - Check whether APIC ID belongs to a primary thread
|
||||
* @id: APIC ID to check
|
||||
*/
|
||||
bool apic_id_is_primary_thread(unsigned int apicid)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
if (smp_num_siblings == 1)
|
||||
return true;
|
||||
/* Isolate the SMT bit(s) in the APICID and check for 0 */
|
||||
mask = (1U << (fls(smp_num_siblings) - 1)) - 1;
|
||||
return !(apicid & mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Should use this API to allocate logical CPU IDs to keep nr_logical_cpuids
|
||||
* and cpuid_to_apicid[] synchronized.
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dmar.h>
|
||||
#include <linux/hpet.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
@@ -394,10 +394,10 @@ extern int uv_hub_info_version(void)
|
||||
EXPORT_SYMBOL(uv_hub_info_version);
|
||||
|
||||
/* Default UV memory block size is 2GB */
|
||||
static unsigned long mem_block_size = (2UL << 30);
|
||||
static unsigned long mem_block_size __initdata = (2UL << 30);
|
||||
|
||||
/* Kernel parameter to specify UV mem block size */
|
||||
static int parse_mem_block_size(char *ptr)
|
||||
static int __init parse_mem_block_size(char *ptr)
|
||||
{
|
||||
unsigned long size = memparse(ptr, NULL);
|
||||
|
||||
|
||||
+27
-24
@@ -315,6 +315,13 @@ static void legacy_fixup_core_id(struct cpuinfo_x86 *c)
|
||||
c->cpu_core_id %= cus_per_node;
|
||||
}
|
||||
|
||||
|
||||
static void amd_get_topology_early(struct cpuinfo_x86 *c)
|
||||
{
|
||||
if (cpu_has(c, X86_FEATURE_TOPOEXT))
|
||||
smp_num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixup core topology information for
|
||||
* (1) AMD multi-node processors
|
||||
@@ -334,7 +341,6 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
|
||||
cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
node_id = ecx & 0xff;
|
||||
smp_num_siblings = ((ebx >> 8) & 0xff) + 1;
|
||||
|
||||
if (c->x86 == 0x15)
|
||||
c->cu_id = ebx & 0xff;
|
||||
@@ -613,6 +619,7 @@ clear_sev:
|
||||
|
||||
static void early_init_amd(struct cpuinfo_x86 *c)
|
||||
{
|
||||
u64 value;
|
||||
u32 dummy;
|
||||
|
||||
early_init_amd_mc(c);
|
||||
@@ -683,6 +690,22 @@ static void early_init_amd(struct cpuinfo_x86 *c)
|
||||
set_cpu_bug(c, X86_BUG_AMD_E400);
|
||||
|
||||
early_detect_mem_encrypt(c);
|
||||
|
||||
/* Re-enable TopologyExtensions if switched off by BIOS */
|
||||
if (c->x86 == 0x15 &&
|
||||
(c->x86_model >= 0x10 && c->x86_model <= 0x6f) &&
|
||||
!cpu_has(c, X86_FEATURE_TOPOEXT)) {
|
||||
|
||||
if (msr_set_bit(0xc0011005, 54) > 0) {
|
||||
rdmsrl(0xc0011005, value);
|
||||
if (value & BIT_64(54)) {
|
||||
set_cpu_cap(c, X86_FEATURE_TOPOEXT);
|
||||
pr_info_once(FW_INFO "CPU: Re-enabling disabled Topology Extensions Support.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amd_get_topology_early(c);
|
||||
}
|
||||
|
||||
static void init_amd_k8(struct cpuinfo_x86 *c)
|
||||
@@ -774,19 +797,6 @@ static void init_amd_bd(struct cpuinfo_x86 *c)
|
||||
{
|
||||
u64 value;
|
||||
|
||||
/* re-enable TopologyExtensions if switched off by BIOS */
|
||||
if ((c->x86_model >= 0x10) && (c->x86_model <= 0x6f) &&
|
||||
!cpu_has(c, X86_FEATURE_TOPOEXT)) {
|
||||
|
||||
if (msr_set_bit(0xc0011005, 54) > 0) {
|
||||
rdmsrl(0xc0011005, value);
|
||||
if (value & BIT_64(54)) {
|
||||
set_cpu_cap(c, X86_FEATURE_TOPOEXT);
|
||||
pr_info_once(FW_INFO "CPU: Re-enabling disabled Topology Extensions Support.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The way access filter has a performance penalty on some workloads.
|
||||
* Disable it on the affected CPUs.
|
||||
@@ -850,16 +860,9 @@ static void init_amd(struct cpuinfo_x86 *c)
|
||||
|
||||
cpu_detect_cache_sizes(c);
|
||||
|
||||
/* Multi core CPU? */
|
||||
if (c->extended_cpuid_level >= 0x80000008) {
|
||||
amd_detect_cmp(c);
|
||||
amd_get_topology(c);
|
||||
srat_detect_node(c);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
detect_ht(c);
|
||||
#endif
|
||||
amd_detect_cmp(c);
|
||||
amd_get_topology(c);
|
||||
srat_detect_node(c);
|
||||
|
||||
init_amd_cacheinfo(c);
|
||||
|
||||
|
||||
+139
-31
@@ -22,15 +22,18 @@
|
||||
#include <asm/processor-flags.h>
|
||||
#include <asm/fpu/internal.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/vmx.h>
|
||||
#include <asm/paravirt.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/set_memory.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/hypervisor.h>
|
||||
#include <asm/e820/api.h>
|
||||
|
||||
static void __init spectre_v2_select_mitigation(void);
|
||||
static void __init ssb_select_mitigation(void);
|
||||
static void __init l1tf_select_mitigation(void);
|
||||
|
||||
/*
|
||||
* Our boot-time value of the SPEC_CTRL MSR. We read it once so that any
|
||||
@@ -56,6 +59,12 @@ void __init check_bugs(void)
|
||||
{
|
||||
identify_boot_cpu();
|
||||
|
||||
/*
|
||||
* identify_boot_cpu() initialized SMT support information, let the
|
||||
* core code know.
|
||||
*/
|
||||
cpu_smt_check_topology_early();
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SMP)) {
|
||||
pr_info("CPU: ");
|
||||
print_cpu_info(&boot_cpu_data);
|
||||
@@ -82,6 +91,8 @@ void __init check_bugs(void)
|
||||
*/
|
||||
ssb_select_mitigation();
|
||||
|
||||
l1tf_select_mitigation();
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
/*
|
||||
* Check whether we are able to run this kernel safely on SMP.
|
||||
@@ -313,23 +324,6 @@ static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/* Check for Skylake-like CPUs (for RSB handling) */
|
||||
static bool __init is_skylake_era(void)
|
||||
{
|
||||
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
|
||||
boot_cpu_data.x86 == 6) {
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
case INTEL_FAM6_SKYLAKE_MOBILE:
|
||||
case INTEL_FAM6_SKYLAKE_DESKTOP:
|
||||
case INTEL_FAM6_SKYLAKE_X:
|
||||
case INTEL_FAM6_KABYLAKE_MOBILE:
|
||||
case INTEL_FAM6_KABYLAKE_DESKTOP:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __init spectre_v2_select_mitigation(void)
|
||||
{
|
||||
enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline();
|
||||
@@ -390,22 +384,15 @@ retpoline_auto:
|
||||
pr_info("%s\n", spectre_v2_strings[mode]);
|
||||
|
||||
/*
|
||||
* If neither SMEP nor PTI are available, there is a risk of
|
||||
* hitting userspace addresses in the RSB after a context switch
|
||||
* from a shallow call stack to a deeper one. To prevent this fill
|
||||
* the entire RSB, even when using IBRS.
|
||||
* If spectre v2 protection has been enabled, unconditionally fill
|
||||
* RSB during a context switch; this protects against two independent
|
||||
* issues:
|
||||
*
|
||||
* Skylake era CPUs have a separate issue with *underflow* of the
|
||||
* RSB, when they will predict 'ret' targets from the generic BTB.
|
||||
* The proper mitigation for this is IBRS. If IBRS is not supported
|
||||
* or deactivated in favour of retpolines the RSB fill on context
|
||||
* switch is required.
|
||||
* - RSB underflow (and switch to BTB) on Skylake+
|
||||
* - SpectreRSB variant of spectre v2 on X86_BUG_SPECTRE_V2 CPUs
|
||||
*/
|
||||
if ((!boot_cpu_has(X86_FEATURE_PTI) &&
|
||||
!boot_cpu_has(X86_FEATURE_SMEP)) || is_skylake_era()) {
|
||||
setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
|
||||
pr_info("Spectre v2 mitigation: Filling RSB on context switch\n");
|
||||
}
|
||||
setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
|
||||
pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
|
||||
|
||||
/* Initialize Indirect Branch Prediction Barrier if supported */
|
||||
if (boot_cpu_has(X86_FEATURE_IBPB)) {
|
||||
@@ -654,8 +641,120 @@ void x86_spec_ctrl_setup_ap(void)
|
||||
x86_amd_ssb_disable();
|
||||
}
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "L1TF: " fmt
|
||||
|
||||
/* Default mitigation for L1TF-affected CPUs */
|
||||
enum l1tf_mitigations l1tf_mitigation __ro_after_init = L1TF_MITIGATION_FLUSH;
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
EXPORT_SYMBOL_GPL(l1tf_mitigation);
|
||||
#endif
|
||||
enum vmx_l1d_flush_state l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_AUTO;
|
||||
EXPORT_SYMBOL_GPL(l1tf_vmx_mitigation);
|
||||
|
||||
static void __init l1tf_select_mitigation(void)
|
||||
{
|
||||
u64 half_pa;
|
||||
|
||||
if (!boot_cpu_has_bug(X86_BUG_L1TF))
|
||||
return;
|
||||
|
||||
switch (l1tf_mitigation) {
|
||||
case L1TF_MITIGATION_OFF:
|
||||
case L1TF_MITIGATION_FLUSH_NOWARN:
|
||||
case L1TF_MITIGATION_FLUSH:
|
||||
break;
|
||||
case L1TF_MITIGATION_FLUSH_NOSMT:
|
||||
case L1TF_MITIGATION_FULL:
|
||||
cpu_smt_disable(false);
|
||||
break;
|
||||
case L1TF_MITIGATION_FULL_FORCE:
|
||||
cpu_smt_disable(true);
|
||||
break;
|
||||
}
|
||||
|
||||
#if CONFIG_PGTABLE_LEVELS == 2
|
||||
pr_warn("Kernel not compiled for PAE. No mitigation for L1TF\n");
|
||||
return;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is extremely unlikely to happen because almost all
|
||||
* systems have far more MAX_PA/2 than RAM can be fit into
|
||||
* DIMM slots.
|
||||
*/
|
||||
half_pa = (u64)l1tf_pfn_limit() << PAGE_SHIFT;
|
||||
if (e820__mapped_any(half_pa, ULLONG_MAX - half_pa, E820_TYPE_RAM)) {
|
||||
pr_warn("System has more than MAX_PA/2 memory. L1TF mitigation not effective.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
setup_force_cpu_cap(X86_FEATURE_L1TF_PTEINV);
|
||||
}
|
||||
|
||||
static int __init l1tf_cmdline(char *str)
|
||||
{
|
||||
if (!boot_cpu_has_bug(X86_BUG_L1TF))
|
||||
return 0;
|
||||
|
||||
if (!str)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(str, "off"))
|
||||
l1tf_mitigation = L1TF_MITIGATION_OFF;
|
||||
else if (!strcmp(str, "flush,nowarn"))
|
||||
l1tf_mitigation = L1TF_MITIGATION_FLUSH_NOWARN;
|
||||
else if (!strcmp(str, "flush"))
|
||||
l1tf_mitigation = L1TF_MITIGATION_FLUSH;
|
||||
else if (!strcmp(str, "flush,nosmt"))
|
||||
l1tf_mitigation = L1TF_MITIGATION_FLUSH_NOSMT;
|
||||
else if (!strcmp(str, "full"))
|
||||
l1tf_mitigation = L1TF_MITIGATION_FULL;
|
||||
else if (!strcmp(str, "full,force"))
|
||||
l1tf_mitigation = L1TF_MITIGATION_FULL_FORCE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_param("l1tf", l1tf_cmdline);
|
||||
|
||||
#undef pr_fmt
|
||||
|
||||
#ifdef CONFIG_SYSFS
|
||||
|
||||
#define L1TF_DEFAULT_MSG "Mitigation: PTE Inversion"
|
||||
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
static const char *l1tf_vmx_states[] = {
|
||||
[VMENTER_L1D_FLUSH_AUTO] = "auto",
|
||||
[VMENTER_L1D_FLUSH_NEVER] = "vulnerable",
|
||||
[VMENTER_L1D_FLUSH_COND] = "conditional cache flushes",
|
||||
[VMENTER_L1D_FLUSH_ALWAYS] = "cache flushes",
|
||||
[VMENTER_L1D_FLUSH_EPT_DISABLED] = "EPT disabled",
|
||||
[VMENTER_L1D_FLUSH_NOT_REQUIRED] = "flush not necessary"
|
||||
};
|
||||
|
||||
static ssize_t l1tf_show_state(char *buf)
|
||||
{
|
||||
if (l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_AUTO)
|
||||
return sprintf(buf, "%s\n", L1TF_DEFAULT_MSG);
|
||||
|
||||
if (l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_EPT_DISABLED ||
|
||||
(l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_NEVER &&
|
||||
cpu_smt_control == CPU_SMT_ENABLED))
|
||||
return sprintf(buf, "%s; VMX: %s\n", L1TF_DEFAULT_MSG,
|
||||
l1tf_vmx_states[l1tf_vmx_mitigation]);
|
||||
|
||||
return sprintf(buf, "%s; VMX: %s, SMT %s\n", L1TF_DEFAULT_MSG,
|
||||
l1tf_vmx_states[l1tf_vmx_mitigation],
|
||||
cpu_smt_control == CPU_SMT_ENABLED ? "vulnerable" : "disabled");
|
||||
}
|
||||
#else
|
||||
static ssize_t l1tf_show_state(char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", L1TF_DEFAULT_MSG);
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr,
|
||||
char *buf, unsigned int bug)
|
||||
{
|
||||
@@ -684,6 +783,10 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
|
||||
case X86_BUG_SPEC_STORE_BYPASS:
|
||||
return sprintf(buf, "%s\n", ssb_strings[ssb_mode]);
|
||||
|
||||
case X86_BUG_L1TF:
|
||||
if (boot_cpu_has(X86_FEATURE_L1TF_PTEINV))
|
||||
return l1tf_show_state(buf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -710,4 +813,9 @@ ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *
|
||||
{
|
||||
return cpu_show_common(dev, attr, buf, X86_BUG_SPEC_STORE_BYPASS);
|
||||
}
|
||||
|
||||
ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return cpu_show_common(dev, attr, buf, X86_BUG_L1TF);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -661,33 +661,36 @@ static void cpu_detect_tlb(struct cpuinfo_x86 *c)
|
||||
tlb_lld_4m[ENTRIES], tlb_lld_1g[ENTRIES]);
|
||||
}
|
||||
|
||||
void detect_ht(struct cpuinfo_x86 *c)
|
||||
int detect_ht_early(struct cpuinfo_x86 *c)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
u32 eax, ebx, ecx, edx;
|
||||
int index_msb, core_bits;
|
||||
static bool printed;
|
||||
|
||||
if (!cpu_has(c, X86_FEATURE_HT))
|
||||
return;
|
||||
return -1;
|
||||
|
||||
if (cpu_has(c, X86_FEATURE_CMP_LEGACY))
|
||||
goto out;
|
||||
return -1;
|
||||
|
||||
if (cpu_has(c, X86_FEATURE_XTOPOLOGY))
|
||||
return;
|
||||
return -1;
|
||||
|
||||
cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
smp_num_siblings = (ebx & 0xff0000) >> 16;
|
||||
|
||||
if (smp_num_siblings == 1) {
|
||||
if (smp_num_siblings == 1)
|
||||
pr_info_once("CPU0: Hyper-Threading is disabled\n");
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (smp_num_siblings <= 1)
|
||||
goto out;
|
||||
void detect_ht(struct cpuinfo_x86 *c)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
int index_msb, core_bits;
|
||||
|
||||
if (detect_ht_early(c) < 0)
|
||||
return;
|
||||
|
||||
index_msb = get_count_order(smp_num_siblings);
|
||||
c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid, index_msb);
|
||||
@@ -700,15 +703,6 @@ void detect_ht(struct cpuinfo_x86 *c)
|
||||
|
||||
c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid, index_msb) &
|
||||
((1 << core_bits) - 1);
|
||||
|
||||
out:
|
||||
if (!printed && (c->x86_max_cores * smp_num_siblings) > 1) {
|
||||
pr_info("CPU: Physical Processor ID: %d\n",
|
||||
c->phys_proc_id);
|
||||
pr_info("CPU: Processor Core ID: %d\n",
|
||||
c->cpu_core_id);
|
||||
printed = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -911,7 +905,7 @@ void get_cpu_cap(struct cpuinfo_x86 *c)
|
||||
apply_forced_caps(c);
|
||||
}
|
||||
|
||||
static void get_cpu_address_sizes(struct cpuinfo_x86 *c)
|
||||
void get_cpu_address_sizes(struct cpuinfo_x86 *c)
|
||||
{
|
||||
u32 eax, ebx, ecx, edx;
|
||||
|
||||
@@ -987,6 +981,21 @@ static const __initconst struct x86_cpu_id cpu_no_spec_store_bypass[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static const __initconst struct x86_cpu_id cpu_no_l1tf[] = {
|
||||
/* in addition to cpu_no_speculation */
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT2 },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_MERRIFIELD },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_MOOREFIELD },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_GOLDMONT },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_DENVERTON },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_GEMINI_LAKE },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNL },
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNM },
|
||||
{}
|
||||
};
|
||||
|
||||
static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
|
||||
{
|
||||
u64 ia32_cap = 0;
|
||||
@@ -1013,6 +1022,11 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
|
||||
return;
|
||||
|
||||
setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN);
|
||||
|
||||
if (x86_match_cpu(cpu_no_l1tf))
|
||||
return;
|
||||
|
||||
setup_force_cpu_bug(X86_BUG_L1TF);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -46,6 +46,7 @@ extern const struct cpu_dev *const __x86_cpu_dev_start[],
|
||||
*const __x86_cpu_dev_end[];
|
||||
|
||||
extern void get_cpu_cap(struct cpuinfo_x86 *c);
|
||||
extern void get_cpu_address_sizes(struct cpuinfo_x86 *c);
|
||||
extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c);
|
||||
extern void init_scattered_cpuid_features(struct cpuinfo_x86 *c);
|
||||
extern u32 get_scattered_cpuid_leaf(unsigned int level,
|
||||
@@ -55,7 +56,9 @@ extern void init_intel_cacheinfo(struct cpuinfo_x86 *c);
|
||||
extern void init_amd_cacheinfo(struct cpuinfo_x86 *c);
|
||||
|
||||
extern void detect_num_cpu_cores(struct cpuinfo_x86 *c);
|
||||
extern int detect_extended_topology_early(struct cpuinfo_x86 *c);
|
||||
extern int detect_extended_topology(struct cpuinfo_x86 *c);
|
||||
extern int detect_ht_early(struct cpuinfo_x86 *c);
|
||||
extern void detect_ht(struct cpuinfo_x86 *c);
|
||||
|
||||
unsigned int aperfmperf_get_khz(int cpu);
|
||||
|
||||
@@ -301,6 +301,13 @@ static void early_init_intel(struct cpuinfo_x86 *c)
|
||||
}
|
||||
|
||||
check_mpx_erratum(c);
|
||||
|
||||
/*
|
||||
* Get the number of SMT siblings early from the extended topology
|
||||
* leaf, if available. Otherwise try the legacy SMT detection.
|
||||
*/
|
||||
if (detect_extended_topology_early(c) < 0)
|
||||
detect_ht_early(c);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
|
||||
@@ -509,12 +509,20 @@ static struct platform_device *microcode_pdev;
|
||||
|
||||
static int check_online_cpus(void)
|
||||
{
|
||||
if (num_online_cpus() == num_present_cpus())
|
||||
return 0;
|
||||
unsigned int cpu;
|
||||
|
||||
pr_err("Not all CPUs online, aborting microcode update.\n");
|
||||
/*
|
||||
* Make sure all CPUs are online. It's fine for SMT to be disabled if
|
||||
* all the primary threads are still online.
|
||||
*/
|
||||
for_each_present_cpu(cpu) {
|
||||
if (topology_is_primary_thread(cpu) && !cpu_online(cpu)) {
|
||||
pr_err("Not all CPUs online, aborting microcode update.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static atomic_t late_cpus_in;
|
||||
|
||||
@@ -22,18 +22,10 @@
|
||||
#define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f)
|
||||
#define LEVEL_MAX_SIBLINGS(ebx) ((ebx) & 0xffff)
|
||||
|
||||
/*
|
||||
* Check for extended topology enumeration cpuid leaf 0xb and if it
|
||||
* exists, use it for populating initial_apicid and cpu topology
|
||||
* detection.
|
||||
*/
|
||||
int detect_extended_topology(struct cpuinfo_x86 *c)
|
||||
int detect_extended_topology_early(struct cpuinfo_x86 *c)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
unsigned int eax, ebx, ecx, edx, sub_index;
|
||||
unsigned int ht_mask_width, core_plus_mask_width;
|
||||
unsigned int core_select_mask, core_level_siblings;
|
||||
static bool printed;
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
|
||||
if (c->cpuid_level < 0xb)
|
||||
return -1;
|
||||
@@ -52,10 +44,30 @@ int detect_extended_topology(struct cpuinfo_x86 *c)
|
||||
* initial apic id, which also represents 32-bit extended x2apic id.
|
||||
*/
|
||||
c->initial_apicid = edx;
|
||||
smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for extended topology enumeration cpuid leaf 0xb and if it
|
||||
* exists, use it for populating initial_apicid and cpu topology
|
||||
* detection.
|
||||
*/
|
||||
int detect_extended_topology(struct cpuinfo_x86 *c)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
unsigned int eax, ebx, ecx, edx, sub_index;
|
||||
unsigned int ht_mask_width, core_plus_mask_width;
|
||||
unsigned int core_select_mask, core_level_siblings;
|
||||
|
||||
if (detect_extended_topology_early(c) < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Populate HT related information from sub-leaf level 0.
|
||||
*/
|
||||
cpuid_count(0xb, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
|
||||
core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
|
||||
core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
|
||||
|
||||
@@ -86,15 +98,6 @@ int detect_extended_topology(struct cpuinfo_x86 *c)
|
||||
c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
|
||||
|
||||
c->x86_max_cores = (core_level_siblings / smp_num_siblings);
|
||||
|
||||
if (!printed) {
|
||||
pr_info("CPU: Physical Processor ID: %d\n",
|
||||
c->phys_proc_id);
|
||||
if (c->x86_max_cores > 1)
|
||||
pr_info("CPU: Processor Core ID: %d\n",
|
||||
c->cpu_core_id);
|
||||
printed = 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <asm/fpu/signal.h>
|
||||
#include <asm/fpu/types.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/pkeys.h>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <asm/traps.h>
|
||||
#include <asm/proto.h>
|
||||
#include <asm/desc.h>
|
||||
#include <asm/hw_irq.h>
|
||||
|
||||
struct idt_data {
|
||||
unsigned int vector;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/apic.h>
|
||||
#include <asm/io_apic.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
@@ -395,8 +395,6 @@ int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn)
|
||||
- (u8 *) real;
|
||||
if ((s64) (s32) newdisp != newdisp) {
|
||||
pr_err("Kprobes error: new displacement does not fit into s32 (%llx)\n", newdisp);
|
||||
pr_err("\tSrc: %p, Dest: %p, old disp: %x\n",
|
||||
src, real, insn->displacement.value);
|
||||
return 0;
|
||||
}
|
||||
disp = (u8 *) dest + insn_offset_displacement(insn);
|
||||
@@ -640,8 +638,7 @@ static int reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
|
||||
* Raise a BUG or we'll continue in an endless reentering loop
|
||||
* and eventually a stack overflow.
|
||||
*/
|
||||
printk(KERN_WARNING "Unrecoverable kprobe detected at %p.\n",
|
||||
p->addr);
|
||||
pr_err("Unrecoverable kprobe detected.\n");
|
||||
dump_kprobe(p);
|
||||
BUG();
|
||||
default:
|
||||
|
||||
@@ -88,10 +88,12 @@ unsigned paravirt_patch_call(void *insnbuf,
|
||||
struct branch *b = insnbuf;
|
||||
unsigned long delta = (unsigned long)target - (addr+5);
|
||||
|
||||
if (tgt_clobbers & ~site_clobbers)
|
||||
return len; /* target would clobber too much for this site */
|
||||
if (len < 5)
|
||||
if (len < 5) {
|
||||
#ifdef CONFIG_RETPOLINE
|
||||
WARN_ONCE("Failing to patch indirect CALL in %ps\n", (void *)addr);
|
||||
#endif
|
||||
return len; /* call too long for patch site */
|
||||
}
|
||||
|
||||
b->opcode = 0xe8; /* call */
|
||||
b->delta = delta;
|
||||
@@ -106,8 +108,12 @@ unsigned paravirt_patch_jmp(void *insnbuf, const void *target,
|
||||
struct branch *b = insnbuf;
|
||||
unsigned long delta = (unsigned long)target - (addr+5);
|
||||
|
||||
if (len < 5)
|
||||
if (len < 5) {
|
||||
#ifdef CONFIG_RETPOLINE
|
||||
WARN_ONCE("Failing to patch indirect JMP in %ps\n", (void *)addr);
|
||||
#endif
|
||||
return len; /* call too long for patch site */
|
||||
}
|
||||
|
||||
b->opcode = 0xe9; /* jmp */
|
||||
b->delta = delta;
|
||||
|
||||
@@ -823,6 +823,12 @@ void __init setup_arch(char **cmdline_p)
|
||||
memblock_reserve(__pa_symbol(_text),
|
||||
(unsigned long)__bss_stop - (unsigned long)_text);
|
||||
|
||||
/*
|
||||
* Make sure page 0 is always reserved because on systems with
|
||||
* L1TF its contents can be leaked to user processes.
|
||||
*/
|
||||
memblock_reserve(0, PAGE_SIZE);
|
||||
|
||||
early_reserve_initrd();
|
||||
|
||||
/*
|
||||
|
||||
@@ -261,6 +261,7 @@ __visible void __irq_entry smp_reschedule_interrupt(struct pt_regs *regs)
|
||||
{
|
||||
ack_APIC_irq();
|
||||
inc_irq_stat(irq_resched_count);
|
||||
kvm_set_cpu_l1tf_flush_l1d();
|
||||
|
||||
if (trace_resched_ipi_enabled()) {
|
||||
/*
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/spec-ctrl.h>
|
||||
#include <asm/hw_irq.h>
|
||||
|
||||
/* representing HT siblings of each logical CPU */
|
||||
DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map);
|
||||
@@ -270,6 +271,23 @@ static void notrace start_secondary(void *unused)
|
||||
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* topology_is_primary_thread - Check whether CPU is the primary SMT thread
|
||||
* @cpu: CPU to check
|
||||
*/
|
||||
bool topology_is_primary_thread(unsigned int cpu)
|
||||
{
|
||||
return apic_id_is_primary_thread(per_cpu(x86_cpu_to_apicid, cpu));
|
||||
}
|
||||
|
||||
/**
|
||||
* topology_smt_supported - Check whether SMT is supported by the CPUs
|
||||
*/
|
||||
bool topology_smt_supported(void)
|
||||
{
|
||||
return smp_num_siblings > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* topology_phys_to_logical_pkg - Map a physical package id to a logical
|
||||
*
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i8253.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
@@ -3840,6 +3840,7 @@ int kvm_handle_page_fault(struct kvm_vcpu *vcpu, u64 error_code,
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
vcpu->arch.l1tf_flush_l1d = true;
|
||||
switch (vcpu->arch.apf.host_apf_reason) {
|
||||
default:
|
||||
trace_kvm_page_fault(fault_address, error_code);
|
||||
|
||||
+377
-78
@@ -188,6 +188,150 @@ module_param(ple_window_max, uint, 0444);
|
||||
|
||||
extern const ulong vmx_return;
|
||||
|
||||
static DEFINE_STATIC_KEY_FALSE(vmx_l1d_should_flush);
|
||||
static DEFINE_STATIC_KEY_FALSE(vmx_l1d_flush_cond);
|
||||
static DEFINE_MUTEX(vmx_l1d_flush_mutex);
|
||||
|
||||
/* Storage for pre module init parameter parsing */
|
||||
static enum vmx_l1d_flush_state __read_mostly vmentry_l1d_flush_param = VMENTER_L1D_FLUSH_AUTO;
|
||||
|
||||
static const struct {
|
||||
const char *option;
|
||||
enum vmx_l1d_flush_state cmd;
|
||||
} vmentry_l1d_param[] = {
|
||||
{"auto", VMENTER_L1D_FLUSH_AUTO},
|
||||
{"never", VMENTER_L1D_FLUSH_NEVER},
|
||||
{"cond", VMENTER_L1D_FLUSH_COND},
|
||||
{"always", VMENTER_L1D_FLUSH_ALWAYS},
|
||||
};
|
||||
|
||||
#define L1D_CACHE_ORDER 4
|
||||
static void *vmx_l1d_flush_pages;
|
||||
|
||||
static int vmx_setup_l1d_flush(enum vmx_l1d_flush_state l1tf)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned int i;
|
||||
|
||||
if (!enable_ept) {
|
||||
l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_EPT_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES)) {
|
||||
u64 msr;
|
||||
|
||||
rdmsrl(MSR_IA32_ARCH_CAPABILITIES, msr);
|
||||
if (msr & ARCH_CAP_SKIP_VMENTRY_L1DFLUSH) {
|
||||
l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_NOT_REQUIRED;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If set to auto use the default l1tf mitigation method */
|
||||
if (l1tf == VMENTER_L1D_FLUSH_AUTO) {
|
||||
switch (l1tf_mitigation) {
|
||||
case L1TF_MITIGATION_OFF:
|
||||
l1tf = VMENTER_L1D_FLUSH_NEVER;
|
||||
break;
|
||||
case L1TF_MITIGATION_FLUSH_NOWARN:
|
||||
case L1TF_MITIGATION_FLUSH:
|
||||
case L1TF_MITIGATION_FLUSH_NOSMT:
|
||||
l1tf = VMENTER_L1D_FLUSH_COND;
|
||||
break;
|
||||
case L1TF_MITIGATION_FULL:
|
||||
case L1TF_MITIGATION_FULL_FORCE:
|
||||
l1tf = VMENTER_L1D_FLUSH_ALWAYS;
|
||||
break;
|
||||
}
|
||||
} else if (l1tf_mitigation == L1TF_MITIGATION_FULL_FORCE) {
|
||||
l1tf = VMENTER_L1D_FLUSH_ALWAYS;
|
||||
}
|
||||
|
||||
if (l1tf != VMENTER_L1D_FLUSH_NEVER && !vmx_l1d_flush_pages &&
|
||||
!boot_cpu_has(X86_FEATURE_FLUSH_L1D)) {
|
||||
page = alloc_pages(GFP_KERNEL, L1D_CACHE_ORDER);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
vmx_l1d_flush_pages = page_address(page);
|
||||
|
||||
/*
|
||||
* Initialize each page with a different pattern in
|
||||
* order to protect against KSM in the nested
|
||||
* virtualization case.
|
||||
*/
|
||||
for (i = 0; i < 1u << L1D_CACHE_ORDER; ++i) {
|
||||
memset(vmx_l1d_flush_pages + i * PAGE_SIZE, i + 1,
|
||||
PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
l1tf_vmx_mitigation = l1tf;
|
||||
|
||||
if (l1tf != VMENTER_L1D_FLUSH_NEVER)
|
||||
static_branch_enable(&vmx_l1d_should_flush);
|
||||
else
|
||||
static_branch_disable(&vmx_l1d_should_flush);
|
||||
|
||||
if (l1tf == VMENTER_L1D_FLUSH_COND)
|
||||
static_branch_enable(&vmx_l1d_flush_cond);
|
||||
else
|
||||
static_branch_disable(&vmx_l1d_flush_cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmentry_l1d_flush_parse(const char *s)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (s) {
|
||||
for (i = 0; i < ARRAY_SIZE(vmentry_l1d_param); i++) {
|
||||
if (sysfs_streq(s, vmentry_l1d_param[i].option))
|
||||
return vmentry_l1d_param[i].cmd;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vmentry_l1d_flush_set(const char *s, const struct kernel_param *kp)
|
||||
{
|
||||
int l1tf, ret;
|
||||
|
||||
if (!boot_cpu_has(X86_BUG_L1TF))
|
||||
return 0;
|
||||
|
||||
l1tf = vmentry_l1d_flush_parse(s);
|
||||
if (l1tf < 0)
|
||||
return l1tf;
|
||||
|
||||
/*
|
||||
* Has vmx_init() run already? If not then this is the pre init
|
||||
* parameter parsing. In that case just store the value and let
|
||||
* vmx_init() do the proper setup after enable_ept has been
|
||||
* established.
|
||||
*/
|
||||
if (l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_AUTO) {
|
||||
vmentry_l1d_flush_param = l1tf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&vmx_l1d_flush_mutex);
|
||||
ret = vmx_setup_l1d_flush(l1tf);
|
||||
mutex_unlock(&vmx_l1d_flush_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmentry_l1d_flush_get(char *s, const struct kernel_param *kp)
|
||||
{
|
||||
return sprintf(s, "%s\n", vmentry_l1d_param[l1tf_vmx_mitigation].option);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops vmentry_l1d_flush_ops = {
|
||||
.set = vmentry_l1d_flush_set,
|
||||
.get = vmentry_l1d_flush_get,
|
||||
};
|
||||
module_param_cb(vmentry_l1d_flush, &vmentry_l1d_flush_ops, NULL, 0644);
|
||||
|
||||
struct kvm_vmx {
|
||||
struct kvm kvm;
|
||||
|
||||
@@ -757,6 +901,11 @@ static inline int pi_test_sn(struct pi_desc *pi_desc)
|
||||
(unsigned long *)&pi_desc->control);
|
||||
}
|
||||
|
||||
struct vmx_msrs {
|
||||
unsigned int nr;
|
||||
struct vmx_msr_entry val[NR_AUTOLOAD_MSRS];
|
||||
};
|
||||
|
||||
struct vcpu_vmx {
|
||||
struct kvm_vcpu vcpu;
|
||||
unsigned long host_rsp;
|
||||
@@ -790,9 +939,8 @@ struct vcpu_vmx {
|
||||
struct loaded_vmcs *loaded_vmcs;
|
||||
bool __launched; /* temporary, used in vmx_vcpu_run */
|
||||
struct msr_autoload {
|
||||
unsigned nr;
|
||||
struct vmx_msr_entry guest[NR_AUTOLOAD_MSRS];
|
||||
struct vmx_msr_entry host[NR_AUTOLOAD_MSRS];
|
||||
struct vmx_msrs guest;
|
||||
struct vmx_msrs host;
|
||||
} msr_autoload;
|
||||
struct {
|
||||
int loaded;
|
||||
@@ -2377,9 +2525,20 @@ static void clear_atomic_switch_msr_special(struct vcpu_vmx *vmx,
|
||||
vm_exit_controls_clearbit(vmx, exit);
|
||||
}
|
||||
|
||||
static int find_msr(struct vmx_msrs *m, unsigned int msr)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < m->nr; ++i) {
|
||||
if (m->val[i].index == msr)
|
||||
return i;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void clear_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr)
|
||||
{
|
||||
unsigned i;
|
||||
int i;
|
||||
struct msr_autoload *m = &vmx->msr_autoload;
|
||||
|
||||
switch (msr) {
|
||||
@@ -2400,18 +2559,21 @@ static void clear_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr)
|
||||
}
|
||||
break;
|
||||
}
|
||||
i = find_msr(&m->guest, msr);
|
||||
if (i < 0)
|
||||
goto skip_guest;
|
||||
--m->guest.nr;
|
||||
m->guest.val[i] = m->guest.val[m->guest.nr];
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, m->guest.nr);
|
||||
|
||||
for (i = 0; i < m->nr; ++i)
|
||||
if (m->guest[i].index == msr)
|
||||
break;
|
||||
|
||||
if (i == m->nr)
|
||||
skip_guest:
|
||||
i = find_msr(&m->host, msr);
|
||||
if (i < 0)
|
||||
return;
|
||||
--m->nr;
|
||||
m->guest[i] = m->guest[m->nr];
|
||||
m->host[i] = m->host[m->nr];
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, m->nr);
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, m->nr);
|
||||
|
||||
--m->host.nr;
|
||||
m->host.val[i] = m->host.val[m->host.nr];
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, m->host.nr);
|
||||
}
|
||||
|
||||
static void add_atomic_switch_msr_special(struct vcpu_vmx *vmx,
|
||||
@@ -2426,9 +2588,9 @@ static void add_atomic_switch_msr_special(struct vcpu_vmx *vmx,
|
||||
}
|
||||
|
||||
static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr,
|
||||
u64 guest_val, u64 host_val)
|
||||
u64 guest_val, u64 host_val, bool entry_only)
|
||||
{
|
||||
unsigned i;
|
||||
int i, j = 0;
|
||||
struct msr_autoload *m = &vmx->msr_autoload;
|
||||
|
||||
switch (msr) {
|
||||
@@ -2463,24 +2625,31 @@ static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr,
|
||||
wrmsrl(MSR_IA32_PEBS_ENABLE, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < m->nr; ++i)
|
||||
if (m->guest[i].index == msr)
|
||||
break;
|
||||
i = find_msr(&m->guest, msr);
|
||||
if (!entry_only)
|
||||
j = find_msr(&m->host, msr);
|
||||
|
||||
if (i == NR_AUTOLOAD_MSRS) {
|
||||
if (i == NR_AUTOLOAD_MSRS || j == NR_AUTOLOAD_MSRS) {
|
||||
printk_once(KERN_WARNING "Not enough msr switch entries. "
|
||||
"Can't add msr %x\n", msr);
|
||||
return;
|
||||
} else if (i == m->nr) {
|
||||
++m->nr;
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, m->nr);
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, m->nr);
|
||||
}
|
||||
if (i < 0) {
|
||||
i = m->guest.nr++;
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, m->guest.nr);
|
||||
}
|
||||
m->guest.val[i].index = msr;
|
||||
m->guest.val[i].value = guest_val;
|
||||
|
||||
m->guest[i].index = msr;
|
||||
m->guest[i].value = guest_val;
|
||||
m->host[i].index = msr;
|
||||
m->host[i].value = host_val;
|
||||
if (entry_only)
|
||||
return;
|
||||
|
||||
if (j < 0) {
|
||||
j = m->host.nr++;
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, m->host.nr);
|
||||
}
|
||||
m->host.val[j].index = msr;
|
||||
m->host.val[j].value = host_val;
|
||||
}
|
||||
|
||||
static bool update_transition_efer(struct vcpu_vmx *vmx, int efer_offset)
|
||||
@@ -2524,7 +2693,7 @@ static bool update_transition_efer(struct vcpu_vmx *vmx, int efer_offset)
|
||||
guest_efer &= ~EFER_LME;
|
||||
if (guest_efer != host_efer)
|
||||
add_atomic_switch_msr(vmx, MSR_EFER,
|
||||
guest_efer, host_efer);
|
||||
guest_efer, host_efer, false);
|
||||
return false;
|
||||
} else {
|
||||
guest_efer &= ~ignore_bits;
|
||||
@@ -3987,7 +4156,7 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
|
||||
vcpu->arch.ia32_xss = data;
|
||||
if (vcpu->arch.ia32_xss != host_xss)
|
||||
add_atomic_switch_msr(vmx, MSR_IA32_XSS,
|
||||
vcpu->arch.ia32_xss, host_xss);
|
||||
vcpu->arch.ia32_xss, host_xss, false);
|
||||
else
|
||||
clear_atomic_switch_msr(vmx, MSR_IA32_XSS);
|
||||
break;
|
||||
@@ -6274,9 +6443,9 @@ static void vmx_vcpu_setup(struct vcpu_vmx *vmx)
|
||||
|
||||
vmcs_write32(VM_EXIT_MSR_STORE_COUNT, 0);
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, 0);
|
||||
vmcs_write64(VM_EXIT_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.host));
|
||||
vmcs_write64(VM_EXIT_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.host.val));
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, 0);
|
||||
vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest));
|
||||
vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest.val));
|
||||
|
||||
if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT)
|
||||
vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat);
|
||||
@@ -6296,8 +6465,7 @@ static void vmx_vcpu_setup(struct vcpu_vmx *vmx)
|
||||
++vmx->nmsrs;
|
||||
}
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))
|
||||
rdmsrl(MSR_IA32_ARCH_CAPABILITIES, vmx->arch_capabilities);
|
||||
vmx->arch_capabilities = kvm_get_arch_capabilities();
|
||||
|
||||
vm_exit_controls_init(vmx, vmcs_config.vmexit_ctrl);
|
||||
|
||||
@@ -9548,6 +9716,79 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Software based L1D cache flush which is used when microcode providing
|
||||
* the cache control MSR is not loaded.
|
||||
*
|
||||
* The L1D cache is 32 KiB on Nehalem and later microarchitectures, but to
|
||||
* flush it is required to read in 64 KiB because the replacement algorithm
|
||||
* is not exactly LRU. This could be sized at runtime via topology
|
||||
* information but as all relevant affected CPUs have 32KiB L1D cache size
|
||||
* there is no point in doing so.
|
||||
*/
|
||||
#define L1D_CACHE_ORDER 4
|
||||
static void *vmx_l1d_flush_pages;
|
||||
|
||||
static void vmx_l1d_flush(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int size = PAGE_SIZE << L1D_CACHE_ORDER;
|
||||
|
||||
/*
|
||||
* This code is only executed when the the flush mode is 'cond' or
|
||||
* 'always'
|
||||
*/
|
||||
if (static_branch_likely(&vmx_l1d_flush_cond)) {
|
||||
bool flush_l1d;
|
||||
|
||||
/*
|
||||
* Clear the per-vcpu flush bit, it gets set again
|
||||
* either from vcpu_run() or from one of the unsafe
|
||||
* VMEXIT handlers.
|
||||
*/
|
||||
flush_l1d = vcpu->arch.l1tf_flush_l1d;
|
||||
vcpu->arch.l1tf_flush_l1d = false;
|
||||
|
||||
/*
|
||||
* Clear the per-cpu flush bit, it gets set again from
|
||||
* the interrupt handlers.
|
||||
*/
|
||||
flush_l1d |= kvm_get_cpu_l1tf_flush_l1d();
|
||||
kvm_clear_cpu_l1tf_flush_l1d();
|
||||
|
||||
if (!flush_l1d)
|
||||
return;
|
||||
}
|
||||
|
||||
vcpu->stat.l1d_flush++;
|
||||
|
||||
if (static_cpu_has(X86_FEATURE_FLUSH_L1D)) {
|
||||
wrmsrl(MSR_IA32_FLUSH_CMD, L1D_FLUSH);
|
||||
return;
|
||||
}
|
||||
|
||||
asm volatile(
|
||||
/* First ensure the pages are in the TLB */
|
||||
"xorl %%eax, %%eax\n"
|
||||
".Lpopulate_tlb:\n\t"
|
||||
"movzbl (%[flush_pages], %%" _ASM_AX "), %%ecx\n\t"
|
||||
"addl $4096, %%eax\n\t"
|
||||
"cmpl %%eax, %[size]\n\t"
|
||||
"jne .Lpopulate_tlb\n\t"
|
||||
"xorl %%eax, %%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
/* Now fill the cache */
|
||||
"xorl %%eax, %%eax\n"
|
||||
".Lfill_cache:\n"
|
||||
"movzbl (%[flush_pages], %%" _ASM_AX "), %%ecx\n\t"
|
||||
"addl $64, %%eax\n\t"
|
||||
"cmpl %%eax, %[size]\n\t"
|
||||
"jne .Lfill_cache\n\t"
|
||||
"lfence\n"
|
||||
:: [flush_pages] "r" (vmx_l1d_flush_pages),
|
||||
[size] "r" (size)
|
||||
: "eax", "ebx", "ecx", "edx");
|
||||
}
|
||||
|
||||
static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr)
|
||||
{
|
||||
struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
|
||||
@@ -9949,7 +10190,7 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx)
|
||||
clear_atomic_switch_msr(vmx, msrs[i].msr);
|
||||
else
|
||||
add_atomic_switch_msr(vmx, msrs[i].msr, msrs[i].guest,
|
||||
msrs[i].host);
|
||||
msrs[i].host, false);
|
||||
}
|
||||
|
||||
static void vmx_arm_hv_timer(struct kvm_vcpu *vcpu)
|
||||
@@ -10044,6 +10285,9 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
evmcs_rsp = static_branch_unlikely(&enable_evmcs) ?
|
||||
(unsigned long)¤t_evmcs->host_rsp : 0;
|
||||
|
||||
if (static_branch_unlikely(&vmx_l1d_should_flush))
|
||||
vmx_l1d_flush(vcpu);
|
||||
|
||||
asm(
|
||||
/* Store host registers */
|
||||
"push %%" _ASM_DX "; push %%" _ASM_BP ";"
|
||||
@@ -10403,10 +10647,37 @@ free_vcpu:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
#define L1TF_MSG_SMT "L1TF CPU bug present and SMT on, data leak possible. See CVE-2018-3646 and https://www.kernel.org/doc/html/latest/admin-guide/l1tf.html for details.\n"
|
||||
#define L1TF_MSG_L1D "L1TF CPU bug present and virtualization mitigation disabled, data leak possible. See CVE-2018-3646 and https://www.kernel.org/doc/html/latest/admin-guide/l1tf.html for details.\n"
|
||||
|
||||
static int vmx_vm_init(struct kvm *kvm)
|
||||
{
|
||||
if (!ple_gap)
|
||||
kvm->arch.pause_in_guest = true;
|
||||
|
||||
if (boot_cpu_has(X86_BUG_L1TF) && enable_ept) {
|
||||
switch (l1tf_mitigation) {
|
||||
case L1TF_MITIGATION_OFF:
|
||||
case L1TF_MITIGATION_FLUSH_NOWARN:
|
||||
/* 'I explicitly don't care' is set */
|
||||
break;
|
||||
case L1TF_MITIGATION_FLUSH:
|
||||
case L1TF_MITIGATION_FLUSH_NOSMT:
|
||||
case L1TF_MITIGATION_FULL:
|
||||
/*
|
||||
* Warn upon starting the first VM in a potentially
|
||||
* insecure environment.
|
||||
*/
|
||||
if (cpu_smt_control == CPU_SMT_ENABLED)
|
||||
pr_warn_once(L1TF_MSG_SMT);
|
||||
if (l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_NEVER)
|
||||
pr_warn_once(L1TF_MSG_L1D);
|
||||
break;
|
||||
case L1TF_MITIGATION_FULL_FORCE:
|
||||
/* Flush is enforced */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -11260,10 +11531,10 @@ static void prepare_vmcs02_full(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
|
||||
* Set the MSR load/store lists to match L0's settings.
|
||||
*/
|
||||
vmcs_write32(VM_EXIT_MSR_STORE_COUNT, 0);
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.nr);
|
||||
vmcs_write64(VM_EXIT_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.host));
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.nr);
|
||||
vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest));
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.host.nr);
|
||||
vmcs_write64(VM_EXIT_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.host.val));
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.guest.nr);
|
||||
vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest.val));
|
||||
|
||||
set_cr4_guest_host_mask(vmx);
|
||||
|
||||
@@ -11899,6 +12170,9 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Hide L1D cache contents from the nested guest. */
|
||||
vmx->vcpu.arch.l1tf_flush_l1d = true;
|
||||
|
||||
/*
|
||||
* If we're entering a halted L2 vcpu and the L2 vcpu won't be woken
|
||||
* by event injection, halt vcpu.
|
||||
@@ -12419,8 +12693,8 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason,
|
||||
vmx_segment_cache_clear(vmx);
|
||||
|
||||
/* Update any VMCS fields that might have changed while L2 ran */
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.nr);
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.nr);
|
||||
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.host.nr);
|
||||
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.guest.nr);
|
||||
vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset);
|
||||
if (vmx->hv_deadline_tsc == -1)
|
||||
vmcs_clear_bits(PIN_BASED_VM_EXEC_CONTROL,
|
||||
@@ -13137,6 +13411,51 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
|
||||
.enable_smi_window = enable_smi_window,
|
||||
};
|
||||
|
||||
static void vmx_cleanup_l1d_flush(void)
|
||||
{
|
||||
if (vmx_l1d_flush_pages) {
|
||||
free_pages((unsigned long)vmx_l1d_flush_pages, L1D_CACHE_ORDER);
|
||||
vmx_l1d_flush_pages = NULL;
|
||||
}
|
||||
/* Restore state so sysfs ignores VMX */
|
||||
l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_AUTO;
|
||||
}
|
||||
|
||||
static void vmx_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
RCU_INIT_POINTER(crash_vmclear_loaded_vmcss, NULL);
|
||||
synchronize_rcu();
|
||||
#endif
|
||||
|
||||
kvm_exit();
|
||||
|
||||
#if IS_ENABLED(CONFIG_HYPERV)
|
||||
if (static_branch_unlikely(&enable_evmcs)) {
|
||||
int cpu;
|
||||
struct hv_vp_assist_page *vp_ap;
|
||||
/*
|
||||
* Reset everything to support using non-enlightened VMCS
|
||||
* access later (e.g. when we reload the module with
|
||||
* enlightened_vmcs=0)
|
||||
*/
|
||||
for_each_online_cpu(cpu) {
|
||||
vp_ap = hv_get_vp_assist_page(cpu);
|
||||
|
||||
if (!vp_ap)
|
||||
continue;
|
||||
|
||||
vp_ap->current_nested_vmcs = 0;
|
||||
vp_ap->enlighten_vmentry = 0;
|
||||
}
|
||||
|
||||
static_branch_disable(&enable_evmcs);
|
||||
}
|
||||
#endif
|
||||
vmx_cleanup_l1d_flush();
|
||||
}
|
||||
module_exit(vmx_exit);
|
||||
|
||||
static int __init vmx_init(void)
|
||||
{
|
||||
int r;
|
||||
@@ -13171,10 +13490,25 @@ static int __init vmx_init(void)
|
||||
#endif
|
||||
|
||||
r = kvm_init(&vmx_x86_ops, sizeof(struct vcpu_vmx),
|
||||
__alignof__(struct vcpu_vmx), THIS_MODULE);
|
||||
__alignof__(struct vcpu_vmx), THIS_MODULE);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* Must be called after kvm_init() so enable_ept is properly set
|
||||
* up. Hand the parameter mitigation value in which was stored in
|
||||
* the pre module init parser. If no parameter was given, it will
|
||||
* contain 'auto' which will be turned into the default 'cond'
|
||||
* mitigation mode.
|
||||
*/
|
||||
if (boot_cpu_has(X86_BUG_L1TF)) {
|
||||
r = vmx_setup_l1d_flush(vmentry_l1d_flush_param);
|
||||
if (r) {
|
||||
vmx_exit();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
rcu_assign_pointer(crash_vmclear_loaded_vmcss,
|
||||
crash_vmclear_local_loaded_vmcss);
|
||||
@@ -13183,39 +13517,4 @@ static int __init vmx_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit vmx_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
RCU_INIT_POINTER(crash_vmclear_loaded_vmcss, NULL);
|
||||
synchronize_rcu();
|
||||
#endif
|
||||
|
||||
kvm_exit();
|
||||
|
||||
#if IS_ENABLED(CONFIG_HYPERV)
|
||||
if (static_branch_unlikely(&enable_evmcs)) {
|
||||
int cpu;
|
||||
struct hv_vp_assist_page *vp_ap;
|
||||
/*
|
||||
* Reset everything to support using non-enlightened VMCS
|
||||
* access later (e.g. when we reload the module with
|
||||
* enlightened_vmcs=0)
|
||||
*/
|
||||
for_each_online_cpu(cpu) {
|
||||
vp_ap = hv_get_vp_assist_page(cpu);
|
||||
|
||||
if (!vp_ap)
|
||||
continue;
|
||||
|
||||
vp_ap->current_nested_vmcs = 0;
|
||||
vp_ap->enlighten_vmentry = 0;
|
||||
}
|
||||
|
||||
static_branch_disable(&enable_evmcs);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(vmx_init)
|
||||
module_exit(vmx_exit)
|
||||
module_init(vmx_init);
|
||||
|
||||
+33
-1
@@ -195,6 +195,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
||||
{ "irq_injections", VCPU_STAT(irq_injections) },
|
||||
{ "nmi_injections", VCPU_STAT(nmi_injections) },
|
||||
{ "req_event", VCPU_STAT(req_event) },
|
||||
{ "l1d_flush", VCPU_STAT(l1d_flush) },
|
||||
{ "mmu_shadow_zapped", VM_STAT(mmu_shadow_zapped) },
|
||||
{ "mmu_pte_write", VM_STAT(mmu_pte_write) },
|
||||
{ "mmu_pte_updated", VM_STAT(mmu_pte_updated) },
|
||||
@@ -1102,11 +1103,35 @@ static u32 msr_based_features[] = {
|
||||
|
||||
static unsigned int num_msr_based_features;
|
||||
|
||||
u64 kvm_get_arch_capabilities(void)
|
||||
{
|
||||
u64 data;
|
||||
|
||||
rdmsrl_safe(MSR_IA32_ARCH_CAPABILITIES, &data);
|
||||
|
||||
/*
|
||||
* If we're doing cache flushes (either "always" or "cond")
|
||||
* we will do one whenever the guest does a vmlaunch/vmresume.
|
||||
* If an outer hypervisor is doing the cache flush for us
|
||||
* (VMENTER_L1D_FLUSH_NESTED_VM), we can safely pass that
|
||||
* capability to the guest too, and if EPT is disabled we're not
|
||||
* vulnerable. Overall, only VMENTER_L1D_FLUSH_NEVER will
|
||||
* require a nested hypervisor to do a flush of its own.
|
||||
*/
|
||||
if (l1tf_vmx_mitigation != VMENTER_L1D_FLUSH_NEVER)
|
||||
data |= ARCH_CAP_SKIP_VMENTRY_L1DFLUSH;
|
||||
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_get_arch_capabilities);
|
||||
|
||||
static int kvm_get_msr_feature(struct kvm_msr_entry *msr)
|
||||
{
|
||||
switch (msr->index) {
|
||||
case MSR_IA32_UCODE_REV:
|
||||
case MSR_IA32_ARCH_CAPABILITIES:
|
||||
msr->data = kvm_get_arch_capabilities();
|
||||
break;
|
||||
case MSR_IA32_UCODE_REV:
|
||||
rdmsrl_safe(msr->index, &msr->data);
|
||||
break;
|
||||
default:
|
||||
@@ -4876,6 +4901,9 @@ static int emulator_write_std(struct x86_emulate_ctxt *ctxt, gva_t addr, void *v
|
||||
int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu, gva_t addr, void *val,
|
||||
unsigned int bytes, struct x86_exception *exception)
|
||||
{
|
||||
/* kvm_write_guest_virt_system can pull in tons of pages. */
|
||||
vcpu->arch.l1tf_flush_l1d = true;
|
||||
|
||||
return kvm_write_guest_virt_helper(addr, val, bytes, vcpu,
|
||||
PFERR_WRITE_MASK, exception);
|
||||
}
|
||||
@@ -6052,6 +6080,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
|
||||
bool writeback = true;
|
||||
bool write_fault_to_spt = vcpu->arch.write_fault_to_shadow_pgtable;
|
||||
|
||||
vcpu->arch.l1tf_flush_l1d = true;
|
||||
|
||||
/*
|
||||
* Clear write_fault_to_shadow_pgtable here to ensure it is
|
||||
* never reused.
|
||||
@@ -7581,6 +7611,7 @@ static int vcpu_run(struct kvm_vcpu *vcpu)
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
|
||||
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
vcpu->arch.l1tf_flush_l1d = true;
|
||||
|
||||
for (;;) {
|
||||
if (kvm_vcpu_running(vcpu)) {
|
||||
@@ -8700,6 +8731,7 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
|
||||
|
||||
void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu)
|
||||
{
|
||||
vcpu->arch.l1tf_flush_l1d = true;
|
||||
kvm_x86_ops->sched_in(vcpu, cpu);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <linux/swap.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/bootmem.h> /* for max_low_pfn */
|
||||
#include <linux/swapfile.h>
|
||||
#include <linux/swapops.h>
|
||||
|
||||
#include <asm/set_memory.h>
|
||||
#include <asm/e820/api.h>
|
||||
@@ -880,3 +882,26 @@ void update_cache_mode_entry(unsigned entry, enum page_cache_mode cache)
|
||||
__cachemode2pte_tbl[cache] = __cm_idx2pte(entry);
|
||||
__pte2cachemode_tbl[entry] = cache;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SWAP
|
||||
unsigned long max_swapfile_size(void)
|
||||
{
|
||||
unsigned long pages;
|
||||
|
||||
pages = generic_max_swapfile_size();
|
||||
|
||||
if (boot_cpu_has_bug(X86_BUG_L1TF)) {
|
||||
/* Limit the swap file size to MAX_PA/2 for L1TF workaround */
|
||||
unsigned long l1tf_limit = l1tf_pfn_limit() + 1;
|
||||
/*
|
||||
* We encode swap offsets also with 3 bits below those for pfn
|
||||
* which makes the usable limit higher.
|
||||
*/
|
||||
#if CONFIG_PGTABLE_LEVELS > 2
|
||||
l1tf_limit <<= PAGE_SHIFT - SWP_OFFSET_FIRST_BIT;
|
||||
#endif
|
||||
pages = min_t(unsigned long, l1tf_limit, pages);
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
#endif
|
||||
|
||||
+15
-10
@@ -126,24 +126,29 @@ static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long addr)
|
||||
|
||||
static void clear_pmd_presence(pmd_t *pmd, bool clear, pmdval_t *old)
|
||||
{
|
||||
pmd_t new_pmd;
|
||||
pmdval_t v = pmd_val(*pmd);
|
||||
if (clear) {
|
||||
*old = v & _PAGE_PRESENT;
|
||||
v &= ~_PAGE_PRESENT;
|
||||
} else /* presume this has been called with clear==true previously */
|
||||
v |= *old;
|
||||
set_pmd(pmd, __pmd(v));
|
||||
*old = v;
|
||||
new_pmd = pmd_mknotpresent(*pmd);
|
||||
} else {
|
||||
/* Presume this has been called with clear==true previously */
|
||||
new_pmd = __pmd(*old);
|
||||
}
|
||||
set_pmd(pmd, new_pmd);
|
||||
}
|
||||
|
||||
static void clear_pte_presence(pte_t *pte, bool clear, pteval_t *old)
|
||||
{
|
||||
pteval_t v = pte_val(*pte);
|
||||
if (clear) {
|
||||
*old = v & _PAGE_PRESENT;
|
||||
v &= ~_PAGE_PRESENT;
|
||||
} else /* presume this has been called with clear==true previously */
|
||||
v |= *old;
|
||||
set_pte_atomic(pte, __pte(v));
|
||||
*old = v;
|
||||
/* Nothing should care about address */
|
||||
pte_clear(&init_mm, 0, pte);
|
||||
} else {
|
||||
/* Presume this has been called with clear==true previously */
|
||||
set_pte_atomic(pte, __pte(*old));
|
||||
}
|
||||
}
|
||||
|
||||
static int clear_page_presence(struct kmmio_fault_page *f, bool clear)
|
||||
|
||||
@@ -240,3 +240,24 @@ int valid_mmap_phys_addr_range(unsigned long pfn, size_t count)
|
||||
|
||||
return phys_addr_valid(addr + count - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only allow root to set high MMIO mappings to PROT_NONE.
|
||||
* This prevents an unpriv. user to set them to PROT_NONE and invert
|
||||
* them, then pointing to valid memory for L1TF speculation.
|
||||
*
|
||||
* Note: for locked down kernels may want to disable the root override.
|
||||
*/
|
||||
bool pfn_modify_allowed(unsigned long pfn, pgprot_t prot)
|
||||
{
|
||||
if (!boot_cpu_has_bug(X86_BUG_L1TF))
|
||||
return true;
|
||||
if (!__pte_needs_invert(pgprot_val(prot)))
|
||||
return true;
|
||||
/* If it's real memory always allow */
|
||||
if (pfn_valid(pfn))
|
||||
return true;
|
||||
if (pfn > l1tf_pfn_limit() && !capable(CAP_SYS_ADMIN))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
+10
-4
@@ -1014,8 +1014,8 @@ static long populate_pmd(struct cpa_data *cpa,
|
||||
|
||||
pmd = pmd_offset(pud, start);
|
||||
|
||||
set_pmd(pmd, __pmd(cpa->pfn << PAGE_SHIFT | _PAGE_PSE |
|
||||
massage_pgprot(pmd_pgprot)));
|
||||
set_pmd(pmd, pmd_mkhuge(pfn_pmd(cpa->pfn,
|
||||
canon_pgprot(pmd_pgprot))));
|
||||
|
||||
start += PMD_SIZE;
|
||||
cpa->pfn += PMD_SIZE >> PAGE_SHIFT;
|
||||
@@ -1087,8 +1087,8 @@ static int populate_pud(struct cpa_data *cpa, unsigned long start, p4d_t *p4d,
|
||||
* Map everything starting from the Gb boundary, possibly with 1G pages
|
||||
*/
|
||||
while (boot_cpu_has(X86_FEATURE_GBPAGES) && end - start >= PUD_SIZE) {
|
||||
set_pud(pud, __pud(cpa->pfn << PAGE_SHIFT | _PAGE_PSE |
|
||||
massage_pgprot(pud_pgprot)));
|
||||
set_pud(pud, pud_mkhuge(pfn_pud(cpa->pfn,
|
||||
canon_pgprot(pud_pgprot))));
|
||||
|
||||
start += PUD_SIZE;
|
||||
cpa->pfn += PUD_SIZE >> PAGE_SHIFT;
|
||||
@@ -1784,6 +1784,12 @@ int set_memory_nonglobal(unsigned long addr, int numpages)
|
||||
__pgprot(_PAGE_GLOBAL), 0);
|
||||
}
|
||||
|
||||
int set_memory_global(unsigned long addr, int numpages)
|
||||
{
|
||||
return change_page_attr_set(&addr, numpages,
|
||||
__pgprot(_PAGE_GLOBAL), 0);
|
||||
}
|
||||
|
||||
static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
|
||||
{
|
||||
struct cpa_data cpa;
|
||||
|
||||
+53
-8
@@ -719,28 +719,50 @@ int pmd_clear_huge(pmd_t *pmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
/**
|
||||
* pud_free_pmd_page - Clear pud entry and free pmd page.
|
||||
* @pud: Pointer to a PUD.
|
||||
* @addr: Virtual address associated with pud.
|
||||
*
|
||||
* Context: The pud range has been unmaped and TLB purged.
|
||||
* Context: The pud range has been unmapped and TLB purged.
|
||||
* Return: 1 if clearing the entry succeeded. 0 otherwise.
|
||||
*
|
||||
* NOTE: Callers must allow a single page allocation.
|
||||
*/
|
||||
int pud_free_pmd_page(pud_t *pud)
|
||||
int pud_free_pmd_page(pud_t *pud, unsigned long addr)
|
||||
{
|
||||
pmd_t *pmd;
|
||||
pmd_t *pmd, *pmd_sv;
|
||||
pte_t *pte;
|
||||
int i;
|
||||
|
||||
if (pud_none(*pud))
|
||||
return 1;
|
||||
|
||||
pmd = (pmd_t *)pud_page_vaddr(*pud);
|
||||
pmd_sv = (pmd_t *)__get_free_page(GFP_KERNEL);
|
||||
if (!pmd_sv)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < PTRS_PER_PMD; i++)
|
||||
if (!pmd_free_pte_page(&pmd[i]))
|
||||
return 0;
|
||||
for (i = 0; i < PTRS_PER_PMD; i++) {
|
||||
pmd_sv[i] = pmd[i];
|
||||
if (!pmd_none(pmd[i]))
|
||||
pmd_clear(&pmd[i]);
|
||||
}
|
||||
|
||||
pud_clear(pud);
|
||||
|
||||
/* INVLPG to clear all paging-structure caches */
|
||||
flush_tlb_kernel_range(addr, addr + PAGE_SIZE-1);
|
||||
|
||||
for (i = 0; i < PTRS_PER_PMD; i++) {
|
||||
if (!pmd_none(pmd_sv[i])) {
|
||||
pte = (pte_t *)pmd_page_vaddr(pmd_sv[i]);
|
||||
free_page((unsigned long)pte);
|
||||
}
|
||||
}
|
||||
|
||||
free_page((unsigned long)pmd_sv);
|
||||
free_page((unsigned long)pmd);
|
||||
|
||||
return 1;
|
||||
@@ -749,11 +771,12 @@ int pud_free_pmd_page(pud_t *pud)
|
||||
/**
|
||||
* pmd_free_pte_page - Clear pmd entry and free pte page.
|
||||
* @pmd: Pointer to a PMD.
|
||||
* @addr: Virtual address associated with pmd.
|
||||
*
|
||||
* Context: The pmd range has been unmaped and TLB purged.
|
||||
* Context: The pmd range has been unmapped and TLB purged.
|
||||
* Return: 1 if clearing the entry succeeded. 0 otherwise.
|
||||
*/
|
||||
int pmd_free_pte_page(pmd_t *pmd)
|
||||
int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
|
||||
{
|
||||
pte_t *pte;
|
||||
|
||||
@@ -762,8 +785,30 @@ int pmd_free_pte_page(pmd_t *pmd)
|
||||
|
||||
pte = (pte_t *)pmd_page_vaddr(*pmd);
|
||||
pmd_clear(pmd);
|
||||
|
||||
/* INVLPG to clear all paging-structure caches */
|
||||
flush_tlb_kernel_range(addr, addr + PAGE_SIZE-1);
|
||||
|
||||
free_page((unsigned long)pte);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_X86_64 */
|
||||
|
||||
int pud_free_pmd_page(pud_t *pud, unsigned long addr)
|
||||
{
|
||||
return pud_none(*pud);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable free page handling on x86-PAE. This assures that ioremap()
|
||||
* does not update sync'd pmd entries. See vmalloc_sync_one().
|
||||
*/
|
||||
int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
|
||||
{
|
||||
return pmd_none(*pmd);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_X86_64 */
|
||||
#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
|
||||
|
||||
+25
-10
@@ -45,6 +45,7 @@
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/desc.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "Kernel/User page tables isolation: " fmt
|
||||
@@ -434,6 +435,13 @@ static inline bool pti_kernel_image_global_ok(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the only user for these and it is not arch-generic
|
||||
* like the other set_memory.h functions. Just extern them.
|
||||
*/
|
||||
extern int set_memory_nonglobal(unsigned long addr, int numpages);
|
||||
extern int set_memory_global(unsigned long addr, int numpages);
|
||||
|
||||
/*
|
||||
* For some configurations, map all of kernel text into the user page
|
||||
* tables. This reduces TLB misses, especially on non-PCID systems.
|
||||
@@ -446,7 +454,8 @@ void pti_clone_kernel_text(void)
|
||||
* clone the areas past rodata, they might contain secrets.
|
||||
*/
|
||||
unsigned long start = PFN_ALIGN(_text);
|
||||
unsigned long end = (unsigned long)__end_rodata_hpage_align;
|
||||
unsigned long end_clone = (unsigned long)__end_rodata_hpage_align;
|
||||
unsigned long end_global = PFN_ALIGN((unsigned long)__stop___ex_table);
|
||||
|
||||
if (!pti_kernel_image_global_ok())
|
||||
return;
|
||||
@@ -458,14 +467,18 @@ void pti_clone_kernel_text(void)
|
||||
* pti_set_kernel_image_nonglobal() did to clear the
|
||||
* global bit.
|
||||
*/
|
||||
pti_clone_pmds(start, end, _PAGE_RW);
|
||||
pti_clone_pmds(start, end_clone, _PAGE_RW);
|
||||
|
||||
/*
|
||||
* pti_clone_pmds() will set the global bit in any PMDs
|
||||
* that it clones, but we also need to get any PTEs in
|
||||
* the last level for areas that are not huge-page-aligned.
|
||||
*/
|
||||
|
||||
/* Set the global bit for normal non-__init kernel text: */
|
||||
set_memory_global(start, (end_global - start) >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the only user for it and it is not arch-generic like
|
||||
* the other set_memory.h functions. Just extern it.
|
||||
*/
|
||||
extern int set_memory_nonglobal(unsigned long addr, int numpages);
|
||||
void pti_set_kernel_image_nonglobal(void)
|
||||
{
|
||||
/*
|
||||
@@ -477,9 +490,11 @@ void pti_set_kernel_image_nonglobal(void)
|
||||
unsigned long start = PFN_ALIGN(_text);
|
||||
unsigned long end = ALIGN((unsigned long)_end, PMD_PAGE_SIZE);
|
||||
|
||||
if (pti_kernel_image_global_ok())
|
||||
return;
|
||||
|
||||
/*
|
||||
* This clears _PAGE_GLOBAL from the entire kernel image.
|
||||
* pti_clone_kernel_text() map put _PAGE_GLOBAL back for
|
||||
* areas that are mapped to userspace.
|
||||
*/
|
||||
set_memory_nonglobal(start, (end - start) >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <asm/io_apic.h>
|
||||
#include <asm/hw_irq.h>
|
||||
|
||||
#define TANGIER_EXT_TIMER0_MSI 12
|
||||
|
||||
|
||||
@@ -1285,6 +1285,7 @@ void uv_bau_message_interrupt(struct pt_regs *regs)
|
||||
struct msg_desc msgdesc;
|
||||
|
||||
ack_APIC_irq();
|
||||
kvm_set_cpu_l1tf_flush_l1d();
|
||||
time_start = get_cycles();
|
||||
|
||||
bcp = &per_cpu(bau_control, smp_processor_id());
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#endif
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <xen/features.h>
|
||||
#include <xen/page.h>
|
||||
|
||||
@@ -1259,6 +1259,9 @@ asmlinkage __visible void __init xen_start_kernel(void)
|
||||
get_cpu_cap(&boot_cpu_data);
|
||||
x86_configure_nx();
|
||||
|
||||
/* Determine virtual and physical address sizes */
|
||||
get_cpu_address_sizes(&boot_cpu_data);
|
||||
|
||||
/* Let's presume PV guests always boot on vCPU with id 0. */
|
||||
per_cpu(xen_vcpu_id, 0) = 0;
|
||||
|
||||
|
||||
+26
-31
@@ -71,11 +71,9 @@ static inline u8 *ablkcipher_get_spot(u8 *start, unsigned int len)
|
||||
return max(start, end_page);
|
||||
}
|
||||
|
||||
static inline unsigned int ablkcipher_done_slow(struct ablkcipher_walk *walk,
|
||||
unsigned int bsize)
|
||||
static inline void ablkcipher_done_slow(struct ablkcipher_walk *walk,
|
||||
unsigned int n)
|
||||
{
|
||||
unsigned int n = bsize;
|
||||
|
||||
for (;;) {
|
||||
unsigned int len_this_page = scatterwalk_pagelen(&walk->out);
|
||||
|
||||
@@ -87,17 +85,13 @@ static inline unsigned int ablkcipher_done_slow(struct ablkcipher_walk *walk,
|
||||
n -= len_this_page;
|
||||
scatterwalk_start(&walk->out, sg_next(walk->out.sg));
|
||||
}
|
||||
|
||||
return bsize;
|
||||
}
|
||||
|
||||
static inline unsigned int ablkcipher_done_fast(struct ablkcipher_walk *walk,
|
||||
unsigned int n)
|
||||
static inline void ablkcipher_done_fast(struct ablkcipher_walk *walk,
|
||||
unsigned int n)
|
||||
{
|
||||
scatterwalk_advance(&walk->in, n);
|
||||
scatterwalk_advance(&walk->out, n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int ablkcipher_walk_next(struct ablkcipher_request *req,
|
||||
@@ -107,39 +101,40 @@ int ablkcipher_walk_done(struct ablkcipher_request *req,
|
||||
struct ablkcipher_walk *walk, int err)
|
||||
{
|
||||
struct crypto_tfm *tfm = req->base.tfm;
|
||||
unsigned int nbytes = 0;
|
||||
unsigned int n; /* bytes processed */
|
||||
bool more;
|
||||
|
||||
if (likely(err >= 0)) {
|
||||
unsigned int n = walk->nbytes - err;
|
||||
if (unlikely(err < 0))
|
||||
goto finish;
|
||||
|
||||
if (likely(!(walk->flags & ABLKCIPHER_WALK_SLOW)))
|
||||
n = ablkcipher_done_fast(walk, n);
|
||||
else if (WARN_ON(err)) {
|
||||
n = walk->nbytes - err;
|
||||
walk->total -= n;
|
||||
more = (walk->total != 0);
|
||||
|
||||
if (likely(!(walk->flags & ABLKCIPHER_WALK_SLOW))) {
|
||||
ablkcipher_done_fast(walk, n);
|
||||
} else {
|
||||
if (WARN_ON(err)) {
|
||||
/* unexpected case; didn't process all bytes */
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
} else
|
||||
n = ablkcipher_done_slow(walk, n);
|
||||
|
||||
nbytes = walk->total - n;
|
||||
err = 0;
|
||||
goto finish;
|
||||
}
|
||||
ablkcipher_done_slow(walk, n);
|
||||
}
|
||||
|
||||
scatterwalk_done(&walk->in, 0, nbytes);
|
||||
scatterwalk_done(&walk->out, 1, nbytes);
|
||||
scatterwalk_done(&walk->in, 0, more);
|
||||
scatterwalk_done(&walk->out, 1, more);
|
||||
|
||||
err:
|
||||
walk->total = nbytes;
|
||||
walk->nbytes = nbytes;
|
||||
|
||||
if (nbytes) {
|
||||
if (more) {
|
||||
crypto_yield(req->base.flags);
|
||||
return ablkcipher_walk_next(req, walk);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
finish:
|
||||
walk->nbytes = 0;
|
||||
if (walk->iv != req->info)
|
||||
memcpy(req->info, walk->iv, tfm->crt_ablkcipher.ivsize);
|
||||
kfree(walk->iv_buffer);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ablkcipher_walk_done);
|
||||
|
||||
+26
-28
@@ -70,19 +70,18 @@ static inline u8 *blkcipher_get_spot(u8 *start, unsigned int len)
|
||||
return max(start, end_page);
|
||||
}
|
||||
|
||||
static inline unsigned int blkcipher_done_slow(struct blkcipher_walk *walk,
|
||||
unsigned int bsize)
|
||||
static inline void blkcipher_done_slow(struct blkcipher_walk *walk,
|
||||
unsigned int bsize)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
addr = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1);
|
||||
addr = blkcipher_get_spot(addr, bsize);
|
||||
scatterwalk_copychunks(addr, &walk->out, bsize, 1);
|
||||
return bsize;
|
||||
}
|
||||
|
||||
static inline unsigned int blkcipher_done_fast(struct blkcipher_walk *walk,
|
||||
unsigned int n)
|
||||
static inline void blkcipher_done_fast(struct blkcipher_walk *walk,
|
||||
unsigned int n)
|
||||
{
|
||||
if (walk->flags & BLKCIPHER_WALK_COPY) {
|
||||
blkcipher_map_dst(walk);
|
||||
@@ -96,49 +95,48 @@ static inline unsigned int blkcipher_done_fast(struct blkcipher_walk *walk,
|
||||
|
||||
scatterwalk_advance(&walk->in, n);
|
||||
scatterwalk_advance(&walk->out, n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int blkcipher_walk_done(struct blkcipher_desc *desc,
|
||||
struct blkcipher_walk *walk, int err)
|
||||
{
|
||||
unsigned int nbytes = 0;
|
||||
unsigned int n; /* bytes processed */
|
||||
bool more;
|
||||
|
||||
if (likely(err >= 0)) {
|
||||
unsigned int n = walk->nbytes - err;
|
||||
if (unlikely(err < 0))
|
||||
goto finish;
|
||||
|
||||
if (likely(!(walk->flags & BLKCIPHER_WALK_SLOW)))
|
||||
n = blkcipher_done_fast(walk, n);
|
||||
else if (WARN_ON(err)) {
|
||||
n = walk->nbytes - err;
|
||||
walk->total -= n;
|
||||
more = (walk->total != 0);
|
||||
|
||||
if (likely(!(walk->flags & BLKCIPHER_WALK_SLOW))) {
|
||||
blkcipher_done_fast(walk, n);
|
||||
} else {
|
||||
if (WARN_ON(err)) {
|
||||
/* unexpected case; didn't process all bytes */
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
} else
|
||||
n = blkcipher_done_slow(walk, n);
|
||||
|
||||
nbytes = walk->total - n;
|
||||
err = 0;
|
||||
goto finish;
|
||||
}
|
||||
blkcipher_done_slow(walk, n);
|
||||
}
|
||||
|
||||
scatterwalk_done(&walk->in, 0, nbytes);
|
||||
scatterwalk_done(&walk->out, 1, nbytes);
|
||||
scatterwalk_done(&walk->in, 0, more);
|
||||
scatterwalk_done(&walk->out, 1, more);
|
||||
|
||||
err:
|
||||
walk->total = nbytes;
|
||||
walk->nbytes = nbytes;
|
||||
|
||||
if (nbytes) {
|
||||
if (more) {
|
||||
crypto_yield(desc->flags);
|
||||
return blkcipher_walk_next(desc, walk);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
finish:
|
||||
walk->nbytes = 0;
|
||||
if (walk->iv != desc->info)
|
||||
memcpy(desc->info, walk->iv, walk->ivsize);
|
||||
if (walk->buffer != walk->page)
|
||||
kfree(walk->buffer);
|
||||
if (walk->page)
|
||||
free_page((unsigned long)walk->page);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blkcipher_walk_done);
|
||||
|
||||
+26
-25
@@ -95,7 +95,7 @@ static inline u8 *skcipher_get_spot(u8 *start, unsigned int len)
|
||||
return max(start, end_page);
|
||||
}
|
||||
|
||||
static int skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
|
||||
static void skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
@@ -103,23 +103,24 @@ static int skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
|
||||
addr = skcipher_get_spot(addr, bsize);
|
||||
scatterwalk_copychunks(addr, &walk->out, bsize,
|
||||
(walk->flags & SKCIPHER_WALK_PHYS) ? 2 : 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int skcipher_walk_done(struct skcipher_walk *walk, int err)
|
||||
{
|
||||
unsigned int n = walk->nbytes - err;
|
||||
unsigned int nbytes;
|
||||
unsigned int n; /* bytes processed */
|
||||
bool more;
|
||||
|
||||
nbytes = walk->total - n;
|
||||
if (unlikely(err < 0))
|
||||
goto finish;
|
||||
|
||||
if (unlikely(err < 0)) {
|
||||
nbytes = 0;
|
||||
n = 0;
|
||||
} else if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS |
|
||||
SKCIPHER_WALK_SLOW |
|
||||
SKCIPHER_WALK_COPY |
|
||||
SKCIPHER_WALK_DIFF)))) {
|
||||
n = walk->nbytes - err;
|
||||
walk->total -= n;
|
||||
more = (walk->total != 0);
|
||||
|
||||
if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS |
|
||||
SKCIPHER_WALK_SLOW |
|
||||
SKCIPHER_WALK_COPY |
|
||||
SKCIPHER_WALK_DIFF)))) {
|
||||
unmap_src:
|
||||
skcipher_unmap_src(walk);
|
||||
} else if (walk->flags & SKCIPHER_WALK_DIFF) {
|
||||
@@ -131,28 +132,28 @@ unmap_src:
|
||||
skcipher_unmap_dst(walk);
|
||||
} else if (unlikely(walk->flags & SKCIPHER_WALK_SLOW)) {
|
||||
if (WARN_ON(err)) {
|
||||
/* unexpected case; didn't process all bytes */
|
||||
err = -EINVAL;
|
||||
nbytes = 0;
|
||||
} else
|
||||
n = skcipher_done_slow(walk, n);
|
||||
goto finish;
|
||||
}
|
||||
skcipher_done_slow(walk, n);
|
||||
goto already_advanced;
|
||||
}
|
||||
|
||||
if (err > 0)
|
||||
err = 0;
|
||||
|
||||
walk->total = nbytes;
|
||||
walk->nbytes = nbytes;
|
||||
|
||||
scatterwalk_advance(&walk->in, n);
|
||||
scatterwalk_advance(&walk->out, n);
|
||||
scatterwalk_done(&walk->in, 0, nbytes);
|
||||
scatterwalk_done(&walk->out, 1, nbytes);
|
||||
already_advanced:
|
||||
scatterwalk_done(&walk->in, 0, more);
|
||||
scatterwalk_done(&walk->out, 1, more);
|
||||
|
||||
if (nbytes) {
|
||||
if (more) {
|
||||
crypto_yield(walk->flags & SKCIPHER_WALK_SLEEP ?
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP : 0);
|
||||
return skcipher_walk_next(walk);
|
||||
}
|
||||
err = 0;
|
||||
finish:
|
||||
walk->nbytes = 0;
|
||||
|
||||
/* Short-circuit for the common/fast path. */
|
||||
if (!((unsigned long)walk->buffer | (unsigned long)walk->page))
|
||||
@@ -399,7 +400,7 @@ static int skcipher_copy_iv(struct skcipher_walk *walk)
|
||||
unsigned size;
|
||||
u8 *iv;
|
||||
|
||||
aligned_bs = ALIGN(bs, alignmask);
|
||||
aligned_bs = ALIGN(bs, alignmask + 1);
|
||||
|
||||
/* Minimum size to align buffer by alignmask. */
|
||||
size = alignmask & ~a;
|
||||
|
||||
+195
-237
@@ -1,6 +1,10 @@
|
||||
/*
|
||||
* Modified to interface to the Linux kernel
|
||||
* VMAC: Message Authentication Code using Universal Hashing
|
||||
*
|
||||
* Reference: https://tools.ietf.org/html/draft-krovetz-vmac-01
|
||||
*
|
||||
* Copyright (c) 2009, Intel Corporation.
|
||||
* Copyright (c) 2018, Google Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@@ -16,14 +20,15 @@
|
||||
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* VMAC and VHASH Implementation by Ted Krovetz (tdk@acm.org) and Wei Dai.
|
||||
* This implementation is herby placed in the public domain.
|
||||
* The authors offers no warranty. Use at your own risk.
|
||||
* Please send bug reports to the authors.
|
||||
* Last modified: 17 APR 08, 1700 PDT
|
||||
* ----------------------------------------------------------------------- */
|
||||
/*
|
||||
* Derived from:
|
||||
* VMAC and VHASH Implementation by Ted Krovetz (tdk@acm.org) and Wei Dai.
|
||||
* This implementation is herby placed in the public domain.
|
||||
* The authors offers no warranty. Use at your own risk.
|
||||
* Last modified: 17 APR 08, 1700 PDT
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/crypto.h>
|
||||
@@ -31,9 +36,35 @@
|
||||
#include <linux/scatterlist.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <crypto/vmac.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
|
||||
/*
|
||||
* User definable settings.
|
||||
*/
|
||||
#define VMAC_TAG_LEN 64
|
||||
#define VMAC_KEY_SIZE 128/* Must be 128, 192 or 256 */
|
||||
#define VMAC_KEY_LEN (VMAC_KEY_SIZE/8)
|
||||
#define VMAC_NHBYTES 128/* Must 2^i for any 3 < i < 13 Standard = 128*/
|
||||
|
||||
/* per-transform (per-key) context */
|
||||
struct vmac_tfm_ctx {
|
||||
struct crypto_cipher *cipher;
|
||||
u64 nhkey[(VMAC_NHBYTES/8)+2*(VMAC_TAG_LEN/64-1)];
|
||||
u64 polykey[2*VMAC_TAG_LEN/64];
|
||||
u64 l3key[2*VMAC_TAG_LEN/64];
|
||||
};
|
||||
|
||||
/* per-request context */
|
||||
struct vmac_desc_ctx {
|
||||
union {
|
||||
u8 partial[VMAC_NHBYTES]; /* partial block */
|
||||
__le64 partial_words[VMAC_NHBYTES / 8];
|
||||
};
|
||||
unsigned int partial_size; /* size of the partial block */
|
||||
bool first_block_processed;
|
||||
u64 polytmp[2*VMAC_TAG_LEN/64]; /* running total of L2-hash */
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants and masks
|
||||
*/
|
||||
@@ -318,13 +349,6 @@ static void poly_step_func(u64 *ahi, u64 *alo,
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static void vhash_abort(struct vmac_ctx *ctx)
|
||||
{
|
||||
ctx->polytmp[0] = ctx->polykey[0] ;
|
||||
ctx->polytmp[1] = ctx->polykey[1] ;
|
||||
ctx->first_block_processed = 0;
|
||||
}
|
||||
|
||||
static u64 l3hash(u64 p1, u64 p2, u64 k1, u64 k2, u64 len)
|
||||
{
|
||||
u64 rh, rl, t, z = 0;
|
||||
@@ -364,280 +388,209 @@ static u64 l3hash(u64 p1, u64 p2, u64 k1, u64 k2, u64 len)
|
||||
return rl;
|
||||
}
|
||||
|
||||
static void vhash_update(const unsigned char *m,
|
||||
unsigned int mbytes, /* Pos multiple of VMAC_NHBYTES */
|
||||
struct vmac_ctx *ctx)
|
||||
/* L1 and L2-hash one or more VMAC_NHBYTES-byte blocks */
|
||||
static void vhash_blocks(const struct vmac_tfm_ctx *tctx,
|
||||
struct vmac_desc_ctx *dctx,
|
||||
const __le64 *mptr, unsigned int blocks)
|
||||
{
|
||||
u64 rh, rl, *mptr;
|
||||
const u64 *kptr = (u64 *)ctx->nhkey;
|
||||
int i;
|
||||
u64 ch, cl;
|
||||
u64 pkh = ctx->polykey[0];
|
||||
u64 pkl = ctx->polykey[1];
|
||||
const u64 *kptr = tctx->nhkey;
|
||||
const u64 pkh = tctx->polykey[0];
|
||||
const u64 pkl = tctx->polykey[1];
|
||||
u64 ch = dctx->polytmp[0];
|
||||
u64 cl = dctx->polytmp[1];
|
||||
u64 rh, rl;
|
||||
|
||||
if (!mbytes)
|
||||
return;
|
||||
|
||||
BUG_ON(mbytes % VMAC_NHBYTES);
|
||||
|
||||
mptr = (u64 *)m;
|
||||
i = mbytes / VMAC_NHBYTES; /* Must be non-zero */
|
||||
|
||||
ch = ctx->polytmp[0];
|
||||
cl = ctx->polytmp[1];
|
||||
|
||||
if (!ctx->first_block_processed) {
|
||||
ctx->first_block_processed = 1;
|
||||
if (!dctx->first_block_processed) {
|
||||
dctx->first_block_processed = true;
|
||||
nh_vmac_nhbytes(mptr, kptr, VMAC_NHBYTES/8, rh, rl);
|
||||
rh &= m62;
|
||||
ADD128(ch, cl, rh, rl);
|
||||
mptr += (VMAC_NHBYTES/sizeof(u64));
|
||||
i--;
|
||||
blocks--;
|
||||
}
|
||||
|
||||
while (i--) {
|
||||
while (blocks--) {
|
||||
nh_vmac_nhbytes(mptr, kptr, VMAC_NHBYTES/8, rh, rl);
|
||||
rh &= m62;
|
||||
poly_step(ch, cl, pkh, pkl, rh, rl);
|
||||
mptr += (VMAC_NHBYTES/sizeof(u64));
|
||||
}
|
||||
|
||||
ctx->polytmp[0] = ch;
|
||||
ctx->polytmp[1] = cl;
|
||||
dctx->polytmp[0] = ch;
|
||||
dctx->polytmp[1] = cl;
|
||||
}
|
||||
|
||||
static u64 vhash(unsigned char m[], unsigned int mbytes,
|
||||
u64 *tagl, struct vmac_ctx *ctx)
|
||||
static int vmac_setkey(struct crypto_shash *tfm,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
u64 rh, rl, *mptr;
|
||||
const u64 *kptr = (u64 *)ctx->nhkey;
|
||||
int i, remaining;
|
||||
u64 ch, cl;
|
||||
u64 pkh = ctx->polykey[0];
|
||||
u64 pkl = ctx->polykey[1];
|
||||
struct vmac_tfm_ctx *tctx = crypto_shash_ctx(tfm);
|
||||
__be64 out[2];
|
||||
u8 in[16] = { 0 };
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
mptr = (u64 *)m;
|
||||
i = mbytes / VMAC_NHBYTES;
|
||||
remaining = mbytes % VMAC_NHBYTES;
|
||||
|
||||
if (ctx->first_block_processed) {
|
||||
ch = ctx->polytmp[0];
|
||||
cl = ctx->polytmp[1];
|
||||
} else if (i) {
|
||||
nh_vmac_nhbytes(mptr, kptr, VMAC_NHBYTES/8, ch, cl);
|
||||
ch &= m62;
|
||||
ADD128(ch, cl, pkh, pkl);
|
||||
mptr += (VMAC_NHBYTES/sizeof(u64));
|
||||
i--;
|
||||
} else if (remaining) {
|
||||
nh_16(mptr, kptr, 2*((remaining+15)/16), ch, cl);
|
||||
ch &= m62;
|
||||
ADD128(ch, cl, pkh, pkl);
|
||||
mptr += (VMAC_NHBYTES/sizeof(u64));
|
||||
goto do_l3;
|
||||
} else {/* Empty String */
|
||||
ch = pkh; cl = pkl;
|
||||
goto do_l3;
|
||||
if (keylen != VMAC_KEY_LEN) {
|
||||
crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (i--) {
|
||||
nh_vmac_nhbytes(mptr, kptr, VMAC_NHBYTES/8, rh, rl);
|
||||
rh &= m62;
|
||||
poly_step(ch, cl, pkh, pkl, rh, rl);
|
||||
mptr += (VMAC_NHBYTES/sizeof(u64));
|
||||
}
|
||||
if (remaining) {
|
||||
nh_16(mptr, kptr, 2*((remaining+15)/16), rh, rl);
|
||||
rh &= m62;
|
||||
poly_step(ch, cl, pkh, pkl, rh, rl);
|
||||
}
|
||||
|
||||
do_l3:
|
||||
vhash_abort(ctx);
|
||||
remaining *= 8;
|
||||
return l3hash(ch, cl, ctx->l3key[0], ctx->l3key[1], remaining);
|
||||
}
|
||||
|
||||
static u64 vmac(unsigned char m[], unsigned int mbytes,
|
||||
const unsigned char n[16], u64 *tagl,
|
||||
struct vmac_ctx_t *ctx)
|
||||
{
|
||||
u64 *in_n, *out_p;
|
||||
u64 p, h;
|
||||
int i;
|
||||
|
||||
in_n = ctx->__vmac_ctx.cached_nonce;
|
||||
out_p = ctx->__vmac_ctx.cached_aes;
|
||||
|
||||
i = n[15] & 1;
|
||||
if ((*(u64 *)(n+8) != in_n[1]) || (*(u64 *)(n) != in_n[0])) {
|
||||
in_n[0] = *(u64 *)(n);
|
||||
in_n[1] = *(u64 *)(n+8);
|
||||
((unsigned char *)in_n)[15] &= 0xFE;
|
||||
crypto_cipher_encrypt_one(ctx->child,
|
||||
(unsigned char *)out_p, (unsigned char *)in_n);
|
||||
|
||||
((unsigned char *)in_n)[15] |= (unsigned char)(1-i);
|
||||
}
|
||||
p = be64_to_cpup(out_p + i);
|
||||
h = vhash(m, mbytes, (u64 *)0, &ctx->__vmac_ctx);
|
||||
return le64_to_cpu(p + h);
|
||||
}
|
||||
|
||||
static int vmac_set_key(unsigned char user_key[], struct vmac_ctx_t *ctx)
|
||||
{
|
||||
u64 in[2] = {0}, out[2];
|
||||
unsigned i;
|
||||
int err = 0;
|
||||
|
||||
err = crypto_cipher_setkey(ctx->child, user_key, VMAC_KEY_LEN);
|
||||
err = crypto_cipher_setkey(tctx->cipher, key, keylen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Fill nh key */
|
||||
((unsigned char *)in)[0] = 0x80;
|
||||
for (i = 0; i < sizeof(ctx->__vmac_ctx.nhkey)/8; i += 2) {
|
||||
crypto_cipher_encrypt_one(ctx->child,
|
||||
(unsigned char *)out, (unsigned char *)in);
|
||||
ctx->__vmac_ctx.nhkey[i] = be64_to_cpup(out);
|
||||
ctx->__vmac_ctx.nhkey[i+1] = be64_to_cpup(out+1);
|
||||
((unsigned char *)in)[15] += 1;
|
||||
in[0] = 0x80;
|
||||
for (i = 0; i < ARRAY_SIZE(tctx->nhkey); i += 2) {
|
||||
crypto_cipher_encrypt_one(tctx->cipher, (u8 *)out, in);
|
||||
tctx->nhkey[i] = be64_to_cpu(out[0]);
|
||||
tctx->nhkey[i+1] = be64_to_cpu(out[1]);
|
||||
in[15]++;
|
||||
}
|
||||
|
||||
/* Fill poly key */
|
||||
((unsigned char *)in)[0] = 0xC0;
|
||||
in[1] = 0;
|
||||
for (i = 0; i < sizeof(ctx->__vmac_ctx.polykey)/8; i += 2) {
|
||||
crypto_cipher_encrypt_one(ctx->child,
|
||||
(unsigned char *)out, (unsigned char *)in);
|
||||
ctx->__vmac_ctx.polytmp[i] =
|
||||
ctx->__vmac_ctx.polykey[i] =
|
||||
be64_to_cpup(out) & mpoly;
|
||||
ctx->__vmac_ctx.polytmp[i+1] =
|
||||
ctx->__vmac_ctx.polykey[i+1] =
|
||||
be64_to_cpup(out+1) & mpoly;
|
||||
((unsigned char *)in)[15] += 1;
|
||||
in[0] = 0xC0;
|
||||
in[15] = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(tctx->polykey); i += 2) {
|
||||
crypto_cipher_encrypt_one(tctx->cipher, (u8 *)out, in);
|
||||
tctx->polykey[i] = be64_to_cpu(out[0]) & mpoly;
|
||||
tctx->polykey[i+1] = be64_to_cpu(out[1]) & mpoly;
|
||||
in[15]++;
|
||||
}
|
||||
|
||||
/* Fill ip key */
|
||||
((unsigned char *)in)[0] = 0xE0;
|
||||
in[1] = 0;
|
||||
for (i = 0; i < sizeof(ctx->__vmac_ctx.l3key)/8; i += 2) {
|
||||
in[0] = 0xE0;
|
||||
in[15] = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(tctx->l3key); i += 2) {
|
||||
do {
|
||||
crypto_cipher_encrypt_one(ctx->child,
|
||||
(unsigned char *)out, (unsigned char *)in);
|
||||
ctx->__vmac_ctx.l3key[i] = be64_to_cpup(out);
|
||||
ctx->__vmac_ctx.l3key[i+1] = be64_to_cpup(out+1);
|
||||
((unsigned char *)in)[15] += 1;
|
||||
} while (ctx->__vmac_ctx.l3key[i] >= p64
|
||||
|| ctx->__vmac_ctx.l3key[i+1] >= p64);
|
||||
crypto_cipher_encrypt_one(tctx->cipher, (u8 *)out, in);
|
||||
tctx->l3key[i] = be64_to_cpu(out[0]);
|
||||
tctx->l3key[i+1] = be64_to_cpu(out[1]);
|
||||
in[15]++;
|
||||
} while (tctx->l3key[i] >= p64 || tctx->l3key[i+1] >= p64);
|
||||
}
|
||||
|
||||
/* Invalidate nonce/aes cache and reset other elements */
|
||||
ctx->__vmac_ctx.cached_nonce[0] = (u64)-1; /* Ensure illegal nonce */
|
||||
ctx->__vmac_ctx.cached_nonce[1] = (u64)0; /* Ensure illegal nonce */
|
||||
ctx->__vmac_ctx.first_block_processed = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int vmac_setkey(struct crypto_shash *parent,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
struct vmac_ctx_t *ctx = crypto_shash_ctx(parent);
|
||||
|
||||
if (keylen != VMAC_KEY_LEN) {
|
||||
crypto_shash_set_flags(parent, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return vmac_set_key((u8 *)key, ctx);
|
||||
}
|
||||
|
||||
static int vmac_init(struct shash_desc *pdesc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmac_update(struct shash_desc *pdesc, const u8 *p,
|
||||
unsigned int len)
|
||||
{
|
||||
struct crypto_shash *parent = pdesc->tfm;
|
||||
struct vmac_ctx_t *ctx = crypto_shash_ctx(parent);
|
||||
int expand;
|
||||
int min;
|
||||
|
||||
expand = VMAC_NHBYTES - ctx->partial_size > 0 ?
|
||||
VMAC_NHBYTES - ctx->partial_size : 0;
|
||||
|
||||
min = len < expand ? len : expand;
|
||||
|
||||
memcpy(ctx->partial + ctx->partial_size, p, min);
|
||||
ctx->partial_size += min;
|
||||
|
||||
if (len < expand)
|
||||
return 0;
|
||||
|
||||
vhash_update(ctx->partial, VMAC_NHBYTES, &ctx->__vmac_ctx);
|
||||
ctx->partial_size = 0;
|
||||
|
||||
len -= expand;
|
||||
p += expand;
|
||||
|
||||
if (len % VMAC_NHBYTES) {
|
||||
memcpy(ctx->partial, p + len - (len % VMAC_NHBYTES),
|
||||
len % VMAC_NHBYTES);
|
||||
ctx->partial_size = len % VMAC_NHBYTES;
|
||||
}
|
||||
|
||||
vhash_update(p, len - len % VMAC_NHBYTES, &ctx->__vmac_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmac_final(struct shash_desc *pdesc, u8 *out)
|
||||
static int vmac_init(struct shash_desc *desc)
|
||||
{
|
||||
struct crypto_shash *parent = pdesc->tfm;
|
||||
struct vmac_ctx_t *ctx = crypto_shash_ctx(parent);
|
||||
vmac_t mac;
|
||||
u8 nonce[16] = {};
|
||||
const struct vmac_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
|
||||
struct vmac_desc_ctx *dctx = shash_desc_ctx(desc);
|
||||
|
||||
/* vmac() ends up accessing outside the array bounds that
|
||||
* we specify. In appears to access up to the next 2-word
|
||||
* boundary. We'll just be uber cautious and zero the
|
||||
* unwritten bytes in the buffer.
|
||||
*/
|
||||
if (ctx->partial_size) {
|
||||
memset(ctx->partial + ctx->partial_size, 0,
|
||||
VMAC_NHBYTES - ctx->partial_size);
|
||||
dctx->partial_size = 0;
|
||||
dctx->first_block_processed = false;
|
||||
memcpy(dctx->polytmp, tctx->polykey, sizeof(dctx->polytmp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmac_update(struct shash_desc *desc, const u8 *p, unsigned int len)
|
||||
{
|
||||
const struct vmac_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
|
||||
struct vmac_desc_ctx *dctx = shash_desc_ctx(desc);
|
||||
unsigned int n;
|
||||
|
||||
if (dctx->partial_size) {
|
||||
n = min(len, VMAC_NHBYTES - dctx->partial_size);
|
||||
memcpy(&dctx->partial[dctx->partial_size], p, n);
|
||||
dctx->partial_size += n;
|
||||
p += n;
|
||||
len -= n;
|
||||
if (dctx->partial_size == VMAC_NHBYTES) {
|
||||
vhash_blocks(tctx, dctx, dctx->partial_words, 1);
|
||||
dctx->partial_size = 0;
|
||||
}
|
||||
}
|
||||
mac = vmac(ctx->partial, ctx->partial_size, nonce, NULL, ctx);
|
||||
memcpy(out, &mac, sizeof(vmac_t));
|
||||
memzero_explicit(&mac, sizeof(vmac_t));
|
||||
memset(&ctx->__vmac_ctx, 0, sizeof(struct vmac_ctx));
|
||||
ctx->partial_size = 0;
|
||||
|
||||
if (len >= VMAC_NHBYTES) {
|
||||
n = round_down(len, VMAC_NHBYTES);
|
||||
/* TODO: 'p' may be misaligned here */
|
||||
vhash_blocks(tctx, dctx, (const __le64 *)p, n / VMAC_NHBYTES);
|
||||
p += n;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
memcpy(dctx->partial, p, len);
|
||||
dctx->partial_size = len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 vhash_final(const struct vmac_tfm_ctx *tctx,
|
||||
struct vmac_desc_ctx *dctx)
|
||||
{
|
||||
unsigned int partial = dctx->partial_size;
|
||||
u64 ch = dctx->polytmp[0];
|
||||
u64 cl = dctx->polytmp[1];
|
||||
|
||||
/* L1 and L2-hash the final block if needed */
|
||||
if (partial) {
|
||||
/* Zero-pad to next 128-bit boundary */
|
||||
unsigned int n = round_up(partial, 16);
|
||||
u64 rh, rl;
|
||||
|
||||
memset(&dctx->partial[partial], 0, n - partial);
|
||||
nh_16(dctx->partial_words, tctx->nhkey, n / 8, rh, rl);
|
||||
rh &= m62;
|
||||
if (dctx->first_block_processed)
|
||||
poly_step(ch, cl, tctx->polykey[0], tctx->polykey[1],
|
||||
rh, rl);
|
||||
else
|
||||
ADD128(ch, cl, rh, rl);
|
||||
}
|
||||
|
||||
/* L3-hash the 128-bit output of L2-hash */
|
||||
return l3hash(ch, cl, tctx->l3key[0], tctx->l3key[1], partial * 8);
|
||||
}
|
||||
|
||||
static int vmac_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
const struct vmac_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
|
||||
struct vmac_desc_ctx *dctx = shash_desc_ctx(desc);
|
||||
static const u8 nonce[16] = {}; /* TODO: this is insecure */
|
||||
union {
|
||||
u8 bytes[16];
|
||||
__be64 pads[2];
|
||||
} block;
|
||||
int index;
|
||||
u64 hash, pad;
|
||||
|
||||
/* Finish calculating the VHASH of the message */
|
||||
hash = vhash_final(tctx, dctx);
|
||||
|
||||
/* Generate pseudorandom pad by encrypting the nonce */
|
||||
memcpy(&block, nonce, 16);
|
||||
index = block.bytes[15] & 1;
|
||||
block.bytes[15] &= ~1;
|
||||
crypto_cipher_encrypt_one(tctx->cipher, block.bytes, block.bytes);
|
||||
pad = be64_to_cpu(block.pads[index]);
|
||||
|
||||
/* The VMAC is the sum of VHASH and the pseudorandom pad */
|
||||
put_unaligned_le64(hash + pad, out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmac_init_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_cipher *cipher;
|
||||
struct crypto_instance *inst = (void *)tfm->__crt_alg;
|
||||
struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
|
||||
struct crypto_spawn *spawn = crypto_instance_ctx(inst);
|
||||
struct vmac_ctx_t *ctx = crypto_tfm_ctx(tfm);
|
||||
struct vmac_tfm_ctx *tctx = crypto_tfm_ctx(tfm);
|
||||
struct crypto_cipher *cipher;
|
||||
|
||||
cipher = crypto_spawn_cipher(spawn);
|
||||
if (IS_ERR(cipher))
|
||||
return PTR_ERR(cipher);
|
||||
|
||||
ctx->child = cipher;
|
||||
tctx->cipher = cipher;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmac_exit_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct vmac_ctx_t *ctx = crypto_tfm_ctx(tfm);
|
||||
crypto_free_cipher(ctx->child);
|
||||
struct vmac_tfm_ctx *tctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
crypto_free_cipher(tctx->cipher);
|
||||
}
|
||||
|
||||
static int vmac_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
@@ -655,6 +608,10 @@ static int vmac_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
if (IS_ERR(alg))
|
||||
return PTR_ERR(alg);
|
||||
|
||||
err = -EINVAL;
|
||||
if (alg->cra_blocksize != 16)
|
||||
goto out_put_alg;
|
||||
|
||||
inst = shash_alloc_instance("vmac", alg);
|
||||
err = PTR_ERR(inst);
|
||||
if (IS_ERR(inst))
|
||||
@@ -670,11 +627,12 @@ static int vmac_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
inst->alg.base.cra_blocksize = alg->cra_blocksize;
|
||||
inst->alg.base.cra_alignmask = alg->cra_alignmask;
|
||||
|
||||
inst->alg.digestsize = sizeof(vmac_t);
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct vmac_ctx_t);
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct vmac_tfm_ctx);
|
||||
inst->alg.base.cra_init = vmac_init_tfm;
|
||||
inst->alg.base.cra_exit = vmac_exit_tfm;
|
||||
|
||||
inst->alg.descsize = sizeof(struct vmac_desc_ctx);
|
||||
inst->alg.digestsize = VMAC_TAG_LEN / 8;
|
||||
inst->alg.init = vmac_init;
|
||||
inst->alg.update = vmac_update;
|
||||
inst->alg.final = vmac_final;
|
||||
|
||||
@@ -540,16 +540,24 @@ ssize_t __weak cpu_show_spec_store_bypass(struct device *dev,
|
||||
return sprintf(buf, "Not affected\n");
|
||||
}
|
||||
|
||||
ssize_t __weak cpu_show_l1tf(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "Not affected\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
|
||||
static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
|
||||
static DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL);
|
||||
static DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL);
|
||||
static DEVICE_ATTR(l1tf, 0444, cpu_show_l1tf, NULL);
|
||||
|
||||
static struct attribute *cpu_root_vulnerabilities_attrs[] = {
|
||||
&dev_attr_meltdown.attr,
|
||||
&dev_attr_spectre_v1.attr,
|
||||
&dev_attr_spectre_v2.attr,
|
||||
&dev_attr_spec_store_bypass.attr,
|
||||
&dev_attr_l1tf.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
@@ -84,8 +84,6 @@ done:
|
||||
|
||||
static void sev_wait_cmd_ioc(struct psp_device *psp, unsigned int *reg)
|
||||
{
|
||||
psp->sev_int_rcvd = 0;
|
||||
|
||||
wait_event(psp->sev_int_queue, psp->sev_int_rcvd);
|
||||
*reg = ioread32(psp->io_regs + PSP_CMDRESP);
|
||||
}
|
||||
@@ -148,6 +146,8 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
|
||||
iowrite32(phys_lsb, psp->io_regs + PSP_CMDBUFF_ADDR_LO);
|
||||
iowrite32(phys_msb, psp->io_regs + PSP_CMDBUFF_ADDR_HI);
|
||||
|
||||
psp->sev_int_rcvd = 0;
|
||||
|
||||
reg = cmd;
|
||||
reg <<= PSP_CMDRESP_CMD_SHIFT;
|
||||
reg |= PSP_CMDRESP_IOC;
|
||||
@@ -856,6 +856,9 @@ void psp_dev_destroy(struct sp_device *sp)
|
||||
{
|
||||
struct psp_device *psp = sp->psp_data;
|
||||
|
||||
if (!psp)
|
||||
return;
|
||||
|
||||
if (psp->sev_misc)
|
||||
kref_put(&misc_dev->refcount, sev_exit);
|
||||
|
||||
|
||||
@@ -593,34 +593,82 @@ static void cc_setup_cipher_data(struct crypto_tfm *tfm,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a CTR-AES 128 bit counter
|
||||
*/
|
||||
static void cc_update_ctr(u8 *ctr, unsigned int increment)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
|
||||
IS_ALIGNED((unsigned long)ctr, 8)) {
|
||||
|
||||
__be64 *high_be = (__be64 *)ctr;
|
||||
__be64 *low_be = high_be + 1;
|
||||
u64 orig_low = __be64_to_cpu(*low_be);
|
||||
u64 new_low = orig_low + (u64)increment;
|
||||
|
||||
*low_be = __cpu_to_be64(new_low);
|
||||
|
||||
if (new_low < orig_low)
|
||||
*high_be = __cpu_to_be64(__be64_to_cpu(*high_be) + 1);
|
||||
} else {
|
||||
u8 *pos = (ctr + AES_BLOCK_SIZE);
|
||||
u8 val;
|
||||
unsigned int size;
|
||||
|
||||
for (; increment; increment--)
|
||||
for (size = AES_BLOCK_SIZE; size; size--) {
|
||||
val = *--pos + 1;
|
||||
*pos = val;
|
||||
if (val)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cc_cipher_complete(struct device *dev, void *cc_req, int err)
|
||||
{
|
||||
struct skcipher_request *req = (struct skcipher_request *)cc_req;
|
||||
struct scatterlist *dst = req->dst;
|
||||
struct scatterlist *src = req->src;
|
||||
struct cipher_req_ctx *req_ctx = skcipher_request_ctx(req);
|
||||
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
|
||||
unsigned int ivsize = crypto_skcipher_ivsize(tfm);
|
||||
struct crypto_skcipher *sk_tfm = crypto_skcipher_reqtfm(req);
|
||||
struct crypto_tfm *tfm = crypto_skcipher_tfm(sk_tfm);
|
||||
struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
|
||||
unsigned int ivsize = crypto_skcipher_ivsize(sk_tfm);
|
||||
unsigned int len;
|
||||
|
||||
switch (ctx_p->cipher_mode) {
|
||||
case DRV_CIPHER_CBC:
|
||||
/*
|
||||
* The crypto API expects us to set the req->iv to the last
|
||||
* ciphertext block. For encrypt, simply copy from the result.
|
||||
* For decrypt, we must copy from a saved buffer since this
|
||||
* could be an in-place decryption operation and the src is
|
||||
* lost by this point.
|
||||
*/
|
||||
if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
|
||||
memcpy(req->iv, req_ctx->backup_info, ivsize);
|
||||
kzfree(req_ctx->backup_info);
|
||||
} else if (!err) {
|
||||
len = req->cryptlen - ivsize;
|
||||
scatterwalk_map_and_copy(req->iv, req->dst, len,
|
||||
ivsize, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case DRV_CIPHER_CTR:
|
||||
/* Compute the counter of the last block */
|
||||
len = ALIGN(req->cryptlen, AES_BLOCK_SIZE) / AES_BLOCK_SIZE;
|
||||
cc_update_ctr((u8 *)req->iv, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cc_unmap_cipher_request(dev, req_ctx, ivsize, src, dst);
|
||||
kzfree(req_ctx->iv);
|
||||
|
||||
/*
|
||||
* The crypto API expects us to set the req->iv to the last
|
||||
* ciphertext block. For encrypt, simply copy from the result.
|
||||
* For decrypt, we must copy from a saved buffer since this
|
||||
* could be an in-place decryption operation and the src is
|
||||
* lost by this point.
|
||||
*/
|
||||
if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
|
||||
memcpy(req->iv, req_ctx->backup_info, ivsize);
|
||||
kzfree(req_ctx->backup_info);
|
||||
} else if (!err) {
|
||||
scatterwalk_map_and_copy(req->iv, req->dst,
|
||||
(req->cryptlen - ivsize),
|
||||
ivsize, 0);
|
||||
}
|
||||
|
||||
skcipher_request_complete(req, err);
|
||||
}
|
||||
|
||||
@@ -752,20 +800,29 @@ static int cc_cipher_encrypt(struct skcipher_request *req)
|
||||
static int cc_cipher_decrypt(struct skcipher_request *req)
|
||||
{
|
||||
struct crypto_skcipher *sk_tfm = crypto_skcipher_reqtfm(req);
|
||||
struct crypto_tfm *tfm = crypto_skcipher_tfm(sk_tfm);
|
||||
struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
|
||||
struct cipher_req_ctx *req_ctx = skcipher_request_ctx(req);
|
||||
unsigned int ivsize = crypto_skcipher_ivsize(sk_tfm);
|
||||
gfp_t flags = cc_gfp_flags(&req->base);
|
||||
unsigned int len;
|
||||
|
||||
/*
|
||||
* Allocate and save the last IV sized bytes of the source, which will
|
||||
* be lost in case of in-place decryption and might be needed for CTS.
|
||||
*/
|
||||
req_ctx->backup_info = kmalloc(ivsize, flags);
|
||||
if (!req_ctx->backup_info)
|
||||
return -ENOMEM;
|
||||
if (ctx_p->cipher_mode == DRV_CIPHER_CBC) {
|
||||
|
||||
/* Allocate and save the last IV sized bytes of the source,
|
||||
* which will be lost in case of in-place decryption.
|
||||
*/
|
||||
req_ctx->backup_info = kzalloc(ivsize, flags);
|
||||
if (!req_ctx->backup_info)
|
||||
return -ENOMEM;
|
||||
|
||||
len = req->cryptlen - ivsize;
|
||||
scatterwalk_map_and_copy(req_ctx->backup_info, req->src, len,
|
||||
ivsize, 0);
|
||||
} else {
|
||||
req_ctx->backup_info = NULL;
|
||||
}
|
||||
|
||||
scatterwalk_map_and_copy(req_ctx->backup_info, req->src,
|
||||
(req->cryptlen - ivsize), ivsize, 0);
|
||||
req_ctx->is_giv = false;
|
||||
|
||||
return cc_cipher_process(req, DRV_CRYPTO_DIRECTION_DECRYPT);
|
||||
|
||||
@@ -602,7 +602,7 @@ static int cc_hash_update(struct ahash_request *req)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cc_hash_finup(struct ahash_request *req)
|
||||
static int cc_do_finup(struct ahash_request *req, bool update)
|
||||
{
|
||||
struct ahash_req_ctx *state = ahash_request_ctx(req);
|
||||
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
|
||||
@@ -619,15 +619,15 @@ static int cc_hash_finup(struct ahash_request *req)
|
||||
int rc;
|
||||
gfp_t flags = cc_gfp_flags(&req->base);
|
||||
|
||||
dev_dbg(dev, "===== %s-finup (%d) ====\n", is_hmac ? "hmac" : "hash",
|
||||
nbytes);
|
||||
dev_dbg(dev, "===== %s-%s (%d) ====\n", is_hmac ? "hmac" : "hash",
|
||||
update ? "finup" : "final", nbytes);
|
||||
|
||||
if (cc_map_req(dev, state, ctx)) {
|
||||
dev_err(dev, "map_ahash_source() failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 1,
|
||||
if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, update,
|
||||
flags)) {
|
||||
dev_err(dev, "map_ahash_request_final() failed\n");
|
||||
cc_unmap_req(dev, state, ctx);
|
||||
@@ -646,67 +646,7 @@ static int cc_hash_finup(struct ahash_request *req)
|
||||
|
||||
idx = cc_restore_hash(desc, ctx, state, idx);
|
||||
|
||||
if (is_hmac)
|
||||
idx = cc_fin_hmac(desc, req, idx);
|
||||
|
||||
idx = cc_fin_result(desc, req, idx);
|
||||
|
||||
rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
|
||||
if (rc != -EINPROGRESS && rc != -EBUSY) {
|
||||
dev_err(dev, "send_request() failed (rc=%d)\n", rc);
|
||||
cc_unmap_hash_request(dev, state, src, true);
|
||||
cc_unmap_result(dev, state, digestsize, result);
|
||||
cc_unmap_req(dev, state, ctx);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cc_hash_final(struct ahash_request *req)
|
||||
{
|
||||
struct ahash_req_ctx *state = ahash_request_ctx(req);
|
||||
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
|
||||
struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
|
||||
u32 digestsize = crypto_ahash_digestsize(tfm);
|
||||
struct scatterlist *src = req->src;
|
||||
unsigned int nbytes = req->nbytes;
|
||||
u8 *result = req->result;
|
||||
struct device *dev = drvdata_to_dev(ctx->drvdata);
|
||||
bool is_hmac = ctx->is_hmac;
|
||||
struct cc_crypto_req cc_req = {};
|
||||
struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
|
||||
unsigned int idx = 0;
|
||||
int rc;
|
||||
gfp_t flags = cc_gfp_flags(&req->base);
|
||||
|
||||
dev_dbg(dev, "===== %s-final (%d) ====\n", is_hmac ? "hmac" : "hash",
|
||||
nbytes);
|
||||
|
||||
if (cc_map_req(dev, state, ctx)) {
|
||||
dev_err(dev, "map_ahash_source() failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 0,
|
||||
flags)) {
|
||||
dev_err(dev, "map_ahash_request_final() failed\n");
|
||||
cc_unmap_req(dev, state, ctx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (cc_map_result(dev, state, digestsize)) {
|
||||
dev_err(dev, "map_ahash_digest() failed\n");
|
||||
cc_unmap_hash_request(dev, state, src, true);
|
||||
cc_unmap_req(dev, state, ctx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Setup request structure */
|
||||
cc_req.user_cb = cc_hash_complete;
|
||||
cc_req.user_arg = req;
|
||||
|
||||
idx = cc_restore_hash(desc, ctx, state, idx);
|
||||
|
||||
/* "DO-PAD" must be enabled only when writing current length to HW */
|
||||
/* Pad the hash */
|
||||
hw_desc_init(&desc[idx]);
|
||||
set_cipher_do(&desc[idx], DO_PAD);
|
||||
set_cipher_mode(&desc[idx], ctx->hw_mode);
|
||||
@@ -731,6 +671,17 @@ static int cc_hash_final(struct ahash_request *req)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cc_hash_finup(struct ahash_request *req)
|
||||
{
|
||||
return cc_do_finup(req, true);
|
||||
}
|
||||
|
||||
|
||||
static int cc_hash_final(struct ahash_request *req)
|
||||
{
|
||||
return cc_do_finup(req, false);
|
||||
}
|
||||
|
||||
static int cc_hash_init(struct ahash_request *req)
|
||||
{
|
||||
struct ahash_req_ctx *state = ahash_request_ctx(req);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright © 2017-2018 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include "i915_pmu.h"
|
||||
#include "intel_ringbuffer.h"
|
||||
#include "i915_drv.h"
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include <linux/irqdomain.h>
|
||||
#include <asm/irqdomain.h>
|
||||
#include <asm/apic.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/refcount.h>
|
||||
|
||||
@@ -1019,8 +1019,8 @@ int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot);
|
||||
int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot);
|
||||
int pud_clear_huge(pud_t *pud);
|
||||
int pmd_clear_huge(pmd_t *pmd);
|
||||
int pud_free_pmd_page(pud_t *pud);
|
||||
int pmd_free_pte_page(pmd_t *pmd);
|
||||
int pud_free_pmd_page(pud_t *pud, unsigned long addr);
|
||||
int pmd_free_pte_page(pmd_t *pmd, unsigned long addr);
|
||||
#else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
|
||||
static inline int p4d_set_huge(p4d_t *p4d, phys_addr_t addr, pgprot_t prot)
|
||||
{
|
||||
@@ -1046,11 +1046,11 @@ static inline int pmd_clear_huge(pmd_t *pmd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int pud_free_pmd_page(pud_t *pud)
|
||||
static inline int pud_free_pmd_page(pud_t *pud, unsigned long addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int pmd_free_pte_page(pmd_t *pmd)
|
||||
static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1083,6 +1083,18 @@ int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
|
||||
static inline void init_espfix_bsp(void) { }
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_PFN_MODIFY_ALLOWED
|
||||
static inline bool pfn_modify_allowed(unsigned long pfn, pgprot_t prot)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool arch_has_pfn_modify_check(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* !_HAVE_ARCH_PFN_MODIFY_ALLOWED */
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#ifndef io_remap_pfn_range
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Modified to interface to the Linux kernel
|
||||
* Copyright (c) 2009, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
#ifndef __CRYPTO_VMAC_H
|
||||
#define __CRYPTO_VMAC_H
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* VMAC and VHASH Implementation by Ted Krovetz (tdk@acm.org) and Wei Dai.
|
||||
* This implementation is herby placed in the public domain.
|
||||
* The authors offers no warranty. Use at your own risk.
|
||||
* Please send bug reports to the authors.
|
||||
* Last modified: 17 APR 08, 1700 PDT
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* User definable settings.
|
||||
*/
|
||||
#define VMAC_TAG_LEN 64
|
||||
#define VMAC_KEY_SIZE 128/* Must be 128, 192 or 256 */
|
||||
#define VMAC_KEY_LEN (VMAC_KEY_SIZE/8)
|
||||
#define VMAC_NHBYTES 128/* Must 2^i for any 3 < i < 13 Standard = 128*/
|
||||
|
||||
/*
|
||||
* This implementation uses u32 and u64 as names for unsigned 32-
|
||||
* and 64-bit integer types. These are defined in C99 stdint.h. The
|
||||
* following may need adaptation if you are not running a C99 or
|
||||
* Microsoft C environment.
|
||||
*/
|
||||
struct vmac_ctx {
|
||||
u64 nhkey[(VMAC_NHBYTES/8)+2*(VMAC_TAG_LEN/64-1)];
|
||||
u64 polykey[2*VMAC_TAG_LEN/64];
|
||||
u64 l3key[2*VMAC_TAG_LEN/64];
|
||||
u64 polytmp[2*VMAC_TAG_LEN/64];
|
||||
u64 cached_nonce[2];
|
||||
u64 cached_aes[2];
|
||||
int first_block_processed;
|
||||
};
|
||||
|
||||
typedef u64 vmac_t;
|
||||
|
||||
struct vmac_ctx_t {
|
||||
struct crypto_cipher *child;
|
||||
struct vmac_ctx __vmac_ctx;
|
||||
u8 partial[VMAC_NHBYTES]; /* partial block */
|
||||
int partial_size; /* size of the partial block */
|
||||
};
|
||||
|
||||
#endif /* __CRYPTO_VMAC_H */
|
||||
@@ -55,6 +55,8 @@ extern ssize_t cpu_show_spectre_v2(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
extern ssize_t cpu_show_spec_store_bypass(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
extern ssize_t cpu_show_l1tf(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
extern __printf(4, 5)
|
||||
struct device *cpu_device_create(struct device *parent, void *drvdata,
|
||||
@@ -166,4 +168,23 @@ void cpuhp_report_idle_dead(void);
|
||||
static inline void cpuhp_report_idle_dead(void) { }
|
||||
#endif /* #ifdef CONFIG_HOTPLUG_CPU */
|
||||
|
||||
enum cpuhp_smt_control {
|
||||
CPU_SMT_ENABLED,
|
||||
CPU_SMT_DISABLED,
|
||||
CPU_SMT_FORCE_DISABLED,
|
||||
CPU_SMT_NOT_SUPPORTED,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT)
|
||||
extern enum cpuhp_smt_control cpu_smt_control;
|
||||
extern void cpu_smt_disable(bool force);
|
||||
extern void cpu_smt_check_topology_early(void);
|
||||
extern void cpu_smt_check_topology(void);
|
||||
#else
|
||||
# define cpu_smt_control (CPU_SMT_ENABLED)
|
||||
static inline void cpu_smt_disable(bool force) { }
|
||||
static inline void cpu_smt_check_topology_early(void) { }
|
||||
static inline void cpu_smt_check_topology(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_CPU_H_ */
|
||||
|
||||
@@ -10,5 +10,7 @@ extern spinlock_t swap_lock;
|
||||
extern struct plist_head swap_active_head;
|
||||
extern struct swap_info_struct *swap_info[];
|
||||
extern int try_to_unuse(unsigned int, bool, unsigned long);
|
||||
extern unsigned long generic_max_swapfile_size(void);
|
||||
extern unsigned long max_swapfile_size(void);
|
||||
|
||||
#endif /* _LINUX_SWAPFILE_H */
|
||||
|
||||
+270
-12
@@ -60,6 +60,7 @@ struct cpuhp_cpu_state {
|
||||
bool rollback;
|
||||
bool single;
|
||||
bool bringup;
|
||||
bool booted_once;
|
||||
struct hlist_node *node;
|
||||
struct hlist_node *last;
|
||||
enum cpuhp_state cb_state;
|
||||
@@ -342,6 +343,85 @@ void cpu_hotplug_enable(void)
|
||||
EXPORT_SYMBOL_GPL(cpu_hotplug_enable);
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_SMT
|
||||
enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED;
|
||||
EXPORT_SYMBOL_GPL(cpu_smt_control);
|
||||
|
||||
static bool cpu_smt_available __read_mostly;
|
||||
|
||||
void __init cpu_smt_disable(bool force)
|
||||
{
|
||||
if (cpu_smt_control == CPU_SMT_FORCE_DISABLED ||
|
||||
cpu_smt_control == CPU_SMT_NOT_SUPPORTED)
|
||||
return;
|
||||
|
||||
if (force) {
|
||||
pr_info("SMT: Force disabled\n");
|
||||
cpu_smt_control = CPU_SMT_FORCE_DISABLED;
|
||||
} else {
|
||||
cpu_smt_control = CPU_SMT_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The decision whether SMT is supported can only be done after the full
|
||||
* CPU identification. Called from architecture code before non boot CPUs
|
||||
* are brought up.
|
||||
*/
|
||||
void __init cpu_smt_check_topology_early(void)
|
||||
{
|
||||
if (!topology_smt_supported())
|
||||
cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* If SMT was disabled by BIOS, detect it here, after the CPUs have been
|
||||
* brought online. This ensures the smt/l1tf sysfs entries are consistent
|
||||
* with reality. cpu_smt_available is set to true during the bringup of non
|
||||
* boot CPUs when a SMT sibling is detected. Note, this may overwrite
|
||||
* cpu_smt_control's previous setting.
|
||||
*/
|
||||
void __init cpu_smt_check_topology(void)
|
||||
{
|
||||
if (!cpu_smt_available)
|
||||
cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static int __init smt_cmdline_disable(char *str)
|
||||
{
|
||||
cpu_smt_disable(str && !strcmp(str, "force"));
|
||||
return 0;
|
||||
}
|
||||
early_param("nosmt", smt_cmdline_disable);
|
||||
|
||||
static inline bool cpu_smt_allowed(unsigned int cpu)
|
||||
{
|
||||
if (topology_is_primary_thread(cpu))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If the CPU is not a 'primary' thread and the booted_once bit is
|
||||
* set then the processor has SMT support. Store this information
|
||||
* for the late check of SMT support in cpu_smt_check_topology().
|
||||
*/
|
||||
if (per_cpu(cpuhp_state, cpu).booted_once)
|
||||
cpu_smt_available = true;
|
||||
|
||||
if (cpu_smt_control == CPU_SMT_ENABLED)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* On x86 it's required to boot all logical CPUs at least once so
|
||||
* that the init code can get a chance to set CR4.MCE on each
|
||||
* CPU. Otherwise, a broadacasted MCE observing CR4.MCE=0b on any
|
||||
* core will shutdown the machine.
|
||||
*/
|
||||
return !per_cpu(cpuhp_state, cpu).booted_once;
|
||||
}
|
||||
#else
|
||||
static inline bool cpu_smt_allowed(unsigned int cpu) { return true; }
|
||||
#endif
|
||||
|
||||
static inline enum cpuhp_state
|
||||
cpuhp_set_state(struct cpuhp_cpu_state *st, enum cpuhp_state target)
|
||||
{
|
||||
@@ -422,6 +502,16 @@ static int bringup_wait_for_ap(unsigned int cpu)
|
||||
stop_machine_unpark(cpu);
|
||||
kthread_unpark(st->thread);
|
||||
|
||||
/*
|
||||
* SMT soft disabling on X86 requires to bring the CPU out of the
|
||||
* BIOS 'wait for SIPI' state in order to set the CR4.MCE bit. The
|
||||
* CPU marked itself as booted_once in cpu_notify_starting() so the
|
||||
* cpu_smt_allowed() check will now return false if this is not the
|
||||
* primary sibling.
|
||||
*/
|
||||
if (!cpu_smt_allowed(cpu))
|
||||
return -ECANCELED;
|
||||
|
||||
if (st->target <= CPUHP_AP_ONLINE_IDLE)
|
||||
return 0;
|
||||
|
||||
@@ -754,7 +844,6 @@ static int takedown_cpu(unsigned int cpu)
|
||||
|
||||
/* Park the smpboot threads */
|
||||
kthread_park(per_cpu_ptr(&cpuhp_state, cpu)->thread);
|
||||
smpboot_park_threads(cpu);
|
||||
|
||||
/*
|
||||
* Prevent irq alloc/free while the dying cpu reorganizes the
|
||||
@@ -907,20 +996,19 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpu_down_maps_locked(unsigned int cpu, enum cpuhp_state target)
|
||||
{
|
||||
if (cpu_hotplug_disabled)
|
||||
return -EBUSY;
|
||||
return _cpu_down(cpu, 0, target);
|
||||
}
|
||||
|
||||
static int do_cpu_down(unsigned int cpu, enum cpuhp_state target)
|
||||
{
|
||||
int err;
|
||||
|
||||
cpu_maps_update_begin();
|
||||
|
||||
if (cpu_hotplug_disabled) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = _cpu_down(cpu, 0, target);
|
||||
|
||||
out:
|
||||
err = cpu_down_maps_locked(cpu, target);
|
||||
cpu_maps_update_done();
|
||||
return err;
|
||||
}
|
||||
@@ -949,6 +1037,7 @@ void notify_cpu_starting(unsigned int cpu)
|
||||
int ret;
|
||||
|
||||
rcu_cpu_starting(cpu); /* Enables RCU usage on this CPU. */
|
||||
st->booted_once = true;
|
||||
while (st->state < target) {
|
||||
st->state++;
|
||||
ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL);
|
||||
@@ -1058,6 +1147,10 @@ static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (!cpu_smt_allowed(cpu)) {
|
||||
err = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = _cpu_up(cpu, 0, target);
|
||||
out:
|
||||
@@ -1332,7 +1425,7 @@ static struct cpuhp_step cpuhp_hp_states[] = {
|
||||
[CPUHP_AP_SMPBOOT_THREADS] = {
|
||||
.name = "smpboot/threads:online",
|
||||
.startup.single = smpboot_unpark_threads,
|
||||
.teardown.single = NULL,
|
||||
.teardown.single = smpboot_park_threads,
|
||||
},
|
||||
[CPUHP_AP_IRQ_AFFINITY_ONLINE] = {
|
||||
.name = "irq/affinity:online",
|
||||
@@ -1906,10 +1999,172 @@ static const struct attribute_group cpuhp_cpu_root_attr_group = {
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_SMT
|
||||
|
||||
static const char *smt_states[] = {
|
||||
[CPU_SMT_ENABLED] = "on",
|
||||
[CPU_SMT_DISABLED] = "off",
|
||||
[CPU_SMT_FORCE_DISABLED] = "forceoff",
|
||||
[CPU_SMT_NOT_SUPPORTED] = "notsupported",
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
show_smt_control(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE - 2, "%s\n", smt_states[cpu_smt_control]);
|
||||
}
|
||||
|
||||
static void cpuhp_offline_cpu_device(unsigned int cpu)
|
||||
{
|
||||
struct device *dev = get_cpu_device(cpu);
|
||||
|
||||
dev->offline = true;
|
||||
/* Tell user space about the state change */
|
||||
kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
|
||||
}
|
||||
|
||||
static void cpuhp_online_cpu_device(unsigned int cpu)
|
||||
{
|
||||
struct device *dev = get_cpu_device(cpu);
|
||||
|
||||
dev->offline = false;
|
||||
/* Tell user space about the state change */
|
||||
kobject_uevent(&dev->kobj, KOBJ_ONLINE);
|
||||
}
|
||||
|
||||
static int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
|
||||
{
|
||||
int cpu, ret = 0;
|
||||
|
||||
cpu_maps_update_begin();
|
||||
for_each_online_cpu(cpu) {
|
||||
if (topology_is_primary_thread(cpu))
|
||||
continue;
|
||||
ret = cpu_down_maps_locked(cpu, CPUHP_OFFLINE);
|
||||
if (ret)
|
||||
break;
|
||||
/*
|
||||
* As this needs to hold the cpu maps lock it's impossible
|
||||
* to call device_offline() because that ends up calling
|
||||
* cpu_down() which takes cpu maps lock. cpu maps lock
|
||||
* needs to be held as this might race against in kernel
|
||||
* abusers of the hotplug machinery (thermal management).
|
||||
*
|
||||
* So nothing would update device:offline state. That would
|
||||
* leave the sysfs entry stale and prevent onlining after
|
||||
* smt control has been changed to 'off' again. This is
|
||||
* called under the sysfs hotplug lock, so it is properly
|
||||
* serialized against the regular offline usage.
|
||||
*/
|
||||
cpuhp_offline_cpu_device(cpu);
|
||||
}
|
||||
if (!ret)
|
||||
cpu_smt_control = ctrlval;
|
||||
cpu_maps_update_done();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpuhp_smt_enable(void)
|
||||
{
|
||||
int cpu, ret = 0;
|
||||
|
||||
cpu_maps_update_begin();
|
||||
cpu_smt_control = CPU_SMT_ENABLED;
|
||||
for_each_present_cpu(cpu) {
|
||||
/* Skip online CPUs and CPUs on offline nodes */
|
||||
if (cpu_online(cpu) || !node_online(cpu_to_node(cpu)))
|
||||
continue;
|
||||
ret = _cpu_up(cpu, 0, CPUHP_ONLINE);
|
||||
if (ret)
|
||||
break;
|
||||
/* See comment in cpuhp_smt_disable() */
|
||||
cpuhp_online_cpu_device(cpu);
|
||||
}
|
||||
cpu_maps_update_done();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
store_smt_control(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ctrlval, ret;
|
||||
|
||||
if (sysfs_streq(buf, "on"))
|
||||
ctrlval = CPU_SMT_ENABLED;
|
||||
else if (sysfs_streq(buf, "off"))
|
||||
ctrlval = CPU_SMT_DISABLED;
|
||||
else if (sysfs_streq(buf, "forceoff"))
|
||||
ctrlval = CPU_SMT_FORCE_DISABLED;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (cpu_smt_control == CPU_SMT_FORCE_DISABLED)
|
||||
return -EPERM;
|
||||
|
||||
if (cpu_smt_control == CPU_SMT_NOT_SUPPORTED)
|
||||
return -ENODEV;
|
||||
|
||||
ret = lock_device_hotplug_sysfs();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ctrlval != cpu_smt_control) {
|
||||
switch (ctrlval) {
|
||||
case CPU_SMT_ENABLED:
|
||||
ret = cpuhp_smt_enable();
|
||||
break;
|
||||
case CPU_SMT_DISABLED:
|
||||
case CPU_SMT_FORCE_DISABLED:
|
||||
ret = cpuhp_smt_disable(ctrlval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unlock_device_hotplug();
|
||||
return ret ? ret : count;
|
||||
}
|
||||
static DEVICE_ATTR(control, 0644, show_smt_control, store_smt_control);
|
||||
|
||||
static ssize_t
|
||||
show_smt_active(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
bool active = topology_max_smt_threads() > 1;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 2, "%d\n", active);
|
||||
}
|
||||
static DEVICE_ATTR(active, 0444, show_smt_active, NULL);
|
||||
|
||||
static struct attribute *cpuhp_smt_attrs[] = {
|
||||
&dev_attr_control.attr,
|
||||
&dev_attr_active.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group cpuhp_smt_attr_group = {
|
||||
.attrs = cpuhp_smt_attrs,
|
||||
.name = "smt",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int __init cpu_smt_state_init(void)
|
||||
{
|
||||
return sysfs_create_group(&cpu_subsys.dev_root->kobj,
|
||||
&cpuhp_smt_attr_group);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int cpu_smt_state_init(void) { return 0; }
|
||||
#endif
|
||||
|
||||
static int __init cpuhp_sysfs_init(void)
|
||||
{
|
||||
int cpu, ret;
|
||||
|
||||
ret = cpu_smt_state_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sysfs_create_group(&cpu_subsys.dev_root->kobj,
|
||||
&cpuhp_cpu_root_attr_group);
|
||||
if (ret)
|
||||
@@ -2012,5 +2267,8 @@ void __init boot_cpu_init(void)
|
||||
*/
|
||||
void __init boot_cpu_hotplug_init(void)
|
||||
{
|
||||
per_cpu_ptr(&cpuhp_state, smp_processor_id())->state = CPUHP_ONLINE;
|
||||
#ifdef CONFIG_SMP
|
||||
this_cpu_write(cpuhp_state.booted_once, true);
|
||||
#endif
|
||||
this_cpu_write(cpuhp_state.state, CPUHP_ONLINE);
|
||||
}
|
||||
|
||||
+12
-18
@@ -5774,6 +5774,18 @@ int sched_cpu_activate(unsigned int cpu)
|
||||
struct rq *rq = cpu_rq(cpu);
|
||||
struct rq_flags rf;
|
||||
|
||||
#ifdef CONFIG_SCHED_SMT
|
||||
/*
|
||||
* The sched_smt_present static key needs to be evaluated on every
|
||||
* hotplug event because at boot time SMT might be disabled when
|
||||
* the number of booted CPUs is limited.
|
||||
*
|
||||
* If then later a sibling gets hotplugged, then the key would stay
|
||||
* off and SMT scheduling would never be functional.
|
||||
*/
|
||||
if (cpumask_weight(cpu_smt_mask(cpu)) > 1)
|
||||
static_branch_enable_cpuslocked(&sched_smt_present);
|
||||
#endif
|
||||
set_cpu_active(cpu, true);
|
||||
|
||||
if (sched_smp_initialized) {
|
||||
@@ -5871,22 +5883,6 @@ int sched_cpu_dying(unsigned int cpu)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCHED_SMT
|
||||
DEFINE_STATIC_KEY_FALSE(sched_smt_present);
|
||||
|
||||
static void sched_init_smt(void)
|
||||
{
|
||||
/*
|
||||
* We've enumerated all CPUs and will assume that if any CPU
|
||||
* has SMT siblings, CPU0 will too.
|
||||
*/
|
||||
if (cpumask_weight(cpu_smt_mask(0)) > 1)
|
||||
static_branch_enable(&sched_smt_present);
|
||||
}
|
||||
#else
|
||||
static inline void sched_init_smt(void) { }
|
||||
#endif
|
||||
|
||||
void __init sched_init_smp(void)
|
||||
{
|
||||
sched_init_numa();
|
||||
@@ -5908,8 +5904,6 @@ void __init sched_init_smp(void)
|
||||
init_sched_rt_class();
|
||||
init_sched_dl_class();
|
||||
|
||||
sched_init_smt();
|
||||
|
||||
sched_smp_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6237,6 +6237,7 @@ static inline int find_idlest_cpu(struct sched_domain *sd, struct task_struct *p
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_SMT
|
||||
DEFINE_STATIC_KEY_FALSE(sched_smt_present);
|
||||
|
||||
static inline void set_idle_cores(int cpu, int val)
|
||||
{
|
||||
|
||||
@@ -584,6 +584,8 @@ void __init smp_init(void)
|
||||
num_nodes, (num_nodes > 1 ? "s" : ""),
|
||||
num_cpus, (num_cpus > 1 ? "s" : ""));
|
||||
|
||||
/* Final decision about SMT support */
|
||||
cpu_smt_check_topology();
|
||||
/* Any cleanup work */
|
||||
smp_cpus_done(setup_max_cpus);
|
||||
}
|
||||
|
||||
+2
-2
@@ -92,7 +92,7 @@ static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
|
||||
if (ioremap_pmd_enabled() &&
|
||||
((next - addr) == PMD_SIZE) &&
|
||||
IS_ALIGNED(phys_addr + addr, PMD_SIZE) &&
|
||||
pmd_free_pte_page(pmd)) {
|
||||
pmd_free_pte_page(pmd, addr)) {
|
||||
if (pmd_set_huge(pmd, phys_addr + addr, prot))
|
||||
continue;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ static inline int ioremap_pud_range(p4d_t *p4d, unsigned long addr,
|
||||
if (ioremap_pud_enabled() &&
|
||||
((next - addr) == PUD_SIZE) &&
|
||||
IS_ALIGNED(phys_addr + addr, PUD_SIZE) &&
|
||||
pud_free_pmd_page(pud)) {
|
||||
pud_free_pmd_page(pud, addr)) {
|
||||
if (pud_set_huge(pud, phys_addr + addr, prot))
|
||||
continue;
|
||||
}
|
||||
|
||||
+27
-10
@@ -1884,6 +1884,9 @@ int vm_insert_pfn_prot(struct vm_area_struct *vma, unsigned long addr,
|
||||
if (addr < vma->vm_start || addr >= vma->vm_end)
|
||||
return -EFAULT;
|
||||
|
||||
if (!pfn_modify_allowed(pfn, pgprot))
|
||||
return -EACCES;
|
||||
|
||||
track_pfn_insert(vma, &pgprot, __pfn_to_pfn_t(pfn, PFN_DEV));
|
||||
|
||||
ret = insert_pfn(vma, addr, __pfn_to_pfn_t(pfn, PFN_DEV), pgprot,
|
||||
@@ -1919,6 +1922,9 @@ static int __vm_insert_mixed(struct vm_area_struct *vma, unsigned long addr,
|
||||
|
||||
track_pfn_insert(vma, &pgprot, pfn);
|
||||
|
||||
if (!pfn_modify_allowed(pfn_t_to_pfn(pfn), pgprot))
|
||||
return -EACCES;
|
||||
|
||||
/*
|
||||
* If we don't have pte special, then we have to use the pfn_valid()
|
||||
* based VM_MIXEDMAP scheme (see vm_normal_page), and thus we *must*
|
||||
@@ -1980,6 +1986,7 @@ static int remap_pte_range(struct mm_struct *mm, pmd_t *pmd,
|
||||
{
|
||||
pte_t *pte;
|
||||
spinlock_t *ptl;
|
||||
int err = 0;
|
||||
|
||||
pte = pte_alloc_map_lock(mm, pmd, addr, &ptl);
|
||||
if (!pte)
|
||||
@@ -1987,12 +1994,16 @@ static int remap_pte_range(struct mm_struct *mm, pmd_t *pmd,
|
||||
arch_enter_lazy_mmu_mode();
|
||||
do {
|
||||
BUG_ON(!pte_none(*pte));
|
||||
if (!pfn_modify_allowed(pfn, prot)) {
|
||||
err = -EACCES;
|
||||
break;
|
||||
}
|
||||
set_pte_at(mm, addr, pte, pte_mkspecial(pfn_pte(pfn, prot)));
|
||||
pfn++;
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
arch_leave_lazy_mmu_mode();
|
||||
pte_unmap_unlock(pte - 1, ptl);
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int remap_pmd_range(struct mm_struct *mm, pud_t *pud,
|
||||
@@ -2001,6 +2012,7 @@ static inline int remap_pmd_range(struct mm_struct *mm, pud_t *pud,
|
||||
{
|
||||
pmd_t *pmd;
|
||||
unsigned long next;
|
||||
int err;
|
||||
|
||||
pfn -= addr >> PAGE_SHIFT;
|
||||
pmd = pmd_alloc(mm, pud, addr);
|
||||
@@ -2009,9 +2021,10 @@ static inline int remap_pmd_range(struct mm_struct *mm, pud_t *pud,
|
||||
VM_BUG_ON(pmd_trans_huge(*pmd));
|
||||
do {
|
||||
next = pmd_addr_end(addr, end);
|
||||
if (remap_pte_range(mm, pmd, addr, next,
|
||||
pfn + (addr >> PAGE_SHIFT), prot))
|
||||
return -ENOMEM;
|
||||
err = remap_pte_range(mm, pmd, addr, next,
|
||||
pfn + (addr >> PAGE_SHIFT), prot);
|
||||
if (err)
|
||||
return err;
|
||||
} while (pmd++, addr = next, addr != end);
|
||||
return 0;
|
||||
}
|
||||
@@ -2022,6 +2035,7 @@ static inline int remap_pud_range(struct mm_struct *mm, p4d_t *p4d,
|
||||
{
|
||||
pud_t *pud;
|
||||
unsigned long next;
|
||||
int err;
|
||||
|
||||
pfn -= addr >> PAGE_SHIFT;
|
||||
pud = pud_alloc(mm, p4d, addr);
|
||||
@@ -2029,9 +2043,10 @@ static inline int remap_pud_range(struct mm_struct *mm, p4d_t *p4d,
|
||||
return -ENOMEM;
|
||||
do {
|
||||
next = pud_addr_end(addr, end);
|
||||
if (remap_pmd_range(mm, pud, addr, next,
|
||||
pfn + (addr >> PAGE_SHIFT), prot))
|
||||
return -ENOMEM;
|
||||
err = remap_pmd_range(mm, pud, addr, next,
|
||||
pfn + (addr >> PAGE_SHIFT), prot);
|
||||
if (err)
|
||||
return err;
|
||||
} while (pud++, addr = next, addr != end);
|
||||
return 0;
|
||||
}
|
||||
@@ -2042,6 +2057,7 @@ static inline int remap_p4d_range(struct mm_struct *mm, pgd_t *pgd,
|
||||
{
|
||||
p4d_t *p4d;
|
||||
unsigned long next;
|
||||
int err;
|
||||
|
||||
pfn -= addr >> PAGE_SHIFT;
|
||||
p4d = p4d_alloc(mm, pgd, addr);
|
||||
@@ -2049,9 +2065,10 @@ static inline int remap_p4d_range(struct mm_struct *mm, pgd_t *pgd,
|
||||
return -ENOMEM;
|
||||
do {
|
||||
next = p4d_addr_end(addr, end);
|
||||
if (remap_pud_range(mm, p4d, addr, next,
|
||||
pfn + (addr >> PAGE_SHIFT), prot))
|
||||
return -ENOMEM;
|
||||
err = remap_pud_range(mm, p4d, addr, next,
|
||||
pfn + (addr >> PAGE_SHIFT), prot);
|
||||
if (err)
|
||||
return err;
|
||||
} while (p4d++, addr = next, addr != end);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -306,6 +306,42 @@ unsigned long change_protection(struct vm_area_struct *vma, unsigned long start,
|
||||
return pages;
|
||||
}
|
||||
|
||||
static int prot_none_pte_entry(pte_t *pte, unsigned long addr,
|
||||
unsigned long next, struct mm_walk *walk)
|
||||
{
|
||||
return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ?
|
||||
0 : -EACCES;
|
||||
}
|
||||
|
||||
static int prot_none_hugetlb_entry(pte_t *pte, unsigned long hmask,
|
||||
unsigned long addr, unsigned long next,
|
||||
struct mm_walk *walk)
|
||||
{
|
||||
return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ?
|
||||
0 : -EACCES;
|
||||
}
|
||||
|
||||
static int prot_none_test(unsigned long addr, unsigned long next,
|
||||
struct mm_walk *walk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prot_none_walk(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end, unsigned long newflags)
|
||||
{
|
||||
pgprot_t new_pgprot = vm_get_page_prot(newflags);
|
||||
struct mm_walk prot_none_walk = {
|
||||
.pte_entry = prot_none_pte_entry,
|
||||
.hugetlb_entry = prot_none_hugetlb_entry,
|
||||
.test_walk = prot_none_test,
|
||||
.mm = current->mm,
|
||||
.private = &new_pgprot,
|
||||
};
|
||||
|
||||
return walk_page_range(start, end, &prot_none_walk);
|
||||
}
|
||||
|
||||
int
|
||||
mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
|
||||
unsigned long start, unsigned long end, unsigned long newflags)
|
||||
@@ -323,6 +359,19 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do PROT_NONE PFN permission checks here when we can still
|
||||
* bail out without undoing a lot of state. This is a rather
|
||||
* uncommon case, so doesn't need to be very optimized.
|
||||
*/
|
||||
if (arch_has_pfn_modify_check() &&
|
||||
(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) &&
|
||||
(newflags & (VM_READ|VM_WRITE|VM_EXEC)) == 0) {
|
||||
error = prot_none_walk(vma, start, end, newflags);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we make a private mapping writable we increase our commit;
|
||||
* but (without finer accounting) cannot reduce our commit if we
|
||||
|
||||
+30
-16
@@ -2909,6 +2909,35 @@ static int claim_swapfile(struct swap_info_struct *p, struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find out how many pages are allowed for a single swap device. There
|
||||
* are two limiting factors:
|
||||
* 1) the number of bits for the swap offset in the swp_entry_t type, and
|
||||
* 2) the number of bits in the swap pte, as defined by the different
|
||||
* architectures.
|
||||
*
|
||||
* In order to find the largest possible bit mask, a swap entry with
|
||||
* swap type 0 and swap offset ~0UL is created, encoded to a swap pte,
|
||||
* decoded to a swp_entry_t again, and finally the swap offset is
|
||||
* extracted.
|
||||
*
|
||||
* This will mask all the bits from the initial ~0UL mask that can't
|
||||
* be encoded in either the swp_entry_t or the architecture definition
|
||||
* of a swap pte.
|
||||
*/
|
||||
unsigned long generic_max_swapfile_size(void)
|
||||
{
|
||||
return swp_offset(pte_to_swp_entry(
|
||||
swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1;
|
||||
}
|
||||
|
||||
/* Can be overridden by an architecture for additional checks. */
|
||||
__weak unsigned long max_swapfile_size(void)
|
||||
{
|
||||
return generic_max_swapfile_size();
|
||||
}
|
||||
|
||||
static unsigned long read_swap_header(struct swap_info_struct *p,
|
||||
union swap_header *swap_header,
|
||||
struct inode *inode)
|
||||
@@ -2944,22 +2973,7 @@ static unsigned long read_swap_header(struct swap_info_struct *p,
|
||||
p->cluster_next = 1;
|
||||
p->cluster_nr = 0;
|
||||
|
||||
/*
|
||||
* Find out how many pages are allowed for a single swap
|
||||
* device. There are two limiting factors: 1) the number
|
||||
* of bits for the swap offset in the swp_entry_t type, and
|
||||
* 2) the number of bits in the swap pte as defined by the
|
||||
* different architectures. In order to find the
|
||||
* largest possible bit mask, a swap entry with swap type 0
|
||||
* and swap offset ~0UL is created, encoded to a swap pte,
|
||||
* decoded to a swp_entry_t again, and finally the swap
|
||||
* offset is extracted. This will mask all the bits from
|
||||
* the initial ~0UL mask that can't be encoded in either
|
||||
* the swp_entry_t or the architecture definition of a
|
||||
* swap pte.
|
||||
*/
|
||||
maxpages = swp_offset(pte_to_swp_entry(
|
||||
swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1;
|
||||
maxpages = max_swapfile_size();
|
||||
last_page = swap_header->info.last_page;
|
||||
if (!last_page) {
|
||||
pr_warn("Empty swap-file\n");
|
||||
|
||||
@@ -431,8 +431,8 @@ static void hidp_del_timer(struct hidp_session *session)
|
||||
del_timer(&session->timer);
|
||||
}
|
||||
|
||||
static void hidp_process_report(struct hidp_session *session,
|
||||
int type, const u8 *data, int len, int intr)
|
||||
static void hidp_process_report(struct hidp_session *session, int type,
|
||||
const u8 *data, unsigned int len, int intr)
|
||||
{
|
||||
if (len > HID_MAX_BUFFER_SIZE)
|
||||
len = HID_MAX_BUFFER_SIZE;
|
||||
|
||||
+7
-1
@@ -10,10 +10,16 @@ fi
|
||||
DEPMOD=$1
|
||||
KERNELRELEASE=$2
|
||||
|
||||
if ! test -r System.map -a -x "$DEPMOD"; then
|
||||
if ! test -r System.map ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z $(command -v $DEPMOD) ]; then
|
||||
echo "'make modules_install' requires $DEPMOD. Please install it." >&2
|
||||
echo "This is probably in the kmod package." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# older versions of depmod require the version string to start with three
|
||||
# numbers, so we cheat with a symlink here
|
||||
depmod_hack_needed=true
|
||||
|
||||
@@ -219,6 +219,7 @@
|
||||
#define X86_FEATURE_IBPB ( 7*32+26) /* Indirect Branch Prediction Barrier */
|
||||
#define X86_FEATURE_STIBP ( 7*32+27) /* Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_ZEN ( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */
|
||||
#define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */
|
||||
|
||||
/* Virtualization flags: Linux defined, word 8 */
|
||||
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
|
||||
@@ -341,6 +342,7 @@
|
||||
#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
|
||||
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
|
||||
#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_FLUSH_L1D (18*32+28) /* Flush L1D cache */
|
||||
#define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */
|
||||
#define X86_FEATURE_SPEC_CTRL_SSBD (18*32+31) /* "" Speculative Store Bypass Disable */
|
||||
|
||||
@@ -373,5 +375,6 @@
|
||||
#define X86_BUG_SPECTRE_V1 X86_BUG(15) /* CPU is affected by Spectre variant 1 attack with conditional branches */
|
||||
#define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */
|
||||
#define X86_BUG_SPEC_STORE_BYPASS X86_BUG(17) /* CPU is affected by speculative store bypass attack */
|
||||
#define X86_BUG_L1TF X86_BUG(18) /* CPU is affected by L1 Terminal Fault */
|
||||
|
||||
#endif /* _ASM_X86_CPUFEATURES_H */
|
||||
|
||||
Reference in New Issue
Block a user