[PATCH v1 3/5] KVM: s390: refactor some functions in priv.c

Claudio Imbrenda posted 5 patches 9 months ago
There is a newer version of this series
[PATCH v1 3/5] KVM: s390: refactor some functions in priv.c
Posted by Claudio Imbrenda 9 months ago
Refactor some functions in priv.c to make them more readable.

handle_{iske,rrbe,sske}: move duplicated checks into a single function.
handle{pfmf,epsw}: improve readability.
handle_lpswe{,y}: merge implementations since they are almost the same.

Use u64_replace_bits() where it makes sense.

Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
 arch/s390/kvm/kvm-s390.h |  15 ++
 arch/s390/kvm/priv.c     | 288 ++++++++++++++++++---------------------
 2 files changed, 148 insertions(+), 155 deletions(-)

diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index 8d3bbb2dd8d2..f8c32527c4e4 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -22,6 +22,21 @@
 
 #define KVM_S390_UCONTROL_MEMSLOT (KVM_USER_MEM_SLOTS + 0)
 
+static inline bool kvm_s390_is_amode_24(struct kvm_vcpu *vcpu)
+{
+	return psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_24BIT;
+}
+
+static inline bool kvm_s390_is_amode_31(struct kvm_vcpu *vcpu)
+{
+	return psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_31BIT;
+}
+
+static inline bool kvm_s390_is_amode_64(struct kvm_vcpu *vcpu)
+{
+	return psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT;
+}
+
 static inline void kvm_s390_fpu_store(struct kvm_run *run)
 {
 	fpu_stfpc(&run->s.regs.fpc);
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 758cefb5bac7..1a26aa591c2e 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -14,6 +14,7 @@
 #include <linux/mm_types.h>
 #include <linux/pgtable.h>
 #include <linux/io.h>
+#include <linux/bitfield.h>
 #include <asm/asm-offsets.h>
 #include <asm/facility.h>
 #include <asm/current.h>
@@ -253,29 +254,50 @@ static int try_handle_skey(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
+struct skeys_ops_state {
+	int reg1;
+	int reg2;
+	int rc;
+	unsigned long gaddr;
+};
+
+static bool skeys_common_checks(struct kvm_vcpu *vcpu, struct skeys_ops_state *state, bool abs)
+{
+	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) {
+		state->rc = kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
+		return true;
+	}
+
+	state->rc = try_handle_skey(vcpu);
+	if (state->rc)
+		return true;
+
+	kvm_s390_get_regs_rre(vcpu, &state->reg1, &state->reg2);
+
+	state->gaddr = vcpu->run->s.regs.gprs[state->reg2] & PAGE_MASK;
+	state->gaddr = kvm_s390_logical_to_effective(vcpu, state->gaddr);
+	if (!abs)
+		state->gaddr = kvm_s390_real_to_abs(vcpu, state->gaddr);
+
+	return false;
+}
+
 static int handle_iske(struct kvm_vcpu *vcpu)
 {
-	unsigned long gaddr, vmaddr;
+	struct skeys_ops_state state;
+	unsigned long vmaddr;
 	unsigned char key;
-	int reg1, reg2;
 	bool unlocked;
+	u64 *r1;
 	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);
-
-	rc = try_handle_skey(vcpu);
-	if (rc)
-		return rc != -EAGAIN ? rc : 0;
-
-	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+	if (skeys_common_checks(vcpu, &state, false))
+		return state.rc;
+	r1 = vcpu->run->s.regs.gprs + state.reg1;
 
-	gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
-	gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
-	gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
-	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
+	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(state.gaddr));
 	if (kvm_is_error_hva(vmaddr))
 		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 retry:
@@ -296,33 +318,23 @@ static int handle_iske(struct kvm_vcpu *vcpu)
 		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 	if (rc < 0)
 		return rc;
-	vcpu->run->s.regs.gprs[reg1] &= ~0xff;
-	vcpu->run->s.regs.gprs[reg1] |= key;
+	*r1 = u64_replace_bits(*r1, key, 0xff);
 	return 0;
 }
 
 static int handle_rrbe(struct kvm_vcpu *vcpu)
 {
-	unsigned long vmaddr, gaddr;
-	int reg1, reg2;
+	struct skeys_ops_state state;
+	unsigned long vmaddr;
 	bool unlocked;
 	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);
-
-	rc = try_handle_skey(vcpu);
-	if (rc)
-		return rc != -EAGAIN ? rc : 0;
-
-	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+	if (skeys_common_checks(vcpu, &state, false))
+		return state.rc;
 
-	gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
-	gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
-	gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
-	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
+	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(state.gaddr));
 	if (kvm_is_error_hva(vmaddr))
 		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 retry:
@@ -353,40 +365,30 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
 static int handle_sske(struct kvm_vcpu *vcpu)
 {
 	unsigned char m3 = vcpu->arch.sie_block->ipb >> 28;
+	struct skeys_ops_state state;
 	unsigned long start, end;
 	unsigned char key, oldkey;
-	int reg1, reg2;
+	bool nq, mr, mc, mb;
 	bool unlocked;
+	u64 *r1, *r2;
 	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);
-
-	rc = try_handle_skey(vcpu);
-	if (rc)
-		return rc != -EAGAIN ? rc : 0;
-
-	if (!test_kvm_facility(vcpu->kvm, 8))
-		m3 &= ~SSKE_MB;
-	if (!test_kvm_facility(vcpu->kvm, 10))
-		m3 &= ~(SSKE_MC | SSKE_MR);
-	if (!test_kvm_facility(vcpu->kvm, 14))
-		m3 &= ~SSKE_NQ;
+	mb = test_kvm_facility(vcpu->kvm, 8) && (m3 & SSKE_MB);
+	mr = test_kvm_facility(vcpu->kvm, 10) && (m3 & SSKE_MR);
+	mc = test_kvm_facility(vcpu->kvm, 10) && (m3 & SSKE_MC);
+	nq = test_kvm_facility(vcpu->kvm, 14) && (m3 & SSKE_NQ);
 
-	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+	/* start already designates an absolute address if MB is set */
+	if (skeys_common_checks(vcpu, &state, mb))
+		return state.rc;
 
-	key = vcpu->run->s.regs.gprs[reg1] & 0xfe;
-	start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
-	start = kvm_s390_logical_to_effective(vcpu, start);
-	if (m3 & SSKE_MB) {
-		/* start already designates an absolute address */
-		end = (start + _SEGMENT_SIZE) & ~(_SEGMENT_SIZE - 1);
-	} else {
-		start = kvm_s390_real_to_abs(vcpu, start);
-		end = start + PAGE_SIZE;
-	}
+	start = state.gaddr;
+	end = mb ? ALIGN(start + 1, _SEGMENT_SIZE) : start + PAGE_SIZE;
+	r1 = vcpu->run->s.regs.gprs + state.reg1;
+	r2 = vcpu->run->s.regs.gprs + state.reg2;
+	key = *r1 & 0xfe;
 
 	while (start != end) {
 		unsigned long vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
@@ -396,9 +398,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
 			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
 		mmap_read_lock(current->mm);
-		rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey,
-						m3 & SSKE_NQ, m3 & SSKE_MR,
-						m3 & SSKE_MC);
+		rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey, nq, mr, mc);
 
 		if (rc < 0) {
 			rc = fixup_user_fault(current->mm, vmaddr,
@@ -415,23 +415,21 @@ static int handle_sske(struct kvm_vcpu *vcpu)
 		start += PAGE_SIZE;
 	}
 
-	if (m3 & (SSKE_MC | SSKE_MR)) {
-		if (m3 & SSKE_MB) {
+	if (mc || mr) {
+		if (mb) {
 			/* skey in reg1 is unpredictable */
 			kvm_s390_set_psw_cc(vcpu, 3);
 		} else {
 			kvm_s390_set_psw_cc(vcpu, rc);
-			vcpu->run->s.regs.gprs[reg1] &= ~0xff00UL;
-			vcpu->run->s.regs.gprs[reg1] |= (u64) oldkey << 8;
+			*r1 = u64_replace_bits(*r1, oldkey << 8, 0xff00);
 		}
 	}
-	if (m3 & SSKE_MB) {
-		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT)
-			vcpu->run->s.regs.gprs[reg2] &= ~PAGE_MASK;
-		else
-			vcpu->run->s.regs.gprs[reg2] &= ~0xfffff000UL;
+	if (mb) {
 		end = kvm_s390_logical_to_effective(vcpu, end);
-		vcpu->run->s.regs.gprs[reg2] |= end;
+		if (kvm_s390_is_amode_64(vcpu))
+			*r2 = u64_replace_bits(*r2, end, PAGE_MASK);
+		else
+			*r2 = u64_replace_bits(*r2, end, 0xfffff000);
 	}
 	return 0;
 }
@@ -773,46 +771,28 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
-static int handle_lpswe(struct kvm_vcpu *vcpu)
+static int handle_lpswe_y(struct kvm_vcpu *vcpu, bool lpswey)
 {
 	psw_t new_psw;
 	u64 addr;
 	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);
-
-	addr = kvm_s390_get_base_disp_s(vcpu, &ar);
-	if (addr & 7)
-		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
-	rc = read_guest(vcpu, addr, ar, &new_psw, sizeof(new_psw));
-	if (rc)
-		return kvm_s390_inject_prog_cond(vcpu, rc);
-	vcpu->arch.sie_block->gpsw = new_psw;
-	if (!is_valid_psw(&vcpu->arch.sie_block->gpsw))
-		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
-	return 0;
-}
-
-static int handle_lpswey(struct kvm_vcpu *vcpu)
-{
-	psw_t new_psw;
-	u64 addr;
-	int rc;
-	u8 ar;
-
-	vcpu->stat.instruction_lpswey++;
+	if (lpswey)
+		vcpu->stat.instruction_lpswey++;
+	else
+		vcpu->stat.instruction_lpswe++;
 
-	if (!test_kvm_facility(vcpu->kvm, 193))
+	if (lpswey && !test_kvm_facility(vcpu->kvm, 193))
 		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
 
 	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
 		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
 
-	addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
+	if (!lpswey)
+		addr = kvm_s390_get_base_disp_s(vcpu, &ar);
+	else
+		addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
 	if (addr & 7)
 		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
@@ -1034,7 +1014,7 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
 	case 0xb1:
 		return handle_stfl(vcpu);
 	case 0xb2:
-		return handle_lpswe(vcpu);
+		return handle_lpswe_y(vcpu, false);
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -1043,42 +1023,50 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
 static int handle_epsw(struct kvm_vcpu *vcpu)
 {
 	int reg1, reg2;
+	u64 *r1, *r2;
 
 	vcpu->stat.instruction_epsw++;
 
 	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+	r1 = vcpu->run->s.regs.gprs + reg1;
+	r2 = vcpu->run->s.regs.gprs + reg2;
 
 	/* This basically extracts the mask half of the psw. */
-	vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000UL;
-	vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32;
-	if (reg2) {
-		vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000UL;
-		vcpu->run->s.regs.gprs[reg2] |=
-			vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffffUL;
-	}
+	*r1 = u64_replace_bits(*r1, vcpu->arch.sie_block->gpsw.mask >> 32, 0xffffffff);
+	if (reg2)
+		*r2 = u64_replace_bits(*r2, vcpu->arch.sie_block->gpsw.mask, 0xffffffff);
 	return 0;
 }
 
 #define PFMF_RESERVED   0xfffc0101UL
-#define PFMF_SK         0x00020000UL
-#define PFMF_CF         0x00010000UL
-#define PFMF_UI         0x00008000UL
-#define PFMF_FSC        0x00007000UL
-#define PFMF_NQ         0x00000800UL
-#define PFMF_MR         0x00000400UL
-#define PFMF_MC         0x00000200UL
-#define PFMF_KEY        0x000000feUL
+union pfmf_r1 {
+	unsigned long val;
+	struct {
+		unsigned long    :46;
+		unsigned long sk : 1;
+		unsigned long cf : 1;
+		unsigned long ui : 1;
+		unsigned long fsc: 3;
+		unsigned long nq : 1;
+		unsigned long mr : 1;
+		unsigned long mc : 1;
+		unsigned long    : 1;
+		unsigned char skey;
+	} __packed;
+};
+
+static_assert(sizeof(union pfmf_r1) == sizeof(unsigned long));
 
 static int handle_pfmf(struct kvm_vcpu *vcpu)
 {
-	bool mr = false, mc = false, nq;
 	int reg1, reg2;
 	unsigned long start, end;
-	unsigned char key;
+	union pfmf_r1 r1;
 
 	vcpu->stat.instruction_pfmf++;
 
 	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+	r1.val = vcpu->run->s.regs.gprs[reg1];
 
 	if (!test_kvm_facility(vcpu->kvm, 8))
 		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
@@ -1086,47 +1074,38 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
 	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
 		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
 
-	if (vcpu->run->s.regs.gprs[reg1] & PFMF_RESERVED)
+	if (r1.val & PFMF_RESERVED)
 		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
 	/* Only provide non-quiescing support if enabled for the guest */
-	if (vcpu->run->s.regs.gprs[reg1] & PFMF_NQ &&
-	    !test_kvm_facility(vcpu->kvm, 14))
+	if (r1.nq && !test_kvm_facility(vcpu->kvm, 14))
 		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
 	/* Only provide conditional-SSKE support if enabled for the guest */
-	if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK &&
-	    test_kvm_facility(vcpu->kvm, 10)) {
-		mr = vcpu->run->s.regs.gprs[reg1] & PFMF_MR;
-		mc = vcpu->run->s.regs.gprs[reg1] & PFMF_MC;
-	}
+	if (!r1.sk || !test_kvm_facility(vcpu->kvm, 10))
+		r1.mr = r1.mc = 0;
 
-	nq = vcpu->run->s.regs.gprs[reg1] & PFMF_NQ;
-	key = vcpu->run->s.regs.gprs[reg1] & PFMF_KEY;
 	start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
 	start = kvm_s390_logical_to_effective(vcpu, start);
 
-	if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
-		if (kvm_s390_check_low_addr_prot_real(vcpu, start))
-			return kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
-	}
+	if (r1.cf && kvm_s390_check_low_addr_prot_real(vcpu, start))
+		return kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
 
-	switch (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
-	case 0x00000000:
+	switch (r1.fsc) {
+	case 0:
 		/* only 4k frames specify a real address */
 		start = kvm_s390_real_to_abs(vcpu, start);
-		end = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
+		end = ALIGN(start + 1, PAGE_SIZE);
 		break;
-	case 0x00001000:
-		end = (start + _SEGMENT_SIZE) & ~(_SEGMENT_SIZE - 1);
+	case 1:
+		end = ALIGN(start + 1, _SEGMENT_SIZE);
 		break;
-	case 0x00002000:
+	case 2:
 		/* only support 2G frame size if EDAT2 is available and we are
 		   not in 24-bit addressing mode */
-		if (!test_kvm_facility(vcpu->kvm, 78) ||
-		    psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_24BIT)
+		if (!test_kvm_facility(vcpu->kvm, 78) || kvm_s390_is_amode_24(vcpu))
 			return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
-		end = (start + _REGION3_SIZE) & ~(_REGION3_SIZE - 1);
+		end = ALIGN(start + 1, _REGION3_SIZE);
 		break;
 	default:
 		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
@@ -1141,19 +1120,17 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
 		if (kvm_is_error_hva(vmaddr))
 			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
-		if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
-			if (kvm_clear_guest(vcpu->kvm, start, PAGE_SIZE))
-				return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-		}
+		if (r1.cf && kvm_clear_guest(vcpu->kvm, start, PAGE_SIZE))
+			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
-		if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK) {
+		if (r1.sk) {
 			int rc = kvm_s390_skey_check_enable(vcpu);
 
 			if (rc)
 				return rc;
 			mmap_read_lock(current->mm);
-			rc = cond_set_guest_storage_key(current->mm, vmaddr,
-							key, NULL, nq, mr, mc);
+			rc = cond_set_guest_storage_key(current->mm, vmaddr, r1.skey, NULL,
+							r1.nq, r1.mr, r1.mc);
 			if (rc < 0) {
 				rc = fixup_user_fault(current->mm, vmaddr,
 						      FAULT_FLAG_WRITE, &unlocked);
@@ -1169,14 +1146,14 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
 		}
 		start += PAGE_SIZE;
 	}
-	if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
-		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT) {
-			vcpu->run->s.regs.gprs[reg2] = end;
-		} else {
-			vcpu->run->s.regs.gprs[reg2] &= ~0xffffffffUL;
-			end = kvm_s390_logical_to_effective(vcpu, end);
-			vcpu->run->s.regs.gprs[reg2] |= end;
-		}
+	if (r1.fsc) {
+		u64 *r2 = vcpu->run->s.regs.gprs + reg2;
+
+		end = kvm_s390_logical_to_effective(vcpu, end);
+		if (kvm_s390_is_amode_64(vcpu))
+			*r2 = u64_replace_bits(*r2, end, PAGE_MASK);
+		else
+			*r2 = u64_replace_bits(*r2, end, 0xfffff000);
 	}
 	return 0;
 }
@@ -1361,8 +1338,9 @@ int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu)
 	reg = reg1;
 	nr_regs = 0;
 	do {
-		vcpu->arch.sie_block->gcr[reg] &= 0xffffffff00000000ul;
-		vcpu->arch.sie_block->gcr[reg] |= ctl_array[nr_regs++];
+		u64 *cr = vcpu->arch.sie_block->gcr + reg;
+
+		*cr = u64_replace_bits(*cr, ctl_array[nr_regs++], 0xffffffff);
 		if (reg == reg3)
 			break;
 		reg = (reg + 1) % 16;
@@ -1489,7 +1467,7 @@ int kvm_s390_handle_eb(struct kvm_vcpu *vcpu)
 	case 0x62:
 		return handle_ri(vcpu);
 	case 0x71:
-		return handle_lpswey(vcpu);
+		return handle_lpswe_y(vcpu, true);
 	default:
 		return -EOPNOTSUPP;
 	}
-- 
2.49.0
Re: [PATCH v1 3/5] KVM: s390: refactor some functions in priv.c
Posted by Nina Schoetterl-Glausch 8 months, 3 weeks ago
On Wed, 2025-05-14 at 18:38 +0200, Claudio Imbrenda wrote:
> Refactor some functions in priv.c to make them more readable.
> 
> handle_{iske,rrbe,sske}: move duplicated checks into a single function.
> handle{pfmf,epsw}: improve readability.
> handle_lpswe{,y}: merge implementations since they are almost the same.
> 
> Use u64_replace_bits() where it makes sense.
> 
> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
> ---
>  arch/s390/kvm/kvm-s390.h |  15 ++
>  arch/s390/kvm/priv.c     | 288 ++++++++++++++++++---------------------
>  2 files changed, 148 insertions(+), 155 deletions(-)
> 
[...]

> diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
> index 758cefb5bac7..1a26aa591c2e 100644
> --- a/arch/s390/kvm/priv.c
> +++ b/arch/s390/kvm/priv.c
> @@ -14,6 +14,7 @@
>  #include <linux/mm_types.h>
>  #include <linux/pgtable.h>
>  #include <linux/io.h>
> +#include <linux/bitfield.h>
>  #include <asm/asm-offsets.h>
>  #include <asm/facility.h>
>  #include <asm/current.h>
> @@ -253,29 +254,50 @@ static int try_handle_skey(struct kvm_vcpu *vcpu)
>  	return 0;
>  }
>  
> +struct skeys_ops_state {
> +	int reg1;
> +	int reg2;
> +	int rc;
> +	unsigned long gaddr;
> +};
> +
> +static bool skeys_common_checks(struct kvm_vcpu *vcpu, struct skeys_ops_state *state, bool abs)
> +{
> +	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) {
> +		state->rc = kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
> +		return true;
> +	}
> +
> +	state->rc = try_handle_skey(vcpu);
> +	if (state->rc)
> +		return true;
> +
> +	kvm_s390_get_regs_rre(vcpu, &state->reg1, &state->reg2);
> +
> +	state->gaddr = vcpu->run->s.regs.gprs[state->reg2] & PAGE_MASK;
> +	state->gaddr = kvm_s390_logical_to_effective(vcpu, state->gaddr);
> +	if (!abs)
> +		state->gaddr = kvm_s390_real_to_abs(vcpu, state->gaddr);
> +
> +	return false;
> +}

I don't really like this function, IMO it makes the calling functions harder to read.
If it was just a chain of checks it be fine, but with the differing control flow
base on the abs parameter and the complex return value it becomes too complicated.

> +
>  static int handle_iske(struct kvm_vcpu *vcpu)
>  {
> -	unsigned long gaddr, vmaddr;
> +	struct skeys_ops_state state;
> +	unsigned long vmaddr;
>  	unsigned char key;
> -	int reg1, reg2;
>  	bool unlocked;
> +	u64 *r1;
>  	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);

How about a macro INJECT_PGM_ON: INJECT_PGM_ON(kvm_s390_problem_state(vcpu), PGM_PRIVILEGED_OP)


> -
> -	rc = try_handle_skey(vcpu);
> -	if (rc)
> -		return rc != -EAGAIN ? rc : 0;

You are not replicating this behavior, are you?
> -
> -	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);

You could introduce a helper

void _kvm_s390_get_gpr_ptrs_rre(vcpu, u64 **reg1, u64 **reg2)
{
	int r1, r2;

	kvm_s390_get_regs_rre(vcpu, &r1, &r2);
	*reg1 = &vcpu->run->s.regs.gprs[r1];
	*reg2 = &vcpu->run->s.regs.gprs[r2];
}

which would remove some clutter from the original function implementations.

> +	if (skeys_common_checks(vcpu, &state, false))
> +		return state.rc;
> +	r1 = vcpu->run->s.regs.gprs + state.reg1;
>  
> -	gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
> -	gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
> -	gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
> -	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
> +	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(state.gaddr));
>  	if (kvm_is_error_hva(vmaddr))
>  		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
>  retry:
> @@ -296,33 +318,23 @@ static int handle_iske(struct kvm_vcpu *vcpu)
>  		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
>  	if (rc < 0)
>  		return rc;
> -	vcpu->run->s.regs.gprs[reg1] &= ~0xff;
> -	vcpu->run->s.regs.gprs[reg1] |= key;
> +	*r1 = u64_replace_bits(*r1, key, 0xff);
>  	return 0;
>  }
>  
> 
[...]

>  retry:
> @@ -353,40 +365,30 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
>  static int handle_sske(struct kvm_vcpu *vcpu)
>  {
>  	unsigned char m3 = vcpu->arch.sie_block->ipb >> 28;
> +	struct skeys_ops_state state;
>  	unsigned long start, end;
>  	unsigned char key, oldkey;
> -	int reg1, reg2;
> +	bool nq, mr, mc, mb;
>  	bool unlocked;
> +	u64 *r1, *r2;
>  	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);
> -
> -	rc = try_handle_skey(vcpu);
> -	if (rc)
> -		return rc != -EAGAIN ? rc : 0;
> -
> -	if (!test_kvm_facility(vcpu->kvm, 8))
> -		m3 &= ~SSKE_MB;
> -	if (!test_kvm_facility(vcpu->kvm, 10))
> -		m3 &= ~(SSKE_MC | SSKE_MR);
> -	if (!test_kvm_facility(vcpu->kvm, 14))
> -		m3 &= ~SSKE_NQ;
> +	mb = test_kvm_facility(vcpu->kvm, 8) && (m3 & SSKE_MB);
> +	mr = test_kvm_facility(vcpu->kvm, 10) && (m3 & SSKE_MR);
> +	mc = test_kvm_facility(vcpu->kvm, 10) && (m3 & SSKE_MC);
> +	nq = test_kvm_facility(vcpu->kvm, 14) && (m3 & SSKE_NQ);

That is indeed much nicer.

>  
> -	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
> +	/* start already designates an absolute address if MB is set */
> +	if (skeys_common_checks(vcpu, &state, mb))
> +		return state.rc;
>  
> -	key = vcpu->run->s.regs.gprs[reg1] & 0xfe;
> -	start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
> -	start = kvm_s390_logical_to_effective(vcpu, start);
> -	if (m3 & SSKE_MB) {
> -		/* start already designates an absolute address */
> -		end = (start + _SEGMENT_SIZE) & ~(_SEGMENT_SIZE - 1);
> -	} else {
> -		start = kvm_s390_real_to_abs(vcpu, start);
> -		end = start + PAGE_SIZE;
> -	}
> +	start = state.gaddr;
> +	end = mb ? ALIGN(start + 1, _SEGMENT_SIZE) : start + PAGE_SIZE;

Alternatively you could do ALIGN_DOWN(start, _SEGMENT_SIZE) + _SEGMENT_SIZE,
which seems a bit easier to read, but it's really minor.

> +	r1 = vcpu->run->s.regs.gprs + state.reg1;
> +	r2 = vcpu->run->s.regs.gprs + state.reg2;
> +	key = *r1 & 0xfe;
>  
>  	while (start != end) {
>  		unsigned long vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
> @@ -396,9 +398,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
>  			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
>  
>  		mmap_read_lock(current->mm);
> -		rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey,
> -						m3 & SSKE_NQ, m3 & SSKE_MR,
> -						m3 & SSKE_MC);
> +		rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey, nq, mr, mc);
>  
>  		if (rc < 0) {
>  			rc = fixup_user_fault(current->mm, vmaddr,
> @@ -415,23 +415,21 @@ static int handle_sske(struct kvm_vcpu *vcpu)
>  		start += PAGE_SIZE;
>  	}
>  
> -	if (m3 & (SSKE_MC | SSKE_MR)) {
> -		if (m3 & SSKE_MB) {
> +	if (mc || mr) {
> +		if (mb) {
>  			/* skey in reg1 is unpredictable */
>  			kvm_s390_set_psw_cc(vcpu, 3);
>  		} else {
>  			kvm_s390_set_psw_cc(vcpu, rc);
> -			vcpu->run->s.regs.gprs[reg1] &= ~0xff00UL;
> -			vcpu->run->s.regs.gprs[reg1] |= (u64) oldkey << 8;
> +			*r1 = u64_replace_bits(*r1, oldkey << 8, 0xff00);

Uh, u64_replace_bits does the shift for you, no?
So it should be u64_replace_bits(*r1, oldkey, 0xff00)

You could also do u64p_replace_bits(r1, oldkey, 0xff00) but I'd actually prefer the assignment
as you do it.

>  		}
>  	}
> -	if (m3 & SSKE_MB) {
> -		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT)
> -			vcpu->run->s.regs.gprs[reg2] &= ~PAGE_MASK;
> -		else
> -			vcpu->run->s.regs.gprs[reg2] &= ~0xfffff000UL;
> +	if (mb) {
>  		end = kvm_s390_logical_to_effective(vcpu, end);
> -		vcpu->run->s.regs.gprs[reg2] |= end;
> +		if (kvm_s390_is_amode_64(vcpu))
> +			*r2 = u64_replace_bits(*r2, end, PAGE_MASK);
> +		else
> +			*r2 = u64_replace_bits(*r2, end, 0xfffff000);

This does not work because of the implicit shift.
So you need to use gpa_to_gfn(end) instead.
(I think I would prefer using start instead of end, since it better shows
the interruptible nature of the instruction, but start == end if
we get here so ...)

>  	}
>  	return 0;
>  }
> @@ -773,46 +771,28 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
>  	return 0;
>  }
>  
> -static int handle_lpswe(struct kvm_vcpu *vcpu)
> +static int handle_lpswe_y(struct kvm_vcpu *vcpu, bool lpswey)
>  {
>  	psw_t new_psw;
>  	u64 addr;
>  	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);
> -
> -	addr = kvm_s390_get_base_disp_s(vcpu, &ar);
> -	if (addr & 7)
> -		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
> -	rc = read_guest(vcpu, addr, ar, &new_psw, sizeof(new_psw));
> -	if (rc)
> -		return kvm_s390_inject_prog_cond(vcpu, rc);
> -	vcpu->arch.sie_block->gpsw = new_psw;
> -	if (!is_valid_psw(&vcpu->arch.sie_block->gpsw))
> -		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
> -	return 0;
> -}
> -
> -static int handle_lpswey(struct kvm_vcpu *vcpu)
> -{
> -	psw_t new_psw;
> -	u64 addr;
> -	int rc;
> -	u8 ar;
> -
> -	vcpu->stat.instruction_lpswey++;
> +	if (lpswey)
> +		vcpu->stat.instruction_lpswey++;
> +	else
> +		vcpu->stat.instruction_lpswe++;
>  
> -	if (!test_kvm_facility(vcpu->kvm, 193))
> +	if (lpswey && !test_kvm_facility(vcpu->kvm, 193))
>  		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
>  
>  	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
>  		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
>  
> -	addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
> +	if (!lpswey)
> +		addr = kvm_s390_get_base_disp_s(vcpu, &ar);
> +	else
> +		addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
>  	if (addr & 7)
>  		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);

I'd prefer a helper function _do_lpswe_y_swap(struct kvm_vcpu *vcpu, gpa_t addr)

and then just

static int handle_lpswey(struct kvm_vcpu *vcpu)
{
        u64 addr;
        u8 ar;

        vcpu->stat.instruction_lpswey++;

        if (!test_kvm_facility(vcpu->kvm, 193))
                return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);

        addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
	return _do_lpswe_y_swap(vcpu, addr);
}

Makes it easier to read IMO because of the simpler control flow.
>  
> @@ -1034,7 +1014,7 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
>  	case 0xb1:
>  		return handle_stfl(vcpu);
>  	case 0xb2:
> -		return handle_lpswe(vcpu);
> +		return handle_lpswe_y(vcpu, false);
>  	default:
>  		return -EOPNOTSUPP;
>  	}
> @@ -1043,42 +1023,50 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
>  static int handle_epsw(struct kvm_vcpu *vcpu)
>  {
>  	int reg1, reg2;
> +	u64 *r1, *r2;
>  
>  	vcpu->stat.instruction_epsw++;
>  
>  	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
> +	r1 = vcpu->run->s.regs.gprs + reg1;
> +	r2 = vcpu->run->s.regs.gprs + reg2;
>  
>  	/* This basically extracts the mask half of the psw. */
> -	vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000UL;
> -	vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32;
> -	if (reg2) {
> -		vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000UL;
> -		vcpu->run->s.regs.gprs[reg2] |=
> -			vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffffUL;
> -	}
> +	*r1 = u64_replace_bits(*r1, vcpu->arch.sie_block->gpsw.mask >> 32, 0xffffffff);
> +	if (reg2)
> +		*r2 = u64_replace_bits(*r2, vcpu->arch.sie_block->gpsw.mask, 0xffffffff);

LGTM although I don't hate the original implementation, which is very easy to understand
compared to u64_replace_bits whose implementation is anything but.
It would be nice to make gprs a union, which I think should be fine from a backwards
compatibility point of view. So:

struct kvm_sync_regs {
	__u64 prefix;	/* prefix register */
	union {
		__u64 gprs[16];	/* general purpose registers */
		struct { __u32 h; __u32 l} gprs32[16];
		struct { __u16 hh; __u16 hl; ...} gprs16[16];
		... 
...

But I don't expect you to do the refactor.
You could of course also contribute documentation to bitfield.h :)

>  	return 0;
>  }

[...]

>  static int handle_pfmf(struct kvm_vcpu *vcpu)
>  {

[...]

> -	if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
> -		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT) {
> -			vcpu->run->s.regs.gprs[reg2] = end;
> -		} else {
> -			vcpu->run->s.regs.gprs[reg2] &= ~0xffffffffUL;
> -			end = kvm_s390_logical_to_effective(vcpu, end);
> -			vcpu->run->s.regs.gprs[reg2] |= end;
> -		}
> +	if (r1.fsc) {
> +		u64 *r2 = vcpu->run->s.regs.gprs + reg2;
> +
> +		end = kvm_s390_logical_to_effective(vcpu, end);
> +		if (kvm_s390_is_amode_64(vcpu))
> +			*r2 = u64_replace_bits(*r2, end, PAGE_MASK);
> +		else
> +			*r2 = u64_replace_bits(*r2, end, 0xfffff000);

Same issue as above regarding the shift.

>  	}
>  	return 0;
>  }
> @@ -1361,8 +1338,9 @@ int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu)
>  	reg = reg1;
>  	nr_regs = 0;
>  	do {
> -		vcpu->arch.sie_block->gcr[reg] &= 0xffffffff00000000ul;
> -		vcpu->arch.sie_block->gcr[reg] |= ctl_array[nr_regs++];
> +		u64 *cr = vcpu->arch.sie_block->gcr + reg;
> +
> +		*cr = u64_replace_bits(*cr, ctl_array[nr_regs++], 0xffffffff);
>  		if (reg == reg3)
>  			break;
>  		reg = (reg + 1) % 16;
> @@ -1489,7 +1467,7 @@ int kvm_s390_handle_eb(struct kvm_vcpu *vcpu)
>  	case 0x62:
>  		return handle_ri(vcpu);
>  	case 0x71:
> -		return handle_lpswey(vcpu);
> +		return handle_lpswe_y(vcpu, true);
>  	default:
>  		return -EOPNOTSUPP;
>  	}

-- 
IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Wolfgang Wendt
Geschäftsführung: David Faller
Sitz der Gesellschaft: Böblingen / Registergericht: Amtsgericht Stuttgart, HRB 243294
Re: [PATCH v1 3/5] KVM: s390: refactor some functions in priv.c
Posted by Claudio Imbrenda 8 months, 3 weeks ago
On Tue, 20 May 2025 14:49:55 +0200
Nina Schoetterl-Glausch <nsg@linux.ibm.com> wrote:

> On Wed, 2025-05-14 at 18:38 +0200, Claudio Imbrenda wrote:
> > Refactor some functions in priv.c to make them more readable.
> > 
> > handle_{iske,rrbe,sske}: move duplicated checks into a single function.
> > handle{pfmf,epsw}: improve readability.
> > handle_lpswe{,y}: merge implementations since they are almost the same.
> > 
> > Use u64_replace_bits() where it makes sense.
> > 
> > Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
> > ---
> >  arch/s390/kvm/kvm-s390.h |  15 ++
> >  arch/s390/kvm/priv.c     | 288 ++++++++++++++++++---------------------
> >  2 files changed, 148 insertions(+), 155 deletions(-)
> >   
> [...]
> 
> > diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
> > index 758cefb5bac7..1a26aa591c2e 100644
> > --- a/arch/s390/kvm/priv.c
> > +++ b/arch/s390/kvm/priv.c
> > @@ -14,6 +14,7 @@
> >  #include <linux/mm_types.h>
> >  #include <linux/pgtable.h>
> >  #include <linux/io.h>
> > +#include <linux/bitfield.h>
> >  #include <asm/asm-offsets.h>
> >  #include <asm/facility.h>
> >  #include <asm/current.h>
> > @@ -253,29 +254,50 @@ static int try_handle_skey(struct kvm_vcpu *vcpu)
> >  	return 0;
> >  }
> >  
> > +struct skeys_ops_state {
> > +	int reg1;
> > +	int reg2;
> > +	int rc;
> > +	unsigned long gaddr;
> > +};
> > +
> > +static bool skeys_common_checks(struct kvm_vcpu *vcpu, struct skeys_ops_state *state, bool abs)
> > +{
> > +	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) {
> > +		state->rc = kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
> > +		return true;
> > +	}
> > +
> > +	state->rc = try_handle_skey(vcpu);
> > +	if (state->rc)
> > +		return true;
> > +
> > +	kvm_s390_get_regs_rre(vcpu, &state->reg1, &state->reg2);
> > +
> > +	state->gaddr = vcpu->run->s.regs.gprs[state->reg2] & PAGE_MASK;
> > +	state->gaddr = kvm_s390_logical_to_effective(vcpu, state->gaddr);
> > +	if (!abs)
> > +		state->gaddr = kvm_s390_real_to_abs(vcpu, state->gaddr);
> > +
> > +	return false;
> > +}  
> 
> I don't really like this function, IMO it makes the calling functions harder to read.
> If it was just a chain of checks it be fine, but with the differing control flow
> base on the abs parameter and the complex return value it becomes too complicated.

I'll try to improve it

> 
> > +
> >  static int handle_iske(struct kvm_vcpu *vcpu)
> >  {
> > -	unsigned long gaddr, vmaddr;
> > +	struct skeys_ops_state state;
> > +	unsigned long vmaddr;
> >  	unsigned char key;
> > -	int reg1, reg2;
> >  	bool unlocked;
> > +	u64 *r1;
> >  	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);  
> 
> How about a macro INJECT_PGM_ON: INJECT_PGM_ON(kvm_s390_problem_state(vcpu), PGM_PRIVILEGED_OP)

no, I would like to avoid hiding control flow in a macro

> 
> 
> > -
> > -	rc = try_handle_skey(vcpu);
> > -	if (rc)
> > -		return rc != -EAGAIN ? rc : 0;  
> 
> You are not replicating this behavior, are you?

no, but it's fine, we can afford a useless trip to userspace literally
once in the lifetime of the guest

> > -
> > -	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);  
> 
> You could introduce a helper
> 
> void _kvm_s390_get_gpr_ptrs_rre(vcpu, u64 **reg1, u64 **reg2)
> {
> 	int r1, r2;
> 
> 	kvm_s390_get_regs_rre(vcpu, &r1, &r2);
> 	*reg1 = &vcpu->run->s.regs.gprs[r1];
> 	*reg2 = &vcpu->run->s.regs.gprs[r2];
> }
> 
> which would remove some clutter from the original function implementations.
> 
> > +	if (skeys_common_checks(vcpu, &state, false))
> > +		return state.rc;
> > +	r1 = vcpu->run->s.regs.gprs + state.reg1;
> >  
> > -	gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
> > -	gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
> > -	gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
> > -	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
> > +	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(state.gaddr));
> >  	if (kvm_is_error_hva(vmaddr))
> >  		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
> >  retry:
> > @@ -296,33 +318,23 @@ static int handle_iske(struct kvm_vcpu *vcpu)
> >  		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
> >  	if (rc < 0)
> >  		return rc;
> > -	vcpu->run->s.regs.gprs[reg1] &= ~0xff;
> > -	vcpu->run->s.regs.gprs[reg1] |= key;
> > +	*r1 = u64_replace_bits(*r1, key, 0xff);
> >  	return 0;
> >  }
> >  
> >   
> [...]
> 
> >  retry:
> > @@ -353,40 +365,30 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
> >  static int handle_sske(struct kvm_vcpu *vcpu)
> >  {
> >  	unsigned char m3 = vcpu->arch.sie_block->ipb >> 28;
> > +	struct skeys_ops_state state;
> >  	unsigned long start, end;
> >  	unsigned char key, oldkey;
> > -	int reg1, reg2;
> > +	bool nq, mr, mc, mb;
> >  	bool unlocked;
> > +	u64 *r1, *r2;
> >  	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);
> > -
> > -	rc = try_handle_skey(vcpu);
> > -	if (rc)
> > -		return rc != -EAGAIN ? rc : 0;
> > -
> > -	if (!test_kvm_facility(vcpu->kvm, 8))
> > -		m3 &= ~SSKE_MB;
> > -	if (!test_kvm_facility(vcpu->kvm, 10))
> > -		m3 &= ~(SSKE_MC | SSKE_MR);
> > -	if (!test_kvm_facility(vcpu->kvm, 14))
> > -		m3 &= ~SSKE_NQ;
> > +	mb = test_kvm_facility(vcpu->kvm, 8) && (m3 & SSKE_MB);
> > +	mr = test_kvm_facility(vcpu->kvm, 10) && (m3 & SSKE_MR);
> > +	mc = test_kvm_facility(vcpu->kvm, 10) && (m3 & SSKE_MC);
> > +	nq = test_kvm_facility(vcpu->kvm, 14) && (m3 & SSKE_NQ);  
> 
> That is indeed much nicer.
> 
> >  
> > -	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
> > +	/* start already designates an absolute address if MB is set */
> > +	if (skeys_common_checks(vcpu, &state, mb))
> > +		return state.rc;
> >  
> > -	key = vcpu->run->s.regs.gprs[reg1] & 0xfe;
> > -	start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
> > -	start = kvm_s390_logical_to_effective(vcpu, start);
> > -	if (m3 & SSKE_MB) {
> > -		/* start already designates an absolute address */
> > -		end = (start + _SEGMENT_SIZE) & ~(_SEGMENT_SIZE - 1);
> > -	} else {
> > -		start = kvm_s390_real_to_abs(vcpu, start);
> > -		end = start + PAGE_SIZE;
> > -	}
> > +	start = state.gaddr;
> > +	end = mb ? ALIGN(start + 1, _SEGMENT_SIZE) : start + PAGE_SIZE;  
> 
> Alternatively you could do ALIGN_DOWN(start, _SEGMENT_SIZE) + _SEGMENT_SIZE,
> which seems a bit easier to read, but it's really minor.
> 
> > +	r1 = vcpu->run->s.regs.gprs + state.reg1;
> > +	r2 = vcpu->run->s.regs.gprs + state.reg2;
> > +	key = *r1 & 0xfe;
> >  
> >  	while (start != end) {
> >  		unsigned long vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
> > @@ -396,9 +398,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
> >  			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
> >  
> >  		mmap_read_lock(current->mm);
> > -		rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey,
> > -						m3 & SSKE_NQ, m3 & SSKE_MR,
> > -						m3 & SSKE_MC);
> > +		rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey, nq, mr, mc);
> >  
> >  		if (rc < 0) {
> >  			rc = fixup_user_fault(current->mm, vmaddr,
> > @@ -415,23 +415,21 @@ static int handle_sske(struct kvm_vcpu *vcpu)
> >  		start += PAGE_SIZE;
> >  	}
> >  
> > -	if (m3 & (SSKE_MC | SSKE_MR)) {
> > -		if (m3 & SSKE_MB) {
> > +	if (mc || mr) {
> > +		if (mb) {
> >  			/* skey in reg1 is unpredictable */
> >  			kvm_s390_set_psw_cc(vcpu, 3);
> >  		} else {
> >  			kvm_s390_set_psw_cc(vcpu, rc);
> > -			vcpu->run->s.regs.gprs[reg1] &= ~0xff00UL;
> > -			vcpu->run->s.regs.gprs[reg1] |= (u64) oldkey << 8;
> > +			*r1 = u64_replace_bits(*r1, oldkey << 8, 0xff00);  
> 
> Uh, u64_replace_bits does the shift for you, no?
> So it should be u64_replace_bits(*r1, oldkey, 0xff00)
> 
> You could also do u64p_replace_bits(r1, oldkey, 0xff00) but I'd actually prefer the assignment
> as you do it.

yeahhhhhh I think I'll completely rewrite those parts using bitfields
and structs / unions

> 
> >  		}
> >  	}
> > -	if (m3 & SSKE_MB) {
> > -		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT)
> > -			vcpu->run->s.regs.gprs[reg2] &= ~PAGE_MASK;
> > -		else
> > -			vcpu->run->s.regs.gprs[reg2] &= ~0xfffff000UL;
> > +	if (mb) {
> >  		end = kvm_s390_logical_to_effective(vcpu, end);
> > -		vcpu->run->s.regs.gprs[reg2] |= end;
> > +		if (kvm_s390_is_amode_64(vcpu))
> > +			*r2 = u64_replace_bits(*r2, end, PAGE_MASK);
> > +		else
> > +			*r2 = u64_replace_bits(*r2, end, 0xfffff000);  
> 
> This does not work because of the implicit shift.
> So you need to use gpa_to_gfn(end) instead.
> (I think I would prefer using start instead of end, since it better shows
> the interruptible nature of the instruction, but start == end if
> we get here so ...)
> 
> >  	}
> >  	return 0;
> >  }
> > @@ -773,46 +771,28 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
> >  	return 0;
> >  }
> >  
> > -static int handle_lpswe(struct kvm_vcpu *vcpu)
> > +static int handle_lpswe_y(struct kvm_vcpu *vcpu, bool lpswey)
> >  {
> >  	psw_t new_psw;
> >  	u64 addr;
> >  	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);
> > -
> > -	addr = kvm_s390_get_base_disp_s(vcpu, &ar);
> > -	if (addr & 7)
> > -		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
> > -	rc = read_guest(vcpu, addr, ar, &new_psw, sizeof(new_psw));
> > -	if (rc)
> > -		return kvm_s390_inject_prog_cond(vcpu, rc);
> > -	vcpu->arch.sie_block->gpsw = new_psw;
> > -	if (!is_valid_psw(&vcpu->arch.sie_block->gpsw))
> > -		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
> > -	return 0;
> > -}
> > -
> > -static int handle_lpswey(struct kvm_vcpu *vcpu)
> > -{
> > -	psw_t new_psw;
> > -	u64 addr;
> > -	int rc;
> > -	u8 ar;
> > -
> > -	vcpu->stat.instruction_lpswey++;
> > +	if (lpswey)
> > +		vcpu->stat.instruction_lpswey++;
> > +	else
> > +		vcpu->stat.instruction_lpswe++;
> >  
> > -	if (!test_kvm_facility(vcpu->kvm, 193))
> > +	if (lpswey && !test_kvm_facility(vcpu->kvm, 193))
> >  		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
> >  
> >  	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
> >  		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
> >  
> > -	addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
> > +	if (!lpswey)
> > +		addr = kvm_s390_get_base_disp_s(vcpu, &ar);
> > +	else
> > +		addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
> >  	if (addr & 7)
> >  		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);  
> 
> I'd prefer a helper function _do_lpswe_y_swap(struct kvm_vcpu *vcpu, gpa_t addr)
> 
> and then just
> 
> static int handle_lpswey(struct kvm_vcpu *vcpu)
> {
>         u64 addr;
>         u8 ar;
> 
>         vcpu->stat.instruction_lpswey++;
> 
>         if (!test_kvm_facility(vcpu->kvm, 193))
>                 return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
> 
>         addr = kvm_s390_get_base_disp_siy(vcpu, &ar);
> 	return _do_lpswe_y_swap(vcpu, addr);
> }
> 
> Makes it easier to read IMO because of the simpler control flow.

hmmm you have a point

> >  
> > @@ -1034,7 +1014,7 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
> >  	case 0xb1:
> >  		return handle_stfl(vcpu);
> >  	case 0xb2:
> > -		return handle_lpswe(vcpu);
> > +		return handle_lpswe_y(vcpu, false);
> >  	default:
> >  		return -EOPNOTSUPP;
> >  	}
> > @@ -1043,42 +1023,50 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
> >  static int handle_epsw(struct kvm_vcpu *vcpu)
> >  {
> >  	int reg1, reg2;
> > +	u64 *r1, *r2;
> >  
> >  	vcpu->stat.instruction_epsw++;
> >  
> >  	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
> > +	r1 = vcpu->run->s.regs.gprs + reg1;
> > +	r2 = vcpu->run->s.regs.gprs + reg2;
> >  
> >  	/* This basically extracts the mask half of the psw. */
> > -	vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000UL;
> > -	vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32;
> > -	if (reg2) {
> > -		vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000UL;
> > -		vcpu->run->s.regs.gprs[reg2] |=
> > -			vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffffUL;
> > -	}
> > +	*r1 = u64_replace_bits(*r1, vcpu->arch.sie_block->gpsw.mask >> 32, 0xffffffff);
> > +	if (reg2)
> > +		*r2 = u64_replace_bits(*r2, vcpu->arch.sie_block->gpsw.mask, 0xffffffff);  
> 
> LGTM although I don't hate the original implementation, which is very easy to understand
> compared to u64_replace_bits whose implementation is anything but.

yeah I agree

> It would be nice to make gprs a union, which I think should be fine from a backwards
> compatibility point of view. So:
> 
> struct kvm_sync_regs {
> 	__u64 prefix;	/* prefix register */
> 	union {
> 		__u64 gprs[16];	/* general purpose registers */
> 		struct { __u32 h; __u32 l} gprs32[16];
> 		struct { __u16 hh; __u16 hl; ...} gprs16[16];
> 		... 
> ...
> 
> But I don't expect you to do the refactor.
> You could of course also contribute documentation to bitfield.h :)

ehhhhhhh

> 
> >  	return 0;
> >  }  
> 
> [...]
> 
> >  static int handle_pfmf(struct kvm_vcpu *vcpu)
> >  {  
> 
> [...]
> 
> > -	if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
> > -		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT) {
> > -			vcpu->run->s.regs.gprs[reg2] = end;
> > -		} else {
> > -			vcpu->run->s.regs.gprs[reg2] &= ~0xffffffffUL;
> > -			end = kvm_s390_logical_to_effective(vcpu, end);
> > -			vcpu->run->s.regs.gprs[reg2] |= end;
> > -		}
> > +	if (r1.fsc) {
> > +		u64 *r2 = vcpu->run->s.regs.gprs + reg2;
> > +
> > +		end = kvm_s390_logical_to_effective(vcpu, end);
> > +		if (kvm_s390_is_amode_64(vcpu))
> > +			*r2 = u64_replace_bits(*r2, end, PAGE_MASK);
> > +		else
> > +			*r2 = u64_replace_bits(*r2, end, 0xfffff000);  
> 
> Same issue as above regarding the shift.
> 
> >  	}
> >  	return 0;
> >  }
> > @@ -1361,8 +1338,9 @@ int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu)
> >  	reg = reg1;
> >  	nr_regs = 0;
> >  	do {
> > -		vcpu->arch.sie_block->gcr[reg] &= 0xffffffff00000000ul;
> > -		vcpu->arch.sie_block->gcr[reg] |= ctl_array[nr_regs++];
> > +		u64 *cr = vcpu->arch.sie_block->gcr + reg;
> > +
> > +		*cr = u64_replace_bits(*cr, ctl_array[nr_regs++], 0xffffffff);
> >  		if (reg == reg3)
> >  			break;
> >  		reg = (reg + 1) % 16;
> > @@ -1489,7 +1467,7 @@ int kvm_s390_handle_eb(struct kvm_vcpu *vcpu)
> >  	case 0x62:
> >  		return handle_ri(vcpu);
> >  	case 0x71:
> > -		return handle_lpswey(vcpu);
> > +		return handle_lpswe_y(vcpu, true);
> >  	default:
> >  		return -EOPNOTSUPP;
> >  	}  
>