Merge 3efc57369a ("Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm") into android-mainline
Steps on the way to 6.12-rc1 Bug: 367265496 Change-Id: I267a0c6bfbc8e3742e3d1b8ef43d11f3ed1725eb Signed-off-by: Matthias Maennich <maennich@google.com>
This commit is contained in:
@@ -2681,6 +2681,23 @@
|
||||
|
||||
Default is Y (on).
|
||||
|
||||
kvm.enable_virt_at_load=[KVM,ARM64,LOONGARCH,MIPS,RISCV,X86]
|
||||
If enabled, KVM will enable virtualization in hardware
|
||||
when KVM is loaded, and disable virtualization when KVM
|
||||
is unloaded (if KVM is built as a module).
|
||||
|
||||
If disabled, KVM will dynamically enable and disable
|
||||
virtualization on-demand when creating and destroying
|
||||
VMs, i.e. on the 0=>1 and 1=>0 transitions of the
|
||||
number of VMs.
|
||||
|
||||
Enabling virtualization at module lode avoids potential
|
||||
latency for creation of the 0=>1 VM, as KVM serializes
|
||||
virtualization enabling across all online CPUs. The
|
||||
"cost" of enabling virtualization when KVM is loaded,
|
||||
is that doing so may interfere with using out-of-tree
|
||||
hypervisors that want to "own" virtualization hardware.
|
||||
|
||||
kvm.enable_vmware_backdoor=[KVM] Support VMware backdoor PV interface.
|
||||
Default is false (don't support).
|
||||
|
||||
|
||||
@@ -999,6 +999,36 @@ the vfio_ap mediated device to which it is assigned as long as each new APQN
|
||||
resulting from plugging it in references a queue device bound to the vfio_ap
|
||||
device driver.
|
||||
|
||||
Driver Features
|
||||
===============
|
||||
The vfio_ap driver exposes a sysfs file containing supported features.
|
||||
This exists so third party tools (like Libvirt and mdevctl) can query the
|
||||
availability of specific features.
|
||||
|
||||
The features list can be found here: /sys/bus/matrix/devices/matrix/features
|
||||
|
||||
Entries are space delimited. Each entry consists of a combination of
|
||||
alphanumeric and underscore characters.
|
||||
|
||||
Example:
|
||||
cat /sys/bus/matrix/devices/matrix/features
|
||||
guest_matrix dyn ap_config
|
||||
|
||||
the following features are advertised:
|
||||
|
||||
---------------+---------------------------------------------------------------+
|
||||
| Flag | Description |
|
||||
+==============+===============================================================+
|
||||
| guest_matrix | guest_matrix attribute exists. It reports the matrix of |
|
||||
| | adapters and domains that are or will be passed through to a |
|
||||
| | guest when the mdev is attached to it. |
|
||||
+--------------+---------------------------------------------------------------+
|
||||
| dyn | Indicates hot plug/unplug of AP adapters, domains and control |
|
||||
| | domains for a guest to which the mdev is attached. |
|
||||
+------------+-----------------------------------------------------------------+
|
||||
| ap_config | ap_config interface for one-shot modifications to mdev config |
|
||||
+--------------+---------------------------------------------------------------+
|
||||
|
||||
Limitations
|
||||
===========
|
||||
Live guest migration is not supported for guests using AP devices without
|
||||
|
||||
@@ -4214,7 +4214,9 @@ whether or not KVM_CAP_X86_USER_SPACE_MSR's KVM_MSR_EXIT_REASON_FILTER is
|
||||
enabled. If KVM_MSR_EXIT_REASON_FILTER is enabled, KVM will exit to userspace
|
||||
on denied accesses, i.e. userspace effectively intercepts the MSR access. If
|
||||
KVM_MSR_EXIT_REASON_FILTER is not enabled, KVM will inject a #GP into the guest
|
||||
on denied accesses.
|
||||
on denied accesses. Note, if an MSR access is denied during emulation of MSR
|
||||
load/stores during VMX transitions, KVM ignores KVM_MSR_EXIT_REASON_FILTER.
|
||||
See the below warning for full details.
|
||||
|
||||
If an MSR access is allowed by userspace, KVM will emulate and/or virtualize
|
||||
the access in accordance with the vCPU model. Note, KVM may still ultimately
|
||||
@@ -4229,9 +4231,22 @@ filtering. In that mode, ``KVM_MSR_FILTER_DEFAULT_DENY`` is invalid and causes
|
||||
an error.
|
||||
|
||||
.. warning::
|
||||
MSR accesses as part of nested VM-Enter/VM-Exit are not filtered.
|
||||
This includes both writes to individual VMCS fields and reads/writes
|
||||
through the MSR lists pointed to by the VMCS.
|
||||
MSR accesses that are side effects of instruction execution (emulated or
|
||||
native) are not filtered as hardware does not honor MSR bitmaps outside of
|
||||
RDMSR and WRMSR, and KVM mimics that behavior when emulating instructions
|
||||
to avoid pointless divergence from hardware. E.g. RDPID reads MSR_TSC_AUX,
|
||||
SYSENTER reads the SYSENTER MSRs, etc.
|
||||
|
||||
MSRs that are loaded/stored via dedicated VMCS fields are not filtered as
|
||||
part of VM-Enter/VM-Exit emulation.
|
||||
|
||||
MSRs that are loaded/store via VMX's load/store lists _are_ filtered as part
|
||||
of VM-Enter/VM-Exit emulation. If an MSR access is denied on VM-Enter, KVM
|
||||
synthesizes a consistency check VM-Exit(EXIT_REASON_MSR_LOAD_FAIL). If an
|
||||
MSR access is denied on VM-Exit, KVM synthesizes a VM-Abort. In short, KVM
|
||||
extends Intel's architectural list of MSRs that cannot be loaded/saved via
|
||||
the VM-Enter/VM-Exit MSR list. It is platform owner's responsibility to
|
||||
to communicate any such restrictions to their end users.
|
||||
|
||||
x2APIC MSR accesses cannot be filtered (KVM silently ignores filters that
|
||||
cover any x2APIC MSRs).
|
||||
@@ -8082,6 +8097,14 @@ KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS By default, KVM emulates MONITOR/MWAIT (if
|
||||
guest CPUID on writes to MISC_ENABLE if
|
||||
KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT is
|
||||
disabled.
|
||||
|
||||
KVM_X86_QUIRK_SLOT_ZAP_ALL By default, KVM invalidates all SPTEs in
|
||||
fast way for memslot deletion when VM type
|
||||
is KVM_X86_DEFAULT_VM.
|
||||
When this quirk is disabled or when VM type
|
||||
is other than KVM_X86_DEFAULT_VM, KVM zaps
|
||||
only leaf SPTEs that are within the range of
|
||||
the memslot being deleted.
|
||||
=================================== ============================================
|
||||
|
||||
7.32 KVM_CAP_MAX_VCPU_ID
|
||||
|
||||
@@ -11,6 +11,8 @@ The acquisition orders for mutexes are as follows:
|
||||
|
||||
- cpus_read_lock() is taken outside kvm_lock
|
||||
|
||||
- kvm_usage_lock is taken outside cpus_read_lock()
|
||||
|
||||
- kvm->lock is taken outside vcpu->mutex
|
||||
|
||||
- kvm->lock is taken outside kvm->slots_lock and kvm->irq_lock
|
||||
@@ -24,6 +26,13 @@ The acquisition orders for mutexes are as follows:
|
||||
are taken on the waiting side when modifying memslots, so MMU notifiers
|
||||
must not take either kvm->slots_lock or kvm->slots_arch_lock.
|
||||
|
||||
cpus_read_lock() vs kvm_lock:
|
||||
|
||||
- Taking cpus_read_lock() outside of kvm_lock is problematic, despite that
|
||||
being the official ordering, as it is quite easy to unknowingly trigger
|
||||
cpus_read_lock() while holding kvm_lock. Use caution when walking vm_list,
|
||||
e.g. avoid complex operations when possible.
|
||||
|
||||
For SRCU:
|
||||
|
||||
- ``synchronize_srcu(&kvm->srcu)`` is called inside critical sections
|
||||
@@ -227,10 +236,16 @@ time it will be set using the Dirty tracking mechanism described above.
|
||||
:Type: mutex
|
||||
:Arch: any
|
||||
:Protects: - vm_list
|
||||
- kvm_usage_count
|
||||
|
||||
``kvm_usage_lock``
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:Type: mutex
|
||||
:Arch: any
|
||||
:Protects: - kvm_usage_count
|
||||
- hardware virtualization enable/disable
|
||||
:Comment: KVM also disables CPU hotplug via cpus_read_lock() during
|
||||
enable/disable.
|
||||
:Comment: Exists to allow taking cpus_read_lock() while kvm_usage_count is
|
||||
protected, which simplifies the virtualization enabling logic.
|
||||
|
||||
``kvm->mn_invalidate_lock``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -290,11 +305,12 @@ time it will be set using the Dirty tracking mechanism described above.
|
||||
wakeup.
|
||||
|
||||
``vendor_module_lock``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
:Type: mutex
|
||||
:Arch: x86
|
||||
:Protects: loading a vendor module (kvm_amd or kvm_intel)
|
||||
:Comment: Exists because using kvm_lock leads to deadlock. cpu_hotplug_lock is
|
||||
taken outside of kvm_lock, e.g. in KVM's CPU online/offline callbacks, and
|
||||
many operations need to take cpu_hotplug_lock when loading a vendor module,
|
||||
e.g. updating static calls.
|
||||
:Comment: Exists because using kvm_lock leads to deadlock. kvm_lock is taken
|
||||
in notifiers, e.g. __kvmclock_cpufreq_notifier(), that may be invoked while
|
||||
cpu_hotplug_lock is held, e.g. from cpufreq_boost_trigger_state(), and many
|
||||
operations need to take cpu_hotplug_lock when loading a vendor module, e.g.
|
||||
updating static calls.
|
||||
|
||||
@@ -217,6 +217,8 @@ remote UML and other VM instances.
|
||||
+-----------+--------+------------------------------------+------------+
|
||||
| fd | vector | dependent on fd type | varies |
|
||||
+-----------+--------+------------------------------------+------------+
|
||||
| vde | vector | dep. on VDE VPN: Virt.Net Locator | varies |
|
||||
+-----------+--------+------------------------------------+------------+
|
||||
| tuntap | legacy | none | ~ 500Mbit |
|
||||
+-----------+--------+------------------------------------+------------+
|
||||
| daemon | legacy | none | ~ 450Mbit |
|
||||
@@ -573,6 +575,41 @@ https://github.com/NetSys/bess/wiki/Built-In-Modules-and-Ports
|
||||
|
||||
BESS transport does not require any special privileges.
|
||||
|
||||
VDE vector transport
|
||||
--------------------
|
||||
|
||||
Virtual Distributed Ethernet (VDE) is a project whose main goal is to provide a
|
||||
highly flexible support for virtual networking.
|
||||
|
||||
http://wiki.virtualsquare.org/#/tutorials/vdebasics
|
||||
|
||||
Common usages of VDE include fast prototyping and teaching.
|
||||
|
||||
Examples:
|
||||
|
||||
``vecX:transport=vde,vnl=tap://tap0``
|
||||
|
||||
use tap0
|
||||
|
||||
``vecX:transport=vde,vnl=slirp://``
|
||||
|
||||
use slirp
|
||||
|
||||
``vec0:transport=vde,vnl=vde:///tmp/switch``
|
||||
|
||||
connect to a vde switch
|
||||
|
||||
``vecX:transport=\"vde,vnl=cmd://ssh remote.host //tmp/sshlirp\"``
|
||||
|
||||
connect to a remote slirp (instant VPN: convert ssh to VPN, it uses sshlirp)
|
||||
https://github.com/virtualsquare/sshlirp
|
||||
|
||||
``vec0:transport=vde,vnl=vxvde://234.0.0.1``
|
||||
|
||||
connect to a local area cloud (all the UML nodes using the same
|
||||
multicast address running on hosts in the same multicast domain (LAN)
|
||||
will be automagically connected together to a virtual LAN.
|
||||
|
||||
Configuring Legacy transports
|
||||
=============================
|
||||
|
||||
|
||||
@@ -15685,6 +15685,9 @@ F: include/dt-bindings/clock/mobileye,eyeq5-clk.h
|
||||
|
||||
MODULE SUPPORT
|
||||
M: Luis Chamberlain <mcgrof@kernel.org>
|
||||
R: Petr Pavlu <petr.pavlu@suse.com>
|
||||
R: Sami Tolvanen <samitolvanen@google.com>
|
||||
R: Daniel Gomez <da.gomez@samsung.com>
|
||||
L: linux-modules@vger.kernel.org
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
@@ -2164,7 +2164,7 @@ static void cpu_hyp_uninit(void *discard)
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_arch_hardware_enable(void)
|
||||
int kvm_arch_enable_virtualization_cpu(void)
|
||||
{
|
||||
/*
|
||||
* Most calls to this function are made with migration
|
||||
@@ -2184,7 +2184,7 @@ int kvm_arch_hardware_enable(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_hardware_disable(void)
|
||||
void kvm_arch_disable_virtualization_cpu(void)
|
||||
{
|
||||
kvm_timer_cpu_down();
|
||||
kvm_vgic_cpu_down();
|
||||
@@ -2380,7 +2380,7 @@ static int __init do_pkvm_init(u32 hyp_va_bits)
|
||||
|
||||
/*
|
||||
* The stub hypercalls are now disabled, so set our local flag to
|
||||
* prevent a later re-init attempt in kvm_arch_hardware_enable().
|
||||
* prevent a later re-init attempt in kvm_arch_enable_virtualization_cpu().
|
||||
*/
|
||||
__this_cpu_write(kvm_hyp_initialized, 1);
|
||||
preempt_enable();
|
||||
|
||||
@@ -261,7 +261,7 @@ long kvm_arch_dev_ioctl(struct file *filp,
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
int kvm_arch_hardware_enable(void)
|
||||
int kvm_arch_enable_virtualization_cpu(void)
|
||||
{
|
||||
unsigned long env, gcfg = 0;
|
||||
|
||||
@@ -300,7 +300,7 @@ int kvm_arch_hardware_enable(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_hardware_disable(void)
|
||||
void kvm_arch_disable_virtualization_cpu(void)
|
||||
{
|
||||
write_csr_gcfg(0);
|
||||
write_csr_gstat(0);
|
||||
|
||||
@@ -728,8 +728,8 @@ struct kvm_mips_callbacks {
|
||||
int (*handle_fpe)(struct kvm_vcpu *vcpu);
|
||||
int (*handle_msa_disabled)(struct kvm_vcpu *vcpu);
|
||||
int (*handle_guest_exit)(struct kvm_vcpu *vcpu);
|
||||
int (*hardware_enable)(void);
|
||||
void (*hardware_disable)(void);
|
||||
int (*enable_virtualization_cpu)(void);
|
||||
void (*disable_virtualization_cpu)(void);
|
||||
int (*check_extension)(struct kvm *kvm, long ext);
|
||||
int (*vcpu_init)(struct kvm_vcpu *vcpu);
|
||||
void (*vcpu_uninit)(struct kvm_vcpu *vcpu);
|
||||
|
||||
@@ -125,14 +125,14 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_arch_hardware_enable(void)
|
||||
int kvm_arch_enable_virtualization_cpu(void)
|
||||
{
|
||||
return kvm_mips_callbacks->hardware_enable();
|
||||
return kvm_mips_callbacks->enable_virtualization_cpu();
|
||||
}
|
||||
|
||||
void kvm_arch_hardware_disable(void)
|
||||
void kvm_arch_disable_virtualization_cpu(void)
|
||||
{
|
||||
kvm_mips_callbacks->hardware_disable();
|
||||
kvm_mips_callbacks->disable_virtualization_cpu();
|
||||
}
|
||||
|
||||
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||
|
||||
+4
-4
@@ -2869,7 +2869,7 @@ static unsigned int kvm_vz_resize_guest_vtlb(unsigned int size)
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
static int kvm_vz_hardware_enable(void)
|
||||
static int kvm_vz_enable_virtualization_cpu(void)
|
||||
{
|
||||
unsigned int mmu_size, guest_mmu_size, ftlb_size;
|
||||
u64 guest_cvmctl, cvmvmconfig;
|
||||
@@ -2983,7 +2983,7 @@ static int kvm_vz_hardware_enable(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvm_vz_hardware_disable(void)
|
||||
static void kvm_vz_disable_virtualization_cpu(void)
|
||||
{
|
||||
u64 cvmvmconfig;
|
||||
unsigned int mmu_size;
|
||||
@@ -3280,8 +3280,8 @@ static struct kvm_mips_callbacks kvm_vz_callbacks = {
|
||||
.handle_msa_disabled = kvm_trap_vz_handle_msa_disabled,
|
||||
.handle_guest_exit = kvm_trap_vz_handle_guest_exit,
|
||||
|
||||
.hardware_enable = kvm_vz_hardware_enable,
|
||||
.hardware_disable = kvm_vz_hardware_disable,
|
||||
.enable_virtualization_cpu = kvm_vz_enable_virtualization_cpu,
|
||||
.disable_virtualization_cpu = kvm_vz_disable_virtualization_cpu,
|
||||
.check_extension = kvm_vz_check_extension,
|
||||
.vcpu_init = kvm_vz_vcpu_init,
|
||||
.vcpu_uninit = kvm_vz_vcpu_uninit,
|
||||
|
||||
@@ -20,7 +20,7 @@ long kvm_arch_dev_ioctl(struct file *filp,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_hardware_enable(void)
|
||||
int kvm_arch_enable_virtualization_cpu(void)
|
||||
{
|
||||
csr_write(CSR_HEDELEG, KVM_HEDELEG_DEFAULT);
|
||||
csr_write(CSR_HIDELEG, KVM_HIDELEG_DEFAULT);
|
||||
@@ -35,7 +35,7 @@ int kvm_arch_hardware_enable(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_hardware_disable(void)
|
||||
void kvm_arch_disable_virtualization_cpu(void)
|
||||
{
|
||||
kvm_riscv_aia_disable();
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ CONFIG_CMM=m
|
||||
CONFIG_APPLDATA_BASE=y
|
||||
CONFIG_S390_HYPFS_FS=y
|
||||
CONFIG_KVM=m
|
||||
CONFIG_KVM_S390_UCONTROL=y
|
||||
CONFIG_S390_UNWIND_SELFTEST=m
|
||||
CONFIG_S390_KPROBES_SANITY_TEST=m
|
||||
CONFIG_S390_MODULES_SANITY_TEST=m
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
* for details.
|
||||
*/
|
||||
.macro vdso_func func
|
||||
.globl __kernel_\func
|
||||
.type __kernel_\func,@function
|
||||
__ALIGN
|
||||
__kernel_\func:
|
||||
SYM_FUNC_START(__kernel_\func)
|
||||
CFI_STARTPROC
|
||||
aghi %r15,-STACK_FRAME_VDSO_OVERHEAD
|
||||
CFI_DEF_CFA_OFFSET (STACK_FRAME_USER_OVERHEAD + STACK_FRAME_VDSO_OVERHEAD)
|
||||
@@ -32,7 +29,7 @@ __kernel_\func:
|
||||
CFI_RESTORE 15
|
||||
br %r14
|
||||
CFI_ENDPROC
|
||||
.size __kernel_\func,.-__kernel_\func
|
||||
SYM_FUNC_END(__kernel_\func)
|
||||
.endm
|
||||
|
||||
vdso_func gettimeofday
|
||||
@@ -41,16 +38,13 @@ vdso_func clock_gettime
|
||||
vdso_func getcpu
|
||||
|
||||
.macro vdso_syscall func,syscall
|
||||
.globl __kernel_\func
|
||||
.type __kernel_\func,@function
|
||||
__ALIGN
|
||||
__kernel_\func:
|
||||
SYM_FUNC_START(__kernel_\func)
|
||||
CFI_STARTPROC
|
||||
svc \syscall
|
||||
/* Make sure we notice when a syscall returns, which shouldn't happen */
|
||||
.word 0
|
||||
CFI_ENDPROC
|
||||
.size __kernel_\func,.-__kernel_\func
|
||||
SYM_FUNC_END(__kernel_\func)
|
||||
.endm
|
||||
|
||||
vdso_syscall restart_syscall,__NR_restart_syscall
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/dwarf.h>
|
||||
#include <asm/fpu-insn.h>
|
||||
|
||||
#define STATE0 %v0
|
||||
@@ -12,9 +14,6 @@
|
||||
#define COPY1 %v5
|
||||
#define COPY2 %v6
|
||||
#define COPY3 %v7
|
||||
#define PERM4 %v16
|
||||
#define PERM8 %v17
|
||||
#define PERM12 %v18
|
||||
#define BEPERM %v19
|
||||
#define TMP0 %v20
|
||||
#define TMP1 %v21
|
||||
@@ -23,13 +22,11 @@
|
||||
|
||||
.section .rodata
|
||||
|
||||
.balign 128
|
||||
.Lconstants:
|
||||
.balign 32
|
||||
SYM_DATA_START_LOCAL(chacha20_constants)
|
||||
.long 0x61707865,0x3320646e,0x79622d32,0x6b206574 # endian-neutral
|
||||
.long 0x04050607,0x08090a0b,0x0c0d0e0f,0x00010203 # rotl 4 bytes
|
||||
.long 0x08090a0b,0x0c0d0e0f,0x00010203,0x04050607 # rotl 8 bytes
|
||||
.long 0x0c0d0e0f,0x00010203,0x04050607,0x08090a0b # rotl 12 bytes
|
||||
.long 0x03020100,0x07060504,0x0b0a0908,0x0f0e0d0c # byte swap
|
||||
SYM_DATA_END(chacha20_constants)
|
||||
|
||||
.text
|
||||
/*
|
||||
@@ -43,13 +40,14 @@
|
||||
* size_t nblocks)
|
||||
*/
|
||||
SYM_FUNC_START(__arch_chacha20_blocks_nostack)
|
||||
larl %r1,.Lconstants
|
||||
CFI_STARTPROC
|
||||
larl %r1,chacha20_constants
|
||||
|
||||
/* COPY0 = "expand 32-byte k" */
|
||||
VL COPY0,0,,%r1
|
||||
|
||||
/* PERM4-PERM12,BEPERM = byte selectors for VPERM */
|
||||
VLM PERM4,BEPERM,16,%r1
|
||||
/* BEPERM = byte selectors for VPERM */
|
||||
ALTERNATIVE __stringify(VL BEPERM,16,,%r1), "brcl 0,0", ALT_FACILITY(148)
|
||||
|
||||
/* COPY1,COPY2 = key */
|
||||
VLM COPY1,COPY2,0,%r3
|
||||
@@ -89,11 +87,11 @@ SYM_FUNC_START(__arch_chacha20_blocks_nostack)
|
||||
VERLLF STATE1,STATE1,7
|
||||
|
||||
/* STATE1[0,1,2,3] = STATE1[1,2,3,0] */
|
||||
VPERM STATE1,STATE1,STATE1,PERM4
|
||||
VSLDB STATE1,STATE1,STATE1,4
|
||||
/* STATE2[0,1,2,3] = STATE2[2,3,0,1] */
|
||||
VPERM STATE2,STATE2,STATE2,PERM8
|
||||
VSLDB STATE2,STATE2,STATE2,8
|
||||
/* STATE3[0,1,2,3] = STATE3[3,0,1,2] */
|
||||
VPERM STATE3,STATE3,STATE3,PERM12
|
||||
VSLDB STATE3,STATE3,STATE3,12
|
||||
|
||||
/* STATE0 += STATE1, STATE3 = rotl32(STATE3 ^ STATE0, 16) */
|
||||
VAF STATE0,STATE0,STATE1
|
||||
@@ -116,32 +114,38 @@ SYM_FUNC_START(__arch_chacha20_blocks_nostack)
|
||||
VERLLF STATE1,STATE1,7
|
||||
|
||||
/* STATE1[0,1,2,3] = STATE1[3,0,1,2] */
|
||||
VPERM STATE1,STATE1,STATE1,PERM12
|
||||
VSLDB STATE1,STATE1,STATE1,12
|
||||
/* STATE2[0,1,2,3] = STATE2[2,3,0,1] */
|
||||
VPERM STATE2,STATE2,STATE2,PERM8
|
||||
VSLDB STATE2,STATE2,STATE2,8
|
||||
/* STATE3[0,1,2,3] = STATE3[1,2,3,0] */
|
||||
VPERM STATE3,STATE3,STATE3,PERM4
|
||||
VSLDB STATE3,STATE3,STATE3,4
|
||||
brctg %r0,.Ldoubleround
|
||||
|
||||
/* OUTPUT0 = STATE0 + STATE0 */
|
||||
/* OUTPUT0 = STATE0 + COPY0 */
|
||||
VAF STATE0,STATE0,COPY0
|
||||
/* OUTPUT1 = STATE1 + STATE1 */
|
||||
/* OUTPUT1 = STATE1 + COPY1 */
|
||||
VAF STATE1,STATE1,COPY1
|
||||
/* OUTPUT2 = STATE2 + STATE2 */
|
||||
/* OUTPUT2 = STATE2 + COPY2 */
|
||||
VAF STATE2,STATE2,COPY2
|
||||
/* OUTPUT2 = STATE3 + STATE3 */
|
||||
/* OUTPUT3 = STATE3 + COPY3 */
|
||||
VAF STATE3,STATE3,COPY3
|
||||
|
||||
/*
|
||||
* 32 bit wise little endian store to OUTPUT. If the vector
|
||||
* enhancement facility 2 is not installed use the slow path.
|
||||
*/
|
||||
ALTERNATIVE "brc 0xf,.Lstoreslow", "nop", ALT_FACILITY(148)
|
||||
VSTBRF STATE0,0,,%r2
|
||||
VSTBRF STATE1,16,,%r2
|
||||
VSTBRF STATE2,32,,%r2
|
||||
VSTBRF STATE3,48,,%r2
|
||||
.Lstoredone:
|
||||
ALTERNATIVE \
|
||||
__stringify( \
|
||||
/* Convert STATE to little endian and store to OUTPUT */\
|
||||
VPERM TMP0,STATE0,STATE0,BEPERM; \
|
||||
VPERM TMP1,STATE1,STATE1,BEPERM; \
|
||||
VPERM TMP2,STATE2,STATE2,BEPERM; \
|
||||
VPERM TMP3,STATE3,STATE3,BEPERM; \
|
||||
VSTM TMP0,TMP3,0,%r2), \
|
||||
__stringify( \
|
||||
/* 32 bit wise little endian store to OUTPUT */ \
|
||||
VSTBRF STATE0,0,,%r2; \
|
||||
VSTBRF STATE1,16,,%r2; \
|
||||
VSTBRF STATE2,32,,%r2; \
|
||||
VSTBRF STATE3,48,,%r2; \
|
||||
brcl 0,0), \
|
||||
ALT_FACILITY(148)
|
||||
|
||||
/* ++COPY3.COUNTER */
|
||||
/* alsih %r3,1 */
|
||||
@@ -173,13 +177,5 @@ SYM_FUNC_START(__arch_chacha20_blocks_nostack)
|
||||
VZERO TMP3
|
||||
|
||||
br %r14
|
||||
|
||||
.Lstoreslow:
|
||||
/* Convert STATE to little endian format and store to OUTPUT */
|
||||
VPERM TMP0,STATE0,STATE0,BEPERM
|
||||
VPERM TMP1,STATE1,STATE1,BEPERM
|
||||
VPERM TMP2,STATE2,STATE2,BEPERM
|
||||
VPERM TMP3,STATE3,STATE3,BEPERM
|
||||
VSTM TMP0,TMP3,0,%r2
|
||||
j .Lstoredone
|
||||
CFI_ENDPROC
|
||||
SYM_FUNC_END(__arch_chacha20_blocks_nostack)
|
||||
|
||||
@@ -348,20 +348,29 @@ static inline int plo_test_bit(unsigned char nr)
|
||||
return cc == 0;
|
||||
}
|
||||
|
||||
static __always_inline void __insn32_query(unsigned int opcode, u8 *query)
|
||||
static __always_inline void __sortl_query(u8 (*query)[32])
|
||||
{
|
||||
asm volatile(
|
||||
" lghi 0,0\n"
|
||||
" lgr 1,%[query]\n"
|
||||
" la 1,%[query]\n"
|
||||
/* Parameter registers are ignored */
|
||||
" .insn rrf,%[opc] << 16,2,4,6,0\n"
|
||||
" .insn rre,0xb9380000,2,4\n"
|
||||
: [query] "=R" (*query)
|
||||
:
|
||||
: [query] "d" ((unsigned long)query), [opc] "i" (opcode)
|
||||
: "cc", "memory", "0", "1");
|
||||
: "cc", "0", "1");
|
||||
}
|
||||
|
||||
#define INSN_SORTL 0xb938
|
||||
#define INSN_DFLTCC 0xb939
|
||||
static __always_inline void __dfltcc_query(u8 (*query)[32])
|
||||
{
|
||||
asm volatile(
|
||||
" lghi 0,0\n"
|
||||
" la 1,%[query]\n"
|
||||
/* Parameter registers are ignored */
|
||||
" .insn rrf,0xb9390000,2,4,6,0\n"
|
||||
: [query] "=R" (*query)
|
||||
:
|
||||
: "cc", "0", "1");
|
||||
}
|
||||
|
||||
static void __init kvm_s390_cpu_feat_init(void)
|
||||
{
|
||||
@@ -415,10 +424,10 @@ static void __init kvm_s390_cpu_feat_init(void)
|
||||
kvm_s390_available_subfunc.kdsa);
|
||||
|
||||
if (test_facility(150)) /* SORTL */
|
||||
__insn32_query(INSN_SORTL, kvm_s390_available_subfunc.sortl);
|
||||
__sortl_query(&kvm_s390_available_subfunc.sortl);
|
||||
|
||||
if (test_facility(151)) /* DFLTCC */
|
||||
__insn32_query(INSN_DFLTCC, kvm_s390_available_subfunc.dfltcc);
|
||||
__dfltcc_query(&kvm_s390_available_subfunc.dfltcc);
|
||||
|
||||
if (MACHINE_HAS_ESOP)
|
||||
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_ESOP);
|
||||
|
||||
@@ -11,7 +11,6 @@ config UML
|
||||
select ARCH_HAS_KCOV
|
||||
select ARCH_HAS_STRNCPY_FROM_USER
|
||||
select ARCH_HAS_STRNLEN_USER
|
||||
select ARCH_NO_PREEMPT_DYNAMIC
|
||||
select HAVE_ARCH_AUDITSYSCALL
|
||||
select HAVE_ARCH_KASAN if X86_64
|
||||
select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN
|
||||
|
||||
+109
-103
@@ -22,6 +22,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/fs.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <uapi/linux/filter.h>
|
||||
#include <init.h>
|
||||
#include <irq_kern.h>
|
||||
@@ -102,18 +103,33 @@ static const struct {
|
||||
|
||||
static void vector_reset_stats(struct vector_private *vp)
|
||||
{
|
||||
/* We reuse the existing queue locks for stats */
|
||||
|
||||
/* RX stats are modified with RX head_lock held
|
||||
* in vector_poll.
|
||||
*/
|
||||
|
||||
spin_lock(&vp->rx_queue->head_lock);
|
||||
vp->estats.rx_queue_max = 0;
|
||||
vp->estats.rx_queue_running_average = 0;
|
||||
vp->estats.tx_queue_max = 0;
|
||||
vp->estats.tx_queue_running_average = 0;
|
||||
vp->estats.rx_encaps_errors = 0;
|
||||
vp->estats.sg_ok = 0;
|
||||
vp->estats.sg_linearized = 0;
|
||||
spin_unlock(&vp->rx_queue->head_lock);
|
||||
|
||||
/* TX stats are modified with TX head_lock held
|
||||
* in vector_send.
|
||||
*/
|
||||
|
||||
spin_lock(&vp->tx_queue->head_lock);
|
||||
vp->estats.tx_timeout_count = 0;
|
||||
vp->estats.tx_restart_queue = 0;
|
||||
vp->estats.tx_kicks = 0;
|
||||
vp->estats.tx_flow_control_xon = 0;
|
||||
vp->estats.tx_flow_control_xoff = 0;
|
||||
vp->estats.sg_ok = 0;
|
||||
vp->estats.sg_linearized = 0;
|
||||
vp->estats.tx_queue_max = 0;
|
||||
vp->estats.tx_queue_running_average = 0;
|
||||
spin_unlock(&vp->tx_queue->head_lock);
|
||||
}
|
||||
|
||||
static int get_mtu(struct arglist *def)
|
||||
@@ -232,12 +248,6 @@ static int get_transport_options(struct arglist *def)
|
||||
|
||||
static char *drop_buffer;
|
||||
|
||||
/* Array backed queues optimized for bulk enqueue/dequeue and
|
||||
* 1:N (small values of N) or 1:1 enqueuer/dequeuer ratios.
|
||||
* For more details and full design rationale see
|
||||
* http://foswiki.cambridgegreys.com/Main/EatYourTailAndEnjoyIt
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Advance the mmsg queue head by n = advance. Resets the queue to
|
||||
@@ -247,27 +257,13 @@ static char *drop_buffer;
|
||||
|
||||
static int vector_advancehead(struct vector_queue *qi, int advance)
|
||||
{
|
||||
int queue_depth;
|
||||
|
||||
qi->head =
|
||||
(qi->head + advance)
|
||||
% qi->max_depth;
|
||||
|
||||
|
||||
spin_lock(&qi->tail_lock);
|
||||
qi->queue_depth -= advance;
|
||||
|
||||
/* we are at 0, use this to
|
||||
* reset head and tail so we can use max size vectors
|
||||
*/
|
||||
|
||||
if (qi->queue_depth == 0) {
|
||||
qi->head = 0;
|
||||
qi->tail = 0;
|
||||
}
|
||||
queue_depth = qi->queue_depth;
|
||||
spin_unlock(&qi->tail_lock);
|
||||
return queue_depth;
|
||||
atomic_sub(advance, &qi->queue_depth);
|
||||
return atomic_read(&qi->queue_depth);
|
||||
}
|
||||
|
||||
/* Advance the queue tail by n = advance.
|
||||
@@ -277,16 +273,11 @@ static int vector_advancehead(struct vector_queue *qi, int advance)
|
||||
|
||||
static int vector_advancetail(struct vector_queue *qi, int advance)
|
||||
{
|
||||
int queue_depth;
|
||||
|
||||
qi->tail =
|
||||
(qi->tail + advance)
|
||||
% qi->max_depth;
|
||||
spin_lock(&qi->head_lock);
|
||||
qi->queue_depth += advance;
|
||||
queue_depth = qi->queue_depth;
|
||||
spin_unlock(&qi->head_lock);
|
||||
return queue_depth;
|
||||
atomic_add(advance, &qi->queue_depth);
|
||||
return atomic_read(&qi->queue_depth);
|
||||
}
|
||||
|
||||
static int prep_msg(struct vector_private *vp,
|
||||
@@ -339,9 +330,7 @@ static int vector_enqueue(struct vector_queue *qi, struct sk_buff *skb)
|
||||
int iov_count;
|
||||
|
||||
spin_lock(&qi->tail_lock);
|
||||
spin_lock(&qi->head_lock);
|
||||
queue_depth = qi->queue_depth;
|
||||
spin_unlock(&qi->head_lock);
|
||||
queue_depth = atomic_read(&qi->queue_depth);
|
||||
|
||||
if (skb)
|
||||
packet_len = skb->len;
|
||||
@@ -360,6 +349,7 @@ static int vector_enqueue(struct vector_queue *qi, struct sk_buff *skb)
|
||||
mmsg_vector->msg_hdr.msg_iovlen = iov_count;
|
||||
mmsg_vector->msg_hdr.msg_name = vp->fds->remote_addr;
|
||||
mmsg_vector->msg_hdr.msg_namelen = vp->fds->remote_addr_size;
|
||||
wmb(); /* Make the packet visible to the NAPI poll thread */
|
||||
queue_depth = vector_advancetail(qi, 1);
|
||||
} else
|
||||
goto drop;
|
||||
@@ -398,7 +388,7 @@ static int consume_vector_skbs(struct vector_queue *qi, int count)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic vector deque via sendmmsg with support for forming headers
|
||||
* Generic vector dequeue via sendmmsg with support for forming headers
|
||||
* using transport specific callback. Allows GRE, L2TPv3, RAW and
|
||||
* other transports to use a common dequeue procedure in vector mode
|
||||
*/
|
||||
@@ -408,69 +398,64 @@ static int vector_send(struct vector_queue *qi)
|
||||
{
|
||||
struct vector_private *vp = netdev_priv(qi->dev);
|
||||
struct mmsghdr *send_from;
|
||||
int result = 0, send_len, queue_depth = qi->max_depth;
|
||||
int result = 0, send_len;
|
||||
|
||||
if (spin_trylock(&qi->head_lock)) {
|
||||
if (spin_trylock(&qi->tail_lock)) {
|
||||
/* update queue_depth to current value */
|
||||
queue_depth = qi->queue_depth;
|
||||
spin_unlock(&qi->tail_lock);
|
||||
while (queue_depth > 0) {
|
||||
/* Calculate the start of the vector */
|
||||
send_len = queue_depth;
|
||||
send_from = qi->mmsg_vector;
|
||||
send_from += qi->head;
|
||||
/* Adjust vector size if wraparound */
|
||||
if (send_len + qi->head > qi->max_depth)
|
||||
send_len = qi->max_depth - qi->head;
|
||||
/* Try to TX as many packets as possible */
|
||||
if (send_len > 0) {
|
||||
result = uml_vector_sendmmsg(
|
||||
vp->fds->tx_fd,
|
||||
send_from,
|
||||
send_len,
|
||||
0
|
||||
);
|
||||
vp->in_write_poll =
|
||||
(result != send_len);
|
||||
}
|
||||
/* For some of the sendmmsg error scenarios
|
||||
* we may end being unsure in the TX success
|
||||
* for all packets. It is safer to declare
|
||||
* them all TX-ed and blame the network.
|
||||
/* update queue_depth to current value */
|
||||
while (atomic_read(&qi->queue_depth) > 0) {
|
||||
/* Calculate the start of the vector */
|
||||
send_len = atomic_read(&qi->queue_depth);
|
||||
send_from = qi->mmsg_vector;
|
||||
send_from += qi->head;
|
||||
/* Adjust vector size if wraparound */
|
||||
if (send_len + qi->head > qi->max_depth)
|
||||
send_len = qi->max_depth - qi->head;
|
||||
/* Try to TX as many packets as possible */
|
||||
if (send_len > 0) {
|
||||
result = uml_vector_sendmmsg(
|
||||
vp->fds->tx_fd,
|
||||
send_from,
|
||||
send_len,
|
||||
0
|
||||
);
|
||||
vp->in_write_poll =
|
||||
(result != send_len);
|
||||
}
|
||||
/* For some of the sendmmsg error scenarios
|
||||
* we may end being unsure in the TX success
|
||||
* for all packets. It is safer to declare
|
||||
* them all TX-ed and blame the network.
|
||||
*/
|
||||
if (result < 0) {
|
||||
if (net_ratelimit())
|
||||
netdev_err(vp->dev, "sendmmsg err=%i\n",
|
||||
result);
|
||||
vp->in_error = true;
|
||||
result = send_len;
|
||||
}
|
||||
if (result > 0) {
|
||||
consume_vector_skbs(qi, result);
|
||||
/* This is equivalent to an TX IRQ.
|
||||
* Restart the upper layers to feed us
|
||||
* more packets.
|
||||
*/
|
||||
if (result < 0) {
|
||||
if (net_ratelimit())
|
||||
netdev_err(vp->dev, "sendmmsg err=%i\n",
|
||||
result);
|
||||
vp->in_error = true;
|
||||
result = send_len;
|
||||
}
|
||||
if (result > 0) {
|
||||
queue_depth =
|
||||
consume_vector_skbs(qi, result);
|
||||
/* This is equivalent to an TX IRQ.
|
||||
* Restart the upper layers to feed us
|
||||
* more packets.
|
||||
*/
|
||||
if (result > vp->estats.tx_queue_max)
|
||||
vp->estats.tx_queue_max = result;
|
||||
vp->estats.tx_queue_running_average =
|
||||
(vp->estats.tx_queue_running_average + result) >> 1;
|
||||
}
|
||||
netif_wake_queue(qi->dev);
|
||||
/* if TX is busy, break out of the send loop,
|
||||
* poll write IRQ will reschedule xmit for us
|
||||
*/
|
||||
if (result != send_len) {
|
||||
vp->estats.tx_restart_queue++;
|
||||
break;
|
||||
}
|
||||
if (result > vp->estats.tx_queue_max)
|
||||
vp->estats.tx_queue_max = result;
|
||||
vp->estats.tx_queue_running_average =
|
||||
(vp->estats.tx_queue_running_average + result) >> 1;
|
||||
}
|
||||
netif_wake_queue(qi->dev);
|
||||
/* if TX is busy, break out of the send loop,
|
||||
* poll write IRQ will reschedule xmit for us.
|
||||
*/
|
||||
if (result != send_len) {
|
||||
vp->estats.tx_restart_queue++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&qi->head_lock);
|
||||
}
|
||||
return queue_depth;
|
||||
return atomic_read(&qi->queue_depth);
|
||||
}
|
||||
|
||||
/* Queue destructor. Deliberately stateless so we can use
|
||||
@@ -589,7 +574,7 @@ static struct vector_queue *create_queue(
|
||||
}
|
||||
spin_lock_init(&result->head_lock);
|
||||
spin_lock_init(&result->tail_lock);
|
||||
result->queue_depth = 0;
|
||||
atomic_set(&result->queue_depth, 0);
|
||||
result->head = 0;
|
||||
result->tail = 0;
|
||||
return result;
|
||||
@@ -668,18 +653,27 @@ done:
|
||||
}
|
||||
|
||||
|
||||
/* Prepare queue for recvmmsg one-shot rx - fill with fresh sk_buffs*/
|
||||
/* Prepare queue for recvmmsg one-shot rx - fill with fresh sk_buffs */
|
||||
|
||||
static void prep_queue_for_rx(struct vector_queue *qi)
|
||||
{
|
||||
struct vector_private *vp = netdev_priv(qi->dev);
|
||||
struct mmsghdr *mmsg_vector = qi->mmsg_vector;
|
||||
void **skbuff_vector = qi->skbuff_vector;
|
||||
int i;
|
||||
int i, queue_depth;
|
||||
|
||||
if (qi->queue_depth == 0)
|
||||
queue_depth = atomic_read(&qi->queue_depth);
|
||||
|
||||
if (queue_depth == 0)
|
||||
return;
|
||||
for (i = 0; i < qi->queue_depth; i++) {
|
||||
|
||||
/* RX is always emptied 100% during each cycle, so we do not
|
||||
* have to do the tail wraparound math for it.
|
||||
*/
|
||||
|
||||
qi->head = qi->tail = 0;
|
||||
|
||||
for (i = 0; i < queue_depth; i++) {
|
||||
/* it is OK if allocation fails - recvmmsg with NULL data in
|
||||
* iov argument still performs an RX, just drops the packet
|
||||
* This allows us stop faffing around with a "drop buffer"
|
||||
@@ -689,7 +683,7 @@ static void prep_queue_for_rx(struct vector_queue *qi)
|
||||
skbuff_vector++;
|
||||
mmsg_vector++;
|
||||
}
|
||||
qi->queue_depth = 0;
|
||||
atomic_set(&qi->queue_depth, 0);
|
||||
}
|
||||
|
||||
static struct vector_device *find_device(int n)
|
||||
@@ -972,7 +966,7 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
|
||||
budget = qi->max_depth;
|
||||
|
||||
packet_count = uml_vector_recvmmsg(
|
||||
vp->fds->rx_fd, qi->mmsg_vector, qi->max_depth, 0);
|
||||
vp->fds->rx_fd, qi->mmsg_vector, budget, 0);
|
||||
|
||||
if (packet_count < 0)
|
||||
vp->in_error = true;
|
||||
@@ -985,7 +979,7 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
|
||||
* many do we need to prep the next time prep_queue_for_rx() is called.
|
||||
*/
|
||||
|
||||
qi->queue_depth = packet_count;
|
||||
atomic_add(packet_count, &qi->queue_depth);
|
||||
|
||||
for (i = 0; i < packet_count; i++) {
|
||||
skb = (*skbuff_vector);
|
||||
@@ -1172,6 +1166,7 @@ static int vector_poll(struct napi_struct *napi, int budget)
|
||||
|
||||
if ((vp->options & VECTOR_TX) != 0)
|
||||
tx_enqueued = (vector_send(vp->tx_queue) > 0);
|
||||
spin_lock(&vp->rx_queue->head_lock);
|
||||
if ((vp->options & VECTOR_RX) > 0)
|
||||
err = vector_mmsg_rx(vp, budget);
|
||||
else {
|
||||
@@ -1179,12 +1174,13 @@ static int vector_poll(struct napi_struct *napi, int budget)
|
||||
if (err > 0)
|
||||
err = 1;
|
||||
}
|
||||
spin_unlock(&vp->rx_queue->head_lock);
|
||||
if (err > 0)
|
||||
work_done += err;
|
||||
|
||||
if (tx_enqueued || err > 0)
|
||||
napi_schedule(napi);
|
||||
if (work_done < budget)
|
||||
if (work_done <= budget)
|
||||
napi_complete_done(napi, work_done);
|
||||
return work_done;
|
||||
}
|
||||
@@ -1225,7 +1221,7 @@ static int vector_net_open(struct net_device *dev)
|
||||
vp->rx_header_size,
|
||||
MAX_IOV_SIZE
|
||||
);
|
||||
vp->rx_queue->queue_depth = get_depth(vp->parsed);
|
||||
atomic_set(&vp->rx_queue->queue_depth, get_depth(vp->parsed));
|
||||
} else {
|
||||
vp->header_rxbuffer = kmalloc(
|
||||
vp->rx_header_size,
|
||||
@@ -1467,7 +1463,17 @@ static void vector_get_ethtool_stats(struct net_device *dev,
|
||||
{
|
||||
struct vector_private *vp = netdev_priv(dev);
|
||||
|
||||
/* Stats are modified in the dequeue portions of
|
||||
* rx/tx which are protected by the head locks
|
||||
* grabbing these locks here ensures they are up
|
||||
* to date.
|
||||
*/
|
||||
|
||||
spin_lock(&vp->tx_queue->head_lock);
|
||||
spin_lock(&vp->rx_queue->head_lock);
|
||||
memcpy(tmp_stats, &vp->estats, sizeof(struct vector_estats));
|
||||
spin_unlock(&vp->rx_queue->head_lock);
|
||||
spin_unlock(&vp->tx_queue->head_lock);
|
||||
}
|
||||
|
||||
static int vector_get_coalesce(struct net_device *netdev,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include "vector_user.h"
|
||||
|
||||
@@ -44,7 +45,8 @@ struct vector_queue {
|
||||
struct net_device *dev;
|
||||
spinlock_t head_lock;
|
||||
spinlock_t tail_lock;
|
||||
int queue_depth, head, tail, max_depth, max_iov_frags;
|
||||
atomic_t queue_depth;
|
||||
int head, tail, max_depth, max_iov_frags;
|
||||
short options;
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
#define TRANS_FD "fd"
|
||||
#define TRANS_FD_LEN strlen(TRANS_FD)
|
||||
|
||||
#define TRANS_VDE "vde"
|
||||
#define TRANS_VDE_LEN strlen(TRANS_VDE)
|
||||
|
||||
#define VNET_HDR_FAIL "could not enable vnet headers on fd %d"
|
||||
#define TUN_GET_F_FAIL "tapraw: TUNGETFEATURES failed: %s"
|
||||
#define L2TPV3_BIND_FAIL "l2tpv3_open : could not bind socket err=%i"
|
||||
@@ -434,6 +437,84 @@ fd_cleanup:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* enough char to store an int type */
|
||||
#define ENOUGH(type) ((CHAR_BIT * sizeof(type) - 1) / 3 + 2)
|
||||
#define ENOUGH_OCTAL(type) ((CHAR_BIT * sizeof(type) + 2) / 3)
|
||||
/* vde_plug --descr xx --port2 xx --mod2 xx --group2 xx seqpacket://NN vnl (NULL) */
|
||||
#define VDE_MAX_ARGC 12
|
||||
#define VDE_SEQPACKET_HEAD "seqpacket://"
|
||||
#define VDE_SEQPACKET_HEAD_LEN (sizeof(VDE_SEQPACKET_HEAD) - 1)
|
||||
#define VDE_DEFAULT_DESCRIPTION "UML"
|
||||
|
||||
static struct vector_fds *user_init_vde_fds(struct arglist *ifspec)
|
||||
{
|
||||
char seqpacketvnl[VDE_SEQPACKET_HEAD_LEN + ENOUGH(int) + 1];
|
||||
char *argv[VDE_MAX_ARGC] = {"vde_plug"};
|
||||
int argc = 1;
|
||||
int rv;
|
||||
int sv[2];
|
||||
struct vector_fds *result = NULL;
|
||||
|
||||
char *vnl = uml_vector_fetch_arg(ifspec,"vnl");
|
||||
char *descr = uml_vector_fetch_arg(ifspec,"descr");
|
||||
char *port = uml_vector_fetch_arg(ifspec,"port");
|
||||
char *mode = uml_vector_fetch_arg(ifspec,"mode");
|
||||
char *group = uml_vector_fetch_arg(ifspec,"group");
|
||||
if (descr == NULL) descr = VDE_DEFAULT_DESCRIPTION;
|
||||
|
||||
argv[argc++] = "--descr";
|
||||
argv[argc++] = descr;
|
||||
if (port != NULL) {
|
||||
argv[argc++] = "--port2";
|
||||
argv[argc++] = port;
|
||||
}
|
||||
if (mode != NULL) {
|
||||
argv[argc++] = "--mod2";
|
||||
argv[argc++] = mode;
|
||||
}
|
||||
if (group != NULL) {
|
||||
argv[argc++] = "--group2";
|
||||
argv[argc++] = group;
|
||||
}
|
||||
argv[argc++] = seqpacketvnl;
|
||||
argv[argc++] = vnl;
|
||||
argv[argc++] = NULL;
|
||||
|
||||
rv = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv);
|
||||
if (rv < 0) {
|
||||
printk(UM_KERN_ERR "vde: seqpacket socketpair err %d", -errno);
|
||||
return NULL;
|
||||
}
|
||||
rv = os_set_exec_close(sv[0]);
|
||||
if (rv < 0) {
|
||||
printk(UM_KERN_ERR "vde: seqpacket socketpair cloexec err %d", -errno);
|
||||
goto vde_cleanup_sv;
|
||||
}
|
||||
snprintf(seqpacketvnl, sizeof(seqpacketvnl), VDE_SEQPACKET_HEAD "%d", sv[1]);
|
||||
|
||||
run_helper(NULL, NULL, argv);
|
||||
|
||||
close(sv[1]);
|
||||
|
||||
result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
|
||||
if (result == NULL) {
|
||||
printk(UM_KERN_ERR "fd open: allocation failed");
|
||||
goto vde_cleanup;
|
||||
}
|
||||
|
||||
result->rx_fd = sv[0];
|
||||
result->tx_fd = sv[0];
|
||||
result->remote_addr_size = 0;
|
||||
result->remote_addr = NULL;
|
||||
return result;
|
||||
|
||||
vde_cleanup_sv:
|
||||
close(sv[1]);
|
||||
vde_cleanup:
|
||||
close(sv[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct vector_fds *user_init_raw_fds(struct arglist *ifspec)
|
||||
{
|
||||
int rxfd = -1, txfd = -1;
|
||||
@@ -673,6 +754,8 @@ struct vector_fds *uml_vector_user_open(
|
||||
return user_init_unix_fds(parsed, ID_BESS);
|
||||
if (strncmp(transport, TRANS_FD, TRANS_FD_LEN) == 0)
|
||||
return user_init_fd_fds(parsed);
|
||||
if (strncmp(transport, TRANS_VDE, TRANS_VDE_LEN) == 0)
|
||||
return user_init_vde_fds(parsed);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -359,11 +359,4 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte)
|
||||
return pte;
|
||||
}
|
||||
|
||||
/* Clear a kernel PTE and flush it from the TLB */
|
||||
#define kpte_clear_flush(ptep, vaddr) \
|
||||
do { \
|
||||
pte_clear(&init_mm, (vaddr), (ptep)); \
|
||||
__flush_tlb_one((vaddr)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,20 +28,10 @@ struct thread_struct {
|
||||
struct arch_thread arch;
|
||||
jmp_buf switch_buf;
|
||||
struct {
|
||||
int op;
|
||||
union {
|
||||
struct {
|
||||
int pid;
|
||||
} fork, exec;
|
||||
struct {
|
||||
int (*proc)(void *);
|
||||
void *arg;
|
||||
} thread;
|
||||
struct {
|
||||
void (*proc)(void *);
|
||||
void *arg;
|
||||
} cb;
|
||||
} u;
|
||||
struct {
|
||||
int (*proc)(void *);
|
||||
void *arg;
|
||||
} thread;
|
||||
} request;
|
||||
};
|
||||
|
||||
@@ -51,7 +41,7 @@ struct thread_struct {
|
||||
.fault_addr = NULL, \
|
||||
.prev_sched = NULL, \
|
||||
.arch = INIT_ARCH_THREAD, \
|
||||
.request = { 0 } \
|
||||
.request = { } \
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __UM_SYSRQ_H
|
||||
#define __UM_SYSRQ_H
|
||||
|
||||
struct task_struct;
|
||||
extern void show_trace(struct task_struct* task, unsigned long *stack);
|
||||
|
||||
#endif
|
||||
@@ -7,10 +7,7 @@
|
||||
#define __MM_ID_H
|
||||
|
||||
struct mm_id {
|
||||
union {
|
||||
int mm_fd;
|
||||
int pid;
|
||||
} u;
|
||||
int pid;
|
||||
unsigned long stack;
|
||||
int syscall_data_len;
|
||||
};
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
|
||||
extern int userspace_pid[];
|
||||
|
||||
extern int user_thread(unsigned long stack, int flags);
|
||||
extern void new_thread_handler(void);
|
||||
extern void handle_syscall(struct uml_pt_regs *regs);
|
||||
extern long execute_syscall_skas(void *r);
|
||||
extern unsigned long current_stub_stack(void);
|
||||
extern struct mm_id *current_mm_id(void);
|
||||
extern void current_mm_sync(void);
|
||||
|
||||
@@ -35,8 +35,5 @@ void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
|
||||
PT_REGS_IP(regs) = eip;
|
||||
PT_REGS_SP(regs) = esp;
|
||||
clear_thread_flag(TIF_SINGLESTEP);
|
||||
#ifdef SUBARCH_EXECVE1
|
||||
SUBARCH_EXECVE1(regs->regs);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(start_thread);
|
||||
|
||||
@@ -109,8 +109,8 @@ void new_thread_handler(void)
|
||||
schedule_tail(current->thread.prev_sched);
|
||||
current->thread.prev_sched = NULL;
|
||||
|
||||
fn = current->thread.request.u.thread.proc;
|
||||
arg = current->thread.request.u.thread.arg;
|
||||
fn = current->thread.request.thread.proc;
|
||||
arg = current->thread.request.thread.arg;
|
||||
|
||||
/*
|
||||
* callback returns only if the kernel thread execs a process
|
||||
@@ -158,8 +158,8 @@ int copy_thread(struct task_struct * p, const struct kernel_clone_args *args)
|
||||
arch_copy_thread(¤t->thread.arch, &p->thread.arch);
|
||||
} else {
|
||||
get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp);
|
||||
p->thread.request.u.thread.proc = args->fn;
|
||||
p->thread.request.u.thread.arg = args->fn_arg;
|
||||
p->thread.request.thread.proc = args->fn;
|
||||
p->thread.request.thread.arg = args->fn_arg;
|
||||
handler = new_thread_handler;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ static void kill_off_processes(void)
|
||||
t = find_lock_task_mm(p);
|
||||
if (!t)
|
||||
continue;
|
||||
pid = t->mm->context.id.u.pid;
|
||||
pid = t->mm->context.id.pid;
|
||||
task_unlock(t);
|
||||
os_kill_ptraced_process(pid, 1);
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ int init_new_context(struct task_struct *task, struct mm_struct *mm)
|
||||
new_id->stack = stack;
|
||||
|
||||
block_signals_trace();
|
||||
new_id->u.pid = start_userspace(stack);
|
||||
new_id->pid = start_userspace(stack);
|
||||
unblock_signals_trace();
|
||||
|
||||
if (new_id->u.pid < 0) {
|
||||
ret = new_id->u.pid;
|
||||
if (new_id->pid < 0) {
|
||||
ret = new_id->pid;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
@@ -83,12 +83,12 @@ void destroy_context(struct mm_struct *mm)
|
||||
* whole UML suddenly dying. Also, cover negative and
|
||||
* 1 cases, since they shouldn't happen either.
|
||||
*/
|
||||
if (mmu->id.u.pid < 2) {
|
||||
if (mmu->id.pid < 2) {
|
||||
printk(KERN_ERR "corrupt mm_context - pid = %d\n",
|
||||
mmu->id.u.pid);
|
||||
mmu->id.pid);
|
||||
return;
|
||||
}
|
||||
os_kill_ptraced_process(mmu->id.u.pid, 1);
|
||||
os_kill_ptraced_process(mmu->id.pid, 1);
|
||||
|
||||
free_pages(mmu->id.stack, ilog2(STUB_DATA_PAGES));
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ int __init start_uml(void)
|
||||
|
||||
init_new_thread_signals();
|
||||
|
||||
init_task.thread.request.u.thread.proc = start_kernel_proc;
|
||||
init_task.thread.request.u.thread.arg = NULL;
|
||||
init_task.thread.request.thread.proc = start_kernel_proc;
|
||||
init_task.thread.request.thread.arg = NULL;
|
||||
return start_idle_thread(task_stack_page(&init_task),
|
||||
&init_task.thread.switch_buf);
|
||||
}
|
||||
|
||||
@@ -12,23 +12,13 @@
|
||||
#include <sysdep/syscalls.h>
|
||||
#include <linux/time-internal.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/delay.h>
|
||||
|
||||
void handle_syscall(struct uml_pt_regs *r)
|
||||
{
|
||||
struct pt_regs *regs = container_of(r, struct pt_regs, regs);
|
||||
int syscall;
|
||||
|
||||
/*
|
||||
* If we have infinite CPU resources, then make every syscall also a
|
||||
* preemption point, since we don't have any other preemption in this
|
||||
* case, and kernel threads would basically never run until userspace
|
||||
* went to sleep, even if said userspace interacts with the kernel in
|
||||
* various ways.
|
||||
*/
|
||||
if (time_travel_mode == TT_MODE_INFCPU ||
|
||||
time_travel_mode == TT_MODE_EXTERNAL)
|
||||
schedule();
|
||||
|
||||
/* Initialize the syscall number and default return value. */
|
||||
UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
|
||||
PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS);
|
||||
@@ -41,9 +31,25 @@ void handle_syscall(struct uml_pt_regs *r)
|
||||
goto out;
|
||||
|
||||
syscall = UPT_SYSCALL_NR(r);
|
||||
if (syscall >= 0 && syscall < __NR_syscalls)
|
||||
PT_REGS_SET_SYSCALL_RETURN(regs,
|
||||
EXECUTE_SYSCALL(syscall, regs));
|
||||
if (syscall >= 0 && syscall < __NR_syscalls) {
|
||||
unsigned long ret = EXECUTE_SYSCALL(syscall, regs);
|
||||
|
||||
PT_REGS_SET_SYSCALL_RETURN(regs, ret);
|
||||
|
||||
/*
|
||||
* An error value here can be some form of -ERESTARTSYS
|
||||
* and then we'd just loop. Make any error syscalls take
|
||||
* some time, so that it won't just loop if something is
|
||||
* not ready, and hopefully other things will make some
|
||||
* progress.
|
||||
*/
|
||||
if (IS_ERR_VALUE(ret) &&
|
||||
(time_travel_mode == TT_MODE_INFCPU ||
|
||||
time_travel_mode == TT_MODE_EXTERNAL)) {
|
||||
um_udelay(1);
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
syscall_trace_leave(regs);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
|
||||
#include <asm/sysrq.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <os.h>
|
||||
|
||||
|
||||
@@ -839,7 +839,7 @@ static irqreturn_t um_timer(int irq, void *dev)
|
||||
if (get_current()->mm != NULL)
|
||||
{
|
||||
/* userspace - relay signal, results in correct userspace timers */
|
||||
os_alarm_process(get_current()->mm->context.id.u.pid);
|
||||
os_alarm_process(get_current()->mm->context.id.pid);
|
||||
}
|
||||
|
||||
(*timer_clockevent.event_handler)(&timer_clockevent);
|
||||
|
||||
@@ -82,16 +82,12 @@ static inline int update_pte_range(pmd_t *pmd, unsigned long addr,
|
||||
(x ? UM_PROT_EXEC : 0));
|
||||
if (pte_newpage(*pte)) {
|
||||
if (pte_present(*pte)) {
|
||||
if (pte_newpage(*pte)) {
|
||||
__u64 offset;
|
||||
unsigned long phys =
|
||||
pte_val(*pte) & PAGE_MASK;
|
||||
int fd = phys_mapping(phys, &offset);
|
||||
__u64 offset;
|
||||
unsigned long phys = pte_val(*pte) & PAGE_MASK;
|
||||
int fd = phys_mapping(phys, &offset);
|
||||
|
||||
ret = ops->mmap(ops->mm_idp, addr,
|
||||
PAGE_SIZE, prot, fd,
|
||||
offset);
|
||||
}
|
||||
ret = ops->mmap(ops->mm_idp, addr, PAGE_SIZE,
|
||||
prot, fd, offset);
|
||||
} else
|
||||
ret = ops->unmap(ops->mm_idp, addr, PAGE_SIZE);
|
||||
} else if (pte_newprot(*pte))
|
||||
|
||||
@@ -528,7 +528,8 @@ int os_shutdown_socket(int fd, int r, int w)
|
||||
ssize_t os_rcv_fd_msg(int fd, int *fds, unsigned int n_fds,
|
||||
void *data, size_t data_len)
|
||||
{
|
||||
char buf[CMSG_SPACE(sizeof(*fds) * n_fds)];
|
||||
#define MAX_RCV_FDS 2
|
||||
char buf[CMSG_SPACE(sizeof(*fds) * MAX_RCV_FDS)];
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov = {
|
||||
.iov_base = data,
|
||||
@@ -538,10 +539,13 @@ ssize_t os_rcv_fd_msg(int fd, int *fds, unsigned int n_fds,
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = buf,
|
||||
.msg_controllen = sizeof(buf),
|
||||
.msg_controllen = CMSG_SPACE(sizeof(*fds) * n_fds),
|
||||
};
|
||||
int n;
|
||||
|
||||
if (n_fds > MAX_RCV_FDS)
|
||||
return -EINVAL;
|
||||
|
||||
n = recvmsg(fd, &msg, 0);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
|
||||
@@ -78,7 +78,7 @@ static inline long do_syscall_stub(struct mm_id *mm_idp)
|
||||
{
|
||||
struct stub_data *proc_data = (void *)mm_idp->stack;
|
||||
int n, i;
|
||||
int err, pid = mm_idp->u.pid;
|
||||
int err, pid = mm_idp->pid;
|
||||
|
||||
n = ptrace_setregs(pid, syscall_regs);
|
||||
if (n < 0) {
|
||||
|
||||
@@ -588,5 +588,5 @@ void reboot_skas(void)
|
||||
|
||||
void __switch_mm(struct mm_id *mm_idp)
|
||||
{
|
||||
userspace_pid[0] = mm_idp->u.pid;
|
||||
userspace_pid[0] = mm_idp->pid;
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ static __always_inline bool cpuid_function_is_indexed(u32 function)
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
case 0x1f:
|
||||
case 0x24:
|
||||
case 0x8000001d:
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ BUILD_BUG_ON(1)
|
||||
* be __static_call_return0.
|
||||
*/
|
||||
KVM_X86_OP(check_processor_compatibility)
|
||||
KVM_X86_OP(hardware_enable)
|
||||
KVM_X86_OP(hardware_disable)
|
||||
KVM_X86_OP(enable_virtualization_cpu)
|
||||
KVM_X86_OP(disable_virtualization_cpu)
|
||||
KVM_X86_OP(hardware_unsetup)
|
||||
KVM_X86_OP(has_emulated_msr)
|
||||
KVM_X86_OP(vcpu_after_set_cpuid)
|
||||
@@ -125,7 +125,7 @@ KVM_X86_OP_OPTIONAL(mem_enc_unregister_region)
|
||||
KVM_X86_OP_OPTIONAL(vm_copy_enc_context_from)
|
||||
KVM_X86_OP_OPTIONAL(vm_move_enc_context_from)
|
||||
KVM_X86_OP_OPTIONAL(guest_memory_reclaimed)
|
||||
KVM_X86_OP(get_msr_feature)
|
||||
KVM_X86_OP(get_feature_msr)
|
||||
KVM_X86_OP(check_emulate_instruction)
|
||||
KVM_X86_OP(apic_init_signal_blocked)
|
||||
KVM_X86_OP_OPTIONAL(enable_l2_tlb_flush)
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <asm/kvm_page_track.h>
|
||||
#include <asm/kvm_vcpu_regs.h>
|
||||
#include <asm/hyperv-tlfs.h>
|
||||
#include <asm/reboot.h>
|
||||
|
||||
#define __KVM_HAVE_ARCH_VCPU_DEBUGFS
|
||||
|
||||
@@ -211,6 +212,7 @@ enum exit_fastpath_completion {
|
||||
EXIT_FASTPATH_NONE,
|
||||
EXIT_FASTPATH_REENTER_GUEST,
|
||||
EXIT_FASTPATH_EXIT_HANDLED,
|
||||
EXIT_FASTPATH_EXIT_USERSPACE,
|
||||
};
|
||||
typedef enum exit_fastpath_completion fastpath_t;
|
||||
|
||||
@@ -280,10 +282,6 @@ enum x86_intercept_stage;
|
||||
#define PFERR_PRIVATE_ACCESS BIT_ULL(49)
|
||||
#define PFERR_SYNTHETIC_MASK (PFERR_IMPLICIT_ACCESS | PFERR_PRIVATE_ACCESS)
|
||||
|
||||
#define PFERR_NESTED_GUEST_PAGE (PFERR_GUEST_PAGE_MASK | \
|
||||
PFERR_WRITE_MASK | \
|
||||
PFERR_PRESENT_MASK)
|
||||
|
||||
/* apic attention bits */
|
||||
#define KVM_APIC_CHECK_VAPIC 0
|
||||
/*
|
||||
@@ -1629,8 +1627,10 @@ struct kvm_x86_ops {
|
||||
|
||||
int (*check_processor_compatibility)(void);
|
||||
|
||||
int (*hardware_enable)(void);
|
||||
void (*hardware_disable)(void);
|
||||
int (*enable_virtualization_cpu)(void);
|
||||
void (*disable_virtualization_cpu)(void);
|
||||
cpu_emergency_virt_cb *emergency_disable_virtualization_cpu;
|
||||
|
||||
void (*hardware_unsetup)(void);
|
||||
bool (*has_emulated_msr)(struct kvm *kvm, u32 index);
|
||||
void (*vcpu_after_set_cpuid)(struct kvm_vcpu *vcpu);
|
||||
@@ -1727,6 +1727,8 @@ struct kvm_x86_ops {
|
||||
void (*enable_nmi_window)(struct kvm_vcpu *vcpu);
|
||||
void (*enable_irq_window)(struct kvm_vcpu *vcpu);
|
||||
void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr);
|
||||
|
||||
const bool x2apic_icr_is_split;
|
||||
const unsigned long required_apicv_inhibits;
|
||||
bool allow_apicv_in_x2apic_without_x2apic_virtualization;
|
||||
void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);
|
||||
@@ -1806,7 +1808,7 @@ struct kvm_x86_ops {
|
||||
int (*vm_move_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
|
||||
void (*guest_memory_reclaimed)(struct kvm *kvm);
|
||||
|
||||
int (*get_msr_feature)(struct kvm_msr_entry *entry);
|
||||
int (*get_feature_msr)(u32 msr, u64 *data);
|
||||
|
||||
int (*check_emulate_instruction)(struct kvm_vcpu *vcpu, int emul_type,
|
||||
void *insn, int insn_len);
|
||||
@@ -2060,6 +2062,8 @@ void kvm_prepare_emulation_failure_exit(struct kvm_vcpu *vcpu);
|
||||
|
||||
void kvm_enable_efer_bits(u64);
|
||||
bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer);
|
||||
int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data);
|
||||
int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data);
|
||||
int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiated);
|
||||
int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data);
|
||||
int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data);
|
||||
@@ -2136,7 +2140,15 @@ int kvm_get_nr_pending_nmis(struct kvm_vcpu *vcpu);
|
||||
|
||||
void kvm_update_dr7(struct kvm_vcpu *vcpu);
|
||||
|
||||
int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn);
|
||||
bool __kvm_mmu_unprotect_gfn_and_retry(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
|
||||
bool always_retry);
|
||||
|
||||
static inline bool kvm_mmu_unprotect_gfn_and_retry(struct kvm_vcpu *vcpu,
|
||||
gpa_t cr2_or_gpa)
|
||||
{
|
||||
return __kvm_mmu_unprotect_gfn_and_retry(vcpu, cr2_or_gpa, false);
|
||||
}
|
||||
|
||||
void kvm_mmu_free_roots(struct kvm *kvm, struct kvm_mmu *mmu,
|
||||
ulong roots_to_free);
|
||||
void kvm_mmu_free_guest_mode_roots(struct kvm *kvm, struct kvm_mmu *mmu);
|
||||
@@ -2254,6 +2266,7 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v);
|
||||
int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
|
||||
int kvm_cpu_has_extint(struct kvm_vcpu *v);
|
||||
int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu);
|
||||
int kvm_cpu_get_extint(struct kvm_vcpu *v);
|
||||
int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
|
||||
void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event);
|
||||
|
||||
@@ -2345,7 +2358,8 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages);
|
||||
KVM_X86_QUIRK_OUT_7E_INC_RIP | \
|
||||
KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT | \
|
||||
KVM_X86_QUIRK_FIX_HYPERCALL_INSN | \
|
||||
KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS)
|
||||
KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS | \
|
||||
KVM_X86_QUIRK_SLOT_ZAP_ALL)
|
||||
|
||||
/*
|
||||
* KVM previously used a u32 field in kvm_run to indicate the hypercall was
|
||||
|
||||
@@ -36,6 +36,20 @@
|
||||
#define EFER_FFXSR (1<<_EFER_FFXSR)
|
||||
#define EFER_AUTOIBRS (1<<_EFER_AUTOIBRS)
|
||||
|
||||
/*
|
||||
* Architectural memory types that are common to MTRRs, PAT, VMX MSRs, etc.
|
||||
* Most MSRs support/allow only a subset of memory types, but the values
|
||||
* themselves are common across all relevant MSRs.
|
||||
*/
|
||||
#define X86_MEMTYPE_UC 0ull /* Uncacheable, a.k.a. Strong Uncacheable */
|
||||
#define X86_MEMTYPE_WC 1ull /* Write Combining */
|
||||
/* RESERVED 2 */
|
||||
/* RESERVED 3 */
|
||||
#define X86_MEMTYPE_WT 4ull /* Write Through */
|
||||
#define X86_MEMTYPE_WP 5ull /* Write Protected */
|
||||
#define X86_MEMTYPE_WB 6ull /* Write Back */
|
||||
#define X86_MEMTYPE_UC_MINUS 7ull /* Weak Uncacheabled (PAT only) */
|
||||
|
||||
/* FRED MSRs */
|
||||
#define MSR_IA32_FRED_RSP0 0x1cc /* Level 0 stack pointer */
|
||||
#define MSR_IA32_FRED_RSP1 0x1cd /* Level 1 stack pointer */
|
||||
@@ -365,6 +379,12 @@
|
||||
|
||||
#define MSR_IA32_CR_PAT 0x00000277
|
||||
|
||||
#define PAT_VALUE(p0, p1, p2, p3, p4, p5, p6, p7) \
|
||||
((X86_MEMTYPE_ ## p0) | (X86_MEMTYPE_ ## p1 << 8) | \
|
||||
(X86_MEMTYPE_ ## p2 << 16) | (X86_MEMTYPE_ ## p3 << 24) | \
|
||||
(X86_MEMTYPE_ ## p4 << 32) | (X86_MEMTYPE_ ## p5 << 40) | \
|
||||
(X86_MEMTYPE_ ## p6 << 48) | (X86_MEMTYPE_ ## p7 << 56))
|
||||
|
||||
#define MSR_IA32_DEBUGCTLMSR 0x000001d9
|
||||
#define MSR_IA32_LASTBRANCHFROMIP 0x000001db
|
||||
#define MSR_IA32_LASTBRANCHTOIP 0x000001dc
|
||||
@@ -1159,15 +1179,6 @@
|
||||
#define MSR_IA32_VMX_VMFUNC 0x00000491
|
||||
#define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492
|
||||
|
||||
/* VMX_BASIC bits and bitmasks */
|
||||
#define VMX_BASIC_VMCS_SIZE_SHIFT 32
|
||||
#define VMX_BASIC_TRUE_CTLS (1ULL << 55)
|
||||
#define VMX_BASIC_64 0x0001000000000000LLU
|
||||
#define VMX_BASIC_MEM_TYPE_SHIFT 50
|
||||
#define VMX_BASIC_MEM_TYPE_MASK 0x003c000000000000LLU
|
||||
#define VMX_BASIC_MEM_TYPE_WB 6LLU
|
||||
#define VMX_BASIC_INOUT 0x0040000000000000LLU
|
||||
|
||||
/* Resctrl MSRs: */
|
||||
/* - Intel: */
|
||||
#define MSR_IA32_L3_QOS_CFG 0xc81
|
||||
@@ -1185,11 +1196,6 @@
|
||||
#define MSR_IA32_SMBA_BW_BASE 0xc0000280
|
||||
#define MSR_IA32_EVT_CFG_BASE 0xc0000400
|
||||
|
||||
/* MSR_IA32_VMX_MISC bits */
|
||||
#define MSR_IA32_VMX_MISC_INTEL_PT (1ULL << 14)
|
||||
#define MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS (1ULL << 29)
|
||||
#define MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE 0x1F
|
||||
|
||||
/* AMD-V MSRs */
|
||||
#define MSR_VM_CR 0xc0010114
|
||||
#define MSR_VM_IGNNE 0xc0010115
|
||||
|
||||
@@ -25,8 +25,8 @@ void __noreturn machine_real_restart(unsigned int type);
|
||||
#define MRR_BIOS 0
|
||||
#define MRR_APM 1
|
||||
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL) || IS_ENABLED(CONFIG_KVM_AMD)
|
||||
typedef void (cpu_emergency_virt_cb)(void);
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL) || IS_ENABLED(CONFIG_KVM_AMD)
|
||||
void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback);
|
||||
void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback);
|
||||
void cpu_emergency_disable_virtualization(void);
|
||||
|
||||
@@ -516,6 +516,20 @@ struct ghcb {
|
||||
u32 ghcb_usage;
|
||||
} __packed;
|
||||
|
||||
struct vmcb {
|
||||
struct vmcb_control_area control;
|
||||
union {
|
||||
struct vmcb_save_area save;
|
||||
|
||||
/*
|
||||
* For SEV-ES VMs, the save area in the VMCB is used only to
|
||||
* save/load host state. Guest state resides in a separate
|
||||
* page, the aptly named VM Save Area (VMSA), that is encrypted
|
||||
* with the guest's private key.
|
||||
*/
|
||||
struct sev_es_save_area host_sev_es_save;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define EXPECTED_VMCB_SAVE_AREA_SIZE 744
|
||||
#define EXPECTED_GHCB_SAVE_AREA_SIZE 1032
|
||||
@@ -532,6 +546,7 @@ static inline void __unused_size_checks(void)
|
||||
BUILD_BUG_ON(sizeof(struct ghcb_save_area) != EXPECTED_GHCB_SAVE_AREA_SIZE);
|
||||
BUILD_BUG_ON(sizeof(struct sev_es_save_area) != EXPECTED_SEV_ES_SAVE_AREA_SIZE);
|
||||
BUILD_BUG_ON(sizeof(struct vmcb_control_area) != EXPECTED_VMCB_CONTROL_AREA_SIZE);
|
||||
BUILD_BUG_ON(offsetof(struct vmcb, save) != EXPECTED_VMCB_CONTROL_AREA_SIZE);
|
||||
BUILD_BUG_ON(sizeof(struct ghcb) != EXPECTED_GHCB_SIZE);
|
||||
|
||||
/* Check offsets of reserved fields */
|
||||
@@ -568,11 +583,6 @@ static inline void __unused_size_checks(void)
|
||||
BUILD_BUG_RESERVED_OFFSET(ghcb, 0xff0);
|
||||
}
|
||||
|
||||
struct vmcb {
|
||||
struct vmcb_control_area control;
|
||||
struct vmcb_save_area save;
|
||||
} __packed;
|
||||
|
||||
#define SVM_CPUID_FUNC 0x8000000a
|
||||
|
||||
#define SVM_SELECTOR_S_SHIFT 4
|
||||
|
||||
+30
-10
@@ -122,19 +122,17 @@
|
||||
|
||||
#define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff
|
||||
|
||||
#define VMX_MISC_PREEMPTION_TIMER_RATE_MASK 0x0000001f
|
||||
#define VMX_MISC_SAVE_EFER_LMA 0x00000020
|
||||
#define VMX_MISC_ACTIVITY_HLT 0x00000040
|
||||
#define VMX_MISC_ACTIVITY_WAIT_SIPI 0x00000100
|
||||
#define VMX_MISC_ZERO_LEN_INS 0x40000000
|
||||
#define VMX_MISC_MSR_LIST_MULTIPLIER 512
|
||||
|
||||
/* VMFUNC functions */
|
||||
#define VMFUNC_CONTROL_BIT(x) BIT((VMX_FEATURE_##x & 0x1f) - 28)
|
||||
|
||||
#define VMX_VMFUNC_EPTP_SWITCHING VMFUNC_CONTROL_BIT(EPTP_SWITCHING)
|
||||
#define VMFUNC_EPTP_ENTRIES 512
|
||||
|
||||
#define VMX_BASIC_32BIT_PHYS_ADDR_ONLY BIT_ULL(48)
|
||||
#define VMX_BASIC_DUAL_MONITOR_TREATMENT BIT_ULL(49)
|
||||
#define VMX_BASIC_INOUT BIT_ULL(54)
|
||||
#define VMX_BASIC_TRUE_CTLS BIT_ULL(55)
|
||||
|
||||
static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic)
|
||||
{
|
||||
return vmx_basic & GENMASK_ULL(30, 0);
|
||||
@@ -145,9 +143,30 @@ static inline u32 vmx_basic_vmcs_size(u64 vmx_basic)
|
||||
return (vmx_basic & GENMASK_ULL(44, 32)) >> 32;
|
||||
}
|
||||
|
||||
static inline u32 vmx_basic_vmcs_mem_type(u64 vmx_basic)
|
||||
{
|
||||
return (vmx_basic & GENMASK_ULL(53, 50)) >> 50;
|
||||
}
|
||||
|
||||
static inline u64 vmx_basic_encode_vmcs_info(u32 revision, u16 size, u8 memtype)
|
||||
{
|
||||
return revision | ((u64)size << 32) | ((u64)memtype << 50);
|
||||
}
|
||||
|
||||
#define VMX_MISC_SAVE_EFER_LMA BIT_ULL(5)
|
||||
#define VMX_MISC_ACTIVITY_HLT BIT_ULL(6)
|
||||
#define VMX_MISC_ACTIVITY_SHUTDOWN BIT_ULL(7)
|
||||
#define VMX_MISC_ACTIVITY_WAIT_SIPI BIT_ULL(8)
|
||||
#define VMX_MISC_INTEL_PT BIT_ULL(14)
|
||||
#define VMX_MISC_RDMSR_IN_SMM BIT_ULL(15)
|
||||
#define VMX_MISC_VMXOFF_BLOCK_SMI BIT_ULL(28)
|
||||
#define VMX_MISC_VMWRITE_SHADOW_RO_FIELDS BIT_ULL(29)
|
||||
#define VMX_MISC_ZERO_LEN_INS BIT_ULL(30)
|
||||
#define VMX_MISC_MSR_LIST_MULTIPLIER 512
|
||||
|
||||
static inline int vmx_misc_preemption_timer_rate(u64 vmx_misc)
|
||||
{
|
||||
return vmx_misc & VMX_MISC_PREEMPTION_TIMER_RATE_MASK;
|
||||
return vmx_misc & GENMASK_ULL(4, 0);
|
||||
}
|
||||
|
||||
static inline int vmx_misc_cr3_count(u64 vmx_misc)
|
||||
@@ -508,9 +527,10 @@ enum vmcs_field {
|
||||
#define VMX_EPTP_PWL_4 0x18ull
|
||||
#define VMX_EPTP_PWL_5 0x20ull
|
||||
#define VMX_EPTP_AD_ENABLE_BIT (1ull << 6)
|
||||
/* The EPTP memtype is encoded in bits 2:0, i.e. doesn't need to be shifted. */
|
||||
#define VMX_EPTP_MT_MASK 0x7ull
|
||||
#define VMX_EPTP_MT_WB 0x6ull
|
||||
#define VMX_EPTP_MT_UC 0x0ull
|
||||
#define VMX_EPTP_MT_WB X86_MEMTYPE_WB
|
||||
#define VMX_EPTP_MT_UC X86_MEMTYPE_UC
|
||||
#define VMX_EPT_READABLE_MASK 0x1ull
|
||||
#define VMX_EPT_WRITABLE_MASK 0x2ull
|
||||
#define VMX_EPT_EXECUTABLE_MASK 0x4ull
|
||||
|
||||
@@ -439,6 +439,7 @@ struct kvm_sync_regs {
|
||||
#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4)
|
||||
#define KVM_X86_QUIRK_FIX_HYPERCALL_INSN (1 << 5)
|
||||
#define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6)
|
||||
#define KVM_X86_QUIRK_SLOT_ZAP_ALL (1 << 7)
|
||||
|
||||
#define KVM_STATE_NESTED_FORMAT_VMX 0
|
||||
#define KVM_STATE_NESTED_FORMAT_SVM 1
|
||||
|
||||
@@ -55,6 +55,12 @@
|
||||
|
||||
#include "mtrr.h"
|
||||
|
||||
static_assert(X86_MEMTYPE_UC == MTRR_TYPE_UNCACHABLE);
|
||||
static_assert(X86_MEMTYPE_WC == MTRR_TYPE_WRCOMB);
|
||||
static_assert(X86_MEMTYPE_WT == MTRR_TYPE_WRTHROUGH);
|
||||
static_assert(X86_MEMTYPE_WP == MTRR_TYPE_WRPROT);
|
||||
static_assert(X86_MEMTYPE_WB == MTRR_TYPE_WRBACK);
|
||||
|
||||
/* arch_phys_wc_add returns an MTRR register index plus this offset. */
|
||||
#define MTRR_TO_PHYS_WC_OFFSET 1000
|
||||
|
||||
|
||||
+28
-2
@@ -705,7 +705,7 @@ void kvm_set_cpu_caps(void)
|
||||
|
||||
kvm_cpu_cap_init_kvm_defined(CPUID_7_1_EDX,
|
||||
F(AVX_VNNI_INT8) | F(AVX_NE_CONVERT) | F(PREFETCHITI) |
|
||||
F(AMX_COMPLEX)
|
||||
F(AMX_COMPLEX) | F(AVX10)
|
||||
);
|
||||
|
||||
kvm_cpu_cap_init_kvm_defined(CPUID_7_2_EDX,
|
||||
@@ -721,6 +721,10 @@ void kvm_set_cpu_caps(void)
|
||||
SF(SGX1) | SF(SGX2) | SF(SGX_EDECCSSA)
|
||||
);
|
||||
|
||||
kvm_cpu_cap_init_kvm_defined(CPUID_24_0_EBX,
|
||||
F(AVX10_128) | F(AVX10_256) | F(AVX10_512)
|
||||
);
|
||||
|
||||
kvm_cpu_cap_mask(CPUID_8000_0001_ECX,
|
||||
F(LAHF_LM) | F(CMP_LEGACY) | 0 /*SVM*/ | 0 /* ExtApicSpace */ |
|
||||
F(CR8_LEGACY) | F(ABM) | F(SSE4A) | F(MISALIGNSSE) |
|
||||
@@ -949,7 +953,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
|
||||
switch (function) {
|
||||
case 0:
|
||||
/* Limited to the highest leaf implemented in KVM. */
|
||||
entry->eax = min(entry->eax, 0x1fU);
|
||||
entry->eax = min(entry->eax, 0x24U);
|
||||
break;
|
||||
case 1:
|
||||
cpuid_entry_override(entry, CPUID_1_EDX);
|
||||
@@ -1174,6 +1178,28 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x24: {
|
||||
u8 avx10_version;
|
||||
|
||||
if (!kvm_cpu_cap_has(X86_FEATURE_AVX10)) {
|
||||
entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The AVX10 version is encoded in EBX[7:0]. Note, the version
|
||||
* is guaranteed to be >=1 if AVX10 is supported. Note #2, the
|
||||
* version needs to be captured before overriding EBX features!
|
||||
*/
|
||||
avx10_version = min_t(u8, entry->ebx & 0xff, 1);
|
||||
cpuid_entry_override(entry, CPUID_24_0_EBX);
|
||||
entry->ebx |= avx10_version;
|
||||
|
||||
entry->eax = 0;
|
||||
entry->ecx = 0;
|
||||
entry->edx = 0;
|
||||
break;
|
||||
}
|
||||
case KVM_CPUID_SIGNATURE: {
|
||||
const u32 *sigptr = (const u32 *)KVM_SIGNATURE;
|
||||
entry->eax = KVM_CPUID_FEATURES;
|
||||
|
||||
+7
-3
@@ -108,7 +108,7 @@ EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt);
|
||||
* Read pending interrupt(from non-APIC source)
|
||||
* vector and intack.
|
||||
*/
|
||||
static int kvm_cpu_get_extint(struct kvm_vcpu *v)
|
||||
int kvm_cpu_get_extint(struct kvm_vcpu *v)
|
||||
{
|
||||
if (!kvm_cpu_has_extint(v)) {
|
||||
WARN_ON(!lapic_in_kernel(v));
|
||||
@@ -131,6 +131,7 @@ static int kvm_cpu_get_extint(struct kvm_vcpu *v)
|
||||
} else
|
||||
return kvm_pic_read_irq(v->kvm); /* PIC */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_cpu_get_extint);
|
||||
|
||||
/*
|
||||
* Read pending interrupt vector and intack.
|
||||
@@ -141,9 +142,12 @@ int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
|
||||
if (vector != -1)
|
||||
return vector; /* PIC */
|
||||
|
||||
return kvm_get_apic_interrupt(v); /* APIC */
|
||||
vector = kvm_apic_has_interrupt(v); /* APIC */
|
||||
if (vector != -1)
|
||||
kvm_apic_ack_interrupt(v, vector);
|
||||
|
||||
return vector;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt);
|
||||
|
||||
void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
||||
+57
-27
@@ -1944,7 +1944,7 @@ static void start_sw_tscdeadline(struct kvm_lapic *apic)
|
||||
u64 ns = 0;
|
||||
ktime_t expire;
|
||||
struct kvm_vcpu *vcpu = apic->vcpu;
|
||||
unsigned long this_tsc_khz = vcpu->arch.virtual_tsc_khz;
|
||||
u32 this_tsc_khz = vcpu->arch.virtual_tsc_khz;
|
||||
unsigned long flags;
|
||||
ktime_t now;
|
||||
|
||||
@@ -2453,6 +2453,43 @@ void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi);
|
||||
|
||||
#define X2APIC_ICR_RESERVED_BITS (GENMASK_ULL(31, 20) | GENMASK_ULL(17, 16) | BIT(13))
|
||||
|
||||
int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
|
||||
{
|
||||
if (data & X2APIC_ICR_RESERVED_BITS)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* The BUSY bit is reserved on both Intel and AMD in x2APIC mode, but
|
||||
* only AMD requires it to be zero, Intel essentially just ignores the
|
||||
* bit. And if IPI virtualization (Intel) or x2AVIC (AMD) is enabled,
|
||||
* the CPU performs the reserved bits checks, i.e. the underlying CPU
|
||||
* behavior will "win". Arbitrarily clear the BUSY bit, as there is no
|
||||
* sane way to provide consistent behavior with respect to hardware.
|
||||
*/
|
||||
data &= ~APIC_ICR_BUSY;
|
||||
|
||||
kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
|
||||
if (kvm_x86_ops.x2apic_icr_is_split) {
|
||||
kvm_lapic_set_reg(apic, APIC_ICR, data);
|
||||
kvm_lapic_set_reg(apic, APIC_ICR2, data >> 32);
|
||||
} else {
|
||||
kvm_lapic_set_reg64(apic, APIC_ICR, data);
|
||||
}
|
||||
trace_kvm_apic_write(APIC_ICR, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 kvm_x2apic_icr_read(struct kvm_lapic *apic)
|
||||
{
|
||||
if (kvm_x86_ops.x2apic_icr_is_split)
|
||||
return (u64)kvm_lapic_get_reg(apic, APIC_ICR) |
|
||||
(u64)kvm_lapic_get_reg(apic, APIC_ICR2) << 32;
|
||||
|
||||
return kvm_lapic_get_reg64(apic, APIC_ICR);
|
||||
}
|
||||
|
||||
/* emulate APIC access in a trap manner */
|
||||
void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
|
||||
{
|
||||
@@ -2470,7 +2507,7 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
|
||||
* maybe-unecessary write, and both are in the noise anyways.
|
||||
*/
|
||||
if (apic_x2apic_mode(apic) && offset == APIC_ICR)
|
||||
kvm_x2apic_icr_write(apic, kvm_lapic_get_reg64(apic, APIC_ICR));
|
||||
WARN_ON_ONCE(kvm_x2apic_icr_write(apic, kvm_x2apic_icr_read(apic)));
|
||||
else
|
||||
kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset));
|
||||
}
|
||||
@@ -2922,14 +2959,13 @@ void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
|
||||
void kvm_apic_ack_interrupt(struct kvm_vcpu *vcpu, int vector)
|
||||
{
|
||||
int vector = kvm_apic_has_interrupt(vcpu);
|
||||
struct kvm_lapic *apic = vcpu->arch.apic;
|
||||
u32 ppr;
|
||||
|
||||
if (vector == -1)
|
||||
return -1;
|
||||
if (WARN_ON_ONCE(vector < 0 || !apic))
|
||||
return;
|
||||
|
||||
/*
|
||||
* We get here even with APIC virtualization enabled, if doing
|
||||
@@ -2957,8 +2993,8 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
|
||||
__apic_update_ppr(apic, &ppr);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_apic_ack_interrupt);
|
||||
|
||||
static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
|
||||
struct kvm_lapic_state *s, bool set)
|
||||
@@ -2990,18 +3026,22 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
|
||||
|
||||
/*
|
||||
* In x2APIC mode, the LDR is fixed and based on the id. And
|
||||
* ICR is internally a single 64-bit register, but needs to be
|
||||
* split to ICR+ICR2 in userspace for backwards compatibility.
|
||||
* if the ICR is _not_ split, ICR is internally a single 64-bit
|
||||
* register, but needs to be split to ICR+ICR2 in userspace for
|
||||
* backwards compatibility.
|
||||
*/
|
||||
if (set) {
|
||||
if (set)
|
||||
*ldr = kvm_apic_calc_x2apic_ldr(x2apic_id);
|
||||
|
||||
icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) |
|
||||
(u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32;
|
||||
__kvm_lapic_set_reg64(s->regs, APIC_ICR, icr);
|
||||
} else {
|
||||
icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
|
||||
__kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
|
||||
if (!kvm_x86_ops.x2apic_icr_is_split) {
|
||||
if (set) {
|
||||
icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) |
|
||||
(u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32;
|
||||
__kvm_lapic_set_reg64(s->regs, APIC_ICR, icr);
|
||||
} else {
|
||||
icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
|
||||
__kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3194,22 +3234,12 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
|
||||
{
|
||||
data &= ~APIC_ICR_BUSY;
|
||||
|
||||
kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
|
||||
kvm_lapic_set_reg64(apic, APIC_ICR, data);
|
||||
trace_kvm_apic_write(APIC_ICR, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_lapic_msr_read(struct kvm_lapic *apic, u32 reg, u64 *data)
|
||||
{
|
||||
u32 low;
|
||||
|
||||
if (reg == APIC_ICR) {
|
||||
*data = kvm_lapic_get_reg64(apic, APIC_ICR);
|
||||
*data = kvm_x2apic_icr_read(apic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,15 +88,14 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu);
|
||||
void kvm_free_lapic(struct kvm_vcpu *vcpu);
|
||||
|
||||
int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu);
|
||||
void kvm_apic_ack_interrupt(struct kvm_vcpu *vcpu, int vector);
|
||||
int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu);
|
||||
int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu);
|
||||
int kvm_apic_accept_events(struct kvm_vcpu *vcpu);
|
||||
void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event);
|
||||
u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu);
|
||||
void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
|
||||
void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu);
|
||||
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
|
||||
u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
|
||||
void kvm_recalculate_apic_map(struct kvm *kvm);
|
||||
void kvm_apic_set_version(struct kvm_vcpu *vcpu);
|
||||
void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu);
|
||||
|
||||
@@ -223,8 +223,6 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
|
||||
|
||||
bool kvm_mmu_may_ignore_guest_pat(void);
|
||||
|
||||
int kvm_arch_write_log_dirty(struct kvm_vcpu *vcpu);
|
||||
|
||||
int kvm_mmu_post_init_vm(struct kvm *kvm);
|
||||
void kvm_mmu_pre_destroy_vm(struct kvm *kvm);
|
||||
|
||||
|
||||
+320
-238
@@ -614,32 +614,6 @@ static u64 mmu_spte_get_lockless(u64 *sptep)
|
||||
return __get_spte_lockless(sptep);
|
||||
}
|
||||
|
||||
/* Returns the Accessed status of the PTE and resets it at the same time. */
|
||||
static bool mmu_spte_age(u64 *sptep)
|
||||
{
|
||||
u64 spte = mmu_spte_get_lockless(sptep);
|
||||
|
||||
if (!is_accessed_spte(spte))
|
||||
return false;
|
||||
|
||||
if (spte_ad_enabled(spte)) {
|
||||
clear_bit((ffs(shadow_accessed_mask) - 1),
|
||||
(unsigned long *)sptep);
|
||||
} else {
|
||||
/*
|
||||
* Capture the dirty status of the page, so that it doesn't get
|
||||
* lost when the SPTE is marked for access tracking.
|
||||
*/
|
||||
if (is_writable_pte(spte))
|
||||
kvm_set_pfn_dirty(spte_to_pfn(spte));
|
||||
|
||||
spte = mark_spte_for_access_track(spte);
|
||||
mmu_spte_update_no_track(sptep, spte);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool is_tdp_mmu_active(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return tdp_mmu_enabled && vcpu->arch.mmu->root_role.direct;
|
||||
@@ -938,6 +912,7 @@ static struct kvm_memory_slot *gfn_to_memslot_dirty_bitmap(struct kvm_vcpu *vcpu
|
||||
* in this rmap chain. Otherwise, (rmap_head->val & ~1) points to a struct
|
||||
* pte_list_desc containing more mappings.
|
||||
*/
|
||||
#define KVM_RMAP_MANY BIT(0)
|
||||
|
||||
/*
|
||||
* Returns the number of pointers in the rmap chain, not counting the new one.
|
||||
@@ -950,16 +925,16 @@ static int pte_list_add(struct kvm_mmu_memory_cache *cache, u64 *spte,
|
||||
|
||||
if (!rmap_head->val) {
|
||||
rmap_head->val = (unsigned long)spte;
|
||||
} else if (!(rmap_head->val & 1)) {
|
||||
} else if (!(rmap_head->val & KVM_RMAP_MANY)) {
|
||||
desc = kvm_mmu_memory_cache_alloc(cache);
|
||||
desc->sptes[0] = (u64 *)rmap_head->val;
|
||||
desc->sptes[1] = spte;
|
||||
desc->spte_count = 2;
|
||||
desc->tail_count = 0;
|
||||
rmap_head->val = (unsigned long)desc | 1;
|
||||
rmap_head->val = (unsigned long)desc | KVM_RMAP_MANY;
|
||||
++count;
|
||||
} else {
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~KVM_RMAP_MANY);
|
||||
count = desc->tail_count + desc->spte_count;
|
||||
|
||||
/*
|
||||
@@ -968,10 +943,10 @@ static int pte_list_add(struct kvm_mmu_memory_cache *cache, u64 *spte,
|
||||
*/
|
||||
if (desc->spte_count == PTE_LIST_EXT) {
|
||||
desc = kvm_mmu_memory_cache_alloc(cache);
|
||||
desc->more = (struct pte_list_desc *)(rmap_head->val & ~1ul);
|
||||
desc->more = (struct pte_list_desc *)(rmap_head->val & ~KVM_RMAP_MANY);
|
||||
desc->spte_count = 0;
|
||||
desc->tail_count = count;
|
||||
rmap_head->val = (unsigned long)desc | 1;
|
||||
rmap_head->val = (unsigned long)desc | KVM_RMAP_MANY;
|
||||
}
|
||||
desc->sptes[desc->spte_count++] = spte;
|
||||
}
|
||||
@@ -982,7 +957,7 @@ static void pte_list_desc_remove_entry(struct kvm *kvm,
|
||||
struct kvm_rmap_head *rmap_head,
|
||||
struct pte_list_desc *desc, int i)
|
||||
{
|
||||
struct pte_list_desc *head_desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
|
||||
struct pte_list_desc *head_desc = (struct pte_list_desc *)(rmap_head->val & ~KVM_RMAP_MANY);
|
||||
int j = head_desc->spte_count - 1;
|
||||
|
||||
/*
|
||||
@@ -1011,7 +986,7 @@ static void pte_list_desc_remove_entry(struct kvm *kvm,
|
||||
if (!head_desc->more)
|
||||
rmap_head->val = 0;
|
||||
else
|
||||
rmap_head->val = (unsigned long)head_desc->more | 1;
|
||||
rmap_head->val = (unsigned long)head_desc->more | KVM_RMAP_MANY;
|
||||
mmu_free_pte_list_desc(head_desc);
|
||||
}
|
||||
|
||||
@@ -1024,13 +999,13 @@ static void pte_list_remove(struct kvm *kvm, u64 *spte,
|
||||
if (KVM_BUG_ON_DATA_CORRUPTION(!rmap_head->val, kvm))
|
||||
return;
|
||||
|
||||
if (!(rmap_head->val & 1)) {
|
||||
if (!(rmap_head->val & KVM_RMAP_MANY)) {
|
||||
if (KVM_BUG_ON_DATA_CORRUPTION((u64 *)rmap_head->val != spte, kvm))
|
||||
return;
|
||||
|
||||
rmap_head->val = 0;
|
||||
} else {
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~KVM_RMAP_MANY);
|
||||
while (desc) {
|
||||
for (i = 0; i < desc->spte_count; ++i) {
|
||||
if (desc->sptes[i] == spte) {
|
||||
@@ -1063,12 +1038,12 @@ static bool kvm_zap_all_rmap_sptes(struct kvm *kvm,
|
||||
if (!rmap_head->val)
|
||||
return false;
|
||||
|
||||
if (!(rmap_head->val & 1)) {
|
||||
if (!(rmap_head->val & KVM_RMAP_MANY)) {
|
||||
mmu_spte_clear_track_bits(kvm, (u64 *)rmap_head->val);
|
||||
goto out;
|
||||
}
|
||||
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~KVM_RMAP_MANY);
|
||||
|
||||
for (; desc; desc = next) {
|
||||
for (i = 0; i < desc->spte_count; i++)
|
||||
@@ -1088,10 +1063,10 @@ unsigned int pte_list_count(struct kvm_rmap_head *rmap_head)
|
||||
|
||||
if (!rmap_head->val)
|
||||
return 0;
|
||||
else if (!(rmap_head->val & 1))
|
||||
else if (!(rmap_head->val & KVM_RMAP_MANY))
|
||||
return 1;
|
||||
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
|
||||
desc = (struct pte_list_desc *)(rmap_head->val & ~KVM_RMAP_MANY);
|
||||
return desc->tail_count + desc->spte_count;
|
||||
}
|
||||
|
||||
@@ -1153,13 +1128,13 @@ static u64 *rmap_get_first(struct kvm_rmap_head *rmap_head,
|
||||
if (!rmap_head->val)
|
||||
return NULL;
|
||||
|
||||
if (!(rmap_head->val & 1)) {
|
||||
if (!(rmap_head->val & KVM_RMAP_MANY)) {
|
||||
iter->desc = NULL;
|
||||
sptep = (u64 *)rmap_head->val;
|
||||
goto out;
|
||||
}
|
||||
|
||||
iter->desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
|
||||
iter->desc = (struct pte_list_desc *)(rmap_head->val & ~KVM_RMAP_MANY);
|
||||
iter->pos = 0;
|
||||
sptep = iter->desc->sptes[iter->pos];
|
||||
out:
|
||||
@@ -1307,15 +1282,6 @@ static bool __rmap_clear_dirty(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
|
||||
return flush;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_mmu_write_protect_pt_masked - write protect selected PT level pages
|
||||
* @kvm: kvm instance
|
||||
* @slot: slot to protect
|
||||
* @gfn_offset: start of the BITS_PER_LONG pages we care about
|
||||
* @mask: indicates which pages we should protect
|
||||
*
|
||||
* Used when we do not need to care about huge page mappings.
|
||||
*/
|
||||
static void kvm_mmu_write_protect_pt_masked(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot,
|
||||
gfn_t gfn_offset, unsigned long mask)
|
||||
@@ -1339,16 +1305,6 @@ static void kvm_mmu_write_protect_pt_masked(struct kvm *kvm,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_mmu_clear_dirty_pt_masked - clear MMU D-bit for PT level pages, or write
|
||||
* protect the page if the D-bit isn't supported.
|
||||
* @kvm: kvm instance
|
||||
* @slot: slot to clear D-bit
|
||||
* @gfn_offset: start of the BITS_PER_LONG pages we care about
|
||||
* @mask: indicates which pages we should clear D-bit
|
||||
*
|
||||
* Used for PML to re-log the dirty GPAs after userspace querying dirty_bitmap.
|
||||
*/
|
||||
static void kvm_mmu_clear_dirty_pt_masked(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot,
|
||||
gfn_t gfn_offset, unsigned long mask)
|
||||
@@ -1372,24 +1328,16 @@ static void kvm_mmu_clear_dirty_pt_masked(struct kvm *kvm,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arch_mmu_enable_log_dirty_pt_masked - enable dirty logging for selected
|
||||
* PT level pages.
|
||||
*
|
||||
* It calls kvm_mmu_write_protect_pt_masked to write protect selected pages to
|
||||
* enable dirty logging for them.
|
||||
*
|
||||
* We need to care about huge page mappings: e.g. during dirty logging we may
|
||||
* have such mappings.
|
||||
*/
|
||||
void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot,
|
||||
gfn_t gfn_offset, unsigned long mask)
|
||||
{
|
||||
/*
|
||||
* Huge pages are NOT write protected when we start dirty logging in
|
||||
* initially-all-set mode; must write protect them here so that they
|
||||
* are split to 4K on the first write.
|
||||
* If the slot was assumed to be "initially all dirty", write-protect
|
||||
* huge pages to ensure they are split to 4KiB on the first write (KVM
|
||||
* dirty logs at 4KiB granularity). If eager page splitting is enabled,
|
||||
* immediately try to split huge pages, e.g. so that vCPUs don't get
|
||||
* saddled with the cost of splitting.
|
||||
*
|
||||
* The gfn_offset is guaranteed to be aligned to 64, but the base_gfn
|
||||
* of memslot has no such restriction, so the range can cross two large
|
||||
@@ -1411,7 +1359,16 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
|
||||
PG_LEVEL_2M);
|
||||
}
|
||||
|
||||
/* Now handle 4K PTEs. */
|
||||
/*
|
||||
* (Re)Enable dirty logging for all 4KiB SPTEs that map the GFNs in
|
||||
* mask. If PML is enabled and the GFN doesn't need to be write-
|
||||
* protected for other reasons, e.g. shadow paging, clear the Dirty bit.
|
||||
* Otherwise clear the Writable bit.
|
||||
*
|
||||
* Note that kvm_mmu_clear_dirty_pt_masked() is called whenever PML is
|
||||
* enabled but it chooses between clearing the Dirty bit and Writeable
|
||||
* bit based on the context.
|
||||
*/
|
||||
if (kvm_x86_ops.cpu_dirty_log_size)
|
||||
kvm_mmu_clear_dirty_pt_masked(kvm, slot, gfn_offset, mask);
|
||||
else
|
||||
@@ -1453,18 +1410,12 @@ static bool kvm_vcpu_write_protect_gfn(struct kvm_vcpu *vcpu, u64 gfn)
|
||||
return kvm_mmu_slot_gfn_write_protect(vcpu->kvm, slot, gfn, PG_LEVEL_4K);
|
||||
}
|
||||
|
||||
static bool __kvm_zap_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
|
||||
const struct kvm_memory_slot *slot)
|
||||
static bool kvm_zap_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
|
||||
const struct kvm_memory_slot *slot)
|
||||
{
|
||||
return kvm_zap_all_rmap_sptes(kvm, rmap_head);
|
||||
}
|
||||
|
||||
static bool kvm_zap_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
|
||||
struct kvm_memory_slot *slot, gfn_t gfn, int level)
|
||||
{
|
||||
return __kvm_zap_rmap(kvm, rmap_head, slot);
|
||||
}
|
||||
|
||||
struct slot_rmap_walk_iterator {
|
||||
/* input fields. */
|
||||
const struct kvm_memory_slot *slot;
|
||||
@@ -1513,7 +1464,7 @@ static bool slot_rmap_walk_okay(struct slot_rmap_walk_iterator *iterator)
|
||||
static void slot_rmap_walk_next(struct slot_rmap_walk_iterator *iterator)
|
||||
{
|
||||
while (++iterator->rmap <= iterator->end_rmap) {
|
||||
iterator->gfn += (1UL << KVM_HPAGE_GFN_SHIFT(iterator->level));
|
||||
iterator->gfn += KVM_PAGES_PER_HPAGE(iterator->level);
|
||||
|
||||
if (iterator->rmap->val)
|
||||
return;
|
||||
@@ -1534,23 +1485,71 @@ static void slot_rmap_walk_next(struct slot_rmap_walk_iterator *iterator)
|
||||
slot_rmap_walk_okay(_iter_); \
|
||||
slot_rmap_walk_next(_iter_))
|
||||
|
||||
typedef bool (*rmap_handler_t)(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
|
||||
struct kvm_memory_slot *slot, gfn_t gfn,
|
||||
int level);
|
||||
/* The return value indicates if tlb flush on all vcpus is needed. */
|
||||
typedef bool (*slot_rmaps_handler) (struct kvm *kvm,
|
||||
struct kvm_rmap_head *rmap_head,
|
||||
const struct kvm_memory_slot *slot);
|
||||
|
||||
static __always_inline bool kvm_handle_gfn_range(struct kvm *kvm,
|
||||
struct kvm_gfn_range *range,
|
||||
rmap_handler_t handler)
|
||||
static __always_inline bool __walk_slot_rmaps(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
slot_rmaps_handler fn,
|
||||
int start_level, int end_level,
|
||||
gfn_t start_gfn, gfn_t end_gfn,
|
||||
bool can_yield, bool flush_on_yield,
|
||||
bool flush)
|
||||
{
|
||||
struct slot_rmap_walk_iterator iterator;
|
||||
bool ret = false;
|
||||
|
||||
for_each_slot_rmap_range(range->slot, PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL,
|
||||
range->start, range->end - 1, &iterator)
|
||||
ret |= handler(kvm, iterator.rmap, range->slot, iterator.gfn,
|
||||
iterator.level);
|
||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||
|
||||
return ret;
|
||||
for_each_slot_rmap_range(slot, start_level, end_level, start_gfn,
|
||||
end_gfn, &iterator) {
|
||||
if (iterator.rmap)
|
||||
flush |= fn(kvm, iterator.rmap, slot);
|
||||
|
||||
if (!can_yield)
|
||||
continue;
|
||||
|
||||
if (need_resched() || rwlock_needbreak(&kvm->mmu_lock)) {
|
||||
if (flush && flush_on_yield) {
|
||||
kvm_flush_remote_tlbs_range(kvm, start_gfn,
|
||||
iterator.gfn - start_gfn + 1);
|
||||
flush = false;
|
||||
}
|
||||
cond_resched_rwlock_write(&kvm->mmu_lock);
|
||||
}
|
||||
}
|
||||
|
||||
return flush;
|
||||
}
|
||||
|
||||
static __always_inline bool walk_slot_rmaps(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
slot_rmaps_handler fn,
|
||||
int start_level, int end_level,
|
||||
bool flush_on_yield)
|
||||
{
|
||||
return __walk_slot_rmaps(kvm, slot, fn, start_level, end_level,
|
||||
slot->base_gfn, slot->base_gfn + slot->npages - 1,
|
||||
true, flush_on_yield, false);
|
||||
}
|
||||
|
||||
static __always_inline bool walk_slot_rmaps_4k(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
slot_rmaps_handler fn,
|
||||
bool flush_on_yield)
|
||||
{
|
||||
return walk_slot_rmaps(kvm, slot, fn, PG_LEVEL_4K, PG_LEVEL_4K, flush_on_yield);
|
||||
}
|
||||
|
||||
static bool __kvm_rmap_zap_gfn_range(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
gfn_t start, gfn_t end, bool can_yield,
|
||||
bool flush)
|
||||
{
|
||||
return __walk_slot_rmaps(kvm, slot, kvm_zap_rmap,
|
||||
PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL,
|
||||
start, end - 1, can_yield, true, flush);
|
||||
}
|
||||
|
||||
bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
@@ -1558,7 +1557,9 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
bool flush = false;
|
||||
|
||||
if (kvm_memslots_have_rmaps(kvm))
|
||||
flush = kvm_handle_gfn_range(kvm, range, kvm_zap_rmap);
|
||||
flush = __kvm_rmap_zap_gfn_range(kvm, range->slot,
|
||||
range->start, range->end,
|
||||
range->may_block, flush);
|
||||
|
||||
if (tdp_mmu_enabled)
|
||||
flush = kvm_tdp_mmu_unmap_gfn_range(kvm, range, flush);
|
||||
@@ -1570,31 +1571,6 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
return flush;
|
||||
}
|
||||
|
||||
static bool kvm_age_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
|
||||
struct kvm_memory_slot *slot, gfn_t gfn, int level)
|
||||
{
|
||||
u64 *sptep;
|
||||
struct rmap_iterator iter;
|
||||
int young = 0;
|
||||
|
||||
for_each_rmap_spte(rmap_head, &iter, sptep)
|
||||
young |= mmu_spte_age(sptep);
|
||||
|
||||
return young;
|
||||
}
|
||||
|
||||
static bool kvm_test_age_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
|
||||
struct kvm_memory_slot *slot, gfn_t gfn, int level)
|
||||
{
|
||||
u64 *sptep;
|
||||
struct rmap_iterator iter;
|
||||
|
||||
for_each_rmap_spte(rmap_head, &iter, sptep)
|
||||
if (is_accessed_spte(*sptep))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#define RMAP_RECYCLE_THRESHOLD 1000
|
||||
|
||||
static void __rmap_add(struct kvm *kvm,
|
||||
@@ -1629,12 +1605,52 @@ static void rmap_add(struct kvm_vcpu *vcpu, const struct kvm_memory_slot *slot,
|
||||
__rmap_add(vcpu->kvm, cache, slot, spte, gfn, access);
|
||||
}
|
||||
|
||||
static bool kvm_rmap_age_gfn_range(struct kvm *kvm,
|
||||
struct kvm_gfn_range *range, bool test_only)
|
||||
{
|
||||
struct slot_rmap_walk_iterator iterator;
|
||||
struct rmap_iterator iter;
|
||||
bool young = false;
|
||||
u64 *sptep;
|
||||
|
||||
for_each_slot_rmap_range(range->slot, PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL,
|
||||
range->start, range->end - 1, &iterator) {
|
||||
for_each_rmap_spte(iterator.rmap, &iter, sptep) {
|
||||
u64 spte = *sptep;
|
||||
|
||||
if (!is_accessed_spte(spte))
|
||||
continue;
|
||||
|
||||
if (test_only)
|
||||
return true;
|
||||
|
||||
if (spte_ad_enabled(spte)) {
|
||||
clear_bit((ffs(shadow_accessed_mask) - 1),
|
||||
(unsigned long *)sptep);
|
||||
} else {
|
||||
/*
|
||||
* Capture the dirty status of the page, so that
|
||||
* it doesn't get lost when the SPTE is marked
|
||||
* for access tracking.
|
||||
*/
|
||||
if (is_writable_pte(spte))
|
||||
kvm_set_pfn_dirty(spte_to_pfn(spte));
|
||||
|
||||
spte = mark_spte_for_access_track(spte);
|
||||
mmu_spte_update_no_track(sptep, spte);
|
||||
}
|
||||
young = true;
|
||||
}
|
||||
}
|
||||
return young;
|
||||
}
|
||||
|
||||
bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
{
|
||||
bool young = false;
|
||||
|
||||
if (kvm_memslots_have_rmaps(kvm))
|
||||
young = kvm_handle_gfn_range(kvm, range, kvm_age_rmap);
|
||||
young = kvm_rmap_age_gfn_range(kvm, range, false);
|
||||
|
||||
if (tdp_mmu_enabled)
|
||||
young |= kvm_tdp_mmu_age_gfn_range(kvm, range);
|
||||
@@ -1647,7 +1663,7 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
bool young = false;
|
||||
|
||||
if (kvm_memslots_have_rmaps(kvm))
|
||||
young = kvm_handle_gfn_range(kvm, range, kvm_test_age_rmap);
|
||||
young = kvm_rmap_age_gfn_range(kvm, range, true);
|
||||
|
||||
if (tdp_mmu_enabled)
|
||||
young |= kvm_tdp_mmu_test_age_gfn(kvm, range);
|
||||
@@ -2713,36 +2729,49 @@ void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned long goal_nr_mmu_pages)
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn)
|
||||
bool __kvm_mmu_unprotect_gfn_and_retry(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
|
||||
bool always_retry)
|
||||
{
|
||||
struct kvm_mmu_page *sp;
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
LIST_HEAD(invalid_list);
|
||||
int r;
|
||||
struct kvm_mmu_page *sp;
|
||||
gpa_t gpa = cr2_or_gpa;
|
||||
bool r = false;
|
||||
|
||||
r = 0;
|
||||
write_lock(&kvm->mmu_lock);
|
||||
for_each_gfn_valid_sp_with_gptes(kvm, sp, gfn) {
|
||||
r = 1;
|
||||
kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list);
|
||||
/*
|
||||
* Bail early if there aren't any write-protected shadow pages to avoid
|
||||
* unnecessarily taking mmu_lock lock, e.g. if the gfn is write-tracked
|
||||
* by a third party. Reading indirect_shadow_pages without holding
|
||||
* mmu_lock is safe, as this is purely an optimization, i.e. a false
|
||||
* positive is benign, and a false negative will simply result in KVM
|
||||
* skipping the unprotect+retry path, which is also an optimization.
|
||||
*/
|
||||
if (!READ_ONCE(kvm->arch.indirect_shadow_pages))
|
||||
goto out;
|
||||
|
||||
if (!vcpu->arch.mmu->root_role.direct) {
|
||||
gpa = kvm_mmu_gva_to_gpa_write(vcpu, cr2_or_gpa, NULL);
|
||||
if (gpa == INVALID_GPA)
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_lock(&kvm->mmu_lock);
|
||||
for_each_gfn_valid_sp_with_gptes(kvm, sp, gpa_to_gfn(gpa))
|
||||
kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list);
|
||||
|
||||
/*
|
||||
* Snapshot the result before zapping, as zapping will remove all list
|
||||
* entries, i.e. checking the list later would yield a false negative.
|
||||
*/
|
||||
r = !list_empty(&invalid_list);
|
||||
kvm_mmu_commit_zap_page(kvm, &invalid_list);
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva)
|
||||
{
|
||||
gpa_t gpa;
|
||||
int r;
|
||||
|
||||
if (vcpu->arch.mmu->root_role.direct)
|
||||
return 0;
|
||||
|
||||
gpa = kvm_mmu_gva_to_gpa_read(vcpu, gva, NULL);
|
||||
|
||||
r = kvm_mmu_unprotect_page(vcpu->kvm, gpa >> PAGE_SHIFT);
|
||||
|
||||
out:
|
||||
if (r || always_retry) {
|
||||
vcpu->arch.last_retry_eip = kvm_rip_read(vcpu);
|
||||
vcpu->arch.last_retry_addr = cr2_or_gpa;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -2914,10 +2943,8 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot,
|
||||
trace_kvm_mmu_set_spte(level, gfn, sptep);
|
||||
}
|
||||
|
||||
if (wrprot) {
|
||||
if (write_fault)
|
||||
ret = RET_PF_EMULATE;
|
||||
}
|
||||
if (wrprot && write_fault)
|
||||
ret = RET_PF_WRITE_PROTECTED;
|
||||
|
||||
if (flush)
|
||||
kvm_flush_remote_tlbs_gfn(vcpu->kvm, gfn, level);
|
||||
@@ -4549,7 +4576,7 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
|
||||
return RET_PF_RETRY;
|
||||
|
||||
if (page_fault_handle_page_track(vcpu, fault))
|
||||
return RET_PF_EMULATE;
|
||||
return RET_PF_WRITE_PROTECTED;
|
||||
|
||||
r = fast_page_fault(vcpu, fault);
|
||||
if (r != RET_PF_INVALID)
|
||||
@@ -4618,8 +4645,6 @@ int kvm_handle_page_fault(struct kvm_vcpu *vcpu, u64 error_code,
|
||||
if (!flags) {
|
||||
trace_kvm_page_fault(vcpu, fault_address, error_code);
|
||||
|
||||
if (kvm_event_needs_reinjection(vcpu))
|
||||
kvm_mmu_unprotect_page_virt(vcpu, fault_address);
|
||||
r = kvm_mmu_page_fault(vcpu, fault_address, error_code, insn,
|
||||
insn_len);
|
||||
} else if (flags & KVM_PV_REASON_PAGE_NOT_PRESENT) {
|
||||
@@ -4642,7 +4667,7 @@ static int kvm_tdp_mmu_page_fault(struct kvm_vcpu *vcpu,
|
||||
int r;
|
||||
|
||||
if (page_fault_handle_page_track(vcpu, fault))
|
||||
return RET_PF_EMULATE;
|
||||
return RET_PF_WRITE_PROTECTED;
|
||||
|
||||
r = fast_page_fault(vcpu, fault);
|
||||
if (r != RET_PF_INVALID)
|
||||
@@ -4719,6 +4744,7 @@ static int kvm_tdp_map_page(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code,
|
||||
switch (r) {
|
||||
case RET_PF_FIXED:
|
||||
case RET_PF_SPURIOUS:
|
||||
case RET_PF_WRITE_PROTECTED:
|
||||
return 0;
|
||||
|
||||
case RET_PF_EMULATE:
|
||||
@@ -5963,6 +5989,106 @@ void kvm_mmu_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
|
||||
write_unlock(&vcpu->kvm->mmu_lock);
|
||||
}
|
||||
|
||||
static bool is_write_to_guest_page_table(u64 error_code)
|
||||
{
|
||||
const u64 mask = PFERR_GUEST_PAGE_MASK | PFERR_WRITE_MASK | PFERR_PRESENT_MASK;
|
||||
|
||||
return (error_code & mask) == mask;
|
||||
}
|
||||
|
||||
static int kvm_mmu_write_protect_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
|
||||
u64 error_code, int *emulation_type)
|
||||
{
|
||||
bool direct = vcpu->arch.mmu->root_role.direct;
|
||||
|
||||
/*
|
||||
* Do not try to unprotect and retry if the vCPU re-faulted on the same
|
||||
* RIP with the same address that was previously unprotected, as doing
|
||||
* so will likely put the vCPU into an infinite. E.g. if the vCPU uses
|
||||
* a non-page-table modifying instruction on the PDE that points to the
|
||||
* instruction, then unprotecting the gfn will unmap the instruction's
|
||||
* code, i.e. make it impossible for the instruction to ever complete.
|
||||
*/
|
||||
if (vcpu->arch.last_retry_eip == kvm_rip_read(vcpu) &&
|
||||
vcpu->arch.last_retry_addr == cr2_or_gpa)
|
||||
return RET_PF_EMULATE;
|
||||
|
||||
/*
|
||||
* Reset the unprotect+retry values that guard against infinite loops.
|
||||
* The values will be refreshed if KVM explicitly unprotects a gfn and
|
||||
* retries, in all other cases it's safe to retry in the future even if
|
||||
* the next page fault happens on the same RIP+address.
|
||||
*/
|
||||
vcpu->arch.last_retry_eip = 0;
|
||||
vcpu->arch.last_retry_addr = 0;
|
||||
|
||||
/*
|
||||
* It should be impossible to reach this point with an MMIO cache hit,
|
||||
* as RET_PF_WRITE_PROTECTED is returned if and only if there's a valid,
|
||||
* writable memslot, and creating a memslot should invalidate the MMIO
|
||||
* cache by way of changing the memslot generation. WARN and disallow
|
||||
* retry if MMIO is detected, as retrying MMIO emulation is pointless
|
||||
* and could put the vCPU into an infinite loop because the processor
|
||||
* will keep faulting on the non-existent MMIO address.
|
||||
*/
|
||||
if (WARN_ON_ONCE(mmio_info_in_cache(vcpu, cr2_or_gpa, direct)))
|
||||
return RET_PF_EMULATE;
|
||||
|
||||
/*
|
||||
* Before emulating the instruction, check to see if the access was due
|
||||
* to a read-only violation while the CPU was walking non-nested NPT
|
||||
* page tables, i.e. for a direct MMU, for _guest_ page tables in L1.
|
||||
* If L1 is sharing (a subset of) its page tables with L2, e.g. by
|
||||
* having nCR3 share lower level page tables with hCR3, then when KVM
|
||||
* (L0) write-protects the nested NPTs, i.e. npt12 entries, KVM is also
|
||||
* unknowingly write-protecting L1's guest page tables, which KVM isn't
|
||||
* shadowing.
|
||||
*
|
||||
* Because the CPU (by default) walks NPT page tables using a write
|
||||
* access (to ensure the CPU can do A/D updates), page walks in L1 can
|
||||
* trigger write faults for the above case even when L1 isn't modifying
|
||||
* PTEs. As a result, KVM will unnecessarily emulate (or at least, try
|
||||
* to emulate) an excessive number of L1 instructions; because L1's MMU
|
||||
* isn't shadowed by KVM, there is no need to write-protect L1's gPTEs
|
||||
* and thus no need to emulate in order to guarantee forward progress.
|
||||
*
|
||||
* Try to unprotect the gfn, i.e. zap any shadow pages, so that L1 can
|
||||
* proceed without triggering emulation. If one or more shadow pages
|
||||
* was zapped, skip emulation and resume L1 to let it natively execute
|
||||
* the instruction. If no shadow pages were zapped, then the write-
|
||||
* fault is due to something else entirely, i.e. KVM needs to emulate,
|
||||
* as resuming the guest will put it into an infinite loop.
|
||||
*
|
||||
* Note, this code also applies to Intel CPUs, even though it is *very*
|
||||
* unlikely that an L1 will share its page tables (IA32/PAE/paging64
|
||||
* format) with L2's page tables (EPT format).
|
||||
*
|
||||
* For indirect MMUs, i.e. if KVM is shadowing the current MMU, try to
|
||||
* unprotect the gfn and retry if an event is awaiting reinjection. If
|
||||
* KVM emulates multiple instructions before completing event injection,
|
||||
* the event could be delayed beyond what is architecturally allowed,
|
||||
* e.g. KVM could inject an IRQ after the TPR has been raised.
|
||||
*/
|
||||
if (((direct && is_write_to_guest_page_table(error_code)) ||
|
||||
(!direct && kvm_event_needs_reinjection(vcpu))) &&
|
||||
kvm_mmu_unprotect_gfn_and_retry(vcpu, cr2_or_gpa))
|
||||
return RET_PF_RETRY;
|
||||
|
||||
/*
|
||||
* The gfn is write-protected, but if KVM detects its emulating an
|
||||
* instruction that is unlikely to be used to modify page tables, or if
|
||||
* emulation fails, KVM can try to unprotect the gfn and let the CPU
|
||||
* re-execute the instruction that caused the page fault. Do not allow
|
||||
* retrying an instruction from a nested guest as KVM is only explicitly
|
||||
* shadowing L1's page tables, i.e. unprotecting something for L1 isn't
|
||||
* going to magically fix whatever issue caused L2 to fail.
|
||||
*/
|
||||
if (!is_guest_mode(vcpu))
|
||||
*emulation_type |= EMULTYPE_ALLOW_RETRY_PF;
|
||||
|
||||
return RET_PF_EMULATE;
|
||||
}
|
||||
|
||||
int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code,
|
||||
void *insn, int insn_len)
|
||||
{
|
||||
@@ -6008,6 +6134,10 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r == RET_PF_WRITE_PROTECTED)
|
||||
r = kvm_mmu_write_protect_fault(vcpu, cr2_or_gpa, error_code,
|
||||
&emulation_type);
|
||||
|
||||
if (r == RET_PF_FIXED)
|
||||
vcpu->stat.pf_fixed++;
|
||||
else if (r == RET_PF_EMULATE)
|
||||
@@ -6018,32 +6148,6 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err
|
||||
if (r != RET_PF_EMULATE)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Before emulating the instruction, check if the error code
|
||||
* was due to a RO violation while translating the guest page.
|
||||
* This can occur when using nested virtualization with nested
|
||||
* paging in both guests. If true, we simply unprotect the page
|
||||
* and resume the guest.
|
||||
*/
|
||||
if (vcpu->arch.mmu->root_role.direct &&
|
||||
(error_code & PFERR_NESTED_GUEST_PAGE) == PFERR_NESTED_GUEST_PAGE) {
|
||||
kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(cr2_or_gpa));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* vcpu->arch.mmu.page_fault returned RET_PF_EMULATE, but we can still
|
||||
* optimistically try to just unprotect the page and let the processor
|
||||
* re-execute the instruction that caused the page fault. Do not allow
|
||||
* retrying MMIO emulation, as it's not only pointless but could also
|
||||
* cause us to enter an infinite loop because the processor will keep
|
||||
* faulting on the non-existent MMIO address. Retrying an instruction
|
||||
* from a nested guest is also pointless and dangerous as we are only
|
||||
* explicitly shadowing L1's page tables, i.e. unprotecting something
|
||||
* for L1 isn't going to magically fix whatever issue cause L2 to fail.
|
||||
*/
|
||||
if (!mmio_info_in_cache(vcpu, cr2_or_gpa, direct) && !is_guest_mode(vcpu))
|
||||
emulation_type |= EMULTYPE_ALLOW_RETRY_PF;
|
||||
emulate:
|
||||
return x86_emulate_instruction(vcpu, cr2_or_gpa, emulation_type, insn,
|
||||
insn_len);
|
||||
@@ -6202,59 +6306,6 @@ void kvm_configure_mmu(bool enable_tdp, int tdp_forced_root_level,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_configure_mmu);
|
||||
|
||||
/* The return value indicates if tlb flush on all vcpus is needed. */
|
||||
typedef bool (*slot_rmaps_handler) (struct kvm *kvm,
|
||||
struct kvm_rmap_head *rmap_head,
|
||||
const struct kvm_memory_slot *slot);
|
||||
|
||||
static __always_inline bool __walk_slot_rmaps(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
slot_rmaps_handler fn,
|
||||
int start_level, int end_level,
|
||||
gfn_t start_gfn, gfn_t end_gfn,
|
||||
bool flush_on_yield, bool flush)
|
||||
{
|
||||
struct slot_rmap_walk_iterator iterator;
|
||||
|
||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||
|
||||
for_each_slot_rmap_range(slot, start_level, end_level, start_gfn,
|
||||
end_gfn, &iterator) {
|
||||
if (iterator.rmap)
|
||||
flush |= fn(kvm, iterator.rmap, slot);
|
||||
|
||||
if (need_resched() || rwlock_needbreak(&kvm->mmu_lock)) {
|
||||
if (flush && flush_on_yield) {
|
||||
kvm_flush_remote_tlbs_range(kvm, start_gfn,
|
||||
iterator.gfn - start_gfn + 1);
|
||||
flush = false;
|
||||
}
|
||||
cond_resched_rwlock_write(&kvm->mmu_lock);
|
||||
}
|
||||
}
|
||||
|
||||
return flush;
|
||||
}
|
||||
|
||||
static __always_inline bool walk_slot_rmaps(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
slot_rmaps_handler fn,
|
||||
int start_level, int end_level,
|
||||
bool flush_on_yield)
|
||||
{
|
||||
return __walk_slot_rmaps(kvm, slot, fn, start_level, end_level,
|
||||
slot->base_gfn, slot->base_gfn + slot->npages - 1,
|
||||
flush_on_yield, false);
|
||||
}
|
||||
|
||||
static __always_inline bool walk_slot_rmaps_4k(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
slot_rmaps_handler fn,
|
||||
bool flush_on_yield)
|
||||
{
|
||||
return walk_slot_rmaps(kvm, slot, fn, PG_LEVEL_4K, PG_LEVEL_4K, flush_on_yield);
|
||||
}
|
||||
|
||||
static void free_mmu_pages(struct kvm_mmu *mmu)
|
||||
{
|
||||
if (!tdp_enabled && mmu->pae_root)
|
||||
@@ -6528,9 +6579,8 @@ static bool kvm_rmap_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_e
|
||||
if (WARN_ON_ONCE(start >= end))
|
||||
continue;
|
||||
|
||||
flush = __walk_slot_rmaps(kvm, memslot, __kvm_zap_rmap,
|
||||
PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL,
|
||||
start, end - 1, true, flush);
|
||||
flush = __kvm_rmap_zap_gfn_range(kvm, memslot, start,
|
||||
end, true, flush);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6818,7 +6868,7 @@ static void kvm_shadow_mmu_try_split_huge_pages(struct kvm *kvm,
|
||||
*/
|
||||
for (level = KVM_MAX_HUGEPAGE_LEVEL; level > target_level; level--)
|
||||
__walk_slot_rmaps(kvm, slot, shadow_mmu_try_split_huge_pages,
|
||||
level, level, start, end - 1, true, false);
|
||||
level, level, start, end - 1, true, true, false);
|
||||
}
|
||||
|
||||
/* Must be called with the mmu_lock held in write-mode. */
|
||||
@@ -6997,10 +7047,42 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
||||
kvm_mmu_zap_all(kvm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Zapping leaf SPTEs with memslot range when a memslot is moved/deleted.
|
||||
*
|
||||
* Zapping non-leaf SPTEs, a.k.a. not-last SPTEs, isn't required, worst
|
||||
* case scenario we'll have unused shadow pages lying around until they
|
||||
* are recycled due to age or when the VM is destroyed.
|
||||
*/
|
||||
static void kvm_mmu_zap_memslot_leafs(struct kvm *kvm, struct kvm_memory_slot *slot)
|
||||
{
|
||||
struct kvm_gfn_range range = {
|
||||
.slot = slot,
|
||||
.start = slot->base_gfn,
|
||||
.end = slot->base_gfn + slot->npages,
|
||||
.may_block = true,
|
||||
};
|
||||
|
||||
write_lock(&kvm->mmu_lock);
|
||||
if (kvm_unmap_gfn_range(kvm, &range))
|
||||
kvm_flush_remote_tlbs_memslot(kvm, slot);
|
||||
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
static inline bool kvm_memslot_flush_zap_all(struct kvm *kvm)
|
||||
{
|
||||
return kvm->arch.vm_type == KVM_X86_DEFAULT_VM &&
|
||||
kvm_check_has_quirk(kvm, KVM_X86_QUIRK_SLOT_ZAP_ALL);
|
||||
}
|
||||
|
||||
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot)
|
||||
{
|
||||
kvm_mmu_zap_all_fast(kvm);
|
||||
if (kvm_memslot_flush_zap_all(kvm))
|
||||
kvm_mmu_zap_all_fast(kvm);
|
||||
else
|
||||
kvm_mmu_zap_memslot_leafs(kvm, slot);
|
||||
}
|
||||
|
||||
void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
|
||||
|
||||
@@ -258,6 +258,8 @@ int kvm_tdp_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault);
|
||||
* RET_PF_CONTINUE: So far, so good, keep handling the page fault.
|
||||
* RET_PF_RETRY: let CPU fault again on the address.
|
||||
* RET_PF_EMULATE: mmio page fault, emulate the instruction directly.
|
||||
* RET_PF_WRITE_PROTECTED: the gfn is write-protected, either unprotected the
|
||||
* gfn and retry, or emulate the instruction directly.
|
||||
* RET_PF_INVALID: the spte is invalid, let the real page fault path update it.
|
||||
* RET_PF_FIXED: The faulting entry has been fixed.
|
||||
* RET_PF_SPURIOUS: The faulting entry was already fixed, e.g. by another vCPU.
|
||||
@@ -274,6 +276,7 @@ enum {
|
||||
RET_PF_CONTINUE = 0,
|
||||
RET_PF_RETRY,
|
||||
RET_PF_EMULATE,
|
||||
RET_PF_WRITE_PROTECTED,
|
||||
RET_PF_INVALID,
|
||||
RET_PF_FIXED,
|
||||
RET_PF_SPURIOUS,
|
||||
@@ -349,8 +352,6 @@ int kvm_mmu_max_mapping_level(struct kvm *kvm,
|
||||
void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault);
|
||||
void disallowed_hugepage_adjust(struct kvm_page_fault *fault, u64 spte, int cur_level);
|
||||
|
||||
void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc);
|
||||
|
||||
void track_possible_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp);
|
||||
void untrack_possible_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp);
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
TRACE_DEFINE_ENUM(RET_PF_CONTINUE);
|
||||
TRACE_DEFINE_ENUM(RET_PF_RETRY);
|
||||
TRACE_DEFINE_ENUM(RET_PF_EMULATE);
|
||||
TRACE_DEFINE_ENUM(RET_PF_WRITE_PROTECTED);
|
||||
TRACE_DEFINE_ENUM(RET_PF_INVALID);
|
||||
TRACE_DEFINE_ENUM(RET_PF_FIXED);
|
||||
TRACE_DEFINE_ENUM(RET_PF_SPURIOUS);
|
||||
|
||||
@@ -646,10 +646,10 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
|
||||
* really care if it changes underneath us after this point).
|
||||
*/
|
||||
if (FNAME(gpte_changed)(vcpu, gw, top_level))
|
||||
goto out_gpte_changed;
|
||||
return RET_PF_RETRY;
|
||||
|
||||
if (WARN_ON_ONCE(!VALID_PAGE(vcpu->arch.mmu->root.hpa)))
|
||||
goto out_gpte_changed;
|
||||
return RET_PF_RETRY;
|
||||
|
||||
/*
|
||||
* Load a new root and retry the faulting instruction in the extremely
|
||||
@@ -659,7 +659,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
|
||||
*/
|
||||
if (unlikely(kvm_mmu_is_dummy_root(vcpu->arch.mmu->root.hpa))) {
|
||||
kvm_make_request(KVM_REQ_MMU_FREE_OBSOLETE_ROOTS, vcpu);
|
||||
goto out_gpte_changed;
|
||||
return RET_PF_RETRY;
|
||||
}
|
||||
|
||||
for_each_shadow_entry(vcpu, fault->addr, it) {
|
||||
@@ -674,34 +674,38 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
|
||||
sp = kvm_mmu_get_child_sp(vcpu, it.sptep, table_gfn,
|
||||
false, access);
|
||||
|
||||
if (sp != ERR_PTR(-EEXIST)) {
|
||||
/*
|
||||
* We must synchronize the pagetable before linking it
|
||||
* because the guest doesn't need to flush tlb when
|
||||
* the gpte is changed from non-present to present.
|
||||
* Otherwise, the guest may use the wrong mapping.
|
||||
*
|
||||
* For PG_LEVEL_4K, kvm_mmu_get_page() has already
|
||||
* synchronized it transiently via kvm_sync_page().
|
||||
*
|
||||
* For higher level pagetable, we synchronize it via
|
||||
* the slower mmu_sync_children(). If it needs to
|
||||
* break, some progress has been made; return
|
||||
* RET_PF_RETRY and retry on the next #PF.
|
||||
* KVM_REQ_MMU_SYNC is not necessary but it
|
||||
* expedites the process.
|
||||
*/
|
||||
if (sp->unsync_children &&
|
||||
mmu_sync_children(vcpu, sp, false))
|
||||
return RET_PF_RETRY;
|
||||
}
|
||||
/*
|
||||
* Synchronize the new page before linking it, as the CPU (KVM)
|
||||
* is architecturally disallowed from inserting non-present
|
||||
* entries into the TLB, i.e. the guest isn't required to flush
|
||||
* the TLB when changing the gPTE from non-present to present.
|
||||
*
|
||||
* For PG_LEVEL_4K, kvm_mmu_find_shadow_page() has already
|
||||
* synchronized the page via kvm_sync_page().
|
||||
*
|
||||
* For higher level pages, which cannot be unsync themselves
|
||||
* but can have unsync children, synchronize via the slower
|
||||
* mmu_sync_children(). If KVM needs to drop mmu_lock due to
|
||||
* contention or to reschedule, instruct the caller to retry
|
||||
* the #PF (mmu_sync_children() ensures forward progress will
|
||||
* be made).
|
||||
*/
|
||||
if (sp != ERR_PTR(-EEXIST) && sp->unsync_children &&
|
||||
mmu_sync_children(vcpu, sp, false))
|
||||
return RET_PF_RETRY;
|
||||
|
||||
/*
|
||||
* Verify that the gpte in the page we've just write
|
||||
* protected is still there.
|
||||
* Verify that the gpte in the page, which is now either
|
||||
* write-protected or unsync, wasn't modified between the fault
|
||||
* and acquiring mmu_lock. This needs to be done even when
|
||||
* reusing an existing shadow page to ensure the information
|
||||
* gathered by the walker matches the information stored in the
|
||||
* shadow page (which could have been modified by a different
|
||||
* vCPU even if the page was already linked). Holding mmu_lock
|
||||
* prevents the shadow page from changing after this point.
|
||||
*/
|
||||
if (FNAME(gpte_changed)(vcpu, gw, it.level - 1))
|
||||
goto out_gpte_changed;
|
||||
return RET_PF_RETRY;
|
||||
|
||||
if (sp != ERR_PTR(-EEXIST))
|
||||
link_shadow_page(vcpu, it.sptep, sp);
|
||||
@@ -755,9 +759,6 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
|
||||
|
||||
FNAME(pte_prefetch)(vcpu, gw, it.sptep);
|
||||
return ret;
|
||||
|
||||
out_gpte_changed:
|
||||
return RET_PF_RETRY;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -805,7 +806,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
|
||||
|
||||
if (page_fault_handle_page_track(vcpu, fault)) {
|
||||
shadow_page_table_clear_flood(vcpu, fault->addr);
|
||||
return RET_PF_EMULATE;
|
||||
return RET_PF_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
r = mmu_topup_memory_caches(vcpu, true);
|
||||
|
||||
@@ -1046,10 +1046,8 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu,
|
||||
* protected, emulation is needed. If the emulation was skipped,
|
||||
* the vCPU would have the same fault again.
|
||||
*/
|
||||
if (wrprot) {
|
||||
if (fault->write)
|
||||
ret = RET_PF_EMULATE;
|
||||
}
|
||||
if (wrprot && fault->write)
|
||||
ret = RET_PF_WRITE_PROTECTED;
|
||||
|
||||
/* If a MMIO SPTE is installed, the MMIO will need to be emulated. */
|
||||
if (unlikely(is_mmio_spte(vcpu->kvm, new_spte))) {
|
||||
|
||||
@@ -17,6 +17,7 @@ enum kvm_only_cpuid_leafs {
|
||||
CPUID_8000_0007_EDX,
|
||||
CPUID_8000_0022_EAX,
|
||||
CPUID_7_2_EDX,
|
||||
CPUID_24_0_EBX,
|
||||
NR_KVM_CPU_CAPS,
|
||||
|
||||
NKVMCAPINTS = NR_KVM_CPU_CAPS - NCAPINTS,
|
||||
@@ -46,6 +47,7 @@ enum kvm_only_cpuid_leafs {
|
||||
#define X86_FEATURE_AVX_NE_CONVERT KVM_X86_FEATURE(CPUID_7_1_EDX, 5)
|
||||
#define X86_FEATURE_AMX_COMPLEX KVM_X86_FEATURE(CPUID_7_1_EDX, 8)
|
||||
#define X86_FEATURE_PREFETCHITI KVM_X86_FEATURE(CPUID_7_1_EDX, 14)
|
||||
#define X86_FEATURE_AVX10 KVM_X86_FEATURE(CPUID_7_1_EDX, 19)
|
||||
|
||||
/* Intel-defined sub-features, CPUID level 0x00000007:2 (EDX) */
|
||||
#define X86_FEATURE_INTEL_PSFD KVM_X86_FEATURE(CPUID_7_2_EDX, 0)
|
||||
@@ -55,6 +57,11 @@ enum kvm_only_cpuid_leafs {
|
||||
#define KVM_X86_FEATURE_BHI_CTRL KVM_X86_FEATURE(CPUID_7_2_EDX, 4)
|
||||
#define X86_FEATURE_MCDT_NO KVM_X86_FEATURE(CPUID_7_2_EDX, 5)
|
||||
|
||||
/* Intel-defined sub-features, CPUID level 0x00000024:0 (EBX) */
|
||||
#define X86_FEATURE_AVX10_128 KVM_X86_FEATURE(CPUID_24_0_EBX, 16)
|
||||
#define X86_FEATURE_AVX10_256 KVM_X86_FEATURE(CPUID_24_0_EBX, 17)
|
||||
#define X86_FEATURE_AVX10_512 KVM_X86_FEATURE(CPUID_24_0_EBX, 18)
|
||||
|
||||
/* CPUID level 0x80000007 (EDX). */
|
||||
#define KVM_X86_FEATURE_CONSTANT_TSC KVM_X86_FEATURE(CPUID_8000_0007_EDX, 8)
|
||||
|
||||
@@ -90,6 +97,7 @@ static const struct cpuid_reg reverse_cpuid[] = {
|
||||
[CPUID_8000_0021_EAX] = {0x80000021, 0, CPUID_EAX},
|
||||
[CPUID_8000_0022_EAX] = {0x80000022, 0, CPUID_EAX},
|
||||
[CPUID_7_2_EDX] = { 7, 2, CPUID_EDX},
|
||||
[CPUID_24_0_EBX] = { 0x24, 0, CPUID_EBX},
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+19
-5
@@ -624,17 +624,31 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Give leave_smm() a chance to make ISA-specific changes to the vCPU
|
||||
* state (e.g. enter guest mode) before loading state from the SMM
|
||||
* state-save area.
|
||||
* FIXME: When resuming L2 (a.k.a. guest mode), the transition to guest
|
||||
* mode should happen _after_ loading state from SMRAM. However, KVM
|
||||
* piggybacks the nested VM-Enter flows (which is wrong for many other
|
||||
* reasons), and so nSVM/nVMX would clobber state that is loaded from
|
||||
* SMRAM and from the VMCS/VMCB.
|
||||
*/
|
||||
if (kvm_x86_call(leave_smm)(vcpu, &smram))
|
||||
return X86EMUL_UNHANDLEABLE;
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
if (guest_cpuid_has(vcpu, X86_FEATURE_LM))
|
||||
return rsm_load_state_64(ctxt, &smram.smram64);
|
||||
ret = rsm_load_state_64(ctxt, &smram.smram64);
|
||||
else
|
||||
#endif
|
||||
return rsm_load_state_32(ctxt, &smram.smram32);
|
||||
ret = rsm_load_state_32(ctxt, &smram.smram32);
|
||||
|
||||
/*
|
||||
* If RSM fails and triggers shutdown, architecturally the shutdown
|
||||
* occurs *before* the transition to guest mode. But due to KVM's
|
||||
* flawed handling of RSM to L2 (see above), the vCPU may already be
|
||||
* in_guest_mode(). Force the vCPU out of guest mode before delivering
|
||||
* the shutdown, so that L1 enters shutdown instead of seeing a VM-Exit
|
||||
* that architecturally shouldn't be possible.
|
||||
*/
|
||||
if (ret != X86EMUL_CONTINUE && is_guest_mode(vcpu))
|
||||
kvm_leave_nested(vcpu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1693,8 +1693,8 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu,
|
||||
return -EINVAL;
|
||||
|
||||
ret = -ENOMEM;
|
||||
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL_ACCOUNT);
|
||||
save = kzalloc(sizeof(*save), GFP_KERNEL_ACCOUNT);
|
||||
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
|
||||
save = kzalloc(sizeof(*save), GFP_KERNEL);
|
||||
if (!ctl || !save)
|
||||
goto out_free;
|
||||
|
||||
|
||||
+49
-38
@@ -573,7 +573,7 @@ static void __svm_write_tsc_multiplier(u64 multiplier)
|
||||
|
||||
static __always_inline struct sev_es_save_area *sev_es_host_save_area(struct svm_cpu_data *sd)
|
||||
{
|
||||
return page_address(sd->save_area) + 0x400;
|
||||
return &sd->save_area->host_sev_es_save;
|
||||
}
|
||||
|
||||
static inline void kvm_cpu_svm_disable(void)
|
||||
@@ -592,14 +592,14 @@ static inline void kvm_cpu_svm_disable(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void svm_emergency_disable(void)
|
||||
static void svm_emergency_disable_virtualization_cpu(void)
|
||||
{
|
||||
kvm_rebooting = true;
|
||||
|
||||
kvm_cpu_svm_disable();
|
||||
}
|
||||
|
||||
static void svm_hardware_disable(void)
|
||||
static void svm_disable_virtualization_cpu(void)
|
||||
{
|
||||
/* Make sure we clean up behind us */
|
||||
if (tsc_scaling)
|
||||
@@ -610,7 +610,7 @@ static void svm_hardware_disable(void)
|
||||
amd_pmu_disable_virt();
|
||||
}
|
||||
|
||||
static int svm_hardware_enable(void)
|
||||
static int svm_enable_virtualization_cpu(void)
|
||||
{
|
||||
|
||||
struct svm_cpu_data *sd;
|
||||
@@ -696,7 +696,7 @@ static void svm_cpu_uninit(int cpu)
|
||||
return;
|
||||
|
||||
kfree(sd->sev_vmcbs);
|
||||
__free_page(sd->save_area);
|
||||
__free_page(__sme_pa_to_page(sd->save_area_pa));
|
||||
sd->save_area_pa = 0;
|
||||
sd->save_area = NULL;
|
||||
}
|
||||
@@ -704,23 +704,24 @@ static void svm_cpu_uninit(int cpu)
|
||||
static int svm_cpu_init(int cpu)
|
||||
{
|
||||
struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, cpu);
|
||||
struct page *save_area_page;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
memset(sd, 0, sizeof(struct svm_cpu_data));
|
||||
sd->save_area = snp_safe_alloc_page_node(cpu_to_node(cpu), GFP_KERNEL);
|
||||
if (!sd->save_area)
|
||||
save_area_page = snp_safe_alloc_page_node(cpu_to_node(cpu), GFP_KERNEL);
|
||||
if (!save_area_page)
|
||||
return ret;
|
||||
|
||||
ret = sev_cpu_init(sd);
|
||||
if (ret)
|
||||
goto free_save_area;
|
||||
|
||||
sd->save_area_pa = __sme_page_pa(sd->save_area);
|
||||
sd->save_area = page_address(save_area_page);
|
||||
sd->save_area_pa = __sme_page_pa(save_area_page);
|
||||
return 0;
|
||||
|
||||
free_save_area:
|
||||
__free_page(sd->save_area);
|
||||
sd->save_area = NULL;
|
||||
__free_page(save_area_page);
|
||||
return ret;
|
||||
|
||||
}
|
||||
@@ -1124,8 +1125,7 @@ static void svm_hardware_unsetup(void)
|
||||
for_each_possible_cpu(cpu)
|
||||
svm_cpu_uninit(cpu);
|
||||
|
||||
__free_pages(pfn_to_page(iopm_base >> PAGE_SHIFT),
|
||||
get_order(IOPM_SIZE));
|
||||
__free_pages(__sme_pa_to_page(iopm_base), get_order(IOPM_SIZE));
|
||||
iopm_base = 0;
|
||||
}
|
||||
|
||||
@@ -1301,7 +1301,7 @@ static void init_vmcb(struct kvm_vcpu *vcpu)
|
||||
if (!kvm_hlt_in_guest(vcpu->kvm))
|
||||
svm_set_intercept(svm, INTERCEPT_HLT);
|
||||
|
||||
control->iopm_base_pa = __sme_set(iopm_base);
|
||||
control->iopm_base_pa = iopm_base;
|
||||
control->msrpm_base_pa = __sme_set(__pa(svm->msrpm));
|
||||
control->int_ctl = V_INTR_MASKING_MASK;
|
||||
|
||||
@@ -1503,7 +1503,7 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu)
|
||||
|
||||
sev_free_vcpu(vcpu);
|
||||
|
||||
__free_page(pfn_to_page(__sme_clr(svm->vmcb01.pa) >> PAGE_SHIFT));
|
||||
__free_page(__sme_pa_to_page(svm->vmcb01.pa));
|
||||
__free_pages(virt_to_page(svm->msrpm), get_order(MSRPM_SIZE));
|
||||
}
|
||||
|
||||
@@ -1533,7 +1533,7 @@ static void svm_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
|
||||
* TSC_AUX is always virtualized for SEV-ES guests when the feature is
|
||||
* available. The user return MSR support is not required in this case
|
||||
* because TSC_AUX is restored on #VMEXIT from the host save area
|
||||
* (which has been initialized in svm_hardware_enable()).
|
||||
* (which has been initialized in svm_enable_virtualization_cpu()).
|
||||
*/
|
||||
if (likely(tsc_aux_uret_slot >= 0) &&
|
||||
(!boot_cpu_has(X86_FEATURE_V_TSC_AUX) || !sev_es_guest(vcpu->kvm)))
|
||||
@@ -2825,17 +2825,17 @@ static int efer_trap(struct kvm_vcpu *vcpu)
|
||||
return kvm_complete_insn_gp(vcpu, ret);
|
||||
}
|
||||
|
||||
static int svm_get_msr_feature(struct kvm_msr_entry *msr)
|
||||
static int svm_get_feature_msr(u32 msr, u64 *data)
|
||||
{
|
||||
msr->data = 0;
|
||||
*data = 0;
|
||||
|
||||
switch (msr->index) {
|
||||
switch (msr) {
|
||||
case MSR_AMD64_DE_CFG:
|
||||
if (cpu_feature_enabled(X86_FEATURE_LFENCE_RDTSC))
|
||||
msr->data |= MSR_AMD64_DE_CFG_LFENCE_SERIALIZE;
|
||||
*data |= MSR_AMD64_DE_CFG_LFENCE_SERIALIZE;
|
||||
break;
|
||||
default:
|
||||
return KVM_MSR_RET_INVALID;
|
||||
return KVM_MSR_RET_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -3144,7 +3144,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
|
||||
* feature is available. The user return MSR support is not
|
||||
* required in this case because TSC_AUX is restored on #VMEXIT
|
||||
* from the host save area (which has been initialized in
|
||||
* svm_hardware_enable()).
|
||||
* svm_enable_virtualization_cpu()).
|
||||
*/
|
||||
if (boot_cpu_has(X86_FEATURE_V_TSC_AUX) && sev_es_guest(vcpu->kvm))
|
||||
break;
|
||||
@@ -3191,18 +3191,21 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
|
||||
kvm_pr_unimpl_wrmsr(vcpu, ecx, data);
|
||||
break;
|
||||
case MSR_AMD64_DE_CFG: {
|
||||
struct kvm_msr_entry msr_entry;
|
||||
u64 supported_de_cfg;
|
||||
|
||||
msr_entry.index = msr->index;
|
||||
if (svm_get_msr_feature(&msr_entry))
|
||||
if (svm_get_feature_msr(ecx, &supported_de_cfg))
|
||||
return 1;
|
||||
|
||||
/* Check the supported bits */
|
||||
if (data & ~msr_entry.data)
|
||||
if (data & ~supported_de_cfg)
|
||||
return 1;
|
||||
|
||||
/* Don't allow the guest to change a bit, #GP */
|
||||
if (!msr->host_initiated && (data ^ msr_entry.data))
|
||||
/*
|
||||
* Don't let the guest change the host-programmed value. The
|
||||
* MSR is very model specific, i.e. contains multiple bits that
|
||||
* are completely unknown to KVM, and the one bit known to KVM
|
||||
* is simply a reflection of hardware capabilities.
|
||||
*/
|
||||
if (!msr->host_initiated && data != svm->msr_decfg)
|
||||
return 1;
|
||||
|
||||
svm->msr_decfg = data;
|
||||
@@ -4156,12 +4159,21 @@ static int svm_vcpu_pre_run(struct kvm_vcpu *vcpu)
|
||||
|
||||
static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (is_guest_mode(vcpu))
|
||||
return EXIT_FASTPATH_NONE;
|
||||
|
||||
if (to_svm(vcpu)->vmcb->control.exit_code == SVM_EXIT_MSR &&
|
||||
to_svm(vcpu)->vmcb->control.exit_info_1)
|
||||
switch (svm->vmcb->control.exit_code) {
|
||||
case SVM_EXIT_MSR:
|
||||
if (!svm->vmcb->control.exit_info_1)
|
||||
break;
|
||||
return handle_fastpath_set_msr_irqoff(vcpu);
|
||||
case SVM_EXIT_HLT:
|
||||
return handle_fastpath_hlt(vcpu);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return EXIT_FASTPATH_NONE;
|
||||
}
|
||||
@@ -4992,8 +5004,9 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
|
||||
.check_processor_compatibility = svm_check_processor_compat,
|
||||
|
||||
.hardware_unsetup = svm_hardware_unsetup,
|
||||
.hardware_enable = svm_hardware_enable,
|
||||
.hardware_disable = svm_hardware_disable,
|
||||
.enable_virtualization_cpu = svm_enable_virtualization_cpu,
|
||||
.disable_virtualization_cpu = svm_disable_virtualization_cpu,
|
||||
.emergency_disable_virtualization_cpu = svm_emergency_disable_virtualization_cpu,
|
||||
.has_emulated_msr = svm_has_emulated_msr,
|
||||
|
||||
.vcpu_create = svm_vcpu_create,
|
||||
@@ -5011,7 +5024,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
|
||||
.vcpu_unblocking = avic_vcpu_unblocking,
|
||||
|
||||
.update_exception_bitmap = svm_update_exception_bitmap,
|
||||
.get_msr_feature = svm_get_msr_feature,
|
||||
.get_feature_msr = svm_get_feature_msr,
|
||||
.get_msr = svm_get_msr,
|
||||
.set_msr = svm_set_msr,
|
||||
.get_segment_base = svm_get_segment_base,
|
||||
@@ -5062,6 +5075,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
|
||||
.enable_nmi_window = svm_enable_nmi_window,
|
||||
.enable_irq_window = svm_enable_irq_window,
|
||||
.update_cr8_intercept = svm_update_cr8_intercept,
|
||||
|
||||
.x2apic_icr_is_split = true,
|
||||
.set_virtual_apic_mode = avic_refresh_virtual_apic_mode,
|
||||
.refresh_apicv_exec_ctrl = avic_refresh_apicv_exec_ctrl,
|
||||
.apicv_post_state_restore = avic_apicv_post_state_restore,
|
||||
@@ -5266,7 +5281,7 @@ static __init int svm_hardware_setup(void)
|
||||
|
||||
iopm_va = page_address(iopm_pages);
|
||||
memset(iopm_va, 0xff, PAGE_SIZE * (1 << order));
|
||||
iopm_base = page_to_pfn(iopm_pages) << PAGE_SHIFT;
|
||||
iopm_base = __sme_page_pa(iopm_pages);
|
||||
|
||||
init_msrpm_offsets();
|
||||
|
||||
@@ -5425,8 +5440,6 @@ static struct kvm_x86_init_ops svm_init_ops __initdata = {
|
||||
static void __svm_exit(void)
|
||||
{
|
||||
kvm_x86_vendor_exit();
|
||||
|
||||
cpu_emergency_unregister_virt_callback(svm_emergency_disable);
|
||||
}
|
||||
|
||||
static int __init svm_init(void)
|
||||
@@ -5442,8 +5455,6 @@ static int __init svm_init(void)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
cpu_emergency_register_virt_callback(svm_emergency_disable);
|
||||
|
||||
/*
|
||||
* Common KVM initialization _must_ come last, after this, /dev/kvm is
|
||||
* exposed to userspace!
|
||||
|
||||
+16
-2
@@ -25,7 +25,21 @@
|
||||
#include "cpuid.h"
|
||||
#include "kvm_cache_regs.h"
|
||||
|
||||
#define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)
|
||||
/*
|
||||
* Helpers to convert to/from physical addresses for pages whose address is
|
||||
* consumed directly by hardware. Even though it's a physical address, SVM
|
||||
* often restricts the address to the natural width, hence 'unsigned long'
|
||||
* instead of 'hpa_t'.
|
||||
*/
|
||||
static inline unsigned long __sme_page_pa(struct page *page)
|
||||
{
|
||||
return __sme_set(page_to_pfn(page) << PAGE_SHIFT);
|
||||
}
|
||||
|
||||
static inline struct page *__sme_pa_to_page(unsigned long pa)
|
||||
{
|
||||
return pfn_to_page(__sme_clr(pa) >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
#define IOPM_SIZE PAGE_SIZE * 3
|
||||
#define MSRPM_SIZE PAGE_SIZE * 2
|
||||
@@ -321,7 +335,7 @@ struct svm_cpu_data {
|
||||
u32 next_asid;
|
||||
u32 min_asid;
|
||||
|
||||
struct page *save_area;
|
||||
struct vmcb *save_area;
|
||||
unsigned long save_area_pa;
|
||||
|
||||
struct vmcb *current_vmcb;
|
||||
|
||||
@@ -209,10 +209,8 @@ SYM_FUNC_START(__svm_vcpu_run)
|
||||
7: vmload %_ASM_AX
|
||||
8:
|
||||
|
||||
#ifdef CONFIG_MITIGATION_RETPOLINE
|
||||
/* IMPORTANT: Stuff the RSB immediately after VM-Exit, before RET! */
|
||||
FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
|
||||
#endif
|
||||
FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_VMEXIT
|
||||
|
||||
/* Clobbers RAX, RCX, RDX. */
|
||||
RESTORE_HOST_SPEC_CTRL
|
||||
@@ -348,10 +346,8 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run)
|
||||
|
||||
2: cli
|
||||
|
||||
#ifdef CONFIG_MITIGATION_RETPOLINE
|
||||
/* IMPORTANT: Stuff the RSB immediately after VM-Exit, before RET! */
|
||||
FILL_RETURN_BUFFER %rax, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
|
||||
#endif
|
||||
FILL_RETURN_BUFFER %rax, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_VMEXIT
|
||||
|
||||
/* Clobbers RAX, RCX, RDX, consumes RDI (@svm) and RSI (@spec_ctrl_intercepted). */
|
||||
RESTORE_HOST_SPEC_CTRL
|
||||
|
||||
@@ -54,9 +54,7 @@ struct nested_vmx_msrs {
|
||||
};
|
||||
|
||||
struct vmcs_config {
|
||||
int size;
|
||||
u32 basic_cap;
|
||||
u32 revision_id;
|
||||
u64 basic;
|
||||
u32 pin_based_exec_ctrl;
|
||||
u32 cpu_based_exec_ctrl;
|
||||
u32 cpu_based_2nd_exec_ctrl;
|
||||
@@ -76,7 +74,7 @@ extern struct vmx_capability vmx_capability __ro_after_init;
|
||||
|
||||
static inline bool cpu_has_vmx_basic_inout(void)
|
||||
{
|
||||
return (((u64)vmcs_config.basic_cap << 32) & VMX_BASIC_INOUT);
|
||||
return vmcs_config.basic & VMX_BASIC_INOUT;
|
||||
}
|
||||
|
||||
static inline bool cpu_has_virtual_nmis(void)
|
||||
@@ -225,7 +223,7 @@ static inline bool cpu_has_vmx_vmfunc(void)
|
||||
static inline bool cpu_has_vmx_shadow_vmcs(void)
|
||||
{
|
||||
/* check if the cpu supports writing r/o exit information fields */
|
||||
if (!(vmcs_config.misc & MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS))
|
||||
if (!(vmcs_config.misc & VMX_MISC_VMWRITE_SHADOW_RO_FIELDS))
|
||||
return false;
|
||||
|
||||
return vmcs_config.cpu_based_2nd_exec_ctrl &
|
||||
@@ -367,7 +365,7 @@ static inline bool cpu_has_vmx_invvpid_global(void)
|
||||
|
||||
static inline bool cpu_has_vmx_intel_pt(void)
|
||||
{
|
||||
return (vmcs_config.misc & MSR_IA32_VMX_MISC_INTEL_PT) &&
|
||||
return (vmcs_config.misc & VMX_MISC_INTEL_PT) &&
|
||||
(vmcs_config.cpu_based_2nd_exec_ctrl & SECONDARY_EXEC_PT_USE_GPA) &&
|
||||
(vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_RTIT_CTL);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
|
||||
|
||||
.hardware_unsetup = vmx_hardware_unsetup,
|
||||
|
||||
.hardware_enable = vmx_hardware_enable,
|
||||
.hardware_disable = vmx_hardware_disable,
|
||||
.enable_virtualization_cpu = vmx_enable_virtualization_cpu,
|
||||
.disable_virtualization_cpu = vmx_disable_virtualization_cpu,
|
||||
.emergency_disable_virtualization_cpu = vmx_emergency_disable_virtualization_cpu,
|
||||
|
||||
.has_emulated_msr = vmx_has_emulated_msr,
|
||||
|
||||
.vm_size = sizeof(struct kvm_vmx),
|
||||
@@ -41,7 +43,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
|
||||
.vcpu_put = vmx_vcpu_put,
|
||||
|
||||
.update_exception_bitmap = vmx_update_exception_bitmap,
|
||||
.get_msr_feature = vmx_get_msr_feature,
|
||||
.get_feature_msr = vmx_get_feature_msr,
|
||||
.get_msr = vmx_get_msr,
|
||||
.set_msr = vmx_set_msr,
|
||||
.get_segment_base = vmx_get_segment_base,
|
||||
@@ -89,6 +91,8 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
|
||||
.enable_nmi_window = vmx_enable_nmi_window,
|
||||
.enable_irq_window = vmx_enable_irq_window,
|
||||
.update_cr8_intercept = vmx_update_cr8_intercept,
|
||||
|
||||
.x2apic_icr_is_split = false,
|
||||
.set_virtual_apic_mode = vmx_set_virtual_apic_mode,
|
||||
.set_apic_access_page_addr = vmx_set_apic_access_page_addr,
|
||||
.refresh_apicv_exec_ctrl = vmx_refresh_apicv_exec_ctrl,
|
||||
|
||||
+96
-38
@@ -981,7 +981,7 @@ static u32 nested_vmx_load_msr(struct kvm_vcpu *vcpu, u64 gpa, u32 count)
|
||||
__func__, i, e.index, e.reserved);
|
||||
goto fail;
|
||||
}
|
||||
if (kvm_set_msr(vcpu, e.index, e.value)) {
|
||||
if (kvm_set_msr_with_filter(vcpu, e.index, e.value)) {
|
||||
pr_debug_ratelimited(
|
||||
"%s cannot write MSR (%u, 0x%x, 0x%llx)\n",
|
||||
__func__, i, e.index, e.value);
|
||||
@@ -1017,7 +1017,7 @@ static bool nested_vmx_get_vmexit_msr_value(struct kvm_vcpu *vcpu,
|
||||
}
|
||||
}
|
||||
|
||||
if (kvm_get_msr(vcpu, msr_index, data)) {
|
||||
if (kvm_get_msr_with_filter(vcpu, msr_index, data)) {
|
||||
pr_debug_ratelimited("%s cannot read MSR (0x%x)\n", __func__,
|
||||
msr_index);
|
||||
return false;
|
||||
@@ -1112,9 +1112,9 @@ static void prepare_vmx_msr_autostore_list(struct kvm_vcpu *vcpu,
|
||||
/*
|
||||
* Emulated VMEntry does not fail here. Instead a less
|
||||
* accurate value will be returned by
|
||||
* nested_vmx_get_vmexit_msr_value() using kvm_get_msr()
|
||||
* instead of reading the value from the vmcs02 VMExit
|
||||
* MSR-store area.
|
||||
* nested_vmx_get_vmexit_msr_value() by reading KVM's
|
||||
* internal MSR state instead of reading the value from
|
||||
* the vmcs02 VMExit MSR-store area.
|
||||
*/
|
||||
pr_warn_ratelimited(
|
||||
"Not enough msr entries in msr_autostore. Can't add msr %x\n",
|
||||
@@ -1251,21 +1251,32 @@ static bool is_bitwise_subset(u64 superset, u64 subset, u64 mask)
|
||||
|
||||
static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data)
|
||||
{
|
||||
const u64 feature_and_reserved =
|
||||
/* feature (except bit 48; see below) */
|
||||
BIT_ULL(49) | BIT_ULL(54) | BIT_ULL(55) |
|
||||
/* reserved */
|
||||
BIT_ULL(31) | GENMASK_ULL(47, 45) | GENMASK_ULL(63, 56);
|
||||
const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT |
|
||||
VMX_BASIC_INOUT |
|
||||
VMX_BASIC_TRUE_CTLS;
|
||||
|
||||
const u64 reserved_bits = GENMASK_ULL(63, 56) |
|
||||
GENMASK_ULL(47, 45) |
|
||||
BIT_ULL(31);
|
||||
|
||||
u64 vmx_basic = vmcs_config.nested.basic;
|
||||
|
||||
if (!is_bitwise_subset(vmx_basic, data, feature_and_reserved))
|
||||
BUILD_BUG_ON(feature_bits & reserved_bits);
|
||||
|
||||
/*
|
||||
* Except for 32BIT_PHYS_ADDR_ONLY, which is an anti-feature bit (has
|
||||
* inverted polarity), the incoming value must not set feature bits or
|
||||
* reserved bits that aren't allowed/supported by KVM. Fields, i.e.
|
||||
* multi-bit values, are explicitly checked below.
|
||||
*/
|
||||
if (!is_bitwise_subset(vmx_basic, data, feature_bits | reserved_bits))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* KVM does not emulate a version of VMX that constrains physical
|
||||
* addresses of VMX structures (e.g. VMCS) to 32-bits.
|
||||
*/
|
||||
if (data & BIT_ULL(48))
|
||||
if (data & VMX_BASIC_32BIT_PHYS_ADDR_ONLY)
|
||||
return -EINVAL;
|
||||
|
||||
if (vmx_basic_vmcs_revision_id(vmx_basic) !=
|
||||
@@ -1334,16 +1345,29 @@ vmx_restore_control_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data)
|
||||
|
||||
static int vmx_restore_vmx_misc(struct vcpu_vmx *vmx, u64 data)
|
||||
{
|
||||
const u64 feature_and_reserved_bits =
|
||||
/* feature */
|
||||
BIT_ULL(5) | GENMASK_ULL(8, 6) | BIT_ULL(14) | BIT_ULL(15) |
|
||||
BIT_ULL(28) | BIT_ULL(29) | BIT_ULL(30) |
|
||||
/* reserved */
|
||||
GENMASK_ULL(13, 9) | BIT_ULL(31);
|
||||
const u64 feature_bits = VMX_MISC_SAVE_EFER_LMA |
|
||||
VMX_MISC_ACTIVITY_HLT |
|
||||
VMX_MISC_ACTIVITY_SHUTDOWN |
|
||||
VMX_MISC_ACTIVITY_WAIT_SIPI |
|
||||
VMX_MISC_INTEL_PT |
|
||||
VMX_MISC_RDMSR_IN_SMM |
|
||||
VMX_MISC_VMWRITE_SHADOW_RO_FIELDS |
|
||||
VMX_MISC_VMXOFF_BLOCK_SMI |
|
||||
VMX_MISC_ZERO_LEN_INS;
|
||||
|
||||
const u64 reserved_bits = BIT_ULL(31) | GENMASK_ULL(13, 9);
|
||||
|
||||
u64 vmx_misc = vmx_control_msr(vmcs_config.nested.misc_low,
|
||||
vmcs_config.nested.misc_high);
|
||||
|
||||
if (!is_bitwise_subset(vmx_misc, data, feature_and_reserved_bits))
|
||||
BUILD_BUG_ON(feature_bits & reserved_bits);
|
||||
|
||||
/*
|
||||
* The incoming value must not set feature bits or reserved bits that
|
||||
* aren't allowed/supported by KVM. Fields, i.e. multi-bit values, are
|
||||
* explicitly checked below.
|
||||
*/
|
||||
if (!is_bitwise_subset(vmx_misc, data, feature_bits | reserved_bits))
|
||||
return -EINVAL;
|
||||
|
||||
if ((vmx->nested.msrs.pinbased_ctls_high &
|
||||
@@ -2317,10 +2341,12 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0
|
||||
|
||||
/* Posted interrupts setting is only taken from vmcs12. */
|
||||
vmx->nested.pi_pending = false;
|
||||
if (nested_cpu_has_posted_intr(vmcs12))
|
||||
if (nested_cpu_has_posted_intr(vmcs12)) {
|
||||
vmx->nested.posted_intr_nv = vmcs12->posted_intr_nv;
|
||||
else
|
||||
} else {
|
||||
vmx->nested.posted_intr_nv = -1;
|
||||
exec_control &= ~PIN_BASED_POSTED_INTR;
|
||||
}
|
||||
pin_controls_set(vmx, exec_control);
|
||||
|
||||
/*
|
||||
@@ -2470,6 +2496,7 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)
|
||||
|
||||
if (!hv_evmcs || !(hv_evmcs->hv_clean_fields &
|
||||
HV_VMX_ENLIGHTENED_CLEAN_FIELD_GUEST_GRP2)) {
|
||||
|
||||
vmcs_write16(GUEST_ES_SELECTOR, vmcs12->guest_es_selector);
|
||||
vmcs_write16(GUEST_CS_SELECTOR, vmcs12->guest_cs_selector);
|
||||
vmcs_write16(GUEST_SS_SELECTOR, vmcs12->guest_ss_selector);
|
||||
@@ -2507,7 +2534,7 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)
|
||||
vmcs_writel(GUEST_GDTR_BASE, vmcs12->guest_gdtr_base);
|
||||
vmcs_writel(GUEST_IDTR_BASE, vmcs12->guest_idtr_base);
|
||||
|
||||
vmx->segment_cache.bitmask = 0;
|
||||
vmx_segment_cache_clear(vmx);
|
||||
}
|
||||
|
||||
if (!hv_evmcs || !(hv_evmcs->hv_clean_fields &
|
||||
@@ -4284,11 +4311,52 @@ static int vmx_check_nested_events(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
|
||||
if (kvm_cpu_has_interrupt(vcpu) && !vmx_interrupt_blocked(vcpu)) {
|
||||
int irq;
|
||||
|
||||
if (block_nested_events)
|
||||
return -EBUSY;
|
||||
if (!nested_exit_on_intr(vcpu))
|
||||
goto no_vmexit;
|
||||
nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT, 0, 0);
|
||||
|
||||
if (!nested_exit_intr_ack_set(vcpu)) {
|
||||
nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
irq = kvm_cpu_get_extint(vcpu);
|
||||
if (irq != -1) {
|
||||
nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT,
|
||||
INTR_INFO_VALID_MASK | INTR_TYPE_EXT_INTR | irq, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
irq = kvm_apic_has_interrupt(vcpu);
|
||||
if (WARN_ON_ONCE(irq < 0))
|
||||
goto no_vmexit;
|
||||
|
||||
/*
|
||||
* If the IRQ is L2's PI notification vector, process posted
|
||||
* interrupts for L2 instead of injecting VM-Exit, as the
|
||||
* detection/morphing architecturally occurs when the IRQ is
|
||||
* delivered to the CPU. Note, only interrupts that are routed
|
||||
* through the local APIC trigger posted interrupt processing,
|
||||
* and enabling posted interrupts requires ACK-on-exit.
|
||||
*/
|
||||
if (irq == vmx->nested.posted_intr_nv) {
|
||||
vmx->nested.pi_pending = true;
|
||||
kvm_apic_clear_irr(vcpu, irq);
|
||||
goto no_vmexit;
|
||||
}
|
||||
|
||||
nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT,
|
||||
INTR_INFO_VALID_MASK | INTR_TYPE_EXT_INTR | irq, 0);
|
||||
|
||||
/*
|
||||
* ACK the interrupt _after_ emulating VM-Exit, as the IRQ must
|
||||
* be marked as in-service in vmcs01.GUEST_INTERRUPT_STATUS.SVI
|
||||
* if APICv is active.
|
||||
*/
|
||||
kvm_apic_ack_interrupt(vcpu, irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4806,7 +4874,7 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu)
|
||||
goto vmabort;
|
||||
}
|
||||
|
||||
if (kvm_set_msr(vcpu, h.index, h.value)) {
|
||||
if (kvm_set_msr_with_filter(vcpu, h.index, h.value)) {
|
||||
pr_debug_ratelimited(
|
||||
"%s WRMSR failed (%u, 0x%x, 0x%llx)\n",
|
||||
__func__, j, h.index, h.value);
|
||||
@@ -4969,14 +5037,6 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
|
||||
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
||||
|
||||
if (likely(!vmx->fail)) {
|
||||
if ((u16)vm_exit_reason == EXIT_REASON_EXTERNAL_INTERRUPT &&
|
||||
nested_exit_intr_ack_set(vcpu)) {
|
||||
int irq = kvm_cpu_get_interrupt(vcpu);
|
||||
WARN_ON(irq < 0);
|
||||
vmcs12->vm_exit_intr_info = irq |
|
||||
INTR_INFO_VALID_MASK | INTR_TYPE_EXT_INTR;
|
||||
}
|
||||
|
||||
if (vm_exit_reason != -1)
|
||||
trace_kvm_nested_vmexit_inject(vmcs12->vm_exit_reason,
|
||||
vmcs12->exit_qualification,
|
||||
@@ -7051,7 +7111,7 @@ static void nested_vmx_setup_misc_data(struct vmcs_config *vmcs_conf,
|
||||
{
|
||||
msrs->misc_low = (u32)vmcs_conf->misc & VMX_MISC_SAVE_EFER_LMA;
|
||||
msrs->misc_low |=
|
||||
MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS |
|
||||
VMX_MISC_VMWRITE_SHADOW_RO_FIELDS |
|
||||
VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE |
|
||||
VMX_MISC_ACTIVITY_HLT |
|
||||
VMX_MISC_ACTIVITY_WAIT_SIPI;
|
||||
@@ -7066,12 +7126,10 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs)
|
||||
* guest, and the VMCS structure we give it - not about the
|
||||
* VMX support of the underlying hardware.
|
||||
*/
|
||||
msrs->basic =
|
||||
VMCS12_REVISION |
|
||||
VMX_BASIC_TRUE_CTLS |
|
||||
((u64)VMCS12_SIZE << VMX_BASIC_VMCS_SIZE_SHIFT) |
|
||||
(VMX_BASIC_MEM_TYPE_WB << VMX_BASIC_MEM_TYPE_SHIFT);
|
||||
msrs->basic = vmx_basic_encode_vmcs_info(VMCS12_REVISION, VMCS12_SIZE,
|
||||
X86_MEMTYPE_WB);
|
||||
|
||||
msrs->basic |= VMX_BASIC_TRUE_CTLS;
|
||||
if (cpu_has_vmx_basic_inout())
|
||||
msrs->basic |= VMX_BASIC_INOUT;
|
||||
}
|
||||
|
||||
@@ -39,11 +39,17 @@ bool nested_vmx_check_io_bitmaps(struct kvm_vcpu *vcpu, unsigned int port,
|
||||
|
||||
static inline struct vmcs12 *get_vmcs12(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
|
||||
!refcount_read(&vcpu->kvm->users_count));
|
||||
|
||||
return to_vmx(vcpu)->nested.cached_vmcs12;
|
||||
}
|
||||
|
||||
static inline struct vmcs12 *get_shadow_vmcs12(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
|
||||
!refcount_read(&vcpu->kvm->users_count));
|
||||
|
||||
return to_vmx(vcpu)->nested.cached_shadow_vmcs12;
|
||||
}
|
||||
|
||||
@@ -109,7 +115,7 @@ static inline unsigned nested_cpu_vmx_misc_cr3_count(struct kvm_vcpu *vcpu)
|
||||
static inline bool nested_cpu_has_vmwrite_any_field(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return to_vmx(vcpu)->nested.msrs.misc_low &
|
||||
MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS;
|
||||
VMX_MISC_VMWRITE_SHADOW_RO_FIELDS;
|
||||
}
|
||||
|
||||
static inline bool nested_cpu_has_zero_length_injection(struct kvm_vcpu *vcpu)
|
||||
|
||||
@@ -274,7 +274,7 @@ static int handle_encls_ecreate(struct kvm_vcpu *vcpu)
|
||||
* simultaneously set SGX_ATTR_PROVISIONKEY to bypass the check to
|
||||
* enforce restriction of access to the PROVISIONKEY.
|
||||
*/
|
||||
contents = (struct sgx_secs *)__get_free_page(GFP_KERNEL_ACCOUNT);
|
||||
contents = (struct sgx_secs *)__get_free_page(GFP_KERNEL);
|
||||
if (!contents)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
+35
-32
@@ -525,10 +525,6 @@ static const struct kvm_vmx_segment_field {
|
||||
VMX_SEGMENT_FIELD(LDTR),
|
||||
};
|
||||
|
||||
static inline void vmx_segment_cache_clear(struct vcpu_vmx *vmx)
|
||||
{
|
||||
vmx->segment_cache.bitmask = 0;
|
||||
}
|
||||
|
||||
static unsigned long host_idt_base;
|
||||
|
||||
@@ -755,7 +751,7 @@ fault:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void vmx_emergency_disable(void)
|
||||
void vmx_emergency_disable_virtualization_cpu(void)
|
||||
{
|
||||
int cpu = raw_smp_processor_id();
|
||||
struct loaded_vmcs *v;
|
||||
@@ -1998,15 +1994,15 @@ static inline bool is_vmx_feature_control_msr_valid(struct vcpu_vmx *vmx,
|
||||
return !(msr->data & ~valid_bits);
|
||||
}
|
||||
|
||||
int vmx_get_msr_feature(struct kvm_msr_entry *msr)
|
||||
int vmx_get_feature_msr(u32 msr, u64 *data)
|
||||
{
|
||||
switch (msr->index) {
|
||||
switch (msr) {
|
||||
case KVM_FIRST_EMULATED_VMX_MSR ... KVM_LAST_EMULATED_VMX_MSR:
|
||||
if (!nested)
|
||||
return 1;
|
||||
return vmx_get_vmx_msr(&vmcs_config.nested, msr->index, &msr->data);
|
||||
return vmx_get_vmx_msr(&vmcs_config.nested, msr, data);
|
||||
default:
|
||||
return KVM_MSR_RET_INVALID;
|
||||
return KVM_MSR_RET_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2605,13 +2601,13 @@ static u64 adjust_vmx_controls64(u64 ctl_opt, u32 msr)
|
||||
static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
|
||||
struct vmx_capability *vmx_cap)
|
||||
{
|
||||
u32 vmx_msr_low, vmx_msr_high;
|
||||
u32 _pin_based_exec_control = 0;
|
||||
u32 _cpu_based_exec_control = 0;
|
||||
u32 _cpu_based_2nd_exec_control = 0;
|
||||
u64 _cpu_based_3rd_exec_control = 0;
|
||||
u32 _vmexit_control = 0;
|
||||
u32 _vmentry_control = 0;
|
||||
u64 basic_msr;
|
||||
u64 misc_msr;
|
||||
int i;
|
||||
|
||||
@@ -2734,29 +2730,29 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
|
||||
_vmexit_control &= ~x_ctrl;
|
||||
}
|
||||
|
||||
rdmsr(MSR_IA32_VMX_BASIC, vmx_msr_low, vmx_msr_high);
|
||||
rdmsrl(MSR_IA32_VMX_BASIC, basic_msr);
|
||||
|
||||
/* IA-32 SDM Vol 3B: VMCS size is never greater than 4kB. */
|
||||
if ((vmx_msr_high & 0x1fff) > PAGE_SIZE)
|
||||
if (vmx_basic_vmcs_size(basic_msr) > PAGE_SIZE)
|
||||
return -EIO;
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
/* IA-32 SDM Vol 3B: 64-bit CPUs always have VMX_BASIC_MSR[48]==0. */
|
||||
if (vmx_msr_high & (1u<<16))
|
||||
/*
|
||||
* KVM expects to be able to shove all legal physical addresses into
|
||||
* VMCS fields for 64-bit kernels, and per the SDM, "This bit is always
|
||||
* 0 for processors that support Intel 64 architecture".
|
||||
*/
|
||||
if (basic_msr & VMX_BASIC_32BIT_PHYS_ADDR_ONLY)
|
||||
return -EIO;
|
||||
#endif
|
||||
|
||||
/* Require Write-Back (WB) memory type for VMCS accesses. */
|
||||
if (((vmx_msr_high >> 18) & 15) != 6)
|
||||
if (vmx_basic_vmcs_mem_type(basic_msr) != X86_MEMTYPE_WB)
|
||||
return -EIO;
|
||||
|
||||
rdmsrl(MSR_IA32_VMX_MISC, misc_msr);
|
||||
|
||||
vmcs_conf->size = vmx_msr_high & 0x1fff;
|
||||
vmcs_conf->basic_cap = vmx_msr_high & ~0x1fff;
|
||||
|
||||
vmcs_conf->revision_id = vmx_msr_low;
|
||||
|
||||
vmcs_conf->basic = basic_msr;
|
||||
vmcs_conf->pin_based_exec_ctrl = _pin_based_exec_control;
|
||||
vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control;
|
||||
vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control;
|
||||
@@ -2844,7 +2840,7 @@ fault:
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
int vmx_hardware_enable(void)
|
||||
int vmx_enable_virtualization_cpu(void)
|
||||
{
|
||||
int cpu = raw_smp_processor_id();
|
||||
u64 phys_addr = __pa(per_cpu(vmxarea, cpu));
|
||||
@@ -2881,7 +2877,7 @@ static void vmclear_local_loaded_vmcss(void)
|
||||
__loaded_vmcs_clear(v);
|
||||
}
|
||||
|
||||
void vmx_hardware_disable(void)
|
||||
void vmx_disable_virtualization_cpu(void)
|
||||
{
|
||||
vmclear_local_loaded_vmcss();
|
||||
|
||||
@@ -2903,13 +2899,13 @@ struct vmcs *alloc_vmcs_cpu(bool shadow, int cpu, gfp_t flags)
|
||||
if (!pages)
|
||||
return NULL;
|
||||
vmcs = page_address(pages);
|
||||
memset(vmcs, 0, vmcs_config.size);
|
||||
memset(vmcs, 0, vmx_basic_vmcs_size(vmcs_config.basic));
|
||||
|
||||
/* KVM supports Enlightened VMCS v1 only */
|
||||
if (kvm_is_using_evmcs())
|
||||
vmcs->hdr.revision_id = KVM_EVMCS_VERSION;
|
||||
else
|
||||
vmcs->hdr.revision_id = vmcs_config.revision_id;
|
||||
vmcs->hdr.revision_id = vmx_basic_vmcs_revision_id(vmcs_config.basic);
|
||||
|
||||
if (shadow)
|
||||
vmcs->hdr.shadow_vmcs = 1;
|
||||
@@ -3002,7 +2998,7 @@ static __init int alloc_kvm_area(void)
|
||||
* physical CPU.
|
||||
*/
|
||||
if (kvm_is_using_evmcs())
|
||||
vmcs->hdr.revision_id = vmcs_config.revision_id;
|
||||
vmcs->hdr.revision_id = vmx_basic_vmcs_revision_id(vmcs_config.basic);
|
||||
|
||||
per_cpu(vmxarea, cpu) = vmcs;
|
||||
}
|
||||
@@ -4219,6 +4215,13 @@ static int vmx_deliver_nested_posted_interrupt(struct kvm_vcpu *vcpu,
|
||||
{
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
|
||||
/*
|
||||
* DO NOT query the vCPU's vmcs12, as vmcs12 is dynamically allocated
|
||||
* and freed, and must not be accessed outside of vcpu->mutex. The
|
||||
* vCPU's cached PI NV is valid if and only if posted interrupts
|
||||
* enabled in its vmcs12, i.e. checking the vector also checks that
|
||||
* L1 has enabled posted interrupts for L2.
|
||||
*/
|
||||
if (is_guest_mode(vcpu) &&
|
||||
vector == vmx->nested.posted_intr_nv) {
|
||||
/*
|
||||
@@ -5804,8 +5807,9 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
|
||||
error_code |= (exit_qualification & EPT_VIOLATION_RWX_MASK)
|
||||
? PFERR_PRESENT_MASK : 0;
|
||||
|
||||
error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) != 0 ?
|
||||
PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK;
|
||||
if (error_code & EPT_VIOLATION_GVA_IS_VALID)
|
||||
error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) ?
|
||||
PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK;
|
||||
|
||||
/*
|
||||
* Check that the GPA doesn't exceed physical memory limits, as that is
|
||||
@@ -7265,6 +7269,8 @@ static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu,
|
||||
return handle_fastpath_set_msr_irqoff(vcpu);
|
||||
case EXIT_REASON_PREEMPTION_TIMER:
|
||||
return handle_fastpath_preemption_timer(vcpu, force_immediate_exit);
|
||||
case EXIT_REASON_HLT:
|
||||
return handle_fastpath_hlt(vcpu);
|
||||
default:
|
||||
return EXIT_FASTPATH_NONE;
|
||||
}
|
||||
@@ -7965,6 +7971,7 @@ static __init void vmx_set_cpu_caps(void)
|
||||
kvm_cpu_cap_clear(X86_FEATURE_SGX_LC);
|
||||
kvm_cpu_cap_clear(X86_FEATURE_SGX1);
|
||||
kvm_cpu_cap_clear(X86_FEATURE_SGX2);
|
||||
kvm_cpu_cap_clear(X86_FEATURE_SGX_EDECCSSA);
|
||||
}
|
||||
|
||||
if (vmx_umip_emulated())
|
||||
@@ -8515,7 +8522,7 @@ __init int vmx_hardware_setup(void)
|
||||
u64 use_timer_freq = 5000ULL * 1000 * 1000;
|
||||
|
||||
cpu_preemption_timer_multi =
|
||||
vmcs_config.misc & VMX_MISC_PREEMPTION_TIMER_RATE_MASK;
|
||||
vmx_misc_preemption_timer_rate(vmcs_config.misc);
|
||||
|
||||
if (tsc_khz)
|
||||
use_timer_freq = (u64)tsc_khz * 1000;
|
||||
@@ -8582,8 +8589,6 @@ static void __vmx_exit(void)
|
||||
{
|
||||
allow_smaller_maxphyaddr = false;
|
||||
|
||||
cpu_emergency_unregister_virt_callback(vmx_emergency_disable);
|
||||
|
||||
vmx_cleanup_l1d_flush();
|
||||
}
|
||||
|
||||
@@ -8630,8 +8635,6 @@ static int __init vmx_init(void)
|
||||
pi_init_cpu(cpu);
|
||||
}
|
||||
|
||||
cpu_emergency_register_virt_callback(vmx_emergency_disable);
|
||||
|
||||
vmx_check_vmcs12_offsets();
|
||||
|
||||
/*
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
#include "run_flags.h"
|
||||
#include "../mmu.h"
|
||||
|
||||
#define MSR_TYPE_R 1
|
||||
#define MSR_TYPE_W 2
|
||||
#define MSR_TYPE_RW 3
|
||||
|
||||
#define X2APIC_MSR(r) (APIC_BASE_MSR + ((r) >> 4))
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
@@ -756,4 +752,9 @@ static inline bool vmx_can_use_ipiv(struct kvm_vcpu *vcpu)
|
||||
return lapic_in_kernel(vcpu) && enable_ipiv;
|
||||
}
|
||||
|
||||
static inline void vmx_segment_cache_clear(struct vcpu_vmx *vmx)
|
||||
{
|
||||
vmx->segment_cache.bitmask = 0;
|
||||
}
|
||||
|
||||
#endif /* __KVM_X86_VMX_H */
|
||||
|
||||
@@ -104,6 +104,14 @@ static inline void evmcs_load(u64 phys_addr)
|
||||
struct hv_vp_assist_page *vp_ap =
|
||||
hv_get_vp_assist_page(smp_processor_id());
|
||||
|
||||
/*
|
||||
* When enabling eVMCS, KVM verifies that every CPU has a valid hv_vp_assist_page()
|
||||
* and aborts enabling the feature otherwise. CPU onlining path is also checked in
|
||||
* vmx_hardware_enable().
|
||||
*/
|
||||
if (KVM_BUG_ON(!vp_ap, kvm_get_running_vcpu()->kvm))
|
||||
return;
|
||||
|
||||
if (current_evmcs->hv_enlightenments_control.nested_flush_hypercall)
|
||||
vp_ap->nested_control.features.directhypercall = 1;
|
||||
vp_ap->current_nested_vmcs = phys_addr;
|
||||
|
||||
@@ -47,7 +47,7 @@ static __always_inline void vmcs_check16(unsigned long field)
|
||||
BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2001,
|
||||
"16-bit accessor invalid for 64-bit high field");
|
||||
BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x4000,
|
||||
"16-bit accessor invalid for 32-bit high field");
|
||||
"16-bit accessor invalid for 32-bit field");
|
||||
BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x6000,
|
||||
"16-bit accessor invalid for natural width field");
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@ extern struct kvm_x86_init_ops vt_init_ops __initdata;
|
||||
|
||||
void vmx_hardware_unsetup(void);
|
||||
int vmx_check_processor_compat(void);
|
||||
int vmx_hardware_enable(void);
|
||||
void vmx_hardware_disable(void);
|
||||
int vmx_enable_virtualization_cpu(void);
|
||||
void vmx_disable_virtualization_cpu(void);
|
||||
void vmx_emergency_disable_virtualization_cpu(void);
|
||||
int vmx_vm_init(struct kvm *kvm);
|
||||
void vmx_vm_destroy(struct kvm *kvm);
|
||||
int vmx_vcpu_precreate(struct kvm *kvm);
|
||||
@@ -56,7 +57,7 @@ bool vmx_has_emulated_msr(struct kvm *kvm, u32 index);
|
||||
void vmx_msr_filter_changed(struct kvm_vcpu *vcpu);
|
||||
void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu);
|
||||
void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu);
|
||||
int vmx_get_msr_feature(struct kvm_msr_entry *msr);
|
||||
int vmx_get_feature_msr(u32 msr, u64 *data);
|
||||
int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
|
||||
u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg);
|
||||
void vmx_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
|
||||
|
||||
+484
-522
File diff suppressed because it is too large
Load Diff
+26
-5
@@ -103,11 +103,18 @@ static inline unsigned int __shrink_ple_window(unsigned int val,
|
||||
return max(val, min);
|
||||
}
|
||||
|
||||
#define MSR_IA32_CR_PAT_DEFAULT 0x0007040600070406ULL
|
||||
#define MSR_IA32_CR_PAT_DEFAULT \
|
||||
PAT_VALUE(WB, WT, UC_MINUS, UC, WB, WT, UC_MINUS, UC)
|
||||
|
||||
void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu);
|
||||
int kvm_check_nested_events(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* Forcibly leave the nested mode in cases like a vCPU reset */
|
||||
static inline void kvm_leave_nested(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_x86_ops.nested_ops->leave_nested(vcpu);
|
||||
}
|
||||
|
||||
static inline bool kvm_vcpu_has_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.last_vmentry_cpu != -1;
|
||||
@@ -334,6 +341,7 @@ int x86_decode_emulated_instruction(struct kvm_vcpu *vcpu, int emulation_type,
|
||||
int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
|
||||
int emulation_type, void *insn, int insn_len);
|
||||
fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu);
|
||||
fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu);
|
||||
|
||||
extern struct kvm_caps kvm_caps;
|
||||
extern struct kvm_host_values kvm_host;
|
||||
@@ -504,13 +512,26 @@ int kvm_handle_memory_failure(struct kvm_vcpu *vcpu, int r,
|
||||
int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva);
|
||||
bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type);
|
||||
|
||||
enum kvm_msr_access {
|
||||
MSR_TYPE_R = BIT(0),
|
||||
MSR_TYPE_W = BIT(1),
|
||||
MSR_TYPE_RW = MSR_TYPE_R | MSR_TYPE_W,
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal error codes that are used to indicate that MSR emulation encountered
|
||||
* an error that should result in #GP in the guest, unless userspace
|
||||
* handles it.
|
||||
* an error that should result in #GP in the guest, unless userspace handles it.
|
||||
* Note, '1', '0', and negative numbers are off limits, as they are used by KVM
|
||||
* as part of KVM's lightly documented internal KVM_RUN return codes.
|
||||
*
|
||||
* UNSUPPORTED - The MSR isn't supported, either because it is completely
|
||||
* unknown to KVM, or because the MSR should not exist according
|
||||
* to the vCPU model.
|
||||
*
|
||||
* FILTERED - Access to the MSR is denied by a userspace MSR filter.
|
||||
*/
|
||||
#define KVM_MSR_RET_INVALID 2 /* in-kernel MSR emulation #GP condition */
|
||||
#define KVM_MSR_RET_FILTERED 3 /* #GP due to userspace MSR filter */
|
||||
#define KVM_MSR_RET_UNSUPPORTED 2
|
||||
#define KVM_MSR_RET_FILTERED 3
|
||||
|
||||
#define __cr4_reserved_bits(__cpu_has, __c) \
|
||||
({ \
|
||||
|
||||
+10
-26
@@ -176,15 +176,6 @@ static inline void set_page_memtype(struct page *pg,
|
||||
}
|
||||
#endif
|
||||
|
||||
enum {
|
||||
PAT_UC = 0, /* uncached */
|
||||
PAT_WC = 1, /* Write combining */
|
||||
PAT_WT = 4, /* Write Through */
|
||||
PAT_WP = 5, /* Write Protected */
|
||||
PAT_WB = 6, /* Write Back (default) */
|
||||
PAT_UC_MINUS = 7, /* UC, but can be overridden by MTRR */
|
||||
};
|
||||
|
||||
#define CM(c) (_PAGE_CACHE_MODE_ ## c)
|
||||
|
||||
static enum page_cache_mode __init pat_get_cache_mode(unsigned int pat_val,
|
||||
@@ -194,13 +185,13 @@ static enum page_cache_mode __init pat_get_cache_mode(unsigned int pat_val,
|
||||
char *cache_mode;
|
||||
|
||||
switch (pat_val) {
|
||||
case PAT_UC: cache = CM(UC); cache_mode = "UC "; break;
|
||||
case PAT_WC: cache = CM(WC); cache_mode = "WC "; break;
|
||||
case PAT_WT: cache = CM(WT); cache_mode = "WT "; break;
|
||||
case PAT_WP: cache = CM(WP); cache_mode = "WP "; break;
|
||||
case PAT_WB: cache = CM(WB); cache_mode = "WB "; break;
|
||||
case PAT_UC_MINUS: cache = CM(UC_MINUS); cache_mode = "UC- "; break;
|
||||
default: cache = CM(WB); cache_mode = "WB "; break;
|
||||
case X86_MEMTYPE_UC: cache = CM(UC); cache_mode = "UC "; break;
|
||||
case X86_MEMTYPE_WC: cache = CM(WC); cache_mode = "WC "; break;
|
||||
case X86_MEMTYPE_WT: cache = CM(WT); cache_mode = "WT "; break;
|
||||
case X86_MEMTYPE_WP: cache = CM(WP); cache_mode = "WP "; break;
|
||||
case X86_MEMTYPE_WB: cache = CM(WB); cache_mode = "WB "; break;
|
||||
case X86_MEMTYPE_UC_MINUS: cache = CM(UC_MINUS); cache_mode = "UC- "; break;
|
||||
default: cache = CM(WB); cache_mode = "WB "; break;
|
||||
}
|
||||
|
||||
memcpy(msg, cache_mode, 4);
|
||||
@@ -257,12 +248,6 @@ void pat_cpu_init(void)
|
||||
void __init pat_bp_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &boot_cpu_data;
|
||||
#define PAT(p0, p1, p2, p3, p4, p5, p6, p7) \
|
||||
(((u64)PAT_ ## p0) | ((u64)PAT_ ## p1 << 8) | \
|
||||
((u64)PAT_ ## p2 << 16) | ((u64)PAT_ ## p3 << 24) | \
|
||||
((u64)PAT_ ## p4 << 32) | ((u64)PAT_ ## p5 << 40) | \
|
||||
((u64)PAT_ ## p6 << 48) | ((u64)PAT_ ## p7 << 56))
|
||||
|
||||
|
||||
if (!IS_ENABLED(CONFIG_X86_PAT))
|
||||
pr_info_once("x86/PAT: PAT support disabled because CONFIG_X86_PAT is disabled in the kernel.\n");
|
||||
@@ -293,7 +278,7 @@ void __init pat_bp_init(void)
|
||||
* NOTE: When WC or WP is used, it is redirected to UC- per
|
||||
* the default setup in __cachemode2pte_tbl[].
|
||||
*/
|
||||
pat_msr_val = PAT(WB, WT, UC_MINUS, UC, WB, WT, UC_MINUS, UC);
|
||||
pat_msr_val = PAT_VALUE(WB, WT, UC_MINUS, UC, WB, WT, UC_MINUS, UC);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -328,7 +313,7 @@ void __init pat_bp_init(void)
|
||||
* NOTE: When WT or WP is used, it is redirected to UC- per
|
||||
* the default setup in __cachemode2pte_tbl[].
|
||||
*/
|
||||
pat_msr_val = PAT(WB, WC, UC_MINUS, UC, WB, WC, UC_MINUS, UC);
|
||||
pat_msr_val = PAT_VALUE(WB, WC, UC_MINUS, UC, WB, WC, UC_MINUS, UC);
|
||||
} else {
|
||||
/*
|
||||
* Full PAT support. We put WT in slot 7 to improve
|
||||
@@ -356,13 +341,12 @@ void __init pat_bp_init(void)
|
||||
* The reserved slots are unused, but mapped to their
|
||||
* corresponding types in the presence of PAT errata.
|
||||
*/
|
||||
pat_msr_val = PAT(WB, WC, UC_MINUS, UC, WB, WP, UC_MINUS, WT);
|
||||
pat_msr_val = PAT_VALUE(WB, WC, UC_MINUS, UC, WB, WP, UC_MINUS, WT);
|
||||
}
|
||||
|
||||
memory_caching_control |= CACHE_PAT;
|
||||
|
||||
init_cache_modes(pat_msr_val);
|
||||
#undef PAT
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(memtype_lock); /* protects memtype accesses */
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/sysrq.h>
|
||||
|
||||
/* This is declared by <linux/sched.h> */
|
||||
void show_regs(struct pt_regs *regs)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <linux/utsname.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/sysrq.h>
|
||||
|
||||
void show_regs(struct pt_regs *regs)
|
||||
{
|
||||
|
||||
@@ -1083,10 +1083,6 @@ struct amdgpu_device {
|
||||
|
||||
struct amdgpu_virt virt;
|
||||
|
||||
/* link all shadow bo */
|
||||
struct list_head shadow_list;
|
||||
struct mutex shadow_list_lock;
|
||||
|
||||
/* record hw reset is performed */
|
||||
bool has_hw_reset;
|
||||
u8 reset_magic[AMDGPU_RESET_MAGIC_NUM];
|
||||
|
||||
@@ -511,7 +511,7 @@ static int __aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *h
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* udpate aca bank to aca source error_cache first */
|
||||
/* update aca bank to aca source error_cache first */
|
||||
ret = aca_banks_update(adev, smu_type, handler_aca_log_bank_error, qctx, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -950,28 +950,30 @@ static void unlock_spi_csq_mutexes(struct amdgpu_device *adev)
|
||||
* @inst: xcc's instance number on a multi-XCC setup
|
||||
*/
|
||||
static void get_wave_count(struct amdgpu_device *adev, int queue_idx,
|
||||
int *wave_cnt, int *vmid, uint32_t inst)
|
||||
struct kfd_cu_occupancy *queue_cnt, uint32_t inst)
|
||||
{
|
||||
int pipe_idx;
|
||||
int queue_slot;
|
||||
unsigned int reg_val;
|
||||
|
||||
unsigned int wave_cnt;
|
||||
/*
|
||||
* Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID
|
||||
* parameters to read out waves in flight. Get VMID if there are
|
||||
* non-zero waves in flight.
|
||||
*/
|
||||
*vmid = 0xFF;
|
||||
*wave_cnt = 0;
|
||||
pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe;
|
||||
queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe;
|
||||
soc15_grbm_select(adev, 1, pipe_idx, queue_slot, 0, inst);
|
||||
reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, inst, mmSPI_CSQ_WF_ACTIVE_COUNT_0) +
|
||||
queue_slot);
|
||||
*wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK;
|
||||
if (*wave_cnt != 0)
|
||||
*vmid = (RREG32_SOC15(GC, inst, mmCP_HQD_VMID) &
|
||||
CP_HQD_VMID__VMID_MASK) >> CP_HQD_VMID__VMID__SHIFT;
|
||||
soc15_grbm_select(adev, 1, pipe_idx, queue_slot, 0, GET_INST(GC, inst));
|
||||
reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, GET_INST(GC, inst),
|
||||
mmSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot);
|
||||
wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK;
|
||||
if (wave_cnt != 0) {
|
||||
queue_cnt->wave_cnt += wave_cnt;
|
||||
queue_cnt->doorbell_off =
|
||||
(RREG32_SOC15(GC, GET_INST(GC, inst), mmCP_HQD_PQ_DOORBELL_CONTROL) &
|
||||
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >>
|
||||
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -981,9 +983,8 @@ static void get_wave_count(struct amdgpu_device *adev, int queue_idx,
|
||||
* or more queues running and submitting waves to compute units.
|
||||
*
|
||||
* @adev: Handle of device from which to get number of waves in flight
|
||||
* @pasid: Identifies the process for which this query call is invoked
|
||||
* @pasid_wave_cnt: Output parameter updated with number of waves in flight that
|
||||
* belong to process with given pasid
|
||||
* @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset
|
||||
* for comparison later.
|
||||
* @max_waves_per_cu: Output parameter updated with maximum number of waves
|
||||
* possible per Compute Unit
|
||||
* @inst: xcc's instance number on a multi-XCC setup
|
||||
@@ -1011,34 +1012,28 @@ static void get_wave_count(struct amdgpu_device *adev, int queue_idx,
|
||||
* number of waves that are in flight for the queue at specified index. The
|
||||
* index ranges from 0 to 7.
|
||||
*
|
||||
* If non-zero waves are in flight, read CP_HQD_VMID register to obtain VMID
|
||||
* of the wave(s).
|
||||
* If non-zero waves are in flight, store the corresponding doorbell offset
|
||||
* of the queue, along with the wave count.
|
||||
*
|
||||
* Determine if VMID from above step maps to pasid provided as parameter. If
|
||||
* it matches agrregate the wave count. That the VMID will not match pasid is
|
||||
* a normal condition i.e. a device is expected to support multiple queues
|
||||
* from multiple proceses.
|
||||
* Determine if the queue belongs to the process by comparing the doorbell
|
||||
* offset against the process's queues. If it matches, aggregate the wave
|
||||
* count for the process.
|
||||
*
|
||||
* Reading registers referenced above involves programming GRBM appropriately
|
||||
*/
|
||||
void kgd_gfx_v9_get_cu_occupancy(struct amdgpu_device *adev, int pasid,
|
||||
int *pasid_wave_cnt, int *max_waves_per_cu, uint32_t inst)
|
||||
void kgd_gfx_v9_get_cu_occupancy(struct amdgpu_device *adev,
|
||||
struct kfd_cu_occupancy *cu_occupancy,
|
||||
int *max_waves_per_cu, uint32_t inst)
|
||||
{
|
||||
int qidx;
|
||||
int vmid;
|
||||
int se_idx;
|
||||
int sh_idx;
|
||||
int se_cnt;
|
||||
int sh_cnt;
|
||||
int wave_cnt;
|
||||
int queue_map;
|
||||
int pasid_tmp;
|
||||
int max_queue_cnt;
|
||||
int vmid_wave_cnt = 0;
|
||||
DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES);
|
||||
|
||||
lock_spi_csq_mutexes(adev);
|
||||
soc15_grbm_select(adev, 1, 0, 0, 0, inst);
|
||||
soc15_grbm_select(adev, 1, 0, 0, 0, GET_INST(GC, inst));
|
||||
|
||||
/*
|
||||
* Iterate through the shader engines and arrays of the device
|
||||
@@ -1048,51 +1043,38 @@ void kgd_gfx_v9_get_cu_occupancy(struct amdgpu_device *adev, int pasid,
|
||||
AMDGPU_MAX_QUEUES);
|
||||
max_queue_cnt = adev->gfx.mec.num_pipe_per_mec *
|
||||
adev->gfx.mec.num_queue_per_pipe;
|
||||
sh_cnt = adev->gfx.config.max_sh_per_se;
|
||||
se_cnt = adev->gfx.config.max_shader_engines;
|
||||
for (se_idx = 0; se_idx < se_cnt; se_idx++) {
|
||||
for (sh_idx = 0; sh_idx < sh_cnt; sh_idx++) {
|
||||
amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst);
|
||||
queue_map = RREG32_SOC15(GC, GET_INST(GC, inst), mmSPI_CSQ_WF_ACTIVE_STATUS);
|
||||
|
||||
amdgpu_gfx_select_se_sh(adev, se_idx, sh_idx, 0xffffffff, inst);
|
||||
queue_map = RREG32_SOC15(GC, inst, mmSPI_CSQ_WF_ACTIVE_STATUS);
|
||||
|
||||
/*
|
||||
* Assumption: queue map encodes following schema: four
|
||||
* pipes per each micro-engine, with each pipe mapping
|
||||
* eight queues. This schema is true for GFX9 devices
|
||||
* and must be verified for newer device families
|
||||
/*
|
||||
* Assumption: queue map encodes following schema: four
|
||||
* pipes per each micro-engine, with each pipe mapping
|
||||
* eight queues. This schema is true for GFX9 devices
|
||||
* and must be verified for newer device families
|
||||
*/
|
||||
for (qidx = 0; qidx < max_queue_cnt; qidx++) {
|
||||
/* Skip qeueus that are not associated with
|
||||
* compute functions
|
||||
*/
|
||||
for (qidx = 0; qidx < max_queue_cnt; qidx++) {
|
||||
if (!test_bit(qidx, cp_queue_bitmap))
|
||||
continue;
|
||||
|
||||
/* Skip qeueus that are not associated with
|
||||
* compute functions
|
||||
*/
|
||||
if (!test_bit(qidx, cp_queue_bitmap))
|
||||
continue;
|
||||
if (!(queue_map & (1 << qidx)))
|
||||
continue;
|
||||
|
||||
if (!(queue_map & (1 << qidx)))
|
||||
continue;
|
||||
|
||||
/* Get number of waves in flight and aggregate them */
|
||||
get_wave_count(adev, qidx, &wave_cnt, &vmid,
|
||||
inst);
|
||||
if (wave_cnt != 0) {
|
||||
pasid_tmp =
|
||||
RREG32(SOC15_REG_OFFSET(OSSSYS, inst,
|
||||
mmIH_VMID_0_LUT) + vmid);
|
||||
if (pasid_tmp == pasid)
|
||||
vmid_wave_cnt += wave_cnt;
|
||||
}
|
||||
}
|
||||
/* Get number of waves in flight and aggregate them */
|
||||
get_wave_count(adev, qidx, &cu_occupancy[qidx],
|
||||
inst);
|
||||
}
|
||||
}
|
||||
|
||||
amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst);
|
||||
soc15_grbm_select(adev, 0, 0, 0, 0, inst);
|
||||
soc15_grbm_select(adev, 0, 0, 0, 0, GET_INST(GC, inst));
|
||||
unlock_spi_csq_mutexes(adev);
|
||||
|
||||
/* Update the output parameters and return */
|
||||
*pasid_wave_cnt = vmid_wave_cnt;
|
||||
*max_waves_per_cu = adev->gfx.cu_info.simd_per_cu *
|
||||
adev->gfx.cu_info.max_waves_per_simd;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,9 @@ bool kgd_gfx_v9_get_atc_vmid_pasid_mapping_info(struct amdgpu_device *adev,
|
||||
uint8_t vmid, uint16_t *p_pasid);
|
||||
void kgd_gfx_v9_set_vm_context_page_table_base(struct amdgpu_device *adev,
|
||||
uint32_t vmid, uint64_t page_table_base);
|
||||
void kgd_gfx_v9_get_cu_occupancy(struct amdgpu_device *adev, int pasid,
|
||||
int *pasid_wave_cnt, int *max_waves_per_cu, uint32_t inst);
|
||||
void kgd_gfx_v9_get_cu_occupancy(struct amdgpu_device *adev,
|
||||
struct kfd_cu_occupancy *cu_occupancy,
|
||||
int *max_waves_per_cu, uint32_t inst);
|
||||
void kgd_gfx_v9_program_trap_handler_settings(struct amdgpu_device *adev,
|
||||
uint32_t vmid, uint64_t tba_addr, uint64_t tma_addr,
|
||||
uint32_t inst);
|
||||
|
||||
@@ -1499,7 +1499,7 @@ static int amdgpu_amdkfd_gpuvm_pin_bo(struct amdgpu_bo *bo, u32 domain)
|
||||
}
|
||||
}
|
||||
|
||||
ret = amdgpu_bo_pin_restricted(bo, domain, 0, 0);
|
||||
ret = amdgpu_bo_pin(bo, domain);
|
||||
if (ret)
|
||||
pr_err("Error in Pinning BO to domain: %d\n", domain);
|
||||
|
||||
|
||||
@@ -87,8 +87,9 @@ static bool check_atom_bios(uint8_t *bios, size_t size)
|
||||
* part of the system bios. On boot, the system bios puts a
|
||||
* copy of the igp rom at the start of vram if a discrete card is
|
||||
* present.
|
||||
* For SR-IOV, the vbios image is also put in VRAM in the VF.
|
||||
*/
|
||||
static bool igp_read_bios_from_vram(struct amdgpu_device *adev)
|
||||
static bool amdgpu_read_bios_from_vram(struct amdgpu_device *adev)
|
||||
{
|
||||
uint8_t __iomem *bios;
|
||||
resource_size_t vram_base;
|
||||
@@ -284,10 +285,6 @@ static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev)
|
||||
acpi_status status;
|
||||
bool found = false;
|
||||
|
||||
/* ATRM is for the discrete card only */
|
||||
if (adev->flags & AMD_IS_APU)
|
||||
return false;
|
||||
|
||||
/* ATRM is for on-platform devices only */
|
||||
if (dev_is_removable(&adev->pdev->dev))
|
||||
return false;
|
||||
@@ -343,11 +340,8 @@ static inline bool amdgpu_atrm_get_bios(struct amdgpu_device *adev)
|
||||
|
||||
static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev)
|
||||
{
|
||||
if (adev->flags & AMD_IS_APU)
|
||||
return igp_read_bios_from_vram(adev);
|
||||
else
|
||||
return (!adev->asic_funcs || !adev->asic_funcs->read_disabled_bios) ?
|
||||
false : amdgpu_asic_read_disabled_bios(adev);
|
||||
return (!adev->asic_funcs || !adev->asic_funcs->read_disabled_bios) ?
|
||||
false : amdgpu_asic_read_disabled_bios(adev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
@@ -414,7 +408,36 @@ static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool amdgpu_get_bios(struct amdgpu_device *adev)
|
||||
static bool amdgpu_get_bios_apu(struct amdgpu_device *adev)
|
||||
{
|
||||
if (amdgpu_acpi_vfct_bios(adev)) {
|
||||
dev_info(adev->dev, "Fetched VBIOS from VFCT\n");
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (amdgpu_read_bios_from_vram(adev)) {
|
||||
dev_info(adev->dev, "Fetched VBIOS from VRAM BAR\n");
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (amdgpu_read_bios(adev)) {
|
||||
dev_info(adev->dev, "Fetched VBIOS from ROM BAR\n");
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (amdgpu_read_platform_bios(adev)) {
|
||||
dev_info(adev->dev, "Fetched VBIOS from platform\n");
|
||||
goto success;
|
||||
}
|
||||
|
||||
dev_err(adev->dev, "Unable to locate a BIOS ROM\n");
|
||||
return false;
|
||||
|
||||
success:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amdgpu_get_bios_dgpu(struct amdgpu_device *adev)
|
||||
{
|
||||
if (amdgpu_atrm_get_bios(adev)) {
|
||||
dev_info(adev->dev, "Fetched VBIOS from ATRM\n");
|
||||
@@ -426,7 +449,8 @@ bool amdgpu_get_bios(struct amdgpu_device *adev)
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (igp_read_bios_from_vram(adev)) {
|
||||
/* this is required for SR-IOV */
|
||||
if (amdgpu_read_bios_from_vram(adev)) {
|
||||
dev_info(adev->dev, "Fetched VBIOS from VRAM BAR\n");
|
||||
goto success;
|
||||
}
|
||||
@@ -455,10 +479,24 @@ bool amdgpu_get_bios(struct amdgpu_device *adev)
|
||||
return false;
|
||||
|
||||
success:
|
||||
adev->is_atom_fw = adev->asic_type >= CHIP_VEGA10;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool amdgpu_get_bios(struct amdgpu_device *adev)
|
||||
{
|
||||
bool found;
|
||||
|
||||
if (adev->flags & AMD_IS_APU)
|
||||
found = amdgpu_get_bios_apu(adev);
|
||||
else
|
||||
found = amdgpu_get_bios_dgpu(adev);
|
||||
|
||||
if (found)
|
||||
adev->is_atom_fw = adev->asic_type >= CHIP_VEGA10;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* helper function for soc15 and onwards to read bios from rom */
|
||||
bool amdgpu_soc15_read_bios_from_rom(struct amdgpu_device *adev,
|
||||
u8 *bios, u32 length_bytes)
|
||||
|
||||
@@ -4107,9 +4107,6 @@ int amdgpu_device_init(struct amdgpu_device *adev,
|
||||
spin_lock_init(&adev->mm_stats.lock);
|
||||
spin_lock_init(&adev->wb.lock);
|
||||
|
||||
INIT_LIST_HEAD(&adev->shadow_list);
|
||||
mutex_init(&adev->shadow_list_lock);
|
||||
|
||||
INIT_LIST_HEAD(&adev->reset_list);
|
||||
|
||||
INIT_LIST_HEAD(&adev->ras_list);
|
||||
@@ -5029,80 +5026,6 @@ static int amdgpu_device_ip_post_soft_reset(struct amdgpu_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_device_recover_vram - Recover some VRAM contents
|
||||
*
|
||||
* @adev: amdgpu_device pointer
|
||||
*
|
||||
* Restores the contents of VRAM buffers from the shadows in GTT. Used to
|
||||
* restore things like GPUVM page tables after a GPU reset where
|
||||
* the contents of VRAM might be lost.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int amdgpu_device_recover_vram(struct amdgpu_device *adev)
|
||||
{
|
||||
struct dma_fence *fence = NULL, *next = NULL;
|
||||
struct amdgpu_bo *shadow;
|
||||
struct amdgpu_bo_vm *vmbo;
|
||||
long r = 1, tmo;
|
||||
|
||||
if (amdgpu_sriov_runtime(adev))
|
||||
tmo = msecs_to_jiffies(8000);
|
||||
else
|
||||
tmo = msecs_to_jiffies(100);
|
||||
|
||||
dev_info(adev->dev, "recover vram bo from shadow start\n");
|
||||
mutex_lock(&adev->shadow_list_lock);
|
||||
list_for_each_entry(vmbo, &adev->shadow_list, shadow_list) {
|
||||
/* If vm is compute context or adev is APU, shadow will be NULL */
|
||||
if (!vmbo->shadow)
|
||||
continue;
|
||||
shadow = vmbo->shadow;
|
||||
|
||||
/* No need to recover an evicted BO */
|
||||
if (!shadow->tbo.resource ||
|
||||
shadow->tbo.resource->mem_type != TTM_PL_TT ||
|
||||
shadow->tbo.resource->start == AMDGPU_BO_INVALID_OFFSET ||
|
||||
shadow->parent->tbo.resource->mem_type != TTM_PL_VRAM)
|
||||
continue;
|
||||
|
||||
r = amdgpu_bo_restore_shadow(shadow, &next);
|
||||
if (r)
|
||||
break;
|
||||
|
||||
if (fence) {
|
||||
tmo = dma_fence_wait_timeout(fence, false, tmo);
|
||||
dma_fence_put(fence);
|
||||
fence = next;
|
||||
if (tmo == 0) {
|
||||
r = -ETIMEDOUT;
|
||||
break;
|
||||
} else if (tmo < 0) {
|
||||
r = tmo;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
fence = next;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&adev->shadow_list_lock);
|
||||
|
||||
if (fence)
|
||||
tmo = dma_fence_wait_timeout(fence, false, tmo);
|
||||
dma_fence_put(fence);
|
||||
|
||||
if (r < 0 || tmo <= 0) {
|
||||
dev_err(adev->dev, "recover vram bo from shadow failed, r is %ld, tmo is %ld\n", r, tmo);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_info(adev->dev, "recover vram bo from shadow done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* amdgpu_device_reset_sriov - reset ASIC for SR-IOV vf
|
||||
*
|
||||
@@ -5165,12 +5088,8 @@ static int amdgpu_device_reset_sriov(struct amdgpu_device *adev,
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (adev->virt.gim_feature & AMDGIM_FEATURE_GIM_FLR_VRAMLOST) {
|
||||
if (adev->virt.gim_feature & AMDGIM_FEATURE_GIM_FLR_VRAMLOST)
|
||||
amdgpu_inc_vram_lost(adev);
|
||||
r = amdgpu_device_recover_vram(adev);
|
||||
}
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* need to be called during full access so we can't do it later like
|
||||
* bare-metal does.
|
||||
@@ -5569,9 +5488,7 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
if (!r)
|
||||
r = amdgpu_device_recover_vram(tmp_adev);
|
||||
else
|
||||
if (r)
|
||||
tmp_adev->asic_reset_res = r;
|
||||
}
|
||||
|
||||
@@ -6189,7 +6106,7 @@ bool amdgpu_device_is_peer_accessible(struct amdgpu_device *adev,
|
||||
p2p_addressable = !(adev->gmc.aper_base & address_mask ||
|
||||
aper_limit & address_mask);
|
||||
}
|
||||
return is_large_bar && p2p_access && p2p_addressable;
|
||||
return pcie_p2p && is_large_bar && p2p_access && p2p_addressable;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@@ -233,6 +233,7 @@ int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,
|
||||
}
|
||||
|
||||
if (!adev->enable_virtual_display) {
|
||||
new_abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
|
||||
r = amdgpu_bo_pin(new_abo,
|
||||
amdgpu_display_supported_domains(adev, new_abo->flags));
|
||||
if (unlikely(r != 0)) {
|
||||
@@ -1474,7 +1475,7 @@ bool amdgpu_display_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
|
||||
if ((!(mode->flags & DRM_MODE_FLAG_INTERLACE)) &&
|
||||
((amdgpu_encoder->underscan_type == UNDERSCAN_ON) ||
|
||||
((amdgpu_encoder->underscan_type == UNDERSCAN_AUTO) &&
|
||||
connector->display_info.is_hdmi &&
|
||||
connector && connector->display_info.is_hdmi &&
|
||||
amdgpu_display_is_hdtv_mode(mode)))) {
|
||||
if (amdgpu_encoder->underscan_hborder != 0)
|
||||
amdgpu_crtc->h_border = amdgpu_encoder->underscan_hborder;
|
||||
@@ -1759,6 +1760,7 @@ int amdgpu_display_resume_helper(struct amdgpu_device *adev)
|
||||
|
||||
r = amdgpu_bo_reserve(aobj, true);
|
||||
if (r == 0) {
|
||||
aobj->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
|
||||
r = amdgpu_bo_pin(aobj, AMDGPU_GEM_DOMAIN_VRAM);
|
||||
if (r != 0)
|
||||
dev_err(adev->dev, "Failed to pin cursor BO (%d)\n", r);
|
||||
|
||||
@@ -117,9 +117,10 @@
|
||||
* - 3.56.0 - Update IB start address and size alignment for decode and encode
|
||||
* - 3.57.0 - Compute tunneling on GFX10+
|
||||
* - 3.58.0 - Add GFX12 DCC support
|
||||
* - 3.59.0 - Cleared VRAM
|
||||
*/
|
||||
#define KMS_DRIVER_MAJOR 3
|
||||
#define KMS_DRIVER_MINOR 58
|
||||
#define KMS_DRIVER_MINOR 59
|
||||
#define KMS_DRIVER_PATCHLEVEL 0
|
||||
|
||||
/*
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
#include "amdgpu_hmm.h"
|
||||
#include "amdgpu_xgmi.h"
|
||||
|
||||
static const struct drm_gem_object_funcs amdgpu_gem_object_funcs;
|
||||
|
||||
static vm_fault_t amdgpu_gem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct ttm_buffer_object *bo = vmf->vma->vm_private_data;
|
||||
@@ -87,11 +85,11 @@ static const struct vm_operations_struct amdgpu_gem_vm_ops = {
|
||||
|
||||
static void amdgpu_gem_object_free(struct drm_gem_object *gobj)
|
||||
{
|
||||
struct amdgpu_bo *robj = gem_to_amdgpu_bo(gobj);
|
||||
struct amdgpu_bo *aobj = gem_to_amdgpu_bo(gobj);
|
||||
|
||||
if (robj) {
|
||||
amdgpu_hmm_unregister(robj);
|
||||
amdgpu_bo_unref(&robj);
|
||||
if (aobj) {
|
||||
amdgpu_hmm_unregister(aobj);
|
||||
ttm_bo_put(&aobj->tbo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +124,6 @@ int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size,
|
||||
|
||||
bo = &ubo->bo;
|
||||
*obj = &bo->tbo.base;
|
||||
(*obj)->funcs = &amdgpu_gem_object_funcs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -295,7 +292,7 @@ static int amdgpu_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_str
|
||||
return drm_gem_ttm_mmap(obj, vma);
|
||||
}
|
||||
|
||||
static const struct drm_gem_object_funcs amdgpu_gem_object_funcs = {
|
||||
const struct drm_gem_object_funcs amdgpu_gem_object_funcs = {
|
||||
.free = amdgpu_gem_object_free,
|
||||
.open = amdgpu_gem_object_open,
|
||||
.close = amdgpu_gem_object_close,
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#define AMDGPU_GEM_DOMAIN_MAX 0x3
|
||||
#define gem_to_amdgpu_bo(gobj) container_of((gobj), struct amdgpu_bo, tbo.base)
|
||||
|
||||
extern const struct drm_gem_object_funcs amdgpu_gem_object_funcs;
|
||||
|
||||
unsigned long amdgpu_gem_timeout(uint64_t timeout_ns);
|
||||
|
||||
/*
|
||||
|
||||
@@ -107,8 +107,11 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job)
|
||||
/*
|
||||
* Do the coredump immediately after a job timeout to get a very
|
||||
* close dump/snapshot/representation of GPU's current error status
|
||||
* Skip it for SRIOV, since VF FLR will be triggered by host driver
|
||||
* before job timeout
|
||||
*/
|
||||
amdgpu_job_core_dump(adev, job);
|
||||
if (!amdgpu_sriov_vf(adev))
|
||||
amdgpu_job_core_dump(adev, job);
|
||||
|
||||
if (amdgpu_gpu_recovery &&
|
||||
amdgpu_ring_soft_recovery(ring, job->vmid, s_job->s_fence->parent)) {
|
||||
|
||||
@@ -77,24 +77,6 @@ static void amdgpu_bo_user_destroy(struct ttm_buffer_object *tbo)
|
||||
amdgpu_bo_destroy(tbo);
|
||||
}
|
||||
|
||||
static void amdgpu_bo_vm_destroy(struct ttm_buffer_object *tbo)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(tbo->bdev);
|
||||
struct amdgpu_bo *shadow_bo = ttm_to_amdgpu_bo(tbo), *bo;
|
||||
struct amdgpu_bo_vm *vmbo;
|
||||
|
||||
bo = shadow_bo->parent;
|
||||
vmbo = to_amdgpu_bo_vm(bo);
|
||||
/* in case amdgpu_device_recover_vram got NULL of bo->parent */
|
||||
if (!list_empty(&vmbo->shadow_list)) {
|
||||
mutex_lock(&adev->shadow_list_lock);
|
||||
list_del_init(&vmbo->shadow_list);
|
||||
mutex_unlock(&adev->shadow_list_lock);
|
||||
}
|
||||
|
||||
amdgpu_bo_destroy(tbo);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_is_amdgpu_bo - check if the buffer object is an &amdgpu_bo
|
||||
* @bo: buffer object to be checked
|
||||
@@ -108,8 +90,7 @@ static void amdgpu_bo_vm_destroy(struct ttm_buffer_object *tbo)
|
||||
bool amdgpu_bo_is_amdgpu_bo(struct ttm_buffer_object *bo)
|
||||
{
|
||||
if (bo->destroy == &amdgpu_bo_destroy ||
|
||||
bo->destroy == &amdgpu_bo_user_destroy ||
|
||||
bo->destroy == &amdgpu_bo_vm_destroy)
|
||||
bo->destroy == &amdgpu_bo_user_destroy)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -583,6 +564,7 @@ int amdgpu_bo_create(struct amdgpu_device *adev,
|
||||
if (bo == NULL)
|
||||
return -ENOMEM;
|
||||
drm_gem_private_object_init(adev_to_drm(adev), &bo->tbo.base, size);
|
||||
bo->tbo.base.funcs = &amdgpu_gem_object_funcs;
|
||||
bo->vm_bo = NULL;
|
||||
bo->preferred_domains = bp->preferred_domain ? bp->preferred_domain :
|
||||
bp->domain;
|
||||
@@ -722,52 +704,6 @@ int amdgpu_bo_create_vm(struct amdgpu_device *adev,
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_add_to_shadow_list - add a BO to the shadow list
|
||||
*
|
||||
* @vmbo: BO that will be inserted into the shadow list
|
||||
*
|
||||
* Insert a BO to the shadow list.
|
||||
*/
|
||||
void amdgpu_bo_add_to_shadow_list(struct amdgpu_bo_vm *vmbo)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(vmbo->bo.tbo.bdev);
|
||||
|
||||
mutex_lock(&adev->shadow_list_lock);
|
||||
list_add_tail(&vmbo->shadow_list, &adev->shadow_list);
|
||||
vmbo->shadow->parent = amdgpu_bo_ref(&vmbo->bo);
|
||||
vmbo->shadow->tbo.destroy = &amdgpu_bo_vm_destroy;
|
||||
mutex_unlock(&adev->shadow_list_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_restore_shadow - restore an &amdgpu_bo shadow
|
||||
*
|
||||
* @shadow: &amdgpu_bo shadow to be restored
|
||||
* @fence: dma_fence associated with the operation
|
||||
*
|
||||
* Copies a buffer object's shadow content back to the object.
|
||||
* This is used for recovering a buffer from its shadow in case of a gpu
|
||||
* reset where vram context may be lost.
|
||||
*
|
||||
* Returns:
|
||||
* 0 for success or a negative error code on failure.
|
||||
*/
|
||||
int amdgpu_bo_restore_shadow(struct amdgpu_bo *shadow, struct dma_fence **fence)
|
||||
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(shadow->tbo.bdev);
|
||||
struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
|
||||
uint64_t shadow_addr, parent_addr;
|
||||
|
||||
shadow_addr = amdgpu_bo_gpu_offset(shadow);
|
||||
parent_addr = amdgpu_bo_gpu_offset(shadow->parent);
|
||||
|
||||
return amdgpu_copy_buffer(ring, shadow_addr, parent_addr,
|
||||
amdgpu_bo_size(shadow), NULL, fence,
|
||||
true, false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_kmap - map an &amdgpu_bo buffer object
|
||||
* @bo: &amdgpu_bo buffer object to be mapped
|
||||
@@ -851,7 +787,7 @@ struct amdgpu_bo *amdgpu_bo_ref(struct amdgpu_bo *bo)
|
||||
if (bo == NULL)
|
||||
return NULL;
|
||||
|
||||
ttm_bo_get(&bo->tbo);
|
||||
drm_gem_object_get(&bo->tbo.base);
|
||||
return bo;
|
||||
}
|
||||
|
||||
@@ -863,40 +799,30 @@ struct amdgpu_bo *amdgpu_bo_ref(struct amdgpu_bo *bo)
|
||||
*/
|
||||
void amdgpu_bo_unref(struct amdgpu_bo **bo)
|
||||
{
|
||||
struct ttm_buffer_object *tbo;
|
||||
|
||||
if ((*bo) == NULL)
|
||||
return;
|
||||
|
||||
tbo = &((*bo)->tbo);
|
||||
ttm_bo_put(tbo);
|
||||
drm_gem_object_put(&(*bo)->tbo.base);
|
||||
*bo = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_pin_restricted - pin an &amdgpu_bo buffer object
|
||||
* amdgpu_bo_pin - pin an &amdgpu_bo buffer object
|
||||
* @bo: &amdgpu_bo buffer object to be pinned
|
||||
* @domain: domain to be pinned to
|
||||
* @min_offset: the start of requested address range
|
||||
* @max_offset: the end of requested address range
|
||||
*
|
||||
* Pins the buffer object according to requested domain and address range. If
|
||||
* the memory is unbound gart memory, binds the pages into gart table. Adjusts
|
||||
* pin_count and pin_size accordingly.
|
||||
* Pins the buffer object according to requested domain. If the memory is
|
||||
* unbound gart memory, binds the pages into gart table. Adjusts pin_count and
|
||||
* pin_size accordingly.
|
||||
*
|
||||
* Pinning means to lock pages in memory along with keeping them at a fixed
|
||||
* offset. It is required when a buffer can not be moved, for example, when
|
||||
* a display buffer is being scanned out.
|
||||
*
|
||||
* Compared with amdgpu_bo_pin(), this function gives more flexibility on
|
||||
* where to pin a buffer if there are specific restrictions on where a buffer
|
||||
* must be located.
|
||||
*
|
||||
* Returns:
|
||||
* 0 for success or a negative error code on failure.
|
||||
*/
|
||||
int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
|
||||
u64 min_offset, u64 max_offset)
|
||||
int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
|
||||
struct ttm_operation_ctx ctx = { false, false };
|
||||
@@ -905,9 +831,6 @@ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
|
||||
if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm))
|
||||
return -EPERM;
|
||||
|
||||
if (WARN_ON_ONCE(min_offset > max_offset))
|
||||
return -EINVAL;
|
||||
|
||||
/* Check domain to be pinned to against preferred domains */
|
||||
if (bo->preferred_domains & domain)
|
||||
domain = bo->preferred_domains & domain;
|
||||
@@ -933,14 +856,6 @@ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
|
||||
return -EINVAL;
|
||||
|
||||
ttm_bo_pin(&bo->tbo);
|
||||
|
||||
if (max_offset != 0) {
|
||||
u64 domain_start = amdgpu_ttm_domain_start(adev,
|
||||
mem_type);
|
||||
WARN_ON_ONCE(max_offset <
|
||||
(amdgpu_bo_gpu_offset(bo) - domain_start));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -957,17 +872,6 @@ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
|
||||
bo->flags |= AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
|
||||
amdgpu_bo_placement_from_domain(bo, domain);
|
||||
for (i = 0; i < bo->placement.num_placement; i++) {
|
||||
unsigned int fpfn, lpfn;
|
||||
|
||||
fpfn = min_offset >> PAGE_SHIFT;
|
||||
lpfn = max_offset >> PAGE_SHIFT;
|
||||
|
||||
if (fpfn > bo->placements[i].fpfn)
|
||||
bo->placements[i].fpfn = fpfn;
|
||||
if (!bo->placements[i].lpfn ||
|
||||
(lpfn && lpfn < bo->placements[i].lpfn))
|
||||
bo->placements[i].lpfn = lpfn;
|
||||
|
||||
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS &&
|
||||
bo->placements[i].mem_type == TTM_PL_VRAM)
|
||||
bo->placements[i].flags |= TTM_PL_FLAG_CONTIGUOUS;
|
||||
@@ -993,24 +897,6 @@ error:
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_pin - pin an &amdgpu_bo buffer object
|
||||
* @bo: &amdgpu_bo buffer object to be pinned
|
||||
* @domain: domain to be pinned to
|
||||
*
|
||||
* A simple wrapper to amdgpu_bo_pin_restricted().
|
||||
* Provides a simpler API for buffers that do not have any strict restrictions
|
||||
* on where a buffer must be located.
|
||||
*
|
||||
* Returns:
|
||||
* 0 for success or a negative error code on failure.
|
||||
*/
|
||||
int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain)
|
||||
{
|
||||
bo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
|
||||
return amdgpu_bo_pin_restricted(bo, domain, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_unpin - unpin an &amdgpu_bo buffer object
|
||||
* @bo: &amdgpu_bo buffer object to be unpinned
|
||||
|
||||
@@ -136,8 +136,6 @@ struct amdgpu_bo_user {
|
||||
|
||||
struct amdgpu_bo_vm {
|
||||
struct amdgpu_bo bo;
|
||||
struct amdgpu_bo *shadow;
|
||||
struct list_head shadow_list;
|
||||
struct amdgpu_vm_bo_base entries[];
|
||||
};
|
||||
|
||||
@@ -275,22 +273,6 @@ static inline bool amdgpu_bo_encrypted(struct amdgpu_bo *bo)
|
||||
return bo->flags & AMDGPU_GEM_CREATE_ENCRYPTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_shadowed - check if the BO is shadowed
|
||||
*
|
||||
* @bo: BO to be tested.
|
||||
*
|
||||
* Returns:
|
||||
* NULL if not shadowed or else return a BO pointer.
|
||||
*/
|
||||
static inline struct amdgpu_bo *amdgpu_bo_shadowed(struct amdgpu_bo *bo)
|
||||
{
|
||||
if (bo->tbo.type == ttm_bo_type_kernel)
|
||||
return to_amdgpu_bo_vm(bo)->shadow;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool amdgpu_bo_is_amdgpu_bo(struct ttm_buffer_object *bo);
|
||||
void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain);
|
||||
|
||||
@@ -322,8 +304,6 @@ void amdgpu_bo_kunmap(struct amdgpu_bo *bo);
|
||||
struct amdgpu_bo *amdgpu_bo_ref(struct amdgpu_bo *bo);
|
||||
void amdgpu_bo_unref(struct amdgpu_bo **bo);
|
||||
int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain);
|
||||
int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
|
||||
u64 min_offset, u64 max_offset);
|
||||
void amdgpu_bo_unpin(struct amdgpu_bo *bo);
|
||||
int amdgpu_bo_init(struct amdgpu_device *adev);
|
||||
void amdgpu_bo_fini(struct amdgpu_device *adev);
|
||||
@@ -349,9 +329,6 @@ u64 amdgpu_bo_gpu_offset(struct amdgpu_bo *bo);
|
||||
u64 amdgpu_bo_gpu_offset_no_check(struct amdgpu_bo *bo);
|
||||
void amdgpu_bo_get_memory(struct amdgpu_bo *bo,
|
||||
struct amdgpu_mem_stats *stats);
|
||||
void amdgpu_bo_add_to_shadow_list(struct amdgpu_bo_vm *vmbo);
|
||||
int amdgpu_bo_restore_shadow(struct amdgpu_bo *shadow,
|
||||
struct dma_fence **fence);
|
||||
uint32_t amdgpu_bo_get_preferred_domain(struct amdgpu_device *adev,
|
||||
uint32_t domain);
|
||||
|
||||
|
||||
@@ -2853,7 +2853,7 @@ static int psp_load_non_psp_fw(struct psp_context *psp)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start rlc autoload after psp recieved all the gfx firmware */
|
||||
/* Start rlc autoload after psp received all the gfx firmware */
|
||||
if (psp->autoload_supported && ucode->ucode_id == (amdgpu_sriov_vf(adev) ?
|
||||
adev->virt.autoload_ucode_id : AMDGPU_UCODE_ID_RLC_G)) {
|
||||
ret = psp_rlc_autoload_start(psp);
|
||||
@@ -3425,9 +3425,11 @@ int psp_init_sos_microcode(struct psp_context *psp, const char *chip_name)
|
||||
const struct psp_firmware_header_v1_2 *sos_hdr_v1_2;
|
||||
const struct psp_firmware_header_v1_3 *sos_hdr_v1_3;
|
||||
const struct psp_firmware_header_v2_0 *sos_hdr_v2_0;
|
||||
int err = 0;
|
||||
const struct psp_firmware_header_v2_1 *sos_hdr_v2_1;
|
||||
int fw_index, fw_bin_count, start_index = 0;
|
||||
const struct psp_fw_bin_desc *fw_bin;
|
||||
uint8_t *ucode_array_start_addr;
|
||||
int fw_index = 0;
|
||||
int err = 0;
|
||||
|
||||
err = amdgpu_ucode_request(adev, &adev->psp.sos_fw, "amdgpu/%s_sos.bin", chip_name);
|
||||
if (err)
|
||||
@@ -3478,15 +3480,30 @@ int psp_init_sos_microcode(struct psp_context *psp, const char *chip_name)
|
||||
case 2:
|
||||
sos_hdr_v2_0 = (const struct psp_firmware_header_v2_0 *)adev->psp.sos_fw->data;
|
||||
|
||||
if (le32_to_cpu(sos_hdr_v2_0->psp_fw_bin_count) >= UCODE_MAX_PSP_PACKAGING) {
|
||||
fw_bin_count = le32_to_cpu(sos_hdr_v2_0->psp_fw_bin_count);
|
||||
|
||||
if (fw_bin_count >= UCODE_MAX_PSP_PACKAGING) {
|
||||
dev_err(adev->dev, "packed SOS count exceeds maximum limit\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (fw_index = 0; fw_index < le32_to_cpu(sos_hdr_v2_0->psp_fw_bin_count); fw_index++) {
|
||||
err = parse_sos_bin_descriptor(psp,
|
||||
&sos_hdr_v2_0->psp_fw_bin[fw_index],
|
||||
if (sos_hdr_v2_0->header.header_version_minor == 1) {
|
||||
sos_hdr_v2_1 = (const struct psp_firmware_header_v2_1 *)adev->psp.sos_fw->data;
|
||||
|
||||
fw_bin = sos_hdr_v2_1->psp_fw_bin;
|
||||
|
||||
if (psp_is_aux_sos_load_required(psp))
|
||||
start_index = le32_to_cpu(sos_hdr_v2_1->psp_aux_fw_bin_index);
|
||||
else
|
||||
fw_bin_count -= le32_to_cpu(sos_hdr_v2_1->psp_aux_fw_bin_index);
|
||||
|
||||
} else {
|
||||
fw_bin = sos_hdr_v2_0->psp_fw_bin;
|
||||
}
|
||||
|
||||
for (fw_index = start_index; fw_index < fw_bin_count; fw_index++) {
|
||||
err = parse_sos_bin_descriptor(psp, fw_bin + fw_index,
|
||||
sos_hdr_v2_0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -138,6 +138,7 @@ struct psp_funcs {
|
||||
int (*vbflash_stat)(struct psp_context *psp);
|
||||
int (*fatal_error_recovery_quirk)(struct psp_context *psp);
|
||||
bool (*get_ras_capability)(struct psp_context *psp);
|
||||
bool (*is_aux_sos_load_required)(struct psp_context *psp);
|
||||
};
|
||||
|
||||
struct ta_funcs {
|
||||
@@ -464,6 +465,9 @@ struct amdgpu_psp_funcs {
|
||||
((psp)->funcs->fatal_error_recovery_quirk ? \
|
||||
(psp)->funcs->fatal_error_recovery_quirk((psp)) : 0)
|
||||
|
||||
#define psp_is_aux_sos_load_required(psp) \
|
||||
((psp)->funcs->is_aux_sos_load_required ? (psp)->funcs->is_aux_sos_load_required((psp)) : 0)
|
||||
|
||||
extern const struct amd_ip_funcs psp_ip_funcs;
|
||||
|
||||
extern const struct amdgpu_ip_block_version psp_v3_1_ip_block;
|
||||
|
||||
@@ -882,7 +882,7 @@ int amdgpu_ras_feature_enable_on_boot(struct amdgpu_device *adev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* gfx block ras dsiable cmd must send to ras-ta */
|
||||
/* gfx block ras disable cmd must send to ras-ta */
|
||||
if (head->block == AMDGPU_RAS_BLOCK__GFX)
|
||||
con->features |= BIT(head->block);
|
||||
|
||||
@@ -3468,6 +3468,11 @@ init_ras_enabled_flag:
|
||||
|
||||
/* aca is disabled by default */
|
||||
adev->aca.is_enabled = false;
|
||||
|
||||
/* bad page feature is not applicable to specific app platform */
|
||||
if (adev->gmc.is_app_apu &&
|
||||
amdgpu_ip_version(adev, UMC_HWIP, 0) == IP_VERSION(12, 0, 0))
|
||||
amdgpu_bad_page_threshold = 0;
|
||||
}
|
||||
|
||||
static void amdgpu_ras_counte_dw(struct work_struct *work)
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
#define EEPROM_I2C_MADDR_4 0x40000
|
||||
|
||||
/*
|
||||
* The 2 macros bellow represent the actual size in bytes that
|
||||
* The 2 macros below represent the actual size in bytes that
|
||||
* those entities occupy in the EEPROM memory.
|
||||
* RAS_TABLE_RECORD_SIZE is different than sizeof(eeprom_table_record) which
|
||||
* uses uint64 to store 6b fields such as retired_page.
|
||||
|
||||
@@ -260,6 +260,36 @@ int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_sync_kfd - sync to KFD fences
|
||||
*
|
||||
* @sync: sync object to add KFD fences to
|
||||
* @resv: reservation object with KFD fences
|
||||
*
|
||||
* Extract all KFD fences and add them to the sync object.
|
||||
*/
|
||||
int amdgpu_sync_kfd(struct amdgpu_sync *sync, struct dma_resv *resv)
|
||||
{
|
||||
struct dma_resv_iter cursor;
|
||||
struct dma_fence *f;
|
||||
int r = 0;
|
||||
|
||||
dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP);
|
||||
dma_resv_for_each_fence_unlocked(&cursor, f) {
|
||||
void *fence_owner = amdgpu_sync_get_owner(f);
|
||||
|
||||
if (fence_owner != AMDGPU_FENCE_OWNER_KFD)
|
||||
continue;
|
||||
|
||||
r = amdgpu_sync_fence(sync, f);
|
||||
if (r)
|
||||
break;
|
||||
}
|
||||
dma_resv_iter_end(&cursor);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Free the entry back to the slab */
|
||||
static void amdgpu_sync_entry_free(struct amdgpu_sync_entry *e)
|
||||
{
|
||||
|
||||
@@ -51,6 +51,7 @@ int amdgpu_sync_fence(struct amdgpu_sync *sync, struct dma_fence *f);
|
||||
int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync,
|
||||
struct dma_resv *resv, enum amdgpu_sync_mode mode,
|
||||
void *owner);
|
||||
int amdgpu_sync_kfd(struct amdgpu_sync *sync, struct dma_resv *resv);
|
||||
struct dma_fence *amdgpu_sync_peek_fence(struct amdgpu_sync *sync,
|
||||
struct amdgpu_ring *ring);
|
||||
struct dma_fence *amdgpu_sync_get_fence(struct amdgpu_sync *sync);
|
||||
|
||||
@@ -1970,7 +1970,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
|
||||
DRM_INFO("amdgpu: %uM of GTT memory ready.\n",
|
||||
(unsigned int)(gtt_size / (1024 * 1024)));
|
||||
|
||||
/* Initiailize doorbell pool on PCI BAR */
|
||||
/* Initialize doorbell pool on PCI BAR */
|
||||
r = amdgpu_ttm_init_on_chip(adev, AMDGPU_PL_DOORBELL, adev->doorbell.size / PAGE_SIZE);
|
||||
if (r) {
|
||||
DRM_ERROR("Failed initializing doorbell heap.\n");
|
||||
|
||||
@@ -136,6 +136,14 @@ struct psp_firmware_header_v2_0 {
|
||||
struct psp_fw_bin_desc psp_fw_bin[];
|
||||
};
|
||||
|
||||
/* version_major=2, version_minor=1 */
|
||||
struct psp_firmware_header_v2_1 {
|
||||
struct common_firmware_header header;
|
||||
uint32_t psp_fw_bin_count;
|
||||
uint32_t psp_aux_fw_bin_index;
|
||||
struct psp_fw_bin_desc psp_fw_bin[];
|
||||
};
|
||||
|
||||
/* version_major=1, version_minor=0 */
|
||||
struct ta_firmware_header_v1_0 {
|
||||
struct common_firmware_header header;
|
||||
@@ -426,6 +434,7 @@ union amdgpu_firmware_header {
|
||||
struct psp_firmware_header_v1_1 psp_v1_1;
|
||||
struct psp_firmware_header_v1_3 psp_v1_3;
|
||||
struct psp_firmware_header_v2_0 psp_v2_0;
|
||||
struct psp_firmware_header_v2_0 psp_v2_1;
|
||||
struct ta_firmware_header_v1_0 ta;
|
||||
struct ta_firmware_header_v2_0 ta_v2_0;
|
||||
struct gfx_firmware_header_v1_0 gfx;
|
||||
@@ -447,7 +456,7 @@ union amdgpu_firmware_header {
|
||||
uint8_t raw[0x100];
|
||||
};
|
||||
|
||||
#define UCODE_MAX_PSP_PACKAGING ((sizeof(union amdgpu_firmware_header) - sizeof(struct common_firmware_header) - 4) / sizeof(struct psp_fw_bin_desc))
|
||||
#define UCODE_MAX_PSP_PACKAGING (((sizeof(union amdgpu_firmware_header) - sizeof(struct common_firmware_header) - 4) / sizeof(struct psp_fw_bin_desc)) * 2)
|
||||
|
||||
/*
|
||||
* fw loading support
|
||||
|
||||
@@ -338,6 +338,7 @@ static int amdgpu_vkms_prepare_fb(struct drm_plane *plane,
|
||||
else
|
||||
domain = AMDGPU_GEM_DOMAIN_VRAM;
|
||||
|
||||
rbo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
|
||||
r = amdgpu_bo_pin(rbo, domain);
|
||||
if (unlikely(r != 0)) {
|
||||
if (r != -ERESTARTSYS)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user