Commit efef127c authored by Paolo Bonzini's avatar Paolo Bonzini

Merge tag 'kvm-s390-next-4.6-1' of...

Merge tag 'kvm-s390-next-4.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD

KVM: s390: Fixes and features for kvm/next (4.6)

1. also provide the floating point registers via sync regs
2. Separate out intruction vs. data accesses
3. Fix program interrupts in some cases
4. Documentation fixes
5. dirty log improvements for huge guests
parents bce87cce 1763f8d0
...@@ -88,6 +88,8 @@ struct kvm_s390_io_adapter_req { ...@@ -88,6 +88,8 @@ struct kvm_s390_io_adapter_req {
perform a gmap translation for the guest address provided in addr, perform a gmap translation for the guest address provided in addr,
pin a userspace page for the translated address and add it to the pin a userspace page for the translated address and add it to the
list of mappings list of mappings
Note: A new mapping will be created unconditionally; therefore,
the calling code should avoid making duplicate mappings.
KVM_S390_IO_ADAPTER_UNMAP KVM_S390_IO_ADAPTER_UNMAP
release a userspace page for the translated address specified in addr release a userspace page for the translated address specified in addr
......
...@@ -84,3 +84,55 @@ Returns: -EBUSY in case 1 or more vcpus are already activated (only in write ...@@ -84,3 +84,55 @@ Returns: -EBUSY in case 1 or more vcpus are already activated (only in write
-EFAULT if the given address is not accessible from kernel space -EFAULT if the given address is not accessible from kernel space
-ENOMEM if not enough memory is available to process the ioctl -ENOMEM if not enough memory is available to process the ioctl
0 in case of success 0 in case of success
3. GROUP: KVM_S390_VM_TOD
Architectures: s390
3.1. ATTRIBUTE: KVM_S390_VM_TOD_HIGH
Allows user space to set/get the TOD clock extension (u8).
Parameters: address of a buffer in user space to store the data (u8) to
Returns: -EFAULT if the given address is not accessible from kernel space
-EINVAL if setting the TOD clock extension to != 0 is not supported
3.2. ATTRIBUTE: KVM_S390_VM_TOD_LOW
Allows user space to set/get bits 0-63 of the TOD clock register as defined in
the POP (u64).
Parameters: address of a buffer in user space to store the data (u64) to
Returns: -EFAULT if the given address is not accessible from kernel space
4. GROUP: KVM_S390_VM_CRYPTO
Architectures: s390
4.1. ATTRIBUTE: KVM_S390_VM_CRYPTO_ENABLE_AES_KW (w/o)
Allows user space to enable aes key wrapping, including generating a new
wrapping key.
Parameters: none
Returns: 0
4.2. ATTRIBUTE: KVM_S390_VM_CRYPTO_ENABLE_DEA_KW (w/o)
Allows user space to enable dea key wrapping, including generating a new
wrapping key.
Parameters: none
Returns: 0
4.3. ATTRIBUTE: KVM_S390_VM_CRYPTO_DISABLE_AES_KW (w/o)
Allows user space to disable aes key wrapping, clearing the wrapping key.
Parameters: none
Returns: 0
4.4. ATTRIBUTE: KVM_S390_VM_CRYPTO_DISABLE_DEA_KW (w/o)
Allows user space to disable dea key wrapping, clearing the wrapping key.
Parameters: none
Returns: 0
...@@ -229,17 +229,11 @@ struct kvm_s390_itdb { ...@@ -229,17 +229,11 @@ struct kvm_s390_itdb {
__u8 data[256]; __u8 data[256];
} __packed; } __packed;
struct kvm_s390_vregs {
__vector128 vrs[32];
__u8 reserved200[512]; /* for future vector expansion */
} __packed;
struct sie_page { struct sie_page {
struct kvm_s390_sie_block sie_block; struct kvm_s390_sie_block sie_block;
__u8 reserved200[1024]; /* 0x0200 */ __u8 reserved200[1024]; /* 0x0200 */
struct kvm_s390_itdb itdb; /* 0x0600 */ struct kvm_s390_itdb itdb; /* 0x0600 */
__u8 reserved700[1280]; /* 0x0700 */ __u8 reserved700[2304]; /* 0x0700 */
struct kvm_s390_vregs vregs; /* 0x0c00 */
} __packed; } __packed;
struct kvm_vcpu_stat { struct kvm_vcpu_stat {
......
...@@ -154,6 +154,7 @@ struct kvm_guest_debug_arch { ...@@ -154,6 +154,7 @@ struct kvm_guest_debug_arch {
#define KVM_SYNC_PFAULT (1UL << 5) #define KVM_SYNC_PFAULT (1UL << 5)
#define KVM_SYNC_VRS (1UL << 6) #define KVM_SYNC_VRS (1UL << 6)
#define KVM_SYNC_RICCB (1UL << 7) #define KVM_SYNC_RICCB (1UL << 7)
#define KVM_SYNC_FPRS (1UL << 8)
/* definition of registers in kvm_run */ /* definition of registers in kvm_run */
struct kvm_sync_regs { struct kvm_sync_regs {
__u64 prefix; /* prefix register */ __u64 prefix; /* prefix register */
...@@ -168,9 +169,12 @@ struct kvm_sync_regs { ...@@ -168,9 +169,12 @@ struct kvm_sync_regs {
__u64 pft; /* pfault token [PFAULT] */ __u64 pft; /* pfault token [PFAULT] */
__u64 pfs; /* pfault select [PFAULT] */ __u64 pfs; /* pfault select [PFAULT] */
__u64 pfc; /* pfault compare [PFAULT] */ __u64 pfc; /* pfault compare [PFAULT] */
__u64 vrs[32][2]; /* vector registers */ union {
__u64 vrs[32][2]; /* vector registers (KVM_SYNC_VRS) */
__u64 fprs[16]; /* fp registers (KVM_SYNC_FPRS) */
};
__u8 reserved[512]; /* for future vector expansion */ __u8 reserved[512]; /* for future vector expansion */
__u32 fpc; /* only valid with vector registers */ __u32 fpc; /* valid on KVM_SYNC_VRS or KVM_SYNC_FPRS */
__u8 padding[52]; /* riccb needs to be 64byte aligned */ __u8 padding[52]; /* riccb needs to be 64byte aligned */
__u8 riccb[64]; /* runtime instrumentation controls block */ __u8 riccb[64]; /* runtime instrumentation controls block */
}; };
......
...@@ -373,7 +373,7 @@ void ipte_unlock(struct kvm_vcpu *vcpu) ...@@ -373,7 +373,7 @@ void ipte_unlock(struct kvm_vcpu *vcpu)
} }
static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, ar_t ar, static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, ar_t ar,
int write) enum gacc_mode mode)
{ {
union alet alet; union alet alet;
struct ale ale; struct ale ale;
...@@ -454,7 +454,7 @@ static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, ar_t ar, ...@@ -454,7 +454,7 @@ static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, ar_t ar,
} }
} }
if (ale.fo == 1 && write) if (ale.fo == 1 && mode == GACC_STORE)
return PGM_PROTECTION; return PGM_PROTECTION;
asce->val = aste.asce; asce->val = aste.asce;
...@@ -477,25 +477,28 @@ enum { ...@@ -477,25 +477,28 @@ enum {
}; };
static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce, static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
ar_t ar, int write) ar_t ar, enum gacc_mode mode)
{ {
int rc; int rc;
psw_t *psw = &vcpu->arch.sie_block->gpsw; struct psw_bits psw = psw_bits(vcpu->arch.sie_block->gpsw);
struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm; struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
struct trans_exc_code_bits *tec_bits; struct trans_exc_code_bits *tec_bits;
memset(pgm, 0, sizeof(*pgm)); memset(pgm, 0, sizeof(*pgm));
tec_bits = (struct trans_exc_code_bits *)&pgm->trans_exc_code; tec_bits = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
tec_bits->fsi = write ? FSI_STORE : FSI_FETCH; tec_bits->fsi = mode == GACC_STORE ? FSI_STORE : FSI_FETCH;
tec_bits->as = psw_bits(*psw).as; tec_bits->as = psw.as;
if (!psw_bits(*psw).t) { if (!psw.t) {
asce->val = 0; asce->val = 0;
asce->r = 1; asce->r = 1;
return 0; return 0;
} }
switch (psw_bits(vcpu->arch.sie_block->gpsw).as) { if (mode == GACC_IFETCH)
psw.as = psw.as == PSW_AS_HOME ? PSW_AS_HOME : PSW_AS_PRIMARY;
switch (psw.as) {
case PSW_AS_PRIMARY: case PSW_AS_PRIMARY:
asce->val = vcpu->arch.sie_block->gcr[1]; asce->val = vcpu->arch.sie_block->gcr[1];
return 0; return 0;
...@@ -506,7 +509,7 @@ static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce, ...@@ -506,7 +509,7 @@ static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
asce->val = vcpu->arch.sie_block->gcr[13]; asce->val = vcpu->arch.sie_block->gcr[13];
return 0; return 0;
case PSW_AS_ACCREG: case PSW_AS_ACCREG:
rc = ar_translation(vcpu, asce, ar, write); rc = ar_translation(vcpu, asce, ar, mode);
switch (rc) { switch (rc) {
case PGM_ALEN_TRANSLATION: case PGM_ALEN_TRANSLATION:
case PGM_ALE_SEQUENCE: case PGM_ALE_SEQUENCE:
...@@ -538,7 +541,7 @@ static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val) ...@@ -538,7 +541,7 @@ static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
* @gva: guest virtual address * @gva: guest virtual address
* @gpa: points to where guest physical (absolute) address should be stored * @gpa: points to where guest physical (absolute) address should be stored
* @asce: effective asce * @asce: effective asce
* @write: indicates if access is a write access * @mode: indicates the access mode to be used
* *
* Translate a guest virtual address into a guest absolute address by means * Translate a guest virtual address into a guest absolute address by means
* of dynamic address translation as specified by the architecture. * of dynamic address translation as specified by the architecture.
...@@ -554,7 +557,7 @@ static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val) ...@@ -554,7 +557,7 @@ static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
*/ */
static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva, static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
unsigned long *gpa, const union asce asce, unsigned long *gpa, const union asce asce,
int write) enum gacc_mode mode)
{ {
union vaddress vaddr = {.addr = gva}; union vaddress vaddr = {.addr = gva};
union raddress raddr = {.addr = gva}; union raddress raddr = {.addr = gva};
...@@ -699,7 +702,7 @@ static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva, ...@@ -699,7 +702,7 @@ static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
real_address: real_address:
raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr); raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr);
absolute_address: absolute_address:
if (write && dat_protection) if (mode == GACC_STORE && dat_protection)
return PGM_PROTECTION; return PGM_PROTECTION;
if (kvm_is_error_gpa(vcpu->kvm, raddr.addr)) if (kvm_is_error_gpa(vcpu->kvm, raddr.addr))
return PGM_ADDRESSING; return PGM_ADDRESSING;
...@@ -728,7 +731,7 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu, ...@@ -728,7 +731,7 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu,
static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga, static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga,
unsigned long *pages, unsigned long nr_pages, unsigned long *pages, unsigned long nr_pages,
const union asce asce, int write) const union asce asce, enum gacc_mode mode)
{ {
struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm; struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
psw_t *psw = &vcpu->arch.sie_block->gpsw; psw_t *psw = &vcpu->arch.sie_block->gpsw;
...@@ -740,13 +743,13 @@ static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga, ...@@ -740,13 +743,13 @@ static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga,
while (nr_pages) { while (nr_pages) {
ga = kvm_s390_logical_to_effective(vcpu, ga); ga = kvm_s390_logical_to_effective(vcpu, ga);
tec_bits->addr = ga >> PAGE_SHIFT; tec_bits->addr = ga >> PAGE_SHIFT;
if (write && lap_enabled && is_low_address(ga)) { if (mode == GACC_STORE && lap_enabled && is_low_address(ga)) {
pgm->code = PGM_PROTECTION; pgm->code = PGM_PROTECTION;
return pgm->code; return pgm->code;
} }
ga &= PAGE_MASK; ga &= PAGE_MASK;
if (psw_bits(*psw).t) { if (psw_bits(*psw).t) {
rc = guest_translate(vcpu, ga, pages, asce, write); rc = guest_translate(vcpu, ga, pages, asce, mode);
if (rc < 0) if (rc < 0)
return rc; return rc;
if (rc == PGM_PROTECTION) if (rc == PGM_PROTECTION)
...@@ -768,7 +771,7 @@ static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga, ...@@ -768,7 +771,7 @@ static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga,
} }
int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
unsigned long len, int write) unsigned long len, enum gacc_mode mode)
{ {
psw_t *psw = &vcpu->arch.sie_block->gpsw; psw_t *psw = &vcpu->arch.sie_block->gpsw;
unsigned long _len, nr_pages, gpa, idx; unsigned long _len, nr_pages, gpa, idx;
...@@ -780,7 +783,7 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, ...@@ -780,7 +783,7 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
if (!len) if (!len)
return 0; return 0;
rc = get_vcpu_asce(vcpu, &asce, ar, write); rc = get_vcpu_asce(vcpu, &asce, ar, mode);
if (rc) if (rc)
return rc; return rc;
nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1; nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1;
...@@ -792,11 +795,11 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, ...@@ -792,11 +795,11 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
need_ipte_lock = psw_bits(*psw).t && !asce.r; need_ipte_lock = psw_bits(*psw).t && !asce.r;
if (need_ipte_lock) if (need_ipte_lock)
ipte_lock(vcpu); ipte_lock(vcpu);
rc = guest_page_range(vcpu, ga, pages, nr_pages, asce, write); rc = guest_page_range(vcpu, ga, pages, nr_pages, asce, mode);
for (idx = 0; idx < nr_pages && !rc; idx++) { for (idx = 0; idx < nr_pages && !rc; idx++) {
gpa = *(pages + idx) + (ga & ~PAGE_MASK); gpa = *(pages + idx) + (ga & ~PAGE_MASK);
_len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len); _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
if (write) if (mode == GACC_STORE)
rc = kvm_write_guest(vcpu->kvm, gpa, data, _len); rc = kvm_write_guest(vcpu->kvm, gpa, data, _len);
else else
rc = kvm_read_guest(vcpu->kvm, gpa, data, _len); rc = kvm_read_guest(vcpu->kvm, gpa, data, _len);
...@@ -812,7 +815,7 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, ...@@ -812,7 +815,7 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
} }
int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
void *data, unsigned long len, int write) void *data, unsigned long len, enum gacc_mode mode)
{ {
unsigned long _len, gpa; unsigned long _len, gpa;
int rc = 0; int rc = 0;
...@@ -820,7 +823,7 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, ...@@ -820,7 +823,7 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
while (len && !rc) { while (len && !rc) {
gpa = kvm_s390_real_to_abs(vcpu, gra); gpa = kvm_s390_real_to_abs(vcpu, gra);
_len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len); _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
if (write) if (mode)
rc = write_guest_abs(vcpu, gpa, data, _len); rc = write_guest_abs(vcpu, gpa, data, _len);
else else
rc = read_guest_abs(vcpu, gpa, data, _len); rc = read_guest_abs(vcpu, gpa, data, _len);
...@@ -841,7 +844,7 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, ...@@ -841,7 +844,7 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
* has to take care of this. * has to take care of this.
*/ */
int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
unsigned long *gpa, int write) unsigned long *gpa, enum gacc_mode mode)
{ {
struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm; struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
psw_t *psw = &vcpu->arch.sie_block->gpsw; psw_t *psw = &vcpu->arch.sie_block->gpsw;
...@@ -851,19 +854,19 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, ...@@ -851,19 +854,19 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
gva = kvm_s390_logical_to_effective(vcpu, gva); gva = kvm_s390_logical_to_effective(vcpu, gva);
tec = (struct trans_exc_code_bits *)&pgm->trans_exc_code; tec = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
rc = get_vcpu_asce(vcpu, &asce, ar, write); rc = get_vcpu_asce(vcpu, &asce, ar, mode);
tec->addr = gva >> PAGE_SHIFT; tec->addr = gva >> PAGE_SHIFT;
if (rc) if (rc)
return rc; return rc;
if (is_low_address(gva) && low_address_protection_enabled(vcpu, asce)) { if (is_low_address(gva) && low_address_protection_enabled(vcpu, asce)) {
if (write) { if (mode == GACC_STORE) {
rc = pgm->code = PGM_PROTECTION; rc = pgm->code = PGM_PROTECTION;
return rc; return rc;
} }
} }
if (psw_bits(*psw).t && !asce.r) { /* Use DAT? */ if (psw_bits(*psw).t && !asce.r) { /* Use DAT? */
rc = guest_translate(vcpu, gva, gpa, asce, write); rc = guest_translate(vcpu, gva, gpa, asce, mode);
if (rc > 0) { if (rc > 0) {
if (rc == PGM_PROTECTION) if (rc == PGM_PROTECTION)
tec->b61 = 1; tec->b61 = 1;
...@@ -883,7 +886,7 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, ...@@ -883,7 +886,7 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
* check_gva_range - test a range of guest virtual addresses for accessibility * check_gva_range - test a range of guest virtual addresses for accessibility
*/ */
int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
unsigned long length, int is_write) unsigned long length, enum gacc_mode mode)
{ {
unsigned long gpa; unsigned long gpa;
unsigned long currlen; unsigned long currlen;
...@@ -892,7 +895,7 @@ int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, ...@@ -892,7 +895,7 @@ int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
ipte_lock(vcpu); ipte_lock(vcpu);
while (length > 0 && !rc) { while (length > 0 && !rc) {
currlen = min(length, PAGE_SIZE - (gva % PAGE_SIZE)); currlen = min(length, PAGE_SIZE - (gva % PAGE_SIZE));
rc = guest_translate_address(vcpu, gva, ar, &gpa, is_write); rc = guest_translate_address(vcpu, gva, ar, &gpa, mode);
gva += currlen; gva += currlen;
length -= currlen; length -= currlen;
} }
......
...@@ -155,16 +155,22 @@ int read_guest_lc(struct kvm_vcpu *vcpu, unsigned long gra, void *data, ...@@ -155,16 +155,22 @@ int read_guest_lc(struct kvm_vcpu *vcpu, unsigned long gra, void *data,
return kvm_read_guest(vcpu->kvm, gpa, data, len); return kvm_read_guest(vcpu->kvm, gpa, data, len);
} }
enum gacc_mode {
GACC_FETCH,
GACC_STORE,
GACC_IFETCH,
};
int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva,
ar_t ar, unsigned long *gpa, int write); ar_t ar, unsigned long *gpa, enum gacc_mode mode);
int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
unsigned long length, int is_write); unsigned long length, enum gacc_mode mode);
int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
unsigned long len, int write); unsigned long len, enum gacc_mode mode);
int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
void *data, unsigned long len, int write); void *data, unsigned long len, enum gacc_mode mode);
/** /**
* write_guest - copy data from kernel space to guest space * write_guest - copy data from kernel space to guest space
...@@ -215,7 +221,7 @@ static inline __must_check ...@@ -215,7 +221,7 @@ static inline __must_check
int write_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, int write_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
unsigned long len) unsigned long len)
{ {
return access_guest(vcpu, ga, ar, data, len, 1); return access_guest(vcpu, ga, ar, data, len, GACC_STORE);
} }
/** /**
...@@ -235,7 +241,27 @@ static inline __must_check ...@@ -235,7 +241,27 @@ static inline __must_check
int read_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, int read_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
unsigned long len) unsigned long len)
{ {
return access_guest(vcpu, ga, ar, data, len, 0); return access_guest(vcpu, ga, ar, data, len, GACC_FETCH);
}
/**
* read_guest_instr - copy instruction data from guest space to kernel space
* @vcpu: virtual cpu
* @data: destination address in kernel space
* @len: number of bytes to copy
*
* Copy @len bytes from the current psw address (guest space) to @data (kernel
* space).
*
* The behaviour of read_guest_instr is identical to read_guest, except that
* instruction data will be read from primary space when in home-space or
* address-space mode.
*/
static inline __must_check
int read_guest_instr(struct kvm_vcpu *vcpu, void *data, unsigned long len)
{
return access_guest(vcpu, vcpu->arch.sie_block->gpsw.addr, 0, data, len,
GACC_IFETCH);
} }
/** /**
......
...@@ -38,17 +38,32 @@ static const intercept_handler_t instruction_handlers[256] = { ...@@ -38,17 +38,32 @@ static const intercept_handler_t instruction_handlers[256] = {
[0xeb] = kvm_s390_handle_eb, [0xeb] = kvm_s390_handle_eb,
}; };
void kvm_s390_rewind_psw(struct kvm_vcpu *vcpu, int ilc) u8 kvm_s390_get_ilen(struct kvm_vcpu *vcpu)
{ {
struct kvm_s390_sie_block *sie_block = vcpu->arch.sie_block; struct kvm_s390_sie_block *sie_block = vcpu->arch.sie_block;
u8 ilen = 0;
/* Use the length of the EXECUTE instruction if necessary */ switch (vcpu->arch.sie_block->icptcode) {
if (sie_block->icptstatus & 1) { case ICPT_INST:
ilc = (sie_block->icptstatus >> 4) & 0x6; case ICPT_INSTPROGI:
if (!ilc) case ICPT_OPEREXC:
ilc = 4; case ICPT_PARTEXEC:
case ICPT_IOINST:
/* instruction only stored for these icptcodes */
ilen = insn_length(vcpu->arch.sie_block->ipa >> 8);
/* Use the length of the EXECUTE instruction if necessary */
if (sie_block->icptstatus & 1) {
ilen = (sie_block->icptstatus >> 4) & 0x6;
if (!ilen)
ilen = 4;
}
break;
case ICPT_PROGI:
/* bit 1+2 of pgmilc are the ilc, so we directly get ilen */
ilen = vcpu->arch.sie_block->pgmilc & 0x6;
break;
} }
sie_block->gpsw.addr = __rewind_psw(sie_block->gpsw, ilc); return ilen;
} }
static int handle_noop(struct kvm_vcpu *vcpu) static int handle_noop(struct kvm_vcpu *vcpu)
...@@ -121,11 +136,13 @@ static int handle_instruction(struct kvm_vcpu *vcpu) ...@@ -121,11 +136,13 @@ static int handle_instruction(struct kvm_vcpu *vcpu)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static void __extract_prog_irq(struct kvm_vcpu *vcpu, static int inject_prog_on_prog_intercept(struct kvm_vcpu *vcpu)
struct kvm_s390_pgm_info *pgm_info)
{ {
memset(pgm_info, 0, sizeof(struct kvm_s390_pgm_info)); struct kvm_s390_pgm_info pgm_info = {
pgm_info->code = vcpu->arch.sie_block->iprcc; .code = vcpu->arch.sie_block->iprcc,
/* the PSW has already been rewound */
.flags = KVM_S390_PGM_FLAGS_NO_REWIND,
};
switch (vcpu->arch.sie_block->iprcc & ~PGM_PER) { switch (vcpu->arch.sie_block->iprcc & ~PGM_PER) {
case PGM_AFX_TRANSLATION: case PGM_AFX_TRANSLATION:
...@@ -138,7 +155,7 @@ static void __extract_prog_irq(struct kvm_vcpu *vcpu, ...@@ -138,7 +155,7 @@ static void __extract_prog_irq(struct kvm_vcpu *vcpu,
case PGM_PRIMARY_AUTHORITY: case PGM_PRIMARY_AUTHORITY:
case PGM_SECONDARY_AUTHORITY: case PGM_SECONDARY_AUTHORITY:
case PGM_SPACE_SWITCH: case PGM_SPACE_SWITCH:
pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc; pgm_info.trans_exc_code = vcpu->arch.sie_block->tecmc;
break; break;
case PGM_ALEN_TRANSLATION: case PGM_ALEN_TRANSLATION:
case PGM_ALE_SEQUENCE: case PGM_ALE_SEQUENCE:
...@@ -146,7 +163,7 @@ static void __extract_prog_irq(struct kvm_vcpu *vcpu, ...@@ -146,7 +163,7 @@ static void __extract_prog_irq(struct kvm_vcpu *vcpu,
case PGM_ASTE_SEQUENCE: case PGM_ASTE_SEQUENCE:
case PGM_ASTE_VALIDITY: case PGM_ASTE_VALIDITY:
case PGM_EXTENDED_AUTHORITY: case PGM_EXTENDED_AUTHORITY:
pgm_info->exc_access_id = vcpu->arch.sie_block->eai; pgm_info.exc_access_id = vcpu->arch.sie_block->eai;
break; break;
case PGM_ASCE_TYPE: case PGM_ASCE_TYPE:
case PGM_PAGE_TRANSLATION: case PGM_PAGE_TRANSLATION:
...@@ -154,32 +171,33 @@ static void __extract_prog_irq(struct kvm_vcpu *vcpu, ...@@ -154,32 +171,33 @@ static void __extract_prog_irq(struct kvm_vcpu *vcpu,
case PGM_REGION_SECOND_TRANS: case PGM_REGION_SECOND_TRANS:
case PGM_REGION_THIRD_TRANS: case PGM_REGION_THIRD_TRANS:
case PGM_SEGMENT_TRANSLATION: case PGM_SEGMENT_TRANSLATION:
pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc; pgm_info.trans_exc_code = vcpu->arch.sie_block->tecmc;
pgm_info->exc_access_id = vcpu->arch.sie_block->eai; pgm_info.exc_access_id = vcpu->arch.sie_block->eai;
pgm_info->op_access_id = vcpu->arch.sie_block->oai; pgm_info.op_access_id = vcpu->arch.sie_block->oai;
break; break;
case PGM_MONITOR: case PGM_MONITOR:
pgm_info->mon_class_nr = vcpu->arch.sie_block->mcn; pgm_info.mon_class_nr = vcpu->arch.sie_block->mcn;
pgm_info->mon_code = vcpu->arch.sie_block->tecmc; pgm_info.mon_code = vcpu->arch.sie_block->tecmc;
break; break;
case PGM_VECTOR_PROCESSING: case PGM_VECTOR_PROCESSING:
case PGM_DATA: case PGM_DATA:
pgm_info->data_exc_code = vcpu->arch.sie_block->dxc; pgm_info.data_exc_code = vcpu->arch.sie_block->dxc;
break; break;
case PGM_PROTECTION: case PGM_PROTECTION:
pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc; pgm_info.trans_exc_code = vcpu->arch.sie_block->tecmc;
pgm_info->exc_access_id = vcpu->arch.sie_block->eai; pgm_info.exc_access_id = vcpu->arch.sie_block->eai;
break; break;
default: default:
break; break;
} }
if (vcpu->arch.sie_block->iprcc & PGM_PER) { if (vcpu->arch.sie_block->iprcc & PGM_PER) {
pgm_info->per_code = vcpu->arch.sie_block->perc; pgm_info.per_code = vcpu->arch.sie_block->perc;
pgm_info->per_atmid = vcpu->arch.sie_block->peratmid; pgm_info.per_atmid = vcpu->arch.sie_block->peratmid;
pgm_info->per_address = vcpu->arch.sie_block->peraddr; pgm_info.per_address = vcpu->arch.sie_block->peraddr;
pgm_info->per_access_id = vcpu->arch.sie_block->peraid; pgm_info.per_access_id = vcpu->arch.sie_block->peraid;
} }
return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
} }
/* /*
...@@ -208,7 +226,6 @@ static int handle_itdb(struct kvm_vcpu *vcpu) ...@@ -208,7 +226,6 @@ static int handle_itdb(struct kvm_vcpu *vcpu)
static int handle_prog(struct kvm_vcpu *vcpu) static int handle_prog(struct kvm_vcpu *vcpu)
{ {
struct kvm_s390_pgm_info pgm_info;
psw_t psw; psw_t psw;
int rc; int rc;
...@@ -234,8 +251,7 @@ static int handle_prog(struct kvm_vcpu *vcpu) ...@@ -234,8 +251,7 @@ static int handle_prog(struct kvm_vcpu *vcpu)
if (rc) if (rc)
return rc; return rc;
__extract_prog_irq(vcpu, &pgm_info); return inject_prog_on_prog_intercept(vcpu);
return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
} }
/** /**
...@@ -302,7 +318,7 @@ static int handle_mvpg_pei(struct kvm_vcpu *vcpu) ...@@ -302,7 +318,7 @@ static int handle_mvpg_pei(struct kvm_vcpu *vcpu)
/* Make sure that the source is paged-in */ /* Make sure that the source is paged-in */
rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg2], rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg2],
reg2, &srcaddr, 0); reg2, &srcaddr, GACC_FETCH);
if (rc) if (rc)
return kvm_s390_inject_prog_cond(vcpu, rc); return kvm_s390_inject_prog_cond(vcpu, rc);
rc = kvm_arch_fault_in_page(vcpu, srcaddr, 0); rc = kvm_arch_fault_in_page(vcpu, srcaddr, 0);
...@@ -311,14 +327,14 @@ static int handle_mvpg_pei(struct kvm_vcpu *vcpu) ...@@ -311,14 +327,14 @@ static int handle_mvpg_pei(struct kvm_vcpu *vcpu)
/* Make sure that the destination is paged-in */ /* Make sure that the destination is paged-in */
rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg1], rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg1],
reg1, &dstaddr, 1); reg1, &dstaddr, GACC_STORE);
if (rc) if (rc)
return kvm_s390_inject_prog_cond(vcpu, rc); return kvm_s390_inject_prog_cond(vcpu, rc);
rc = kvm_arch_fault_in_page(vcpu, dstaddr, 1); rc = kvm_arch_fault_in_page(vcpu, dstaddr, 1);
if (rc != 0) if (rc != 0)
return rc; return rc;
kvm_s390_rewind_psw(vcpu, 4); kvm_s390_retry_instr(vcpu);
return 0; return 0;
} }
......
...@@ -335,23 +335,6 @@ static void set_intercept_indicators(struct kvm_vcpu *vcpu) ...@@ -335,23 +335,6 @@ static void set_intercept_indicators(struct kvm_vcpu *vcpu)
set_intercept_indicators_stop(vcpu); set_intercept_indicators_stop(vcpu);
} }
static u16 get_ilc(struct kvm_vcpu *vcpu)
{
switch (vcpu->arch.sie_block->icptcode) {
case ICPT_INST:
case ICPT_INSTPROGI:
case ICPT_OPEREXC:
case ICPT_PARTEXEC:
case ICPT_IOINST:
/* last instruction only stored for these icptcodes */
return insn_length(vcpu->arch.sie_block->ipa >> 8);
case ICPT_PROGI:
return vcpu->arch.sie_block->pgmilc;
default:
return 0;
}
}
static int __must_check __deliver_cpu_timer(struct kvm_vcpu *vcpu) static int __must_check __deliver_cpu_timer(struct kvm_vcpu *vcpu)
{ {
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
...@@ -588,7 +571,7 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu) ...@@ -588,7 +571,7 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu)
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
struct kvm_s390_pgm_info pgm_info; struct kvm_s390_pgm_info pgm_info;
int rc = 0, nullifying = false; int rc = 0, nullifying = false;
u16 ilc = get_ilc(vcpu); u16 ilen;
spin_lock(&li->lock); spin_lock(&li->lock);
pgm_info = li->irq.pgm; pgm_info = li->irq.pgm;
...@@ -596,8 +579,9 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu) ...@@ -596,8 +579,9 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu)
memset(&li->irq.pgm, 0, sizeof(pgm_info)); memset(&li->irq.pgm, 0, sizeof(pgm_info));
spin_unlock(&li->lock); spin_unlock(&li->lock);
VCPU_EVENT(vcpu, 3, "deliver: program irq code 0x%x, ilc:%d", ilen = pgm_info.flags & KVM_S390_PGM_FLAGS_ILC_MASK;
pgm_info.code, ilc); VCPU_EVENT(vcpu, 3, "deliver: program irq code 0x%x, ilen:%d",
pgm_info.code, ilen);
vcpu->stat.deliver_program_int++; vcpu->stat.deliver_program_int++;
trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_PROGRAM_INT,
pgm_info.code, 0); pgm_info.code, 0);
...@@ -681,10 +665,11 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu) ...@@ -681,10 +665,11 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu)
(u8 *) __LC_PER_ACCESS_ID); (u8 *) __LC_PER_ACCESS_ID);
} }
if (nullifying && vcpu->arch.sie_block->icptcode == ICPT_INST) if (nullifying && !(pgm_info.flags & KVM_S390_PGM_FLAGS_NO_REWIND))
kvm_s390_rewind_psw(vcpu, ilc); kvm_s390_rewind_psw(vcpu, ilen);
rc |= put_guest_lc(vcpu, ilc, (u16 *) __LC_PGM_ILC); /* bit 1+2 of the target are the ilc, so we can directly use ilen */
rc |= put_guest_lc(vcpu, ilen, (u16 *) __LC_PGM_ILC);
rc |= put_guest_lc(vcpu, vcpu->arch.sie_block->gbea, rc |= put_guest_lc(vcpu, vcpu->arch.sie_block->gbea,
(u64 *) __LC_LAST_BREAK); (u64 *) __LC_LAST_BREAK);
rc |= put_guest_lc(vcpu, pgm_info.code, rc |= put_guest_lc(vcpu, pgm_info.code,
...@@ -1059,8 +1044,16 @@ static int __inject_prog(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) ...@@ -1059,8 +1044,16 @@ static int __inject_prog(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT,
irq->u.pgm.code, 0); irq->u.pgm.code, 0);
if (!(irq->u.pgm.flags & KVM_S390_PGM_FLAGS_ILC_VALID)) {
/* auto detection if no valid ILC was given */
irq->u.pgm.flags &= ~KVM_S390_PGM_FLAGS_ILC_MASK;
irq->u.pgm.flags |= kvm_s390_get_ilen(vcpu);
irq->u.pgm.flags |= KVM_S390_PGM_FLAGS_ILC_VALID;
}
if (irq->u.pgm.code == PGM_PER) { if (irq->u.pgm.code == PGM_PER) {
li->irq.pgm.code |= PGM_PER; li->irq.pgm.code |= PGM_PER;
li->irq.pgm.flags = irq->u.pgm.flags;
/* only modify PER related information */ /* only modify PER related information */
li->irq.pgm.per_address = irq->u.pgm.per_address; li->irq.pgm.per_address = irq->u.pgm.per_address;
li->irq.pgm.per_code = irq->u.pgm.per_code; li->irq.pgm.per_code = irq->u.pgm.per_code;
...@@ -1069,6 +1062,7 @@ static int __inject_prog(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) ...@@ -1069,6 +1062,7 @@ static int __inject_prog(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
} else if (!(irq->u.pgm.code & PGM_PER)) { } else if (!(irq->u.pgm.code & PGM_PER)) {
li->irq.pgm.code = (li->irq.pgm.code & PGM_PER) | li->irq.pgm.code = (li->irq.pgm.code & PGM_PER) |
irq->u.pgm.code; irq->u.pgm.code;
li->irq.pgm.flags = irq->u.pgm.flags;
/* only modify non-PER information */ /* only modify non-PER information */
li->irq.pgm.trans_exc_code = irq->u.pgm.trans_exc_code; li->irq.pgm.trans_exc_code = irq->u.pgm.trans_exc_code;
li->irq.pgm.mon_code = irq->u.pgm.mon_code; li->irq.pgm.mon_code = irq->u.pgm.mon_code;
......
...@@ -274,7 +274,6 @@ static void kvm_s390_sync_dirty_log(struct kvm *kvm, ...@@ -274,7 +274,6 @@ static void kvm_s390_sync_dirty_log(struct kvm *kvm,
unsigned long address; unsigned long address;
struct gmap *gmap = kvm->arch.gmap; struct gmap *gmap = kvm->arch.gmap;
down_read(&gmap->mm->mmap_sem);
/* Loop over all guest pages */ /* Loop over all guest pages */
last_gfn = memslot->base_gfn + memslot->npages; last_gfn = memslot->base_gfn + memslot->npages;
for (cur_gfn = memslot->base_gfn; cur_gfn <= last_gfn; cur_gfn++) { for (cur_gfn = memslot->base_gfn; cur_gfn <= last_gfn; cur_gfn++) {
...@@ -282,8 +281,10 @@ static void kvm_s390_sync_dirty_log(struct kvm *kvm, ...@@ -282,8 +281,10 @@ static void kvm_s390_sync_dirty_log(struct kvm *kvm,
if (gmap_test_and_clear_dirty(address, gmap)) if (gmap_test_and_clear_dirty(address, gmap))
mark_page_dirty(kvm, cur_gfn); mark_page_dirty(kvm, cur_gfn);
if (fatal_signal_pending(current))
return;
cond_resched();
} }
up_read(&gmap->mm->mmap_sem);
} }
/* Section: vm related */ /* Section: vm related */
...@@ -1414,8 +1415,13 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) ...@@ -1414,8 +1415,13 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
KVM_SYNC_PFAULT; KVM_SYNC_PFAULT;
if (test_kvm_facility(vcpu->kvm, 64)) if (test_kvm_facility(vcpu->kvm, 64))
vcpu->run->kvm_valid_regs |= KVM_SYNC_RICCB; vcpu->run->kvm_valid_regs |= KVM_SYNC_RICCB;
if (test_kvm_facility(vcpu->kvm, 129)) /* fprs can be synchronized via vrs, even if the guest has no vx. With
* MACHINE_HAS_VX, (load|store)_fpu_regs() will work with vrs format.
*/
if (MACHINE_HAS_VX)
vcpu->run->kvm_valid_regs |= KVM_SYNC_VRS; vcpu->run->kvm_valid_regs |= KVM_SYNC_VRS;
else
vcpu->run->kvm_valid_regs |= KVM_SYNC_FPRS;
if (kvm_is_ucontrol(vcpu->kvm)) if (kvm_is_ucontrol(vcpu->kvm))
return __kvm_ucontrol_vcpu_init(vcpu); return __kvm_ucontrol_vcpu_init(vcpu);
...@@ -1430,10 +1436,10 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) ...@@ -1430,10 +1436,10 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
vcpu->arch.host_fpregs.fpc = current->thread.fpu.fpc; vcpu->arch.host_fpregs.fpc = current->thread.fpu.fpc;
vcpu->arch.host_fpregs.regs = current->thread.fpu.regs; vcpu->arch.host_fpregs.regs = current->thread.fpu.regs;
/* Depending on MACHINE_HAS_VX, data stored to vrs either if (MACHINE_HAS_VX)
* has vector register or floating point register format. current->thread.fpu.regs = vcpu->run->s.regs.vrs;
*/ else
current->thread.fpu.regs = vcpu->run->s.regs.vrs; current->thread.fpu.regs = vcpu->run->s.regs.fprs;
current->thread.fpu.fpc = vcpu->run->s.regs.fpc; current->thread.fpu.fpc = vcpu->run->s.regs.fpc;
if (test_fp_ctl(current->thread.fpu.fpc)) if (test_fp_ctl(current->thread.fpu.fpc))
/* User space provided an invalid FPC, let's clear it */ /* User space provided an invalid FPC, let's clear it */
...@@ -2158,8 +2164,10 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu) ...@@ -2158,8 +2164,10 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu)
static int vcpu_post_run_fault_in_sie(struct kvm_vcpu *vcpu) static int vcpu_post_run_fault_in_sie(struct kvm_vcpu *vcpu)
{ {
psw_t *psw = &vcpu->arch.sie_block->gpsw; struct kvm_s390_pgm_info pgm_info = {
u8 opcode; .code = PGM_ADDRESSING,
};
u8 opcode, ilen;
int rc; int rc;
VCPU_EVENT(vcpu, 3, "%s", "fault in sie instruction"); VCPU_EVENT(vcpu, 3, "%s", "fault in sie instruction");
...@@ -2173,12 +2181,21 @@ static int vcpu_post_run_fault_in_sie(struct kvm_vcpu *vcpu) ...@@ -2173,12 +2181,21 @@ static int vcpu_post_run_fault_in_sie(struct kvm_vcpu *vcpu)
* to look up the current opcode to get the length of the instruction * to look up the current opcode to get the length of the instruction
* to be able to forward the PSW. * to be able to forward the PSW.
*/ */
rc = read_guest(vcpu, psw->addr, 0, &opcode, 1); rc = read_guest_instr(vcpu, &opcode, 1);
if (rc) ilen = insn_length(opcode);
return kvm_s390_inject_prog_cond(vcpu, rc); if (rc < 0) {
psw->addr = __rewind_psw(*psw, -insn_length(opcode)); return rc;
} else if (rc) {
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); /* Instruction-Fetching Exceptions - we can't detect the ilen.
* Forward by arbitrary ilc, injection will take care of
* nullification if necessary.
*/
pgm_info = vcpu->arch.pgm;
ilen = 4;
}
pgm_info.flags = ilen | KVM_S390_PGM_FLAGS_ILC_VALID;
kvm_s390_forward_psw(vcpu, ilen);
return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
} }
static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason) static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
...@@ -2386,7 +2403,7 @@ int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long gpa) ...@@ -2386,7 +2403,7 @@ int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long gpa)
fprs, 128); fprs, 128);
} else { } else {
rc = write_guest_abs(vcpu, gpa + __LC_FPREGS_SAVE_AREA, rc = write_guest_abs(vcpu, gpa + __LC_FPREGS_SAVE_AREA,
vcpu->run->s.regs.vrs, 128); vcpu->run->s.regs.fprs, 128);
} }
rc |= write_guest_abs(vcpu, gpa + __LC_GPREGS_SAVE_AREA, rc |= write_guest_abs(vcpu, gpa + __LC_GPREGS_SAVE_AREA,
vcpu->run->s.regs.gprs, 128); vcpu->run->s.regs.gprs, 128);
...@@ -2605,7 +2622,8 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu, ...@@ -2605,7 +2622,8 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
switch (mop->op) { switch (mop->op) {
case KVM_S390_MEMOP_LOGICAL_READ: case KVM_S390_MEMOP_LOGICAL_READ:
if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) { if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size, false); r = check_gva_range(vcpu, mop->gaddr, mop->ar,
mop->size, GACC_FETCH);
break; break;
} }
r = read_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size); r = read_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size);
...@@ -2616,7 +2634,8 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu, ...@@ -2616,7 +2634,8 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
break; break;
case KVM_S390_MEMOP_LOGICAL_WRITE: case KVM_S390_MEMOP_LOGICAL_WRITE:
if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) { if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size, true); r = check_gva_range(vcpu, mop->gaddr, mop->ar,
mop->size, GACC_STORE);
break; break;
} }
if (copy_from_user(tmpbuf, uaddr, mop->size)) { if (copy_from_user(tmpbuf, uaddr, mop->size)) {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/kvm.h> #include <linux/kvm.h>
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
#include <asm/facility.h> #include <asm/facility.h>
#include <asm/processor.h>
typedef int (*intercept_handler_t)(struct kvm_vcpu *vcpu); typedef int (*intercept_handler_t)(struct kvm_vcpu *vcpu);
...@@ -212,8 +213,22 @@ int kvm_s390_reinject_io_int(struct kvm *kvm, ...@@ -212,8 +213,22 @@ int kvm_s390_reinject_io_int(struct kvm *kvm,
int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked); int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked);
/* implemented in intercept.c */ /* implemented in intercept.c */
void kvm_s390_rewind_psw(struct kvm_vcpu *vcpu, int ilc); u8 kvm_s390_get_ilen(struct kvm_vcpu *vcpu);
int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu); int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu);
static inline void kvm_s390_rewind_psw(struct kvm_vcpu *vcpu, int ilen)
{
struct kvm_s390_sie_block *sie_block = vcpu->arch.sie_block;
sie_block->gpsw.addr = __rewind_psw(sie_block->gpsw, ilen);
}
static inline void kvm_s390_forward_psw(struct kvm_vcpu *vcpu, int ilen)
{
kvm_s390_rewind_psw(vcpu, -ilen);
}
static inline void kvm_s390_retry_instr(struct kvm_vcpu *vcpu)
{
kvm_s390_rewind_psw(vcpu, kvm_s390_get_ilen(vcpu));
}
/* implemented in priv.c */ /* implemented in priv.c */
int is_valid_psw(psw_t *psw); int is_valid_psw(psw_t *psw);
......
...@@ -173,7 +173,7 @@ static int handle_skey(struct kvm_vcpu *vcpu) ...@@ -173,7 +173,7 @@ static int handle_skey(struct kvm_vcpu *vcpu)
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
kvm_s390_rewind_psw(vcpu, 4); kvm_s390_retry_instr(vcpu);
VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation"); VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation");
return 0; return 0;
} }
...@@ -184,7 +184,7 @@ static int handle_ipte_interlock(struct kvm_vcpu *vcpu) ...@@ -184,7 +184,7 @@ static int handle_ipte_interlock(struct kvm_vcpu *vcpu)
if (psw_bits(vcpu->arch.sie_block->gpsw).p) if (psw_bits(vcpu->arch.sie_block->gpsw).p)
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu)); wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu));
kvm_s390_rewind_psw(vcpu, 4); kvm_s390_retry_instr(vcpu);
VCPU_EVENT(vcpu, 4, "%s", "retrying ipte interlock operation"); VCPU_EVENT(vcpu, 4, "%s", "retrying ipte interlock operation");
return 0; return 0;
} }
...@@ -759,8 +759,8 @@ static int handle_essa(struct kvm_vcpu *vcpu) ...@@ -759,8 +759,8 @@ static int handle_essa(struct kvm_vcpu *vcpu)
if (((vcpu->arch.sie_block->ipb & 0xf0000000) >> 28) > 6) if (((vcpu->arch.sie_block->ipb & 0xf0000000) >> 28) > 6)
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
/* Rewind PSW to repeat the ESSA instruction */ /* Retry the ESSA instruction */
kvm_s390_rewind_psw(vcpu, 4); kvm_s390_retry_instr(vcpu);
vcpu->arch.sie_block->cbrlo &= PAGE_MASK; /* reset nceo */ vcpu->arch.sie_block->cbrlo &= PAGE_MASK; /* reset nceo */
cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo); cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo);
down_read(&gmap->mm->mmap_sem); down_read(&gmap->mm->mmap_sem);
...@@ -981,11 +981,12 @@ static int handle_tprot(struct kvm_vcpu *vcpu) ...@@ -981,11 +981,12 @@ static int handle_tprot(struct kvm_vcpu *vcpu)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT) if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
ipte_lock(vcpu); ipte_lock(vcpu);
ret = guest_translate_address(vcpu, address1, ar, &gpa, 1); ret = guest_translate_address(vcpu, address1, ar, &gpa, GACC_STORE);
if (ret == PGM_PROTECTION) { if (ret == PGM_PROTECTION) {
/* Write protected? Try again with read-only... */ /* Write protected? Try again with read-only... */
cc = 1; cc = 1;
ret = guest_translate_address(vcpu, address1, ar, &gpa, 0); ret = guest_translate_address(vcpu, address1, ar, &gpa,
GACC_FETCH);
} }
if (ret) { if (ret) {
if (ret == PGM_ADDRESSING || ret == PGM_TRANSLATION_SPEC) { if (ret == PGM_ADDRESSING || ret == PGM_TRANSLATION_SPEC) {
......
...@@ -541,7 +541,13 @@ struct kvm_s390_pgm_info { ...@@ -541,7 +541,13 @@ struct kvm_s390_pgm_info {
__u8 exc_access_id; __u8 exc_access_id;
__u8 per_access_id; __u8 per_access_id;
__u8 op_access_id; __u8 op_access_id;
__u8 pad[3]; #define KVM_S390_PGM_FLAGS_ILC_VALID 0x01
#define KVM_S390_PGM_FLAGS_ILC_0 0x02
#define KVM_S390_PGM_FLAGS_ILC_1 0x04
#define KVM_S390_PGM_FLAGS_ILC_MASK 0x06
#define KVM_S390_PGM_FLAGS_NO_REWIND 0x08
__u8 flags;
__u8 pad[2];
}; };
struct kvm_s390_prefix_info { struct kvm_s390_prefix_info {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment