Message ID | 20221012205609.2811294-3-scgl@linux.ibm.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | KVM: s390: Extend MEM_OP ioctl by storage key checked cmpxchg | expand |
Hi Janis,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on 4fe89d07dcc2804c8b562f6c7896a45643d34b2f]
url: https://github.com/intel-lab-lkp/linux/commits/Janis-Schoetterl-Glausch/KVM-s390-Extend-MEM_OP-ioctl-by-storage-key-checked-cmpxchg/20221013-045733
base: 4fe89d07dcc2804c8b562f6c7896a45643d34b2f
config: s390-randconfig-s051-20221012
compiler: s390-linux-gcc (GCC) 12.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-39-gce1a6720-dirty
# https://github.com/intel-lab-lkp/linux/commit/427c3a07629c563c58a83fa1febb07d1345e7a9d
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Janis-Schoetterl-Glausch/KVM-s390-Extend-MEM_OP-ioctl-by-storage-key-checked-cmpxchg/20221013-045733
git checkout 427c3a07629c563c58a83fa1febb07d1345e7a9d
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=s390 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
sparse warnings: (new ones prefixed by >>)
arch/s390/kvm/gaccess.c: note: in included file (through include/linux/uaccess.h, include/linux/sched/task.h, include/linux/sched/signal.h, ...):
>> arch/s390/include/asm/uaccess.h:560:56: sparse: sparse: cast removes address space '__user' of expression
vim +/__user +560 arch/s390/include/asm/uaccess.h
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 459
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 460 /**
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 461 * cmpxchg_user_key_size() - cmpxchg with user space target, honoring storage keys
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 462 * @size: Size of the value being cmpxchg'ed, one of 1,2,4,8,16.
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 463 * @address: User space address of value to compare to *@old_p and exchange with
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 464 * @new. Must be aligned to @size.
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 465 * @old_p: Pointer to old value. Interpreted as a @size byte integer and compared
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 466 * to the content pointed to by @address in order to determine if the
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 467 * exchange occurs. The value read from @address is written back to *@old_p.
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 468 * @new: New value to place at @address, interpreted as a @size byte integer.
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 469 * @access_key: Access key to use for checking storage key protection.
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 470 *
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 471 * Perform a cmpxchg on a user space target, honoring storage key protection.
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 472 * @access_key alone determines how key checking is performed, neither
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 473 * storage-protection-override nor fetch-protection-override apply.
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 474 *
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 475 * Return: 0: successful exchange
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 476 * 1: exchange failed
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 477 * -EFAULT: @address not accessible or not naturally aligned
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 478 * -EINVAL: invalid @size
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 479 */
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 480 static __always_inline int cmpxchg_user_key_size(int size, void __user *address,
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 481 unsigned __int128 *old_p,
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 482 unsigned __int128 new, u8 access_key)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 483 {
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 484 union {
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 485 u32 word;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 486 u64 doubleword;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 487 } old;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 488 int ret = -EFAULT;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 489
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 490 /*
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 491 * The following assumes that:
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 492 * * the current psw key is the default key
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 493 * * no storage protection overrides are in effect
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 494 */
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 495 might_fault();
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 496 switch (size) {
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 497 case 16:
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 498 asm volatile(
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 499 "spka 0(%[access_key])\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 500 " sacf 256\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 501 "0: cdsg %[old],%[new],%[target]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 502 "1: ipm %[ret]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 503 " srl %[ret],28\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 504 "2: sacf 768\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 505 " spka %[default_key]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 506 EX_TABLE(0b, 2b) EX_TABLE(1b, 2b)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 507 : [old] "+d" (*old_p),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 508 [target] "+Q" (*(unsigned __int128 __user *)address),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 509 [ret] "+d" (ret)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 510 : [access_key] "a" (access_key << 4),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 511 [new] "d" (new),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 512 [default_key] "J" (PAGE_DEFAULT_KEY)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 513 : "cc"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 514 );
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 515 return ret;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 516 case 8:
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 517 old.doubleword = *old_p;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 518 asm volatile(
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 519 "spka 0(%[access_key])\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 520 " sacf 256\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 521 "0: csg %[old],%[new],%[target]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 522 "1: ipm %[ret]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 523 " srl %[ret],28\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 524 "2: sacf 768\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 525 " spka %[default_key]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 526 EX_TABLE(0b, 2b) EX_TABLE(1b, 2b)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 527 : [old] "+d" (old.doubleword),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 528 [target] "+Q" (*(u64 __user *)address),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 529 [ret] "+d" (ret)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 530 : [access_key] "a" (access_key << 4),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 531 [new] "d" ((u64)new),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 532 [default_key] "J" (PAGE_DEFAULT_KEY)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 533 : "cc"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 534 );
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 535 *old_p = old.doubleword;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 536 return ret;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 537 case 4:
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 538 old.word = *old_p;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 539 asm volatile(
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 540 "spka 0(%[access_key])\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 541 " sacf 256\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 542 "0: cs %[old],%[new],%[target]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 543 "1: ipm %[ret]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 544 " srl %[ret],28\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 545 "2: sacf 768\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 546 " spka %[default_key]\n"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 547 EX_TABLE(0b, 2b) EX_TABLE(1b, 2b)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 548 : [old] "+d" (old.word),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 549 [target] "+Q" (*(u32 __user *)address),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 550 [ret] "+d" (ret)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 551 : [access_key] "a" (access_key << 4),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 552 [new] "d" ((u32)new),
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 553 [default_key] "J" (PAGE_DEFAULT_KEY)
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 554 : "cc"
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 555 );
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 556 *old_p = old.word;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 557 return ret;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 558 case 2:
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 559 case 1:
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 @560 return __cmpxchg_user_key_small(size, (u64)address, old_p, new, access_key);
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 561 default:
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 562 return -EINVAL;
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 563 }
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 564 }
a5a09360ca0f69 Janis Schoetterl-Glausch 2022-10-12 565
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index eed0315a77a6..b28ef88eff41 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -588,6 +588,8 @@ struct kvm_s390_mem_op { struct { __u8 ar; /* the access register number */ __u8 key; /* access key, ignored if flag unset */ + __u8 pad1[6]; /* ignored */ + __u64 old_p; /* ignored if flag unset */ }; __u32 sida_offset; /* offset into the sida */ __u8 reserved[32]; /* ignored */ @@ -604,6 +606,9 @@ struct kvm_s390_mem_op { #define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0) #define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1) #define KVM_S390_MEMOP_F_SKEY_PROTECTION (1ULL << 2) +#define KVM_S390_MEMOP_F_CMPXCHG (1ULL << 3) +/* Non program exception return codes (pgm codes are 16 bit) */ +#define KVM_S390_MEMOP_R_NO_XCHG ((1 << 16) + 0) /* for KVM_INTERRUPT */ struct kvm_interrupt { diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h index 9408d6cc8e2c..a1cb66ae0995 100644 --- a/arch/s390/kvm/gaccess.h +++ b/arch/s390/kvm/gaccess.h @@ -206,6 +206,10 @@ int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data, unsigned long len, enum gacc_mode mode); +int cmpxchg_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, int len, + unsigned __int128 *old, + unsigned __int128 new, u8 access_key); + /** * write_guest_with_key - copy data from kernel space to guest space * @vcpu: virtual cpu diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 0243b6e38d36..d51cbae4f228 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1161,6 +1161,63 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, return rc; } +/** + * cmpxchg_guest_abs_with_key() - Perform cmpxchg on guest absolute address. + * @kvm: Virtual machine instance. + * @gpa: Absolute guest address of the location to be changed. + * @len: Operand length of the cmpxchg, required: 1 <= len <= 16. Providing a + * non power of two will result in failure. + * @old_p: Pointer to old value. If the location at @gpa contains this value, the + * exchange will succeed. After calling cmpxchg_guest_abs_with_key() *@old + * contains the value at @gpa before the attempt to exchange the value. + * @new: The value to place at @gpa. + * @access_key: The access key to use for the guest access. + * + * Atomically exchange the value at @gpa by @new, if it contains *@old. + * Honors storage keys. + * + * Return: * 0: successful exchange + * * 1: exchange unsuccessful + * * a program interruption code indicating the reason cmpxchg could + * not be attempted + * * -EINVAL: address misaligned or len not power of two + */ +int cmpxchg_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, int len, + unsigned __int128 *old_p, unsigned __int128 new, + u8 access_key) +{ + gfn_t gfn = gpa >> PAGE_SHIFT; + struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn); + bool writable; + hva_t hva; + int ret; + + if (!IS_ALIGNED(gpa, len)) + return -EINVAL; + + hva = gfn_to_hva_memslot_prot(slot, gfn, &writable); + if (kvm_is_error_hva(hva)) + return PGM_ADDRESSING; + /* + * Check if it's a read-only memslot, even though that cannot occur + * since those are unsupported. + * Don't try to actually handle that case. + */ + if (!writable) + return -EOPNOTSUPP; + + hva += offset_in_page(gpa); + ret = cmpxchg_user_key_size(len, (void __user *)hva, old_p, new, access_key); + mark_page_dirty_in_slot(kvm, slot, gfn); + /* + * Assume that the fault is caused by protection, either key protection + * or user page write protection. + */ + if (ret == -EFAULT) + ret = PGM_PROTECTION; + return ret; +} + /** * guest_translate_address_with_key - translate guest logical into guest absolute address * @vcpu: virtual cpu diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index b7ef0b71014d..88f0b83229f6 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -576,7 +576,6 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_S390_VCPU_RESETS: case KVM_CAP_SET_GUEST_DEBUG: case KVM_CAP_S390_DIAG318: - case KVM_CAP_S390_MEM_OP_EXTENSION: r = 1; break; case KVM_CAP_SET_GUEST_DEBUG2: @@ -590,6 +589,14 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_S390_MEM_OP: r = MEM_OP_MAX_SIZE; break; + case KVM_CAP_S390_MEM_OP_EXTENSION: + /* + * Flag bits indicating which extensions are supported. + * The first extension doesn't use a flag, but pretend it does, + * this way that can be changed in the future. + */ + r = 0x3; + break; case KVM_CAP_NR_VCPUS: case KVM_CAP_MAX_VCPUS: case KVM_CAP_MAX_VCPU_ID: @@ -2714,12 +2721,19 @@ static bool access_key_invalid(u8 access_key) static int kvm_s390_vm_mem_op(struct kvm *kvm, struct kvm_s390_mem_op *mop) { void __user *uaddr = (void __user *)mop->buf; + void __user *old_p = (void __user *)mop->old_p; + union { + unsigned __int128 quad; + char raw[sizeof(unsigned __int128)]; + } old = { .quad = 0}, new = { .quad = 0 }; + unsigned int off_in_quad = sizeof(unsigned __int128) - mop->size; u64 supported_flags; void *tmpbuf = NULL; int r, srcu_idx; supported_flags = KVM_S390_MEMOP_F_SKEY_PROTECTION - | KVM_S390_MEMOP_F_CHECK_ONLY; + | KVM_S390_MEMOP_F_CHECK_ONLY + | KVM_S390_MEMOP_F_CMPXCHG; if (mop->flags & ~supported_flags || !mop->size) return -EINVAL; if (mop->size > MEM_OP_MAX_SIZE) @@ -2741,6 +2755,15 @@ static int kvm_s390_vm_mem_op(struct kvm *kvm, struct kvm_s390_mem_op *mop) } else { mop->key = 0; } + if (mop->flags & KVM_S390_MEMOP_F_CMPXCHG) { + if (mop->size > sizeof(new)) + return -EINVAL; + /* off_in_quad has been validated */ + if (copy_from_user(&new.raw[off_in_quad], uaddr, mop->size)) + return -EFAULT; + if (copy_from_user(&old.raw[off_in_quad], old_p, mop->size)) + return -EFAULT; + } if (!(mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY)) { tmpbuf = vmalloc(mop->size); if (!tmpbuf) @@ -2771,6 +2794,14 @@ static int kvm_s390_vm_mem_op(struct kvm *kvm, struct kvm_s390_mem_op *mop) case KVM_S390_MEMOP_ABSOLUTE_WRITE: { if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) { r = check_gpa_range(kvm, mop->gaddr, mop->size, GACC_STORE, mop->key); + } else if (mop->flags & KVM_S390_MEMOP_F_CMPXCHG) { + r = cmpxchg_guest_abs_with_key(kvm, mop->gaddr, mop->size, + &old.quad, new.quad, mop->key); + if (r == 1) { + r = KVM_S390_MEMOP_R_NO_XCHG; + if (copy_to_user(old_p, &old.raw[off_in_quad], mop->size)) + r = -EFAULT; + } } else { if (copy_from_user(tmpbuf, uaddr, mop->size)) { r = -EFAULT;
User space can use the MEM_OP ioctl to make storage key checked reads and writes to the guest, however, it has no way of performing atomic, key checked, accesses to the guest. Extend the MEM_OP ioctl in order to allow for this, by adding a cmpxchg mode. For now, support this mode for absolute accesses only. This mode can be use, for example, to set the device-state-change indicator and the adapter-local-summary indicator atomically. Signed-off-by: Janis Schoetterl-Glausch <scgl@linux.ibm.com> --- include/uapi/linux/kvm.h | 5 ++++ arch/s390/kvm/gaccess.h | 4 +++ arch/s390/kvm/gaccess.c | 57 ++++++++++++++++++++++++++++++++++++++++ arch/s390/kvm/kvm-s390.c | 35 ++++++++++++++++++++++-- 4 files changed, 99 insertions(+), 2 deletions(-)