mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
Merge tag 'kvm-s390-next-7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD
KVM: s390: Features and Fixes for 7.3 - merged kvms390/master to pick up additional fixes that came too late for 7.2 - Fixes for vfio-ap - Fixes for the gmap rework - Fixes for vsie - AI triggered fixes all over - diag9c tracing - code move preparation for the additional arm64 support - enable CONTEXT_ANALYSIS - update to vfio maintainer file location
This commit is contained in:
commit
1526a27e79
|
|
@ -112,9 +112,20 @@ Groups:
|
|||
mask or unmask the adapter, as specified in mask
|
||||
|
||||
KVM_S390_IO_ADAPTER_MAP
|
||||
This is now a no-op. The mapping is purely done by the irq route.
|
||||
Map an adapter indicator or summary page for long-term pinning so that
|
||||
interrupt injection can be performed in atomic context. If long-term
|
||||
pinning is not possible (e.g. file-backed memory), the page is verified
|
||||
via a short-term pin and the ioctl returns success; interrupt injection
|
||||
will use the non-atomic irqfd path with short-term pinning on each
|
||||
interrupt. In Secure Execution mode this is a no-op and the ioctl
|
||||
returns success.
|
||||
|
||||
KVM_S390_IO_ADAPTER_UNMAP
|
||||
This is now a no-op. The mapping is purely done by the irq route.
|
||||
Unmap a previously mapped adapter indicator or summary page and release
|
||||
the long-term pin. If the page was not long-term pinned (e.g. file-backed
|
||||
memory), the map entry is removed and success is returned; if no prior
|
||||
map entry exists, -ENOENT is returned. In Secure Execution mode this is
|
||||
a no-op and the ioctl returns success.
|
||||
|
||||
KVM_DEV_FLIC_AISM
|
||||
modify the adapter-interruption-suppression mode for a given isc if the
|
||||
|
|
|
|||
|
|
@ -23890,12 +23890,14 @@ S: Supported
|
|||
F: drivers/s390/block/scm*
|
||||
F: drivers/s390/cio/scm.c
|
||||
|
||||
S390 VFIO AP DRIVER
|
||||
S390 VFIO-AP DRIVER
|
||||
M: Tony Krowiak <akrowiak@linux.ibm.com>
|
||||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Jason Herne <jjherne@linux.ibm.com>
|
||||
L: linux-s390@vger.kernel.org
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
|
||||
F: Documentation/arch/s390/vfio-ap*
|
||||
F: drivers/s390/crypto/vfio_ap*
|
||||
|
||||
|
|
@ -23906,6 +23908,7 @@ R: Halil Pasic <pasic@linux.ibm.com>
|
|||
L: linux-s390@vger.kernel.org
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
|
||||
F: Documentation/arch/s390/vfio-ccw.rst
|
||||
F: drivers/s390/cio/vfio_ccw*
|
||||
F: include/uapi/linux/vfio_ccw.h
|
||||
|
|
@ -23917,7 +23920,8 @@ R: Eric Farman <farman@linux.ibm.com>
|
|||
L: linux-s390@vger.kernel.org
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
F: arch/s390/kvm/pci*
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
|
||||
F: arch/s390/kvm/s390/pci*
|
||||
F: drivers/vfio/pci/vfio_pci_zdev.c
|
||||
F: include/uapi/linux/vfio_zdev.h
|
||||
|
||||
|
|
|
|||
|
|
@ -1,154 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* definition for kernel virtual machines on s390
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2018
|
||||
*
|
||||
* Author(s): Carsten Otte <cotte@de.ibm.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ASM_KVM_HOST_H
|
||||
#define ASM_KVM_HOST_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kvm_types.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mmu_notifier.h>
|
||||
#include <asm/kvm_host_types.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/isc.h>
|
||||
#include <asm/guarded_storage.h>
|
||||
|
||||
#define KVM_HAVE_MMU_RWLOCK
|
||||
#define KVM_MAX_VCPUS 255
|
||||
|
||||
#define KVM_INTERNAL_MEM_SLOTS 1
|
||||
|
||||
/*
|
||||
* These seem to be used for allocating ->chip in the routing table, which we
|
||||
* don't use. 1 is as small as we can get to reduce the needed memory. If we
|
||||
* need to look at ->chip later on, we'll need to revisit this.
|
||||
*/
|
||||
#define KVM_NR_IRQCHIPS 1
|
||||
#define KVM_IRQCHIP_NUM_PINS 1
|
||||
#define KVM_HALT_POLL_NS_DEFAULT 50000
|
||||
|
||||
/* s390-specific vcpu->requests bit members */
|
||||
#define KVM_REQ_ENABLE_IBS KVM_ARCH_REQ(0)
|
||||
#define KVM_REQ_DISABLE_IBS KVM_ARCH_REQ(1)
|
||||
#define KVM_REQ_ICPT_OPEREXC KVM_ARCH_REQ(2)
|
||||
#define KVM_REQ_START_MIGRATION KVM_ARCH_REQ(3)
|
||||
#define KVM_REQ_STOP_MIGRATION KVM_ARCH_REQ(4)
|
||||
#define KVM_REQ_VSIE_RESTART KVM_ARCH_REQ(5)
|
||||
#define KVM_REQ_REFRESH_GUEST_PREFIX \
|
||||
KVM_ARCH_REQ_FLAGS(6, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
|
||||
|
||||
struct kvm_vcpu_stat {
|
||||
struct kvm_vcpu_stat_generic generic;
|
||||
u64 exit_userspace;
|
||||
u64 exit_null;
|
||||
u64 exit_external_request;
|
||||
u64 exit_io_request;
|
||||
u64 exit_external_interrupt;
|
||||
u64 exit_stop_request;
|
||||
u64 exit_validity;
|
||||
u64 exit_instruction;
|
||||
u64 exit_pei;
|
||||
u64 halt_no_poll_steal;
|
||||
u64 instruction_lctl;
|
||||
u64 instruction_lctlg;
|
||||
u64 instruction_stctl;
|
||||
u64 instruction_stctg;
|
||||
u64 exit_program_interruption;
|
||||
u64 exit_instr_and_program;
|
||||
u64 exit_operation_exception;
|
||||
u64 deliver_ckc;
|
||||
u64 deliver_cputm;
|
||||
u64 deliver_external_call;
|
||||
u64 deliver_emergency_signal;
|
||||
u64 deliver_service_signal;
|
||||
u64 deliver_virtio;
|
||||
u64 deliver_stop_signal;
|
||||
u64 deliver_prefix_signal;
|
||||
u64 deliver_restart_signal;
|
||||
u64 deliver_program;
|
||||
u64 deliver_io;
|
||||
u64 deliver_machine_check;
|
||||
u64 exit_wait_state;
|
||||
u64 inject_ckc;
|
||||
u64 inject_cputm;
|
||||
u64 inject_external_call;
|
||||
u64 inject_emergency_signal;
|
||||
u64 inject_mchk;
|
||||
u64 inject_pfault_init;
|
||||
u64 inject_program;
|
||||
u64 inject_restart;
|
||||
u64 inject_set_prefix;
|
||||
u64 inject_stop_signal;
|
||||
u64 instruction_epsw;
|
||||
u64 instruction_gs;
|
||||
u64 instruction_io_other;
|
||||
u64 instruction_lpsw;
|
||||
u64 instruction_lpswe;
|
||||
u64 instruction_lpswey;
|
||||
u64 instruction_pfmf;
|
||||
u64 instruction_ptff;
|
||||
u64 instruction_sck;
|
||||
u64 instruction_sckpf;
|
||||
u64 instruction_stidp;
|
||||
u64 instruction_spx;
|
||||
u64 instruction_stpx;
|
||||
u64 instruction_stap;
|
||||
u64 instruction_iske;
|
||||
u64 instruction_ri;
|
||||
u64 instruction_rrbe;
|
||||
u64 instruction_sske;
|
||||
u64 instruction_ipte_interlock;
|
||||
u64 instruction_stsi;
|
||||
u64 instruction_stfl;
|
||||
u64 instruction_tb;
|
||||
u64 instruction_tpi;
|
||||
u64 instruction_tprot;
|
||||
u64 instruction_tsch;
|
||||
u64 instruction_sie;
|
||||
u64 instruction_essa;
|
||||
u64 instruction_sthyi;
|
||||
u64 instruction_sigp_sense;
|
||||
u64 instruction_sigp_sense_running;
|
||||
u64 instruction_sigp_external_call;
|
||||
u64 instruction_sigp_emergency;
|
||||
u64 instruction_sigp_cond_emergency;
|
||||
u64 instruction_sigp_start;
|
||||
u64 instruction_sigp_stop;
|
||||
u64 instruction_sigp_stop_store_status;
|
||||
u64 instruction_sigp_store_status;
|
||||
u64 instruction_sigp_store_adtl_status;
|
||||
u64 instruction_sigp_arch;
|
||||
u64 instruction_sigp_prefix;
|
||||
u64 instruction_sigp_restart;
|
||||
u64 instruction_sigp_init_cpu_reset;
|
||||
u64 instruction_sigp_cpu_reset;
|
||||
u64 instruction_sigp_unknown;
|
||||
u64 instruction_diagnose_10;
|
||||
u64 instruction_diagnose_44;
|
||||
u64 instruction_diagnose_9c;
|
||||
u64 diag_9c_ignored;
|
||||
u64 diag_9c_forward;
|
||||
u64 instruction_diagnose_258;
|
||||
u64 instruction_diagnose_308;
|
||||
u64 instruction_diagnose_500;
|
||||
u64 instruction_diagnose_other;
|
||||
u64 pfault_sync;
|
||||
u64 signal_exits;
|
||||
};
|
||||
#include <asm/kvm_host_s390.h>
|
||||
|
||||
#define PGM_OPERATION 0x01
|
||||
#define PGM_PRIVILEGED_OP 0x02
|
||||
|
|
@ -207,572 +62,4 @@ struct kvm_vcpu_stat {
|
|||
#define PGM_PER 0x80
|
||||
#define PGM_CRYPTO_OPERATION 0x119
|
||||
|
||||
/* irq types in ascend order of priorities */
|
||||
enum irq_types {
|
||||
IRQ_PEND_SET_PREFIX = 0,
|
||||
IRQ_PEND_RESTART,
|
||||
IRQ_PEND_SIGP_STOP,
|
||||
IRQ_PEND_IO_ISC_7,
|
||||
IRQ_PEND_IO_ISC_6,
|
||||
IRQ_PEND_IO_ISC_5,
|
||||
IRQ_PEND_IO_ISC_4,
|
||||
IRQ_PEND_IO_ISC_3,
|
||||
IRQ_PEND_IO_ISC_2,
|
||||
IRQ_PEND_IO_ISC_1,
|
||||
IRQ_PEND_IO_ISC_0,
|
||||
IRQ_PEND_VIRTIO,
|
||||
IRQ_PEND_PFAULT_DONE,
|
||||
IRQ_PEND_PFAULT_INIT,
|
||||
IRQ_PEND_EXT_HOST,
|
||||
IRQ_PEND_EXT_SERVICE,
|
||||
IRQ_PEND_EXT_SERVICE_EV,
|
||||
IRQ_PEND_EXT_TIMING,
|
||||
IRQ_PEND_EXT_CPU_TIMER,
|
||||
IRQ_PEND_EXT_CLOCK_COMP,
|
||||
IRQ_PEND_EXT_EXTERNAL,
|
||||
IRQ_PEND_EXT_EMERGENCY,
|
||||
IRQ_PEND_EXT_MALFUNC,
|
||||
IRQ_PEND_EXT_IRQ_KEY,
|
||||
IRQ_PEND_MCHK_REP,
|
||||
IRQ_PEND_PROG,
|
||||
IRQ_PEND_SVC,
|
||||
IRQ_PEND_MCHK_EX,
|
||||
IRQ_PEND_COUNT
|
||||
};
|
||||
|
||||
/* We have 2M for virtio device descriptor pages. Smallest amount of
|
||||
* memory per page is 24 bytes (1 queue), so (2048*1024) / 24 = 87381
|
||||
*/
|
||||
#define KVM_S390_MAX_VIRTIO_IRQS 87381
|
||||
|
||||
/*
|
||||
* Repressible (non-floating) machine check interrupts
|
||||
* subclass bits in MCIC
|
||||
*/
|
||||
#define MCHK_EXTD_BIT 58
|
||||
#define MCHK_DEGR_BIT 56
|
||||
#define MCHK_WARN_BIT 55
|
||||
#define MCHK_REP_MASK ((1UL << MCHK_DEGR_BIT) | \
|
||||
(1UL << MCHK_EXTD_BIT) | \
|
||||
(1UL << MCHK_WARN_BIT))
|
||||
|
||||
/* Exigent machine check interrupts subclass bits in MCIC */
|
||||
#define MCHK_SD_BIT 63
|
||||
#define MCHK_PD_BIT 62
|
||||
#define MCHK_EX_MASK ((1UL << MCHK_SD_BIT) | (1UL << MCHK_PD_BIT))
|
||||
|
||||
#define IRQ_PEND_EXT_MASK ((1UL << IRQ_PEND_EXT_IRQ_KEY) | \
|
||||
(1UL << IRQ_PEND_EXT_CLOCK_COMP) | \
|
||||
(1UL << IRQ_PEND_EXT_CPU_TIMER) | \
|
||||
(1UL << IRQ_PEND_EXT_MALFUNC) | \
|
||||
(1UL << IRQ_PEND_EXT_EMERGENCY) | \
|
||||
(1UL << IRQ_PEND_EXT_EXTERNAL) | \
|
||||
(1UL << IRQ_PEND_EXT_TIMING) | \
|
||||
(1UL << IRQ_PEND_EXT_HOST) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE_EV) | \
|
||||
(1UL << IRQ_PEND_VIRTIO) | \
|
||||
(1UL << IRQ_PEND_PFAULT_INIT) | \
|
||||
(1UL << IRQ_PEND_PFAULT_DONE))
|
||||
|
||||
#define IRQ_PEND_IO_MASK ((1UL << IRQ_PEND_IO_ISC_0) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_1) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_2) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_3) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_4) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_5) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_6) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_7))
|
||||
|
||||
#define IRQ_PEND_MCHK_MASK ((1UL << IRQ_PEND_MCHK_REP) | \
|
||||
(1UL << IRQ_PEND_MCHK_EX))
|
||||
|
||||
#define IRQ_PEND_EXT_II_MASK ((1UL << IRQ_PEND_EXT_CPU_TIMER) | \
|
||||
(1UL << IRQ_PEND_EXT_CLOCK_COMP) | \
|
||||
(1UL << IRQ_PEND_EXT_EMERGENCY) | \
|
||||
(1UL << IRQ_PEND_EXT_EXTERNAL) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE_EV))
|
||||
|
||||
struct kvm_s390_interrupt_info {
|
||||
struct list_head list;
|
||||
u64 type;
|
||||
union {
|
||||
struct kvm_s390_io_info io;
|
||||
struct kvm_s390_ext_info ext;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
struct kvm_s390_emerg_info emerg;
|
||||
struct kvm_s390_extcall_info extcall;
|
||||
struct kvm_s390_prefix_info prefix;
|
||||
struct kvm_s390_stop_info stop;
|
||||
struct kvm_s390_mchk_info mchk;
|
||||
};
|
||||
};
|
||||
|
||||
struct kvm_s390_irq_payload {
|
||||
struct kvm_s390_io_info io;
|
||||
struct kvm_s390_ext_info ext;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
struct kvm_s390_emerg_info emerg;
|
||||
struct kvm_s390_extcall_info extcall;
|
||||
struct kvm_s390_prefix_info prefix;
|
||||
struct kvm_s390_stop_info stop;
|
||||
struct kvm_s390_mchk_info mchk;
|
||||
};
|
||||
|
||||
struct kvm_s390_local_interrupt {
|
||||
spinlock_t lock;
|
||||
DECLARE_BITMAP(sigp_emerg_pending, KVM_MAX_VCPUS);
|
||||
struct kvm_s390_irq_payload irq;
|
||||
unsigned long pending_irqs;
|
||||
};
|
||||
|
||||
#define FIRQ_LIST_IO_ISC_0 0
|
||||
#define FIRQ_LIST_IO_ISC_1 1
|
||||
#define FIRQ_LIST_IO_ISC_2 2
|
||||
#define FIRQ_LIST_IO_ISC_3 3
|
||||
#define FIRQ_LIST_IO_ISC_4 4
|
||||
#define FIRQ_LIST_IO_ISC_5 5
|
||||
#define FIRQ_LIST_IO_ISC_6 6
|
||||
#define FIRQ_LIST_IO_ISC_7 7
|
||||
#define FIRQ_LIST_PFAULT 8
|
||||
#define FIRQ_LIST_VIRTIO 9
|
||||
#define FIRQ_LIST_COUNT 10
|
||||
#define FIRQ_CNTR_IO 0
|
||||
#define FIRQ_CNTR_SERVICE 1
|
||||
#define FIRQ_CNTR_VIRTIO 2
|
||||
#define FIRQ_CNTR_PFAULT 3
|
||||
#define FIRQ_MAX_COUNT 4
|
||||
|
||||
/* mask the AIS mode for a given ISC */
|
||||
#define AIS_MODE_MASK(isc) (0x80 >> isc)
|
||||
|
||||
#define KVM_S390_AIS_MODE_ALL 0
|
||||
#define KVM_S390_AIS_MODE_SINGLE 1
|
||||
|
||||
struct kvm_s390_float_interrupt {
|
||||
unsigned long pending_irqs;
|
||||
unsigned long masked_irqs;
|
||||
spinlock_t lock;
|
||||
struct list_head lists[FIRQ_LIST_COUNT];
|
||||
int counters[FIRQ_MAX_COUNT];
|
||||
struct kvm_s390_mchk_info mchk;
|
||||
struct kvm_s390_ext_info srv_signal;
|
||||
int last_sleep_cpu;
|
||||
spinlock_t ais_lock;
|
||||
u8 simm;
|
||||
u8 nimm;
|
||||
};
|
||||
|
||||
struct kvm_hw_wp_info_arch {
|
||||
unsigned long addr;
|
||||
unsigned long phys_addr;
|
||||
int len;
|
||||
char *old_data;
|
||||
};
|
||||
|
||||
struct kvm_hw_bp_info_arch {
|
||||
unsigned long addr;
|
||||
int len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Only the upper 16 bits of kvm_guest_debug->control are arch specific.
|
||||
* Further KVM_GUESTDBG flags which an be used from userspace can be found in
|
||||
* arch/s390/include/uapi/asm/kvm.h
|
||||
*/
|
||||
#define KVM_GUESTDBG_EXIT_PENDING 0x10000000
|
||||
|
||||
#define guestdbg_enabled(vcpu) \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_ENABLE)
|
||||
#define guestdbg_sstep_enabled(vcpu) \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
|
||||
#define guestdbg_hw_bp_enabled(vcpu) \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
|
||||
#define guestdbg_exit_pending(vcpu) (guestdbg_enabled(vcpu) && \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_EXIT_PENDING))
|
||||
|
||||
#define KVM_GUESTDBG_VALID_MASK \
|
||||
(KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP |\
|
||||
KVM_GUESTDBG_USE_HW_BP | KVM_GUESTDBG_EXIT_PENDING)
|
||||
|
||||
struct kvm_guestdbg_info_arch {
|
||||
unsigned long cr0;
|
||||
unsigned long cr9;
|
||||
unsigned long cr10;
|
||||
unsigned long cr11;
|
||||
struct kvm_hw_bp_info_arch *hw_bp_info;
|
||||
struct kvm_hw_wp_info_arch *hw_wp_info;
|
||||
int nr_hw_bp;
|
||||
int nr_hw_wp;
|
||||
unsigned long last_bp;
|
||||
};
|
||||
|
||||
struct kvm_s390_pv_vcpu {
|
||||
u64 handle;
|
||||
unsigned long stor_base;
|
||||
};
|
||||
|
||||
struct kvm_vcpu_arch {
|
||||
struct kvm_s390_sie_block *sie_block;
|
||||
/* if vsie is active, currently executed shadow sie control block */
|
||||
struct kvm_s390_sie_block *vsie_block;
|
||||
unsigned int host_acrs[NUM_ACRS];
|
||||
struct gs_cb *host_gscb;
|
||||
struct kvm_s390_local_interrupt local_int;
|
||||
struct hrtimer ckc_timer;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
struct gmap *gmap;
|
||||
struct kvm_guestdbg_info_arch guestdbg;
|
||||
unsigned long pfault_token;
|
||||
unsigned long pfault_select;
|
||||
unsigned long pfault_compare;
|
||||
bool cputm_enabled;
|
||||
/*
|
||||
* The seqcount protects updates to cputm_start and sie_block.cputm,
|
||||
* this way we can have non-blocking reads with consistent values.
|
||||
* Only the owning VCPU thread (vcpu->cpu) is allowed to change these
|
||||
* values and to start/stop/enable/disable cpu timer accounting.
|
||||
*/
|
||||
seqcount_t cputm_seqcount;
|
||||
__u64 cputm_start;
|
||||
bool gs_enabled;
|
||||
bool skey_enabled;
|
||||
/* Indicator if the access registers have been loaded from guest */
|
||||
bool acrs_loaded;
|
||||
struct kvm_s390_pv_vcpu pv;
|
||||
union diag318_info diag318_info;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
struct kvm_vm_stat {
|
||||
struct kvm_vm_stat_generic generic;
|
||||
u64 inject_io;
|
||||
u64 io_390_adapter_map;
|
||||
u64 io_390_adapter_unmap;
|
||||
u64 io_390_inatomic;
|
||||
u64 io_flic_inject_airq;
|
||||
u64 io_set_adapter_int;
|
||||
u64 io_390_inatomic_no_inject;
|
||||
u64 inject_float_mchk;
|
||||
u64 inject_pfault_done;
|
||||
u64 inject_service_signal;
|
||||
u64 inject_virtio;
|
||||
u64 aen_forward;
|
||||
u64 gmap_shadow_create;
|
||||
u64 gmap_shadow_reuse;
|
||||
u64 gmap_shadow_r1_entry;
|
||||
u64 gmap_shadow_r2_entry;
|
||||
u64 gmap_shadow_r3_entry;
|
||||
u64 gmap_shadow_sg_entry;
|
||||
u64 gmap_shadow_pg_entry;
|
||||
};
|
||||
|
||||
struct kvm_arch_memory_slot {
|
||||
};
|
||||
|
||||
struct s390_map_info {
|
||||
struct list_head list;
|
||||
__u64 guest_addr;
|
||||
__u64 addr;
|
||||
struct page *page;
|
||||
};
|
||||
|
||||
struct s390_io_adapter {
|
||||
unsigned int id;
|
||||
int isc;
|
||||
bool maskable;
|
||||
bool masked;
|
||||
bool swap;
|
||||
bool suppressible;
|
||||
spinlock_t maps_lock;
|
||||
struct list_head maps;
|
||||
unsigned int nr_maps;
|
||||
};
|
||||
|
||||
#define MAX_S390_IO_ADAPTERS ((MAX_ISC + 1) * 8)
|
||||
#define MAX_S390_ADAPTER_MAPS 256
|
||||
|
||||
/* maximum size of facilities and facility mask is 2k bytes */
|
||||
#define S390_ARCH_FAC_LIST_SIZE_BYTE (1<<11)
|
||||
#define S390_ARCH_FAC_LIST_SIZE_U64 \
|
||||
(S390_ARCH_FAC_LIST_SIZE_BYTE / sizeof(u64))
|
||||
#define S390_ARCH_FAC_MASK_SIZE_BYTE S390_ARCH_FAC_LIST_SIZE_BYTE
|
||||
#define S390_ARCH_FAC_MASK_SIZE_U64 \
|
||||
(S390_ARCH_FAC_MASK_SIZE_BYTE / sizeof(u64))
|
||||
|
||||
struct kvm_s390_cpu_model {
|
||||
/* facility mask supported by kvm & hosting machine */
|
||||
__u64 fac_mask[S390_ARCH_FAC_MASK_SIZE_U64];
|
||||
struct kvm_s390_vm_cpu_subfunc subfuncs;
|
||||
/* facility list requested by guest (in dma page) */
|
||||
__u64 *fac_list;
|
||||
u64 cpuid;
|
||||
unsigned short ibc;
|
||||
/* subset of available UV-features for pv-guests enabled by user space */
|
||||
struct kvm_s390_vm_cpu_uv_feat uv_feat_guest;
|
||||
};
|
||||
|
||||
#define S390_ARCH_FAC_FORMAT_2 2
|
||||
struct kvm_s390_flcb2 {
|
||||
union {
|
||||
struct {
|
||||
u8 reserved0[7];
|
||||
u8 length;
|
||||
};
|
||||
u64 header_val;
|
||||
};
|
||||
u64 facilities[S390_ARCH_FAC_LIST_SIZE_U64];
|
||||
};
|
||||
|
||||
typedef int (*crypto_hook)(struct kvm_vcpu *vcpu);
|
||||
|
||||
struct kvm_s390_crypto {
|
||||
struct kvm_s390_crypto_cb *crycb;
|
||||
struct rw_semaphore pqap_hook_rwsem;
|
||||
crypto_hook *pqap_hook;
|
||||
__u32 crycbd;
|
||||
__u8 aes_kw;
|
||||
__u8 dea_kw;
|
||||
__u8 apie;
|
||||
};
|
||||
|
||||
#define APCB0_MASK_SIZE 1
|
||||
struct kvm_s390_apcb0 {
|
||||
__u64 apm[APCB0_MASK_SIZE]; /* 0x0000 */
|
||||
__u64 aqm[APCB0_MASK_SIZE]; /* 0x0008 */
|
||||
__u64 adm[APCB0_MASK_SIZE]; /* 0x0010 */
|
||||
__u64 reserved18; /* 0x0018 */
|
||||
};
|
||||
|
||||
#define APCB1_MASK_SIZE 4
|
||||
struct kvm_s390_apcb1 {
|
||||
__u64 apm[APCB1_MASK_SIZE]; /* 0x0000 */
|
||||
__u64 aqm[APCB1_MASK_SIZE]; /* 0x0020 */
|
||||
__u64 adm[APCB1_MASK_SIZE]; /* 0x0040 */
|
||||
__u64 reserved60[4]; /* 0x0060 */
|
||||
};
|
||||
|
||||
struct kvm_s390_crypto_cb {
|
||||
struct kvm_s390_apcb0 apcb0; /* 0x0000 */
|
||||
__u8 reserved20[0x0048 - 0x0020]; /* 0x0020 */
|
||||
__u8 dea_wrapping_key_mask[24]; /* 0x0048 */
|
||||
__u8 aes_wrapping_key_mask[32]; /* 0x0060 */
|
||||
struct kvm_s390_apcb1 apcb1; /* 0x0080 */
|
||||
};
|
||||
|
||||
struct kvm_s390_gisa {
|
||||
union {
|
||||
struct { /* common to all formats */
|
||||
u32 next_alert;
|
||||
u8 ipm;
|
||||
u8 reserved01[2];
|
||||
u8 iam;
|
||||
};
|
||||
struct { /* format 0 */
|
||||
u32 next_alert;
|
||||
u8 ipm;
|
||||
u8 reserved01;
|
||||
u8 : 6;
|
||||
u8 g : 1;
|
||||
u8 c : 1;
|
||||
u8 iam;
|
||||
u8 reserved02[4];
|
||||
u32 airq_count;
|
||||
} g0;
|
||||
struct { /* format 1 */
|
||||
u32 next_alert;
|
||||
u8 ipm;
|
||||
u8 simm;
|
||||
u8 nimm;
|
||||
u8 iam;
|
||||
u8 aism[8];
|
||||
u8 : 6;
|
||||
u8 g : 1;
|
||||
u8 c : 1;
|
||||
u8 reserved03[11];
|
||||
u32 airq_count;
|
||||
} g1;
|
||||
struct {
|
||||
u64 word[4];
|
||||
} u64;
|
||||
};
|
||||
};
|
||||
|
||||
struct kvm_s390_gib {
|
||||
u32 alert_list_origin;
|
||||
u32 reserved01;
|
||||
u8:5;
|
||||
u8 nisc:3;
|
||||
u8 reserved03[3];
|
||||
u32 reserved04[5];
|
||||
};
|
||||
|
||||
/*
|
||||
* sie_page2 has to be allocated as DMA because fac_list, crycb and
|
||||
* gisa need 31bit addresses in the sie control block.
|
||||
*/
|
||||
struct sie_page2 {
|
||||
__u64 fac_list[S390_ARCH_FAC_LIST_SIZE_U64]; /* 0x0000 */
|
||||
struct kvm_s390_crypto_cb crycb; /* 0x0800 */
|
||||
struct kvm_s390_gisa gisa; /* 0x0900 */
|
||||
struct kvm *kvm; /* 0x0920 */
|
||||
u8 reserved928[0x1000 - 0x928]; /* 0x0928 */
|
||||
};
|
||||
|
||||
struct vsie_page;
|
||||
|
||||
struct kvm_s390_vsie {
|
||||
struct mutex mutex;
|
||||
struct radix_tree_root addr_to_page;
|
||||
int page_count;
|
||||
int next;
|
||||
struct vsie_page *pages[KVM_MAX_VCPUS];
|
||||
};
|
||||
|
||||
struct kvm_s390_gisa_iam {
|
||||
u8 mask;
|
||||
spinlock_t ref_lock;
|
||||
u32 ref_count[MAX_ISC + 1];
|
||||
};
|
||||
|
||||
struct kvm_s390_gisa_interrupt {
|
||||
struct kvm_s390_gisa *origin;
|
||||
struct kvm_s390_gisa_iam alert;
|
||||
struct hrtimer timer;
|
||||
u64 expires;
|
||||
DECLARE_BITMAP(kicked_mask, KVM_MAX_VCPUS);
|
||||
};
|
||||
|
||||
struct kvm_s390_pv {
|
||||
u64 handle;
|
||||
u64 guest_len;
|
||||
unsigned long stor_base;
|
||||
void *stor_var;
|
||||
bool dumping;
|
||||
void *set_aside;
|
||||
struct list_head need_cleanup;
|
||||
struct mmu_notifier mmu_notifier;
|
||||
/* Protects against concurrent import-like operations */
|
||||
struct mutex import_lock;
|
||||
};
|
||||
|
||||
struct kvm_s390_mmu_cache;
|
||||
|
||||
struct kvm_arch {
|
||||
struct esca_block *sca;
|
||||
debug_info_t *dbf;
|
||||
struct kvm_s390_float_interrupt float_int;
|
||||
struct kvm_device *flic;
|
||||
struct gmap *gmap;
|
||||
unsigned long mem_limit;
|
||||
int css_support;
|
||||
int use_irqchip;
|
||||
int use_cmma;
|
||||
int use_pfmfi;
|
||||
int use_skf;
|
||||
int use_zpci_interp;
|
||||
int user_cpu_state_ctrl;
|
||||
int user_sigp;
|
||||
int user_stsi;
|
||||
int user_instr0;
|
||||
int user_operexec;
|
||||
int allow_vsie_esamode;
|
||||
struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
|
||||
wait_queue_head_t ipte_wq;
|
||||
int ipte_lock_count;
|
||||
struct mutex ipte_mutex;
|
||||
spinlock_t start_stop_lock;
|
||||
struct sie_page2 *sie_page2;
|
||||
struct kvm_s390_cpu_model model;
|
||||
struct kvm_s390_crypto crypto;
|
||||
struct kvm_s390_vsie vsie;
|
||||
u8 epdx;
|
||||
u64 epoch;
|
||||
int migration_mode;
|
||||
atomic64_t cmma_dirty_pages;
|
||||
/* subset of available cpu features enabled by user space */
|
||||
DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
|
||||
/* indexed by vcpu_idx */
|
||||
DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS);
|
||||
struct kvm_s390_gisa_interrupt gisa_int;
|
||||
struct kvm_s390_pv pv;
|
||||
struct list_head kzdev_list;
|
||||
spinlock_t kzdev_list_lock;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
#define KVM_HVA_ERR_BAD (-1UL)
|
||||
#define KVM_HVA_ERR_RO_BAD (-2UL)
|
||||
|
||||
static inline bool kvm_is_error_hva(unsigned long addr)
|
||||
{
|
||||
return IS_ERR_VALUE(addr);
|
||||
}
|
||||
|
||||
#define ASYNC_PF_PER_VCPU 64
|
||||
struct kvm_arch_async_pf {
|
||||
unsigned long pfault_token;
|
||||
};
|
||||
|
||||
bool kvm_arch_can_dequeue_async_page_present(struct kvm_vcpu *vcpu);
|
||||
|
||||
void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work);
|
||||
|
||||
bool kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work);
|
||||
|
||||
void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work);
|
||||
|
||||
static inline void kvm_arch_async_page_present_queued(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
void kvm_arch_crypto_clear_masks(struct kvm *kvm);
|
||||
void kvm_arch_crypto_set_masks(struct kvm *kvm, unsigned long *apm,
|
||||
unsigned long *aqm, unsigned long *adm);
|
||||
|
||||
#define SIE64_RETURN_NORMAL 0
|
||||
#define SIE64_RETURN_MCCK 1
|
||||
|
||||
int __sie64a(phys_addr_t sie_block_phys, struct kvm_s390_sie_block *sie_block, u64 *rsa,
|
||||
unsigned long gasce);
|
||||
|
||||
static inline int sie64a(struct kvm_s390_sie_block *sie_block, u64 *rsa, unsigned long gasce)
|
||||
{
|
||||
return __sie64a(virt_to_phys(sie_block), sie_block, rsa, gasce);
|
||||
}
|
||||
|
||||
extern char sie_exit;
|
||||
|
||||
bool kvm_s390_pv_is_protected(struct kvm *kvm);
|
||||
bool kvm_s390_pv_cpu_is_protected(struct kvm_vcpu *vcpu);
|
||||
|
||||
extern int kvm_s390_enter_exit_sie(struct kvm_s390_sie_block *scb,
|
||||
u64 *gprs, unsigned long gasce);
|
||||
|
||||
extern int kvm_s390_gisc_register(struct kvm *kvm, u32 gisc);
|
||||
extern int kvm_s390_gisc_unregister(struct kvm *kvm, u32 gisc);
|
||||
|
||||
bool kvm_s390_is_gpa_in_memslot(struct kvm *kvm, gpa_t gpa);
|
||||
|
||||
static inline void kvm_arch_free_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot) {}
|
||||
static inline void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) {}
|
||||
static inline void kvm_arch_flush_shadow_all(struct kvm *kvm) {}
|
||||
static inline void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot) {}
|
||||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
#define __KVM_HAVE_ARCH_VM_FREE
|
||||
void kvm_arch_free_vm(struct kvm *kvm);
|
||||
|
||||
struct zpci_kvm_hook {
|
||||
int (*kvm_register)(void *opaque, struct kvm *kvm);
|
||||
void (*kvm_unregister)(void *opaque);
|
||||
};
|
||||
|
||||
extern struct zpci_kvm_hook zpci_kvm_hook;
|
||||
|
||||
#endif
|
||||
#endif /* ASM_KVM_HOST_H */
|
||||
|
|
|
|||
729
arch/s390/include/asm/kvm_host_s390.h
Normal file
729
arch/s390/include/asm/kvm_host_s390.h
Normal file
|
|
@ -0,0 +1,729 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* definition for kernel virtual machines on s390
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2018
|
||||
*
|
||||
* Author(s): Carsten Otte <cotte@de.ibm.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ASM_KVM_HOST_S390_H
|
||||
#define ASM_KVM_HOST_S390_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kvm_types.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mmu_notifier.h>
|
||||
#include <asm/kvm_host_types.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/isc.h>
|
||||
#include <asm/guarded_storage.h>
|
||||
|
||||
#define KVM_HAVE_MMU_RWLOCK
|
||||
#define KVM_MAX_VCPUS 255
|
||||
|
||||
#define KVM_INTERNAL_MEM_SLOTS 1
|
||||
|
||||
#define KVM_S390_MANAGES_S390_GUEST 1
|
||||
|
||||
/*
|
||||
* These seem to be used for allocating ->chip in the routing table, which we
|
||||
* don't use. 1 is as small as we can get to reduce the needed memory. If we
|
||||
* need to look at ->chip later on, we'll need to revisit this.
|
||||
*/
|
||||
#define KVM_NR_IRQCHIPS 1
|
||||
#define KVM_IRQCHIP_NUM_PINS 1
|
||||
#define KVM_HALT_POLL_NS_DEFAULT 50000
|
||||
|
||||
/* s390-specific vcpu->requests bit members */
|
||||
#define KVM_REQ_ENABLE_IBS KVM_ARCH_REQ(0)
|
||||
#define KVM_REQ_DISABLE_IBS KVM_ARCH_REQ(1)
|
||||
#define KVM_REQ_ICPT_OPEREXC KVM_ARCH_REQ(2)
|
||||
#define KVM_REQ_START_MIGRATION KVM_ARCH_REQ(3)
|
||||
#define KVM_REQ_STOP_MIGRATION KVM_ARCH_REQ(4)
|
||||
#define KVM_REQ_VSIE_RESTART KVM_ARCH_REQ(5)
|
||||
#define KVM_REQ_REFRESH_GUEST_PREFIX \
|
||||
KVM_ARCH_REQ_FLAGS(6, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
|
||||
|
||||
struct kvm_vcpu_stat {
|
||||
struct kvm_vcpu_stat_generic generic;
|
||||
u64 exit_userspace;
|
||||
u64 exit_null;
|
||||
u64 exit_external_request;
|
||||
u64 exit_io_request;
|
||||
u64 exit_external_interrupt;
|
||||
u64 exit_stop_request;
|
||||
u64 exit_validity;
|
||||
u64 exit_instruction;
|
||||
u64 exit_pei;
|
||||
u64 halt_no_poll_steal;
|
||||
u64 instruction_lctl;
|
||||
u64 instruction_lctlg;
|
||||
u64 instruction_stctl;
|
||||
u64 instruction_stctg;
|
||||
u64 exit_program_interruption;
|
||||
u64 exit_instr_and_program;
|
||||
u64 exit_operation_exception;
|
||||
u64 deliver_ckc;
|
||||
u64 deliver_cputm;
|
||||
u64 deliver_external_call;
|
||||
u64 deliver_emergency_signal;
|
||||
u64 deliver_service_signal;
|
||||
u64 deliver_virtio;
|
||||
u64 deliver_stop_signal;
|
||||
u64 deliver_prefix_signal;
|
||||
u64 deliver_restart_signal;
|
||||
u64 deliver_program;
|
||||
u64 deliver_io;
|
||||
u64 deliver_machine_check;
|
||||
u64 exit_wait_state;
|
||||
u64 inject_ckc;
|
||||
u64 inject_cputm;
|
||||
u64 inject_external_call;
|
||||
u64 inject_emergency_signal;
|
||||
u64 inject_mchk;
|
||||
u64 inject_pfault_init;
|
||||
u64 inject_program;
|
||||
u64 inject_restart;
|
||||
u64 inject_set_prefix;
|
||||
u64 inject_stop_signal;
|
||||
u64 instruction_epsw;
|
||||
u64 instruction_gs;
|
||||
u64 instruction_io_other;
|
||||
u64 instruction_lpsw;
|
||||
u64 instruction_lpswe;
|
||||
u64 instruction_lpswey;
|
||||
u64 instruction_pfmf;
|
||||
u64 instruction_ptff;
|
||||
u64 instruction_sck;
|
||||
u64 instruction_sckpf;
|
||||
u64 instruction_stidp;
|
||||
u64 instruction_spx;
|
||||
u64 instruction_stpx;
|
||||
u64 instruction_stap;
|
||||
u64 instruction_iske;
|
||||
u64 instruction_ri;
|
||||
u64 instruction_rrbe;
|
||||
u64 instruction_sske;
|
||||
u64 instruction_ipte_interlock;
|
||||
u64 instruction_stsi;
|
||||
u64 instruction_stfl;
|
||||
u64 instruction_tb;
|
||||
u64 instruction_tpi;
|
||||
u64 instruction_tprot;
|
||||
u64 instruction_tsch;
|
||||
u64 instruction_sie;
|
||||
u64 instruction_essa;
|
||||
u64 instruction_sthyi;
|
||||
u64 instruction_sigp_sense;
|
||||
u64 instruction_sigp_sense_running;
|
||||
u64 instruction_sigp_external_call;
|
||||
u64 instruction_sigp_emergency;
|
||||
u64 instruction_sigp_cond_emergency;
|
||||
u64 instruction_sigp_start;
|
||||
u64 instruction_sigp_stop;
|
||||
u64 instruction_sigp_stop_store_status;
|
||||
u64 instruction_sigp_store_status;
|
||||
u64 instruction_sigp_store_adtl_status;
|
||||
u64 instruction_sigp_arch;
|
||||
u64 instruction_sigp_prefix;
|
||||
u64 instruction_sigp_restart;
|
||||
u64 instruction_sigp_init_cpu_reset;
|
||||
u64 instruction_sigp_cpu_reset;
|
||||
u64 instruction_sigp_unknown;
|
||||
u64 instruction_diagnose_10;
|
||||
u64 instruction_diagnose_44;
|
||||
u64 instruction_diagnose_9c;
|
||||
u64 diag_9c_ignored;
|
||||
u64 diag_9c_forward;
|
||||
u64 instruction_diagnose_258;
|
||||
u64 instruction_diagnose_308;
|
||||
u64 instruction_diagnose_500;
|
||||
u64 instruction_diagnose_other;
|
||||
u64 pfault_sync;
|
||||
u64 signal_exits;
|
||||
};
|
||||
|
||||
/* irq types in ascend order of priorities */
|
||||
enum irq_types {
|
||||
IRQ_PEND_SET_PREFIX = 0,
|
||||
IRQ_PEND_RESTART,
|
||||
IRQ_PEND_SIGP_STOP,
|
||||
IRQ_PEND_IO_ISC_7,
|
||||
IRQ_PEND_IO_ISC_6,
|
||||
IRQ_PEND_IO_ISC_5,
|
||||
IRQ_PEND_IO_ISC_4,
|
||||
IRQ_PEND_IO_ISC_3,
|
||||
IRQ_PEND_IO_ISC_2,
|
||||
IRQ_PEND_IO_ISC_1,
|
||||
IRQ_PEND_IO_ISC_0,
|
||||
IRQ_PEND_VIRTIO,
|
||||
IRQ_PEND_PFAULT_DONE,
|
||||
IRQ_PEND_PFAULT_INIT,
|
||||
IRQ_PEND_EXT_HOST,
|
||||
IRQ_PEND_EXT_SERVICE,
|
||||
IRQ_PEND_EXT_SERVICE_EV,
|
||||
IRQ_PEND_EXT_TIMING,
|
||||
IRQ_PEND_EXT_CPU_TIMER,
|
||||
IRQ_PEND_EXT_CLOCK_COMP,
|
||||
IRQ_PEND_EXT_EXTERNAL,
|
||||
IRQ_PEND_EXT_EMERGENCY,
|
||||
IRQ_PEND_EXT_MALFUNC,
|
||||
IRQ_PEND_EXT_IRQ_KEY,
|
||||
IRQ_PEND_MCHK_REP,
|
||||
IRQ_PEND_PROG,
|
||||
IRQ_PEND_SVC,
|
||||
IRQ_PEND_MCHK_EX,
|
||||
IRQ_PEND_COUNT
|
||||
};
|
||||
|
||||
/* We have 2M for virtio device descriptor pages. Smallest amount of
|
||||
* memory per page is 24 bytes (1 queue), so (2048*1024) / 24 = 87381
|
||||
*/
|
||||
#define KVM_S390_MAX_VIRTIO_IRQS 87381
|
||||
|
||||
/*
|
||||
* Repressible (non-floating) machine check interrupts
|
||||
* subclass bits in MCIC
|
||||
*/
|
||||
#define MCHK_EXTD_BIT 58
|
||||
#define MCHK_DEGR_BIT 56
|
||||
#define MCHK_WARN_BIT 55
|
||||
#define MCHK_REP_MASK ((1UL << MCHK_DEGR_BIT) | \
|
||||
(1UL << MCHK_EXTD_BIT) | \
|
||||
(1UL << MCHK_WARN_BIT))
|
||||
|
||||
/* Exigent machine check interrupts subclass bits in MCIC */
|
||||
#define MCHK_SD_BIT 63
|
||||
#define MCHK_PD_BIT 62
|
||||
#define MCHK_EX_MASK ((1UL << MCHK_SD_BIT) | (1UL << MCHK_PD_BIT))
|
||||
|
||||
#define IRQ_PEND_EXT_MASK ((1UL << IRQ_PEND_EXT_IRQ_KEY) | \
|
||||
(1UL << IRQ_PEND_EXT_CLOCK_COMP) | \
|
||||
(1UL << IRQ_PEND_EXT_CPU_TIMER) | \
|
||||
(1UL << IRQ_PEND_EXT_MALFUNC) | \
|
||||
(1UL << IRQ_PEND_EXT_EMERGENCY) | \
|
||||
(1UL << IRQ_PEND_EXT_EXTERNAL) | \
|
||||
(1UL << IRQ_PEND_EXT_TIMING) | \
|
||||
(1UL << IRQ_PEND_EXT_HOST) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE_EV) | \
|
||||
(1UL << IRQ_PEND_VIRTIO) | \
|
||||
(1UL << IRQ_PEND_PFAULT_INIT) | \
|
||||
(1UL << IRQ_PEND_PFAULT_DONE))
|
||||
|
||||
#define IRQ_PEND_IO_MASK ((1UL << IRQ_PEND_IO_ISC_0) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_1) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_2) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_3) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_4) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_5) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_6) | \
|
||||
(1UL << IRQ_PEND_IO_ISC_7))
|
||||
|
||||
#define IRQ_PEND_MCHK_MASK ((1UL << IRQ_PEND_MCHK_REP) | \
|
||||
(1UL << IRQ_PEND_MCHK_EX))
|
||||
|
||||
#define IRQ_PEND_EXT_II_MASK ((1UL << IRQ_PEND_EXT_CPU_TIMER) | \
|
||||
(1UL << IRQ_PEND_EXT_CLOCK_COMP) | \
|
||||
(1UL << IRQ_PEND_EXT_EMERGENCY) | \
|
||||
(1UL << IRQ_PEND_EXT_EXTERNAL) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE) | \
|
||||
(1UL << IRQ_PEND_EXT_SERVICE_EV))
|
||||
|
||||
struct kvm_s390_interrupt_info {
|
||||
struct list_head list;
|
||||
u64 type;
|
||||
union {
|
||||
struct kvm_s390_io_info io;
|
||||
struct kvm_s390_ext_info ext;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
struct kvm_s390_emerg_info emerg;
|
||||
struct kvm_s390_extcall_info extcall;
|
||||
struct kvm_s390_prefix_info prefix;
|
||||
struct kvm_s390_stop_info stop;
|
||||
struct kvm_s390_mchk_info mchk;
|
||||
};
|
||||
};
|
||||
|
||||
struct kvm_s390_irq_payload {
|
||||
struct kvm_s390_io_info io;
|
||||
struct kvm_s390_ext_info ext;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
struct kvm_s390_emerg_info emerg;
|
||||
struct kvm_s390_extcall_info extcall;
|
||||
struct kvm_s390_prefix_info prefix;
|
||||
struct kvm_s390_stop_info stop;
|
||||
struct kvm_s390_mchk_info mchk;
|
||||
};
|
||||
|
||||
struct kvm_s390_local_interrupt {
|
||||
spinlock_t lock;
|
||||
DECLARE_BITMAP(sigp_emerg_pending, KVM_MAX_VCPUS);
|
||||
struct kvm_s390_irq_payload irq;
|
||||
unsigned long pending_irqs;
|
||||
};
|
||||
|
||||
#define FIRQ_LIST_IO_ISC_0 0
|
||||
#define FIRQ_LIST_IO_ISC_1 1
|
||||
#define FIRQ_LIST_IO_ISC_2 2
|
||||
#define FIRQ_LIST_IO_ISC_3 3
|
||||
#define FIRQ_LIST_IO_ISC_4 4
|
||||
#define FIRQ_LIST_IO_ISC_5 5
|
||||
#define FIRQ_LIST_IO_ISC_6 6
|
||||
#define FIRQ_LIST_IO_ISC_7 7
|
||||
#define FIRQ_LIST_PFAULT 8
|
||||
#define FIRQ_LIST_VIRTIO 9
|
||||
#define FIRQ_LIST_COUNT 10
|
||||
#define FIRQ_CNTR_IO 0
|
||||
#define FIRQ_CNTR_SERVICE 1
|
||||
#define FIRQ_CNTR_VIRTIO 2
|
||||
#define FIRQ_CNTR_PFAULT 3
|
||||
#define FIRQ_MAX_COUNT 4
|
||||
|
||||
/* mask the AIS mode for a given ISC */
|
||||
#define AIS_MODE_MASK(isc) (0x80 >> isc)
|
||||
|
||||
#define KVM_S390_AIS_MODE_ALL 0
|
||||
#define KVM_S390_AIS_MODE_SINGLE 1
|
||||
|
||||
struct kvm_s390_float_interrupt {
|
||||
unsigned long pending_irqs;
|
||||
unsigned long masked_irqs;
|
||||
spinlock_t lock;
|
||||
struct list_head lists[FIRQ_LIST_COUNT];
|
||||
int counters[FIRQ_MAX_COUNT];
|
||||
struct kvm_s390_mchk_info mchk;
|
||||
struct kvm_s390_ext_info srv_signal;
|
||||
int last_sleep_cpu;
|
||||
spinlock_t ais_lock;
|
||||
u8 simm;
|
||||
u8 nimm;
|
||||
};
|
||||
|
||||
struct kvm_hw_wp_info_arch {
|
||||
unsigned long addr;
|
||||
unsigned long phys_addr;
|
||||
int len;
|
||||
char *old_data;
|
||||
};
|
||||
|
||||
struct kvm_hw_bp_info_arch {
|
||||
unsigned long addr;
|
||||
int len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Only the upper 16 bits of kvm_guest_debug->control are arch specific.
|
||||
* Further KVM_GUESTDBG flags which an be used from userspace can be found in
|
||||
* arch/s390/include/uapi/asm/kvm.h
|
||||
*/
|
||||
#define KVM_GUESTDBG_EXIT_PENDING 0x10000000
|
||||
|
||||
#define guestdbg_enabled(vcpu) \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_ENABLE)
|
||||
#define guestdbg_sstep_enabled(vcpu) \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
|
||||
#define guestdbg_hw_bp_enabled(vcpu) \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
|
||||
#define guestdbg_exit_pending(vcpu) (guestdbg_enabled(vcpu) && \
|
||||
(vcpu->guest_debug & KVM_GUESTDBG_EXIT_PENDING))
|
||||
|
||||
#define KVM_GUESTDBG_VALID_MASK \
|
||||
(KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP |\
|
||||
KVM_GUESTDBG_USE_HW_BP | KVM_GUESTDBG_EXIT_PENDING)
|
||||
|
||||
struct kvm_guestdbg_info_arch {
|
||||
unsigned long cr0;
|
||||
unsigned long cr9;
|
||||
unsigned long cr10;
|
||||
unsigned long cr11;
|
||||
struct kvm_hw_bp_info_arch *hw_bp_info;
|
||||
struct kvm_hw_wp_info_arch *hw_wp_info;
|
||||
int nr_hw_bp;
|
||||
int nr_hw_wp;
|
||||
unsigned long last_bp;
|
||||
};
|
||||
|
||||
struct kvm_s390_pv_vcpu {
|
||||
u64 handle;
|
||||
unsigned long stor_base;
|
||||
};
|
||||
|
||||
struct kvm_vcpu_arch {
|
||||
struct kvm_s390_sie_block *sie_block;
|
||||
/* if vsie is active, currently executed shadow sie control block */
|
||||
struct kvm_s390_sie_block *vsie_block;
|
||||
unsigned int host_acrs[NUM_ACRS];
|
||||
struct gs_cb *host_gscb;
|
||||
struct kvm_s390_local_interrupt local_int;
|
||||
struct hrtimer ckc_timer;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
struct gmap *gmap;
|
||||
struct kvm_guestdbg_info_arch guestdbg;
|
||||
unsigned long pfault_token;
|
||||
unsigned long pfault_select;
|
||||
unsigned long pfault_compare;
|
||||
bool cputm_enabled;
|
||||
/*
|
||||
* The seqcount protects updates to cputm_start and sie_block.cputm,
|
||||
* this way we can have non-blocking reads with consistent values.
|
||||
* Only the owning VCPU thread (vcpu->cpu) is allowed to change these
|
||||
* values and to start/stop/enable/disable cpu timer accounting.
|
||||
*/
|
||||
seqcount_t cputm_seqcount;
|
||||
__u64 cputm_start;
|
||||
bool gs_enabled;
|
||||
bool skey_enabled;
|
||||
/* Indicator if the access registers have been loaded from guest */
|
||||
bool acrs_loaded;
|
||||
bool initialized;
|
||||
struct kvm_s390_pv_vcpu pv;
|
||||
union diag318_info diag318_info;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
struct kvm_vm_stat {
|
||||
struct kvm_vm_stat_generic generic;
|
||||
u64 inject_io;
|
||||
u64 io_390_adapter_map;
|
||||
u64 io_390_adapter_unmap;
|
||||
u64 io_390_inatomic;
|
||||
u64 io_flic_inject_airq;
|
||||
u64 io_set_adapter_int;
|
||||
u64 io_390_inatomic_no_inject;
|
||||
u64 inject_float_mchk;
|
||||
u64 inject_pfault_done;
|
||||
u64 inject_service_signal;
|
||||
u64 inject_virtio;
|
||||
u64 aen_forward;
|
||||
u64 gmap_shadow_create;
|
||||
u64 gmap_shadow_reuse;
|
||||
u64 gmap_shadow_r1_entry;
|
||||
u64 gmap_shadow_r2_entry;
|
||||
u64 gmap_shadow_r3_entry;
|
||||
u64 gmap_shadow_sg_entry;
|
||||
u64 gmap_shadow_pg_entry;
|
||||
};
|
||||
|
||||
struct kvm_arch_memory_slot {
|
||||
};
|
||||
|
||||
struct s390_map_info {
|
||||
struct list_head list;
|
||||
__u64 guest_addr;
|
||||
__u64 addr;
|
||||
struct page *page;
|
||||
/*
|
||||
* True if the page is long-term pinned. False if long-term pinning
|
||||
* failed and this entry exists only to preserve MAP/UNMAP symmetry.
|
||||
*/
|
||||
bool pinned;
|
||||
};
|
||||
|
||||
struct s390_io_adapter {
|
||||
unsigned int id;
|
||||
int isc;
|
||||
bool maskable;
|
||||
bool masked;
|
||||
bool swap;
|
||||
bool suppressible;
|
||||
spinlock_t maps_lock;
|
||||
struct list_head maps;
|
||||
unsigned int nr_maps;
|
||||
};
|
||||
|
||||
#define MAX_S390_IO_ADAPTERS ((MAX_ISC + 1) * 8)
|
||||
#define MAX_S390_ADAPTER_MAPS 256
|
||||
|
||||
/* maximum size of facilities and facility mask is 2k bytes */
|
||||
#define S390_ARCH_FAC_LIST_SIZE_BYTE (1<<11)
|
||||
#define S390_ARCH_FAC_LIST_SIZE_U64 \
|
||||
(S390_ARCH_FAC_LIST_SIZE_BYTE / sizeof(u64))
|
||||
#define S390_ARCH_FAC_MASK_SIZE_BYTE S390_ARCH_FAC_LIST_SIZE_BYTE
|
||||
#define S390_ARCH_FAC_MASK_SIZE_U64 \
|
||||
(S390_ARCH_FAC_MASK_SIZE_BYTE / sizeof(u64))
|
||||
|
||||
struct kvm_s390_cpu_model {
|
||||
/* facility mask supported by kvm & hosting machine */
|
||||
__u64 fac_mask[S390_ARCH_FAC_MASK_SIZE_U64];
|
||||
struct kvm_s390_vm_cpu_subfunc subfuncs;
|
||||
/* facility list requested by guest (in dma page) */
|
||||
__u64 *fac_list;
|
||||
u64 cpuid;
|
||||
unsigned short ibc;
|
||||
/* subset of available UV-features for pv-guests enabled by user space */
|
||||
struct kvm_s390_vm_cpu_uv_feat uv_feat_guest;
|
||||
};
|
||||
|
||||
#define S390_ARCH_FAC_FORMAT_2 2
|
||||
struct kvm_s390_flcb2 {
|
||||
union {
|
||||
struct {
|
||||
u8 reserved0[7];
|
||||
u8 length;
|
||||
};
|
||||
u64 header_val;
|
||||
};
|
||||
u64 facilities[S390_ARCH_FAC_LIST_SIZE_U64];
|
||||
};
|
||||
|
||||
typedef int (*crypto_hook)(struct kvm_vcpu *vcpu);
|
||||
|
||||
struct kvm_s390_crypto {
|
||||
struct kvm_s390_crypto_cb *crycb;
|
||||
struct rw_semaphore pqap_hook_rwsem;
|
||||
crypto_hook *pqap_hook;
|
||||
__u32 crycbd;
|
||||
__u8 aes_kw;
|
||||
__u8 dea_kw;
|
||||
__u8 apie;
|
||||
};
|
||||
|
||||
#define APCB0_MASK_SIZE 1
|
||||
struct kvm_s390_apcb0 {
|
||||
__u64 apm[APCB0_MASK_SIZE]; /* 0x0000 */
|
||||
__u64 aqm[APCB0_MASK_SIZE]; /* 0x0008 */
|
||||
__u64 adm[APCB0_MASK_SIZE]; /* 0x0010 */
|
||||
__u64 reserved18; /* 0x0018 */
|
||||
};
|
||||
|
||||
#define APCB1_MASK_SIZE 4
|
||||
struct kvm_s390_apcb1 {
|
||||
__u64 apm[APCB1_MASK_SIZE]; /* 0x0000 */
|
||||
__u64 aqm[APCB1_MASK_SIZE]; /* 0x0020 */
|
||||
__u64 adm[APCB1_MASK_SIZE]; /* 0x0040 */
|
||||
__u64 reserved60[4]; /* 0x0060 */
|
||||
};
|
||||
|
||||
struct kvm_s390_crypto_cb {
|
||||
struct kvm_s390_apcb0 apcb0; /* 0x0000 */
|
||||
__u8 reserved20[0x0048 - 0x0020]; /* 0x0020 */
|
||||
__u8 dea_wrapping_key_mask[24]; /* 0x0048 */
|
||||
__u8 aes_wrapping_key_mask[32]; /* 0x0060 */
|
||||
struct kvm_s390_apcb1 apcb1; /* 0x0080 */
|
||||
};
|
||||
|
||||
struct kvm_s390_gisa {
|
||||
union {
|
||||
struct { /* common to all formats */
|
||||
u32 next_alert;
|
||||
u8 ipm;
|
||||
u8 reserved01[2];
|
||||
u8 iam;
|
||||
};
|
||||
struct { /* format 0 */
|
||||
u32 next_alert;
|
||||
u8 ipm;
|
||||
u8 reserved01;
|
||||
u8 : 6;
|
||||
u8 g : 1;
|
||||
u8 c : 1;
|
||||
u8 iam;
|
||||
u8 reserved02[4];
|
||||
u32 airq_count;
|
||||
} g0;
|
||||
struct { /* format 1 */
|
||||
u32 next_alert;
|
||||
u8 ipm;
|
||||
u8 simm;
|
||||
u8 nimm;
|
||||
u8 iam;
|
||||
u8 aism[8];
|
||||
u8 : 6;
|
||||
u8 g : 1;
|
||||
u8 c : 1;
|
||||
u8 reserved03[11];
|
||||
u32 airq_count;
|
||||
} g1;
|
||||
struct {
|
||||
u64 word[4];
|
||||
} u64;
|
||||
};
|
||||
};
|
||||
|
||||
struct kvm_s390_gib {
|
||||
u32 alert_list_origin;
|
||||
u32 reserved01;
|
||||
u8:5;
|
||||
u8 nisc:3;
|
||||
u8 reserved03[3];
|
||||
u32 reserved04[5];
|
||||
};
|
||||
|
||||
/*
|
||||
* sie_page2 has to be allocated as DMA because fac_list, crycb and
|
||||
* gisa need 31bit addresses in the sie control block.
|
||||
*/
|
||||
struct sie_page2 {
|
||||
__u64 fac_list[S390_ARCH_FAC_LIST_SIZE_U64]; /* 0x0000 */
|
||||
struct kvm_s390_crypto_cb crycb; /* 0x0800 */
|
||||
struct kvm_s390_gisa gisa; /* 0x0900 */
|
||||
struct kvm *kvm; /* 0x0920 */
|
||||
u8 reserved928[0x1000 - 0x928]; /* 0x0928 */
|
||||
};
|
||||
|
||||
struct vsie_page;
|
||||
|
||||
struct kvm_s390_vsie {
|
||||
struct mutex mutex;
|
||||
struct radix_tree_root addr_to_page;
|
||||
int page_count;
|
||||
int next;
|
||||
struct vsie_page *pages[KVM_MAX_VCPUS];
|
||||
};
|
||||
|
||||
struct kvm_s390_gisa_iam {
|
||||
u8 mask;
|
||||
spinlock_t ref_lock;
|
||||
u32 ref_count[MAX_ISC + 1];
|
||||
};
|
||||
|
||||
struct kvm_s390_gisa_interrupt {
|
||||
struct kvm_s390_gisa *origin;
|
||||
struct kvm_s390_gisa_iam alert;
|
||||
struct hrtimer timer;
|
||||
u64 expires;
|
||||
DECLARE_BITMAP(kicked_mask, KVM_MAX_VCPUS);
|
||||
};
|
||||
|
||||
struct kvm_s390_pv {
|
||||
u64 handle;
|
||||
u64 guest_len;
|
||||
unsigned long stor_base;
|
||||
void *stor_var;
|
||||
bool dumping;
|
||||
void *set_aside;
|
||||
struct list_head need_cleanup;
|
||||
struct mmu_notifier mmu_notifier;
|
||||
/* Protects against concurrent import-like operations */
|
||||
struct mutex import_lock;
|
||||
};
|
||||
|
||||
struct kvm_s390_mmu_cache;
|
||||
|
||||
struct kvm_arch {
|
||||
struct esca_block *sca;
|
||||
debug_info_t *dbf;
|
||||
struct kvm_s390_float_interrupt float_int;
|
||||
struct kvm_device *flic;
|
||||
struct gmap *gmap;
|
||||
unsigned long mem_limit;
|
||||
int css_support;
|
||||
int use_irqchip;
|
||||
int use_cmma;
|
||||
int use_pfmfi;
|
||||
int use_skf;
|
||||
int use_zpci_interp;
|
||||
int user_cpu_state_ctrl;
|
||||
int user_sigp;
|
||||
int user_stsi;
|
||||
int user_instr0;
|
||||
int user_operexec;
|
||||
int allow_vsie_esamode;
|
||||
struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
|
||||
wait_queue_head_t ipte_wq;
|
||||
int ipte_lock_count;
|
||||
struct mutex ipte_mutex;
|
||||
spinlock_t start_stop_lock;
|
||||
struct sie_page2 *sie_page2;
|
||||
struct kvm_s390_cpu_model model;
|
||||
struct kvm_s390_crypto crypto;
|
||||
struct kvm_s390_vsie vsie;
|
||||
u8 epdx;
|
||||
u64 epoch;
|
||||
int migration_mode;
|
||||
atomic64_t cmma_dirty_pages;
|
||||
/* subset of available cpu features enabled by user space */
|
||||
DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
|
||||
/* indexed by vcpu_idx */
|
||||
DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS);
|
||||
struct kvm_s390_gisa_interrupt gisa_int;
|
||||
struct kvm_s390_pv pv;
|
||||
struct list_head kzdev_list;
|
||||
spinlock_t kzdev_list_lock;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
#define KVM_HVA_ERR_BAD (-1UL)
|
||||
#define KVM_HVA_ERR_RO_BAD (-2UL)
|
||||
|
||||
static inline bool kvm_is_error_hva(unsigned long addr)
|
||||
{
|
||||
return IS_ERR_VALUE(addr);
|
||||
}
|
||||
|
||||
#define ASYNC_PF_PER_VCPU 64
|
||||
struct kvm_arch_async_pf {
|
||||
unsigned long pfault_token;
|
||||
};
|
||||
|
||||
bool kvm_arch_can_dequeue_async_page_present(struct kvm_vcpu *vcpu);
|
||||
|
||||
void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work);
|
||||
|
||||
bool kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work);
|
||||
|
||||
void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work);
|
||||
|
||||
static inline void kvm_arch_async_page_present_queued(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
void kvm_arch_crypto_clear_masks(struct kvm *kvm);
|
||||
void kvm_arch_crypto_set_masks(struct kvm *kvm, unsigned long *apm,
|
||||
unsigned long *aqm, unsigned long *adm);
|
||||
|
||||
#define SIE64_RETURN_NORMAL 0
|
||||
#define SIE64_RETURN_MCCK 1
|
||||
|
||||
int __sie64a(phys_addr_t sie_block_phys, struct kvm_s390_sie_block *sie_block, u64 *rsa,
|
||||
unsigned long gasce);
|
||||
|
||||
static inline int sie64a(struct kvm_s390_sie_block *sie_block, u64 *rsa, unsigned long gasce)
|
||||
{
|
||||
return __sie64a(virt_to_phys(sie_block), sie_block, rsa, gasce);
|
||||
}
|
||||
|
||||
extern char sie_exit;
|
||||
|
||||
bool kvm_s390_pv_is_protected(struct kvm *kvm);
|
||||
bool kvm_s390_pv_cpu_is_protected(struct kvm_vcpu *vcpu);
|
||||
|
||||
extern int kvm_s390_enter_exit_sie(struct kvm_s390_sie_block *scb,
|
||||
u64 *gprs, unsigned long gasce);
|
||||
|
||||
extern int kvm_s390_gisc_register(struct kvm *kvm, u32 gisc);
|
||||
extern int kvm_s390_gisc_unregister(struct kvm *kvm, u32 gisc);
|
||||
|
||||
bool kvm_s390_is_gpa_in_memslot(struct kvm *kvm, gpa_t gpa);
|
||||
|
||||
static inline void kvm_arch_free_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot) {}
|
||||
static inline void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) {}
|
||||
static inline void kvm_arch_flush_shadow_all(struct kvm *kvm) {}
|
||||
static inline void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot) {}
|
||||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
#define __KVM_HAVE_ARCH_VM_FREE
|
||||
void kvm_arch_free_vm(struct kvm *kvm);
|
||||
|
||||
struct zpci_kvm_hook {
|
||||
int (*kvm_register)(void *opaque, struct kvm *kvm);
|
||||
void (*kvm_unregister)(void *opaque);
|
||||
};
|
||||
|
||||
extern struct zpci_kvm_hook zpci_kvm_hook;
|
||||
|
||||
#endif /* ASM_KVM_HOST_S390_H */
|
||||
349
arch/s390/include/asm/kvm_host_s390_types.h
Normal file
349
arch/s390/include/asm/kvm_host_s390_types.h
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _ASM_KVM_HOST_S390_TYPES_H
|
||||
#define _ASM_KVM_HOST_S390_TYPES_H
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define KVM_S390_BSCA_CPU_SLOTS 64
|
||||
#define KVM_S390_ESCA_CPU_SLOTS 248
|
||||
|
||||
#define SCB_ALIGNMENT_SHIFT 9
|
||||
|
||||
#define SIGP_CTRL_C 0x80
|
||||
#define SIGP_CTRL_SCN_MASK 0x3f
|
||||
|
||||
union bsca_sigp_ctrl {
|
||||
__u8 value;
|
||||
struct {
|
||||
__u8 c : 1;
|
||||
__u8 r : 1;
|
||||
__u8 scn : 6;
|
||||
};
|
||||
};
|
||||
|
||||
union esca_sigp_ctrl {
|
||||
__u16 value;
|
||||
struct {
|
||||
__u8 c : 1;
|
||||
__u8 reserved: 7;
|
||||
__u8 scn;
|
||||
};
|
||||
};
|
||||
|
||||
struct esca_entry {
|
||||
union esca_sigp_ctrl sigp_ctrl;
|
||||
__u16 reserved1[3];
|
||||
__u64 sda;
|
||||
__u64 reserved2[6];
|
||||
};
|
||||
|
||||
struct bsca_entry {
|
||||
__u8 reserved0;
|
||||
union bsca_sigp_ctrl sigp_ctrl;
|
||||
__u16 reserved[3];
|
||||
__u64 sda;
|
||||
__u64 reserved2[2];
|
||||
};
|
||||
|
||||
union ipte_control {
|
||||
unsigned long val;
|
||||
struct {
|
||||
unsigned long k : 1;
|
||||
unsigned long kh : 31;
|
||||
unsigned long kg : 32;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Utility is defined as two bytes but having it four bytes wide
|
||||
* generates more efficient code. Since the following bytes are
|
||||
* reserved this makes no functional difference.
|
||||
*/
|
||||
union sca_utility {
|
||||
__u32 val;
|
||||
struct {
|
||||
__u32 mtcr : 1;
|
||||
__u32 : 31;
|
||||
};
|
||||
};
|
||||
|
||||
struct bsca_block {
|
||||
union ipte_control ipte_control;
|
||||
__u64 reserved[5];
|
||||
__u64 mcn;
|
||||
union sca_utility utility;
|
||||
__u8 reserved2[4];
|
||||
struct bsca_entry cpu[KVM_S390_BSCA_CPU_SLOTS];
|
||||
};
|
||||
|
||||
struct esca_block {
|
||||
union ipte_control ipte_control;
|
||||
__u64 reserved1[6];
|
||||
union sca_utility utility;
|
||||
__u8 reserved2[4];
|
||||
__u64 mcn[4];
|
||||
__u64 reserved3[20];
|
||||
struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS];
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct is used to store some machine check info from lowcore
|
||||
* for machine checks that happen while the guest is running.
|
||||
* This info in host's lowcore might be overwritten by a second machine
|
||||
* check from host when host is in the machine check's high-level handling.
|
||||
* The size is 24 bytes.
|
||||
*/
|
||||
struct mcck_volatile_info {
|
||||
__u64 mcic;
|
||||
__u64 failing_storage_address;
|
||||
__u32 ext_damage_code;
|
||||
__u32 reserved;
|
||||
};
|
||||
|
||||
#define CR0_INITIAL_MASK (CR0_UNUSED_56 | CR0_INTERRUPT_KEY_SUBMASK | \
|
||||
CR0_MEASUREMENT_ALERT_SUBMASK)
|
||||
#define CR14_INITIAL_MASK (CR14_UNUSED_32 | CR14_UNUSED_33 | \
|
||||
CR14_EXTERNAL_DAMAGE_SUBMASK)
|
||||
|
||||
#define SIDAD_SIZE_MASK 0xff
|
||||
#define sida_addr(sie_block) phys_to_virt((sie_block)->sidad & PAGE_MASK)
|
||||
#define sida_size(sie_block) \
|
||||
((((sie_block)->sidad & SIDAD_SIZE_MASK) + 1) * PAGE_SIZE)
|
||||
|
||||
#define CPUSTAT_STOPPED 0x80000000
|
||||
#define CPUSTAT_WAIT 0x10000000
|
||||
#define CPUSTAT_ECALL_PEND 0x08000000
|
||||
#define CPUSTAT_STOP_INT 0x04000000
|
||||
#define CPUSTAT_IO_INT 0x02000000
|
||||
#define CPUSTAT_EXT_INT 0x01000000
|
||||
#define CPUSTAT_RUNNING 0x00800000
|
||||
#define CPUSTAT_RETAINED 0x00400000
|
||||
#define CPUSTAT_TIMING_SUB 0x00020000
|
||||
#define CPUSTAT_SIE_SUB 0x00010000
|
||||
#define CPUSTAT_RRF 0x00008000
|
||||
#define CPUSTAT_SLSV 0x00004000
|
||||
#define CPUSTAT_SLSR 0x00002000
|
||||
#define CPUSTAT_ZARCH 0x00000800
|
||||
#define CPUSTAT_MCDS 0x00000100
|
||||
#define CPUSTAT_KSS 0x00000200
|
||||
#define CPUSTAT_SM 0x00000080
|
||||
#define CPUSTAT_IBS 0x00000040
|
||||
#define CPUSTAT_GED2 0x00000010
|
||||
#define CPUSTAT_G 0x00000008
|
||||
#define CPUSTAT_GED 0x00000004
|
||||
#define CPUSTAT_J 0x00000002
|
||||
#define CPUSTAT_P 0x00000001
|
||||
|
||||
struct kvm_s390_sie_block {
|
||||
atomic_t cpuflags; /* 0x0000 */
|
||||
__u32 : 1; /* 0x0004 */
|
||||
__u32 prefix : 19;
|
||||
__u32 ibc : 12;
|
||||
__u8 reserved08[4]; /* 0x0008 */
|
||||
#define PROG_IN_SIE (1<<0)
|
||||
__u32 prog0c; /* 0x000c */
|
||||
union {
|
||||
__u8 reserved10[16]; /* 0x0010 */
|
||||
struct {
|
||||
__u64 pv_handle_cpu;
|
||||
__u64 pv_handle_config;
|
||||
};
|
||||
};
|
||||
#define PROG_BLOCK_SIE (1<<0)
|
||||
#define PROG_REQUEST (1<<1)
|
||||
atomic_t prog20; /* 0x0020 */
|
||||
__u8 reserved24[4]; /* 0x0024 */
|
||||
__u64 cputm; /* 0x0028 */
|
||||
__u64 ckc; /* 0x0030 */
|
||||
__u64 epoch; /* 0x0038 */
|
||||
__u32 svcc; /* 0x0040 */
|
||||
#define LCTL_CR0 0x8000
|
||||
#define LCTL_CR6 0x0200
|
||||
#define LCTL_CR9 0x0040
|
||||
#define LCTL_CR10 0x0020
|
||||
#define LCTL_CR11 0x0010
|
||||
#define LCTL_CR14 0x0002
|
||||
__u16 lctl; /* 0x0044 */
|
||||
__s16 icpua; /* 0x0046 */
|
||||
#define ICTL_OPEREXC 0x80000000
|
||||
#define ICTL_PINT 0x20000000
|
||||
#define ICTL_LPSW 0x00400000
|
||||
#define ICTL_STCTL 0x00040000
|
||||
#define ICTL_ISKE 0x00004000
|
||||
#define ICTL_SSKE 0x00002000
|
||||
#define ICTL_RRBE 0x00001000
|
||||
#define ICTL_TPROT 0x00000200
|
||||
__u32 ictl; /* 0x0048 */
|
||||
#define ECA_CEI 0x80000000
|
||||
#define ECA_IB 0x40000000
|
||||
#define ECA_SIGPI 0x10000000
|
||||
#define ECA_MVPGI 0x01000000
|
||||
#define ECA_AIV 0x00200000
|
||||
#define ECA_VX 0x00020000
|
||||
#define ECA_PROTEXCI 0x00002000
|
||||
#define ECA_APIE 0x00000008
|
||||
#define ECA_SII 0x00000001
|
||||
__u32 eca; /* 0x004c */
|
||||
#define ICPT_INST 0x04
|
||||
#define ICPT_PROGI 0x08
|
||||
#define ICPT_INSTPROGI 0x0C
|
||||
#define ICPT_EXTREQ 0x10
|
||||
#define ICPT_EXTINT 0x14
|
||||
#define ICPT_IOREQ 0x18
|
||||
#define ICPT_WAIT 0x1c
|
||||
#define ICPT_VALIDITY 0x20
|
||||
#define ICPT_STOP 0x28
|
||||
#define ICPT_OPEREXC 0x2C
|
||||
#define ICPT_PARTEXEC 0x38
|
||||
#define ICPT_IOINST 0x40
|
||||
#define ICPT_KSS 0x5c
|
||||
#define ICPT_MCHKREQ 0x60
|
||||
#define ICPT_INT_ENABLE 0x64
|
||||
#define ICPT_PV_INSTR 0x68
|
||||
#define ICPT_PV_NOTIFY 0x6c
|
||||
#define ICPT_PV_PREF 0x70
|
||||
__u8 icptcode; /* 0x0050 */
|
||||
__u8 icptstatus; /* 0x0051 */
|
||||
__u16 ihcpu; /* 0x0052 */
|
||||
__u8 reserved54; /* 0x0054 */
|
||||
#define IICTL_CODE_NONE 0x00
|
||||
#define IICTL_CODE_MCHK 0x01
|
||||
#define IICTL_CODE_EXT 0x02
|
||||
#define IICTL_CODE_IO 0x03
|
||||
#define IICTL_CODE_RESTART 0x04
|
||||
#define IICTL_CODE_SPECIFICATION 0x10
|
||||
#define IICTL_CODE_OPERAND 0x11
|
||||
__u8 iictl; /* 0x0055 */
|
||||
__u16 ipa; /* 0x0056 */
|
||||
__u32 ipb; /* 0x0058 */
|
||||
__u32 scaoh; /* 0x005c */
|
||||
#define FPF_BPBC 0x20
|
||||
__u8 fpf; /* 0x0060 */
|
||||
#define ECB_GS 0x40
|
||||
#define ECB_TE 0x10
|
||||
#define ECB_SPECI 0x08
|
||||
#define ECB_SRSI 0x04
|
||||
#define ECB_HOSTPROTINT 0x02
|
||||
#define ECB_PTF 0x01
|
||||
__u8 ecb; /* 0x0061 */
|
||||
#define ECB2_CMMA 0x80
|
||||
#define ECB2_IEP 0x20
|
||||
#define ECB2_PFMFI 0x08
|
||||
#define ECB2_ESCA 0x04
|
||||
#define ECB2_ZPCI_LSI 0x02
|
||||
__u8 ecb2; /* 0x0062 */
|
||||
#define ECB3_AISI 0x20
|
||||
#define ECB3_AISII 0x10
|
||||
#define ECB3_DEA 0x08
|
||||
#define ECB3_AES 0x04
|
||||
#define ECB3_RI 0x01
|
||||
__u8 ecb3; /* 0x0063 */
|
||||
#define ESCA_SCAOL_MASK ~0x3fU
|
||||
__u32 scaol; /* 0x0064 */
|
||||
__u8 sdf; /* 0x0068 */
|
||||
__u8 epdx; /* 0x0069 */
|
||||
__u8 cpnc; /* 0x006a */
|
||||
__u8 reserved6b; /* 0x006b */
|
||||
__u32 todpr; /* 0x006c */
|
||||
#define GISA_FORMAT1 0x00000001
|
||||
__u32 gd; /* 0x0070 */
|
||||
__u8 reserved74[12]; /* 0x0074 */
|
||||
__u64 mso; /* 0x0080 */
|
||||
__u64 msl; /* 0x0088 */
|
||||
psw_t gpsw; /* 0x0090 */
|
||||
__u64 gg14; /* 0x00a0 */
|
||||
__u64 gg15; /* 0x00a8 */
|
||||
__u8 reservedb0[8]; /* 0x00b0 */
|
||||
#define HPID_KVM 0x4
|
||||
#define HPID_VSIE 0x5
|
||||
__u8 hpid; /* 0x00b8 */
|
||||
__u8 reservedb9[7]; /* 0x00b9 */
|
||||
union {
|
||||
struct {
|
||||
__u32 eiparams; /* 0x00c0 */
|
||||
__u16 extcpuaddr; /* 0x00c4 */
|
||||
__u16 eic; /* 0x00c6 */
|
||||
};
|
||||
__u64 mcic; /* 0x00c0 */
|
||||
} __packed;
|
||||
__u32 reservedc8; /* 0x00c8 */
|
||||
union {
|
||||
struct {
|
||||
__u16 pgmilc; /* 0x00cc */
|
||||
__u16 iprcc; /* 0x00ce */
|
||||
};
|
||||
__u32 edc; /* 0x00cc */
|
||||
} __packed;
|
||||
union {
|
||||
struct {
|
||||
__u32 dxc; /* 0x00d0 */
|
||||
__u16 mcn; /* 0x00d4 */
|
||||
__u8 perc; /* 0x00d6 */
|
||||
__u8 peratmid; /* 0x00d7 */
|
||||
};
|
||||
__u64 faddr; /* 0x00d0 */
|
||||
} __packed;
|
||||
__u64 peraddr; /* 0x00d8 */
|
||||
__u8 eai; /* 0x00e0 */
|
||||
__u8 peraid; /* 0x00e1 */
|
||||
__u8 oai; /* 0x00e2 */
|
||||
__u8 armid; /* 0x00e3 */
|
||||
__u8 reservede4[4]; /* 0x00e4 */
|
||||
union {
|
||||
__u64 tecmc; /* 0x00e8 */
|
||||
struct {
|
||||
__u16 subchannel_id; /* 0x00e8 */
|
||||
__u16 subchannel_nr; /* 0x00ea */
|
||||
__u32 io_int_parm; /* 0x00ec */
|
||||
__u32 io_int_word; /* 0x00f0 */
|
||||
};
|
||||
} __packed;
|
||||
__u8 reservedf4[8]; /* 0x00f4 */
|
||||
#define CRYCB_FORMAT_MASK 0x00000003
|
||||
#define CRYCB_FORMAT0 0x00000000
|
||||
#define CRYCB_FORMAT1 0x00000001
|
||||
#define CRYCB_FORMAT2 0x00000003
|
||||
__u32 crycbd; /* 0x00fc */
|
||||
__u64 gcr[16]; /* 0x0100 */
|
||||
union {
|
||||
__u64 gbea; /* 0x0180 */
|
||||
__u64 sidad;
|
||||
};
|
||||
__u8 reserved188[8]; /* 0x0188 */
|
||||
__u64 sdnxo; /* 0x0190 */
|
||||
__u8 reserved198[8]; /* 0x0198 */
|
||||
__u32 fac; /* 0x01a0 */
|
||||
__u8 reserved1a4[20]; /* 0x01a4 */
|
||||
__u64 cbrlo; /* 0x01b8 */
|
||||
__u8 reserved1c0[8]; /* 0x01c0 */
|
||||
#define ECD_HOSTREGMGMT 0x20000000
|
||||
#define ECD_MEF 0x08000000
|
||||
#define ECD_ETOKENF 0x02000000
|
||||
#define ECD_ECC 0x00200000
|
||||
#define ECD_HMAC 0x00004000
|
||||
__u32 ecd; /* 0x01c8 */
|
||||
__u8 reserved1cc[18]; /* 0x01cc */
|
||||
__u64 pp; /* 0x01de */
|
||||
__u8 reserved1e6[2]; /* 0x01e6 */
|
||||
__u64 itdba; /* 0x01e8 */
|
||||
__u64 riccbd; /* 0x01f0 */
|
||||
__u64 gvrd; /* 0x01f8 */
|
||||
} __packed __aligned(512);
|
||||
|
||||
struct kvm_s390_itdb {
|
||||
__u8 data[256];
|
||||
};
|
||||
|
||||
struct sie_page {
|
||||
struct kvm_s390_sie_block sie_block;
|
||||
struct mcck_volatile_info mcck_info; /* 0x0200 */
|
||||
__u8 reserved218[360]; /* 0x0218 */
|
||||
__u64 pv_grregs[16]; /* 0x0380 */
|
||||
__u8 reserved400[512]; /* 0x0400 */
|
||||
struct kvm_s390_itdb itdb; /* 0x0600 */
|
||||
__u8 reserved700[2304]; /* 0x0700 */
|
||||
};
|
||||
|
||||
#endif /* _ASM_KVM_HOST_S390_TYPES_H */
|
||||
|
|
@ -1,347 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _ASM_KVM_HOST_TYPES_H
|
||||
#define _ASM_KVM_HOST_TYPES_H
|
||||
#ifndef ASM_KVM_HOST_TYPES_H
|
||||
#define ASM_KVM_HOST_TYPES_H
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/kvm_host_s390_types.h>
|
||||
|
||||
#define KVM_S390_BSCA_CPU_SLOTS 64
|
||||
#define KVM_S390_ESCA_CPU_SLOTS 248
|
||||
|
||||
#define SIGP_CTRL_C 0x80
|
||||
#define SIGP_CTRL_SCN_MASK 0x3f
|
||||
|
||||
union bsca_sigp_ctrl {
|
||||
__u8 value;
|
||||
struct {
|
||||
__u8 c : 1;
|
||||
__u8 r : 1;
|
||||
__u8 scn : 6;
|
||||
};
|
||||
};
|
||||
|
||||
union esca_sigp_ctrl {
|
||||
__u16 value;
|
||||
struct {
|
||||
__u8 c : 1;
|
||||
__u8 reserved: 7;
|
||||
__u8 scn;
|
||||
};
|
||||
};
|
||||
|
||||
struct esca_entry {
|
||||
union esca_sigp_ctrl sigp_ctrl;
|
||||
__u16 reserved1[3];
|
||||
__u64 sda;
|
||||
__u64 reserved2[6];
|
||||
};
|
||||
|
||||
struct bsca_entry {
|
||||
__u8 reserved0;
|
||||
union bsca_sigp_ctrl sigp_ctrl;
|
||||
__u16 reserved[3];
|
||||
__u64 sda;
|
||||
__u64 reserved2[2];
|
||||
};
|
||||
|
||||
union ipte_control {
|
||||
unsigned long val;
|
||||
struct {
|
||||
unsigned long k : 1;
|
||||
unsigned long kh : 31;
|
||||
unsigned long kg : 32;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Utility is defined as two bytes but having it four bytes wide
|
||||
* generates more efficient code. Since the following bytes are
|
||||
* reserved this makes no functional difference.
|
||||
*/
|
||||
union sca_utility {
|
||||
__u32 val;
|
||||
struct {
|
||||
__u32 mtcr : 1;
|
||||
__u32 : 31;
|
||||
};
|
||||
};
|
||||
|
||||
struct bsca_block {
|
||||
union ipte_control ipte_control;
|
||||
__u64 reserved[5];
|
||||
__u64 mcn;
|
||||
union sca_utility utility;
|
||||
__u8 reserved2[4];
|
||||
struct bsca_entry cpu[KVM_S390_BSCA_CPU_SLOTS];
|
||||
};
|
||||
|
||||
struct esca_block {
|
||||
union ipte_control ipte_control;
|
||||
__u64 reserved1[6];
|
||||
union sca_utility utility;
|
||||
__u8 reserved2[4];
|
||||
__u64 mcn[4];
|
||||
__u64 reserved3[20];
|
||||
struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS];
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct is used to store some machine check info from lowcore
|
||||
* for machine checks that happen while the guest is running.
|
||||
* This info in host's lowcore might be overwritten by a second machine
|
||||
* check from host when host is in the machine check's high-level handling.
|
||||
* The size is 24 bytes.
|
||||
*/
|
||||
struct mcck_volatile_info {
|
||||
__u64 mcic;
|
||||
__u64 failing_storage_address;
|
||||
__u32 ext_damage_code;
|
||||
__u32 reserved;
|
||||
};
|
||||
|
||||
#define CR0_INITIAL_MASK (CR0_UNUSED_56 | CR0_INTERRUPT_KEY_SUBMASK | \
|
||||
CR0_MEASUREMENT_ALERT_SUBMASK)
|
||||
#define CR14_INITIAL_MASK (CR14_UNUSED_32 | CR14_UNUSED_33 | \
|
||||
CR14_EXTERNAL_DAMAGE_SUBMASK)
|
||||
|
||||
#define SIDAD_SIZE_MASK 0xff
|
||||
#define sida_addr(sie_block) phys_to_virt((sie_block)->sidad & PAGE_MASK)
|
||||
#define sida_size(sie_block) \
|
||||
((((sie_block)->sidad & SIDAD_SIZE_MASK) + 1) * PAGE_SIZE)
|
||||
|
||||
#define CPUSTAT_STOPPED 0x80000000
|
||||
#define CPUSTAT_WAIT 0x10000000
|
||||
#define CPUSTAT_ECALL_PEND 0x08000000
|
||||
#define CPUSTAT_STOP_INT 0x04000000
|
||||
#define CPUSTAT_IO_INT 0x02000000
|
||||
#define CPUSTAT_EXT_INT 0x01000000
|
||||
#define CPUSTAT_RUNNING 0x00800000
|
||||
#define CPUSTAT_RETAINED 0x00400000
|
||||
#define CPUSTAT_TIMING_SUB 0x00020000
|
||||
#define CPUSTAT_SIE_SUB 0x00010000
|
||||
#define CPUSTAT_RRF 0x00008000
|
||||
#define CPUSTAT_SLSV 0x00004000
|
||||
#define CPUSTAT_SLSR 0x00002000
|
||||
#define CPUSTAT_ZARCH 0x00000800
|
||||
#define CPUSTAT_MCDS 0x00000100
|
||||
#define CPUSTAT_KSS 0x00000200
|
||||
#define CPUSTAT_SM 0x00000080
|
||||
#define CPUSTAT_IBS 0x00000040
|
||||
#define CPUSTAT_GED2 0x00000010
|
||||
#define CPUSTAT_G 0x00000008
|
||||
#define CPUSTAT_GED 0x00000004
|
||||
#define CPUSTAT_J 0x00000002
|
||||
#define CPUSTAT_P 0x00000001
|
||||
|
||||
struct kvm_s390_sie_block {
|
||||
atomic_t cpuflags; /* 0x0000 */
|
||||
__u32 : 1; /* 0x0004 */
|
||||
__u32 prefix : 19;
|
||||
__u32 ibc : 12;
|
||||
__u8 reserved08[4]; /* 0x0008 */
|
||||
#define PROG_IN_SIE (1<<0)
|
||||
__u32 prog0c; /* 0x000c */
|
||||
union {
|
||||
__u8 reserved10[16]; /* 0x0010 */
|
||||
struct {
|
||||
__u64 pv_handle_cpu;
|
||||
__u64 pv_handle_config;
|
||||
};
|
||||
};
|
||||
#define PROG_BLOCK_SIE (1<<0)
|
||||
#define PROG_REQUEST (1<<1)
|
||||
atomic_t prog20; /* 0x0020 */
|
||||
__u8 reserved24[4]; /* 0x0024 */
|
||||
__u64 cputm; /* 0x0028 */
|
||||
__u64 ckc; /* 0x0030 */
|
||||
__u64 epoch; /* 0x0038 */
|
||||
__u32 svcc; /* 0x0040 */
|
||||
#define LCTL_CR0 0x8000
|
||||
#define LCTL_CR6 0x0200
|
||||
#define LCTL_CR9 0x0040
|
||||
#define LCTL_CR10 0x0020
|
||||
#define LCTL_CR11 0x0010
|
||||
#define LCTL_CR14 0x0002
|
||||
__u16 lctl; /* 0x0044 */
|
||||
__s16 icpua; /* 0x0046 */
|
||||
#define ICTL_OPEREXC 0x80000000
|
||||
#define ICTL_PINT 0x20000000
|
||||
#define ICTL_LPSW 0x00400000
|
||||
#define ICTL_STCTL 0x00040000
|
||||
#define ICTL_ISKE 0x00004000
|
||||
#define ICTL_SSKE 0x00002000
|
||||
#define ICTL_RRBE 0x00001000
|
||||
#define ICTL_TPROT 0x00000200
|
||||
__u32 ictl; /* 0x0048 */
|
||||
#define ECA_CEI 0x80000000
|
||||
#define ECA_IB 0x40000000
|
||||
#define ECA_SIGPI 0x10000000
|
||||
#define ECA_MVPGI 0x01000000
|
||||
#define ECA_AIV 0x00200000
|
||||
#define ECA_VX 0x00020000
|
||||
#define ECA_PROTEXCI 0x00002000
|
||||
#define ECA_APIE 0x00000008
|
||||
#define ECA_SII 0x00000001
|
||||
__u32 eca; /* 0x004c */
|
||||
#define ICPT_INST 0x04
|
||||
#define ICPT_PROGI 0x08
|
||||
#define ICPT_INSTPROGI 0x0C
|
||||
#define ICPT_EXTREQ 0x10
|
||||
#define ICPT_EXTINT 0x14
|
||||
#define ICPT_IOREQ 0x18
|
||||
#define ICPT_WAIT 0x1c
|
||||
#define ICPT_VALIDITY 0x20
|
||||
#define ICPT_STOP 0x28
|
||||
#define ICPT_OPEREXC 0x2C
|
||||
#define ICPT_PARTEXEC 0x38
|
||||
#define ICPT_IOINST 0x40
|
||||
#define ICPT_KSS 0x5c
|
||||
#define ICPT_MCHKREQ 0x60
|
||||
#define ICPT_INT_ENABLE 0x64
|
||||
#define ICPT_PV_INSTR 0x68
|
||||
#define ICPT_PV_NOTIFY 0x6c
|
||||
#define ICPT_PV_PREF 0x70
|
||||
__u8 icptcode; /* 0x0050 */
|
||||
__u8 icptstatus; /* 0x0051 */
|
||||
__u16 ihcpu; /* 0x0052 */
|
||||
__u8 reserved54; /* 0x0054 */
|
||||
#define IICTL_CODE_NONE 0x00
|
||||
#define IICTL_CODE_MCHK 0x01
|
||||
#define IICTL_CODE_EXT 0x02
|
||||
#define IICTL_CODE_IO 0x03
|
||||
#define IICTL_CODE_RESTART 0x04
|
||||
#define IICTL_CODE_SPECIFICATION 0x10
|
||||
#define IICTL_CODE_OPERAND 0x11
|
||||
__u8 iictl; /* 0x0055 */
|
||||
__u16 ipa; /* 0x0056 */
|
||||
__u32 ipb; /* 0x0058 */
|
||||
__u32 scaoh; /* 0x005c */
|
||||
#define FPF_BPBC 0x20
|
||||
__u8 fpf; /* 0x0060 */
|
||||
#define ECB_GS 0x40
|
||||
#define ECB_TE 0x10
|
||||
#define ECB_SPECI 0x08
|
||||
#define ECB_SRSI 0x04
|
||||
#define ECB_HOSTPROTINT 0x02
|
||||
#define ECB_PTF 0x01
|
||||
__u8 ecb; /* 0x0061 */
|
||||
#define ECB2_CMMA 0x80
|
||||
#define ECB2_IEP 0x20
|
||||
#define ECB2_PFMFI 0x08
|
||||
#define ECB2_ESCA 0x04
|
||||
#define ECB2_ZPCI_LSI 0x02
|
||||
__u8 ecb2; /* 0x0062 */
|
||||
#define ECB3_AISI 0x20
|
||||
#define ECB3_AISII 0x10
|
||||
#define ECB3_DEA 0x08
|
||||
#define ECB3_AES 0x04
|
||||
#define ECB3_RI 0x01
|
||||
__u8 ecb3; /* 0x0063 */
|
||||
#define ESCA_SCAOL_MASK ~0x3fU
|
||||
__u32 scaol; /* 0x0064 */
|
||||
__u8 sdf; /* 0x0068 */
|
||||
__u8 epdx; /* 0x0069 */
|
||||
__u8 cpnc; /* 0x006a */
|
||||
__u8 reserved6b; /* 0x006b */
|
||||
__u32 todpr; /* 0x006c */
|
||||
#define GISA_FORMAT1 0x00000001
|
||||
__u32 gd; /* 0x0070 */
|
||||
__u8 reserved74[12]; /* 0x0074 */
|
||||
__u64 mso; /* 0x0080 */
|
||||
__u64 msl; /* 0x0088 */
|
||||
psw_t gpsw; /* 0x0090 */
|
||||
__u64 gg14; /* 0x00a0 */
|
||||
__u64 gg15; /* 0x00a8 */
|
||||
__u8 reservedb0[8]; /* 0x00b0 */
|
||||
#define HPID_KVM 0x4
|
||||
#define HPID_VSIE 0x5
|
||||
__u8 hpid; /* 0x00b8 */
|
||||
__u8 reservedb9[7]; /* 0x00b9 */
|
||||
union {
|
||||
struct {
|
||||
__u32 eiparams; /* 0x00c0 */
|
||||
__u16 extcpuaddr; /* 0x00c4 */
|
||||
__u16 eic; /* 0x00c6 */
|
||||
};
|
||||
__u64 mcic; /* 0x00c0 */
|
||||
} __packed;
|
||||
__u32 reservedc8; /* 0x00c8 */
|
||||
union {
|
||||
struct {
|
||||
__u16 pgmilc; /* 0x00cc */
|
||||
__u16 iprcc; /* 0x00ce */
|
||||
};
|
||||
__u32 edc; /* 0x00cc */
|
||||
} __packed;
|
||||
union {
|
||||
struct {
|
||||
__u32 dxc; /* 0x00d0 */
|
||||
__u16 mcn; /* 0x00d4 */
|
||||
__u8 perc; /* 0x00d6 */
|
||||
__u8 peratmid; /* 0x00d7 */
|
||||
};
|
||||
__u64 faddr; /* 0x00d0 */
|
||||
} __packed;
|
||||
__u64 peraddr; /* 0x00d8 */
|
||||
__u8 eai; /* 0x00e0 */
|
||||
__u8 peraid; /* 0x00e1 */
|
||||
__u8 oai; /* 0x00e2 */
|
||||
__u8 armid; /* 0x00e3 */
|
||||
__u8 reservede4[4]; /* 0x00e4 */
|
||||
union {
|
||||
__u64 tecmc; /* 0x00e8 */
|
||||
struct {
|
||||
__u16 subchannel_id; /* 0x00e8 */
|
||||
__u16 subchannel_nr; /* 0x00ea */
|
||||
__u32 io_int_parm; /* 0x00ec */
|
||||
__u32 io_int_word; /* 0x00f0 */
|
||||
};
|
||||
} __packed;
|
||||
__u8 reservedf4[8]; /* 0x00f4 */
|
||||
#define CRYCB_FORMAT_MASK 0x00000003
|
||||
#define CRYCB_FORMAT0 0x00000000
|
||||
#define CRYCB_FORMAT1 0x00000001
|
||||
#define CRYCB_FORMAT2 0x00000003
|
||||
__u32 crycbd; /* 0x00fc */
|
||||
__u64 gcr[16]; /* 0x0100 */
|
||||
union {
|
||||
__u64 gbea; /* 0x0180 */
|
||||
__u64 sidad;
|
||||
};
|
||||
__u8 reserved188[8]; /* 0x0188 */
|
||||
__u64 sdnxo; /* 0x0190 */
|
||||
__u8 reserved198[8]; /* 0x0198 */
|
||||
__u32 fac; /* 0x01a0 */
|
||||
__u8 reserved1a4[20]; /* 0x01a4 */
|
||||
__u64 cbrlo; /* 0x01b8 */
|
||||
__u8 reserved1c0[8]; /* 0x01c0 */
|
||||
#define ECD_HOSTREGMGMT 0x20000000
|
||||
#define ECD_MEF 0x08000000
|
||||
#define ECD_ETOKENF 0x02000000
|
||||
#define ECD_ECC 0x00200000
|
||||
#define ECD_HMAC 0x00004000
|
||||
__u32 ecd; /* 0x01c8 */
|
||||
__u8 reserved1cc[18]; /* 0x01cc */
|
||||
__u64 pp; /* 0x01de */
|
||||
__u8 reserved1e6[2]; /* 0x01e6 */
|
||||
__u64 itdba; /* 0x01e8 */
|
||||
__u64 riccbd; /* 0x01f0 */
|
||||
__u64 gvrd; /* 0x01f8 */
|
||||
} __packed __aligned(512);
|
||||
|
||||
struct kvm_s390_itdb {
|
||||
__u8 data[256];
|
||||
};
|
||||
|
||||
struct sie_page {
|
||||
struct kvm_s390_sie_block sie_block;
|
||||
struct mcck_volatile_info mcck_info; /* 0x0200 */
|
||||
__u8 reserved218[360]; /* 0x0218 */
|
||||
__u64 pv_grregs[16]; /* 0x0380 */
|
||||
__u8 reserved400[512]; /* 0x0400 */
|
||||
struct kvm_s390_itdb itdb; /* 0x0600 */
|
||||
__u8 reserved700[2304]; /* 0x0700 */
|
||||
};
|
||||
|
||||
#endif /* _ASM_KVM_HOST_TYPES_H */
|
||||
#endif /* ASM_KVM_HOST_TYPES_H */
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#define MCCK_CODE_SYSTEM_DAMAGE BIT(63)
|
||||
#define MCCK_CODE_EXT_DAMAGE BIT(63 - 5)
|
||||
#define MCCK_CODE_CP BIT(63 - 9)
|
||||
#define MCCK_CODE_CK BIT(63 - 11)
|
||||
#define MCCK_CODE_STG_ERROR BIT(63 - 16)
|
||||
#define MCCK_CODE_STG_KEY_ERROR BIT(63 - 18)
|
||||
#define MCCK_CODE_STG_DEGRAD BIT(63 - 19)
|
||||
|
|
@ -33,6 +34,8 @@
|
|||
#define MCCK_CODE_FC_VALID BIT(63 - 43)
|
||||
#define MCCK_CODE_CPU_TIMER_VALID BIT(63 - 46)
|
||||
|
||||
#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE | MCCK_CODE_CK)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
union mci {
|
||||
|
|
|
|||
|
|
@ -344,8 +344,7 @@ static void notrace s390_backup_mcck_info(struct pt_regs *regs)
|
|||
|
||||
sie_page = container_of(sie_block, struct sie_page, sie_block);
|
||||
mcck_backup = &sie_page->mcck_info;
|
||||
mcck_backup->mcic = get_lowcore()->mcck_interruption_code &
|
||||
~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE);
|
||||
mcck_backup->mcic = get_lowcore()->mcck_interruption_code & ~MCCK_CODE_NO_GUEST;
|
||||
mcck_backup->ext_damage_code = get_lowcore()->external_damage_code;
|
||||
mcck_backup->failing_storage_address = get_lowcore()->failing_storage_address;
|
||||
}
|
||||
|
|
@ -357,8 +356,6 @@ NOKPROBE_SYMBOL(s390_backup_mcck_info);
|
|||
#define ED_STP_ISLAND 6 /* External damage STP island check */
|
||||
#define ED_STP_SYNC 7 /* External damage STP sync check */
|
||||
|
||||
#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE)
|
||||
|
||||
/*
|
||||
* machine check handler.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,13 +3,4 @@
|
|||
#
|
||||
# Copyright IBM Corp. 2008
|
||||
|
||||
include $(srctree)/virt/kvm/Makefile.kvm
|
||||
|
||||
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
|
||||
|
||||
kvm-y += kvm-s390.o intercept.o interrupt.o priv.o sigp.o
|
||||
kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o
|
||||
kvm-y += dat.o gmap.o faultin.o
|
||||
|
||||
kvm-$(CONFIG_VFIO_PCI_ZDEV_KVM) += pci.o
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
obj-$(CONFIG_KVM) += s390/
|
||||
|
|
|
|||
5
arch/s390/kvm/gmap/Makefile
Normal file
5
arch/s390/kvm/gmap/Makefile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
GMAP ?= ../gmap
|
||||
|
||||
gmap-y += $(GMAP)/dat.o $(GMAP)/gmap.o $(GMAP)/faultin.o $(GMAP)/kvm_mmu.o
|
||||
|
|
@ -613,6 +613,7 @@ long _dat_walk_gfn_range(gfn_t start, gfn_t end, union asce asce,
|
|||
return dat_crste_walk_range(start, min(end, asce_end(asce)), table, &walk);
|
||||
}
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey)
|
||||
{
|
||||
union crste *crstep;
|
||||
|
|
@ -722,9 +723,12 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep)
|
||||
if (!ptep) {
|
||||
if (!oldkey)
|
||||
oldkey = &prev;
|
||||
return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey,
|
||||
nq, mr, mc);
|
||||
}
|
||||
|
||||
old = pgste_get_lock(ptep);
|
||||
pgste = old;
|
||||
|
|
@ -734,6 +738,7 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
pgste.fp = skey.fp;
|
||||
pgste.gc = skey.c;
|
||||
pgste.gr = skey.r;
|
||||
prev.skey = 0;
|
||||
|
||||
if (!ptep->h.i) {
|
||||
rc = page_cond_set_storage_key(pte_origin(*ptep), skey, &prev, nq, mr, mc);
|
||||
|
|
@ -755,13 +760,15 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
return rc;
|
||||
}
|
||||
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn)
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
|
||||
{
|
||||
union pgste pgste, old;
|
||||
union crste *crstep;
|
||||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
skey->skey = 0;
|
||||
|
||||
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
@ -771,21 +778,23 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn)
|
|||
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
return 0;
|
||||
return page_reset_referenced(large_crste_to_phys(*crstep, gfn));
|
||||
skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1;
|
||||
return 0;
|
||||
}
|
||||
old = pgste_get_lock(ptep);
|
||||
pgste = old;
|
||||
|
||||
if (!ptep->h.i) {
|
||||
rc = page_reset_referenced(pte_origin(*ptep));
|
||||
pgste.hr = rc >> 1;
|
||||
skey->skey = page_reset_referenced(pte_origin(*ptep)) << 1;
|
||||
pgste.hr = skey->r;
|
||||
}
|
||||
rc |= (pgste.gr << 1) | pgste.gc;
|
||||
skey->r |= pgste.gr;
|
||||
skey->c |= pgste.gc;
|
||||
pgste.gr = 0;
|
||||
|
||||
dat_update_ptep_sd(old, pgste, ptep);
|
||||
pgste_set_unlock(ptep, pgste);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
|
|
@ -835,6 +844,7 @@ long dat_reset_skeys(union asce asce, gfn_t start)
|
|||
|
||||
return _dat_walk_gfn_range(start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, NULL);
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
struct slot_priv {
|
||||
unsigned long token;
|
||||
|
|
@ -846,6 +856,7 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
|
|||
struct slot_priv *p = walk->priv;
|
||||
union crste dummy = { .val = p->token };
|
||||
union pte new_pte, pte = READ_ONCE(*ptep);
|
||||
union pgste pgste;
|
||||
|
||||
new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);
|
||||
|
||||
|
|
@ -853,7 +864,11 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
|
|||
if (pte.val == new_pte.val)
|
||||
return 0;
|
||||
|
||||
dat_ptep_xchg(ptep, new_pte, gfn, walk->asce, false);
|
||||
pgste = pgste_get_lock(ptep);
|
||||
pgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk->asce, false);
|
||||
pgste.cmma_d = 0;
|
||||
pgste_set_unlock(ptep, pgste);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -911,11 +926,8 @@ static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgst
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!pgstes[i].pcl)
|
||||
break;
|
||||
for (i = 0; i < n; i++)
|
||||
pgste_set_unlock(first + i, pgstes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgstes)
|
||||
|
|
@ -928,7 +940,7 @@ static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgs
|
|||
}
|
||||
if (i == n)
|
||||
return true;
|
||||
pgste_set_unlock_multiple(first, n, pgstes);
|
||||
pgste_set_unlock_multiple(first, i, pgstes);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1000,6 +1012,7 @@ bool dat_test_age_gfn(union asce asce, gfn_t start, gfn_t end)
|
|||
return _dat_walk_gfn_range(start, end, asce, &test_age_ops, 0, NULL) > 0;
|
||||
}
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
static long dat_set_pn_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
union crste newcrste, oldcrste;
|
||||
|
|
@ -1325,3 +1338,4 @@ int dat_set_cmma_bits(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
|
|||
}
|
||||
return _dat_walk_gfn_range(gfn, gfn + count, asce, &ops, DAT_WALK_IGN_HOLES, &state);
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
|
@ -6,9 +6,10 @@
|
|||
* Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#ifndef __KVM_S390_DAT_H
|
||||
#define __KVM_S390_DAT_H
|
||||
#ifndef ARCH_KVM_GMAP_DAT_H
|
||||
#define ARCH_KVM_GMAP_DAT_H
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/radix-tree.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -532,28 +533,42 @@ int dat_entry_walk(struct kvm_s390_mmu_cache *mc, gfn_t gfn, union asce asce, in
|
|||
void dat_free_level(struct crst_table *table, bool owns_ptes);
|
||||
struct crst_table *dat_alloc_crst_sleepable(unsigned long init);
|
||||
int dat_set_asce_limit(struct kvm_s390_mmu_cache *mc, union asce *asce, int newtype);
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey);
|
||||
int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
|
||||
union skey skey, bool nq);
|
||||
int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,
|
||||
union skey skey, union skey *oldkey, bool nq, bool mr, bool mc);
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn);
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey);
|
||||
long dat_reset_skeys(union asce asce, gfn_t start);
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param);
|
||||
void dat_set_ptval(struct page_table *table, struct ptval_param param, unsigned long val);
|
||||
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
|
||||
u16 type, u16 param);
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
int dat_set_prefix_notif_bit(union asce asce, gfn_t gfn);
|
||||
#else
|
||||
static inline int dat_set_prefix_notif_bit(union asce asce, gfn_t gfn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
bool dat_test_age_gfn(union asce asce, gfn_t start, gfn_t end);
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
int dat_perform_essa(union asce asce, gfn_t gfn, int orc, union essa_state *state, bool *dirty);
|
||||
long dat_reset_cmma(union asce asce, gfn_t start_gfn);
|
||||
int dat_peek_cmma(gfn_t start, union asce asce, unsigned int *count, u8 *values);
|
||||
int dat_get_cmma(union asce asce, gfn_t *start, unsigned int *count, u8 *values, atomic64_t *rem);
|
||||
int dat_set_cmma_bits(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
|
||||
unsigned long count, unsigned long mask, const uint8_t *bits);
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc);
|
||||
|
||||
|
|
@ -975,4 +990,4 @@ static inline bool crste_is_ucas(union crste crste)
|
|||
return is_pmd(crste) && crste.h.i && crste.h.fc0.tl == 1 && crste.h.fc == 0;
|
||||
}
|
||||
|
||||
#endif /* __KVM_S390_DAT_H */
|
||||
#endif /* ARCH_KVM_GMAP_DAT_H */
|
||||
|
|
@ -9,10 +9,11 @@
|
|||
#include <linux/kvm_host.h>
|
||||
|
||||
#include "gmap.h"
|
||||
#include "trace.h"
|
||||
#include "faultin.h"
|
||||
|
||||
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu);
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace_gmap.h"
|
||||
|
||||
/*
|
||||
* kvm_s390_faultin_gfn() - handle a dat fault.
|
||||
|
|
@ -91,9 +92,9 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa
|
|||
/* Access outside memory, addressing exception. */
|
||||
if (is_noslot_pfn(f->pfn))
|
||||
return PGM_ADDRESSING;
|
||||
/* Signal pending: try again. */
|
||||
if (f->pfn == KVM_PFN_ERR_SIGPENDING)
|
||||
return -EAGAIN;
|
||||
/* Fatal signal pending: bail out. */
|
||||
if (is_sigpending_pfn(f->pfn))
|
||||
return -EINTR;
|
||||
/* Check if it's read-only memory; don't try to actually handle that case. */
|
||||
if (f->pfn == KVM_PFN_ERR_RO_FAULT)
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
* Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#ifndef __KVM_S390_FAULTIN_H
|
||||
#define __KVM_S390_FAULTIN_H
|
||||
#ifndef ARCH_KVM_GMAP_FAULTIN_H
|
||||
#define ARCH_KVM_GMAP_FAULTIN_H
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
|
|
@ -89,4 +89,4 @@ static inline int kvm_s390_get_guest_pages(struct kvm *kvm, struct guest_fault *
|
|||
#define kvm_s390_array_needs_retry_safe(kvm, seq, array) \
|
||||
kvm_s390_multiple_faults_need_retry(kvm, seq, array, ARRAY_SIZE(array), false)
|
||||
|
||||
#endif /* __KVM_S390_FAULTIN_H */
|
||||
#endif /* ARCH_KVM_GMAP_FAULTIN_H */
|
||||
|
|
@ -21,14 +21,9 @@
|
|||
|
||||
#include "dat.h"
|
||||
#include "gmap.h"
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "faultin.h"
|
||||
|
||||
static inline bool kvm_s390_is_in_sie(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.sie_block->prog0c & PROG_IN_SIE;
|
||||
}
|
||||
|
||||
static int gmap_limit_to_type(gfn_t limit)
|
||||
{
|
||||
if (!limit)
|
||||
|
|
@ -256,6 +251,12 @@ int s390_replace_asce(struct gmap *gmap)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
static inline bool kvm_s390_is_in_sie(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.sie_block->prog0c & PROG_IN_SIE;
|
||||
}
|
||||
|
||||
bool _gmap_unmap_prefix(struct gmap *gmap, gfn_t gfn, gfn_t end, bool hint)
|
||||
{
|
||||
struct kvm *kvm = gmap->kvm;
|
||||
|
|
@ -278,6 +279,7 @@ bool _gmap_unmap_prefix(struct gmap *gmap, gfn_t gfn, gfn_t end, bool hint)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
struct clear_young_pte_priv {
|
||||
struct gmap *gmap;
|
||||
|
|
@ -945,6 +947,8 @@ void gmap_split_huge_pages(struct gmap *gmap)
|
|||
} while (start);
|
||||
}
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
|
||||
static int _gmap_enable_skeys(struct gmap *gmap)
|
||||
{
|
||||
gfn_t start = 0;
|
||||
|
|
@ -977,6 +981,7 @@ int gmap_enable_skeys(struct gmap *gmap)
|
|||
mmap_write_unlock(gmap->kvm->mm);
|
||||
return rc;
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
static long _destroy_pages_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
|
|
@ -1098,6 +1103,7 @@ int gmap_protect_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gf
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
static long __set_cmma_clean_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
union pgste pgste;
|
||||
|
|
@ -1141,6 +1147,7 @@ void _gmap_set_cmma_all(struct gmap *gmap, bool dirty)
|
|||
cond_resched();
|
||||
} while (gfn);
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
static void gmap_unshadow_level(struct gmap *sg, gfn_t r_gfn, int level)
|
||||
{
|
||||
|
|
@ -7,8 +7,10 @@
|
|||
* Claudio Imbrenda <imbrenda@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#ifndef ARCH_KVM_S390_GMAP_H
|
||||
#define ARCH_KVM_S390_GMAP_H
|
||||
#ifndef ARCH_KVM_GMAP_GMAP_H
|
||||
#define ARCH_KVM_GMAP_GMAP_H
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include "dat.h"
|
||||
|
||||
|
|
@ -83,7 +85,6 @@ struct gmap_cache {
|
|||
for (pos = (head); n = pos ? pos->next : NULL, pos; pos = n)
|
||||
|
||||
int s390_replace_asce(struct gmap *gmap);
|
||||
bool _gmap_unmap_prefix(struct gmap *gmap, gfn_t gfn, gfn_t end, bool hint);
|
||||
bool gmap_age_gfn(struct gmap *gmap, gfn_t start, gfn_t end);
|
||||
bool gmap_unmap_gfn_range(struct gmap *gmap, struct kvm_memory_slot *slot, gfn_t start, gfn_t end);
|
||||
int gmap_try_fixup_minor(struct gmap *gmap, struct guest_fault *fault);
|
||||
|
|
@ -98,13 +99,16 @@ int gmap_set_limit(struct gmap *gmap, gfn_t limit);
|
|||
int gmap_ucas_translate(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, gpa_t *gaddr);
|
||||
int gmap_ucas_map(struct gmap *gmap, gfn_t p_gfn, gfn_t c_gfn, unsigned long count);
|
||||
void gmap_ucas_unmap(struct gmap *gmap, gfn_t c_gfn, unsigned long count);
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
int gmap_enable_skeys(struct gmap *gmap);
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
int gmap_pv_destroy_range(struct gmap *gmap, gfn_t start, gfn_t end, bool interruptible);
|
||||
int gmap_insert_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn,
|
||||
gfn_t r_gfn, int level);
|
||||
int gmap_protect_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn,
|
||||
kvm_pfn_t pfn, int level, bool wr);
|
||||
void _gmap_set_cmma_all(struct gmap *gmap, bool dirty);
|
||||
void _gmap_handle_vsie_unshadow_event(struct gmap *parent, gfn_t gfn);
|
||||
struct gmap *gmap_create_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *gmap,
|
||||
union asce asce, int edat_level);
|
||||
|
|
@ -158,6 +162,14 @@ static inline void gmap_handle_vsie_unshadow_event(struct gmap *parent, gfn_t gf
|
|||
_gmap_handle_vsie_unshadow_event(parent, gfn);
|
||||
}
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
bool _gmap_unmap_prefix(struct gmap *gmap, gfn_t gfn, gfn_t end, bool hint);
|
||||
#else
|
||||
static inline bool _gmap_unmap_prefix(struct gmap *gmap, gfn_t gfn, gfn_t end, bool hint)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
static inline bool gmap_mkold_prefix(struct gmap *gmap, gfn_t gfn, gfn_t end)
|
||||
{
|
||||
return _gmap_unmap_prefix(gmap, gfn, end, true);
|
||||
|
|
@ -198,6 +210,8 @@ static inline bool pte_needs_unshadow(union pte oldpte, union pte newpte, union
|
|||
return !newpte.h.p || !newpte.s.pr;
|
||||
}
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
void _gmap_set_cmma_all(struct gmap *gmap, bool dirty);
|
||||
static inline void gmap_set_cmma_all_dirty(struct gmap *gmap)
|
||||
{
|
||||
_gmap_set_cmma_all(gmap, true);
|
||||
|
|
@ -207,6 +221,7 @@ static inline void gmap_set_cmma_all_clean(struct gmap *gmap)
|
|||
{
|
||||
_gmap_set_cmma_all(gmap, false);
|
||||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
static inline union pgste _gmap_ptep_xchg(struct gmap *gmap, union pte *ptep, union pte newpte,
|
||||
union pgste pgste, gfn_t gfn, bool needs_lock)
|
||||
|
|
@ -330,4 +345,4 @@ static inline bool gmap_is_shadow_valid(struct gmap *sg, union asce asce, int ed
|
|||
return sg->guest_asce.val == asce.val && sg->edat_level == edat_level;
|
||||
}
|
||||
|
||||
#endif /* ARCH_KVM_S390_GMAP_H */
|
||||
#endif /* ARCH_KVM_GMAP_GMAP_H */
|
||||
133
arch/s390/kvm/gmap/kvm_mmu.c
Normal file
133
arch/s390/kvm/gmap/kvm_mmu.c
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/kvm_types.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include "s390.h"
|
||||
#include "gmap.h"
|
||||
#include "dat.h"
|
||||
#include "kvm_mmu.h"
|
||||
|
||||
/*
|
||||
* Get (and clear) the dirty memory log for a memory slot.
|
||||
*/
|
||||
int s390_kvm_mmu_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
|
||||
{
|
||||
int r;
|
||||
unsigned long n;
|
||||
struct kvm_memory_slot *memslot;
|
||||
int is_dirty;
|
||||
|
||||
if (kvm_is_ucontrol(kvm))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
|
||||
r = -EINVAL;
|
||||
if (log->slot >= KVM_USER_MEM_SLOTS)
|
||||
goto out;
|
||||
|
||||
r = kvm_get_dirty_log(kvm, log, &is_dirty, &memslot);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
/* Clear the dirty log */
|
||||
if (is_dirty) {
|
||||
n = kvm_dirty_bitmap_bytes(memslot);
|
||||
memset(memslot->dirty_bitmap, 0, n);
|
||||
}
|
||||
r = 0;
|
||||
out:
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *old,
|
||||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
if (kvm_is_ucontrol(kvm) && new && new->id < KVM_USER_MEM_SLOTS)
|
||||
return -EINVAL;
|
||||
|
||||
/* When we are protected, we should not change the memory slots */
|
||||
if (kvm_s390_pv_get_handle(kvm))
|
||||
return -EINVAL;
|
||||
|
||||
if (change != KVM_MR_DELETE && change != KVM_MR_FLAGS_ONLY) {
|
||||
/*
|
||||
* A few sanity checks. The memory in userland is ok to be
|
||||
* fragmented into various different vmas. It is okay to mmap()
|
||||
* and munmap() stuff in this slot after doing this call at any
|
||||
* time.
|
||||
*/
|
||||
if (new->userspace_addr & ~PAGE_MASK)
|
||||
return -EINVAL;
|
||||
if ((new->base_gfn + new->npages) * PAGE_SIZE > kvm->arch.mem_limit)
|
||||
return -EINVAL;
|
||||
if (!asce_contains_gfn(kvm->arch.gmap->asce, new->base_gfn + new->npages - 1))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!kvm_s390_is_migration_mode(kvm))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Turn off migration mode when:
|
||||
* - userspace creates a new memslot with dirty logging off,
|
||||
* - userspace modifies an existing memslot (MOVE or FLAGS_ONLY) and
|
||||
* dirty logging is turned off.
|
||||
* Migration mode expects dirty page logging being enabled to store
|
||||
* its dirty bitmap.
|
||||
*/
|
||||
if (change != KVM_MR_DELETE &&
|
||||
!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
|
||||
WARN(kvm_s390_vm_stop_migration(kvm),
|
||||
"Failed to stop migration mode");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s390_kvm_mmu_commit_memory_region(struct kvm *kvm,
|
||||
struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
|
||||
int rc = 0;
|
||||
|
||||
guard(mutex)(&kvm->slots_arch_lock);
|
||||
|
||||
if (change == KVM_MR_FLAGS_ONLY)
|
||||
return;
|
||||
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
scoped_guard(write_lock, &kvm->mmu_lock) {
|
||||
kvm_s390_update_cmma_dirty(kvm, old);
|
||||
switch (change) {
|
||||
case KVM_MR_DELETE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
break;
|
||||
case KVM_MR_MOVE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
if (rc)
|
||||
break;
|
||||
fallthrough;
|
||||
case KVM_MR_CREATE:
|
||||
rc = dat_create_slot(mc, kvm->arch.gmap->asce, new->base_gfn, new->npages);
|
||||
break;
|
||||
case KVM_MR_FLAGS_ONLY:
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (rc)
|
||||
pr_warn("failed to commit memory region\n");
|
||||
}
|
||||
18
arch/s390/kvm/gmap/kvm_mmu.h
Normal file
18
arch/s390/kvm/gmap/kvm_mmu.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef ARCH_KVM_GMAP_KVM_MMU_H
|
||||
#define ARCH_KVM_GMAP_KVM_MMU_H
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
int s390_kvm_mmu_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log);
|
||||
int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *old,
|
||||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change);
|
||||
void s390_kvm_mmu_commit_memory_region(struct kvm *kvm,
|
||||
struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change);
|
||||
|
||||
#endif /* ARCH_KVM_GMAP_KVM_MMU_H */
|
||||
47
arch/s390/kvm/gmap/trace_gmap.h
Normal file
47
arch/s390/kvm/gmap/trace_gmap.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#if !defined(GMAP_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define GMAP_TRACE_KVM_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM kvm
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH ../gmap
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace_gmap
|
||||
|
||||
#define __KVM_FIELDS \
|
||||
__field(unsigned long, pswmask) \
|
||||
__field(unsigned long, pswaddr)
|
||||
#define __KVM_ASSIGN ({\
|
||||
__entry->pswmask = vcpu->arch.sie_block->gpsw.mask; \
|
||||
__entry->pswaddr = vcpu->arch.sie_block->gpsw.addr; \
|
||||
})
|
||||
#define __KVM_PRINT \
|
||||
__entry->pswmask,\
|
||||
__entry->pswaddr
|
||||
|
||||
TRACE_EVENT(kvm_s390_major_guest_pfault,
|
||||
TP_PROTO(struct kvm_vcpu *vcpu),
|
||||
TP_ARGS(vcpu),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(int, id)
|
||||
__KVM_FIELDS
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->id = vcpu->vcpu_id;
|
||||
__KVM_ASSIGN
|
||||
),
|
||||
TP_printk("%02d[%016lx-%016lx]: major fault, maybe applicable for pfault",
|
||||
__entry->id,
|
||||
__KVM_PRINT
|
||||
)
|
||||
);
|
||||
|
||||
#endif /* GMAP_TRACE_KVM_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
14
arch/s390/kvm/s390/Makefile
Normal file
14
arch/s390/kvm/s390/Makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
KVM := ../../../../virt/kvm
|
||||
include $(srctree)/virt/kvm/Makefile.kvm
|
||||
include $(srctree)/arch/s390/kvm/gmap/Makefile
|
||||
|
||||
ccflags-y := -I$(src) -I$(srctree)/arch/s390/kvm/gmap
|
||||
|
||||
kvm-y += s390.o intercept.o interrupt.o priv.o sigp.o
|
||||
kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o
|
||||
kvm-y += $(gmap-y)
|
||||
|
||||
kvm-$(CONFIG_VFIO_PCI_ZDEV_KVM) += pci.o
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
#include <linux/kvm_host.h>
|
||||
#include <asm/gmap_helpers.h>
|
||||
#include <asm/virtio-ccw.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "trace.h"
|
||||
#include "trace-s390.h"
|
||||
#include "gaccess.h"
|
||||
|
|
@ -186,7 +186,8 @@ static int diag9c_forwarding_overrun(void)
|
|||
static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_vcpu *tcpu;
|
||||
int tcpu_cpu;
|
||||
const char *result;
|
||||
int tcpu_cpu = -1;
|
||||
int tid;
|
||||
|
||||
tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
|
||||
|
|
@ -211,21 +212,23 @@ static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
|
|||
if (!vcpu_is_preempted(tcpu_cpu))
|
||||
goto no_yield;
|
||||
smp_yield_cpu(tcpu_cpu);
|
||||
VCPU_EVENT(vcpu, 5,
|
||||
"diag time slice end directed to %d: yield forwarded",
|
||||
tid);
|
||||
vcpu->stat.diag_9c_forward++;
|
||||
return 0;
|
||||
result = "yield forwarded";
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kvm_vcpu_yield_to(tcpu) <= 0)
|
||||
goto no_yield;
|
||||
|
||||
VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: done", tid);
|
||||
return 0;
|
||||
result = "done";
|
||||
goto out;
|
||||
no_yield:
|
||||
VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: ignored", tid);
|
||||
vcpu->stat.diag_9c_ignored++;
|
||||
result = "ignored";
|
||||
out:
|
||||
VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: %s", tid,
|
||||
result);
|
||||
trace_kvm_s390_diag_9c(vcpu, tid, tcpu_cpu, result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
#include <asm/access-regs.h>
|
||||
#include <asm/fault.h>
|
||||
#include <asm/dat-bits.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "dat.h"
|
||||
#include "gmap.h"
|
||||
#include "gaccess.h"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
#include <linux/kvm_host.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
|
||||
/**
|
||||
* kvm_s390_real_to_abs - convert guest real address to guest absolute address
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/errno.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "gaccess.h"
|
||||
|
||||
/*
|
||||
|
|
@ -184,7 +184,7 @@ static int __import_wp_info(struct kvm_vcpu *vcpu,
|
|||
if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL_ACCOUNT);
|
||||
wp_info->old_data = kmalloc(wp_info->len, GFP_KERNEL_ACCOUNT);
|
||||
if (!wp_info->old_data)
|
||||
return -ENOMEM;
|
||||
/* try to backup the original value */
|
||||
|
|
@ -252,7 +252,7 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
|
|||
ret = __import_wp_info(vcpu, &bp_data[i],
|
||||
&wp_info[nr_wp]);
|
||||
if (ret)
|
||||
goto error;
|
||||
goto error_wp;
|
||||
nr_wp++;
|
||||
break;
|
||||
case KVM_HW_BP:
|
||||
|
|
@ -267,7 +267,12 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
|
|||
vcpu->arch.guestdbg.hw_bp_info = bp_info;
|
||||
vcpu->arch.guestdbg.nr_hw_wp = nr_wp;
|
||||
vcpu->arch.guestdbg.hw_wp_info = wp_info;
|
||||
kfree(bp_data);
|
||||
return 0;
|
||||
|
||||
error_wp:
|
||||
while (nr_wp--)
|
||||
kfree(wp_info[nr_wp].old_data);
|
||||
error:
|
||||
kfree(bp_data);
|
||||
kfree(wp_info);
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
#include <asm/sysinfo.h>
|
||||
#include <asm/uv.h>
|
||||
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "gaccess.h"
|
||||
#include "trace.h"
|
||||
#include "trace-s390.h"
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
#include <asm/nmi.h>
|
||||
#include <asm/airq.h>
|
||||
#include <asm/tpi.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "gaccess.h"
|
||||
#include "trace-s390.h"
|
||||
#include "pci.h"
|
||||
|
|
@ -45,13 +45,16 @@ static struct kvm_s390_gib *gib;
|
|||
static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
|
||||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
union esca_sigp_ctrl sigp_ctrl;
|
||||
|
||||
if (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND))
|
||||
return 0;
|
||||
if (kvm_is_ucontrol(vcpu->kvm))
|
||||
return 0;
|
||||
|
||||
BUG_ON(!kvm_s390_use_sca_entries());
|
||||
|
||||
sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
if (src_id)
|
||||
*src_id = sigp_ctrl.scn;
|
||||
|
||||
|
|
@ -60,13 +63,16 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
|
|||
|
||||
static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
|
||||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
union esca_sigp_ctrl old_val, new_val = {.scn = src_id, .c = 1};
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl *sigp_ctrl;
|
||||
int expect, rc;
|
||||
|
||||
BUG_ON(!kvm_s390_use_sca_entries());
|
||||
if (kvm_is_ucontrol(vcpu->kvm))
|
||||
return -EINVAL;
|
||||
|
||||
sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
old_val = READ_ONCE(*sigp_ctrl);
|
||||
old_val.c = 0;
|
||||
|
||||
|
|
@ -84,10 +90,13 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
|
|||
static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
union esca_sigp_ctrl *sigp_ctrl;
|
||||
|
||||
if (!kvm_s390_use_sca_entries())
|
||||
if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized || kvm_is_ucontrol(vcpu->kvm))
|
||||
return;
|
||||
|
||||
/* Initialize after the above check, to prevent going out of bounds */
|
||||
sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
|
||||
|
||||
WRITE_ONCE(sigp_ctrl->value, 0);
|
||||
|
|
@ -264,6 +273,11 @@ static inline int gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
|
|||
return test_and_clear_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa);
|
||||
}
|
||||
|
||||
static inline int gisa_test_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
|
||||
{
|
||||
return test_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *)gisa);
|
||||
}
|
||||
|
||||
static inline unsigned long pending_irqs_no_gisa(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long pending = vcpu->kvm->arch.float_int.pending_irqs |
|
||||
|
|
@ -1541,23 +1555,21 @@ static int __inject_set_prefix(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
|||
}
|
||||
|
||||
#define KVM_S390_STOP_SUPP_FLAGS (KVM_S390_STOP_FLAG_STORE_STATUS)
|
||||
static int __inject_sigp_stop(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
static int __inject_sigp_stop(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq, bool *storestatus)
|
||||
{
|
||||
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
||||
struct kvm_s390_stop_info *stop = &li->irq.stop;
|
||||
int rc = 0;
|
||||
|
||||
vcpu->stat.inject_stop_signal++;
|
||||
trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_SIGP_STOP, 0, 0);
|
||||
|
||||
if (irq->u.stop.flags & ~KVM_S390_STOP_SUPP_FLAGS)
|
||||
return -EINVAL;
|
||||
|
||||
if (is_vcpu_stopped(vcpu)) {
|
||||
if (irq->u.stop.flags & KVM_S390_STOP_FLAG_STORE_STATUS)
|
||||
rc = kvm_s390_store_status_unloaded(vcpu,
|
||||
KVM_S390_STORE_STATUS_NOADDR);
|
||||
return rc;
|
||||
if (!(irq->u.stop.flags & KVM_S390_STOP_FLAG_STORE_STATUS))
|
||||
return 0;
|
||||
*storestatus = true;
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(IRQ_PEND_SIGP_STOP, &li->pending_irqs))
|
||||
|
|
@ -2093,7 +2105,7 @@ void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu)
|
|||
spin_unlock(&li->lock);
|
||||
}
|
||||
|
||||
static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq, bool *storestatus)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -2105,7 +2117,7 @@ static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
|||
rc = __inject_set_prefix(vcpu, irq);
|
||||
break;
|
||||
case KVM_S390_SIGP_STOP:
|
||||
rc = __inject_sigp_stop(vcpu, irq);
|
||||
rc = __inject_sigp_stop(vcpu, irq, storestatus);
|
||||
break;
|
||||
case KVM_S390_RESTART:
|
||||
rc = __inject_sigp_restart(vcpu);
|
||||
|
|
@ -2141,11 +2153,16 @@ static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
|||
int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
{
|
||||
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
||||
bool storestatus = false;
|
||||
int rc;
|
||||
|
||||
spin_lock(&li->lock);
|
||||
rc = do_inject_vcpu(vcpu, irq);
|
||||
rc = do_inject_vcpu(vcpu, irq, &storestatus);
|
||||
spin_unlock(&li->lock);
|
||||
|
||||
if (rc == -EWOULDBLOCK && storestatus)
|
||||
rc = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
|
||||
if (!rc)
|
||||
kvm_s390_vcpu_wakeup(vcpu);
|
||||
return rc;
|
||||
|
|
@ -2233,7 +2250,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
|
|||
ret = -ENOMEM;
|
||||
goto out_nolock;
|
||||
}
|
||||
if (gisa_tac_ipm_gisc(gi->origin, i)) {
|
||||
if (gisa_test_ipm_gisc(gi->origin, i)) {
|
||||
irq = (struct kvm_s390_irq *) &buf[n];
|
||||
irq->type = KVM_S390_INT_IO(1, 0, 0, 0);
|
||||
irq->u.io.io_int_word = isc_to_int_word(i);
|
||||
|
|
@ -2520,8 +2537,22 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
map->addr = host_addr;
|
||||
map->page = pin_map_page(kvm, host_addr, FOLL_LONGTERM);
|
||||
if (!map->page) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
/*
|
||||
* Long-term pinning may fail for memory types such as file-backed
|
||||
* memory. Verify that short-term pinning succeeds so that the
|
||||
* non-atomic irqfd path can handle interrupt injection.
|
||||
*/
|
||||
map->page = pin_map_page(kvm, host_addr, 0);
|
||||
if (!map->page) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
unpin_user_page(map->page);
|
||||
map->page = NULL;
|
||||
map->pinned = false;
|
||||
/* Add an entry to preserve MAP/UNMAP symmetry. */
|
||||
} else {
|
||||
map->pinned = true;
|
||||
}
|
||||
spin_lock_irqsave(&adapter->maps_lock, flags);
|
||||
if (adapter->nr_maps < MAX_S390_ADAPTER_MAPS) {
|
||||
|
|
@ -2532,7 +2563,7 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
ret = -EINVAL;
|
||||
}
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
if (ret)
|
||||
if (ret && map->page)
|
||||
unpin_user_page(map->page);
|
||||
out:
|
||||
if (ret)
|
||||
|
|
@ -2546,6 +2577,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
struct s390_map_info *map, *tmp, *map_to_free;
|
||||
struct page *map_page_to_put = NULL;
|
||||
u64 map_addr_to_mark = 0;
|
||||
bool map_pinned = false;
|
||||
unsigned long flags;
|
||||
int found = 0, idx;
|
||||
|
||||
|
|
@ -2560,6 +2592,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
list_del(&map->list);
|
||||
map_page_to_put = map->page;
|
||||
map_addr_to_mark = map->guest_addr;
|
||||
map_pinned = map->pinned;
|
||||
map_to_free = map;
|
||||
break;
|
||||
}
|
||||
|
|
@ -2568,11 +2601,18 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
|
||||
if (found) {
|
||||
kfree(map_to_free);
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT);
|
||||
set_page_dirty_lock(map_page_to_put);
|
||||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
unpin_user_page(map_page_to_put);
|
||||
if (map_pinned) {
|
||||
/*
|
||||
* Only long-term pinned pages need to be marked dirty
|
||||
* and released. Fallback entries exist only for
|
||||
* MAP/UNMAP symmetry.
|
||||
*/
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT);
|
||||
set_page_dirty_lock(map_page_to_put);
|
||||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
unpin_user_page(map_page_to_put);
|
||||
}
|
||||
}
|
||||
|
||||
return found ? 0 : -ENOENT;
|
||||
|
|
@ -2598,11 +2638,13 @@ void kvm_s390_unmap_all_adapters(struct kvm *kvm)
|
|||
|
||||
list_for_each_entry_safe(map, tmp, &local_list, list) {
|
||||
list_del(&map->list);
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT);
|
||||
set_page_dirty_lock(map->page);
|
||||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
unpin_user_page(map->page);
|
||||
if (map->pinned) {
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT);
|
||||
set_page_dirty_lock(map->page);
|
||||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
unpin_user_page(map->page);
|
||||
}
|
||||
kfree(map);
|
||||
}
|
||||
}
|
||||
|
|
@ -2929,8 +2971,11 @@ static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter,
|
|||
return NULL;
|
||||
|
||||
list_for_each_entry(map, &adapter->maps, list) {
|
||||
if (map->addr == addr)
|
||||
if (map->addr == addr) {
|
||||
if (!map->pinned)
|
||||
return NULL;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -2976,7 +3021,7 @@ static int adapter_indicators_set(struct kvm *kvm,
|
|||
if (!summary_info) {
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0);
|
||||
if (WARN_ON_ONCE(!summary_page))
|
||||
if (!summary_page)
|
||||
return -1;
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
map = page_address(summary_page);
|
||||
|
|
@ -3071,9 +3116,7 @@ static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e,
|
|||
void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu,
|
||||
struct mcck_volatile_info *mcck_info)
|
||||
{
|
||||
struct kvm_s390_interrupt_info inti;
|
||||
struct kvm_s390_irq irq;
|
||||
struct kvm_s390_mchk_info *mchk;
|
||||
struct kvm_s390_irq irq = {};
|
||||
union mci mci;
|
||||
__u64 cr14 = 0; /* upper bits are not used */
|
||||
int rc;
|
||||
|
|
@ -3092,20 +3135,14 @@ void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu,
|
|||
if (mci.w)
|
||||
cr14 |= CR14_WARNING_SUBMASK;
|
||||
|
||||
mchk = mci.ck ? &inti.mchk : &irq.u.mchk;
|
||||
mchk->cr14 = cr14;
|
||||
mchk->mcic = mcck_info->mcic;
|
||||
mchk->ext_damage_code = mcck_info->ext_damage_code;
|
||||
mchk->failing_storage_address = mcck_info->failing_storage_address;
|
||||
if (mci.ck) {
|
||||
/* Inject the floating machine check */
|
||||
inti.type = KVM_S390_MCHK;
|
||||
rc = __inject_vm(vcpu->kvm, &inti);
|
||||
} else {
|
||||
/* Inject the machine check to specified vcpu */
|
||||
irq.type = KVM_S390_MCHK;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &irq);
|
||||
}
|
||||
irq.u.mchk.cr14 = cr14;
|
||||
irq.u.mchk.mcic = mcck_info->mcic;
|
||||
irq.u.mchk.ext_damage_code = mcck_info->ext_damage_code;
|
||||
irq.u.mchk.failing_storage_address = mcck_info->failing_storage_address;
|
||||
|
||||
/* Inject the machine check to specified vcpu */
|
||||
irq.type = KVM_S390_MCHK;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &irq);
|
||||
WARN_ON_ONCE(rc);
|
||||
}
|
||||
|
||||
|
|
@ -3160,7 +3197,8 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
|
|||
int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, void __user *irqstate, int len)
|
||||
{
|
||||
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
||||
struct kvm_s390_irq *buf;
|
||||
struct kvm_s390_irq *buf __free(kvfree) = NULL;
|
||||
bool tmp, storestatus = false;
|
||||
int r = 0;
|
||||
int n;
|
||||
|
||||
|
|
@ -3168,32 +3206,34 @@ int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, void __user *irqstate, int len
|
|||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user((void *) buf, irqstate, len)) {
|
||||
r = -EFAULT;
|
||||
goto out_free;
|
||||
if (copy_from_user((void *)buf, irqstate, len))
|
||||
return -EFAULT;
|
||||
|
||||
scoped_guard(spinlock, &li->lock) {
|
||||
/*
|
||||
* Don't allow setting the interrupt state
|
||||
* when there are already interrupts pending
|
||||
*/
|
||||
if (li->pending_irqs)
|
||||
return -EBUSY;
|
||||
|
||||
for (n = 0; n < len / sizeof(*buf); n++) {
|
||||
tmp = false;
|
||||
r = do_inject_vcpu(vcpu, &buf[n], &tmp);
|
||||
if (r == -EWOULDBLOCK && tmp) {
|
||||
storestatus = true;
|
||||
r = 0;
|
||||
}
|
||||
if (r)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow setting the interrupt state
|
||||
* when there are already interrupts pending
|
||||
*/
|
||||
spin_lock(&li->lock);
|
||||
if (li->pending_irqs) {
|
||||
r = -EBUSY;
|
||||
goto out_unlock;
|
||||
if (storestatus) {
|
||||
n = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
return r ? r : n;
|
||||
}
|
||||
|
||||
for (n = 0; n < len / sizeof(*buf); n++) {
|
||||
r = do_inject_vcpu(vcpu, &buf[n]);
|
||||
if (r)
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&li->lock);
|
||||
out_free:
|
||||
vfree(buf);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
#include <asm/pci_io.h>
|
||||
#include <asm/sclp.h>
|
||||
#include "pci.h"
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
|
||||
struct zpci_aift *aift;
|
||||
|
||||
|
|
@ -190,40 +190,61 @@ static int kvm_zpci_clear_airq(struct zpci_dev *zdev)
|
|||
return cc ? -EIO : 0;
|
||||
}
|
||||
|
||||
static inline void unaccount_mem(unsigned long nr_pages)
|
||||
static inline void unaccount_mem(struct kvm_zdev *kzdev, unsigned long nr_pages)
|
||||
{
|
||||
struct user_struct *user = get_uid(current_user());
|
||||
struct user_struct *user = kzdev->user_account;
|
||||
struct mm_struct *mm_account = kzdev->mm_account;
|
||||
|
||||
if (user)
|
||||
if (user) {
|
||||
atomic_long_sub(nr_pages, &user->locked_vm);
|
||||
if (current->mm)
|
||||
atomic64_sub(nr_pages, ¤t->mm->pinned_vm);
|
||||
free_uid(user);
|
||||
kzdev->user_account = NULL;
|
||||
}
|
||||
|
||||
if (mm_account) {
|
||||
atomic64_sub(nr_pages, &mm_account->pinned_vm);
|
||||
mmdrop(mm_account);
|
||||
kzdev->mm_account = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int account_mem(unsigned long nr_pages)
|
||||
static inline int account_mem(struct kvm_zdev *kzdev, unsigned long nr_pages)
|
||||
{
|
||||
struct user_struct *user = get_uid(current_user());
|
||||
unsigned long page_limit, cur_pages, new_pages;
|
||||
int rc = 0;
|
||||
|
||||
page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
|
||||
|
||||
cur_pages = atomic_long_read(&user->locked_vm);
|
||||
do {
|
||||
new_pages = cur_pages + nr_pages;
|
||||
if (new_pages > page_limit)
|
||||
return -ENOMEM;
|
||||
if (new_pages > page_limit) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
} while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages));
|
||||
|
||||
atomic64_add(nr_pages, ¤t->mm->pinned_vm);
|
||||
if (current->mm) {
|
||||
mmgrab(current->mm);
|
||||
atomic64_add(nr_pages, ¤t->mm->pinned_vm);
|
||||
}
|
||||
|
||||
kzdev->user_account = user;
|
||||
kzdev->mm_account = current->mm;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
free_uid(user);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
||||
bool assist)
|
||||
{
|
||||
struct page *pages[1], *aibv_page, *aisb_page = NULL;
|
||||
unsigned int msi_vecs, idx;
|
||||
unsigned int msi_vecs, idx, size;
|
||||
struct zpci_gaite *gaite;
|
||||
unsigned long hva, bit;
|
||||
struct kvm *kvm;
|
||||
|
|
@ -237,6 +258,10 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
if (zdev->gisa == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* AIF already enabled for the device */
|
||||
if (zdev->kzdev->fib.fmt0.aibv != 0)
|
||||
return -EINVAL;
|
||||
|
||||
kvm = zdev->kzdev->kvm;
|
||||
msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi);
|
||||
|
||||
|
|
@ -246,6 +271,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
return gisc;
|
||||
|
||||
/* Replace AIBV address */
|
||||
size = BITS_TO_LONGS(msi_vecs + fib->fmt0.aibvo) * sizeof(unsigned long);
|
||||
npages = DIV_ROUND_UP((fib->fmt0.aibv & ~PAGE_MASK) + size, PAGE_SIZE);
|
||||
/* AIBV cannot span more than 1 page */
|
||||
if (npages > 1) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv));
|
||||
npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages);
|
||||
|
|
@ -261,6 +294,12 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
|
||||
/* Pin the guest AISB if one was specified */
|
||||
if (fib->fmt0.sum == 1) {
|
||||
/* AISB must be dword aligned */
|
||||
if (fib->fmt0.aisb & 0x7) {
|
||||
rc = -EINVAL;
|
||||
goto unpin1;
|
||||
}
|
||||
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb));
|
||||
npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM,
|
||||
|
|
@ -275,20 +314,28 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
}
|
||||
|
||||
/* Account for pinned pages, roll back on failure */
|
||||
if (account_mem(pcount))
|
||||
rc = account_mem(zdev->kzdev, pcount);
|
||||
if (rc)
|
||||
goto unpin2;
|
||||
|
||||
/* AISB must be allocated before we can fill in GAITE */
|
||||
mutex_lock(&aift->aift_lock);
|
||||
bit = airq_iv_alloc_bit(aift->sbv);
|
||||
if (bit == -1UL)
|
||||
if (bit == -1UL) {
|
||||
rc = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
zdev->aisb = bit; /* store the summary bit number */
|
||||
zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA |
|
||||
AIRQ_IV_BITLOCK |
|
||||
AIRQ_IV_GUESTVEC,
|
||||
phys_to_virt(fib->fmt0.aibv));
|
||||
|
||||
if (!zdev->aibv) {
|
||||
rc = -ENOMEM;
|
||||
goto free_aisb;
|
||||
}
|
||||
|
||||
spin_lock_irq(&aift->gait_lock);
|
||||
gaite = aift->gait + zdev->aisb;
|
||||
|
||||
|
|
@ -311,21 +358,39 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
aift->kzdev[zdev->aisb] = zdev->kzdev;
|
||||
spin_unlock_irq(&aift->gait_lock);
|
||||
|
||||
/* Update guest FIB for re-issue */
|
||||
fib->fmt0.aisbo = zdev->aisb & 63;
|
||||
fib->fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8;
|
||||
fib->fmt0.isc = gisc;
|
||||
|
||||
/* Save some guest fib values in the host for later use */
|
||||
zdev->kzdev->fib.fmt0.isc = fib->fmt0.isc;
|
||||
zdev->kzdev->fib.fmt0.isc = gisc;
|
||||
zdev->kzdev->fib.fmt0.aibv = fib->fmt0.aibv;
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
|
||||
/* Issue the clp to setup the irq now */
|
||||
rc = kvm_zpci_set_airq(zdev);
|
||||
return rc;
|
||||
if (!rc) {
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Start cleanup */
|
||||
zdev->kzdev->fib.fmt0.isc = 0;
|
||||
zdev->kzdev->fib.fmt0.aibv = 0;
|
||||
|
||||
spin_lock_irq(&aift->gait_lock);
|
||||
gaite->count--;
|
||||
gaite->aisb = 0;
|
||||
gaite->gisc = 0;
|
||||
gaite->aisbo = 0;
|
||||
gaite->gisa = 0;
|
||||
aift->kzdev[zdev->aisb] = NULL;
|
||||
spin_unlock_irq(&aift->gait_lock);
|
||||
|
||||
airq_iv_release(zdev->aibv);
|
||||
zdev->aibv = NULL;
|
||||
|
||||
free_aisb:
|
||||
airq_iv_free_bit(aift->sbv, zdev->aisb);
|
||||
zdev->aisb = 0;
|
||||
unlock:
|
||||
if (pcount > 0)
|
||||
unaccount_mem(zdev->kzdev, pcount);
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
unpin2:
|
||||
if (fib->fmt0.sum == 1)
|
||||
|
|
@ -396,7 +461,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force)
|
|||
pcount++;
|
||||
}
|
||||
if (pcount > 0)
|
||||
unaccount_mem(pcount);
|
||||
unaccount_mem(kzdev, pcount);
|
||||
out:
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
|
||||
|
|
@ -22,6 +22,8 @@ struct kvm_zdev {
|
|||
struct kvm *kvm;
|
||||
struct zpci_fib fib;
|
||||
struct list_head entry;
|
||||
struct user_struct *user_account;
|
||||
struct mm_struct *mm_account;
|
||||
};
|
||||
|
||||
struct zpci_gaite {
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
#include <asm/ap.h>
|
||||
#include <asm/gmap_helpers.h>
|
||||
#include "gaccess.h"
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "trace.h"
|
||||
#include "gmap.h"
|
||||
|
||||
|
|
@ -289,6 +289,7 @@ static int handle_iske(struct kvm_vcpu *vcpu)
|
|||
static int handle_rrbe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long gaddr;
|
||||
union skey skey;
|
||||
int reg1, reg2;
|
||||
int rc;
|
||||
|
||||
|
|
@ -307,12 +308,12 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
|
|||
gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
|
||||
gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
|
||||
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
|
||||
rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr));
|
||||
rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr), &skey);
|
||||
if (rc > 0)
|
||||
return kvm_s390_inject_program_int(vcpu, rc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
kvm_s390_set_psw_cc(vcpu, rc);
|
||||
kvm_s390_set_psw_cc(vcpu, (skey.skey >> 1) & 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1260,8 +1261,9 @@ static int handle_essa(struct kvm_vcpu *vcpu)
|
|||
/* Retry the ESSA instruction */
|
||||
kvm_s390_retry_instr(vcpu);
|
||||
} else {
|
||||
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
|
||||
i = __do_essa(vcpu, orc);
|
||||
scoped_guard(mutex, &vcpu->kvm->slots_arch_lock)
|
||||
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
|
||||
i = __do_essa(vcpu, orc);
|
||||
if (i < 0)
|
||||
return i;
|
||||
/* Account for the possible extra cbrl entry */
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#include <linux/sched/mm.h>
|
||||
#include <linux/mmu_notifier.h>
|
||||
#include <asm/gmap_helpers.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "dat.h"
|
||||
#include "gaccess.h"
|
||||
#include "gmap.h"
|
||||
|
|
@ -242,6 +242,28 @@ static void kvm_s390_clear_pv_state(struct kvm *kvm)
|
|||
kvm->arch.pv.guest_len = 0;
|
||||
kvm->arch.pv.stor_base = 0;
|
||||
kvm->arch.pv.stor_var = NULL;
|
||||
if (kvm->arch.pv.dumping) {
|
||||
kvm_s390_vcpu_unblock_all(kvm);
|
||||
kvm->arch.pv.dumping = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_s390_pv_dispose_cpu(struct kvm_vcpu *vcpu, bool free_stor_base)
|
||||
{
|
||||
if (free_stor_base)
|
||||
free_pages(vcpu->arch.pv.stor_base, get_order(uv_info.guest_cpu_stor_len));
|
||||
free_page((unsigned long)sida_addr(vcpu->arch.sie_block));
|
||||
vcpu->arch.sie_block->pv_handle_cpu = 0;
|
||||
vcpu->arch.sie_block->pv_handle_config = 0;
|
||||
memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
|
||||
vcpu->arch.sie_block->sdf = 0;
|
||||
/*
|
||||
* The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).
|
||||
* Use the reset value of gbea to avoid leaking the kernel pointer of
|
||||
* the just freed sida.
|
||||
*/
|
||||
vcpu->arch.sie_block->gbea = 1;
|
||||
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
|
||||
}
|
||||
|
||||
int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
||||
|
|
@ -258,24 +280,9 @@ int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
|||
WARN_ONCE(cc, "protvirt destroy cpu failed rc %x rrc %x", *rc, *rrc);
|
||||
|
||||
/* Intended memory leak for something that should never happen. */
|
||||
if (!cc)
|
||||
free_pages(vcpu->arch.pv.stor_base,
|
||||
get_order(uv_info.guest_cpu_stor_len));
|
||||
kvm_s390_pv_dispose_cpu(vcpu, !cc);
|
||||
|
||||
free_page((unsigned long)sida_addr(vcpu->arch.sie_block));
|
||||
vcpu->arch.sie_block->pv_handle_cpu = 0;
|
||||
vcpu->arch.sie_block->pv_handle_config = 0;
|
||||
memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
|
||||
vcpu->arch.sie_block->sdf = 0;
|
||||
/*
|
||||
* The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).
|
||||
* Use the reset value of gbea to avoid leaking the kernel pointer of
|
||||
* the just freed sida.
|
||||
*/
|
||||
vcpu->arch.sie_block->gbea = 1;
|
||||
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
|
||||
|
||||
return cc ? EIO : 0;
|
||||
return cc ? -EIO : 0;
|
||||
}
|
||||
|
||||
int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
||||
|
|
@ -319,9 +326,7 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
|||
uvcb.header.rrc);
|
||||
|
||||
if (cc) {
|
||||
u16 dummy;
|
||||
|
||||
kvm_s390_pv_destroy_cpu(vcpu, &dummy, &dummy);
|
||||
kvm_s390_pv_dispose_cpu(vcpu, true);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
|
@ -809,7 +814,7 @@ static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ret && ret != -EAGAIN)
|
||||
if (ret && ret != -EAGAIN && ret != -EINTR)
|
||||
KVM_UV_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx with rc %x rrc %x",
|
||||
uvcb.gaddr, *rc, *rrc);
|
||||
return ret;
|
||||
|
|
@ -50,11 +50,12 @@
|
|||
#include <asm/fpu.h>
|
||||
#include <asm/ap.h>
|
||||
#include <asm/uv.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "gaccess.h"
|
||||
#include "gmap.h"
|
||||
#include "faultin.h"
|
||||
#include "pci.h"
|
||||
#include "kvm_mmu.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
|
|
@ -455,8 +456,7 @@ static void __init kvm_s390_cpu_feat_init(void)
|
|||
!test_facility(3) || !nested)
|
||||
return;
|
||||
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_SIEF2);
|
||||
if (sclp.has_64bscao)
|
||||
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_64BSCAO);
|
||||
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_64BSCAO);
|
||||
if (sclp.has_siif)
|
||||
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_SIIF);
|
||||
if (sclp.has_gpere)
|
||||
|
|
@ -562,16 +562,17 @@ static void __kvm_s390_exit(void)
|
|||
static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op,
|
||||
unsigned long addr, union skey skey)
|
||||
{
|
||||
union asce asce = kvm->arch.gmap->asce;
|
||||
gfn_t gfn = gpa_to_gfn(addr);
|
||||
union asce asce;
|
||||
int r;
|
||||
|
||||
guard(read_lock)(&kvm->mmu_lock);
|
||||
asce = kvm->arch.gmap->asce;
|
||||
|
||||
switch (op) {
|
||||
case KVM_S390_KEYOP_SSKE:
|
||||
r = dat_cond_set_storage_key(mc, asce, gfn, skey, &skey, 0, 0, 0);
|
||||
if (r >= 0)
|
||||
if (r == 0 || r == 1)
|
||||
return skey.skey;
|
||||
break;
|
||||
case KVM_S390_KEYOP_ISKE:
|
||||
|
|
@ -580,14 +581,14 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op
|
|||
return skey.skey;
|
||||
break;
|
||||
case KVM_S390_KEYOP_RRBE:
|
||||
r = dat_reset_reference_bit(asce, gfn);
|
||||
if (r > 0)
|
||||
return r << 1;
|
||||
r = dat_reset_reference_bit(asce, gfn, &skey);
|
||||
if (!r)
|
||||
return skey.skey;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return r;
|
||||
return r > 0 ? -EFAULT : r;
|
||||
}
|
||||
|
||||
/* Section: device related */
|
||||
|
|
@ -746,33 +747,7 @@ static void sca_del_vcpu(struct kvm_vcpu *vcpu);
|
|||
int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
|
||||
struct kvm_dirty_log *log)
|
||||
{
|
||||
int r;
|
||||
unsigned long n;
|
||||
struct kvm_memory_slot *memslot;
|
||||
int is_dirty;
|
||||
|
||||
if (kvm_is_ucontrol(kvm))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
|
||||
r = -EINVAL;
|
||||
if (log->slot >= KVM_USER_MEM_SLOTS)
|
||||
goto out;
|
||||
|
||||
r = kvm_get_dirty_log(kvm, log, &is_dirty, &memslot);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
/* Clear the dirty log */
|
||||
if (is_dirty) {
|
||||
n = kvm_dirty_bitmap_bytes(memslot);
|
||||
memset(memslot->dirty_bitmap, 0, n);
|
||||
}
|
||||
r = 0;
|
||||
out:
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
return r;
|
||||
return s390_kvm_mmu_get_dirty_log(kvm, log);
|
||||
}
|
||||
|
||||
static void icpt_operexc_on_all_vcpus(struct kvm *kvm)
|
||||
|
|
@ -1219,8 +1194,8 @@ static void kvm_s390_sync_request_broadcast(struct kvm *kvm, int req)
|
|||
|
||||
/*
|
||||
* Must be called with kvm->srcu held to avoid races on memslots, and with
|
||||
* kvm->slots_lock to avoid races with ourselves, kvm_s390_vm_stop_migration(),
|
||||
* and kvm_s390_get_cmma_bits().
|
||||
* kvm->slots_arch_lock to avoid races with ourselves,
|
||||
* kvm_s390_vm_stop_migration(), and kvm_s390_get_cmma_bits().
|
||||
*/
|
||||
static int kvm_s390_vm_start_migration(struct kvm *kvm)
|
||||
{
|
||||
|
|
@ -1265,10 +1240,10 @@ static int kvm_s390_vm_start_migration(struct kvm *kvm)
|
|||
}
|
||||
|
||||
/*
|
||||
* Must be called with kvm->slots_lock to avoid races with ourselves,
|
||||
* Must be called with kvm->slots_arch_lock to avoid races with ourselves,
|
||||
* kvm_s390_vm_start_migration() and kvm_s390_get_cmma_bits().
|
||||
*/
|
||||
static int kvm_s390_vm_stop_migration(struct kvm *kvm)
|
||||
int kvm_s390_vm_stop_migration(struct kvm *kvm)
|
||||
{
|
||||
/* migration mode already disabled */
|
||||
if (!kvm->arch.migration_mode)
|
||||
|
|
@ -1300,7 +1275,9 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,
|
|||
{
|
||||
int res = -ENXIO;
|
||||
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
guard(srcu)(&kvm->srcu);
|
||||
guard(mutex)(&kvm->slots_arch_lock);
|
||||
|
||||
switch (attr->attr) {
|
||||
case KVM_S390_VM_MIGRATION_START:
|
||||
res = kvm_s390_vm_start_migration(kvm);
|
||||
|
|
@ -1311,7 +1288,6 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -2214,7 +2190,7 @@ static int kvm_s390_get_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
|
|||
}
|
||||
|
||||
kvfree(keys);
|
||||
return r;
|
||||
return r <= 0 ? r : -EFAULT;
|
||||
}
|
||||
|
||||
static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
|
||||
|
|
@ -2276,7 +2252,7 @@ static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
|
|||
kvm_s390_free_mmu_cache(mc);
|
||||
out:
|
||||
kvfree(keys);
|
||||
return r;
|
||||
return r <= 0 ? r : -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2386,7 +2362,7 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
|
|||
|
||||
set_bit(GMAP_FLAG_USES_CMM, &kvm->arch.gmap->flags);
|
||||
|
||||
return r;
|
||||
return r <= 0 ? r : -EFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2934,6 +2910,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|||
case KVM_S390_INTERRUPT: {
|
||||
struct kvm_s390_interrupt s390int;
|
||||
|
||||
r = -EINVAL;
|
||||
if (kvm_is_ucontrol(kvm))
|
||||
break;
|
||||
r = -EFAULT;
|
||||
if (copy_from_user(&s390int, argp, sizeof(s390int)))
|
||||
break;
|
||||
|
|
@ -2998,9 +2977,8 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|||
r = -EFAULT;
|
||||
if (copy_from_user(&args, argp, sizeof(args)))
|
||||
break;
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
r = kvm_s390_get_cmma_bits(kvm, &args);
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
scoped_guard(mutex, &kvm->slots_arch_lock)
|
||||
r = kvm_s390_get_cmma_bits(kvm, &args);
|
||||
if (!r) {
|
||||
r = copy_to_user(argp, &args, sizeof(args));
|
||||
if (r)
|
||||
|
|
@ -3014,9 +2992,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|||
r = -EFAULT;
|
||||
if (copy_from_user(&args, argp, sizeof(args)))
|
||||
break;
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
mutex_lock(&kvm->slots_arch_lock);
|
||||
r = kvm_s390_set_cmma_bits(kvm, &args);
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
mutex_unlock(&kvm->slots_arch_lock);
|
||||
break;
|
||||
}
|
||||
case KVM_S390_PV_COMMAND: {
|
||||
|
|
@ -3247,7 +3225,8 @@ static void kvm_s390_crypto_init(struct kvm *kvm)
|
|||
|
||||
static void sca_dispose(struct kvm *kvm)
|
||||
{
|
||||
free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca));
|
||||
if (kvm->arch.sca)
|
||||
free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca));
|
||||
kvm->arch.sca = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -3406,6 +3385,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
|||
trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id);
|
||||
kvm_s390_clear_local_irqs(vcpu);
|
||||
kvm_clear_async_pf_completion_queue(vcpu);
|
||||
kvm_s390_clear_bp_data(vcpu);
|
||||
if (!kvm_is_ucontrol(vcpu->kvm))
|
||||
sca_del_vcpu(vcpu);
|
||||
kvm_s390_update_topology_change_report(vcpu->kvm, 1);
|
||||
|
|
@ -3461,7 +3441,7 @@ static void sca_del_vcpu(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
|
||||
if (!kvm_s390_use_sca_entries())
|
||||
if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
|
||||
return;
|
||||
|
||||
clear_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn);
|
||||
|
|
@ -3481,8 +3461,8 @@ static void sca_add_vcpu(struct kvm_vcpu *vcpu)
|
|||
if (!kvm_s390_use_sca_entries())
|
||||
return;
|
||||
|
||||
WRITE_ONCE(sca->cpu[vcpu->vcpu_id].sda, virt_to_phys(vcpu->arch.sie_block));
|
||||
set_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn);
|
||||
sca->cpu[vcpu->vcpu_id].sda = virt_to_phys(vcpu->arch.sie_block);
|
||||
}
|
||||
|
||||
static int sca_can_add_vcpu(struct kvm *kvm, unsigned int id)
|
||||
|
|
@ -3613,6 +3593,9 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
|
|||
if (test_kvm_facility(vcpu->kvm, 74) || vcpu->kvm->arch.user_instr0 ||
|
||||
vcpu->kvm->arch.user_operexec)
|
||||
vcpu->arch.sie_block->ictl |= ICTL_OPEREXC;
|
||||
|
||||
/* Pairs with smp_load_acquire() in kvm_arch_vcpu_ioctl_run() and kvm_arch_vcpu_ioctl() */
|
||||
smp_store_release(&vcpu->arch.initialized, true);
|
||||
}
|
||||
|
||||
static bool kvm_has_pckmo_subfunc(struct kvm *kvm, unsigned long nr)
|
||||
|
|
@ -3674,7 +3657,8 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu)
|
|||
|
||||
void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
|
||||
if (vcpu->arch.sie_block->cbrlo)
|
||||
free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
|
||||
vcpu->arch.sie_block->cbrlo = 0;
|
||||
}
|
||||
|
||||
|
|
@ -3792,21 +3776,21 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_FREE(sie_page, struct sie_page *, if (_T) free_page((unsigned long)(_T)))
|
||||
|
||||
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct sie_page *sie_page;
|
||||
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
|
||||
struct sie_page *sie_page __free(sie_page) = NULL;
|
||||
int rc;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct sie_page) != 4096);
|
||||
vcpu->arch.mc = kvm_s390_new_mmu_cache();
|
||||
if (!vcpu->arch.mc)
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL_ACCOUNT);
|
||||
if (!sie_page) {
|
||||
kvm_s390_free_mmu_cache(vcpu->arch.mc);
|
||||
vcpu->arch.mc = NULL;
|
||||
if (!sie_page)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vcpu->arch.sie_block = &sie_page->sie_block;
|
||||
vcpu->arch.sie_block->itdba = virt_to_phys(&sie_page->itdb);
|
||||
|
|
@ -3848,10 +3832,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||
vcpu->run->kvm_valid_regs |= KVM_SYNC_FPRS;
|
||||
|
||||
if (kvm_is_ucontrol(vcpu->kvm)) {
|
||||
rc = -ENOMEM;
|
||||
vcpu->arch.gmap = gmap_new_child(vcpu->kvm->arch.gmap, -1UL);
|
||||
if (!vcpu->arch.gmap)
|
||||
goto out_free_sie_block;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
VM_EVENT(vcpu->kvm, 3, "create cpu %d at 0x%p, sie block at 0x%p",
|
||||
|
|
@ -3859,20 +3842,19 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||
trace_kvm_s390_create_vcpu(vcpu->vcpu_id, vcpu, vcpu->arch.sie_block);
|
||||
|
||||
rc = kvm_s390_vcpu_setup(vcpu);
|
||||
if (rc)
|
||||
goto out_ucontrol_uninit;
|
||||
if (rc) {
|
||||
if (kvm_is_ucontrol(vcpu->kvm)) {
|
||||
scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock)
|
||||
gmap_remove_child(vcpu->arch.gmap);
|
||||
vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
vcpu->arch.mc = no_free_ptr(mc);
|
||||
sie_page = NULL;
|
||||
kvm_s390_update_topology_change_report(vcpu->kvm, 1);
|
||||
return 0;
|
||||
|
||||
out_ucontrol_uninit:
|
||||
if (kvm_is_ucontrol(vcpu->kvm)) {
|
||||
gmap_remove_child(vcpu->arch.gmap);
|
||||
vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
|
||||
}
|
||||
out_free_sie_block:
|
||||
free_page((unsigned long)(vcpu->arch.sie_block));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
|
||||
|
|
@ -4242,8 +4224,10 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
|
|||
/* enforce guest PER */
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_P);
|
||||
|
||||
if (dbg->control & KVM_GUESTDBG_USE_HW_BP)
|
||||
rc = kvm_s390_import_bp_data(vcpu, dbg);
|
||||
if (dbg->control & KVM_GUESTDBG_USE_HW_BP) {
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
rc = kvm_s390_import_bp_data(vcpu, dbg);
|
||||
}
|
||||
} else {
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_P);
|
||||
vcpu->arch.guestdbg.last_bp = 0;
|
||||
|
|
@ -4468,8 +4452,8 @@ int kvm_s390_try_set_tod_clock(struct kvm *kvm, const struct kvm_s390_vm_tod_clo
|
|||
static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token,
|
||||
unsigned long token)
|
||||
{
|
||||
struct kvm_s390_interrupt inti;
|
||||
struct kvm_s390_irq irq;
|
||||
struct kvm_s390_interrupt inti = {};
|
||||
struct kvm_s390_irq irq = {};
|
||||
struct kvm_s390_interrupt_info *inti_mem = NULL;
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -5039,6 +5023,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
kvm_run->kvm_dirty_regs & ~KVM_SYNC_S390_VALID_FIELDS)
|
||||
return -EINVAL;
|
||||
|
||||
/* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */
|
||||
if (!smp_load_acquire(&vcpu->arch.initialized))
|
||||
return -EINVAL;
|
||||
|
||||
vcpu_load(vcpu);
|
||||
|
||||
if (guestdbg_exit_pending(vcpu)) {
|
||||
|
|
@ -5059,7 +5047,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
pr_err_ratelimited("can't run stopped vcpu %d\n",
|
||||
vcpu->vcpu_id);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
goto out_sigset;
|
||||
}
|
||||
|
||||
kernel_fpu_begin(&fpu, KERNEL_FPC | KERNEL_VXR);
|
||||
|
|
@ -5089,9 +5077,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
store_regs(vcpu);
|
||||
kernel_fpu_end(&fpu, KERNEL_FPC | KERNEL_VXR);
|
||||
|
||||
vcpu->stat.exit_userspace++;
|
||||
|
||||
out_sigset:
|
||||
kvm_sigset_deactivate(vcpu);
|
||||
|
||||
vcpu->stat.exit_userspace++;
|
||||
out:
|
||||
vcpu_put(vcpu);
|
||||
return rc;
|
||||
|
|
@ -5440,18 +5430,22 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
|
|||
|
||||
if (copy_from_user(&s390irq, argp, sizeof(s390irq)))
|
||||
return -EFAULT;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
break;
|
||||
}
|
||||
case KVM_S390_INTERRUPT: {
|
||||
struct kvm_s390_interrupt s390int;
|
||||
struct kvm_s390_irq s390irq = {};
|
||||
|
||||
if (kvm_is_ucontrol(vcpu->kvm))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&s390int, argp, sizeof(s390int)))
|
||||
return -EFAULT;
|
||||
if (s390int_to_s390irq(&s390int, &s390irq))
|
||||
return -EINVAL;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -5523,6 +5517,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
|||
long r;
|
||||
u16 rc, rrc;
|
||||
|
||||
/* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */
|
||||
if (!smp_load_acquire(&vcpu->arch.initialized))
|
||||
return -EINVAL;
|
||||
|
||||
vcpu_load(vcpu);
|
||||
|
||||
switch (ioctl) {
|
||||
|
|
@ -5716,7 +5714,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
|||
r = kvm_s390_handle_pv_vcpu_dump(vcpu, &cmd);
|
||||
|
||||
/* Always copy over UV rc / rrc data */
|
||||
if (copy_to_user((__u8 __user *)argp, &cmd.rc,
|
||||
if (copy_to_user(argp + offsetof(struct kvm_pv_cmd, rc), &cmd.rc,
|
||||
sizeof(cmd.rc) + sizeof(cmd.rrc)))
|
||||
r = -EFAULT;
|
||||
break;
|
||||
|
|
@ -5753,88 +5751,38 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
|||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
if (kvm_is_ucontrol(kvm) && new && new->id < KVM_USER_MEM_SLOTS)
|
||||
return -EINVAL;
|
||||
return s390_kvm_mmu_prepare_memory_region(kvm, old, new, change);
|
||||
}
|
||||
|
||||
/* When we are protected, we should not change the memory slots */
|
||||
if (kvm_s390_pv_get_handle(kvm))
|
||||
return -EINVAL;
|
||||
static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
union pgste pgste;
|
||||
|
||||
if (change != KVM_MR_DELETE && change != KVM_MR_FLAGS_ONLY) {
|
||||
/*
|
||||
* A few sanity checks. The memory in userland is ok to be
|
||||
* fragmented into various different vmas. It is okay to mmap()
|
||||
* and munmap() stuff in this slot after doing this call at any
|
||||
* time.
|
||||
*/
|
||||
if (new->userspace_addr & ~PAGE_MASK)
|
||||
return -EINVAL;
|
||||
if ((new->base_gfn + new->npages) * PAGE_SIZE > kvm->arch.mem_limit)
|
||||
return -EINVAL;
|
||||
if (!asce_contains_gfn(kvm->arch.gmap->asce, new->base_gfn + new->npages - 1))
|
||||
return -EINVAL;
|
||||
pgste = pgste_get_lock(ptep);
|
||||
if (pgste.cmma_d) {
|
||||
pgste.cmma_d = 0;
|
||||
atomic64_dec(walk->priv);
|
||||
}
|
||||
|
||||
if (!kvm->arch.migration_mode)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Turn off migration mode when:
|
||||
* - userspace creates a new memslot with dirty logging off,
|
||||
* - userspace modifies an existing memslot (MOVE or FLAGS_ONLY) and
|
||||
* dirty logging is turned off.
|
||||
* Migration mode expects dirty page logging being enabled to store
|
||||
* its dirty bitmap.
|
||||
*/
|
||||
if (change != KVM_MR_DELETE &&
|
||||
!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
|
||||
WARN(kvm_s390_vm_stop_migration(kvm),
|
||||
"Failed to stop migration mode");
|
||||
|
||||
pgste_set_unlock(ptep, pgste);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_commit_memory_region(struct kvm *kvm,
|
||||
struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, struct kvm_memory_slot *old)
|
||||
{
|
||||
struct kvm_s390_mmu_cache *mc = NULL;
|
||||
int rc = 0;
|
||||
const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
|
||||
|
||||
if (change == KVM_MR_FLAGS_ONLY)
|
||||
return;
|
||||
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) {
|
||||
_dat_walk_gfn_range(old->base_gfn, old->base_gfn + old->npages,
|
||||
kvm->arch.gmap->asce, &ops, DAT_WALK_IGN_HOLES,
|
||||
&kvm->arch.cmma_dirty_pages);
|
||||
}
|
||||
}
|
||||
|
||||
scoped_guard(write_lock, &kvm->mmu_lock) {
|
||||
switch (change) {
|
||||
case KVM_MR_DELETE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
break;
|
||||
case KVM_MR_MOVE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
if (rc)
|
||||
break;
|
||||
fallthrough;
|
||||
case KVM_MR_CREATE:
|
||||
rc = dat_create_slot(mc, kvm->arch.gmap->asce, new->base_gfn, new->npages);
|
||||
break;
|
||||
case KVM_MR_FLAGS_ONLY:
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (rc)
|
||||
pr_warn("failed to commit memory region\n");
|
||||
kvm_s390_free_mmu_cache(mc);
|
||||
return;
|
||||
void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
s390_kvm_mmu_commit_memory_region(kvm, old, new, change);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -452,7 +452,7 @@ void kvm_s390_vsie_destroy(struct kvm *kvm);
|
|||
int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
|
||||
int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* implemented in kvm-s390.c */
|
||||
/* implemented in s390.c */
|
||||
int kvm_s390_try_set_tod_clock(struct kvm *kvm, const struct kvm_s390_vm_tod_clock *gtod);
|
||||
int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long addr);
|
||||
int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr);
|
||||
|
|
@ -472,6 +472,9 @@ int __kvm_s390_mprotect_many(struct gmap *gmap, gpa_t gpa, u8 npages, unsigned i
|
|||
unsigned long bits);
|
||||
|
||||
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu);
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, struct kvm_memory_slot *old);
|
||||
int kvm_s390_vm_stop_migration(struct kvm *kvm);
|
||||
|
||||
|
||||
/* implemented in diag.c */
|
||||
int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
|
||||
|
|
@ -594,6 +597,11 @@ static inline bool kvm_s390_cur_gmap_fault_is_write(void)
|
|||
return test_facility(75) && (current->thread.gmap_teid.fsi == TEID_FSI_STORE);
|
||||
}
|
||||
|
||||
static __always_inline int kvm_s390_is_migration_mode(struct kvm *kvm)
|
||||
{
|
||||
return kvm->arch.migration_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_s390_vcpu_crypto_reset_all
|
||||
*
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <asm/sigp.h>
|
||||
#include "gaccess.h"
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "trace.h"
|
||||
|
||||
static int __sigp_sense(struct kvm_vcpu *vcpu, struct kvm_vcpu *dst_vcpu,
|
||||
|
|
@ -45,20 +45,6 @@ TRACE_EVENT(kvm_s390_skey_related_inst,
|
|||
VCPU_TP_PRINTK("%s", "storage key related instruction")
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_s390_major_guest_pfault,
|
||||
TP_PROTO(VCPU_PROTO_COMMON),
|
||||
TP_ARGS(VCPU_ARGS_COMMON),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
VCPU_FIELD_COMMON
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
VCPU_ASSIGN_COMMON
|
||||
),
|
||||
VCPU_TP_PRINTK("%s", "major fault, maybe applicable for pfault")
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_s390_pfault_init,
|
||||
TP_PROTO(VCPU_PROTO_COMMON, long pfault_token),
|
||||
TP_ARGS(VCPU_ARGS_COMMON, pfault_token),
|
||||
|
|
@ -283,6 +269,32 @@ TRACE_EVENT(kvm_s390_handle_diag,
|
|||
__print_symbolic(__entry->code, diagnose_codes))
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_s390_diag_9c,
|
||||
TP_PROTO(VCPU_PROTO_COMMON, int target_vcpu, int target_cpu,
|
||||
const char *result),
|
||||
TP_ARGS(VCPU_ARGS_COMMON, target_vcpu, target_cpu, result),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
VCPU_FIELD_COMMON
|
||||
__field(int, target_vcpu)
|
||||
__field(int, target_cpu)
|
||||
__string(result, result)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
VCPU_ASSIGN_COMMON
|
||||
__entry->target_vcpu = target_vcpu;
|
||||
__entry->target_cpu = target_cpu;
|
||||
__assign_str(result);
|
||||
),
|
||||
|
||||
VCPU_TP_PRINTK(
|
||||
"diag=9c target_vcpu=%d target_pcpu=%d result=%s",
|
||||
__entry->target_vcpu,
|
||||
__entry->target_cpu,
|
||||
__get_str(result))
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_s390_handle_lctl,
|
||||
TP_PROTO(VCPU_PROTO_COMMON, int g, int reg1, int reg3, u64 addr),
|
||||
TP_ARGS(VCPU_ARGS_COMMON, g, reg1, reg3, addr),
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
#include <asm/nmi.h>
|
||||
#include <asm/dis.h>
|
||||
#include <asm/facility.h>
|
||||
#include "kvm-s390.h"
|
||||
#include "s390.h"
|
||||
#include "gaccess.h"
|
||||
#include "gmap.h"
|
||||
|
||||
|
|
@ -33,10 +33,7 @@ enum vsie_page_flags {
|
|||
|
||||
struct vsie_page {
|
||||
struct kvm_s390_sie_block scb_s; /* 0x0000 */
|
||||
/*
|
||||
* the backup info for machine check. ensure it's at
|
||||
* the same offset as that in struct sie_page!
|
||||
*/
|
||||
/* backup info for machine check */
|
||||
struct mcck_volatile_info mcck_info; /* 0x0200 */
|
||||
/*
|
||||
* The pinned original scb. Be aware that other VCPUs can modify
|
||||
|
|
@ -71,6 +68,8 @@ struct vsie_page {
|
|||
};
|
||||
|
||||
static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
|
||||
static_assert(offsetof(struct vsie_page, mcck_info) == offsetof(struct sie_page, mcck_info));
|
||||
static_assert(IS_ALIGNED(offsetof(struct vsie_page, crycb), 8));
|
||||
|
||||
/* trigger a validity icpt for the given scb */
|
||||
static int set_validity_icpt(struct kvm_s390_sie_block *scb,
|
||||
|
|
@ -173,6 +172,7 @@ static int setup_apcb10(struct kvm_vcpu *vcpu, struct kvm_s390_apcb1 *apcb_s,
|
|||
sizeof(struct kvm_s390_apcb0)))
|
||||
return -EFAULT;
|
||||
|
||||
memset(apcb_s, 0, sizeof(*apcb_s));
|
||||
apcb_s->apm[0] = apcb_h->apm[0] & tmp.apm[0];
|
||||
apcb_s->aqm[0] = apcb_h->aqm[0] & tmp.aqm[0] & 0xffff000000000000UL;
|
||||
apcb_s->adm[0] = apcb_h->adm[0] & tmp.adm[0] & 0xffff000000000000UL;
|
||||
|
|
@ -701,7 +701,7 @@ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa)
|
|||
/* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */
|
||||
static void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa)
|
||||
{
|
||||
kvm_release_page_dirty(pfn_to_page(hpa >> PAGE_SHIFT));
|
||||
kvm_release_page_dirty(pfn_to_page(phys_to_pfn(hpa)));
|
||||
/* mark the page always as dirty for migration */
|
||||
mark_page_dirty(kvm, gpa_to_gfn(gpa));
|
||||
}
|
||||
|
|
@ -1486,7 +1486,7 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
|
|||
int nr_vcpus;
|
||||
|
||||
rcu_read_lock();
|
||||
vsie_page = radix_tree_lookup(&kvm->arch.vsie.addr_to_page, addr >> 9);
|
||||
vsie_page = radix_tree_lookup(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT);
|
||||
rcu_read_unlock();
|
||||
if (vsie_page) {
|
||||
if (try_get_vsie_page(vsie_page)) {
|
||||
|
|
@ -1527,13 +1527,14 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
|
|||
}
|
||||
if (vsie_page->scb_gpa != ULONG_MAX)
|
||||
radix_tree_delete(&kvm->arch.vsie.addr_to_page,
|
||||
vsie_page->scb_gpa >> 9);
|
||||
vsie_page->scb_gpa >> SCB_ALIGNMENT_SHIFT);
|
||||
}
|
||||
/* Mark it as invalid until it resides in the tree. */
|
||||
vsie_page->scb_gpa = ULONG_MAX;
|
||||
|
||||
/* Double use of the same address or allocation failure. */
|
||||
if (radix_tree_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, vsie_page)) {
|
||||
if (radix_tree_insert(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT,
|
||||
vsie_page)) {
|
||||
put_vsie_page(vsie_page);
|
||||
mutex_unlock(&kvm->arch.vsie.mutex);
|
||||
return NULL;
|
||||
|
|
@ -1565,7 +1566,6 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
|
|||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct vsie_page) != PAGE_SIZE);
|
||||
scb_addr = kvm_s390_get_base_disp_s(vcpu, NULL);
|
||||
|
||||
/* 512 byte alignment */
|
||||
|
|
@ -1632,7 +1632,7 @@ void kvm_s390_vsie_destroy(struct kvm *kvm)
|
|||
/* free the radix tree entry */
|
||||
if (vsie_page->scb_gpa != ULONG_MAX)
|
||||
radix_tree_delete(&kvm->arch.vsie.addr_to_page,
|
||||
vsie_page->scb_gpa >> 9);
|
||||
vsie_page->scb_gpa >> SCB_ALIGNMENT_SHIFT);
|
||||
free_page((unsigned long)vsie_page);
|
||||
}
|
||||
kvm->arch.vsie.page_count = 0;
|
||||
|
|
@ -96,6 +96,13 @@ static struct facility_def facility_defs[] = {
|
|||
150, /* enhanced sort */
|
||||
151, /* deflate conversion */
|
||||
155, /* msa extension 9 */
|
||||
165, /* nnpa facility */
|
||||
170, /* ineffective-nonconstrained-transaction facility */
|
||||
193, /* bear enhancement facility */
|
||||
194, /* rdp enhancement facility */
|
||||
196, /* processor activity instrumentation facility */
|
||||
197, /* processor activity instrumentation extension 1 */
|
||||
201, /* concurrent-functions facility */
|
||||
-1 /* END */
|
||||
}
|
||||
},
|
||||
|
|
@ -112,13 +119,6 @@ static struct facility_def facility_defs[] = {
|
|||
12, /* AP Query Configuration Information */
|
||||
15, /* AP Facilities Test */
|
||||
156, /* etoken facility */
|
||||
165, /* nnpa facility */
|
||||
170, /* ineffective-nonconstrained-transaction facility */
|
||||
193, /* bear enhancement facility */
|
||||
194, /* rdp enhancement facility */
|
||||
196, /* processor activity instrumentation facility */
|
||||
197, /* processor activity instrumentation extension 1 */
|
||||
201, /* concurrent-functions facility */
|
||||
-1 /* END */
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
# Makefile for the S/390 common i/o drivers
|
||||
#
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
# The following is required for define_trace.h to find ./trace.h
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
CFLAGS_vfio_ccw_trace.o := -I$(src)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <linux/nospec.h>
|
||||
|
||||
#include "vfio_ccw_private.h"
|
||||
|
||||
|
|
@ -24,11 +25,20 @@ static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private,
|
|||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
if (copy_to_user(buf, (void *)region + pos, count))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = count;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -48,6 +58,12 @@ static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private,
|
|||
if (!mutex_trylock(&private->io_mutex))
|
||||
return -EAGAIN;
|
||||
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
if (copy_from_user((void *)region + pos, buf, count)) {
|
||||
ret = -EFAULT;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/vfio.h>
|
||||
#include "vfio_ccw_private.h"
|
||||
|
||||
|
|
@ -26,6 +27,13 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private,
|
|||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
|
||||
if (cio_update_schib(sch)) {
|
||||
|
|
@ -85,19 +93,30 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
|
|||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_crw_region *region;
|
||||
struct vfio_ccw_crw *crw;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (pos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
crw = list_first_entry_or_null(&private->crw,
|
||||
struct vfio_ccw_crw, next);
|
||||
|
||||
if (crw)
|
||||
list_del(&crw->next);
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
region = private->region[i].data;
|
||||
/* Drop CRW lock while copying to userspace */
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
if (crw)
|
||||
memcpy(®ion->crw, &crw->crw, sizeof(region->crw));
|
||||
|
|
@ -108,14 +127,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
|
|||
ret = count;
|
||||
|
||||
region->crw = 0;
|
||||
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
kfree(crw);
|
||||
|
||||
/* Notify the guest if more CRWs are on our queue */
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
if (!list_empty(&private->crw) && private->crw_trigger)
|
||||
eventfd_signal(private->crw_trigger);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
out:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len)
|
|||
}
|
||||
|
||||
#define idal_is_2k(_cp) (!(_cp)->orb.cmd.c64 || (_cp)->orb.cmd.i2k)
|
||||
#define get_idaw_size(_cp) ((_cp)->orb.cmd.c64 ? sizeof(u64) : sizeof(u32))
|
||||
|
||||
/*
|
||||
* Helpers to operate ccwchain.
|
||||
|
|
@ -332,6 +333,7 @@ static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len)
|
|||
goto out_err;
|
||||
|
||||
list_add_tail(&chain->next, &cp->ccwchain_list);
|
||||
cp->ccwchain_count++;
|
||||
|
||||
return chain;
|
||||
|
||||
|
|
@ -376,11 +378,9 @@ static void ccwchain_cda_free(struct ccwchain *chain, int idx)
|
|||
static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
|
||||
{
|
||||
struct ccw1 *ccw = cp->guest_cp;
|
||||
int cnt = 0;
|
||||
|
||||
do {
|
||||
cnt++;
|
||||
int cnt;
|
||||
|
||||
for (cnt = 1; cnt <= CCWCHAIN_LEN_MAX; cnt++, ccw++) {
|
||||
/*
|
||||
* We want to keep counting if the current CCW has the
|
||||
* command-chaining flag enabled, or if it is a TIC CCW
|
||||
|
|
@ -390,15 +390,10 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
|
|||
* after the TIC, depending on the results of its operation.
|
||||
*/
|
||||
if (!ccw_is_chain(ccw) && !is_tic_within_range(ccw, iova, cnt))
|
||||
break;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
ccw++;
|
||||
} while (cnt < CCWCHAIN_LEN_MAX + 1);
|
||||
|
||||
if (cnt == CCWCHAIN_LEN_MAX + 1)
|
||||
cnt = -EINVAL;
|
||||
|
||||
return cnt;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp)
|
||||
|
|
@ -441,6 +436,10 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp)
|
|||
if (len < 0)
|
||||
return len;
|
||||
|
||||
/* Limit number of chains in a single channel program */
|
||||
if (cp->ccwchain_count >= CCWCHAIN_COUNT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
/* Need alloc a new chain for this one. */
|
||||
chain = ccwchain_alloc(cp, len);
|
||||
if (!chain)
|
||||
|
|
@ -455,9 +454,6 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp)
|
|||
/* Loop for tics on this new chain. */
|
||||
ret = ccwchain_loop_tic(chain, cp);
|
||||
|
||||
if (ret)
|
||||
ccwchain_free(chain);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -486,6 +482,23 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ccwchain_build_ccws(dma32_t cda, struct channel_program *cp)
|
||||
{
|
||||
struct ccwchain *chain, *temp;
|
||||
int ret;
|
||||
|
||||
ret = ccwchain_handle_ccw(cda, cp);
|
||||
|
||||
if (ret) {
|
||||
/* Cleanup if an error occurred */
|
||||
list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) {
|
||||
ccwchain_free(chain);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccwchain_fetch_tic(struct ccw1 *ccw,
|
||||
struct channel_program *cp)
|
||||
{
|
||||
|
|
@ -511,7 +524,8 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int
|
|||
&container_of(cp, struct vfio_ccw_private, cp)->vdev;
|
||||
dma64_t *idaws;
|
||||
dma32_t *idaws_f1;
|
||||
int idal_len = idaw_nr * sizeof(*idaws);
|
||||
u64 first_idaw;
|
||||
int idal_len = idaw_nr * get_idaw_size(cp);
|
||||
int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE;
|
||||
int idaw_mask = ~(idaw_size - 1);
|
||||
int i, ret;
|
||||
|
|
@ -527,6 +541,18 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int
|
|||
kfree(idaws);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
idaws_f1 = (dma32_t *)idaws;
|
||||
if (cp->orb.cmd.c64)
|
||||
first_idaw = dma64_to_u64(idaws[0]);
|
||||
else
|
||||
first_idaw = dma32_to_u32(idaws_f1[0]);
|
||||
|
||||
/* Unexpected mismatch from earlier read */
|
||||
if (first_idaw != cp->guest_iova) {
|
||||
kfree(idaws);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
} else {
|
||||
/* Fabricate an IDAL based off CCW data address */
|
||||
if (cp->orb.cmd.c64) {
|
||||
|
|
@ -568,7 +594,7 @@ static int ccw_count_idaws(struct ccw1 *ccw,
|
|||
struct vfio_device *vdev =
|
||||
&container_of(cp, struct vfio_ccw_private, cp)->vdev;
|
||||
u64 iova;
|
||||
int size = cp->orb.cmd.c64 ? sizeof(u64) : sizeof(u32);
|
||||
int size = get_idaw_size(cp);
|
||||
int ret;
|
||||
int bytes = 1;
|
||||
|
||||
|
|
@ -592,6 +618,9 @@ static int ccw_count_idaws(struct ccw1 *ccw,
|
|||
iova = dma32_to_u32(ccw->cda);
|
||||
}
|
||||
|
||||
/* Save the read address for later */
|
||||
cp->guest_iova = iova;
|
||||
|
||||
/* Format-1 IDAWs operate on 2K each */
|
||||
if (!cp->orb.cmd.c64)
|
||||
return idal_2k_nr_words((void *)iova, bytes);
|
||||
|
|
@ -731,11 +760,12 @@ int cp_init(struct channel_program *cp, union orb *orb)
|
|||
vdev->dev,
|
||||
"Prefetching channel program even though prefetch not specified in ORB");
|
||||
|
||||
cp->ccwchain_count = 0;
|
||||
INIT_LIST_HEAD(&cp->ccwchain_list);
|
||||
memcpy(&cp->orb, orb, sizeof(*orb));
|
||||
|
||||
/* Build a ccwchain for the first CCW segment */
|
||||
ret = ccwchain_handle_ccw(orb->cmd.cpa, cp);
|
||||
ret = ccwchain_build_ccws(orb->cmd.cpa, cp);
|
||||
|
||||
if (!ret)
|
||||
cp->initialized = true;
|
||||
|
|
@ -947,17 +977,23 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw)
|
|||
*/
|
||||
bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length)
|
||||
{
|
||||
struct vfio_ccw_private *private =
|
||||
container_of(cp, struct vfio_ccw_private, cp);
|
||||
struct ccwchain *chain;
|
||||
int i;
|
||||
|
||||
if (!cp->initialized)
|
||||
return false;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
list_for_each_entry(chain, &cp->ccwchain_list, next) {
|
||||
for (i = 0; i < chain->ch_len; i++)
|
||||
if (page_array_iova_pinned(&chain->ch_pa[i], iova, length))
|
||||
if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) {
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,19 @@
|
|||
*/
|
||||
#define CCWCHAIN_LEN_MAX 256
|
||||
|
||||
/*
|
||||
* Maximum number of chains
|
||||
*/
|
||||
#define CCWCHAIN_COUNT_MAX 16
|
||||
|
||||
/**
|
||||
* struct channel_program - manage information for channel program
|
||||
* @ccwchain_list: list head of ccwchains
|
||||
* @orb: orb for the currently processed ssch request
|
||||
* @initialized: whether this instance is actually initialized
|
||||
* @guest_cp: copy of guest channel program
|
||||
* @ccwchain_count: number of channel program segments (linked by TIC)
|
||||
* @guest_iova: first data address of a guest channel program
|
||||
*
|
||||
* @ccwchain_list is the head of a ccwchain list, that contents the
|
||||
* translated result of the guest channel program that pointed out by
|
||||
|
|
@ -38,6 +46,8 @@ struct channel_program {
|
|||
union orb orb;
|
||||
bool initialized;
|
||||
struct ccw1 *guest_cp;
|
||||
unsigned int ccwchain_count;
|
||||
u64 guest_iova;
|
||||
};
|
||||
|
||||
int cp_init(struct channel_program *cp, union orb *orb);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ debug_info_t *vfio_ccw_debug_trace_id;
|
|||
* Helpers
|
||||
*/
|
||||
int vfio_ccw_sch_quiesce(struct subchannel *sch)
|
||||
__must_hold(&sch->lock)
|
||||
{
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
|
||||
|
|
@ -91,6 +92,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
|
||||
is_final = !(scsw_actl(&irb->scsw) &
|
||||
(SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT));
|
||||
mutex_lock(&private->io_mutex);
|
||||
if (scsw_is_solicited(&irb->scsw)) {
|
||||
cp_update_scsw(&private->cp, &irb->scsw);
|
||||
if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) {
|
||||
|
|
@ -98,9 +100,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
cp_is_finished = true;
|
||||
}
|
||||
}
|
||||
mutex_lock(&private->io_mutex);
|
||||
memcpy(private->io_region->irb_area, irb, sizeof(*irb));
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
/*
|
||||
* Reset to IDLE only if processing of a channel program
|
||||
|
|
@ -110,6 +110,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
*/
|
||||
if (cp_is_finished)
|
||||
private->state = VFIO_CCW_STATE_IDLE;
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
if (private->io_trigger)
|
||||
eventfd_signal(private->io_trigger);
|
||||
|
|
@ -118,11 +119,25 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
void vfio_ccw_crw_todo(struct work_struct *work)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
unsigned long flags;
|
||||
|
||||
private = container_of(work, struct vfio_ccw_private, crw_work);
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
if (!list_empty(&private->crw) && private->crw_trigger)
|
||||
eventfd_signal(private->crw_trigger);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
}
|
||||
|
||||
void vfio_ccw_notoper_todo(struct work_struct *work)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
|
||||
private = container_of(work, struct vfio_ccw_private, notoper_work);
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
cp_free(&private->cp);
|
||||
mutex_unlock(&private->io_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -275,6 +290,7 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
|
|||
unsigned int rsid)
|
||||
{
|
||||
struct vfio_ccw_crw *crw;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* If unable to allocate a CRW, just drop the event and
|
||||
|
|
@ -292,7 +308,9 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
|
|||
crw->crw.erc = erc;
|
||||
crw->crw.rsid = rsid;
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
list_add_tail(&crw->next, &private->crw);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
queue_work(vfio_ccw_work_q, &private->crw_work);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,8 +170,8 @@ static void fsm_notoper(struct vfio_ccw_private *private,
|
|||
css_sched_sch_todo(sch, SCH_TODO_UNREG);
|
||||
private->state = VFIO_CCW_STATE_NOT_OPER;
|
||||
|
||||
/* This is usually handled during CLOSE event */
|
||||
cp_free(&private->cp);
|
||||
/* This routine could be called from IRQ context, so defer */
|
||||
queue_work(vfio_ccw_work_q, &private->notoper_work);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -410,7 +410,11 @@ static void fsm_close(struct vfio_ccw_private *private,
|
|||
|
||||
private->state = VFIO_CCW_STATE_STANDBY;
|
||||
spin_unlock_irq(&sch->lock);
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
cp_free(&private->cp);
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
return;
|
||||
|
||||
err_unlock:
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev)
|
|||
INIT_LIST_HEAD(&private->crw);
|
||||
INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
|
||||
INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
|
||||
INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo);
|
||||
spin_lock_init(&private->crw_lock);
|
||||
|
||||
private->cp.guest_cp = kzalloc_objs(struct ccw1, CCWCHAIN_LEN_MAX);
|
||||
if (!private->cp.guest_cp)
|
||||
|
|
@ -130,11 +132,28 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
|
|||
struct vfio_ccw_private *private =
|
||||
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||
struct vfio_ccw_crw *crw, *temp;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Ensure these work items are fully drained, so none can
|
||||
* fire after being released.
|
||||
*
|
||||
* notoper_work should have nothing to do here, because only
|
||||
* open devices could have channel_program resources in use
|
||||
* and those would be released during close. Nevertheless,
|
||||
* call flush here as well to be certain anything that was
|
||||
* allocated is freed.
|
||||
*/
|
||||
cancel_work_sync(&private->io_work);
|
||||
cancel_work_sync(&private->crw_work);
|
||||
flush_work(&private->notoper_work);
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
list_for_each_entry_safe(crw, temp, &private->crw, next) {
|
||||
list_del(&crw->next);
|
||||
kfree(crw);
|
||||
}
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
|
||||
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
|
||||
|
|
@ -202,6 +221,19 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev)
|
|||
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
|
||||
|
||||
/*
|
||||
* Ensure these work items are drained, in the event the
|
||||
* device is re-opened instead of released.
|
||||
*
|
||||
* notoper_work needs to be given a chance to run if it
|
||||
* is queued, so any memory associated with the channel
|
||||
* program can be returned.
|
||||
*/
|
||||
cancel_work_sync(&private->io_work);
|
||||
cancel_work_sync(&private->crw_work);
|
||||
flush_work(&private->notoper_work);
|
||||
|
||||
vfio_ccw_unregister_dev_regions(private);
|
||||
}
|
||||
|
||||
|
|
@ -243,6 +275,7 @@ static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev,
|
|||
return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
|
||||
default:
|
||||
index -= VFIO_CCW_NUM_REGIONS;
|
||||
index = array_index_nospec(index, private->num_regions);
|
||||
return private->region[index].ops->read(private, buf, count,
|
||||
ppos);
|
||||
}
|
||||
|
|
@ -295,6 +328,7 @@ static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev,
|
|||
return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
|
||||
default:
|
||||
index -= VFIO_CCW_NUM_REGIONS;
|
||||
index = array_index_nospec(index, private->num_regions);
|
||||
return private->region[index].ops->write(private, buf, count,
|
||||
ppos);
|
||||
}
|
||||
|
|
@ -338,11 +372,8 @@ static int vfio_ccw_mdev_ioctl_get_region_info(struct vfio_device *vdev,
|
|||
VFIO_CCW_NUM_REGIONS + private->num_regions)
|
||||
return -EINVAL;
|
||||
|
||||
info->index = array_index_nospec(info->index,
|
||||
VFIO_CCW_NUM_REGIONS +
|
||||
private->num_regions);
|
||||
|
||||
i = info->index - VFIO_CCW_NUM_REGIONS;
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
|
||||
info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
|
||||
info->size = private->region[i].size;
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ struct vfio_ccw_parent {
|
|||
* @state: internal state of the device
|
||||
* @completion: synchronization helper of the I/O completion
|
||||
* @io_region: MMIO region to input/output I/O arguments/results
|
||||
* @io_mutex: protect against concurrent update of I/O regions
|
||||
* @io_mutex: protect against concurrent update of I/O resources
|
||||
* and @cp lifecycle
|
||||
* @region: additional regions for other subchannel operations
|
||||
* @cmd_region: MMIO region for asynchronous I/O commands other than START
|
||||
* @schib_region: MMIO region for SCHIB information
|
||||
|
|
@ -97,11 +98,14 @@ struct vfio_ccw_parent {
|
|||
* @cp: channel program for the current I/O operation
|
||||
* @irb: irb info received from interrupt
|
||||
* @scsw: scsw info
|
||||
* @crw_lock: serialization of CRW list information
|
||||
* @crw: list of Channel Report Word elements
|
||||
* @io_trigger: eventfd ctx for signaling userspace I/O results
|
||||
* @crw_trigger: eventfd ctx for signaling userspace CRW information
|
||||
* @req_trigger: eventfd ctx for signaling userspace to return device
|
||||
* @io_work: work for deferral process of I/O handling
|
||||
* @crw_work: work for deferral process of CRW handling
|
||||
* @notoper_work: work for deferred processing in not-operational state
|
||||
*/
|
||||
struct vfio_ccw_private {
|
||||
struct vfio_device vdev;
|
||||
|
|
@ -118,6 +122,8 @@ struct vfio_ccw_private {
|
|||
struct channel_program cp;
|
||||
struct irb irb;
|
||||
union scsw scsw;
|
||||
|
||||
spinlock_t crw_lock;
|
||||
struct list_head crw;
|
||||
|
||||
struct eventfd_ctx *io_trigger;
|
||||
|
|
@ -125,11 +131,14 @@ struct vfio_ccw_private {
|
|||
struct eventfd_ctx *req_trigger;
|
||||
struct work_struct io_work;
|
||||
struct work_struct crw_work;
|
||||
struct work_struct notoper_work;
|
||||
} __aligned(8);
|
||||
|
||||
int vfio_ccw_sch_quiesce(struct subchannel *sch);
|
||||
int vfio_ccw_sch_quiesce(struct subchannel *sch)
|
||||
__must_hold(&sch->lock);
|
||||
void vfio_ccw_sch_io_todo(struct work_struct *work);
|
||||
void vfio_ccw_crw_todo(struct work_struct *work);
|
||||
void vfio_ccw_notoper_todo(struct work_struct *work);
|
||||
|
||||
extern struct mdev_driver vfio_ccw_mdev_driver;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,15 +48,19 @@ static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q);
|
|||
* 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM
|
||||
* guest's APCB.
|
||||
* 2. kvm->lock: required to update a guest's APCB
|
||||
* 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev
|
||||
* 3. kvm->arch.crypto.pqap_hook_rwsem: required to update pqap_hook and
|
||||
* serialize against PQAP intercepts
|
||||
* 4. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev
|
||||
*
|
||||
* Note: If @kvm is NULL, the KVM lock will not be taken.
|
||||
* Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be taken.
|
||||
*/
|
||||
static inline void get_update_locks_for_kvm(struct kvm *kvm)
|
||||
{
|
||||
mutex_lock(&matrix_dev->guests_lock);
|
||||
if (kvm)
|
||||
if (kvm) {
|
||||
mutex_lock(&kvm->lock);
|
||||
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
}
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
}
|
||||
|
||||
|
|
@ -68,16 +72,19 @@ static inline void get_update_locks_for_kvm(struct kvm *kvm)
|
|||
*
|
||||
* The proper unlocking order is:
|
||||
* 1. matrix_dev->mdevs_lock
|
||||
* 2. kvm->lock
|
||||
* 3. matrix_dev->guests_lock
|
||||
* 2. kvm->arch.crypto.pqap_hook_rwsem
|
||||
* 3. kvm->lock
|
||||
* 4. matrix_dev->guests_lock
|
||||
*
|
||||
* Note: If @kvm is NULL, the KVM lock will not be released.
|
||||
* Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be released.
|
||||
*/
|
||||
static inline void release_update_locks_for_kvm(struct kvm *kvm)
|
||||
{
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
if (kvm)
|
||||
if (kvm) {
|
||||
up_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
mutex_unlock(&kvm->lock);
|
||||
}
|
||||
mutex_unlock(&matrix_dev->guests_lock);
|
||||
}
|
||||
|
||||
|
|
@ -800,12 +807,17 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)
|
|||
ret = vfio_register_emulated_iommu_dev(&matrix_mdev->vdev);
|
||||
if (ret)
|
||||
goto err_put_vdev;
|
||||
matrix_mdev->req_trigger = NULL;
|
||||
matrix_mdev->cfg_chg_trigger = NULL;
|
||||
|
||||
/*
|
||||
* Take the matrix_dev->guests_lock mutex before adding the matrix_mdev
|
||||
* to the mdev_list. All functions that traverse the list must also hold
|
||||
* this lock to guard against additions to or removals from the list
|
||||
* while it is being traversed.
|
||||
*/
|
||||
mutex_lock(&matrix_dev->guests_lock);
|
||||
dev_set_drvdata(&mdev->dev, matrix_mdev);
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
list_add(&matrix_mdev->node, &matrix_dev->mdev_list);
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
mutex_unlock(&matrix_dev->guests_lock);
|
||||
return 0;
|
||||
|
||||
err_put_vdev:
|
||||
|
|
@ -1821,26 +1833,17 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
|
|||
static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
|
||||
struct kvm *kvm)
|
||||
{
|
||||
struct ap_matrix_mdev *m;
|
||||
|
||||
if (kvm->arch.crypto.crycbd) {
|
||||
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
|
||||
up_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
|
||||
get_update_locks_for_kvm(kvm);
|
||||
|
||||
list_for_each_entry(m, &matrix_dev->mdev_list, node) {
|
||||
if (m != matrix_mdev && m->kvm == kvm) {
|
||||
release_update_locks_for_kvm(kvm);
|
||||
return -EPERM;
|
||||
}
|
||||
if (kvm->arch.crypto.pqap_hook) {
|
||||
release_update_locks_for_kvm(kvm);
|
||||
return -EPERM;
|
||||
}
|
||||
kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
|
||||
|
||||
kvm_get_kvm(kvm);
|
||||
matrix_mdev->kvm = kvm;
|
||||
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
|
||||
|
||||
release_update_locks_for_kvm(kvm);
|
||||
}
|
||||
|
||||
|
|
@ -1883,18 +1886,15 @@ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
|
|||
struct kvm *kvm = matrix_mdev->kvm;
|
||||
|
||||
if (kvm && kvm->arch.crypto.crycbd) {
|
||||
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
kvm->arch.crypto.pqap_hook = NULL;
|
||||
up_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
|
||||
get_update_locks_for_kvm(kvm);
|
||||
kvm->arch.crypto.pqap_hook = NULL;
|
||||
|
||||
kvm_arch_crypto_clear_masks(kvm);
|
||||
vfio_ap_mdev_reset_queues(matrix_mdev);
|
||||
kvm_put_kvm(kvm);
|
||||
matrix_mdev->kvm = NULL;
|
||||
|
||||
release_update_locks_for_kvm(kvm);
|
||||
kvm_put_kvm(kvm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2297,6 +2297,8 @@ static struct ap_matrix_mdev *vfio_ap_mdev_for_queue(struct vfio_ap_queue *q)
|
|||
unsigned long apid = AP_QID_CARD(q->apqn);
|
||||
unsigned long apqi = AP_QID_QUEUE(q->apqn);
|
||||
|
||||
lockdep_assert_held(&matrix_dev->guests_lock);
|
||||
|
||||
list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
|
||||
if (test_bit_inv(apid, matrix_mdev->matrix.apm) &&
|
||||
test_bit_inv(apqi, matrix_mdev->matrix.aqm))
|
||||
|
|
@ -2316,8 +2318,26 @@ static ssize_t status_show(struct device *dev,
|
|||
struct ap_matrix_mdev *matrix_mdev;
|
||||
struct ap_device *apdev = to_ap_dev(dev);
|
||||
|
||||
mutex_lock(&matrix_dev->guests_lock);
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
q = dev_get_drvdata(&apdev->device);
|
||||
|
||||
/*
|
||||
* Make sure the drvdata has been set before proceeding. There is a
|
||||
* possibility that the drvdata was not set if the vfio_ap_queue object
|
||||
* could not be allocated when the queue device was probed. In that case,
|
||||
* the locks used in vfio_ap_mdev_probe_queue() are released prior to
|
||||
* removing the sysfs status attribute to avoid a lockdep
|
||||
* splat. That opens a very small window where the status attribute is
|
||||
* still available without the vfio_ap_queue object having been
|
||||
* stored in the device drvdata. In that case, indicate the queue is not
|
||||
* assigned.
|
||||
*/
|
||||
if (!q) {
|
||||
nchars = sysfs_emit(buf, "%s\n", AP_QUEUE_UNASSIGNED);
|
||||
goto done;
|
||||
}
|
||||
|
||||
matrix_mdev = vfio_ap_mdev_for_queue(q);
|
||||
|
||||
/* If the queue is assigned to the matrix mediated device, then
|
||||
|
|
@ -2342,7 +2362,9 @@ static ssize_t status_show(struct device *dev,
|
|||
nchars = sysfs_emit(buf, "%s\n", AP_QUEUE_UNASSIGNED);
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
mutex_unlock(&matrix_dev->guests_lock);
|
||||
|
||||
return nchars;
|
||||
}
|
||||
|
|
@ -2415,14 +2437,17 @@ void vfio_ap_mdev_unregister(void)
|
|||
|
||||
int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
|
||||
{
|
||||
int ret;
|
||||
int ret, apqn;
|
||||
struct vfio_ap_queue *q;
|
||||
DECLARE_BITMAP(apm_filtered, AP_DEVICES);
|
||||
struct ap_matrix_mdev *matrix_mdev;
|
||||
|
||||
apqn = to_ap_queue(&apdev->device)->qid;
|
||||
matrix_mdev = get_update_locks_by_apqn(apqn);
|
||||
|
||||
ret = sysfs_create_group(&apdev->device.kobj, &vfio_queue_attr_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_release_locks;
|
||||
|
||||
q = kzalloc_obj(*q);
|
||||
if (!q) {
|
||||
|
|
@ -2430,11 +2455,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
|
|||
goto err_remove_group;
|
||||
}
|
||||
|
||||
q->apqn = to_ap_queue(&apdev->device)->qid;
|
||||
q->apqn = apqn;
|
||||
q->saved_isc = VFIO_AP_ISC_INVALID;
|
||||
memset(&q->reset_status, 0, sizeof(q->reset_status));
|
||||
INIT_WORK(&q->reset_work, apq_reset_check);
|
||||
matrix_mdev = get_update_locks_by_apqn(q->apqn);
|
||||
|
||||
if (matrix_mdev) {
|
||||
vfio_ap_mdev_link_queue(matrix_mdev, q);
|
||||
|
|
@ -2463,8 +2487,13 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
|
|||
return ret;
|
||||
|
||||
err_remove_group:
|
||||
release_update_locks_for_mdev(matrix_mdev);
|
||||
sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group);
|
||||
return ret;
|
||||
|
||||
err_release_locks:
|
||||
release_update_locks_for_mdev(matrix_mdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void vfio_ap_mdev_remove_queue(struct ap_device *apdev)
|
||||
|
|
@ -2559,24 +2588,28 @@ static void vfio_ap_mdev_hot_unplug_cfg(struct ap_matrix_mdev *matrix_mdev,
|
|||
unsigned long *aqrem,
|
||||
unsigned long *cdrem)
|
||||
{
|
||||
int do_hotplug = 0;
|
||||
bool do_hotplug = false;
|
||||
|
||||
if (!bitmap_empty(aprem, AP_DEVICES)) {
|
||||
do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.apm,
|
||||
matrix_mdev->shadow_apcb.apm,
|
||||
aprem, AP_DEVICES);
|
||||
if (bitmap_intersects(matrix_mdev->shadow_apcb.apm, aprem, AP_DEVICES)) {
|
||||
bitmap_andnot(matrix_mdev->shadow_apcb.apm,
|
||||
matrix_mdev->shadow_apcb.apm,
|
||||
aprem, AP_DEVICES);
|
||||
do_hotplug = true;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(aqrem, AP_DOMAINS)) {
|
||||
do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.aqm,
|
||||
matrix_mdev->shadow_apcb.aqm,
|
||||
aqrem, AP_DEVICES);
|
||||
if (bitmap_intersects(matrix_mdev->shadow_apcb.aqm, aqrem, AP_DOMAINS)) {
|
||||
bitmap_andnot(matrix_mdev->shadow_apcb.aqm,
|
||||
matrix_mdev->shadow_apcb.aqm,
|
||||
aqrem, AP_DOMAINS);
|
||||
do_hotplug = true;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(cdrem, AP_DOMAINS))
|
||||
do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.adm,
|
||||
matrix_mdev->shadow_apcb.adm,
|
||||
cdrem, AP_DOMAINS);
|
||||
if (bitmap_intersects(matrix_mdev->shadow_apcb.adm, cdrem, AP_DOMAINS)) {
|
||||
bitmap_andnot(matrix_mdev->shadow_apcb.adm,
|
||||
matrix_mdev->shadow_apcb.adm,
|
||||
cdrem, AP_DOMAINS);
|
||||
do_hotplug = true;
|
||||
}
|
||||
|
||||
if (do_hotplug)
|
||||
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
|
||||
|
|
@ -2603,28 +2636,41 @@ static void vfio_ap_mdev_cfg_remove(unsigned long *ap_remove,
|
|||
DECLARE_BITMAP(aprem, AP_DEVICES);
|
||||
DECLARE_BITMAP(aqrem, AP_DOMAINS);
|
||||
DECLARE_BITMAP(cdrem, AP_DOMAINS);
|
||||
int do_remove = 0;
|
||||
int do_remove;
|
||||
|
||||
/*
|
||||
* It is safe to traverse this list here because the
|
||||
* required guard - matrix_dev->guests_lock - is taken in the
|
||||
* vfio_ap_on_cfg_changed function prior to this function getting
|
||||
* called.
|
||||
*/
|
||||
list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
|
||||
mutex_lock(&matrix_mdev->kvm->lock);
|
||||
/*
|
||||
* The mdevs_lock must be held to access fields within matrix_mdev,
|
||||
* and kvm->lock must be taken before mdevs_lock to satisfy the lock
|
||||
* ordering requirement and prevent a lockdep splat.
|
||||
*/
|
||||
if (matrix_mdev->kvm)
|
||||
mutex_lock(&matrix_mdev->kvm->lock);
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
|
||||
do_remove |= bitmap_and(aprem, ap_remove,
|
||||
matrix_mdev->matrix.apm,
|
||||
AP_DEVICES);
|
||||
do_remove = bitmap_and(aprem, ap_remove,
|
||||
matrix_mdev->matrix.apm,
|
||||
AP_DEVICES);
|
||||
do_remove |= bitmap_and(aqrem, aq_remove,
|
||||
matrix_mdev->matrix.aqm,
|
||||
AP_DOMAINS);
|
||||
do_remove |= bitmap_andnot(cdrem, cd_remove,
|
||||
matrix_mdev->matrix.adm,
|
||||
AP_DOMAINS);
|
||||
do_remove |= bitmap_and(cdrem, cd_remove,
|
||||
matrix_mdev->matrix.adm,
|
||||
AP_DOMAINS);
|
||||
|
||||
if (do_remove)
|
||||
vfio_ap_mdev_hot_unplug_cfg(matrix_mdev, aprem, aqrem,
|
||||
cdrem);
|
||||
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
mutex_unlock(&matrix_mdev->kvm->lock);
|
||||
if (matrix_mdev->kvm)
|
||||
mutex_unlock(&matrix_mdev->kvm->lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2748,13 +2794,27 @@ static void vfio_ap_mdev_cfg_add(unsigned long *apm_add, unsigned long *aqm_add,
|
|||
|
||||
vfio_ap_filter_apid_by_qtype(apm_add, aqm_add);
|
||||
|
||||
/*
|
||||
* It is safe to traverse this list here because the
|
||||
* required guard - matrix_dev->guests_lock - is taken in the
|
||||
* vfio_ap_on_cfg_changed function prior to this function getting
|
||||
* called.
|
||||
*/
|
||||
list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
|
||||
/*
|
||||
* The mdevs_lock must be held in order to access fields
|
||||
* within matrix_mdev
|
||||
*/
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
|
||||
bitmap_and(matrix_mdev->apm_add,
|
||||
matrix_mdev->matrix.apm, apm_add, AP_DEVICES);
|
||||
bitmap_and(matrix_mdev->aqm_add,
|
||||
matrix_mdev->matrix.aqm, aqm_add, AP_DOMAINS);
|
||||
bitmap_and(matrix_mdev->adm_add,
|
||||
matrix_mdev->matrix.adm, adm_add, AP_DEVICES);
|
||||
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2807,6 +2867,10 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *cur_cfg_info,
|
|||
if (!cur_cfg_info || !prev_cfg_info)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Take the guests_lock mutex here to guard access to the
|
||||
* matrix_dev->mdev_list in the two functions called below.
|
||||
*/
|
||||
mutex_lock(&matrix_dev->guests_lock);
|
||||
|
||||
vfio_ap_mdev_on_cfg_remove(cur_cfg_info, prev_cfg_info);
|
||||
|
|
@ -2821,8 +2885,14 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev)
|
|||
DECLARE_BITMAP(apm_filtered, AP_DEVICES);
|
||||
bool filter_domains, filter_adapters, filter_cdoms, do_hotplug = false;
|
||||
|
||||
mutex_lock(&matrix_mdev->kvm->lock);
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
/*
|
||||
* Zero out the apm_filtered bitmap in case there are no adapters or
|
||||
* domains to be added, but only control domains. In that case,
|
||||
* vfio_ap_mdev_filter_matrix() - which initializes apm_filtered - will
|
||||
* not get called and the reset_queues_for_apids will crash because it
|
||||
* will access an uninitialized bitmap.
|
||||
*/
|
||||
bitmap_zero(apm_filtered, AP_DEVICES);
|
||||
|
||||
filter_adapters = bitmap_intersects(matrix_mdev->matrix.apm,
|
||||
matrix_mdev->apm_add, AP_DEVICES);
|
||||
|
|
@ -2841,9 +2911,6 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev)
|
|||
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
|
||||
|
||||
reset_queues_for_apids(matrix_mdev, apm_filtered);
|
||||
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
mutex_unlock(&matrix_mdev->kvm->lock);
|
||||
}
|
||||
|
||||
void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
|
||||
|
|
@ -2854,15 +2921,29 @@ void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
|
|||
mutex_lock(&matrix_dev->guests_lock);
|
||||
|
||||
list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
|
||||
/*
|
||||
* The mdevs_lock must be held to access fields within matrix_mdev,
|
||||
* and kvm->lock must be taken before mdevs_lock to satisfy the lock
|
||||
* ordering requirement and prevent a lockdep splat.
|
||||
*/
|
||||
if (matrix_mdev->kvm)
|
||||
mutex_lock(&matrix_mdev->kvm->lock);
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
|
||||
if (bitmap_empty(matrix_mdev->apm_add, AP_DEVICES) &&
|
||||
bitmap_empty(matrix_mdev->aqm_add, AP_DOMAINS) &&
|
||||
bitmap_empty(matrix_mdev->adm_add, AP_DOMAINS))
|
||||
continue;
|
||||
goto do_unlock;
|
||||
|
||||
vfio_ap_mdev_hot_plug_cfg(matrix_mdev);
|
||||
bitmap_clear(matrix_mdev->apm_add, 0, AP_DEVICES);
|
||||
bitmap_clear(matrix_mdev->aqm_add, 0, AP_DOMAINS);
|
||||
bitmap_clear(matrix_mdev->adm_add, 0, AP_DOMAINS);
|
||||
|
||||
do_unlock:
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
if (matrix_mdev->kvm)
|
||||
mutex_unlock(&matrix_mdev->kvm->lock);
|
||||
}
|
||||
|
||||
mutex_unlock(&matrix_dev->guests_lock);
|
||||
|
|
|
|||
|
|
@ -3,4 +3,6 @@
|
|||
#
|
||||
# Copyright IBM Corp. 2008
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
obj-$(CONFIG_S390_GUEST) += virtio_ccw.o
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user