mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-24 14:45:12 +08:00
KVM: s390: Fixes and features for 4.16 part 2
- exitless interrupts for emulated devices (Michael Mueller) - cleanup of cpuflag handling (David Hildenbrand) - kvm stat counter improvements (Christian Borntraeger) - vsie improvements (David Hildenbrand) - mm cleanup (Janosch Frank) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJaayoSAAoJEBF7vIC1phx8T8EP/1xGy6ZZBdVCAT00u3GWZ9eh M5m1thSUuhKuHdJHaN1ORrBPNhR5l+Bvf5VMi5LkWORUpQc4jOYz1BX1qrryvWXk Bt6363v1rhbInk7uKv4E0q9i3Ei6dSfoT0YByihqiPjkaeJyG830Ez2IUFFdGUQQ ulyRtKXJ8Sk9L3LhO9uLHFtU9CZ+2CpiEEM6q3TCNzduU7LC9NHdl/bx4uKqgz4h 9l/i+P3CXFglBpFDL+JTD72myBbbm78bQAXDoJWSTm9EKolpUZaTP2xpCrrG+A7f RRPzJoYOtxEgDTnNcjH16OX2TGXpPL0Q2cTl4vJihaZW9KrTPjYHQY5BIf4EQzhU Kd2p1yN/aYQFsLA5cofpkC4HPBKeiocg0HAu6byo9N+uLTjnxoZUkekfq03UXVan xwENUYknFmuI0SLnz3f2fHqWKXsdrron+gzldtBUBtTd8EOvh7c7/TNE+IrpTgRo HvC4KddOp30filp8GBxXU79i2qFzTyvjGwxzuiMo29n0R0i8tzdCokywsvAgWHvX KC8ukcmwsGq1lJB32rZ5RkacBCDS/iSaD0xg7iseHoYYpUPRuWxzoTVV134LGh5Q IX42NGvS8mA2I5byd3R9DtDY5eQYSN7bRGsNWSKJCKRSX9zEHpIKTQXT8N/aXvxe PQoxcEau3AuzbQs9KjFj =YJ0E -----END PGP SIGNATURE----- Merge tag 'kvm-s390-next-4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux KVM: s390: Fixes and features for 4.16 part 2 - exitless interrupts for emulated devices (Michael Mueller) - cleanup of cpuflag handling (David Hildenbrand) - kvm stat counter improvements (Christian Borntraeger) - vsie improvements (David Hildenbrand) - mm cleanup (Janosch Frank)
This commit is contained in:
commit
92ea2b3381
@ -261,6 +261,11 @@ static inline void clear_bit_inv(unsigned long nr, volatile unsigned long *ptr)
|
||||
return clear_bit(nr ^ (BITS_PER_LONG - 1), ptr);
|
||||
}
|
||||
|
||||
static inline int test_and_clear_bit_inv(unsigned long nr, volatile unsigned long *ptr)
|
||||
{
|
||||
return test_and_clear_bit(nr ^ (BITS_PER_LONG - 1), ptr);
|
||||
}
|
||||
|
||||
static inline void __set_bit_inv(unsigned long nr, volatile unsigned long *ptr)
|
||||
{
|
||||
return __set_bit(nr ^ (BITS_PER_LONG - 1), ptr);
|
||||
|
@ -20,7 +20,9 @@ struct css_general_char {
|
||||
u32 aif_tdd : 1; /* bit 56 */
|
||||
u32 : 1;
|
||||
u32 qebsm : 1; /* bit 58 */
|
||||
u32 : 8;
|
||||
u32 : 2;
|
||||
u32 aiv : 1; /* bit 61 */
|
||||
u32 : 5;
|
||||
u32 aif_osa : 1; /* bit 67 */
|
||||
u32 : 12;
|
||||
u32 eadm_rf : 1; /* bit 80 */
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* definition for kernel virtual machines on s390
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2009
|
||||
* Copyright IBM Corp. 2008, 2018
|
||||
*
|
||||
* Author(s): Carsten Otte <cotte@de.ibm.com>
|
||||
*/
|
||||
@ -183,6 +183,7 @@ struct kvm_s390_sie_block {
|
||||
#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_SII 0x00000001
|
||||
@ -227,7 +228,9 @@ struct kvm_s390_sie_block {
|
||||
__u8 epdx; /* 0x0069 */
|
||||
__u8 reserved6a[2]; /* 0x006a */
|
||||
__u32 todpr; /* 0x006c */
|
||||
__u8 reserved70[16]; /* 0x0070 */
|
||||
#define GISA_FORMAT1 0x00000001
|
||||
__u32 gd; /* 0x0070 */
|
||||
__u8 reserved74[12]; /* 0x0074 */
|
||||
__u64 mso; /* 0x0080 */
|
||||
__u64 msl; /* 0x0088 */
|
||||
psw_t gpsw; /* 0x0090 */
|
||||
@ -316,18 +319,30 @@ struct kvm_vcpu_stat {
|
||||
u64 deliver_program_int;
|
||||
u64 deliver_io_int;
|
||||
u64 exit_wait_state;
|
||||
u64 instruction_epsw;
|
||||
u64 instruction_gs;
|
||||
u64 instruction_io_other;
|
||||
u64 instruction_lpsw;
|
||||
u64 instruction_lpswe;
|
||||
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_storage_key;
|
||||
u64 instruction_iske;
|
||||
u64 instruction_ri;
|
||||
u64 instruction_rrbe;
|
||||
u64 instruction_sske;
|
||||
u64 instruction_ipte_interlock;
|
||||
u64 instruction_stsch;
|
||||
u64 instruction_chsc;
|
||||
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;
|
||||
@ -353,6 +368,7 @@ struct kvm_vcpu_stat {
|
||||
u64 diagnose_258;
|
||||
u64 diagnose_308;
|
||||
u64 diagnose_500;
|
||||
u64 diagnose_other;
|
||||
};
|
||||
|
||||
#define PGM_OPERATION 0x01
|
||||
@ -409,35 +425,35 @@ struct kvm_vcpu_stat {
|
||||
#define PGM_PER 0x80
|
||||
#define PGM_CRYPTO_OPERATION 0x119
|
||||
|
||||
/* irq types in order of priority */
|
||||
/* irq types in ascend order of priorities */
|
||||
enum irq_types {
|
||||
IRQ_PEND_MCHK_EX = 0,
|
||||
IRQ_PEND_SVC,
|
||||
IRQ_PEND_PROG,
|
||||
IRQ_PEND_MCHK_REP,
|
||||
IRQ_PEND_EXT_IRQ_KEY,
|
||||
IRQ_PEND_EXT_MALFUNC,
|
||||
IRQ_PEND_EXT_EMERGENCY,
|
||||
IRQ_PEND_EXT_EXTERNAL,
|
||||
IRQ_PEND_EXT_CLOCK_COMP,
|
||||
IRQ_PEND_EXT_CPU_TIMER,
|
||||
IRQ_PEND_EXT_TIMING,
|
||||
IRQ_PEND_EXT_SERVICE,
|
||||
IRQ_PEND_EXT_HOST,
|
||||
IRQ_PEND_PFAULT_INIT,
|
||||
IRQ_PEND_PFAULT_DONE,
|
||||
IRQ_PEND_VIRTIO,
|
||||
IRQ_PEND_IO_ISC_0,
|
||||
IRQ_PEND_IO_ISC_1,
|
||||
IRQ_PEND_IO_ISC_2,
|
||||
IRQ_PEND_IO_ISC_3,
|
||||
IRQ_PEND_IO_ISC_4,
|
||||
IRQ_PEND_IO_ISC_5,
|
||||
IRQ_PEND_IO_ISC_6,
|
||||
IRQ_PEND_IO_ISC_7,
|
||||
IRQ_PEND_SIGP_STOP,
|
||||
IRQ_PEND_SET_PREFIX = 0,
|
||||
IRQ_PEND_RESTART,
|
||||
IRQ_PEND_SET_PREFIX,
|
||||
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_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
|
||||
};
|
||||
|
||||
@ -703,14 +719,50 @@ struct kvm_s390_crypto_cb {
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* sie_page2 has to be allocated as DMA because fac_list and crycb need
|
||||
* 31bit addresses in the sie control block.
|
||||
* 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 */
|
||||
u8 reserved900[0x1000 - 0x900]; /* 0x0900 */
|
||||
struct kvm_s390_gisa gisa; /* 0x0900 */
|
||||
u8 reserved920[0x1000 - 0x920]; /* 0x0920 */
|
||||
};
|
||||
|
||||
struct kvm_s390_vsie {
|
||||
@ -757,6 +809,7 @@ struct kvm_arch{
|
||||
struct kvm_s390_migration_state *migration_state;
|
||||
/* subset of available cpu features enabled by user space */
|
||||
DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
|
||||
struct kvm_s390_gisa *gisa;
|
||||
};
|
||||
|
||||
#define KVM_HVA_ERR_BAD (-1UL)
|
||||
|
@ -77,6 +77,7 @@ struct sclp_info {
|
||||
unsigned char has_ibs : 1;
|
||||
unsigned char has_skey : 1;
|
||||
unsigned char has_kss : 1;
|
||||
unsigned char has_gisaf : 1;
|
||||
unsigned int ibc;
|
||||
unsigned int mtid;
|
||||
unsigned int mtid_cp;
|
||||
|
@ -257,6 +257,7 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu)
|
||||
case 0x500:
|
||||
return __diag_virtio_hypercall(vcpu);
|
||||
default:
|
||||
vcpu->stat.diagnose_other++;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
|
||||
{
|
||||
int c, scn;
|
||||
|
||||
if (!(atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND))
|
||||
if (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND))
|
||||
return 0;
|
||||
|
||||
BUG_ON(!kvm_s390_use_sca_entries());
|
||||
@ -101,7 +101,7 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
|
||||
/* another external call is pending */
|
||||
return -EBUSY;
|
||||
}
|
||||
atomic_or(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
|
||||
|
||||
if (!kvm_s390_use_sca_entries())
|
||||
return;
|
||||
atomic_andnot(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
|
||||
read_lock(&vcpu->kvm->arch.sca_lock);
|
||||
if (vcpu->kvm->arch.use_esca) {
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
@ -189,8 +189,8 @@ static int cpu_timer_irq_pending(struct kvm_vcpu *vcpu)
|
||||
|
||||
static inline int is_ioirq(unsigned long irq_type)
|
||||
{
|
||||
return ((irq_type >= IRQ_PEND_IO_ISC_0) &&
|
||||
(irq_type <= IRQ_PEND_IO_ISC_7));
|
||||
return ((irq_type >= IRQ_PEND_IO_ISC_7) &&
|
||||
(irq_type <= IRQ_PEND_IO_ISC_0));
|
||||
}
|
||||
|
||||
static uint64_t isc_to_isc_bits(int isc)
|
||||
@ -198,25 +198,59 @@ static uint64_t isc_to_isc_bits(int isc)
|
||||
return (0x80 >> isc) << 24;
|
||||
}
|
||||
|
||||
static inline u32 isc_to_int_word(u8 isc)
|
||||
{
|
||||
return ((u32)isc << 27) | 0x80000000;
|
||||
}
|
||||
|
||||
static inline u8 int_word_to_isc(u32 int_word)
|
||||
{
|
||||
return (int_word & 0x38000000) >> 27;
|
||||
}
|
||||
|
||||
/*
|
||||
* To use atomic bitmap functions, we have to provide a bitmap address
|
||||
* that is u64 aligned. However, the ipm might be u32 aligned.
|
||||
* Therefore, we logically start the bitmap at the very beginning of the
|
||||
* struct and fixup the bit number.
|
||||
*/
|
||||
#define IPM_BIT_OFFSET (offsetof(struct kvm_s390_gisa, ipm) * BITS_PER_BYTE)
|
||||
|
||||
static inline void kvm_s390_gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
|
||||
{
|
||||
set_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa);
|
||||
}
|
||||
|
||||
static inline u8 kvm_s390_gisa_get_ipm(struct kvm_s390_gisa *gisa)
|
||||
{
|
||||
return READ_ONCE(gisa->ipm);
|
||||
}
|
||||
|
||||
static inline void kvm_s390_gisa_clear_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
|
||||
{
|
||||
clear_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa);
|
||||
}
|
||||
|
||||
static inline int kvm_s390_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 unsigned long pending_irqs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->kvm->arch.float_int.pending_irqs |
|
||||
vcpu->arch.local_int.pending_irqs;
|
||||
vcpu->arch.local_int.pending_irqs |
|
||||
kvm_s390_gisa_get_ipm(vcpu->kvm->arch.gisa) << IRQ_PEND_IO_ISC_7;
|
||||
}
|
||||
|
||||
static inline int isc_to_irq_type(unsigned long isc)
|
||||
{
|
||||
return IRQ_PEND_IO_ISC_0 + isc;
|
||||
return IRQ_PEND_IO_ISC_0 - isc;
|
||||
}
|
||||
|
||||
static inline int irq_type_to_isc(unsigned long irq_type)
|
||||
{
|
||||
return irq_type - IRQ_PEND_IO_ISC_0;
|
||||
return IRQ_PEND_IO_ISC_0 - irq_type;
|
||||
}
|
||||
|
||||
static unsigned long disable_iscs(struct kvm_vcpu *vcpu,
|
||||
@ -277,20 +311,20 @@ static unsigned long deliverable_irqs(struct kvm_vcpu *vcpu)
|
||||
|
||||
static void __set_cpu_idle(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
atomic_or(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_WAIT);
|
||||
set_bit(vcpu->vcpu_id, vcpu->kvm->arch.float_int.idle_mask);
|
||||
}
|
||||
|
||||
static void __unset_cpu_idle(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
atomic_andnot(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_WAIT);
|
||||
clear_bit(vcpu->vcpu_id, vcpu->kvm->arch.float_int.idle_mask);
|
||||
}
|
||||
|
||||
static void __reset_intercept_indicators(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
atomic_andnot(CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
|
||||
&vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_IO_INT | CPUSTAT_EXT_INT |
|
||||
CPUSTAT_STOP_INT);
|
||||
vcpu->arch.sie_block->lctl = 0x0000;
|
||||
vcpu->arch.sie_block->ictl &= ~(ICTL_LPSW | ICTL_STCTL | ICTL_PINT);
|
||||
|
||||
@ -301,17 +335,12 @@ static void __reset_intercept_indicators(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag)
|
||||
{
|
||||
atomic_or(flag, &vcpu->arch.sie_block->cpuflags);
|
||||
}
|
||||
|
||||
static void set_intercept_indicators_io(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!(pending_irqs(vcpu) & IRQ_PEND_IO_MASK))
|
||||
return;
|
||||
else if (psw_ioint_disabled(vcpu))
|
||||
__set_cpuflag(vcpu, CPUSTAT_IO_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_IO_INT);
|
||||
else
|
||||
vcpu->arch.sie_block->lctl |= LCTL_CR6;
|
||||
}
|
||||
@ -321,7 +350,7 @@ static void set_intercept_indicators_ext(struct kvm_vcpu *vcpu)
|
||||
if (!(pending_irqs(vcpu) & IRQ_PEND_EXT_MASK))
|
||||
return;
|
||||
if (psw_extint_disabled(vcpu))
|
||||
__set_cpuflag(vcpu, CPUSTAT_EXT_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT);
|
||||
else
|
||||
vcpu->arch.sie_block->lctl |= LCTL_CR0;
|
||||
}
|
||||
@ -339,7 +368,7 @@ static void set_intercept_indicators_mchk(struct kvm_vcpu *vcpu)
|
||||
static void set_intercept_indicators_stop(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_s390_is_stop_irq_pending(vcpu))
|
||||
__set_cpuflag(vcpu, CPUSTAT_STOP_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOP_INT);
|
||||
}
|
||||
|
||||
/* Set interception request for non-deliverable interrupts */
|
||||
@ -896,18 +925,38 @@ static int __must_check __deliver_virtio(struct kvm_vcpu *vcpu)
|
||||
return rc ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int __do_deliver_io(struct kvm_vcpu *vcpu, struct kvm_s390_io_info *io)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = put_guest_lc(vcpu, io->subchannel_id, (u16 *)__LC_SUBCHANNEL_ID);
|
||||
rc |= put_guest_lc(vcpu, io->subchannel_nr, (u16 *)__LC_SUBCHANNEL_NR);
|
||||
rc |= put_guest_lc(vcpu, io->io_int_parm, (u32 *)__LC_IO_INT_PARM);
|
||||
rc |= put_guest_lc(vcpu, io->io_int_word, (u32 *)__LC_IO_INT_WORD);
|
||||
rc |= write_guest_lc(vcpu, __LC_IO_OLD_PSW,
|
||||
&vcpu->arch.sie_block->gpsw,
|
||||
sizeof(psw_t));
|
||||
rc |= read_guest_lc(vcpu, __LC_IO_NEW_PSW,
|
||||
&vcpu->arch.sie_block->gpsw,
|
||||
sizeof(psw_t));
|
||||
return rc ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int __must_check __deliver_io(struct kvm_vcpu *vcpu,
|
||||
unsigned long irq_type)
|
||||
{
|
||||
struct list_head *isc_list;
|
||||
struct kvm_s390_float_interrupt *fi;
|
||||
struct kvm_s390_interrupt_info *inti = NULL;
|
||||
struct kvm_s390_io_info io;
|
||||
u32 isc;
|
||||
int rc = 0;
|
||||
|
||||
fi = &vcpu->kvm->arch.float_int;
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
isc_list = &fi->lists[irq_type_to_isc(irq_type)];
|
||||
isc = irq_type_to_isc(irq_type);
|
||||
isc_list = &fi->lists[isc];
|
||||
inti = list_first_entry_or_null(isc_list,
|
||||
struct kvm_s390_interrupt_info,
|
||||
list);
|
||||
@ -935,24 +984,31 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu,
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
if (inti) {
|
||||
rc = put_guest_lc(vcpu, inti->io.subchannel_id,
|
||||
(u16 *)__LC_SUBCHANNEL_ID);
|
||||
rc |= put_guest_lc(vcpu, inti->io.subchannel_nr,
|
||||
(u16 *)__LC_SUBCHANNEL_NR);
|
||||
rc |= put_guest_lc(vcpu, inti->io.io_int_parm,
|
||||
(u32 *)__LC_IO_INT_PARM);
|
||||
rc |= put_guest_lc(vcpu, inti->io.io_int_word,
|
||||
(u32 *)__LC_IO_INT_WORD);
|
||||
rc |= write_guest_lc(vcpu, __LC_IO_OLD_PSW,
|
||||
&vcpu->arch.sie_block->gpsw,
|
||||
sizeof(psw_t));
|
||||
rc |= read_guest_lc(vcpu, __LC_IO_NEW_PSW,
|
||||
&vcpu->arch.sie_block->gpsw,
|
||||
sizeof(psw_t));
|
||||
rc = __do_deliver_io(vcpu, &(inti->io));
|
||||
kfree(inti);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return rc ? -EFAULT : 0;
|
||||
if (vcpu->kvm->arch.gisa &&
|
||||
kvm_s390_gisa_tac_ipm_gisc(vcpu->kvm->arch.gisa, isc)) {
|
||||
/*
|
||||
* in case an adapter interrupt was not delivered
|
||||
* in SIE context KVM will handle the delivery
|
||||
*/
|
||||
VCPU_EVENT(vcpu, 4, "%s isc %u", "deliver: I/O (AI/gisa)", isc);
|
||||
memset(&io, 0, sizeof(io));
|
||||
io.io_int_word = isc_to_int_word(isc);
|
||||
vcpu->stat.deliver_io_int++;
|
||||
trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id,
|
||||
KVM_S390_INT_IO(1, 0, 0, 0),
|
||||
((__u32)io.subchannel_id << 16) |
|
||||
io.subchannel_nr,
|
||||
((__u64)io.io_int_parm << 32) |
|
||||
io.io_int_word);
|
||||
rc = __do_deliver_io(vcpu, &io);
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef int (*deliver_irq_t)(struct kvm_vcpu *vcpu);
|
||||
@ -1154,8 +1210,8 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
|
||||
set_bit(IRQ_PEND_EXT_CPU_TIMER, &li->pending_irqs);
|
||||
|
||||
while ((irqs = deliverable_irqs(vcpu)) && !rc) {
|
||||
/* bits are in the order of interrupt priority */
|
||||
irq_type = find_first_bit(&irqs, IRQ_PEND_COUNT);
|
||||
/* bits are in the reverse order of interrupt priority */
|
||||
irq_type = find_last_bit(&irqs, IRQ_PEND_COUNT);
|
||||
if (is_ioirq(irq_type)) {
|
||||
rc = __deliver_io(vcpu, irq_type);
|
||||
} else {
|
||||
@ -1227,7 +1283,7 @@ static int __inject_pfault_init(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
|
||||
li->irq.ext = irq->u.ext;
|
||||
set_bit(IRQ_PEND_PFAULT_INIT, &li->pending_irqs);
|
||||
__set_cpuflag(vcpu, CPUSTAT_EXT_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1252,7 +1308,7 @@ static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
if (test_and_set_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs))
|
||||
return -EBUSY;
|
||||
*extcall = irq->u.extcall;
|
||||
__set_cpuflag(vcpu, CPUSTAT_EXT_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1296,7 +1352,7 @@ static int __inject_sigp_stop(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
if (test_and_set_bit(IRQ_PEND_SIGP_STOP, &li->pending_irqs))
|
||||
return -EBUSY;
|
||||
stop->flags = irq->u.stop.flags;
|
||||
__set_cpuflag(vcpu, CPUSTAT_STOP_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOP_INT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1328,7 +1384,7 @@ static int __inject_sigp_emergency(struct kvm_vcpu *vcpu,
|
||||
|
||||
set_bit(irq->u.emerg.code, li->sigp_emerg_pending);
|
||||
set_bit(IRQ_PEND_EXT_EMERGENCY, &li->pending_irqs);
|
||||
__set_cpuflag(vcpu, CPUSTAT_EXT_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1372,7 +1428,7 @@ static int __inject_ckc(struct kvm_vcpu *vcpu)
|
||||
0, 0);
|
||||
|
||||
set_bit(IRQ_PEND_EXT_CLOCK_COMP, &li->pending_irqs);
|
||||
__set_cpuflag(vcpu, CPUSTAT_EXT_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1385,7 +1441,7 @@ static int __inject_cpu_timer(struct kvm_vcpu *vcpu)
|
||||
0, 0);
|
||||
|
||||
set_bit(IRQ_PEND_EXT_CPU_TIMER, &li->pending_irqs);
|
||||
__set_cpuflag(vcpu, CPUSTAT_EXT_INT);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1415,12 +1471,8 @@ static struct kvm_s390_interrupt_info *get_io_int(struct kvm *kvm,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue and return an I/O interrupt matching any of the interruption
|
||||
* subclasses as designated by the isc mask in cr6 and the schid (if != 0).
|
||||
*/
|
||||
struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
|
||||
u64 isc_mask, u32 schid)
|
||||
static struct kvm_s390_interrupt_info *get_top_io_int(struct kvm *kvm,
|
||||
u64 isc_mask, u32 schid)
|
||||
{
|
||||
struct kvm_s390_interrupt_info *inti = NULL;
|
||||
int isc;
|
||||
@ -1432,6 +1484,76 @@ struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
|
||||
return inti;
|
||||
}
|
||||
|
||||
static int get_top_gisa_isc(struct kvm *kvm, u64 isc_mask, u32 schid)
|
||||
{
|
||||
unsigned long active_mask;
|
||||
int isc;
|
||||
|
||||
if (schid)
|
||||
goto out;
|
||||
if (!kvm->arch.gisa)
|
||||
goto out;
|
||||
|
||||
active_mask = (isc_mask & kvm_s390_gisa_get_ipm(kvm->arch.gisa) << 24) << 32;
|
||||
while (active_mask) {
|
||||
isc = __fls(active_mask) ^ (BITS_PER_LONG - 1);
|
||||
if (kvm_s390_gisa_tac_ipm_gisc(kvm->arch.gisa, isc))
|
||||
return isc;
|
||||
clear_bit_inv(isc, &active_mask);
|
||||
}
|
||||
out:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue and return an I/O interrupt matching any of the interruption
|
||||
* subclasses as designated by the isc mask in cr6 and the schid (if != 0).
|
||||
* Take into account the interrupts pending in the interrupt list and in GISA.
|
||||
*
|
||||
* Note that for a guest that does not enable I/O interrupts
|
||||
* but relies on TPI, a flood of classic interrupts may starve
|
||||
* out adapter interrupts on the same isc. Linux does not do
|
||||
* that, and it is possible to work around the issue by configuring
|
||||
* different iscs for classic and adapter interrupts in the guest,
|
||||
* but we may want to revisit this in the future.
|
||||
*/
|
||||
struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
|
||||
u64 isc_mask, u32 schid)
|
||||
{
|
||||
struct kvm_s390_interrupt_info *inti, *tmp_inti;
|
||||
int isc;
|
||||
|
||||
inti = get_top_io_int(kvm, isc_mask, schid);
|
||||
|
||||
isc = get_top_gisa_isc(kvm, isc_mask, schid);
|
||||
if (isc < 0)
|
||||
/* no AI in GISA */
|
||||
goto out;
|
||||
|
||||
if (!inti)
|
||||
/* AI in GISA but no classical IO int */
|
||||
goto gisa_out;
|
||||
|
||||
/* both types of interrupts present */
|
||||
if (int_word_to_isc(inti->io.io_int_word) <= isc) {
|
||||
/* classical IO int with higher priority */
|
||||
kvm_s390_gisa_set_ipm_gisc(kvm->arch.gisa, isc);
|
||||
goto out;
|
||||
}
|
||||
gisa_out:
|
||||
tmp_inti = kzalloc(sizeof(*inti), GFP_KERNEL);
|
||||
if (tmp_inti) {
|
||||
tmp_inti->type = KVM_S390_INT_IO(1, 0, 0, 0);
|
||||
tmp_inti->io.io_int_word = isc_to_int_word(isc);
|
||||
if (inti)
|
||||
kvm_s390_reinject_io_int(kvm, inti);
|
||||
inti = tmp_inti;
|
||||
} else
|
||||
kvm_s390_gisa_set_ipm_gisc(kvm->arch.gisa, isc);
|
||||
out:
|
||||
return inti;
|
||||
}
|
||||
|
||||
#define SCCB_MASK 0xFFFFFFF8
|
||||
#define SCCB_EVENT_PENDING 0x3
|
||||
|
||||
@ -1516,6 +1638,15 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
|
||||
struct list_head *list;
|
||||
int isc;
|
||||
|
||||
isc = int_word_to_isc(inti->io.io_int_word);
|
||||
|
||||
if (kvm->arch.gisa && inti->type & KVM_S390_INT_IO_AI_MASK) {
|
||||
VM_EVENT(kvm, 4, "%s isc %1u", "inject: I/O (AI/gisa)", isc);
|
||||
kvm_s390_gisa_set_ipm_gisc(kvm->arch.gisa, isc);
|
||||
kfree(inti);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fi = &kvm->arch.float_int;
|
||||
spin_lock(&fi->lock);
|
||||
if (fi->counters[FIRQ_CNTR_IO] >= KVM_S390_MAX_FLOAT_IRQS) {
|
||||
@ -1531,7 +1662,6 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
|
||||
inti->io.subchannel_id >> 8,
|
||||
inti->io.subchannel_id >> 1 & 0x3,
|
||||
inti->io.subchannel_nr);
|
||||
isc = int_word_to_isc(inti->io.io_int_word);
|
||||
list = &fi->lists[FIRQ_LIST_IO_ISC_0 + isc];
|
||||
list_add_tail(&inti->list, list);
|
||||
set_bit(isc_to_irq_type(isc), &fi->pending_irqs);
|
||||
@ -1568,13 +1698,13 @@ static void __floating_irq_kick(struct kvm *kvm, u64 type)
|
||||
/* make the VCPU drop out of the SIE, or wake it up if sleeping */
|
||||
switch (type) {
|
||||
case KVM_S390_MCHK:
|
||||
__set_cpuflag(dst_vcpu, CPUSTAT_STOP_INT);
|
||||
kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_STOP_INT);
|
||||
break;
|
||||
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
||||
__set_cpuflag(dst_vcpu, CPUSTAT_IO_INT);
|
||||
kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_IO_INT);
|
||||
break;
|
||||
default:
|
||||
__set_cpuflag(dst_vcpu, CPUSTAT_EXT_INT);
|
||||
kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_EXT_INT);
|
||||
break;
|
||||
}
|
||||
kvm_s390_vcpu_wakeup(dst_vcpu);
|
||||
@ -1815,6 +1945,7 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm)
|
||||
for (i = 0; i < FIRQ_MAX_COUNT; i++)
|
||||
fi->counters[i] = 0;
|
||||
spin_unlock(&fi->lock);
|
||||
kvm_s390_gisa_clear(kvm);
|
||||
};
|
||||
|
||||
static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
|
||||
@ -1842,6 +1973,22 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
|
||||
|
||||
max_irqs = len / sizeof(struct kvm_s390_irq);
|
||||
|
||||
if (kvm->arch.gisa &&
|
||||
kvm_s390_gisa_get_ipm(kvm->arch.gisa)) {
|
||||
for (i = 0; i <= MAX_ISC; i++) {
|
||||
if (n == max_irqs) {
|
||||
/* signal userspace to try again */
|
||||
ret = -ENOMEM;
|
||||
goto out_nolock;
|
||||
}
|
||||
if (kvm_s390_gisa_tac_ipm_gisc(kvm->arch.gisa, 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);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fi = &kvm->arch.float_int;
|
||||
spin_lock(&fi->lock);
|
||||
for (i = 0; i < FIRQ_LIST_COUNT; i++) {
|
||||
@ -1880,6 +2027,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
|
||||
|
||||
out:
|
||||
spin_unlock(&fi->lock);
|
||||
out_nolock:
|
||||
if (!ret && n > 0) {
|
||||
if (copy_to_user(usrbuf, buf, sizeof(struct kvm_s390_irq) * n))
|
||||
ret = -EFAULT;
|
||||
@ -2240,7 +2388,7 @@ static int kvm_s390_inject_airq(struct kvm *kvm,
|
||||
struct kvm_s390_interrupt s390int = {
|
||||
.type = KVM_S390_INT_IO(1, 0, 0, 0),
|
||||
.parm = 0,
|
||||
.parm64 = (adapter->isc << 27) | 0x80000000,
|
||||
.parm64 = isc_to_int_word(adapter->isc),
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
@ -2682,3 +2830,28 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len)
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void kvm_s390_gisa_clear(struct kvm *kvm)
|
||||
{
|
||||
if (kvm->arch.gisa) {
|
||||
memset(kvm->arch.gisa, 0, sizeof(struct kvm_s390_gisa));
|
||||
kvm->arch.gisa->next_alert = (u32)(u64)kvm->arch.gisa;
|
||||
VM_EVENT(kvm, 3, "gisa 0x%pK cleared", kvm->arch.gisa);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_s390_gisa_init(struct kvm *kvm)
|
||||
{
|
||||
if (css_general_characteristics.aiv) {
|
||||
kvm->arch.gisa = &kvm->arch.sie_page2->gisa;
|
||||
VM_EVENT(kvm, 3, "gisa 0x%pK initialized", kvm->arch.gisa);
|
||||
kvm_s390_gisa_clear(kvm);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_s390_gisa_destroy(struct kvm *kvm)
|
||||
{
|
||||
if (!kvm->arch.gisa)
|
||||
return;
|
||||
kvm->arch.gisa = NULL;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* hosting IBM Z kernel virtual machines (s390x)
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2017
|
||||
* Copyright IBM Corp. 2008, 2018
|
||||
*
|
||||
* Author(s): Carsten Otte <cotte@de.ibm.com>
|
||||
* Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
@ -87,19 +87,31 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
||||
{ "deliver_restart_signal", VCPU_STAT(deliver_restart_signal) },
|
||||
{ "deliver_program_interruption", VCPU_STAT(deliver_program_int) },
|
||||
{ "exit_wait_state", VCPU_STAT(exit_wait_state) },
|
||||
{ "instruction_epsw", VCPU_STAT(instruction_epsw) },
|
||||
{ "instruction_gs", VCPU_STAT(instruction_gs) },
|
||||
{ "instruction_io_other", VCPU_STAT(instruction_io_other) },
|
||||
{ "instruction_lpsw", VCPU_STAT(instruction_lpsw) },
|
||||
{ "instruction_lpswe", VCPU_STAT(instruction_lpswe) },
|
||||
{ "instruction_pfmf", VCPU_STAT(instruction_pfmf) },
|
||||
{ "instruction_ptff", VCPU_STAT(instruction_ptff) },
|
||||
{ "instruction_stidp", VCPU_STAT(instruction_stidp) },
|
||||
{ "instruction_sck", VCPU_STAT(instruction_sck) },
|
||||
{ "instruction_sckpf", VCPU_STAT(instruction_sckpf) },
|
||||
{ "instruction_spx", VCPU_STAT(instruction_spx) },
|
||||
{ "instruction_stpx", VCPU_STAT(instruction_stpx) },
|
||||
{ "instruction_stap", VCPU_STAT(instruction_stap) },
|
||||
{ "instruction_storage_key", VCPU_STAT(instruction_storage_key) },
|
||||
{ "instruction_iske", VCPU_STAT(instruction_iske) },
|
||||
{ "instruction_ri", VCPU_STAT(instruction_ri) },
|
||||
{ "instruction_rrbe", VCPU_STAT(instruction_rrbe) },
|
||||
{ "instruction_sske", VCPU_STAT(instruction_sske) },
|
||||
{ "instruction_ipte_interlock", VCPU_STAT(instruction_ipte_interlock) },
|
||||
{ "instruction_stsch", VCPU_STAT(instruction_stsch) },
|
||||
{ "instruction_chsc", VCPU_STAT(instruction_chsc) },
|
||||
{ "instruction_essa", VCPU_STAT(instruction_essa) },
|
||||
{ "instruction_stsi", VCPU_STAT(instruction_stsi) },
|
||||
{ "instruction_stfl", VCPU_STAT(instruction_stfl) },
|
||||
{ "instruction_tb", VCPU_STAT(instruction_tb) },
|
||||
{ "instruction_tpi", VCPU_STAT(instruction_tpi) },
|
||||
{ "instruction_tprot", VCPU_STAT(instruction_tprot) },
|
||||
{ "instruction_tsch", VCPU_STAT(instruction_tsch) },
|
||||
{ "instruction_sthyi", VCPU_STAT(instruction_sthyi) },
|
||||
{ "instruction_sie", VCPU_STAT(instruction_sie) },
|
||||
{ "instruction_sigp_sense", VCPU_STAT(instruction_sigp_sense) },
|
||||
@ -118,12 +130,13 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
||||
{ "instruction_sigp_cpu_reset", VCPU_STAT(instruction_sigp_cpu_reset) },
|
||||
{ "instruction_sigp_init_cpu_reset", VCPU_STAT(instruction_sigp_init_cpu_reset) },
|
||||
{ "instruction_sigp_unknown", VCPU_STAT(instruction_sigp_unknown) },
|
||||
{ "diagnose_10", VCPU_STAT(diagnose_10) },
|
||||
{ "diagnose_44", VCPU_STAT(diagnose_44) },
|
||||
{ "diagnose_9c", VCPU_STAT(diagnose_9c) },
|
||||
{ "diagnose_258", VCPU_STAT(diagnose_258) },
|
||||
{ "diagnose_308", VCPU_STAT(diagnose_308) },
|
||||
{ "diagnose_500", VCPU_STAT(diagnose_500) },
|
||||
{ "instruction_diag_10", VCPU_STAT(diagnose_10) },
|
||||
{ "instruction_diag_44", VCPU_STAT(diagnose_44) },
|
||||
{ "instruction_diag_9c", VCPU_STAT(diagnose_9c) },
|
||||
{ "instruction_diag_258", VCPU_STAT(diagnose_258) },
|
||||
{ "instruction_diag_308", VCPU_STAT(diagnose_308) },
|
||||
{ "instruction_diag_500", VCPU_STAT(diagnose_500) },
|
||||
{ "instruction_diag_other", VCPU_STAT(diagnose_other) },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -1915,6 +1928,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||
if (!kvm->arch.dbf)
|
||||
goto out_err;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct sie_page2) != 4096);
|
||||
kvm->arch.sie_page2 =
|
||||
(struct sie_page2 *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
||||
if (!kvm->arch.sie_page2)
|
||||
@ -1985,6 +1999,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||
|
||||
spin_lock_init(&kvm->arch.start_stop_lock);
|
||||
kvm_s390_vsie_init(kvm);
|
||||
kvm_s390_gisa_init(kvm);
|
||||
KVM_EVENT(3, "vm 0x%pK created by pid %u", kvm, current->pid);
|
||||
|
||||
return 0;
|
||||
@ -2047,6 +2062,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
|
||||
kvm_free_vcpus(kvm);
|
||||
sca_dispose(kvm);
|
||||
debug_unregister(kvm->arch.dbf);
|
||||
kvm_s390_gisa_destroy(kvm);
|
||||
free_page((unsigned long)kvm->arch.sie_page2);
|
||||
if (!kvm_is_ucontrol(kvm))
|
||||
gmap_remove(kvm->arch.gmap);
|
||||
@ -2316,7 +2332,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
{
|
||||
|
||||
gmap_enable(vcpu->arch.enabled_gmap);
|
||||
atomic_or(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_RUNNING);
|
||||
if (vcpu->arch.cputm_enabled && !is_vcpu_idle(vcpu))
|
||||
__start_cpu_timer_accounting(vcpu);
|
||||
vcpu->cpu = cpu;
|
||||
@ -2327,7 +2343,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
vcpu->cpu = -1;
|
||||
if (vcpu->arch.cputm_enabled && !is_vcpu_idle(vcpu))
|
||||
__stop_cpu_timer_accounting(vcpu);
|
||||
atomic_andnot(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_RUNNING);
|
||||
vcpu->arch.enabled_gmap = gmap_get_enabled();
|
||||
gmap_disable(vcpu->arch.enabled_gmap);
|
||||
|
||||
@ -2423,9 +2439,9 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
|
||||
CPUSTAT_STOPPED);
|
||||
|
||||
if (test_kvm_facility(vcpu->kvm, 78))
|
||||
atomic_or(CPUSTAT_GED2, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_GED2);
|
||||
else if (test_kvm_facility(vcpu->kvm, 8))
|
||||
atomic_or(CPUSTAT_GED, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_GED);
|
||||
|
||||
kvm_s390_vcpu_setup_model(vcpu);
|
||||
|
||||
@ -2457,12 +2473,17 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
|
||||
if (test_kvm_facility(vcpu->kvm, 139))
|
||||
vcpu->arch.sie_block->ecd |= ECD_MEF;
|
||||
|
||||
if (vcpu->arch.sie_block->gd) {
|
||||
vcpu->arch.sie_block->eca |= ECA_AIV;
|
||||
VCPU_EVENT(vcpu, 3, "AIV gisa format-%u enabled for cpu %03u",
|
||||
vcpu->arch.sie_block->gd & 0x3, vcpu->vcpu_id);
|
||||
}
|
||||
vcpu->arch.sie_block->sdnxo = ((unsigned long) &vcpu->run->s.regs.sdnx)
|
||||
| SDNXC;
|
||||
vcpu->arch.sie_block->riccbd = (unsigned long) &vcpu->run->s.regs.riccb;
|
||||
|
||||
if (sclp.has_kss)
|
||||
atomic_or(CPUSTAT_KSS, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_KSS);
|
||||
else
|
||||
vcpu->arch.sie_block->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE;
|
||||
|
||||
@ -2509,6 +2530,9 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
|
||||
|
||||
vcpu->arch.sie_block->icpua = id;
|
||||
spin_lock_init(&vcpu->arch.local_int.lock);
|
||||
vcpu->arch.sie_block->gd = (u32)(u64)kvm->arch.gisa;
|
||||
if (vcpu->arch.sie_block->gd && sclp.has_gisaf)
|
||||
vcpu->arch.sie_block->gd |= GISA_FORMAT1;
|
||||
seqcount_init(&vcpu->arch.cputm_seqcount);
|
||||
|
||||
rc = kvm_vcpu_init(vcpu, kvm, id);
|
||||
@ -2565,7 +2589,7 @@ static void kvm_s390_vcpu_request_handled(struct kvm_vcpu *vcpu)
|
||||
* return immediately. */
|
||||
void exit_sie(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
atomic_or(CPUSTAT_STOP_INT, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOP_INT);
|
||||
while (vcpu->arch.sie_block->prog0c & PROG_IN_SIE)
|
||||
cpu_relax();
|
||||
}
|
||||
@ -2840,19 +2864,19 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
|
||||
if (dbg->control & KVM_GUESTDBG_ENABLE) {
|
||||
vcpu->guest_debug = dbg->control;
|
||||
/* enforce guest PER */
|
||||
atomic_or(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_P);
|
||||
|
||||
if (dbg->control & KVM_GUESTDBG_USE_HW_BP)
|
||||
rc = kvm_s390_import_bp_data(vcpu, dbg);
|
||||
} else {
|
||||
atomic_andnot(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_P);
|
||||
vcpu->arch.guestdbg.last_bp = 0;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
vcpu->guest_debug = 0;
|
||||
kvm_s390_clear_bp_data(vcpu);
|
||||
atomic_andnot(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_P);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -2905,7 +2929,7 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
|
||||
|
||||
static bool ibs_enabled(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_IBS;
|
||||
return kvm_s390_test_cpuflags(vcpu, CPUSTAT_IBS);
|
||||
}
|
||||
|
||||
static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
|
||||
@ -2941,8 +2965,7 @@ retry:
|
||||
if (kvm_check_request(KVM_REQ_ENABLE_IBS, vcpu)) {
|
||||
if (!ibs_enabled(vcpu)) {
|
||||
trace_kvm_s390_enable_disable_ibs(vcpu->vcpu_id, 1);
|
||||
atomic_or(CPUSTAT_IBS,
|
||||
&vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_IBS);
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
@ -2950,8 +2973,7 @@ retry:
|
||||
if (kvm_check_request(KVM_REQ_DISABLE_IBS, vcpu)) {
|
||||
if (ibs_enabled(vcpu)) {
|
||||
trace_kvm_s390_enable_disable_ibs(vcpu->vcpu_id, 0);
|
||||
atomic_andnot(CPUSTAT_IBS,
|
||||
&vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_IBS);
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
@ -3601,7 +3623,7 @@ void kvm_s390_vcpu_start(struct kvm_vcpu *vcpu)
|
||||
__disable_ibs_on_all_vcpus(vcpu->kvm);
|
||||
}
|
||||
|
||||
atomic_andnot(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_STOPPED);
|
||||
/*
|
||||
* Another VCPU might have used IBS while we were offline.
|
||||
* Let's play safe and flush the VCPU at startup.
|
||||
@ -3627,7 +3649,7 @@ void kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu)
|
||||
/* SIGP STOP and SIGP STOP AND STORE STATUS has been fully processed */
|
||||
kvm_s390_clear_stop_irq(vcpu);
|
||||
|
||||
atomic_or(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOPPED);
|
||||
__disable_ibs_on_vcpu(vcpu);
|
||||
|
||||
for (i = 0; i < online_vcpus; i++) {
|
||||
|
@ -47,9 +47,24 @@ do { \
|
||||
d_args); \
|
||||
} while (0)
|
||||
|
||||
static inline void kvm_s390_set_cpuflags(struct kvm_vcpu *vcpu, u32 flags)
|
||||
{
|
||||
atomic_or(flags, &vcpu->arch.sie_block->cpuflags);
|
||||
}
|
||||
|
||||
static inline void kvm_s390_clear_cpuflags(struct kvm_vcpu *vcpu, u32 flags)
|
||||
{
|
||||
atomic_andnot(flags, &vcpu->arch.sie_block->cpuflags);
|
||||
}
|
||||
|
||||
static inline bool kvm_s390_test_cpuflags(struct kvm_vcpu *vcpu, u32 flags)
|
||||
{
|
||||
return (atomic_read(&vcpu->arch.sie_block->cpuflags) & flags) == flags;
|
||||
}
|
||||
|
||||
static inline int is_vcpu_stopped(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_STOPPED;
|
||||
return kvm_s390_test_cpuflags(vcpu, CPUSTAT_STOPPED);
|
||||
}
|
||||
|
||||
static inline int is_vcpu_idle(struct kvm_vcpu *vcpu)
|
||||
@ -367,6 +382,9 @@ int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu,
|
||||
void __user *buf, int len);
|
||||
int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu,
|
||||
__u8 __user *buf, int len);
|
||||
void kvm_s390_gisa_init(struct kvm *kvm);
|
||||
void kvm_s390_gisa_clear(struct kvm *kvm);
|
||||
void kvm_s390_gisa_destroy(struct kvm *kvm);
|
||||
|
||||
/* implemented in guestdbg.c */
|
||||
void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu);
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* handling privileged instructions
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2013
|
||||
* Copyright IBM Corp. 2008, 2018
|
||||
*
|
||||
* Author(s): Carsten Otte <cotte@de.ibm.com>
|
||||
* Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
@ -34,6 +34,8 @@
|
||||
|
||||
static int handle_ri(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->stat.instruction_ri++;
|
||||
|
||||
if (test_kvm_facility(vcpu->kvm, 64)) {
|
||||
VCPU_EVENT(vcpu, 3, "%s", "ENABLE: RI (lazy)");
|
||||
vcpu->arch.sie_block->ecb3 |= ECB3_RI;
|
||||
@ -53,6 +55,8 @@ int kvm_s390_handle_aa(struct kvm_vcpu *vcpu)
|
||||
|
||||
static int handle_gs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->stat.instruction_gs++;
|
||||
|
||||
if (test_kvm_facility(vcpu->kvm, 133)) {
|
||||
VCPU_EVENT(vcpu, 3, "%s", "ENABLE: GS (lazy)");
|
||||
preempt_disable();
|
||||
@ -85,6 +89,8 @@ static int handle_set_clock(struct kvm_vcpu *vcpu)
|
||||
u8 ar;
|
||||
u64 op2, val;
|
||||
|
||||
vcpu->stat.instruction_sck++;
|
||||
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -203,14 +209,14 @@ int kvm_s390_skey_check_enable(struct kvm_vcpu *vcpu)
|
||||
|
||||
trace_kvm_s390_skey_related_inst(vcpu);
|
||||
if (!(sie_block->ictl & (ICTL_ISKE | ICTL_SSKE | ICTL_RRBE)) &&
|
||||
!(atomic_read(&sie_block->cpuflags) & CPUSTAT_KSS))
|
||||
!kvm_s390_test_cpuflags(vcpu, CPUSTAT_KSS))
|
||||
return rc;
|
||||
|
||||
rc = s390_enable_skey();
|
||||
VCPU_EVENT(vcpu, 3, "enabling storage keys for guest: %d", rc);
|
||||
if (!rc) {
|
||||
if (atomic_read(&sie_block->cpuflags) & CPUSTAT_KSS)
|
||||
atomic_andnot(CPUSTAT_KSS, &sie_block->cpuflags);
|
||||
if (kvm_s390_test_cpuflags(vcpu, CPUSTAT_KSS))
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_KSS);
|
||||
else
|
||||
sie_block->ictl &= ~(ICTL_ISKE | ICTL_SSKE |
|
||||
ICTL_RRBE);
|
||||
@ -222,7 +228,6 @@ static int try_handle_skey(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int rc;
|
||||
|
||||
vcpu->stat.instruction_storage_key++;
|
||||
rc = kvm_s390_skey_check_enable(vcpu);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -242,6 +247,8 @@ static int handle_iske(struct kvm_vcpu *vcpu)
|
||||
int reg1, reg2;
|
||||
int rc;
|
||||
|
||||
vcpu->stat.instruction_iske++;
|
||||
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -274,6 +281,8 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
|
||||
int reg1, reg2;
|
||||
int rc;
|
||||
|
||||
vcpu->stat.instruction_rrbe++;
|
||||
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -312,6 +321,8 @@ static int handle_sske(struct kvm_vcpu *vcpu)
|
||||
int reg1, reg2;
|
||||
int rc;
|
||||
|
||||
vcpu->stat.instruction_sske++;
|
||||
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -392,6 +403,8 @@ static int handle_test_block(struct kvm_vcpu *vcpu)
|
||||
gpa_t addr;
|
||||
int reg2;
|
||||
|
||||
vcpu->stat.instruction_tb++;
|
||||
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -424,6 +437,8 @@ static int handle_tpi(struct kvm_vcpu *vcpu)
|
||||
u64 addr;
|
||||
u8 ar;
|
||||
|
||||
vcpu->stat.instruction_tpi++;
|
||||
|
||||
addr = kvm_s390_get_base_disp_s(vcpu, &ar);
|
||||
if (addr & 3)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
@ -484,6 +499,8 @@ static int handle_tsch(struct kvm_vcpu *vcpu)
|
||||
struct kvm_s390_interrupt_info *inti = NULL;
|
||||
const u64 isc_mask = 0xffUL << 24; /* all iscs set */
|
||||
|
||||
vcpu->stat.instruction_tsch++;
|
||||
|
||||
/* a valid schid has at least one bit set */
|
||||
if (vcpu->run->s.regs.gprs[1])
|
||||
inti = kvm_s390_get_io_int(vcpu->kvm, isc_mask,
|
||||
@ -527,6 +544,7 @@ static int handle_io_inst(struct kvm_vcpu *vcpu)
|
||||
if (vcpu->arch.sie_block->ipa == 0xb235)
|
||||
return handle_tsch(vcpu);
|
||||
/* Handle in userspace. */
|
||||
vcpu->stat.instruction_io_other++;
|
||||
return -EOPNOTSUPP;
|
||||
} else {
|
||||
/*
|
||||
@ -592,6 +610,8 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
|
||||
int rc;
|
||||
u8 ar;
|
||||
|
||||
vcpu->stat.instruction_lpsw++;
|
||||
|
||||
if (gpsw->mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -619,6 +639,8 @@ static int handle_lpswe(struct kvm_vcpu *vcpu)
|
||||
int rc;
|
||||
u8 ar;
|
||||
|
||||
vcpu->stat.instruction_lpswe++;
|
||||
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -828,6 +850,8 @@ static int handle_epsw(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int reg1, reg2;
|
||||
|
||||
vcpu->stat.instruction_epsw++;
|
||||
|
||||
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
|
||||
|
||||
/* This basically extracts the mask half of the psw. */
|
||||
@ -1332,6 +1356,8 @@ static int handle_sckpf(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
vcpu->stat.instruction_sckpf++;
|
||||
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
@ -1347,6 +1373,8 @@ static int handle_sckpf(struct kvm_vcpu *vcpu)
|
||||
|
||||
static int handle_ptff(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->stat.instruction_ptff++;
|
||||
|
||||
/* we don't emulate any control instructions yet */
|
||||
kvm_s390_set_psw_cc(vcpu, 3);
|
||||
return 0;
|
||||
|
@ -20,19 +20,18 @@
|
||||
static int __sigp_sense(struct kvm_vcpu *vcpu, struct kvm_vcpu *dst_vcpu,
|
||||
u64 *reg)
|
||||
{
|
||||
int cpuflags;
|
||||
const bool stopped = kvm_s390_test_cpuflags(dst_vcpu, CPUSTAT_STOPPED);
|
||||
int rc;
|
||||
int ext_call_pending;
|
||||
|
||||
cpuflags = atomic_read(&dst_vcpu->arch.sie_block->cpuflags);
|
||||
ext_call_pending = kvm_s390_ext_call_pending(dst_vcpu);
|
||||
if (!(cpuflags & CPUSTAT_STOPPED) && !ext_call_pending)
|
||||
if (!stopped && !ext_call_pending)
|
||||
rc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
else {
|
||||
*reg &= 0xffffffff00000000UL;
|
||||
if (ext_call_pending)
|
||||
*reg |= SIGP_STATUS_EXT_CALL_PENDING;
|
||||
if (cpuflags & CPUSTAT_STOPPED)
|
||||
if (stopped)
|
||||
*reg |= SIGP_STATUS_STOPPED;
|
||||
rc = SIGP_CC_STATUS_STORED;
|
||||
}
|
||||
@ -205,11 +204,9 @@ static int __sigp_store_status_at_addr(struct kvm_vcpu *vcpu,
|
||||
struct kvm_vcpu *dst_vcpu,
|
||||
u32 addr, u64 *reg)
|
||||
{
|
||||
int flags;
|
||||
int rc;
|
||||
|
||||
flags = atomic_read(&dst_vcpu->arch.sie_block->cpuflags);
|
||||
if (!(flags & CPUSTAT_STOPPED)) {
|
||||
if (!kvm_s390_test_cpuflags(dst_vcpu, CPUSTAT_STOPPED)) {
|
||||
*reg &= 0xffffffff00000000UL;
|
||||
*reg |= SIGP_STATUS_INCORRECT_STATE;
|
||||
return SIGP_CC_STATUS_STORED;
|
||||
@ -236,8 +233,7 @@ static int __sigp_sense_running(struct kvm_vcpu *vcpu,
|
||||
return SIGP_CC_STATUS_STORED;
|
||||
}
|
||||
|
||||
if (atomic_read(&dst_vcpu->arch.sie_block->cpuflags) &
|
||||
CPUSTAT_RUNNING) {
|
||||
if (kvm_s390_test_cpuflags(dst_vcpu, CPUSTAT_RUNNING)) {
|
||||
/* running */
|
||||
rc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
} else {
|
||||
|
@ -28,13 +28,23 @@ struct vsie_page {
|
||||
* the same offset as that in struct sie_page!
|
||||
*/
|
||||
struct mcck_volatile_info mcck_info; /* 0x0200 */
|
||||
/* the pinned originial scb */
|
||||
/*
|
||||
* The pinned original scb. Be aware that other VCPUs can modify
|
||||
* it while we read from it. Values that are used for conditions or
|
||||
* are reused conditionally, should be accessed via READ_ONCE.
|
||||
*/
|
||||
struct kvm_s390_sie_block *scb_o; /* 0x0218 */
|
||||
/* the shadow gmap in use by the vsie_page */
|
||||
struct gmap *gmap; /* 0x0220 */
|
||||
/* address of the last reported fault to guest2 */
|
||||
unsigned long fault_addr; /* 0x0228 */
|
||||
__u8 reserved[0x0700 - 0x0230]; /* 0x0230 */
|
||||
/* calculated guest addresses of satellite control blocks */
|
||||
gpa_t sca_gpa; /* 0x0230 */
|
||||
gpa_t itdba_gpa; /* 0x0238 */
|
||||
gpa_t gvrd_gpa; /* 0x0240 */
|
||||
gpa_t riccbd_gpa; /* 0x0248 */
|
||||
gpa_t sdnx_gpa; /* 0x0250 */
|
||||
__u8 reserved[0x0700 - 0x0258]; /* 0x0258 */
|
||||
struct kvm_s390_crypto_cb crycb; /* 0x0700 */
|
||||
__u8 fac[S390_ARCH_FAC_LIST_SIZE_BYTE]; /* 0x0800 */
|
||||
};
|
||||
@ -140,12 +150,13 @@ static int shadow_crycb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
{
|
||||
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
|
||||
struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
|
||||
u32 crycb_addr = scb_o->crycbd & 0x7ffffff8U;
|
||||
const uint32_t crycbd_o = READ_ONCE(scb_o->crycbd);
|
||||
const u32 crycb_addr = crycbd_o & 0x7ffffff8U;
|
||||
unsigned long *b1, *b2;
|
||||
u8 ecb3_flags;
|
||||
|
||||
scb_s->crycbd = 0;
|
||||
if (!(scb_o->crycbd & vcpu->arch.sie_block->crycbd & CRYCB_FORMAT1))
|
||||
if (!(crycbd_o & vcpu->arch.sie_block->crycbd & CRYCB_FORMAT1))
|
||||
return 0;
|
||||
/* format-1 is supported with message-security-assist extension 3 */
|
||||
if (!test_kvm_facility(vcpu->kvm, 76))
|
||||
@ -183,12 +194,15 @@ static void prepare_ibc(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
{
|
||||
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
|
||||
struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
|
||||
/* READ_ONCE does not work on bitfields - use a temporary variable */
|
||||
const uint32_t __new_ibc = scb_o->ibc;
|
||||
const uint32_t new_ibc = READ_ONCE(__new_ibc) & 0x0fffU;
|
||||
__u64 min_ibc = (sclp.ibc >> 16) & 0x0fffU;
|
||||
|
||||
scb_s->ibc = 0;
|
||||
/* ibc installed in g2 and requested for g3 */
|
||||
if (vcpu->kvm->arch.model.ibc && (scb_o->ibc & 0x0fffU)) {
|
||||
scb_s->ibc = scb_o->ibc & 0x0fffU;
|
||||
if (vcpu->kvm->arch.model.ibc && new_ibc) {
|
||||
scb_s->ibc = new_ibc;
|
||||
/* takte care of the minimum ibc level of the machine */
|
||||
if (scb_s->ibc < min_ibc)
|
||||
scb_s->ibc = min_ibc;
|
||||
@ -253,6 +267,10 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
{
|
||||
struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
|
||||
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
|
||||
/* READ_ONCE does not work on bitfields - use a temporary variable */
|
||||
const uint32_t __new_prefix = scb_o->prefix;
|
||||
const uint32_t new_prefix = READ_ONCE(__new_prefix);
|
||||
const bool wants_tx = READ_ONCE(scb_o->ecb) & ECB_TE;
|
||||
bool had_tx = scb_s->ecb & ECB_TE;
|
||||
unsigned long new_mso = 0;
|
||||
int rc;
|
||||
@ -299,14 +317,14 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
scb_s->icpua = scb_o->icpua;
|
||||
|
||||
if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_SM))
|
||||
new_mso = scb_o->mso & 0xfffffffffff00000UL;
|
||||
new_mso = READ_ONCE(scb_o->mso) & 0xfffffffffff00000UL;
|
||||
/* if the hva of the prefix changes, we have to remap the prefix */
|
||||
if (scb_s->mso != new_mso || scb_s->prefix != scb_o->prefix)
|
||||
if (scb_s->mso != new_mso || scb_s->prefix != new_prefix)
|
||||
prefix_unmapped(vsie_page);
|
||||
/* SIE will do mso/msl validity and exception checks for us */
|
||||
scb_s->msl = scb_o->msl & 0xfffffffffff00000UL;
|
||||
scb_s->mso = new_mso;
|
||||
scb_s->prefix = scb_o->prefix;
|
||||
scb_s->prefix = new_prefix;
|
||||
|
||||
/* We have to definetly flush the tlb if this scb never ran */
|
||||
if (scb_s->ihcpu != 0xffffU)
|
||||
@ -318,11 +336,11 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_ESOP))
|
||||
scb_s->ecb |= scb_o->ecb & ECB_HOSTPROTINT;
|
||||
/* transactional execution */
|
||||
if (test_kvm_facility(vcpu->kvm, 73)) {
|
||||
if (test_kvm_facility(vcpu->kvm, 73) && wants_tx) {
|
||||
/* remap the prefix is tx is toggled on */
|
||||
if ((scb_o->ecb & ECB_TE) && !had_tx)
|
||||
if (!had_tx)
|
||||
prefix_unmapped(vsie_page);
|
||||
scb_s->ecb |= scb_o->ecb & ECB_TE;
|
||||
scb_s->ecb |= ECB_TE;
|
||||
}
|
||||
/* SIMD */
|
||||
if (test_kvm_facility(vcpu->kvm, 129)) {
|
||||
@ -463,46 +481,42 @@ static void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa)
|
||||
/* unpin all blocks previously pinned by pin_blocks(), marking them dirty */
|
||||
static void unpin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
{
|
||||
struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
|
||||
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
|
||||
hpa_t hpa;
|
||||
gpa_t gpa;
|
||||
|
||||
hpa = (u64) scb_s->scaoh << 32 | scb_s->scaol;
|
||||
if (hpa) {
|
||||
gpa = scb_o->scaol & ~0xfUL;
|
||||
if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_64BSCAO))
|
||||
gpa |= (u64) scb_o->scaoh << 32;
|
||||
unpin_guest_page(vcpu->kvm, gpa, hpa);
|
||||
unpin_guest_page(vcpu->kvm, vsie_page->sca_gpa, hpa);
|
||||
vsie_page->sca_gpa = 0;
|
||||
scb_s->scaol = 0;
|
||||
scb_s->scaoh = 0;
|
||||
}
|
||||
|
||||
hpa = scb_s->itdba;
|
||||
if (hpa) {
|
||||
gpa = scb_o->itdba & ~0xffUL;
|
||||
unpin_guest_page(vcpu->kvm, gpa, hpa);
|
||||
unpin_guest_page(vcpu->kvm, vsie_page->itdba_gpa, hpa);
|
||||
vsie_page->itdba_gpa = 0;
|
||||
scb_s->itdba = 0;
|
||||
}
|
||||
|
||||
hpa = scb_s->gvrd;
|
||||
if (hpa) {
|
||||
gpa = scb_o->gvrd & ~0x1ffUL;
|
||||
unpin_guest_page(vcpu->kvm, gpa, hpa);
|
||||
unpin_guest_page(vcpu->kvm, vsie_page->gvrd_gpa, hpa);
|
||||
vsie_page->gvrd_gpa = 0;
|
||||
scb_s->gvrd = 0;
|
||||
}
|
||||
|
||||
hpa = scb_s->riccbd;
|
||||
if (hpa) {
|
||||
gpa = scb_o->riccbd & ~0x3fUL;
|
||||
unpin_guest_page(vcpu->kvm, gpa, hpa);
|
||||
unpin_guest_page(vcpu->kvm, vsie_page->riccbd_gpa, hpa);
|
||||
vsie_page->riccbd_gpa = 0;
|
||||
scb_s->riccbd = 0;
|
||||
}
|
||||
|
||||
hpa = scb_s->sdnxo;
|
||||
if (hpa) {
|
||||
gpa = scb_o->sdnxo;
|
||||
unpin_guest_page(vcpu->kvm, gpa, hpa);
|
||||
unpin_guest_page(vcpu->kvm, vsie_page->sdnx_gpa, hpa);
|
||||
vsie_page->sdnx_gpa = 0;
|
||||
scb_s->sdnxo = 0;
|
||||
}
|
||||
}
|
||||
@ -529,9 +543,9 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
gpa_t gpa;
|
||||
int rc = 0;
|
||||
|
||||
gpa = scb_o->scaol & ~0xfUL;
|
||||
gpa = READ_ONCE(scb_o->scaol) & ~0xfUL;
|
||||
if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_64BSCAO))
|
||||
gpa |= (u64) scb_o->scaoh << 32;
|
||||
gpa |= (u64) READ_ONCE(scb_o->scaoh) << 32;
|
||||
if (gpa) {
|
||||
if (!(gpa & ~0x1fffUL))
|
||||
rc = set_validity_icpt(scb_s, 0x0038U);
|
||||
@ -547,11 +561,12 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
}
|
||||
if (rc)
|
||||
goto unpin;
|
||||
vsie_page->sca_gpa = gpa;
|
||||
scb_s->scaoh = (u32)((u64)hpa >> 32);
|
||||
scb_s->scaol = (u32)(u64)hpa;
|
||||
}
|
||||
|
||||
gpa = scb_o->itdba & ~0xffUL;
|
||||
gpa = READ_ONCE(scb_o->itdba) & ~0xffUL;
|
||||
if (gpa && (scb_s->ecb & ECB_TE)) {
|
||||
if (!(gpa & ~0x1fffU)) {
|
||||
rc = set_validity_icpt(scb_s, 0x0080U);
|
||||
@ -563,10 +578,11 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
rc = set_validity_icpt(scb_s, 0x0080U);
|
||||
goto unpin;
|
||||
}
|
||||
vsie_page->itdba_gpa = gpa;
|
||||
scb_s->itdba = hpa;
|
||||
}
|
||||
|
||||
gpa = scb_o->gvrd & ~0x1ffUL;
|
||||
gpa = READ_ONCE(scb_o->gvrd) & ~0x1ffUL;
|
||||
if (gpa && (scb_s->eca & ECA_VX) && !(scb_s->ecd & ECD_HOSTREGMGMT)) {
|
||||
if (!(gpa & ~0x1fffUL)) {
|
||||
rc = set_validity_icpt(scb_s, 0x1310U);
|
||||
@ -581,10 +597,11 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
rc = set_validity_icpt(scb_s, 0x1310U);
|
||||
goto unpin;
|
||||
}
|
||||
vsie_page->gvrd_gpa = gpa;
|
||||
scb_s->gvrd = hpa;
|
||||
}
|
||||
|
||||
gpa = scb_o->riccbd & ~0x3fUL;
|
||||
gpa = READ_ONCE(scb_o->riccbd) & ~0x3fUL;
|
||||
if (gpa && (scb_s->ecb3 & ECB3_RI)) {
|
||||
if (!(gpa & ~0x1fffUL)) {
|
||||
rc = set_validity_icpt(scb_s, 0x0043U);
|
||||
@ -597,13 +614,14 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
goto unpin;
|
||||
}
|
||||
/* Validity 0x0044 will be checked by SIE */
|
||||
vsie_page->riccbd_gpa = gpa;
|
||||
scb_s->riccbd = hpa;
|
||||
}
|
||||
if ((scb_s->ecb & ECB_GS) && !(scb_s->ecd & ECD_HOSTREGMGMT)) {
|
||||
unsigned long sdnxc;
|
||||
|
||||
gpa = scb_o->sdnxo & ~0xfUL;
|
||||
sdnxc = scb_o->sdnxo & 0xfUL;
|
||||
gpa = READ_ONCE(scb_o->sdnxo) & ~0xfUL;
|
||||
sdnxc = READ_ONCE(scb_o->sdnxo) & 0xfUL;
|
||||
if (!gpa || !(gpa & ~0x1fffUL)) {
|
||||
rc = set_validity_icpt(scb_s, 0x10b0U);
|
||||
goto unpin;
|
||||
@ -624,6 +642,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
rc = set_validity_icpt(scb_s, 0x10b0U);
|
||||
goto unpin;
|
||||
}
|
||||
vsie_page->sdnx_gpa = gpa;
|
||||
scb_s->sdnxo = hpa | sdnxc;
|
||||
}
|
||||
return 0;
|
||||
@ -768,7 +787,7 @@ static void retry_vsie_icpt(struct vsie_page *vsie_page)
|
||||
static int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
|
||||
{
|
||||
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
|
||||
__u32 fac = vsie_page->scb_o->fac & 0x7ffffff8U;
|
||||
__u32 fac = READ_ONCE(vsie_page->scb_o->fac) & 0x7ffffff8U;
|
||||
|
||||
if (fac && test_kvm_facility(vcpu->kvm, 7)) {
|
||||
retry_vsie_icpt(vsie_page);
|
||||
@ -894,7 +913,7 @@ static void register_shadow_scb(struct kvm_vcpu *vcpu,
|
||||
* External calls have to lead to a kick of the vcpu and
|
||||
* therefore the vsie -> Simulate Wait state.
|
||||
*/
|
||||
atomic_or(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_WAIT);
|
||||
/*
|
||||
* We have to adjust the g3 epoch by the g2 epoch. The epoch will
|
||||
* automatically be adjusted on tod clock changes via kvm_sync_clock.
|
||||
@ -916,7 +935,7 @@ static void register_shadow_scb(struct kvm_vcpu *vcpu,
|
||||
*/
|
||||
static void unregister_shadow_scb(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
atomic_andnot(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags);
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_WAIT);
|
||||
WRITE_ONCE(vcpu->arch.vsie_block, NULL);
|
||||
}
|
||||
|
||||
|
@ -1021,18 +1021,17 @@ static inline void gmap_insert_rmap(struct gmap *sg, unsigned long vmaddr,
|
||||
}
|
||||
|
||||
/**
|
||||
* gmap_protect_rmap - modify access rights to memory and create an rmap
|
||||
* gmap_protect_rmap - restrict access rights to memory (RO) and create an rmap
|
||||
* @sg: pointer to the shadow guest address space structure
|
||||
* @raddr: rmap address in the shadow gmap
|
||||
* @paddr: address in the parent guest address space
|
||||
* @len: length of the memory area to protect
|
||||
* @prot: indicates access rights: none, read-only or read-write
|
||||
*
|
||||
* Returns 0 if successfully protected and the rmap was created, -ENOMEM
|
||||
* if out of memory and -EFAULT if paddr is invalid.
|
||||
*/
|
||||
static int gmap_protect_rmap(struct gmap *sg, unsigned long raddr,
|
||||
unsigned long paddr, unsigned long len, int prot)
|
||||
unsigned long paddr, unsigned long len)
|
||||
{
|
||||
struct gmap *parent;
|
||||
struct gmap_rmap *rmap;
|
||||
@ -1060,7 +1059,7 @@ static int gmap_protect_rmap(struct gmap *sg, unsigned long raddr,
|
||||
ptep = gmap_pte_op_walk(parent, paddr, &ptl);
|
||||
if (ptep) {
|
||||
spin_lock(&sg->guest_table_lock);
|
||||
rc = ptep_force_prot(parent->mm, paddr, ptep, prot,
|
||||
rc = ptep_force_prot(parent->mm, paddr, ptep, PROT_READ,
|
||||
PGSTE_VSIE_BIT);
|
||||
if (!rc)
|
||||
gmap_insert_rmap(sg, vmaddr, rmap);
|
||||
@ -1070,7 +1069,7 @@ static int gmap_protect_rmap(struct gmap *sg, unsigned long raddr,
|
||||
radix_tree_preload_end();
|
||||
if (rc) {
|
||||
kfree(rmap);
|
||||
rc = gmap_pte_op_fixup(parent, paddr, vmaddr, prot);
|
||||
rc = gmap_pte_op_fixup(parent, paddr, vmaddr, PROT_READ);
|
||||
if (rc)
|
||||
return rc;
|
||||
continue;
|
||||
@ -1609,7 +1608,7 @@ int gmap_shadow_r2t(struct gmap *sg, unsigned long saddr, unsigned long r2t,
|
||||
origin = r2t & _REGION_ENTRY_ORIGIN;
|
||||
offset = ((r2t & _REGION_ENTRY_OFFSET) >> 6) * PAGE_SIZE;
|
||||
len = ((r2t & _REGION_ENTRY_LENGTH) + 1) * PAGE_SIZE - offset;
|
||||
rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ);
|
||||
rc = gmap_protect_rmap(sg, raddr, origin + offset, len);
|
||||
spin_lock(&sg->guest_table_lock);
|
||||
if (!rc) {
|
||||
table = gmap_table_walk(sg, saddr, 4);
|
||||
@ -1692,7 +1691,7 @@ int gmap_shadow_r3t(struct gmap *sg, unsigned long saddr, unsigned long r3t,
|
||||
origin = r3t & _REGION_ENTRY_ORIGIN;
|
||||
offset = ((r3t & _REGION_ENTRY_OFFSET) >> 6) * PAGE_SIZE;
|
||||
len = ((r3t & _REGION_ENTRY_LENGTH) + 1) * PAGE_SIZE - offset;
|
||||
rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ);
|
||||
rc = gmap_protect_rmap(sg, raddr, origin + offset, len);
|
||||
spin_lock(&sg->guest_table_lock);
|
||||
if (!rc) {
|
||||
table = gmap_table_walk(sg, saddr, 3);
|
||||
@ -1776,7 +1775,7 @@ int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt,
|
||||
origin = sgt & _REGION_ENTRY_ORIGIN;
|
||||
offset = ((sgt & _REGION_ENTRY_OFFSET) >> 6) * PAGE_SIZE;
|
||||
len = ((sgt & _REGION_ENTRY_LENGTH) + 1) * PAGE_SIZE - offset;
|
||||
rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ);
|
||||
rc = gmap_protect_rmap(sg, raddr, origin + offset, len);
|
||||
spin_lock(&sg->guest_table_lock);
|
||||
if (!rc) {
|
||||
table = gmap_table_walk(sg, saddr, 2);
|
||||
@ -1895,7 +1894,7 @@ int gmap_shadow_pgt(struct gmap *sg, unsigned long saddr, unsigned long pgt,
|
||||
/* Make pgt read-only in parent gmap page table (not the pgste) */
|
||||
raddr = (saddr & _SEGMENT_MASK) | _SHADOW_RMAP_SEGMENT;
|
||||
origin = pgt & _SEGMENT_ENTRY_ORIGIN & PAGE_MASK;
|
||||
rc = gmap_protect_rmap(sg, raddr, origin, PAGE_SIZE, PROT_READ);
|
||||
rc = gmap_protect_rmap(sg, raddr, origin, PAGE_SIZE);
|
||||
spin_lock(&sg->guest_table_lock);
|
||||
if (!rc) {
|
||||
table = gmap_table_walk(sg, saddr, 1);
|
||||
@ -1998,7 +1997,7 @@ EXPORT_SYMBOL_GPL(gmap_shadow_page);
|
||||
* Called with sg->parent->shadow_lock.
|
||||
*/
|
||||
static void gmap_shadow_notify(struct gmap *sg, unsigned long vmaddr,
|
||||
unsigned long gaddr, pte_t *pte)
|
||||
unsigned long gaddr)
|
||||
{
|
||||
struct gmap_rmap *rmap, *rnext, *head;
|
||||
unsigned long start, end, bits, raddr;
|
||||
@ -2083,7 +2082,7 @@ void ptep_notify(struct mm_struct *mm, unsigned long vmaddr,
|
||||
spin_lock(&gmap->shadow_lock);
|
||||
list_for_each_entry_safe(sg, next,
|
||||
&gmap->children, list)
|
||||
gmap_shadow_notify(sg, vmaddr, gaddr, pte);
|
||||
gmap_shadow_notify(sg, vmaddr, gaddr);
|
||||
spin_unlock(&gmap->shadow_lock);
|
||||
}
|
||||
if (bits & PGSTE_IN_BIT)
|
||||
|
@ -49,7 +49,7 @@ struct read_info_sccb {
|
||||
u8 _pad_112[116 - 112]; /* 112-115 */
|
||||
u8 fac116; /* 116 */
|
||||
u8 fac117; /* 117 */
|
||||
u8 _pad_118; /* 118 */
|
||||
u8 fac118; /* 118 */
|
||||
u8 fac119; /* 119 */
|
||||
u16 hcpua; /* 120-121 */
|
||||
u8 _pad_122[124 - 122]; /* 122-123 */
|
||||
@ -100,6 +100,7 @@ static void __init sclp_early_facilities_detect(struct read_info_sccb *sccb)
|
||||
sclp.has_esca = !!(sccb->fac116 & 0x08);
|
||||
sclp.has_pfmfi = !!(sccb->fac117 & 0x40);
|
||||
sclp.has_ibs = !!(sccb->fac117 & 0x20);
|
||||
sclp.has_gisaf = !!(sccb->fac118 & 0x08);
|
||||
sclp.has_hvs = !!(sccb->fac119 & 0x80);
|
||||
sclp.has_kss = !!(sccb->fac98 & 0x01);
|
||||
if (sccb->fac85 & 0x02)
|
||||
|
Loading…
Reference in New Issue
Block a user