Skip to contentSkip to conversation

KVM: s390: Add support for protected VMs

RFC In review Janosch Frank 8 participants 213 messages Oct 24, 2019 – Nov 19, 2019 Page 1 of 2

Subsystems (5)
Documentation KVM s390 s390 MM +1 more subsystems
lore
Share & tools
lore

Janosch Frank

author maintainer
#0 lore raw

cover KVM: s390: Add support for protected VMs

Protected VMs (PVM) are KVM VMs, where KVM can't access the VM's state like guest memory and guest registers anymore. Instead the PVMs are mostly managed by a new entity called Ultravisor (UV), which provides an API, so KVM and the PV can request management actions.

PVMs are encrypted at rest and protected from hypervisor access while running. They switch from a normal operation into protected mode, so we can still use the standard boot process to load a encrypted blob and then move it into protected mode.

Rebooting is only possible by passing through the unprotected/normal mode and switching to protected again.

All patches are in the protvirt branch of the korg s390 kvm git.

Claudio will present the technology at his presentation at KVM Forum 2019.

Christian Borntraeger (1):
KVM: s390: protvirt: Add SCLP handling

Claudio Imbrenda (2):
KVM: s390: add missing include in gmap.h
KVM: s390: protvirt: Implement on-demand pinning

Janosch Frank (27):
DOCUMENTATION: protvirt: Protected virtual machine introduction
KVM: s390: protvirt: Add initial lifecycle handling
s390: KVM: Export PV handle to gmap
s390: UV: Add import and export to UV library
KVM: s390: protvirt: Secure memory is not mergeable
DOCUMENTATION: protvirt: Interrupt injection
KVM: s390: protvirt: Handle SE notification interceptions
DOCUMENTATION: protvirt: Instruction emulation
KVM: s390: protvirt: Handle spec exception loops
KVM: s390: protvirt: Add new gprs location handling
KVM: S390: protvirt: Introduce instruction data area bounce buffer
KVM: S390: protvirt: Instruction emulation
KVM: s390: protvirt: Make sure prefix is always protected
KVM: s390: protvirt: Write sthyi data to instruction data area
KVM: s390: protvirt: STSI handling
KVM: s390: protvirt: Only sync fmt4 registers
KVM: s390: protvirt: SIGP handling
KVM: s390: protvirt: Add program exception injection
KVM: s390: protvirt: Sync pv state
DOCUMENTATION: protvirt: Diag 308 IPL
KVM: s390: protvirt: Add diag 308 subcode 8 - 10 handling
KVM: s390: protvirt: UV calls diag308 0, 1
KVM: s390: Introduce VCPU reset IOCTL
KVM: s390: protvirt: Report CPU state to Ultravisor
KVM: s390: Fix cpu reset local IRQ clearing
KVM: s390: protvirt: Support cmd 5 operation state
KVM: s390: protvirt: Add UV debug trace

Michael Mueller (4):
KVM: s390: protvirt: Add interruption injection controls
KVM: s390: protvirt: Implement interruption injection
KVM: s390: protvirt: Add machine-check interruption injection controls
KVM: s390: protvirt: Implement machine-check interruption injection

Vasily Gorbik (3):
s390/protvirt: introduce host side setup s390/protvirt: add ultravisor initialization
s390: add (non)secure page access exceptions handlers

 .../admin-guide/kernel-parameters.txt         |   5 +
 Documentation/virtual/kvm/s390-pv-boot.txt    |  62 +++
 Documentation/virtual/kvm/s390-pv.txt         |  97 ++++
 arch/s390/boot/Makefile                       |   2 +-
 arch/s390/boot/uv.c                           |  20 +-
 arch/s390/include/asm/gmap.h                  |   4 +
 arch/s390/include/asm/kvm_host.h              | 103 +++-
 arch/s390/include/asm/uv.h                    | 255 +++++++++-
 arch/s390/include/uapi/asm/kvm.h              |   5 +-
 arch/s390/kernel/Makefile                     |   1 +
 arch/s390/kernel/pgm_check.S                  |   4 +-
 arch/s390/kernel/setup.c                      |   7 +-
 arch/s390/kernel/uv.c                         | 121 +++++
 arch/s390/kvm/Kconfig                         |   9 +
 arch/s390/kvm/Makefile                        |   2 +-
 arch/s390/kvm/diag.c                          |   7 +
 arch/s390/kvm/intercept.c                     |  91 +++-
 arch/s390/kvm/interrupt.c                     | 208 ++++++--
 arch/s390/kvm/kvm-s390.c                      | 476 +++++++++++++++---
 arch/s390/kvm/kvm-s390.h                      |  58 +++
 arch/s390/kvm/priv.c                          |   9 +-
 arch/s390/kvm/pv.c                            | 317 ++++++++++++
 arch/s390/mm/fault.c                          |  64 +++
 arch/s390/mm/gmap.c                           |  28 +-
 include/uapi/linux/kvm.h                      |  42 ++
 25 files changed, 1848 insertions(+), 149 deletions(-)
 create mode 100644 Documentation/virtual/kvm/s390-pv-boot.txt
 create mode 100644 Documentation/virtual/kvm/s390-pv.txt
 create mode 100644 arch/s390/kernel/uv.c
 create mode 100644 arch/s390/kvm/pv.c
Signature · 1 line
2.20.1
author maintainer
#1 lore raw

RFC 1/37 DOCUMENTATION: protvirt: Protected virtual machine introduction

Introduction to Protected VMs.

---
 Documentation/virtual/kvm/s390-pv.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/virtual/kvm/s390-pv.txt
Documentation/virtual/kvm/s390-pv.txt new file +23 -0
new file mode 100644
index 000000000000..86ed95f36759
@@ -0,0 +1,23 @@
+Ultravisor and Protected VMs
+===========================
+
+Summary:
+
+Protected VMs (PVM) are KVM VMs, where KVM can't access the VM's state
+like guest memory and guest registers anymore. Instead the PVMs are
+mostly managed by a new entity called Ultravisor (UV), which provides
+an API, so KVM and the PVM can request management actions.
+
+Each guest starts in the non-protected mode and then transitions into
+protected mode. On transition KVM registers the guest and its VCPUs
+with the Ultravisor and prepares everything for running it.
+
+The Ultravisor will secure and decrypt the guest's boot memory
+(i.e. kernel/initrd). It will safeguard state changes like VCPU
+starts/stops and injected interrupts while the guest is running.
+
+As access to the guest's state, like the SIE state description is
+normally needed to be able to run a VM, some changes have been made in
+SIE behavior and fields have different meaning for a PVM. SIE exits
+are minimized as much as possible to improve speed and reduce exposed
+guest state.
Signature · 1 line
2.20.1
Acked-by on v1 1/37
#2 lore raw

Re: [RFC 01/37] DOCUMENTATION: protvirt: Protected virtual machine introduction

Quoted 23 lines · Janosch Frank

Introduction to Protected VMs.

---
 Documentation/virtual/kvm/s390-pv.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/virtual/kvm/s390-pv.txt
Documentation/virtual/kvm/s390-pv.txt new file +9 -0
new file mode 100644
index 000000000000..86ed95f36759
@@ -0,0 +1,23 @@
+Ultravisor and Protected VMs
+===========================
+
+Summary:
+
+Protected VMs (PVM) are KVM VMs, where KVM can't access the VM's state
+like guest memory and guest registers anymore. Instead the PVMs are
+mostly managed by a new entity called Ultravisor (UV), which provides
+an API, so KVM and the PVM can request management actions.

Documentation/virtual/kvm/s390-pv.txt line 19

Quoted 10 lines
Documentation/virtual/kvm/s390-pv.txt new file +10 -0
+
+Each guest starts in the non-protected mode and then transitions into
+protected mode. On transition KVM registers the guest and its VCPUs
+with the Ultravisor and prepares everything for running it.
+
+The Ultravisor will secure and decrypt the guest's boot memory
+(i.e. kernel/initrd). It will safeguard state changes like VCPU
+starts/stops and injected interrupts while the guest is running.
+
+As access to the guest's state, like the SIE state description is

not a native speaker, but do we need a , /here\ ?

Documentation/virtual/kvm/s390-pv.txt line 23

Quoted 6 lines
Documentation/virtual/kvm/s390-pv.txt new file +4 -0
+normally needed to be able to run a VM, some changes have been made in
 
+SIE behavior and fields have different meaning for a PVM. SIE exits
+are minimized as much as possible to improve speed and reduce exposed
+guest state.

After review we could merge all documentation patches into one, if we want.

#3 lore raw

Re: [RFC 01/37] DOCUMENTATION: protvirt: Protected virtual machine introduction

In reply to Janosch Frank

Documentation/virtual/kvm/s390-pv.txt

Quoted 13 lines · On Thu, 24 Oct 2019 07:40:23 -0400 Janosch Frank <[email protected]> wrote:

Introduction to Protected VMs.

---
 Documentation/virtual/kvm/s390-pv.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/virtual/kvm/s390-pv.txt
Documentation/virtual/kvm/s390-pv.txt new file +0 -0
new file mode 100644
index 000000000000..86ed95f36759

This should be under /virt/, I think. Also, maybe start out with RST already for new files?

Documentation/virtual/kvm/s390-pv.txt line 7

Quoted 8 lines
Documentation/virtual/kvm/s390-pv.txt new file +7 -0
@@ -0,0 +1,23 @@
+Ultravisor and Protected VMs
+===========================
+
+Summary:
+
+Protected VMs (PVM) are KVM VMs, where KVM can't access the VM's state
+like guest memory and guest registers anymore. Instead the PVMs are

s/Instead/Instead,/

Documentation/virtual/kvm/s390-pv.txt line 9

Quoted 2 lines
Documentation/virtual/kvm/s390-pv.txt new file +2 -0
+mostly managed by a new entity called Ultravisor (UV), which provides
+an API, so KVM and the PVM can request management actions.

Hm...

"The UV provides an API (both for guests and hypervisors), where PVMs and KVM can request management actions." ?

Documentation/virtual/kvm/s390-pv.txt line 11

Quoted 2 lines
Documentation/virtual/kvm/s390-pv.txt new file +2 -0
+
+Each guest starts in the non-protected mode and then transitions into

"and then may make a request to transition into protected mode" ?

Documentation/virtual/kvm/s390-pv.txt line 19

Quoted 8 lines
Documentation/virtual/kvm/s390-pv.txt new file +8 -0
+protected mode. On transition KVM registers the guest and its VCPUs
+with the Ultravisor and prepares everything for running it.
+
+The Ultravisor will secure and decrypt the guest's boot memory
+(i.e. kernel/initrd). It will safeguard state changes like VCPU
+starts/stops and injected interrupts while the guest is running.
+
+As access to the guest's state, like the SIE state description is

"such as the SIE state description," ?

Quoted 4 lines
Documentation/virtual/kvm/s390-pv.txt new file +4 -0
+normally needed to be able to run a VM, some changes have been made in
+SIE behavior and fields have different meaning for a PVM. SIE exits
+are minimized as much as possible to improve speed and reduce exposed
+guest state.
author maintainer
#4 lore raw

Re: [RFC 01/37] DOCUMENTATION: protvirt: Protected virtual machine introduction

Quoted 20 lines · Cornelia Huck

Documentation/virtual/kvm/s390-pv.txt

Quoted 13 lines · On Thu, 24 Oct 2019 07:40:23 -0400 Janosch Frank <[email protected]> wrote:

Introduction to Protected VMs.

---
 Documentation/virtual/kvm/s390-pv.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/virtual/kvm/s390-pv.txt
Documentation/virtual/kvm/s390-pv.txt new file +0 -0
new file mode 100644
index 000000000000..86ed95f36759

This should be under /virt/, I think. Also, maybe start out with RST already for new files?

Quoted 10 lines

Documentation/virtual/kvm/s390-pv.txt line 7

Quoted 8 lines
Documentation/virtual/kvm/s390-pv.txt new file +7 -0
@@ -0,0 +1,23 @@
+Ultravisor and Protected VMs
+===========================
+
+Summary:
+
+Protected VMs (PVM) are KVM VMs, where KVM can't access the VM's state
+like guest memory and guest registers anymore. Instead the PVMs are

s/Instead/Instead,/

Fixed

Quoted 8 lines

Documentation/virtual/kvm/s390-pv.txt line 9

Quoted 3 lines
Documentation/virtual/kvm/s390-pv.txt new file +2 -0
+mostly managed by a new entity called Ultravisor (UV), which provides
+an API, so KVM and the PVM can request management actions.

Hm...

"The UV provides an API (both for guests and hypervisors), where PVMs and KVM can request management actions." ?

I applied your proposal, but removed the part in the brace, as it is obvious from the words that follow.

Quoted 5 lines

Documentation/virtual/kvm/s390-pv.txt line 11

Quoted 3 lines
Documentation/virtual/kvm/s390-pv.txt new file +2 -0
+
+Each guest starts in the non-protected mode and then transitions into

"and then may make a request to transition into protected mode" ?

Sure

Quoted 17 lines

Documentation/virtual/kvm/s390-pv.txt line 19

Quoted 9 lines
Documentation/virtual/kvm/s390-pv.txt new file +8 -0
+protected mode. On transition KVM registers the guest and its VCPUs
+with the Ultravisor and prepares everything for running it.
+
+The Ultravisor will secure and decrypt the guest's boot memory
+(i.e. kernel/initrd). It will safeguard state changes like VCPU
+starts/stops and injected interrupts while the guest is running.
+
+As access to the guest's state, like the SIE state description is

"such as the SIE state description," ?

Quoted 4 lines
Documentation/virtual/kvm/s390-pv.txt new file +4 -0
+normally needed to be able to run a VM, some changes have been made in
+SIE behavior and fields have different meaning for a PVM. SIE exits
+are minimized as much as possible to improve speed and reduce exposed
+guest state.
author maintainer
#5 lore raw

RFC 2/37 s390/protvirt: introduce host side setup

In reply to Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 arch/s390/boot/Makefile                       |  2 +-
 arch/s390/boot/uv.c                           | 20 +++++++-
 arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
 arch/s390/kernel/Makefile                     |  1 +
 arch/s390/kernel/setup.c                      |  4 --
 arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
 arch/s390/kvm/Kconfig                         |  9 ++++
 8 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/kernel/uv.c
Documentation/admin-guide/kernel-parameters.txt +5 -0
index c7ac2f3ac99f..aa22e36b3105 100644
@@ -3693,6 +3693,11 @@
 			before loading.
 			See Documentation/admin-guide/blockdev/ramdisk.rst.
 
+	prot_virt=	[S390] enable hosting protected virtual machines
+			isolated from the hypervisor (if hardware supports
+			that).
+			Format: <bool>
+
 	psi=		[KNL] Enable or disable pressure stall information
 			tracking.
 			Format: <bool>
arch/s390/boot/Makefile +1 -1
index e2c47d3a1c89..82247e71617a 100644
@@ -37,7 +37,7 @@ CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
 obj-y	:= head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o
 obj-y	+= string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o
 obj-y	+= version.o pgm_check_info.o ctype.o text_dma.o
-obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST)	+= uv.o
+obj-$(findstring y, $(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) $(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST))	+= uv.o
 obj-$(CONFIG_RELOCATABLE)	+= machine_kexec_reloc.o
 obj-$(CONFIG_RANDOMIZE_BASE)	+= kaslr.o
 targets	:= bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y)
arch/s390/boot/uv.c +19 -1
index ed007f4a6444..88cf8825d169 100644
@@ -3,7 +3,12 @@
 #include <asm/facility.h>
 #include <asm/sections.h>
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 int __bootdata_preserved(prot_virt_guest);
+#endif
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+struct uv_info __bootdata_preserved(uv_info);
+#endif
 
 void uv_query_info(void)
 {
@@ -18,7 +23,20 @@ void uv_query_info(void)
 	if (uv_call(0, (uint64_t)&uvcb))
 		return;
 
-	if (test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)) {
+		memcpy(uv_info.inst_calls_list, uvcb.inst_calls_list, sizeof(uv_info.inst_calls_list));
+		uv_info.uv_base_stor_len = uvcb.uv_base_stor_len;
+		uv_info.guest_base_stor_len = uvcb.conf_base_phys_stor_len;
+		uv_info.guest_virt_base_stor_len = uvcb.conf_base_virt_stor_len;
+		uv_info.guest_virt_var_stor_len = uvcb.conf_virt_var_stor_len;
+		uv_info.guest_cpu_stor_len = uvcb.cpu_stor_len;
+		uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE);
+		uv_info.max_num_sec_conf = uvcb.max_num_sec_conf;
+		uv_info.max_guest_cpus = uvcb.max_guest_cpus;
+	}
+
+	if (IS_ENABLED(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) &&
+	    test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
 	    test_bit_inv(BIT_UVC_CMD_REMOVE_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list))
 		prot_virt_guest = 1;
 }
arch/s390/include/asm/uv.h +43 -3

70 lines not on this page · raw message

arch/s390/kernel/Makefile +1 -0
index 7edbbcd8228a..fe4fe475f526 100644
@@ -78,6 +78,7 @@ obj-$(CONFIG_PERF_EVENTS)	+= perf_cpum_cf_events.o perf_regs.o
 obj-$(CONFIG_PERF_EVENTS)	+= perf_cpum_cf_diag.o
 
 obj-$(CONFIG_TRACEPOINTS)	+= trace.o
+obj-$(findstring y, $(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) $(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST))	+= uv.o
 
 # vdso
 obj-y				+= vdso64/
arch/s390/kernel/setup.c +0 -4
index 3ff291bc63b7..f36370f8af38 100644
@@ -92,10 +92,6 @@ char elf_platform[ELF_PLATFORM_SIZE];
 
 unsigned long int_hwcap = 0;
 
-#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
-int __bootdata_preserved(prot_virt_guest);
-#endif
-
 int __bootdata(noexec_disabled);
 int __bootdata(memory_end_set);
 unsigned long __bootdata(memory_end);
arch/s390/kernel/uv.c new file +48 -0

48 lines not on this page · raw message

arch/s390/kvm/Kconfig +9 -0
index d3db3d7ed077..652b36f0efca 100644
@@ -55,6 +55,15 @@ config KVM_S390_UCONTROL
 
 	  If unsure, say N.
 
+config KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	bool "Protected guests execution support"
+	depends on KVM
+	---help---
+	  Support hosting protected virtual machines isolated from the
+	  hypervisor.
+
+	  If unsure, say Y.
+
 # OK, it's a little counter-intuitive to do this, but it puts it neatly under
 # the virtualization menu.
 source "drivers/vhost/Kconfig"
Signature · 1 line
2.20.1
#6 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 29 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
  .../admin-guide/kernel-parameters.txt         |  5 ++
  arch/s390/boot/Makefile                       |  2 +-
  arch/s390/boot/uv.c                           | 20 +++++++-
  arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
  arch/s390/kernel/Makefile                     |  1 +
  arch/s390/kernel/setup.c                      |  4 --
  arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
  arch/s390/kvm/Kconfig                         |  9 ++++
  8 files changed, 126 insertions(+), 9 deletions(-)

create mode 100644 arch/s390/kernel/uv.c

Documentation/admin-guide/kernel-parameters.txt +0 -0
index c7ac2f3ac99f..aa22e36b3105 100644

Documentation/admin-guide/kernel-parameters.txt line 3699

Quoted 7 lines
Documentation/admin-guide/kernel-parameters.txt +4 -0
@@ -3693,6 +3693,11 @@
  			before loading.
  			See Documentation/admin-guide/blockdev/ramdisk.rst.
 
+	prot_virt=	[S390] enable hosting protected virtual machines
+			isolated from the hypervisor (if hardware supports
+			that).
+			Format: <bool>

Isn't that a virt driver detail that should come in via KVM module parameters? I don't see quite yet why this has to be a kernel parameter (that can be changed at runtime).

Signature · 3 lines
Thanks,

David / dhildenb
#7 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 33 lines · David Hildenbrand
Quoted 32 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
   .../admin-guide/kernel-parameters.txt         |  5 ++
   arch/s390/boot/Makefile                       |  2 +-
   arch/s390/boot/uv.c                           | 20 +++++++-
   arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
   arch/s390/kernel/Makefile                     |  1 +
   arch/s390/kernel/setup.c                      |  4 --
   arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
   arch/s390/kvm/Kconfig                         |  9 ++++
   8 files changed, 126 insertions(+), 9 deletions(-)

create mode 100644 arch/s390/kernel/uv.c

Documentation/admin-guide/kernel-parameters.txt +0 -0
index c7ac2f3ac99f..aa22e36b3105 100644
@@ -3693,6 +3693,11 @@
   			before loading.
   			See Documentation/admin-guide/blockdev/ramdisk.rst.
 
Quoted 9 lines

Documentation/admin-guide/kernel-parameters.txt line 3699

Quoted 4 lines
Documentation/admin-guide/kernel-parameters.txt +4 -0
+	prot_virt=	[S390] enable hosting protected virtual machines
+			isolated from the hypervisor (if hardware supports
+			that).
+			Format: <bool>

Isn't that a virt driver detail that should come in via KVM module parameters? I don't see quite yet why this has to be a kernel parameter (that can be changed at runtime).

I was confused by "runtime" in "which controls if the kernel protected VMs support is enabled at runtime"

So this can't be changed at runtime. Can you clarify why kvm can't initialize that when loaded and why we need a kernel parameter?

Signature · 3 lines
Thanks,

David / dhildenb
#8 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 43 lines · David Hildenbrand
Quoted 25 lines · David Hildenbrand
Quoted 24 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
   .../admin-guide/kernel-parameters.txt         |  5 ++ arch/s390/boot/Makefile                       |  2 +- arch/s390/boot/uv.c                           | 20 +++++++- arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++-- arch/s390/kernel/Makefile                     |  1 + arch/s390/kernel/setup.c                      |  4 -- arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++ arch/s390/kvm/Kconfig                         |  9 ++++

   8 files changed, 126 insertions(+), 9 deletions(-)

   create mode 100644 arch/s390/kernel/uv.c

Quoted 16 lines
Quoted 11 lines
Documentation/admin-guide/kernel-parameters.txt +0 -0
index c7ac2f3ac99f..aa22e36b3105 100644
@@ -3693,6 +3693,11 @@

               before loading.
               See Documentation/admin-guide/blockdev/ramdisk.rst. +    prot_virt=    [S390] enable hosting protected virtual machines +            isolated from the hypervisor (if hardware supports +            that).
+            Format: <bool>

Isn't that a virt driver detail that should come in via KVM module parameters? I don't see quite yet why this has to be a kernel parameter (that can be changed at runtime).

Quoted 3 lines

I was confused by "runtime" in "which controls if the kernel protected VMs support is enabled at runtime"

So this can't be changed at runtime. Can you clarify why kvm can't initialize that when loaded and why we need a kernel parameter?

We have to do the opt-in very early for several reasons:
- we have to donate a potentially largish contiguous (in real) range of memory to the ultravisor
- The opt-in will also disable some features in the host that could affect guest integrity (e.g. time sync via STP to avoid the host messing with the guest time stepping). Linux is not happy when you remove features at a later point in time

#9 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 46 lines · Christian Borntraeger
Quoted 43 lines · David Hildenbrand
Quoted 41 lines · David Hildenbrand
Quoted 29 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
    .../admin-guide/kernel-parameters.txt         |  5 ++
    arch/s390/boot/Makefile                       |  2 +-
    arch/s390/boot/uv.c                           | 20 +++++++-
    arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
    arch/s390/kernel/Makefile                     |  1 +
    arch/s390/kernel/setup.c                      |  4 --
    arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
    arch/s390/kvm/Kconfig                         |  9 ++++
    8 files changed, 126 insertions(+), 9 deletions(-)

   create mode 100644 arch/s390/kernel/uv.c

Documentation/admin-guide/kernel-parameters.txt +0 -0
index c7ac2f3ac99f..aa22e36b3105 100644

Documentation/admin-guide/kernel-parameters.txt line 3698

Quoted 6 lines
Documentation/admin-guide/kernel-parameters.txt +3 -0
@@ -3693,6 +3693,11 @@
                before loading.
                See Documentation/admin-guide/blockdev/ramdisk.rst.
    +    prot_virt=    [S390] enable hosting protected virtual machines
+            isolated from the hypervisor (if hardware supports
+            that).
+            Format: <bool>

Isn't that a virt driver detail that should come in via KVM module parameters? I don't see quite yet why this has to be a kernel parameter (that can be changed at runtime).

Quoted 6 lines
Quoted 3 lines

I was confused by "runtime" in "which controls if the kernel protected VMs support is enabled at runtime"

So this can't be changed at runtime. Can you clarify why kvm can't initialize that when loaded and why we need a kernel parameter?

We have to do the opt-in very early for several reasons:
- we have to donate a potentially largish contiguous (in real) range of memory to the ultravisor

If you'd be using CMA (or alloc_contig_pages() with less guarantees) you could be making good use of the memory until you actually start an encrypted guest.

I can see that using the memblock allocator early is easier. But you waste "largish ... range of memory" even if you never run VMs.

Maybe something to work on in the future.

Quoted 3 lines

- The opt-in will also disable some features in the host that could affect guest integrity (e.g. time sync via STP to avoid the host messing with the guest time stepping). Linux is not happy when you remove features at a later point in time

At least disabling STP shouldn't be a real issue if I'm not wrong (maybe I am). But there seem to be more features.

(when I saw "prot_virt" it felt like somebody is using a big hammer for small nails (e.g., compared to "stp=off").)

Can you guys add these details to the patch description?

Signature · 3 lines
Thanks,

David / dhildenb
maintainer
#10 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 56 lines · On Thu, 24 Oct 2019 17:52:31 +0200 David Hildenbrand <[email protected]> wrote:
Quoted 55 lines · Christian Borntraeger
Quoted 51 lines · David Hildenbrand
Quoted 35 lines · David Hildenbrand
Quoted 34 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
    .../admin-guide/kernel-parameters.txt         |  5 ++
    arch/s390/boot/Makefile                       |  2 +-
    arch/s390/boot/uv.c                           | 20 +++++++-
    arch/s390/include/asm/uv.h                    | 46
++++++++++++++++-- arch/s390/kernel/Makefile
|  1 + arch/s390/kernel/setup.c                      |  4 --
    arch/s390/kernel/uv.c                         | 48

+++++++++++++++++++
arch/s390/kvm/Kconfig                         |  9 ++++ 8 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 arch/s390/kernel/uv.c

(quoted diff) +0 -0

b/Documentation/admin-guide/kernel-parameters.txt index c7ac2f3ac99f..aa22e36b3105 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3693,6 +3693,11 @@ before loading.
               See
Documentation/admin-guide/blockdev/ramdisk.rst.
   +    prot_virt=    [S390] enable hosting protected virtual

Quoted 9 lines
Quoted 4 lines

machines
+            isolated from the hypervisor (if hardware supports +            that).
+            Format: <bool>

Isn't that a virt driver detail that should come in via KVM module parameters? I don't see quite yet why this has to be a kernel parameter (that can be changed at runtime).

I was confused by "runtime" in "which controls if the kernel protected VMs support is enabled at runtime"

So this can't be changed at runtime. Can you clarify why kvm can't initialize that when loaded and why we need a kernel parameter?

Quoted 7 lines
Quoted 3 lines

We have to do the opt-in very early for several reasons:
- we have to donate a potentially largish contiguous (in real) range of memory to the ultravisor

If you'd be using CMA (or alloc_contig_pages() with less guarantees) you could be making good use of the memory until you actually start an encrypted guest.

no, the memory needs to be allocated before any other interaction with the ultravisor is attempted, and the size depends on the size of the _host_ memory. it can be a very substantial amount of memory, and thus it's very likely to fail unless it's done very early at boot time.

Quoted 3 lines

I can see that using the memblock allocator early is easier. But you waste "largish ... range of memory" even if you never run VMs.

this is inevitable

Quoted 2 lines

Maybe something to work on in the future.

honestly unlikely. which is why protected virtualization needs to be enabled explicitly

Quoted 6 lines
Quoted 5 lines

- The opt-in will also disable some features in the host that could affect guest integrity (e.g. time sync via STP to avoid the host messing with the guest time stepping). Linux is not happy when you remove features at a later point in time

Quoted 7 lines

At least disabling STP shouldn't be a real issue if I'm not wrong (maybe I am). But there seem to be more features.

(when I saw "prot_virt" it felt like somebody is using a big hammer for small nails (e.g., compared to "stp=off").)

Can you guys add these details to the patch description?

yeah, probably a good idea

#11 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 63 lines · Claudio Imbrenda
Quoted 60 lines · On Thu, 24 Oct 2019 17:52:31 +0200 David Hildenbrand <[email protected]> wrote:
Quoted 58 lines · Christian Borntraeger
Quoted 46 lines · David Hildenbrand
Quoted 35 lines · David Hildenbrand
Quoted 34 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
     .../admin-guide/kernel-parameters.txt         |  5 ++
     arch/s390/boot/Makefile                       |  2 +-
     arch/s390/boot/uv.c                           | 20 +++++++-
     arch/s390/include/asm/uv.h                    | 46
++++++++++++++++-- arch/s390/kernel/Makefile
|  1 + arch/s390/kernel/setup.c                      |  4 --
     arch/s390/kernel/uv.c                         | 48

+++++++++++++++++++
arch/s390/kvm/Kconfig                         |  9 ++++ 8 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 arch/s390/kernel/uv.c

(quoted diff) +0 -0

b/Documentation/admin-guide/kernel-parameters.txt index c7ac2f3ac99f..aa22e36b3105 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3693,6 +3693,11 @@ before loading.
               See
Documentation/admin-guide/blockdev/ramdisk.rst.
   +    prot_virt=    [S390] enable hosting protected virtual

Quoted 9 lines
Quoted 4 lines

machines
+            isolated from the hypervisor (if hardware supports +            that).
+            Format: <bool>

Isn't that a virt driver detail that should come in via KVM module parameters? I don't see quite yet why this has to be a kernel parameter (that can be changed at runtime).

Quoted 5 lines

I was confused by "runtime" in "which controls if the kernel protected VMs support is enabled at runtime"

So this can't be changed at runtime. Can you clarify why kvm can't initialize that when loaded and why we need a kernel parameter?

We have to do the opt-in very early for several reasons:
- we have to donate a potentially largish contiguous (in real) range of memory to the ultravisor

Quoted 6 lines
Quoted 3 lines

If you'd be using CMA (or alloc_contig_pages() with less guarantees) you could be making good use of the memory until you actually start an encrypted guest.

no, the memory needs to be allocated before any other interaction with the ultravisor is attempted, and the size depends on the size of the

I fail to see why you need interaction with the UV before you actually start/create an encrypted guest (IOW why you can't defer uv_init()) - but I am not past "[RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable" yet and ...

Quoted 2 lines

_host_ memory. it can be a very substantial amount of memory, and thus it's very likely to fail unless it's done very early at boot time.

... I guess you could still do that via CMA ... but it doesn't really matter right now :) I understood the rational.

Quoted 10 lines
Quoted 10 lines
Quoted 6 lines

- The opt-in will also disable some features in the host that could affect guest integrity (e.g. time sync via STP to avoid the host messing with the guest time stepping). Linux is not happy when you remove features at a later point in time

At least disabling STP shouldn't be a real issue if I'm not wrong (maybe I am). But there seem to be more features.

Quoted 7 lines
Quoted 4 lines

(when I saw "prot_virt" it felt like somebody is using a big hammer for small nails (e.g., compared to "stp=off").)

Can you guys add these details to the patch description?

yeah, probably a good idea

If a patch explains why it does something and not only what it does usually makes me ask less stupid questions :)

Signature · 3 lines
Thanks,

David / dhildenb
#12 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

In reply to Janosch Frank
Quoted 23 lines · On Thu, 24 Oct 2019 07:40:24 -0400 Janosch Frank <[email protected]> wrote:

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 arch/s390/boot/Makefile                       |  2 +-
 arch/s390/boot/uv.c                           | 20 +++++++-
 arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
 arch/s390/kernel/Makefile                     |  1 +
 arch/s390/kernel/setup.c                      |  4 --
 arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
 arch/s390/kvm/Kconfig                         |  9 ++++
 8 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/kernel/uv.c

(...)

Quoted 5 lines
arch/s390/kvm/Kconfig +0 -0
index d3db3d7ed077..652b36f0efca 100644

arch/s390/kvm/Kconfig config KVM_S390_UCONTROL · line 63

Quoted 9 lines
arch/s390/kvm/Kconfig +6 -0
@@ -55,6 +55,15 @@ config KVM_S390_UCONTROL
 
 	  If unsure, say N.
 
+config KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	bool "Protected guests execution support"
+	depends on KVM
+	---help---
+	  Support hosting protected virtual machines isolated from the
+	  hypervisor.

I'm currently in the process of glancing across this patch set (won't be able to get around to properly looking at it until next week the earliest), so just a very high level comment:

I think there's not enough information in here to allow someone configuring the kernel to decide what this is and if it would be useful to them. This should probably be at least point to some document giving some more details. Also, can you add a sentence where this feature is actually expected to be available?

arch/s390/kvm/Kconfig config KVM_S390_UCONTROL · line 65

Quoted 2 lines
arch/s390/kvm/Kconfig +2 -0
+
+	  If unsure, say Y.

Is 'Y' really the safe choice here? AFAICS, this is introducing new code and not only trying to call new interfaces, if available. Is there any drawback to enabling this on a kernel that won't run on a platform supporting this feature? Is this supposed to be a common setup?

Quoted 4 lines
arch/s390/kvm/Kconfig +1 -0
+
 # OK, it's a little counter-intuitive to do this, but it puts it neatly under
 # the virtualization menu.
 source "drivers/vhost/Kconfig"
#13 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 6 lines · Cornelia Huck

I think there's not enough information in here to allow someone configuring the kernel to decide what this is and if it would be useful to them. This should probably be at least point to some document giving some more details. Also, can you add a sentence where this feature is actually expected to be available?

Quoted 7 lines

arch/s390/kvm/Kconfig config KVM_S390_UCONTROL · line 65

Quoted 2 lines
arch/s390/kvm/Kconfig +2 -0
+
+	  If unsure, say Y.

Is 'Y' really the safe choice here? AFAICS, this is introducing new code and not only trying to call new interfaces, if available. Is there any drawback to enabling this on a kernel that won't run on a platform supporting this feature? Is this supposed to be a common setup?

I would expect that this is enabled on distributions in the future. So I think we should actually get rid of this Kconfig and always enable that code. We just must pay attention to fence of all the new code if the user does not opt in. (e.g. prot_virt=0). We need to do that anyway and not hanving a Kconfig forces us to be extra careful.

Reviewed-by on v1 2/37
#14 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

In reply to Janosch Frank
Quoted 261 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 arch/s390/boot/Makefile                       |  2 +-
 arch/s390/boot/uv.c                           | 20 +++++++-
 arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
 arch/s390/kernel/Makefile                     |  1 +
 arch/s390/kernel/setup.c                      |  4 --
 arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
 arch/s390/kvm/Kconfig                         |  9 ++++
 8 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/kernel/uv.c
Documentation/admin-guide/kernel-parameters.txt +5 -0
index c7ac2f3ac99f..aa22e36b3105 100644
@@ -3693,6 +3693,11 @@
 			before loading.
 			See Documentation/admin-guide/blockdev/ramdisk.rst.
 
+	prot_virt=	[S390] enable hosting protected virtual machines
+			isolated from the hypervisor (if hardware supports
+			that).
+			Format: <bool>
+
 	psi=		[KNL] Enable or disable pressure stall information
 			tracking.
 			Format: <bool>
arch/s390/boot/Makefile +1 -1
index e2c47d3a1c89..82247e71617a 100644
@@ -37,7 +37,7 @@ CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
 obj-y	:= head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o
 obj-y	+= string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o
 obj-y	+= version.o pgm_check_info.o ctype.o text_dma.o
-obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST)	+= uv.o
+obj-$(findstring y, $(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) $(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST))	+= uv.o
 obj-$(CONFIG_RELOCATABLE)	+= machine_kexec_reloc.o
 obj-$(CONFIG_RANDOMIZE_BASE)	+= kaslr.o
 targets	:= bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y)
arch/s390/boot/uv.c +19 -1
index ed007f4a6444..88cf8825d169 100644
@@ -3,7 +3,12 @@
 #include <asm/facility.h>
 #include <asm/sections.h>
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 int __bootdata_preserved(prot_virt_guest);
+#endif
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+struct uv_info __bootdata_preserved(uv_info);
+#endif
 
 void uv_query_info(void)
 {
@@ -18,7 +23,20 @@ void uv_query_info(void)
 	if (uv_call(0, (uint64_t)&uvcb))
 		return;
 
-	if (test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)) {
+		memcpy(uv_info.inst_calls_list, uvcb.inst_calls_list, sizeof(uv_info.inst_calls_list));
+		uv_info.uv_base_stor_len = uvcb.uv_base_stor_len;
+		uv_info.guest_base_stor_len = uvcb.conf_base_phys_stor_len;
+		uv_info.guest_virt_base_stor_len = uvcb.conf_base_virt_stor_len;
+		uv_info.guest_virt_var_stor_len = uvcb.conf_virt_var_stor_len;
+		uv_info.guest_cpu_stor_len = uvcb.cpu_stor_len;
+		uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE);
+		uv_info.max_num_sec_conf = uvcb.max_num_sec_conf;
+		uv_info.max_guest_cpus = uvcb.max_guest_cpus;
+	}
+
+	if (IS_ENABLED(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) &&
+	    test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
 	    test_bit_inv(BIT_UVC_CMD_REMOVE_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list))
 		prot_virt_guest = 1;
 }
arch/s390/include/asm/uv.h +43 -3
index ef3c00b049ab..6db1bc495e67 100644
@@ -44,7 +44,19 @@ struct uv_cb_qui {
 	struct uv_cb_header header;
 	u64 reserved08;
 	u64 inst_calls_list[4];
-	u64 reserved30[15];
+	u64 reserved30[2];
+	u64 uv_base_stor_len;
+	u64 reserved48;
+	u64 conf_base_phys_stor_len;
+	u64 conf_base_virt_stor_len;
+	u64 conf_virt_var_stor_len;
+	u64 cpu_stor_len;
+	u32 reserved68[3];
+	u32 max_num_sec_conf;
+	u64 max_guest_stor_addr;
+	u8  reserved80[150-128];
+	u16 max_guest_cpus;
+	u64 reserved98;
 } __packed __aligned(8);
 
 struct uv_cb_share {
@@ -69,9 +81,21 @@ static inline int uv_call(unsigned long r1, unsigned long r2)
 	return cc;
 }
 
-#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
+struct uv_info {
+	unsigned long inst_calls_list[4];
+	unsigned long uv_base_stor_len;
+	unsigned long guest_base_stor_len;
+	unsigned long guest_virt_base_stor_len;
+	unsigned long guest_virt_var_stor_len;
+	unsigned long guest_cpu_stor_len;
+	unsigned long max_sec_stor_addr;
+	unsigned int max_num_sec_conf;
+	unsigned short max_guest_cpus;
+};
+extern struct uv_info uv_info;
 extern int prot_virt_guest;
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 static inline int is_prot_virt_guest(void)
 {
 	return prot_virt_guest;
@@ -121,11 +145,27 @@ static inline int uv_remove_shared(unsigned long addr)
 	return share(addr, UVC_CMD_REMOVE_SHARED_ACCESS);
 }
 
-void uv_query_info(void);
 #else
 #define is_prot_virt_guest() 0
 static inline int uv_set_shared(unsigned long addr) { return 0; }
 static inline int uv_remove_shared(unsigned long addr) { return 0; }
+#endif
+
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+extern int prot_virt_host;
+
+static inline int is_prot_virt_host(void)
+{
+	return prot_virt_host;
+}
+#else
+#define is_prot_virt_host() 0
+#endif
+
+#if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
+	defined(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)
+void uv_query_info(void);
+#else
 static inline void uv_query_info(void) {}
 #endif
 
arch/s390/kernel/Makefile +1 -0
index 7edbbcd8228a..fe4fe475f526 100644
@@ -78,6 +78,7 @@ obj-$(CONFIG_PERF_EVENTS)	+= perf_cpum_cf_events.o perf_regs.o
 obj-$(CONFIG_PERF_EVENTS)	+= perf_cpum_cf_diag.o
 
 obj-$(CONFIG_TRACEPOINTS)	+= trace.o
+obj-$(findstring y, $(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) $(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST))	+= uv.o
 
 # vdso
 obj-y				+= vdso64/
arch/s390/kernel/setup.c +0 -4
index 3ff291bc63b7..f36370f8af38 100644
@@ -92,10 +92,6 @@ char elf_platform[ELF_PLATFORM_SIZE];
 
 unsigned long int_hwcap = 0;
 
-#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
-int __bootdata_preserved(prot_virt_guest);
-#endif
-
 int __bootdata(noexec_disabled);
 int __bootdata(memory_end_set);
 unsigned long __bootdata(memory_end);
arch/s390/kernel/uv.c new file +48 -0
new file mode 100644
index 000000000000..35ce89695509
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common Ultravisor functions and initialization
+ *
+ * Copyright IBM Corp. 2019
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+#include <linux/bitmap.h>
+#include <linux/memblock.h>
+#include <asm/facility.h>
+#include <asm/sections.h>
+#include <asm/uv.h>
+
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
+int __bootdata_preserved(prot_virt_guest);
+#endif
+
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+int prot_virt_host;
+EXPORT_SYMBOL(prot_virt_host);
+struct uv_info __bootdata_preserved(uv_info);
+EXPORT_SYMBOL(uv_info);
+
+static int __init prot_virt_setup(char *val)
+{
+	bool enabled;
+	int rc;
+
+	rc = kstrtobool(val, &enabled);
+	if (!rc && enabled)
+		prot_virt_host = 1;
+
+	if (is_prot_virt_guest() && prot_virt_host) {
+		prot_virt_host = 0;
+		pr_info("Running as protected virtualization guest.");
+	}
+
+	if (prot_virt_host && !test_facility(158)) {
+		prot_virt_host = 0;
+		pr_info("The ultravisor call facility is not available.");
+	}
+
+	return rc;
+}
+early_param("prot_virt", prot_virt_setup);
+#endif
arch/s390/kvm/Kconfig +2 -0
index d3db3d7ed077..652b36f0efca 100644
@@ -55,6 +55,15 @@ config KVM_S390_UCONTROL
 
 	  If unsure, say N.
 
+config KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	bool "Protected guests execution support"

arch/s390/kvm/Kconfig config KVM_S390_UCONTROL · line 69

Quoted 10 lines
arch/s390/kvm/Kconfig +7 -0
+	depends on KVM
+	---help---
+	  Support hosting protected virtual machines isolated from the
+	  hypervisor.
+
+	  If unsure, say Y.
+
 # OK, it's a little counter-intuitive to do this, but it puts it neatly under
 # the virtualization menu.
 source "drivers/vhost/Kconfig"

As we have the prot_virt kernel paramter there is a way to fence this during runtime Not sure if we really need a build time fence. We could get rid of CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST and just use CONFIG_KVM instead, assuming that in the long run all distros will enable that anyway.
If other reviewers prefer to keep that extra option what about the following to the help section:

----
Support hosting protected virtual machines in KVM. The state of these machines like memory content or register content is protected from the host or host administrators.

Enabling this option will enable extra code that talks to a new firmware instance called ultravisor that will take care of protecting the guest while also enabling KVM to run this guest.

This feature must be enable by the kernel command line option prot_virt.

If unsure, say Y.

----

Either way, the remaining part is

#15 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 24 lines · On Fri, 1 Nov 2019 09:53:12 +0100 Christian Borntraeger…
Quoted 23 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 arch/s390/boot/Makefile                       |  2 +-
 arch/s390/boot/uv.c                           | 20 +++++++-
 arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
 arch/s390/kernel/Makefile                     |  1 +
 arch/s390/kernel/setup.c                      |  4 --
 arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
 arch/s390/kvm/Kconfig                         |  9 ++++
 8 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/kernel/uv.c

(...)

Quoted 18 lines
Quoted 18 lines
arch/s390/kvm/Kconfig +9 -0
index d3db3d7ed077..652b36f0efca 100644
@@ -55,6 +55,15 @@ config KVM_S390_UCONTROL
 
 	  If unsure, say N.
 
+config KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	bool "Protected guests execution support"
+	depends on KVM
+	---help---
+	  Support hosting protected virtual machines isolated from the
+	  hypervisor.
+
+	  If unsure, say Y.
+
 # OK, it's a little counter-intuitive to do this, but it puts it neatly under
Quoted 8 lines

arch/s390/kvm/Kconfig config KVM_S390_UCONTROL · line 69

Quoted 3 lines
arch/s390/kvm/Kconfig +0 -0
 # the virtualization menu.
 source "drivers/vhost/Kconfig"

As we have the prot_virt kernel paramter there is a way to fence this during runtime Not sure if we really need a build time fence. We could get rid of CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST and just use CONFIG_KVM instead, assuming that in the long run all distros will enable that anyway.

I still need to read through the rest of this patch set to have an informed opinion on that, which will probably take some more time.

Quoted 8 lines

If other reviewers prefer to keep that extra option what about the following to the help section:

----
Support hosting protected virtual machines in KVM. The state of these machines like memory content or register content is protected from the host or host administrators.

Enabling this option will enable extra code that talks to a new firmware instance

"...that allows the host kernel to talk..." ?

Quoted 4 lines

called ultravisor that will take care of protecting the guest while also enabling KVM to run this guest.

This feature must be enable by the kernel command line option prot_virt.

s/enable by/enabled via/

Quoted 2 lines

If unsure, say Y.

Looks better. I'm continuing to read the rest of this series before I say more, though :)

author maintainer
#16 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 63 lines · Cornelia Huck
Quoted 24 lines · On Fri, 1 Nov 2019 09:53:12 +0100 Christian Borntraeger…
Quoted 23 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 arch/s390/boot/Makefile                       |  2 +-
 arch/s390/boot/uv.c                           | 20 +++++++-
 arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
 arch/s390/kernel/Makefile                     |  1 +
 arch/s390/kernel/setup.c                      |  4 --
 arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
 arch/s390/kvm/Kconfig                         |  9 ++++
 8 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/kernel/uv.c

(...)

Quoted 18 lines
Quoted 18 lines
arch/s390/kvm/Kconfig +9 -0
index d3db3d7ed077..652b36f0efca 100644
@@ -55,6 +55,15 @@ config KVM_S390_UCONTROL
 
 	  If unsure, say N.
 
+config KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	bool "Protected guests execution support"
+	depends on KVM
+	---help---
+	  Support hosting protected virtual machines isolated from the
+	  hypervisor.
+
+	  If unsure, say Y.
+
 # OK, it's a little counter-intuitive to do this, but it puts it neatly under
Quoted 8 lines

arch/s390/kvm/Kconfig config KVM_S390_UCONTROL · line 69

Quoted 3 lines
arch/s390/kvm/Kconfig +0 -0
 # the virtualization menu.
 source "drivers/vhost/Kconfig"

As we have the prot_virt kernel paramter there is a way to fence this during runtime Not sure if we really need a build time fence. We could get rid of CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST and just use CONFIG_KVM instead, assuming that in the long run all distros will enable that anyway.

I still need to read through the rest of this patch set to have an informed opinion on that, which will probably take some more time.

Quoted 3 lines

If other reviewers prefer to keep that extra option what about the following to the help section:

Quoted 7 lines
Quoted 5 lines

----
Support hosting protected virtual machines in KVM. The state of these machines like memory content or register content is protected from the host or host administrators.

Enabling this option will enable extra code that talks to a new firmware instance

"...that allows the host kernel to talk..." ?

"allows a Linux hypervisor to talk..." ?

Quoted 14 lines
Quoted 5 lines

called ultravisor that will take care of protecting the guest while also enabling KVM to run this guest.

This feature must be enable by the kernel command line option prot_virt.

s/enable by/enabled via/

Quoted 2 lines

If unsure, say Y.

Looks better. I'm continuing to read the rest of this series before I say more, though :)

#17 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

In reply to Janosch Frank
Quoted 23 lines · On Thu, 24 Oct 2019 07:40:24 -0400 Janosch Frank <[email protected]> wrote:

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 arch/s390/boot/Makefile                       |  2 +-
 arch/s390/boot/uv.c                           | 20 +++++++-
 arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
 arch/s390/kernel/Makefile                     |  1 +
 arch/s390/kernel/setup.c                      |  4 --
 arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
 arch/s390/kvm/Kconfig                         |  9 ++++
 8 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/kernel/uv.c

(...)

Quoted 5 lines
arch/s390/boot/uv.c +0 -0
index ed007f4a6444..88cf8825d169 100644

arch/s390/boot/uv.c line 11

Quoted 9 lines
arch/s390/boot/uv.c +5 -0
@@ -3,7 +3,12 @@
 #include <asm/facility.h>
 #include <asm/sections.h>
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 int __bootdata_preserved(prot_virt_guest);
+#endif
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+struct uv_info __bootdata_preserved(uv_info);
+#endif

Two functions with the same name, but different signatures look really ugly.

Also, what happens if I want to build just a single kernel image for both guest and host?

arch/s390/boot/uv.c void uv_query_info(void) · line 26

Quoted 9 lines
arch/s390/boot/uv.c +1 -1
 void uv_query_info(void)
 {
@@ -18,7 +23,20 @@ void uv_query_info(void)
 	if (uv_call(0, (uint64_t)&uvcb))
 		return;
 
-	if (test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)) {

Do we always have everything needed for a host if uv_call() is successful?

Quoted 4 lines
arch/s390/boot/uv.c +4 -0
+		memcpy(uv_info.inst_calls_list, uvcb.inst_calls_list, sizeof(uv_info.inst_calls_list));
+		uv_info.uv_base_stor_len = uvcb.uv_base_stor_len;
+		uv_info.guest_base_stor_len = uvcb.conf_base_phys_stor_len;
+		uv_info.guest_virt_base_stor_len = uvcb.conf_base_virt_stor_len;

arch/s390/boot/uv.c void uv_query_info(void) · line 40

Quoted 10 lines
arch/s390/boot/uv.c +9 -0
+		uv_info.guest_virt_var_stor_len = uvcb.conf_virt_var_stor_len;
+		uv_info.guest_cpu_stor_len = uvcb.cpu_stor_len;
+		uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE);
+		uv_info.max_num_sec_conf = uvcb.max_num_sec_conf;
+		uv_info.max_guest_cpus = uvcb.max_guest_cpus;
+	}
+
+	if (IS_ENABLED(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) &&
+	    test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
 	    test_bit_inv(BIT_UVC_CMD_REMOVE_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list))

Especially as it looks like we need to test for those two commands to determine whether we have support for a guest.

Quoted 33 lines
arch/s390/boot/uv.c +0 -0
 		prot_virt_guest = 1;
 }
arch/s390/include/asm/uv.h +14 -2
index ef3c00b049ab..6db1bc495e67 100644
@@ -44,7 +44,19 @@ struct uv_cb_qui {
 	struct uv_cb_header header;
 	u64 reserved08;
 	u64 inst_calls_list[4];
-	u64 reserved30[15];
+	u64 reserved30[2];
+	u64 uv_base_stor_len;
+	u64 reserved48;
+	u64 conf_base_phys_stor_len;
+	u64 conf_base_virt_stor_len;
+	u64 conf_virt_var_stor_len;
+	u64 cpu_stor_len;
+	u32 reserved68[3];
+	u32 max_num_sec_conf;
+	u64 max_guest_stor_addr;
+	u8  reserved80[150-128];
+	u16 max_guest_cpus;
+	u64 reserved98;
 } __packed __aligned(8);
 
 struct uv_cb_share {
@@ -69,9 +81,21 @@ static inline int uv_call(unsigned long r1, unsigned long r2)
 	return cc;
 }
 
-#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
+struct uv_info {

arch/s390/include/asm/uv.h static inline int uv_call(unsigned long r1, unsigned long r2) · line 94

Quoted 10 lines
arch/s390/include/asm/uv.h +10 -0
+	unsigned long inst_calls_list[4];
+	unsigned long uv_base_stor_len;
+	unsigned long guest_base_stor_len;
+	unsigned long guest_virt_base_stor_len;
+	unsigned long guest_virt_var_stor_len;
+	unsigned long guest_cpu_stor_len;
+	unsigned long max_sec_stor_addr;
+	unsigned int max_num_sec_conf;
+	unsigned short max_guest_cpus;
+};

What is the main difference between uv_info and uv_cb_qui? The alignment of max_sec_stor_addr?

Quoted 35 lines
arch/s390/include/asm/uv.h +19 -1
+extern struct uv_info uv_info;
 extern int prot_virt_guest;
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 static inline int is_prot_virt_guest(void)
 {
 	return prot_virt_guest;
@@ -121,11 +145,27 @@ static inline int uv_remove_shared(unsigned long addr)
 	return share(addr, UVC_CMD_REMOVE_SHARED_ACCESS);
 }
 
-void uv_query_info(void);
 #else
 #define is_prot_virt_guest() 0
 static inline int uv_set_shared(unsigned long addr) { return 0; }
 static inline int uv_remove_shared(unsigned long addr) { return 0; }
+#endif
+
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+extern int prot_virt_host;
+
+static inline int is_prot_virt_host(void)
+{
+	return prot_virt_host;
+}
+#else
+#define is_prot_virt_host() 0
+#endif
+
+#if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
+	defined(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)
+void uv_query_info(void);
+#else
 static inline void uv_query_info(void) {}
 #endif

(...)

Quoted 36 lines
arch/s390/kernel/uv.c new file +30 -0
new file mode 100644
index 000000000000..35ce89695509
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common Ultravisor functions and initialization
+ *
+ * Copyright IBM Corp. 2019
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+#include <linux/bitmap.h>
+#include <linux/memblock.h>
+#include <asm/facility.h>
+#include <asm/sections.h>
+#include <asm/uv.h>
+
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
+int __bootdata_preserved(prot_virt_guest);
+#endif
+
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+int prot_virt_host;
+EXPORT_SYMBOL(prot_virt_host);
+struct uv_info __bootdata_preserved(uv_info);
+EXPORT_SYMBOL(uv_info);
+
+static int __init prot_virt_setup(char *val)
+{
+	bool enabled;
+	int rc;
+

arch/s390/kernel/uv.c line 40

Quoted 10 lines
arch/s390/kernel/uv.c new file +10 -0
+	rc = kstrtobool(val, &enabled);
+	if (!rc && enabled)
+		prot_virt_host = 1;
+
+	if (is_prot_virt_guest() && prot_virt_host) {
+		prot_virt_host = 0;
+		pr_info("Running as protected virtualization guest.");
+	}
+
+	if (prot_virt_host && !test_facility(158)) {

Why not check for that facility earlier? If it is not present, we cannot run as a prot virt guest, either.

arch/s390/kernel/uv.c line 47

Quoted 7 lines
arch/s390/kernel/uv.c new file +7 -0
+		prot_virt_host = 0;
+		pr_info("The ultravisor call facility is not available.");
+	}
+
+	return rc;
+}
+early_param("prot_virt", prot_virt_setup);

Maybe rename this to prot_virt_host?

Quoted 1 line
arch/s390/kernel/uv.c new file +1 -0
+#endif

(...)

#18 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 47 lines · Cornelia Huck
Quoted 23 lines · On Thu, 24 Oct 2019 07:40:24 -0400 Janosch Frank <[email protected]> wrote:

From: Vasily Gorbik <[email protected]>

Introduce KVM_S390_PROTECTED_VIRTUALIZATION_HOST kbuild option for protected virtual machines hosting support code.

Add "prot_virt" command line option which controls if the kernel protected VMs support is enabled at runtime.

Extend ultravisor info definitions and expose it via uv_info struct filled in during startup.

---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 arch/s390/boot/Makefile                       |  2 +-
 arch/s390/boot/uv.c                           | 20 +++++++-
 arch/s390/include/asm/uv.h                    | 46 ++++++++++++++++--
 arch/s390/kernel/Makefile                     |  1 +
 arch/s390/kernel/setup.c                      |  4 --
 arch/s390/kernel/uv.c                         | 48 +++++++++++++++++++
 arch/s390/kvm/Kconfig                         |  9 ++++
 8 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/kernel/uv.c

(...)

Quoted 5 lines
arch/s390/boot/uv.c +0 -0
index ed007f4a6444..88cf8825d169 100644

arch/s390/boot/uv.c line 11

Quoted 9 lines
arch/s390/boot/uv.c +5 -0
@@ -3,7 +3,12 @@
 #include <asm/facility.h>
 #include <asm/sections.h>
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 int __bootdata_preserved(prot_virt_guest);
+#endif
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+struct uv_info __bootdata_preserved(uv_info);
+#endif

Two functions with the same name, but different signatures look really ugly.

Quoted 2 lines

Also, what happens if I want to build just a single kernel image for both guest and host?

This is not two functions with the same name. It is 2 variable declarations with the __bootdata_preserved helper. We expect to have all distro kernels to enable both.

Quoted 8 lines
Quoted 8 lines
arch/s390/boot/uv.c +0 -0
 void uv_query_info(void)
 {
@@ -18,7 +23,20 @@ void uv_query_info(void)
 	if (uv_call(0, (uint64_t)&uvcb))
 		return;
 
Quoted 5 lines

arch/s390/boot/uv.c void uv_query_info(void) · line 26

Quoted 2 lines
arch/s390/boot/uv.c +1 -1
-	if (test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)) {

Do we always have everything needed for a host if uv_call() is successful?

The uv_call is the query call. It will provide the list of features. We check that later on.

Quoted 50 lines
Quoted 5 lines
arch/s390/boot/uv.c +4 -0
+		memcpy(uv_info.inst_calls_list, uvcb.inst_calls_list, sizeof(uv_info.inst_calls_list));
+		uv_info.uv_base_stor_len = uvcb.uv_base_stor_len;
+		uv_info.guest_base_stor_len = uvcb.conf_base_phys_stor_len;
+		uv_info.guest_virt_base_stor_len = uvcb.conf_base_virt_stor_len;

arch/s390/boot/uv.c void uv_query_info(void) · line 40

Quoted 10 lines
arch/s390/boot/uv.c +9 -0
+		uv_info.guest_virt_var_stor_len = uvcb.conf_virt_var_stor_len;
+		uv_info.guest_cpu_stor_len = uvcb.cpu_stor_len;
+		uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE);
+		uv_info.max_num_sec_conf = uvcb.max_num_sec_conf;
+		uv_info.max_guest_cpus = uvcb.max_guest_cpus;
+	}
+
+	if (IS_ENABLED(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) &&
+	    test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
 	    test_bit_inv(BIT_UVC_CMD_REMOVE_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list))

Especially as it looks like we need to test for those two commands to determine whether we have support for a guest.

Quoted 31 lines
arch/s390/boot/uv.c +0 -0
 		prot_virt_guest = 1;
 }
arch/s390/include/asm/uv.h +13 -1
index ef3c00b049ab..6db1bc495e67 100644
@@ -44,7 +44,19 @@ struct uv_cb_qui {
 	struct uv_cb_header header;
 	u64 reserved08;
 	u64 inst_calls_list[4];
-	u64 reserved30[15];
+	u64 reserved30[2];
+	u64 uv_base_stor_len;
+	u64 reserved48;
+	u64 conf_base_phys_stor_len;
+	u64 conf_base_virt_stor_len;
+	u64 conf_virt_var_stor_len;
+	u64 cpu_stor_len;
+	u32 reserved68[3];
+	u32 max_num_sec_conf;
+	u64 max_guest_stor_addr;
+	u8  reserved80[150-128];
+	u16 max_guest_cpus;
+	u64 reserved98;
 } __packed __aligned(8);
 
 struct uv_cb_share {
@@ -69,9 +81,21 @@ static inline int uv_call(unsigned long r1, unsigned long r2)
 	return cc;
 }
 
Quoted 15 lines

arch/s390/include/asm/uv.h static inline int uv_call(unsigned long r1, unsigned long r2) · line 94

Quoted 12 lines
arch/s390/include/asm/uv.h +11 -1
-#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
+struct uv_info {
+	unsigned long inst_calls_list[4];
+	unsigned long uv_base_stor_len;
+	unsigned long guest_base_stor_len;
+	unsigned long guest_virt_base_stor_len;
+	unsigned long guest_virt_var_stor_len;
+	unsigned long guest_cpu_stor_len;
+	unsigned long max_sec_stor_addr;
+	unsigned int max_num_sec_conf;
+	unsigned short max_guest_cpus;
+};

What is the main difference between uv_info and uv_cb_qui? The alignment of max_sec_stor_addr?

One is the hardware data structure for query, the other one is the Linux internal state.

Quoted 39 lines
Quoted 36 lines
arch/s390/include/asm/uv.h +19 -1
+extern struct uv_info uv_info;
 extern int prot_virt_guest;
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 static inline int is_prot_virt_guest(void)
 {
 	return prot_virt_guest;
@@ -121,11 +145,27 @@ static inline int uv_remove_shared(unsigned long addr)
 	return share(addr, UVC_CMD_REMOVE_SHARED_ACCESS);
 }
 
-void uv_query_info(void);
 #else
 #define is_prot_virt_guest() 0
 static inline int uv_set_shared(unsigned long addr) { return 0; }
 static inline int uv_remove_shared(unsigned long addr) { return 0; }
+#endif
+
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+extern int prot_virt_host;
+
+static inline int is_prot_virt_host(void)
+{
+	return prot_virt_host;
+}
+#else
+#define is_prot_virt_host() 0
+#endif
+
+#if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
+	defined(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)
+void uv_query_info(void);
+#else
 static inline void uv_query_info(void) {}
 #endif

(...)

[...]

#19 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 22 lines · On Mon, 4 Nov 2019 18:50:12 +0100 Christian Borntraeger…
Quoted 21 lines · Cornelia Huck
Quoted 5 lines · On Thu, 24 Oct 2019 07:40:24 -0400 Janosch Frank <[email protected]> wrote:
arch/s390/boot/uv.c +0 -0
index ed007f4a6444..88cf8825d169 100644

arch/s390/boot/uv.c line 11

Quoted 9 lines
arch/s390/boot/uv.c +5 -0
@@ -3,7 +3,12 @@
 #include <asm/facility.h>
 #include <asm/sections.h>
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 int __bootdata_preserved(prot_virt_guest);
+#endif
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+struct uv_info __bootdata_preserved(uv_info);
+#endif  

Two functions with the same name, but different signatures look really ugly.

Quoted 6 lines
Quoted 2 lines

Also, what happens if I want to build just a single kernel image for both guest and host?

This is not two functions with the same name. It is 2 variable declarations with the __bootdata_preserved helper. We expect to have all distro kernels to enable both.

Ah ok, I misread that. (I'm blaming lack of sleep :/)

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
arch/s390/boot/uv.c +0 -0
 void uv_query_info(void)
 {
@@ -18,7 +23,20 @@ void uv_query_info(void)
 	if (uv_call(0, (uint64_t)&uvcb))
 		return;
 
Quoted 8 lines
Quoted 5 lines

arch/s390/boot/uv.c void uv_query_info(void) · line 26

Quoted 2 lines
arch/s390/boot/uv.c +1 -1
-	if (test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST)) {  

Do we always have everything needed for a host if uv_call() is successful?

The uv_call is the query call. It will provide the list of features. We check that later on.

Hm yes. I'm just seeing the guest side check for features, while the host code just seems to go ahead and copies things. (later on == later patches?)

Quoted 67 lines
Quoted 51 lines
Quoted 6 lines
arch/s390/boot/uv.c +4 -0
+		memcpy(uv_info.inst_calls_list, uvcb.inst_calls_list, sizeof(uv_info.inst_calls_list));
+		uv_info.uv_base_stor_len = uvcb.uv_base_stor_len;
+		uv_info.guest_base_stor_len = uvcb.conf_base_phys_stor_len;
+		uv_info.guest_virt_base_stor_len = uvcb.conf_base_virt_stor_len;

arch/s390/boot/uv.c void uv_query_info(void) · line 40

Quoted 10 lines
arch/s390/boot/uv.c +9 -0
+		uv_info.guest_virt_var_stor_len = uvcb.conf_virt_var_stor_len;
+		uv_info.guest_cpu_stor_len = uvcb.cpu_stor_len;
+		uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE);
+		uv_info.max_num_sec_conf = uvcb.max_num_sec_conf;
+		uv_info.max_guest_cpus = uvcb.max_guest_cpus;
+	}
+
+	if (IS_ENABLED(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) &&
+	    test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
 	    test_bit_inv(BIT_UVC_CMD_REMOVE_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list))  

Especially as it looks like we need to test for those two commands to determine whether we have support for a guest.

Quoted 31 lines
arch/s390/boot/uv.c +0 -0
 		prot_virt_guest = 1;
 }
arch/s390/include/asm/uv.h +13 -1
index ef3c00b049ab..6db1bc495e67 100644
@@ -44,7 +44,19 @@ struct uv_cb_qui {
 	struct uv_cb_header header;
 	u64 reserved08;
 	u64 inst_calls_list[4];
-	u64 reserved30[15];
+	u64 reserved30[2];
+	u64 uv_base_stor_len;
+	u64 reserved48;
+	u64 conf_base_phys_stor_len;
+	u64 conf_base_virt_stor_len;
+	u64 conf_virt_var_stor_len;
+	u64 cpu_stor_len;
+	u32 reserved68[3];
+	u32 max_num_sec_conf;
+	u64 max_guest_stor_addr;
+	u8  reserved80[150-128];
+	u16 max_guest_cpus;
+	u64 reserved98;
 } __packed __aligned(8);
 
 struct uv_cb_share {
@@ -69,9 +81,21 @@ static inline int uv_call(unsigned long r1, unsigned long r2)
 	return cc;
 }
 
Quoted 15 lines

arch/s390/include/asm/uv.h static inline int uv_call(unsigned long r1, unsigned long r2) · line 94

Quoted 12 lines
arch/s390/include/asm/uv.h +11 -1
-#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
+struct uv_info {
+	unsigned long inst_calls_list[4];
+	unsigned long uv_base_stor_len;
+	unsigned long guest_base_stor_len;
+	unsigned long guest_virt_base_stor_len;
+	unsigned long guest_virt_var_stor_len;
+	unsigned long guest_cpu_stor_len;
+	unsigned long max_sec_stor_addr;
+	unsigned int max_num_sec_conf;
+	unsigned short max_guest_cpus;
+};  

What is the main difference between uv_info and uv_cb_qui? The alignment of max_sec_stor_addr?

Quoted 2 lines

One is the hardware data structure for query, the other one is the Linux internal state.

That's clear; I'm mainly wondering about what is simply copied vs. what needs to be calculated.

#20 lore raw

Re: [RFC 02/37] s390/protvirt: introduce host side setup

Quoted 25 lines · Cornelia Huck
Quoted 22 lines · On Mon, 4 Nov 2019 18:50:12 +0100 Christian Borntraeger…
Quoted 21 lines · Cornelia Huck
Quoted 5 lines · On Thu, 24 Oct 2019 07:40:24 -0400 Janosch Frank <[email protected]> wrote:
arch/s390/boot/uv.c +0 -0
index ed007f4a6444..88cf8825d169 100644

arch/s390/boot/uv.c line 11

Quoted 9 lines
arch/s390/boot/uv.c +5 -0
@@ -3,7 +3,12 @@
  #include <asm/facility.h>
  #include <asm/sections.h>
 
+#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
  int __bootdata_preserved(prot_virt_guest);
+#endif
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+struct uv_info __bootdata_preserved(uv_info);
+#endif

Two functions with the same name, but different signatures look really ugly.

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Also, what happens if I want to build just a single kernel image for both guest and host?

This is not two functions with the same name. It is 2 variable declarations with the __bootdata_preserved helper. We expect to have all distro kernels to enable both.

Ah ok, I misread that. (I'm blaming lack of sleep :/)

Honestly, I have to admit that I mis-read this in the same way as Cornelia at the first glance. Why is that macro not using capital letters? ... then it would be way more obvious that it's not about a function prototype...

Thomas

author maintainer
#21 lore raw

RFC 3/37 s390/protvirt: add ultravisor initialization

In reply to Janosch Frank

From: Vasily Gorbik <[email protected]>

Before being able to host protected virtual machines, donate some of the memory to the ultravisor. Besides that the ultravisor might impose addressing limitations for memory used to back protected VM storage. Treat that limit as protected virtualization host's virtual memory limit.

---
 arch/s390/include/asm/uv.h | 16 ++++++++++++
 arch/s390/kernel/setup.c   |  3 +++
 arch/s390/kernel/uv.c      | 53 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)
arch/s390/include/asm/uv.h +16 -0
index 6db1bc495e67..82a46fb913e7 100644
@@ -23,12 +23,14 @@
 #define UVC_RC_NO_RESUME	0x0007
 
 #define UVC_CMD_QUI			0x0001
+#define UVC_CMD_INIT_UV			0x000f
 #define UVC_CMD_SET_SHARED_ACCESS	0x1000
 #define UVC_CMD_REMOVE_SHARED_ACCESS	0x1001
 
 /* Bits in installed uv calls */
 enum uv_cmds_inst {
 	BIT_UVC_CMD_QUI = 0,
+	BIT_UVC_CMD_INIT_UV = 1,
 	BIT_UVC_CMD_SET_SHARED_ACCESS = 8,
 	BIT_UVC_CMD_REMOVE_SHARED_ACCESS = 9,
 };
@@ -59,6 +61,15 @@ struct uv_cb_qui {
 	u64 reserved98;
 } __packed __aligned(8);
 
+struct uv_cb_init {
+	struct uv_cb_header header;
+	u64 reserved08[2];
+	u64 stor_origin;
+	u64 stor_len;
+	u64 reserved28[4];
+
+} __packed __aligned(8);
+
 struct uv_cb_share {
 	struct uv_cb_header header;
 	u64 reserved08[3];
@@ -158,8 +169,13 @@ static inline int is_prot_virt_host(void)
 {
 	return prot_virt_host;
 }
+
+void setup_uv(void);
+void adjust_to_uv_max(unsigned long *vmax);
 #else
 #define is_prot_virt_host() 0
+static inline void setup_uv(void) {}
+static inline void adjust_to_uv_max(unsigned long *vmax) {}
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
arch/s390/kernel/setup.c +3 -0
index f36370f8af38..d29d83c0b8df 100644
@@ -567,6 +567,8 @@ static void __init setup_memory_end(void)
 			vmax = _REGION1_SIZE; /* 4-level kernel page table */
 	}
 
+	adjust_to_uv_max(&vmax);
+
 	/* module area is at the end of the kernel address space. */
 	MODULES_END = vmax;
 	MODULES_VADDR = MODULES_END - MODULES_LEN;
@@ -1147,6 +1149,7 @@ void __init setup_arch(char **cmdline_p)
 	 */
 	memblock_trim_memory(1UL << (MAX_ORDER - 1 + PAGE_SHIFT));
 
+	setup_uv();
 	setup_memory_end();
 	setup_memory();
 	dma_contiguous_reserve(memory_end);
arch/s390/kernel/uv.c +53 -0

57 lines not on this page · raw message

Signature · 1 line
2.20.1
#22 lore raw

Re: [RFC 03/37] s390/protvirt: add ultravisor initialization

Quoted 68 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Before being able to host protected virtual machines, donate some of the memory to the ultravisor. Besides that the ultravisor might impose addressing limitations for memory used to back protected VM storage. Treat that limit as protected virtualization host's virtual memory limit.

---
  arch/s390/include/asm/uv.h | 16 ++++++++++++
  arch/s390/kernel/setup.c   |  3 +++
  arch/s390/kernel/uv.c      | 53 ++++++++++++++++++++++++++++++++++++++
  3 files changed, 72 insertions(+)
arch/s390/include/asm/uv.h +16 -0
index 6db1bc495e67..82a46fb913e7 100644
@@ -23,12 +23,14 @@
  #define UVC_RC_NO_RESUME	0x0007
 
  #define UVC_CMD_QUI			0x0001
+#define UVC_CMD_INIT_UV			0x000f
  #define UVC_CMD_SET_SHARED_ACCESS	0x1000
  #define UVC_CMD_REMOVE_SHARED_ACCESS	0x1001
 
  /* Bits in installed uv calls */
  enum uv_cmds_inst {
  	BIT_UVC_CMD_QUI = 0,
+	BIT_UVC_CMD_INIT_UV = 1,
  	BIT_UVC_CMD_SET_SHARED_ACCESS = 8,
  	BIT_UVC_CMD_REMOVE_SHARED_ACCESS = 9,
  };
@@ -59,6 +61,15 @@ struct uv_cb_qui {
  	u64 reserved98;
  } __packed __aligned(8);
 
+struct uv_cb_init {
+	struct uv_cb_header header;
+	u64 reserved08[2];
+	u64 stor_origin;
+	u64 stor_len;
+	u64 reserved28[4];
+
+} __packed __aligned(8);
+
  struct uv_cb_share {
  	struct uv_cb_header header;
  	u64 reserved08[3];
@@ -158,8 +169,13 @@ static inline int is_prot_virt_host(void)
  {
  	return prot_virt_host;
  }
+
+void setup_uv(void);
+void adjust_to_uv_max(unsigned long *vmax);
  #else
  #define is_prot_virt_host() 0
+static inline void setup_uv(void) {}
+static inline void adjust_to_uv_max(unsigned long *vmax) {}
  #endif
 
  #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
arch/s390/kernel/setup.c +0 -0
index f36370f8af38..d29d83c0b8df 100644

arch/s390/kernel/setup.c static void __init setup_memory_end(void) · line 570

Quoted 4 lines
arch/s390/kernel/setup.c +1 -0
@@ -567,6 +567,8 @@ static void __init setup_memory_end(void)
  			vmax = _REGION1_SIZE; /* 4-level kernel page table */
  	}
 
+	adjust_to_uv_max(&vmax);

I do wonder what would happen if vmax < max_physmem_end. Not sure if that is relevant at all.

Quoted 45 lines
arch/s390/kernel/setup.c +2 -0
+
  	/* module area is at the end of the kernel address space. */
  	MODULES_END = vmax;
  	MODULES_VADDR = MODULES_END - MODULES_LEN;
@@ -1147,6 +1149,7 @@ void __init setup_arch(char **cmdline_p)
  	 */
  	memblock_trim_memory(1UL << (MAX_ORDER - 1 + PAGE_SHIFT));
 
+	setup_uv();
  	setup_memory_end();
  	setup_memory();
  	dma_contiguous_reserve(memory_end);
arch/s390/kernel/uv.c +25 -0
index 35ce89695509..f7778493e829 100644
@@ -45,4 +45,57 @@ static int __init prot_virt_setup(char *val)
  	return rc;
  }
  early_param("prot_virt", prot_virt_setup);
+
+static int __init uv_init(unsigned long stor_base, unsigned long stor_len)
+{
+	struct uv_cb_init uvcb = {
+		.header.cmd = UVC_CMD_INIT_UV,
+		.header.len = sizeof(uvcb),
+		.stor_origin = stor_base,
+		.stor_len = stor_len,
+	};
+	int cc;
+
+	cc = uv_call(0, (uint64_t)&uvcb);
+	if (cc || uvcb.header.rc != UVC_RC_EXECUTED) {
+		pr_err("Ultravisor init failed with cc: %d rc: 0x%hx\n", cc,
+		       uvcb.header.rc);
+		return -1;
+	}
+	return 0;
+}
+
+void __init setup_uv(void)
+{
+	unsigned long uv_stor_base;
+
+	if (!prot_virt_host)

arch/s390/kernel/uv.c static int __init prot_virt_setup(char *val) · line 82

Quoted 10 lines
arch/s390/kernel/uv.c +10 -0
+		return;
+
+	uv_stor_base = (unsigned long)memblock_alloc_try_nid(
+		uv_info.uv_base_stor_len, SZ_1M, SZ_2G,
+		MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);
+	if (!uv_stor_base) {
+		pr_info("Failed to reserve %lu bytes for ultravisor base storage\n",
+			uv_info.uv_base_stor_len);
+		goto fail;
+	}

If I'm not wrong, we could setup/reserve a CMA area here and defer the actual allocation. Then, any MOVABLE data can end up on this CMA area until needed.

But I am neither an expert on CMA nor on UV, so most probably what I say is wrong ;)

Quoted 10 lines
arch/s390/kernel/uv.c +9 -0
+
+	if (uv_init(uv_stor_base, uv_info.uv_base_stor_len)) {
+		memblock_free(uv_stor_base, uv_info.uv_base_stor_len);
+		goto fail;
+	}
+
+	pr_info("Reserving %luMB as ultravisor base storage\n",
+		uv_info.uv_base_stor_len >> 20);
+	return;

arch/s390/kernel/uv.c static int __init prot_virt_setup(char *val) · line 101

Quoted 10 lines
arch/s390/kernel/uv.c +9 -0
+fail:
+	prot_virt_host = 0;
+}
+
+void adjust_to_uv_max(unsigned long *vmax)
+{
+	if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
+		*vmax = uv_info.max_sec_stor_addr;
+}
  #endif

Looks good to me from what I can tell.

Signature · 3 lines
Thanks,

David / dhildenb
maintainer
#23 lore raw

Re: [RFC 03/37] s390/protvirt: add ultravisor initialization

Quoted 15 lines · David Hildenbrand
Quoted 14 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Before being able to host protected virtual machines, donate some of the memory to the ultravisor. Besides that the ultravisor might impose addressing limitations for memory used to back protected VM storage. Treat that limit as protected virtualization host's virtual memory limit.

---
  arch/s390/include/asm/uv.h | 16 ++++++++++++
  arch/s390/kernel/setup.c   |  3 +++
  arch/s390/kernel/uv.c      | 53 ++++++++++++++++++++++++++++++++++++++
  3 files changed, 72 insertions(+)
Quoted 9 lines

arch/s390/kernel/setup.c static void __init setup_memory_end(void) · line 569

Quoted 6 lines
arch/s390/kernel/setup.c +1 -0
@@ -567,6 +567,8 @@ static void __init setup_memory_end(void)
  			vmax = _REGION1_SIZE; /* 4-level kernel page table */
  	}
+	adjust_to_uv_max(&vmax);

I do wonder what would happen if vmax < max_physmem_end. Not sure if that is relevant at all.

Then identity mapping would be shorter then actual physical memory available and everything above would be lost. But in reality "max_sec_stor_addr" is big enough to not worry about it in the foreseeable future at all.

Quoted 20 lines
Quoted 5 lines
arch/s390/kernel/setup.c +5 -0
+void __init setup_uv(void)
+{
+	unsigned long uv_stor_base;
+
+	if (!prot_virt_host)

arch/s390/kernel/setup.c static void __init setup_memory_end(void)

Quoted 10 lines
arch/s390/kernel/setup.c +10 -0
+		return;
+
+	uv_stor_base = (unsigned long)memblock_alloc_try_nid(
+		uv_info.uv_base_stor_len, SZ_1M, SZ_2G,
+		MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);
+	if (!uv_stor_base) {
+		pr_info("Failed to reserve %lu bytes for ultravisor base storage\n",
+			uv_info.uv_base_stor_len);
+		goto fail;
+	}

If I'm not wrong, we could setup/reserve a CMA area here and defer the actual allocation. Then, any MOVABLE data can end up on this CMA area until needed.

Quoted 2 lines

But I am neither an expert on CMA nor on UV, so most probably what I say is wrong ;)

From pure memory management this sounds like a good idea. And I tried it and cma_declare_contiguous fulfills our needs, just had to export cma_alloc/cma_release symbols. Nevertheless, delaying ultravisor init means we would be potentially left with vmax == max_sec_stor_addr even if we wouldn't be able to run protected VMs after all (currently setup_uv() is called before kernel address space layout setup). Another much more fundamental reason is that ultravisor init has to be called with a single cpu running, which means it's easy to do before bringing other cpus up and we currently don't have api to stop cpus at a later point (stop_machine won't cut it).

Quoted 29 lines
Quoted 10 lines
arch/s390/kernel/setup.c +9 -0
+
+	if (uv_init(uv_stor_base, uv_info.uv_base_stor_len)) {
+		memblock_free(uv_stor_base, uv_info.uv_base_stor_len);
+		goto fail;
+	}
+
+	pr_info("Reserving %luMB as ultravisor base storage\n",
+		uv_info.uv_base_stor_len >> 20);
+	return;

arch/s390/kernel/setup.c

Quoted 10 lines
arch/s390/kernel/setup.c +9 -0
+fail:
+	prot_virt_host = 0;
+}
+
+void adjust_to_uv_max(unsigned long *vmax)
+{
+	if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
+		*vmax = uv_info.max_sec_stor_addr;
+}
  #endif

Looks good to me from what I can tell.

Signature · 3 lines
Thanks,

David / dhildenb
#24 lore raw

Re: [RFC 03/37] s390/protvirt: add ultravisor initialization

Quoted 53 lines · Vasily Gorbik
Quoted 15 lines · David Hildenbrand
Quoted 14 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Before being able to host protected virtual machines, donate some of the memory to the ultravisor. Besides that the ultravisor might impose addressing limitations for memory used to back protected VM storage. Treat that limit as protected virtualization host's virtual memory limit.

---
   arch/s390/include/asm/uv.h | 16 ++++++++++++
   arch/s390/kernel/setup.c   |  3 +++
   arch/s390/kernel/uv.c      | 53 ++++++++++++++++++++++++++++++++++++++
   3 files changed, 72 insertions(+)
Quoted 9 lines

arch/s390/kernel/setup.c static void __init setup_memory_end(void) · line 569

Quoted 6 lines
arch/s390/kernel/setup.c +1 -0
@@ -567,6 +567,8 @@ static void __init setup_memory_end(void)
   			vmax = _REGION1_SIZE; /* 4-level kernel page table */
   	}
+	adjust_to_uv_max(&vmax);

I do wonder what would happen if vmax < max_physmem_end. Not sure if that is relevant at all.

Then identity mapping would be shorter then actual physical memory available and everything above would be lost. But in reality "max_sec_stor_addr" is big enough to not worry about it in the foreseeable future at all.

Quoted 20 lines
Quoted 5 lines
arch/s390/kernel/setup.c +5 -0
+void __init setup_uv(void)
+{
+	unsigned long uv_stor_base;
+
+	if (!prot_virt_host)

arch/s390/kernel/setup.c static void __init setup_memory_end(void)

Quoted 10 lines
arch/s390/kernel/setup.c +10 -0
+		return;
+
+	uv_stor_base = (unsigned long)memblock_alloc_try_nid(
+		uv_info.uv_base_stor_len, SZ_1M, SZ_2G,
+		MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);
+	if (!uv_stor_base) {
+		pr_info("Failed to reserve %lu bytes for ultravisor base storage\n",
+			uv_info.uv_base_stor_len);
+		goto fail;
+	}

If I'm not wrong, we could setup/reserve a CMA area here and defer the actual allocation. Then, any MOVABLE data can end up on this CMA area until needed.

Quoted 2 lines

But I am neither an expert on CMA nor on UV, so most probably what I say is wrong ;)

Quoted 9 lines

From pure memory management this sounds like a good idea. And I tried
it and cma_declare_contiguous fulfills our needs, just had to export cma_alloc/cma_release symbols. Nevertheless, delaying ultravisor init means we would be potentially left with vmax == max_sec_stor_addr even if we wouldn't be able to run protected VMs after all (currently setup_uv() is called before kernel address space layout setup). Another much more fundamental reason is that ultravisor init has to be called with a single cpu running, which means it's easy to do before bringing other cpus up and we currently don't have api to stop cpus at a later point (stop_machine won't cut it).

Interesting point, I guess. One could hack around that. Emphasis on *hack* :) In stop_machine() you caught all CPUs. You could just temporarily SIGP STOP all running ones, issue the UV init call, and SIGP START them again. Not sure how that works with SMP, though ...

But yeah, this is stuff for the future, just an idea from my side :)

Signature · 3 lines
Thanks,

David / dhildenb
Reviewed-by on v1 3/37
#25 lore raw

Re: [RFC 03/37] s390/protvirt: add ultravisor initialization

In reply to Janosch Frank
Quoted 8 lines · Janosch Frank

From: Vasily Gorbik <[email protected]>

Before being able to host protected virtual machines, donate some of the memory to the ultravisor. Besides that the ultravisor might impose addressing limitations for memory used to back protected VM storage. Treat that limit as protected virtualization host's virtual memory limit.

Quoted 139 lines
---
 arch/s390/include/asm/uv.h | 16 ++++++++++++
 arch/s390/kernel/setup.c   |  3 +++
 arch/s390/kernel/uv.c      | 53 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)
arch/s390/include/asm/uv.h +16 -0
index 6db1bc495e67..82a46fb913e7 100644
@@ -23,12 +23,14 @@
 #define UVC_RC_NO_RESUME	0x0007
 
 #define UVC_CMD_QUI			0x0001
+#define UVC_CMD_INIT_UV			0x000f
 #define UVC_CMD_SET_SHARED_ACCESS	0x1000
 #define UVC_CMD_REMOVE_SHARED_ACCESS	0x1001
 
 /* Bits in installed uv calls */
 enum uv_cmds_inst {
 	BIT_UVC_CMD_QUI = 0,
+	BIT_UVC_CMD_INIT_UV = 1,
 	BIT_UVC_CMD_SET_SHARED_ACCESS = 8,
 	BIT_UVC_CMD_REMOVE_SHARED_ACCESS = 9,
 };
@@ -59,6 +61,15 @@ struct uv_cb_qui {
 	u64 reserved98;
 } __packed __aligned(8);
 
+struct uv_cb_init {
+	struct uv_cb_header header;
+	u64 reserved08[2];
+	u64 stor_origin;
+	u64 stor_len;
+	u64 reserved28[4];
+
+} __packed __aligned(8);
+
 struct uv_cb_share {
 	struct uv_cb_header header;
 	u64 reserved08[3];
@@ -158,8 +169,13 @@ static inline int is_prot_virt_host(void)
 {
 	return prot_virt_host;
 }
+
+void setup_uv(void);
+void adjust_to_uv_max(unsigned long *vmax);
 #else
 #define is_prot_virt_host() 0
+static inline void setup_uv(void) {}
+static inline void adjust_to_uv_max(unsigned long *vmax) {}
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
arch/s390/kernel/setup.c +3 -0
index f36370f8af38..d29d83c0b8df 100644
@@ -567,6 +567,8 @@ static void __init setup_memory_end(void)
 			vmax = _REGION1_SIZE; /* 4-level kernel page table */
 	}
 
+	adjust_to_uv_max(&vmax);
+
 	/* module area is at the end of the kernel address space. */
 	MODULES_END = vmax;
 	MODULES_VADDR = MODULES_END - MODULES_LEN;
@@ -1147,6 +1149,7 @@ void __init setup_arch(char **cmdline_p)
 	 */
 	memblock_trim_memory(1UL << (MAX_ORDER - 1 + PAGE_SHIFT));
 
+	setup_uv();
 	setup_memory_end();
 	setup_memory();
 	dma_contiguous_reserve(memory_end);
arch/s390/kernel/uv.c +53 -0
index 35ce89695509..f7778493e829 100644
@@ -45,4 +45,57 @@ static int __init prot_virt_setup(char *val)
 	return rc;
 }
 early_param("prot_virt", prot_virt_setup);
+
+static int __init uv_init(unsigned long stor_base, unsigned long stor_len)
+{
+	struct uv_cb_init uvcb = {
+		.header.cmd = UVC_CMD_INIT_UV,
+		.header.len = sizeof(uvcb),
+		.stor_origin = stor_base,
+		.stor_len = stor_len,
+	};
+	int cc;
+
+	cc = uv_call(0, (uint64_t)&uvcb);
+	if (cc || uvcb.header.rc != UVC_RC_EXECUTED) {
+		pr_err("Ultravisor init failed with cc: %d rc: 0x%hx\n", cc,
+		       uvcb.header.rc);
+		return -1;
+	}
+	return 0;
+}
+
+void __init setup_uv(void)
+{
+	unsigned long uv_stor_base;
+
+	if (!prot_virt_host)
+		return;
+
+	uv_stor_base = (unsigned long)memblock_alloc_try_nid(
+		uv_info.uv_base_stor_len, SZ_1M, SZ_2G,
+		MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);
+	if (!uv_stor_base) {
+		pr_info("Failed to reserve %lu bytes for ultravisor base storage\n",
+			uv_info.uv_base_stor_len);
+		goto fail;
+	}
+
+	if (uv_init(uv_stor_base, uv_info.uv_base_stor_len)) {
+		memblock_free(uv_stor_base, uv_info.uv_base_stor_len);
+		goto fail;
+	}
+
+	pr_info("Reserving %luMB as ultravisor base storage\n",
+		uv_info.uv_base_stor_len >> 20);
+	return;
+fail:
+	prot_virt_host = 0;
+}
+
+void adjust_to_uv_max(unsigned long *vmax)
+{
+	if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
+		*vmax = uv_info.max_sec_stor_addr;
+}
 #endif
#26 lore raw

Re: [RFC 03/37] s390/protvirt: add ultravisor initialization

In reply to Janosch Frank
Quoted 13 lines · On Thu, 24 Oct 2019 07:40:25 -0400 Janosch Frank <[email protected]> wrote:

From: Vasily Gorbik <[email protected]>

Before being able to host protected virtual machines, donate some of the memory to the ultravisor. Besides that the ultravisor might impose addressing limitations for memory used to back protected VM storage. Treat that limit as protected virtualization host's virtual memory limit.

---
 arch/s390/include/asm/uv.h | 16 ++++++++++++
 arch/s390/kernel/setup.c   |  3 +++
 arch/s390/kernel/uv.c      | 53 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)

(...)

Quoted 14 lines
arch/s390/kernel/uv.c +6 -0
index 35ce89695509..f7778493e829 100644
@@ -45,4 +45,57 @@ static int __init prot_virt_setup(char *val)
 	return rc;
 }
 early_param("prot_virt", prot_virt_setup);
+
+static int __init uv_init(unsigned long stor_base, unsigned long stor_len)
+{
+	struct uv_cb_init uvcb = {
+		.header.cmd = UVC_CMD_INIT_UV,
+		.header.len = sizeof(uvcb),

arch/s390/kernel/uv.c static int __init prot_virt_setup(char *val) · line 63

Quoted 10 lines
arch/s390/kernel/uv.c +10 -0
+		.stor_origin = stor_base,
+		.stor_len = stor_len,
+	};
+	int cc;
+
+	cc = uv_call(0, (uint64_t)&uvcb);
+	if (cc || uvcb.header.rc != UVC_RC_EXECUTED) {
+		pr_err("Ultravisor init failed with cc: %d rc: 0x%hx\n", cc,
+		       uvcb.header.rc);
+		return -1;

Is there any reasonable case where that call might fail if we have the facility installed? Bad stor_base, maybe?

Quoted 20 lines
arch/s390/kernel/uv.c +20 -0
+	}
+	return 0;
+}
+
+void __init setup_uv(void)
+{
+	unsigned long uv_stor_base;
+
+	if (!prot_virt_host)
+		return;
+
+	uv_stor_base = (unsigned long)memblock_alloc_try_nid(
+		uv_info.uv_base_stor_len, SZ_1M, SZ_2G,
+		MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);
+	if (!uv_stor_base) {
+		pr_info("Failed to reserve %lu bytes for ultravisor base storage\n",
+			uv_info.uv_base_stor_len);
+		goto fail;
+	}
+

arch/s390/kernel/uv.c static int __init prot_virt_setup(char *val) · line 93

Quoted 10 lines
arch/s390/kernel/uv.c +10 -0
+	if (uv_init(uv_stor_base, uv_info.uv_base_stor_len)) {
+		memblock_free(uv_stor_base, uv_info.uv_base_stor_len);
+		goto fail;
+	}
+
+	pr_info("Reserving %luMB as ultravisor base storage\n",
+		uv_info.uv_base_stor_len >> 20);
+	return;
+fail:
+	prot_virt_host = 0;

So, what happens if the user requested protected virtualization and any of the above failed? We turn off host support, so any attempt to start a protected virtualization guest on that host will fail (hopefully with a meaningful error), I guess.

Is there any use case where we'd want to make failure to set this up fatal?

Quoted 8 lines
arch/s390/kernel/uv.c +7 -0
+}
+
+void adjust_to_uv_max(unsigned long *vmax)
+{
+	if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
+		*vmax = uv_info.max_sec_stor_addr;
+}
 #endif
author maintainer
#27 lore raw

Re: [RFC 03/37] s390/protvirt: add ultravisor initialization

Quoted 38 lines · Cornelia Huck
Quoted 13 lines · On Thu, 24 Oct 2019 07:40:25 -0400 Janosch Frank <[email protected]> wrote:

From: Vasily Gorbik <[email protected]>

Before being able to host protected virtual machines, donate some of the memory to the ultravisor. Besides that the ultravisor might impose addressing limitations for memory used to back protected VM storage. Treat that limit as protected virtualization host's virtual memory limit.

---
 arch/s390/include/asm/uv.h | 16 ++++++++++++
 arch/s390/kernel/setup.c   |  3 +++
 arch/s390/kernel/uv.c      | 53 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)

(...)

Quoted 19 lines
arch/s390/kernel/uv.c +11 -0
index 35ce89695509..f7778493e829 100644
@@ -45,4 +45,57 @@ static int __init prot_virt_setup(char *val)
 	return rc;
 }
 early_param("prot_virt", prot_virt_setup);
+
+static int __init uv_init(unsigned long stor_base, unsigned long stor_len)
+{
+	struct uv_cb_init uvcb = {
+		.header.cmd = UVC_CMD_INIT_UV,
+		.header.len = sizeof(uvcb),
+		.stor_origin = stor_base,
+		.stor_len = stor_len,
+	};
+	int cc;
+
Quoted 8 lines

arch/s390/kernel/uv.c static int __init prot_virt_setup(char *val) · line 63

Quoted 5 lines
arch/s390/kernel/uv.c +5 -0
+	cc = uv_call(0, (uint64_t)&uvcb);
+	if (cc || uvcb.header.rc != UVC_RC_EXECUTED) {
+		pr_err("Ultravisor init failed with cc: %d rc: 0x%hx\n", cc,
+		       uvcb.header.rc);
+		return -1;

Is there any reasonable case where that call might fail if we have the facility installed? Bad stor_base, maybe?

Yes, wrong storage locations, length, etc...
Also if we are running with more than one CPU or the Ultravisor encountered some internal error.

Quoted 28 lines
Quoted 28 lines
arch/s390/kernel/uv.c +27 -0
+	}
+	return 0;
+}
+
+void __init setup_uv(void)
+{
+	unsigned long uv_stor_base;
+
+	if (!prot_virt_host)
+		return;
+
+	uv_stor_base = (unsigned long)memblock_alloc_try_nid(
+		uv_info.uv_base_stor_len, SZ_1M, SZ_2G,
+		MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);
+	if (!uv_stor_base) {
+		pr_info("Failed to reserve %lu bytes for ultravisor base storage\n",
+			uv_info.uv_base_stor_len);
+		goto fail;
+	}
+
+	if (uv_init(uv_stor_base, uv_info.uv_base_stor_len)) {
+		memblock_free(uv_stor_base, uv_info.uv_base_stor_len);
+		goto fail;
+	}
+
+	pr_info("Reserving %luMB as ultravisor base storage\n",
+		uv_info.uv_base_stor_len >> 20);
Quoted 9 lines

arch/s390/kernel/uv.c static int __init prot_virt_setup(char *val) · line 93

Quoted 3 lines
arch/s390/kernel/uv.c +3 -0
+	return;
+fail:
+	prot_virt_host = 0;

So, what happens if the user requested protected virtualization and any of the above failed? We turn off host support, so any attempt to start a protected virtualization guest on that host will fail (hopefully with a meaningful error), I guess.

STFLE 161, and the associated diag308 subcodes 8-10 will not be available to any VM. So yes, the stuv that starts a protected guest will print a message.

Quoted 2 lines

Is there any use case where we'd want to make failure to set this up fatal?

Not really.

Quoted 10 lines
Quoted 9 lines
arch/s390/kernel/uv.c +7 -0
+}
+
+void adjust_to_uv_max(unsigned long *vmax)
+{
+	if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
+		*vmax = uv_info.max_sec_stor_addr;
+}
 #endif
author maintainer
#28 lore raw

RFC 4/37 KVM: s390: protvirt: Add initial lifecycle handling

In reply to Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

---
 arch/s390/include/asm/kvm_host.h |  24 +++-
 arch/s390/include/asm/uv.h       | 110 ++++++++++++++
 arch/s390/kvm/Makefile           |   2 +-
 arch/s390/kvm/kvm-s390.c         | 173 +++++++++++++++++++++-
 arch/s390/kvm/kvm-s390.h         |  47 ++++++
 arch/s390/kvm/pv.c               | 237 +++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h         |  33 +++++
 7 files changed, 622 insertions(+), 4 deletions(-)
 create mode 100644 arch/s390/kvm/pv.c
arch/s390/include/asm/kvm_host.h +22 -2
index 02f4c21c57f6..d4fd0f3af676 100644
@@ -155,7 +155,13 @@ struct kvm_s390_sie_block {
 	__u8	reserved08[4];		/* 0x0008 */
 #define PROG_IN_SIE (1<<0)
 	__u32	prog0c;			/* 0x000c */
-	__u8	reserved10[16];		/* 0x0010 */
+	union {
+		__u8	reserved10[16];		/* 0x0010 */
+		struct {
+			__u64	pv_handle_cpu;
+			__u64	pv_handle_config;
+		};
+	};
 #define PROG_BLOCK_SIE	(1<<0)
 #define PROG_REQUEST	(1<<1)
 	atomic_t prog20;		/* 0x0020 */
@@ -228,7 +234,7 @@ struct kvm_s390_sie_block {
 #define ECB3_RI  0x01
 	__u8    ecb3;			/* 0x0063 */
 	__u32	scaol;			/* 0x0064 */
-	__u8	reserved68;		/* 0x0068 */
+	__u8	sdf;			/* 0x0068 */
 	__u8    epdx;			/* 0x0069 */
 	__u8    reserved6a[2];		/* 0x006a */
 	__u32	todpr;			/* 0x006c */
@@ -640,6 +646,11 @@ struct kvm_guestdbg_info_arch {
 	unsigned long last_bp;
 };
 
+struct kvm_s390_pv_vcpu {
+	u64 handle;
+	unsigned long stor_base;
+};
+
 struct kvm_vcpu_arch {
 	struct kvm_s390_sie_block *sie_block;
 	/* if vsie is active, currently executed shadow sie control block */
@@ -668,6 +679,7 @@ struct kvm_vcpu_arch {
 	__u64 cputm_start;
 	bool gs_enabled;
 	bool skey_enabled;
+	struct kvm_s390_pv_vcpu pv;
 };
 
 struct kvm_vm_stat {
@@ -841,6 +853,13 @@ struct kvm_s390_gisa_interrupt {
 	DECLARE_BITMAP(kicked_mask, KVM_MAX_VCPUS);
 };
 
+struct kvm_s390_pv {
+	u64 handle;
+	u64 guest_len;
+	unsigned long stor_base;
+	void *stor_var;
+};
+
 struct kvm_arch{
 	void *sca;
 	int use_esca;
@@ -876,6 +895,7 @@ struct kvm_arch{
 	DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
 	DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS);
 	struct kvm_s390_gisa_interrupt gisa_int;
+	struct kvm_s390_pv pv;
 };
 
 #define KVM_HVA_ERR_BAD		(-1UL)
arch/s390/include/asm/uv.h +110 -0

145 lines not on this page · raw message

arch/s390/kvm/Makefile +1 -1
index 05ee90a5ea08..eaaedf9e61a7 100644
@@ -9,6 +9,6 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o  $(KVM)/async_pf.o $(KVM)/irqch
 ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
 
 kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o
-kvm-objs += diag.o gaccess.o guestdbg.o vsie.o
+kvm-objs += diag.o gaccess.o guestdbg.o vsie.o $(if $(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST),pv.o)
 
 obj-$(CONFIG_KVM) += kvm.o
arch/s390/kvm/kvm-s390.c +172 -1

240 lines not on this page · raw message

arch/s390/kvm/kvm-s390.h +47 -0

53 lines not on this page · raw message

arch/s390/kvm/pv.c new file +237 -0

237 lines not on this page · raw message

include/uapi/linux/kvm.h +33 -0
index 52641d8ca9e8..bb37d5710c89 100644
@@ -1000,6 +1000,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_PMU_EVENT_FILTER 173
 #define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
 #define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175
+#define KVM_CAP_S390_PROTECTED 180
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1461,6 +1462,38 @@ struct kvm_enc_region {
 /* Available with KVM_CAP_ARM_SVE */
 #define KVM_ARM_VCPU_FINALIZE	  _IOW(KVMIO,  0xc2, int)
 
+struct kvm_s390_pv_sec_parm {
+	__u64	origin;
+	__u64	length;
+};
+
+struct kvm_s390_pv_unp {
+	__u64 addr;
+	__u64 size;
+	__u64 tweak;
+};
+
+enum pv_cmd_id {
+	KVM_PV_VM_CREATE,
+	KVM_PV_VM_DESTROY,
+	KVM_PV_VM_SET_SEC_PARMS,
+	KVM_PV_VM_UNPACK,
+	KVM_PV_VM_VERIFY,
+	KVM_PV_VCPU_CREATE,
+	KVM_PV_VCPU_DESTROY,
+};
+
+struct kvm_pv_cmd {
+	__u32	cmd;
+	__u16	rc;
+	__u16	rrc;
+	__u64	data;
+};
+
+/* Available with KVM_CAP_S390_SE */
+#define KVM_S390_PV_COMMAND		_IOW(KVMIO, 0xc3, struct kvm_pv_cmd)
+#define KVM_S390_PV_COMMAND_VCPU	_IOW(KVMIO, 0xc4, struct kvm_pv_cmd)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 	/* Guest initialization commands */
Signature · 1 line
2.20.1
#29 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 1 line · Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

More details please.

[...]

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2177

Quoted 12 lines
arch/s390/kvm/kvm-s390.c +11 -0
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VM_CREATE: {
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;

So ... I can create multiple VMs?

Especially, I can call KVM_PV_VM_CREATE two times, setting "kvm->arch.pv.stor_var = NULL and leaking memory" on the second call. Not sure if that's desirable.

Shouldn't this be something like "KVM_PV_VM_INIT" and then make sure it can only be called once?

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2190

Quoted 13 lines
arch/s390/kvm/kvm-s390.c +13 -0
+
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_DESTROY: {
+		/* All VCPUs have to be destroyed before this call. */

Then please verify that? "KVM_PV_VM_DEINIT"

Also, who guarantees that user space calls this at all? Why is that needed? (IOW, when does user space call this?)

Quoted 297 lines
arch/s390/kvm/kvm-s390.c +141 -1
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		r = kvm_s390_pv_destroy_vm(kvm);
+		if (!r)
+			kvm_s390_pv_dealloc_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_SET_SEC_PARMS: {
+		struct kvm_s390_pv_sec_parm parms = {};
+		void *hdr;
+
+		r = -EFAULT;
+		if (copy_from_user(&parms, argp, sizeof(parms)))
+			break;
+
+		/* Currently restricted to 8KB */
+		r = -EINVAL;
+		if (parms.length > PAGE_SIZE * 2)
+			break;
+
+		r = -ENOMEM;
+		hdr = vmalloc(parms.length);
+		if (!hdr)
+			break;
+
+		r = -EFAULT;
+		if (!copy_from_user(hdr, (void __user *)parms.origin,
+				   parms.length))
+			r = kvm_s390_pv_set_sec_parms(kvm, hdr, parms.length);
+
+		vfree(hdr);
+		break;
+	}
+	case KVM_PV_VM_UNPACK: {
+		struct kvm_s390_pv_unp unp = {};
+
+		r = -EFAULT;
+		if (copy_from_user(&unp, argp, sizeof(unp)))
+			break;
+
+		r = kvm_s390_pv_unpack(kvm, unp.addr, unp.size, unp.tweak);
+		break;
+	}
+	case KVM_PV_VM_VERIFY: {
+		u32 ret;
+
+		r = -EINVAL;
+		if (!kvm_s390_pv_is_protected(kvm))
+			break;
+
+		r = uv_cmd_nodata(kvm_s390_pv_handle(kvm),
+				  UVC_CMD_VERIFY_IMG,
+				  &ret);
+		VM_EVENT(kvm, 3, "PROTVIRT VERIFY: rc %x rrc %x",
+			 ret >> 16, ret & 0x0000ffff);
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+	return r;
+}
+#endif
+
  long kvm_arch_vm_ioctl(struct file *filp,
  		       unsigned int ioctl, unsigned long arg)
  {
@@ -2254,6 +2351,22 @@ long kvm_arch_vm_ioctl(struct file *filp,
  		mutex_unlock(&kvm->slots_lock);
  		break;
  	}
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	case KVM_S390_PV_COMMAND: {
+		struct kvm_pv_cmd args;
+
+		r = -EINVAL;
+		if (!is_prot_virt_host())
+			break;
+
+		r = -EFAULT;
+		if (copy_from_user(&args, argp, sizeof(args)))
+			break;
+
+		r = kvm_s390_handle_pv(kvm, &args);
+		break;
+	}
+#endif
  	default:
  		r = -ENOTTY;
  	}
@@ -2529,6 +2642,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
  	if (vcpu->kvm->arch.use_cmma)
  		kvm_s390_vcpu_unsetup_cmma(vcpu);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_handle_cpu(vcpu))
+		kvm_s390_pv_destroy_cpu(vcpu);
  	free_page((unsigned long)(vcpu->arch.sie_block));
 
  	kvm_vcpu_uninit(vcpu);
@@ -2555,8 +2671,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
  {
  	kvm_free_vcpus(kvm);
  	sca_dispose(kvm);
-	debug_unregister(kvm->arch.dbf);
  	kvm_s390_gisa_destroy(kvm);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);
+	}
+	debug_unregister(kvm->arch.dbf);
  	free_page((unsigned long)kvm->arch.sie_page2);
  	if (!kvm_is_ucontrol(kvm))
  		gmap_remove(kvm->arch.gmap);
@@ -2652,6 +2773,9 @@ static int sca_switch_to_extended(struct kvm *kvm)
  	unsigned int vcpu_idx;
  	u32 scaol, scaoh;
 
+	if (kvm->arch.use_esca)
+		return 0;
+
  	new_sca = alloc_pages_exact(sizeof(*new_sca), GFP_KERNEL|__GFP_ZERO);
  	if (!new_sca)
  		return -ENOMEM;
@@ -3073,6 +3197,15 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
  	rc = kvm_vcpu_init(vcpu, kvm, id);
  	if (rc)
  		goto out_free_sie_block;
+
+	if (kvm_s390_pv_is_protected(kvm)) {
+		rc = kvm_s390_pv_create_cpu(vcpu);
+		if (rc) {
+			kvm_vcpu_uninit(vcpu);
+			goto out_free_sie_block;
+		}
+	}
+
  	VM_EVENT(kvm, 3, "create cpu %d at 0x%pK, sie block at 0x%pK", id, vcpu,
  		 vcpu->arch.sie_block);
  	trace_kvm_s390_create_vcpu(id, vcpu, vcpu->arch.sie_block);
@@ -4338,6 +4471,28 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp,
  	return -ENOIOCTLCMD;
  }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv_vcpu(struct kvm_vcpu *vcpu,
+				   struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VCPU_CREATE: {
+		r = kvm_s390_pv_create_cpu(vcpu);
+		break;
+	}
+	case KVM_PV_VCPU_DESTROY: {
+		r = kvm_s390_pv_destroy_cpu(vcpu);
+		break;
+	}
+	default:
+		r = -ENOTTY;
+	}
+	return r;
+}
+#endif
+
  long kvm_arch_vcpu_ioctl(struct file *filp,
  			 unsigned int ioctl, unsigned long arg)
  {
@@ -4470,6 +4625,22 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
  					   irq_state.len);
  		break;
  	}
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	case KVM_S390_PV_COMMAND_VCPU: {
+		struct kvm_pv_cmd args;
+
+		r = -EINVAL;
+		if (!is_prot_virt_host())
+			break;
+
+		r = -EFAULT;
+		if (copy_from_user(&args, argp, sizeof(args)))
+			break;
+
+		r = kvm_s390_handle_pv_vcpu(vcpu, &args);
+		break;
+	}
+#endif
  	default:
  		r = -ENOTTY;
  	}
arch/s390/kvm/kvm-s390.h +47 -0
index 6d9448dbd052..0d61dcc51f0e 100644
@@ -196,6 +196,53 @@ static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm)
  	return kvm->arch.user_cpu_state_ctrl != 0;
  }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+/* implemented in pv.c */
+void kvm_s390_pv_unpin(struct kvm *kvm);
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm);
+int kvm_s390_pv_alloc_vm(struct kvm *kvm);
+int kvm_s390_pv_create_vm(struct kvm *kvm);
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_destroy_vm(struct kvm *kvm);
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length);
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak);
+int kvm_s390_pv_verify(struct kvm *kvm);
+
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm)
+{
+	return !!kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm)
+{
+	return kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu)
+{
+	return vcpu->arch.pv.handle;
+}
+#else
+static inline void kvm_s390_pv_unpin(struct kvm *kvm) {}
+static inline void kvm_s390_pv_dealloc_vm(struct kvm *kvm) {}
+static inline int kvm_s390_pv_alloc_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_destroy_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+					    u64 origin, u64 length) { return 0; }
+static inline int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr,
+				     unsigned long size,  unsigned long tweak)
+{ return 0; }
+static inline int kvm_s390_pv_verify(struct kvm *kvm) { return 0; }
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu) { return 0; }
+#endif
+
  /* implemented in interrupt.c */
  int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
  void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu);
arch/s390/kvm/pv.c new file +38 -0
new file mode 100644
index 000000000000..94cf16f40f25
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hosting Secure Execution virtual machines
+ *
+ * Copyright IBM Corp. 2019
+ *    Author(s): Janosch Frank <[email protected]>
+ */
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/pagemap.h>
+#include <asm/pgalloc.h>
+#include <asm/gmap.h>
+#include <asm/uv.h>
+#include <asm/gmap.h>
+#include <asm/mman.h>
+#include "kvm-s390.h"
+
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm)
+{
+	vfree(kvm->arch.pv.stor_var);
+	free_pages(kvm->arch.pv.stor_base,
+		   get_order(uv_info.guest_base_stor_len));
+	memset(&kvm->arch.pv, 0, sizeof(kvm->arch.pv));
+}
+
+int kvm_s390_pv_alloc_vm(struct kvm *kvm)
+{
+	unsigned long base = uv_info.guest_base_stor_len;
+	unsigned long virt = uv_info.guest_virt_var_stor_len;
+	unsigned long npages = 0, vlen = 0;
+	struct kvm_memslots *slots;
+	struct kvm_memory_slot *memslot;
+
+	kvm->arch.pv.stor_var = NULL;
+	kvm->arch.pv.stor_base = __get_free_pages(GFP_KERNEL, get_order(base));
+	if (!kvm->arch.pv.stor_base)
+		return -ENOMEM;
+

arch/s390/kvm/pv.c line 48

Quoted 10 lines
arch/s390/kvm/pv.c new file +10 -0
+	/*
+	 * Calculate current guest storage for allocation of the
+	 * variable storage, which is based on the length in MB.
+	 *
+	 * Slots are sorted by GFN
+	 */
+	mutex_lock(&kvm->slots_lock);
+	slots = kvm_memslots(kvm);
+	memslot = slots->memslots;
+	npages = memslot->base_gfn + memslot->npages;

What if

a) your guest has multiple memory slots
b) you hotplug memory and add memslots later

Do you dence that, and if so, how?

arch/s390/kvm/pv.c line 54

Quoted 6 lines
arch/s390/kvm/pv.c new file +6 -0
+
+	mutex_unlock(&kvm->slots_lock);
+	kvm->arch.pv.guest_len = npages * PAGE_SIZE;
+
+	/* Allocate variable storage */
+	vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE);

I get the feeling that prot virt mainly consumes memory ;)

Quoted 225 lines
arch/s390/kvm/pv.c new file +183 -0
+	vlen += uv_info.guest_virt_base_stor_len;
+	kvm->arch.pv.stor_var = vzalloc(vlen);
+	if (!kvm->arch.pv.stor_var) {
+		kvm_s390_pv_dealloc_vm(kvm);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+int kvm_s390_pv_destroy_vm(struct kvm *kvm)
+{
+	int rc;
+	u32 ret;
+
+	rc = uv_cmd_nodata(kvm_s390_pv_handle(kvm),
+			   UVC_CMD_DESTROY_SEC_CONF, &ret);
+	VM_EVENT(kvm, 3, "PROTVIRT DESTROY VM: rc %x rrc %x",
+		 ret >> 16, ret & 0x0000ffff);
+	return rc;
+}
+
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc = 0;
+	u32 ret;
+
+	if (kvm_s390_pv_handle_cpu(vcpu)) {
+		rc = uv_cmd_nodata(kvm_s390_pv_handle_cpu(vcpu),
+				   UVC_CMD_DESTROY_SEC_CPU,
+				   &ret);
+
+		VCPU_EVENT(vcpu, 3, "PROTVIRT DESTROY VCPU: cpu %d rc %x rrc %x",
+			   vcpu->vcpu_id, ret >> 16, ret & 0x0000ffff);
+	}
+
+	free_pages(vcpu->arch.pv.stor_base,
+		   get_order(uv_info.guest_cpu_stor_len));
+	/* Clear cpu and vm handle */
+	memset(&vcpu->arch.sie_block->reserved10, 0,
+	       sizeof(vcpu->arch.sie_block->reserved10));
+	memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
+	vcpu->arch.sie_block->sdf = 0;
+	return rc;
+}
+
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc;
+	struct uv_cb_csc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CPU,
+		.header.len = sizeof(uvcb),
+	};
+
+	/* EEXIST and ENOENT? */
+	if (kvm_s390_pv_handle_cpu(vcpu))
+		return -EINVAL;
+
+	vcpu->arch.pv.stor_base = __get_free_pages(GFP_KERNEL,
+						   get_order(uv_info.guest_cpu_stor_len));
+	if (!vcpu->arch.pv.stor_base)
+		return -ENOMEM;
+
+	/* Input */
+	uvcb.guest_handle = kvm_s390_pv_handle(vcpu->kvm);
+	uvcb.num = vcpu->arch.sie_block->icpua;
+	uvcb.state_origin = (u64)vcpu->arch.sie_block;
+	uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VCPU_EVENT(vcpu, 3, "PROTVIRT CREATE VCPU: cpu %d handle %llx rc %x rrc %x",
+		   vcpu->vcpu_id, uvcb.cpu_handle, uvcb.header.rc,
+		   uvcb.header.rrc);
+
+	/* Output */
+	vcpu->arch.pv.handle = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_cpu = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_config = kvm_s390_pv_handle(vcpu->kvm);
+	vcpu->arch.sie_block->sdf = 2;
+	if (!rc)
+		return 0;
+
+	kvm_s390_pv_destroy_cpu(vcpu);
+	return -EINVAL;
+}
+
+int kvm_s390_pv_create_vm(struct kvm *kvm)
+{
+	int rc;
+
+	struct uv_cb_cgc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CONF,
+		.header.len = sizeof(uvcb)
+	};
+
+	if (kvm_s390_pv_handle(kvm))
+		return -EINVAL;
+
+	/* Inputs */
+	uvcb.guest_stor_origin = 0; /* MSO is 0 for KVM */
+	uvcb.guest_stor_len = kvm->arch.pv.guest_len;
+	uvcb.guest_asce = kvm->arch.gmap->asce;
+	uvcb.conf_base_stor_origin = (u64)kvm->arch.pv.stor_base;
+	uvcb.conf_var_stor_origin = (u64)kvm->arch.pv.stor_var;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VM_EVENT(kvm, 3, "PROTVIRT CREATE VM: handle %llx len %llx rc %x rrc %x",
+		 uvcb.guest_handle, uvcb.guest_stor_len, uvcb.header.rc,
+		 uvcb.header.rrc);
+
+	/* Outputs */
+	kvm->arch.pv.handle = uvcb.guest_handle;
+
+	if (rc && (uvcb.header.rc & 0x8000)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+			      void *hdr, u64 length)
+{
+	int rc;
+	struct uv_cb_ssc uvcb = {
+		.header.cmd = UVC_CMD_SET_SEC_CONF_PARAMS,
+		.header.len = sizeof(uvcb),
+		.sec_header_origin = (u64)hdr,
+		.sec_header_len = length,
+		.guest_handle = kvm_s390_pv_handle(kvm),
+	};
+
+	if (!kvm_s390_pv_handle(kvm))
+		return -EINVAL;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VM_EVENT(kvm, 3, "PROTVIRT VM SET PARMS: rc %x rrc %x",
+		 uvcb.header.rc, uvcb.header.rrc);
+	if (rc)
+		return -EINVAL;
+	return 0;
+}
+
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int i, rc = 0;
+	struct uv_cb_unp uvcb = {
+		.header.cmd = UVC_CMD_UNPACK_IMG,
+		.header.len = sizeof(uvcb),
+		.guest_handle = kvm_s390_pv_handle(kvm),
+		.tweak[0] = tweak
+	};
+
+	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
+		return -EINVAL;
+
+
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
+		 addr, size);
+	for (i = 0; i < size / PAGE_SIZE; i++) {
+		uvcb.gaddr = addr + i * PAGE_SIZE;
+		uvcb.tweak[1] = i * PAGE_SIZE;
+retry:
+		rc = uv_call(0, (u64)&uvcb);
+		if (!rc)
+			continue;
+		/* If not yet mapped fault and retry */
+		if (uvcb.header.rc == 0x10a) {
+			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+					FAULT_FLAG_WRITE);
+			if (rc)
+				return rc;
+			goto retry;
+		}
+		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+		break;
+	}
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished with rc %x rrc %x",
+		 uvcb.header.rc, uvcb.header.rrc);
+	return rc;
+}
include/uapi/linux/kvm.h +26 -0
index 52641d8ca9e8..bb37d5710c89 100644
@@ -1000,6 +1000,7 @@ struct kvm_ppc_resize_hpt {
  #define KVM_CAP_PMU_EVENT_FILTER 173
  #define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
  #define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175
+#define KVM_CAP_S390_PROTECTED 180
 
  #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1461,6 +1462,38 @@ struct kvm_enc_region {
  /* Available with KVM_CAP_ARM_SVE */
  #define KVM_ARM_VCPU_FINALIZE	  _IOW(KVMIO,  0xc2, int)
 
+struct kvm_s390_pv_sec_parm {
+	__u64	origin;
+	__u64	length;
+};
+
+struct kvm_s390_pv_unp {
+	__u64 addr;
+	__u64 size;
+	__u64 tweak;
+};
+
+enum pv_cmd_id {
+	KVM_PV_VM_CREATE,
+	KVM_PV_VM_DESTROY,
+	KVM_PV_VM_SET_SEC_PARMS,
+	KVM_PV_VM_UNPACK,
+	KVM_PV_VM_VERIFY,
+	KVM_PV_VCPU_CREATE,
+	KVM_PV_VCPU_DESTROY,
+};
+
+struct kvm_pv_cmd {
+	__u32	cmd;
+	__u16	rc;
+	__u16	rrc;

include/uapi/linux/kvm.h struct kvm_enc_region { · line 1499

Quoted 10 lines
include/uapi/linux/kvm.h +7 -0
+	__u64	data;
+};
+
+/* Available with KVM_CAP_S390_SE */
+#define KVM_S390_PV_COMMAND		_IOW(KVMIO, 0xc3, struct kvm_pv_cmd)
+#define KVM_S390_PV_COMMAND_VCPU	_IOW(KVMIO, 0xc4, struct kvm_pv_cmd)
+
  /* Secure Encrypted Virtualization command */
  enum sev_cmd_id {
  	/* Guest initialization commands */

This is a lengthy patch and I ahven't explored anything yet :)

I do wonder if it makes sense to split this up. VM, VCPUs, parameters, Extract+verify ...

Signature · 3 lines
Thanks,

David / dhildenb
#30 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 602 lines · David Hildenbrand
Quoted 1 line · Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

More details please.

[...]

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2177

Quoted 12 lines
arch/s390/kvm/kvm-s390.c +11 -0
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VM_CREATE: {
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;

So ... I can create multiple VMs?

Especially, I can call KVM_PV_VM_CREATE two times, setting "kvm->arch.pv.stor_var = NULL and leaking memory" on the second call. Not sure if that's desirable.

Shouldn't this be something like "KVM_PV_VM_INIT" and then make sure it can only be called once?

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2190

Quoted 13 lines
arch/s390/kvm/kvm-s390.c +13 -0
+
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_DESTROY: {
+		/* All VCPUs have to be destroyed before this call. */

Then please verify that? "KVM_PV_VM_DEINIT"

Also, who guarantees that user space calls this at all? Why is that needed? (IOW, when does user space call this?)

Quoted 297 lines
arch/s390/kvm/kvm-s390.c +141 -1
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		r = kvm_s390_pv_destroy_vm(kvm);
+		if (!r)
+			kvm_s390_pv_dealloc_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_SET_SEC_PARMS: {
+		struct kvm_s390_pv_sec_parm parms = {};
+		void *hdr;
+
+		r = -EFAULT;
+		if (copy_from_user(&parms, argp, sizeof(parms)))
+			break;
+
+		/* Currently restricted to 8KB */
+		r = -EINVAL;
+		if (parms.length > PAGE_SIZE * 2)
+			break;
+
+		r = -ENOMEM;
+		hdr = vmalloc(parms.length);
+		if (!hdr)
+			break;
+
+		r = -EFAULT;
+		if (!copy_from_user(hdr, (void __user *)parms.origin,
+				   parms.length))
+			r = kvm_s390_pv_set_sec_parms(kvm, hdr, parms.length);
+
+		vfree(hdr);
+		break;
+	}
+	case KVM_PV_VM_UNPACK: {
+		struct kvm_s390_pv_unp unp = {};
+
+		r = -EFAULT;
+		if (copy_from_user(&unp, argp, sizeof(unp)))
+			break;
+
+		r = kvm_s390_pv_unpack(kvm, unp.addr, unp.size, unp.tweak);
+		break;
+	}
+	case KVM_PV_VM_VERIFY: {
+		u32 ret;
+
+		r = -EINVAL;
+		if (!kvm_s390_pv_is_protected(kvm))
+			break;
+
+		r = uv_cmd_nodata(kvm_s390_pv_handle(kvm),
+				  UVC_CMD_VERIFY_IMG,
+				  &ret);
+		VM_EVENT(kvm, 3, "PROTVIRT VERIFY: rc %x rrc %x",
+			 ret >> 16, ret & 0x0000ffff);
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+	return r;
+}
+#endif
+
   long kvm_arch_vm_ioctl(struct file *filp,
   		       unsigned int ioctl, unsigned long arg)
   {
@@ -2254,6 +2351,22 @@ long kvm_arch_vm_ioctl(struct file *filp,
   		mutex_unlock(&kvm->slots_lock);
   		break;
   	}
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	case KVM_S390_PV_COMMAND: {
+		struct kvm_pv_cmd args;
+
+		r = -EINVAL;
+		if (!is_prot_virt_host())
+			break;
+
+		r = -EFAULT;
+		if (copy_from_user(&args, argp, sizeof(args)))
+			break;
+
+		r = kvm_s390_handle_pv(kvm, &args);
+		break;
+	}
+#endif
   	default:
   		r = -ENOTTY;
   	}
@@ -2529,6 +2642,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
   	if (vcpu->kvm->arch.use_cmma)
   		kvm_s390_vcpu_unsetup_cmma(vcpu);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_handle_cpu(vcpu))
+		kvm_s390_pv_destroy_cpu(vcpu);
   	free_page((unsigned long)(vcpu->arch.sie_block));
 
   	kvm_vcpu_uninit(vcpu);
@@ -2555,8 +2671,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
   {
   	kvm_free_vcpus(kvm);
   	sca_dispose(kvm);
-	debug_unregister(kvm->arch.dbf);
   	kvm_s390_gisa_destroy(kvm);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);
+	}
+	debug_unregister(kvm->arch.dbf);
   	free_page((unsigned long)kvm->arch.sie_page2);
   	if (!kvm_is_ucontrol(kvm))
   		gmap_remove(kvm->arch.gmap);
@@ -2652,6 +2773,9 @@ static int sca_switch_to_extended(struct kvm *kvm)
   	unsigned int vcpu_idx;
   	u32 scaol, scaoh;
 
+	if (kvm->arch.use_esca)
+		return 0;
+
   	new_sca = alloc_pages_exact(sizeof(*new_sca), GFP_KERNEL|__GFP_ZERO);
   	if (!new_sca)
   		return -ENOMEM;
@@ -3073,6 +3197,15 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
   	rc = kvm_vcpu_init(vcpu, kvm, id);
   	if (rc)
   		goto out_free_sie_block;
+
+	if (kvm_s390_pv_is_protected(kvm)) {
+		rc = kvm_s390_pv_create_cpu(vcpu);
+		if (rc) {
+			kvm_vcpu_uninit(vcpu);
+			goto out_free_sie_block;
+		}
+	}
+
   	VM_EVENT(kvm, 3, "create cpu %d at 0x%pK, sie block at 0x%pK", id, vcpu,
   		 vcpu->arch.sie_block);
   	trace_kvm_s390_create_vcpu(id, vcpu, vcpu->arch.sie_block);
@@ -4338,6 +4471,28 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp,
   	return -ENOIOCTLCMD;
   }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv_vcpu(struct kvm_vcpu *vcpu,
+				   struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VCPU_CREATE: {
+		r = kvm_s390_pv_create_cpu(vcpu);
+		break;
+	}
+	case KVM_PV_VCPU_DESTROY: {
+		r = kvm_s390_pv_destroy_cpu(vcpu);
+		break;
+	}
+	default:
+		r = -ENOTTY;
+	}
+	return r;
+}
+#endif
+
   long kvm_arch_vcpu_ioctl(struct file *filp,
   			 unsigned int ioctl, unsigned long arg)
   {
@@ -4470,6 +4625,22 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
   					   irq_state.len);
   		break;
   	}
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+	case KVM_S390_PV_COMMAND_VCPU: {
+		struct kvm_pv_cmd args;
+
+		r = -EINVAL;
+		if (!is_prot_virt_host())
+			break;
+
+		r = -EFAULT;
+		if (copy_from_user(&args, argp, sizeof(args)))
+			break;
+
+		r = kvm_s390_handle_pv_vcpu(vcpu, &args);
+		break;
+	}
+#endif
   	default:
   		r = -ENOTTY;
   	}
arch/s390/kvm/kvm-s390.h +47 -0
index 6d9448dbd052..0d61dcc51f0e 100644
@@ -196,6 +196,53 @@ static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm)
   	return kvm->arch.user_cpu_state_ctrl != 0;
   }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+/* implemented in pv.c */
+void kvm_s390_pv_unpin(struct kvm *kvm);
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm);
+int kvm_s390_pv_alloc_vm(struct kvm *kvm);
+int kvm_s390_pv_create_vm(struct kvm *kvm);
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_destroy_vm(struct kvm *kvm);
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length);
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak);
+int kvm_s390_pv_verify(struct kvm *kvm);
+
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm)
+{
+	return !!kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm)
+{
+	return kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu)
+{
+	return vcpu->arch.pv.handle;
+}
+#else
+static inline void kvm_s390_pv_unpin(struct kvm *kvm) {}
+static inline void kvm_s390_pv_dealloc_vm(struct kvm *kvm) {}
+static inline int kvm_s390_pv_alloc_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_destroy_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+					    u64 origin, u64 length) { return 0; }
+static inline int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr,
+				     unsigned long size,  unsigned long tweak)
+{ return 0; }
+static inline int kvm_s390_pv_verify(struct kvm *kvm) { return 0; }
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu) { return 0; }
+#endif
+
   /* implemented in interrupt.c */
   int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
   void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu);
arch/s390/kvm/pv.c new file +38 -0
new file mode 100644
index 000000000000..94cf16f40f25
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hosting Secure Execution virtual machines
+ *
+ * Copyright IBM Corp. 2019
+ *    Author(s): Janosch Frank <[email protected]>
+ */
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/pagemap.h>
+#include <asm/pgalloc.h>
+#include <asm/gmap.h>
+#include <asm/uv.h>
+#include <asm/gmap.h>
+#include <asm/mman.h>
+#include "kvm-s390.h"
+
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm)
+{
+	vfree(kvm->arch.pv.stor_var);
+	free_pages(kvm->arch.pv.stor_base,
+		   get_order(uv_info.guest_base_stor_len));
+	memset(&kvm->arch.pv, 0, sizeof(kvm->arch.pv));
+}
+
+int kvm_s390_pv_alloc_vm(struct kvm *kvm)
+{
+	unsigned long base = uv_info.guest_base_stor_len;
+	unsigned long virt = uv_info.guest_virt_var_stor_len;
+	unsigned long npages = 0, vlen = 0;
+	struct kvm_memslots *slots;
+	struct kvm_memory_slot *memslot;
+
+	kvm->arch.pv.stor_var = NULL;
+	kvm->arch.pv.stor_base = __get_free_pages(GFP_KERNEL, get_order(base));
+	if (!kvm->arch.pv.stor_base)
+		return -ENOMEM;
+

arch/s390/kvm/pv.c line 48

Quoted 10 lines
arch/s390/kvm/pv.c new file +10 -0
+	/*
+	 * Calculate current guest storage for allocation of the
+	 * variable storage, which is based on the length in MB.
+	 *
+	 * Slots are sorted by GFN
+	 */
+	mutex_lock(&kvm->slots_lock);
+	slots = kvm_memslots(kvm);
+	memslot = slots->memslots;
+	npages = memslot->base_gfn + memslot->npages;

What if

a) your guest has multiple memory slots
b) you hotplug memory and add memslots later

Do you dence that, and if so, how?

arch/s390/kvm/pv.c line 54

Quoted 6 lines
arch/s390/kvm/pv.c new file +6 -0
+
+	mutex_unlock(&kvm->slots_lock);
+	kvm->arch.pv.guest_len = npages * PAGE_SIZE;
+
+	/* Allocate variable storage */
+	vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE);

I get the feeling that prot virt mainly consumes memory ;)

Quoted 229 lines
arch/s390/kvm/pv.c new file +183 -0
+	vlen += uv_info.guest_virt_base_stor_len;
+	kvm->arch.pv.stor_var = vzalloc(vlen);
+	if (!kvm->arch.pv.stor_var) {
+		kvm_s390_pv_dealloc_vm(kvm);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+int kvm_s390_pv_destroy_vm(struct kvm *kvm)
+{
+	int rc;
+	u32 ret;
+
+	rc = uv_cmd_nodata(kvm_s390_pv_handle(kvm),
+			   UVC_CMD_DESTROY_SEC_CONF, &ret);
+	VM_EVENT(kvm, 3, "PROTVIRT DESTROY VM: rc %x rrc %x",
+		 ret >> 16, ret & 0x0000ffff);
+	return rc;
+}
+
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc = 0;
+	u32 ret;
+
+	if (kvm_s390_pv_handle_cpu(vcpu)) {
+		rc = uv_cmd_nodata(kvm_s390_pv_handle_cpu(vcpu),
+				   UVC_CMD_DESTROY_SEC_CPU,
+				   &ret);
+
+		VCPU_EVENT(vcpu, 3, "PROTVIRT DESTROY VCPU: cpu %d rc %x rrc %x",
+			   vcpu->vcpu_id, ret >> 16, ret & 0x0000ffff);
+	}
+
+	free_pages(vcpu->arch.pv.stor_base,
+		   get_order(uv_info.guest_cpu_stor_len));
+	/* Clear cpu and vm handle */
+	memset(&vcpu->arch.sie_block->reserved10, 0,
+	       sizeof(vcpu->arch.sie_block->reserved10));
+	memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
+	vcpu->arch.sie_block->sdf = 0;
+	return rc;
+}
+
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc;
+	struct uv_cb_csc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CPU,
+		.header.len = sizeof(uvcb),
+	};
+
+	/* EEXIST and ENOENT? */
+	if (kvm_s390_pv_handle_cpu(vcpu))
+		return -EINVAL;
+
+	vcpu->arch.pv.stor_base = __get_free_pages(GFP_KERNEL,
+						   get_order(uv_info.guest_cpu_stor_len));
+	if (!vcpu->arch.pv.stor_base)
+		return -ENOMEM;
+
+	/* Input */
+	uvcb.guest_handle = kvm_s390_pv_handle(vcpu->kvm);
+	uvcb.num = vcpu->arch.sie_block->icpua;
+	uvcb.state_origin = (u64)vcpu->arch.sie_block;
+	uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VCPU_EVENT(vcpu, 3, "PROTVIRT CREATE VCPU: cpu %d handle %llx rc %x rrc %x",
+		   vcpu->vcpu_id, uvcb.cpu_handle, uvcb.header.rc,
+		   uvcb.header.rrc);
+
+	/* Output */
+	vcpu->arch.pv.handle = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_cpu = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_config = kvm_s390_pv_handle(vcpu->kvm);
+	vcpu->arch.sie_block->sdf = 2;
+	if (!rc)
+		return 0;
+
+	kvm_s390_pv_destroy_cpu(vcpu);
+	return -EINVAL;
+}
+
+int kvm_s390_pv_create_vm(struct kvm *kvm)
+{
+	int rc;
+
+	struct uv_cb_cgc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CONF,
+		.header.len = sizeof(uvcb)
+	};
+
+	if (kvm_s390_pv_handle(kvm))
+		return -EINVAL;
+
+	/* Inputs */
+	uvcb.guest_stor_origin = 0; /* MSO is 0 for KVM */
+	uvcb.guest_stor_len = kvm->arch.pv.guest_len;
+	uvcb.guest_asce = kvm->arch.gmap->asce;
+	uvcb.conf_base_stor_origin = (u64)kvm->arch.pv.stor_base;
+	uvcb.conf_var_stor_origin = (u64)kvm->arch.pv.stor_var;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VM_EVENT(kvm, 3, "PROTVIRT CREATE VM: handle %llx len %llx rc %x rrc %x",
+		 uvcb.guest_handle, uvcb.guest_stor_len, uvcb.header.rc,
+		 uvcb.header.rrc);
+
+	/* Outputs */
+	kvm->arch.pv.handle = uvcb.guest_handle;
+
+	if (rc && (uvcb.header.rc & 0x8000)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+			      void *hdr, u64 length)
+{
+	int rc;
+	struct uv_cb_ssc uvcb = {
+		.header.cmd = UVC_CMD_SET_SEC_CONF_PARAMS,
+		.header.len = sizeof(uvcb),
+		.sec_header_origin = (u64)hdr,
+		.sec_header_len = length,
+		.guest_handle = kvm_s390_pv_handle(kvm),
+	};
+
+	if (!kvm_s390_pv_handle(kvm))
+		return -EINVAL;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VM_EVENT(kvm, 3, "PROTVIRT VM SET PARMS: rc %x rrc %x",
+		 uvcb.header.rc, uvcb.header.rrc);
+	if (rc)
+		return -EINVAL;
+	return 0;
+}
+
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int i, rc = 0;
+	struct uv_cb_unp uvcb = {
+		.header.cmd = UVC_CMD_UNPACK_IMG,
+		.header.len = sizeof(uvcb),
+		.guest_handle = kvm_s390_pv_handle(kvm),
+		.tweak[0] = tweak
+	};
+
+	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
+		return -EINVAL;
+
+
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
+		 addr, size);
+	for (i = 0; i < size / PAGE_SIZE; i++) {
+		uvcb.gaddr = addr + i * PAGE_SIZE;
+		uvcb.tweak[1] = i * PAGE_SIZE;
+retry:
+		rc = uv_call(0, (u64)&uvcb);
+		if (!rc)
+			continue;
+		/* If not yet mapped fault and retry */
+		if (uvcb.header.rc == 0x10a) {
+			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+					FAULT_FLAG_WRITE);
+			if (rc)
+				return rc;
+			goto retry;
+		}
+		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+		break;
+	}
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished with rc %x rrc %x",
+		 uvcb.header.rc, uvcb.header.rrc);
+	return rc;
+}
include/uapi/linux/kvm.h +31 -0
index 52641d8ca9e8..bb37d5710c89 100644
@@ -1000,6 +1000,7 @@ struct kvm_ppc_resize_hpt {
   #define KVM_CAP_PMU_EVENT_FILTER 173
   #define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
   #define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175
+#define KVM_CAP_S390_PROTECTED 180
 
   #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1461,6 +1462,38 @@ struct kvm_enc_region {
   /* Available with KVM_CAP_ARM_SVE */
   #define KVM_ARM_VCPU_FINALIZE	  _IOW(KVMIO,  0xc2, int)
 
+struct kvm_s390_pv_sec_parm {
+	__u64	origin;
+	__u64	length;
+};
+
+struct kvm_s390_pv_unp {
+	__u64 addr;
+	__u64 size;
+	__u64 tweak;
+};
+
+enum pv_cmd_id {
+	KVM_PV_VM_CREATE,
+	KVM_PV_VM_DESTROY,
+	KVM_PV_VM_SET_SEC_PARMS,
+	KVM_PV_VM_UNPACK,
+	KVM_PV_VM_VERIFY,
+	KVM_PV_VCPU_CREATE,
+	KVM_PV_VCPU_DESTROY,
+};
+
+struct kvm_pv_cmd {
+	__u32	cmd;
+	__u16	rc;
+	__u16	rrc;
+	__u64	data;
+};
+
+/* Available with KVM_CAP_S390_SE */
+#define KVM_S390_PV_COMMAND		_IOW(KVMIO, 0xc3, struct kvm_pv_cmd)
Quoted 8 lines

include/uapi/linux/kvm.h struct kvm_enc_region { · line 1499

Quoted 6 lines
include/uapi/linux/kvm.h +2 -0
+#define KVM_S390_PV_COMMAND_VCPU	_IOW(KVMIO, 0xc4, struct kvm_pv_cmd)
+
   /* Secure Encrypted Virtualization command */
   enum sev_cmd_id {
   	/* Guest initialization commands */

This is a lengthy patch and I ahven't explored anything yet :)

lol, everything. :)

Signature · 3 lines
Thanks,

David / dhildenb
#31 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

In reply to Janosch Frank
Quoted 13 lines · Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

---
 arch/s390/include/asm/kvm_host.h |  24 +++-
 arch/s390/include/asm/uv.h       | 110 ++++++++++++++
 arch/s390/kvm/Makefile           |   2 +-
 arch/s390/kvm/kvm-s390.c         | 173 +++++++++++++++++++++-
 arch/s390/kvm/kvm-s390.h         |  47 ++++++
 arch/s390/kvm/pv.c               | 237 +++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h         |  33 +++++
 7 files changed, 622 insertions(+), 4 deletions(-)
 create mode 100644 arch/s390/kvm/pv.c

[...]

Quoted 10 lines
arch/s390/kvm/kvm-s390.c +10 -0
+	case KVM_PV_VM_UNPACK: {
+		struct kvm_s390_pv_unp unp = {};
+
+		r = -EFAULT;
+		if (copy_from_user(&unp, argp, sizeof(unp)))
+			break;
+
+		r = kvm_s390_pv_unpack(kvm, unp.addr, unp.size, unp.tweak);
+		break;
+	}

[....]

Quoted 7 lines
arch/s390/kvm/pv.c new file +7 -0
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int i, rc = 0;
+	struct uv_cb_unp uvcb = {
+		.header.cmd = UVC_CMD_UNPACK_IMG,
+		.header.len = sizeof(uvcb),

arch/s390/kvm/pv.c line 214

Quoted 10 lines
arch/s390/kvm/pv.c new file +10 -0
+		.guest_handle = kvm_s390_pv_handle(kvm),
+		.tweak[0] = tweak
+	};
+
+	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
+		return -EINVAL;
+
+
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
+		 addr, size);

Does it make sense to check for addr and addr+size to be within the memory size of the guest? The uv_call or gmap_fault will fail later on, but we could do an early exit if the the site is wrong.

Quoted 22 lines
arch/s390/kvm/pv.c new file +19 -0
+	for (i = 0; i < size / PAGE_SIZE; i++) {
+		uvcb.gaddr = addr + i * PAGE_SIZE;
+		uvcb.tweak[1] = i * PAGE_SIZE;
 
 
+retry:
 
+		rc = uv_call(0, (u64)&uvcb);
+		if (!rc)
+			continue;
+		/* If not yet mapped fault and retry */
+		if (uvcb.header.rc == 0x10a) {
+			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+					FAULT_FLAG_WRITE);
+			if (rc)
+				return rc;
+			goto retry;
+		}
+		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+		break;
+	}

[...]

author maintainer
#32 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 46 lines · Christian Borntraeger
Quoted 13 lines · Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

---
 arch/s390/include/asm/kvm_host.h |  24 +++-
 arch/s390/include/asm/uv.h       | 110 ++++++++++++++
 arch/s390/kvm/Makefile           |   2 +-
 arch/s390/kvm/kvm-s390.c         | 173 +++++++++++++++++++++-
 arch/s390/kvm/kvm-s390.h         |  47 ++++++
 arch/s390/kvm/pv.c               | 237 +++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h         |  33 +++++
 7 files changed, 622 insertions(+), 4 deletions(-)
 create mode 100644 arch/s390/kvm/pv.c

[...]

Quoted 10 lines
arch/s390/kvm/kvm-s390.c +10 -0
+	case KVM_PV_VM_UNPACK: {
+		struct kvm_s390_pv_unp unp = {};
+
+		r = -EFAULT;
+		if (copy_from_user(&unp, argp, sizeof(unp)))
+			break;
+
+		r = kvm_s390_pv_unpack(kvm, unp.addr, unp.size, unp.tweak);
+		break;
+	}

[....]

Quoted 13 lines
arch/s390/kvm/pv.c new file +13 -0
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int i, rc = 0;
+	struct uv_cb_unp uvcb = {
+		.header.cmd = UVC_CMD_UNPACK_IMG,
+		.header.len = sizeof(uvcb),
+		.guest_handle = kvm_s390_pv_handle(kvm),
+		.tweak[0] = tweak
+	};
+
+	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
+		return -EINVAL;
Quoted 8 lines

arch/s390/kvm/pv.c line 214

Quoted 4 lines
arch/s390/kvm/pv.c new file +4 -0
+
+
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
+		 addr, size);

Does it make sense to check for addr and addr+size to be within the memory size of the guest? The uv_call or gmap_fault will fail later on, but we could do an early exit if the the site is wrong.

Yeah, Marc already brought that up because of a testcase of his. I'll add a check, but before that I need to understand what makes us loop so long that we get RCU warnings.

Quoted 28 lines
Quoted 26 lines
arch/s390/kvm/pv.c new file +19 -0
+	for (i = 0; i < size / PAGE_SIZE; i++) {
+		uvcb.gaddr = addr + i * PAGE_SIZE;
+		uvcb.tweak[1] = i * PAGE_SIZE;
 
 
+retry:
 
+		rc = uv_call(0, (u64)&uvcb);
+		if (!rc)
+			continue;
+		/* If not yet mapped fault and retry */
+		if (uvcb.header.rc == 0x10a) {
+			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+					FAULT_FLAG_WRITE);
+			if (rc)
+				return rc;
+			goto retry;
+		}
+		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+		break;
+	}

[...]

#33 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

In reply to Janosch Frank
Quoted 11 lines · On Thu, 24 Oct 2019 07:40:26 -0400 Janosch Frank <[email protected]> wrote:

Let's add a KVM interface to create and destroy protected VMs.

---
 arch/s390/include/asm/kvm_host.h |  24 +++-
 arch/s390/include/asm/uv.h       | 110 ++++++++++++++
 arch/s390/kvm/Makefile           |   2 +-
 arch/s390/kvm/kvm-s390.c         | 173 +++++++++++++++++++++-
 arch/s390/kvm/kvm-s390.h         |  47 ++++++
 arch/s390/kvm/pv.c               | 237 +++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h         |  33 +++++

Any new ioctls and caps probably want a mention in Documentation/virt/kvm/api.txt :)

Quoted 2 lines
 7 files changed, 622 insertions(+), 4 deletions(-)
 create mode 100644 arch/s390/kvm/pv.c

(...)

Quoted 27 lines
arch/s390/kvm/kvm-s390.c +23 -0
@@ -2157,6 +2164,96 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
 	return r;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VM_CREATE: {
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;
+
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_DESTROY: {

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2199

Quoted 10 lines
arch/s390/kvm/kvm-s390.c +10 -0
+		/* All VCPUs have to be destroyed before this call. */
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		r = kvm_s390_pv_destroy_vm(kvm);
+		if (!r)
+			kvm_s390_pv_dealloc_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}

Would be helpful to have some code that shows when/how these are called
- do you have any plans to post something soon?

(...)

arch/s390/kvm/kvm-s390.c void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) · line 2646

Quoted 6 lines
arch/s390/kvm/kvm-s390.c +2 -0
@@ -2529,6 +2642,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
 	if (vcpu->kvm->arch.use_cmma)
 		kvm_s390_vcpu_unsetup_cmma(vcpu);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_handle_cpu(vcpu))

I was a bit confused by that function name... maybe kvm_s390_pv_cpu_get_handle()?

Also, if this always returns 0 if the config option is off, you probably don't need to check for that option?

Quoted 5 lines
arch/s390/kvm/kvm-s390.c +1 -0
+		kvm_s390_pv_destroy_cpu(vcpu);
 	free_page((unsigned long)(vcpu->arch.sie_block));
 
 	kvm_vcpu_uninit(vcpu);

arch/s390/kvm/kvm-s390.c void kvm_arch_destroy_vm(struct kvm *kvm) · line 2678

Quoted 9 lines
arch/s390/kvm/kvm-s390.c +4 -1
@@ -2555,8 +2671,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 {
 	kvm_free_vcpus(kvm);
 	sca_dispose(kvm);
-	debug_unregister(kvm->arch.dbf);
 	kvm_s390_gisa_destroy(kvm);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);

It seems the pv vm can be either destroyed via the ioctl above or in the course of normal vm destruction. When is which way supposed to be used? Also, it seems kvm_s390_pv_destroy_vm() can fail -- can that be a problem in this code path?

Quoted 5 lines
arch/s390/kvm/kvm-s390.c +2 -0
+	}
+	debug_unregister(kvm->arch.dbf);
 	free_page((unsigned long)kvm->arch.sie_page2);
 	if (!kvm_is_ucontrol(kvm))
 		gmap_remove(kvm->arch.gmap);

(...)

Quoted 18 lines
arch/s390/kvm/kvm-s390.h +10 -0
index 6d9448dbd052..0d61dcc51f0e 100644
@@ -196,6 +196,53 @@ static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm)
 	return kvm->arch.user_cpu_state_ctrl != 0;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+/* implemented in pv.c */
+void kvm_s390_pv_unpin(struct kvm *kvm);
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm);
+int kvm_s390_pv_alloc_vm(struct kvm *kvm);
+int kvm_s390_pv_create_vm(struct kvm *kvm);
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_destroy_vm(struct kvm *kvm);
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length);

arch/s390/kvm/kvm-s390.h static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm) · line 218

Quoted 10 lines
arch/s390/kvm/kvm-s390.h +10 -0
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak);
+int kvm_s390_pv_verify(struct kvm *kvm);
+
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm)
+{
+	return !!kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm)

This function name is less confusing than the one below, but maybe also rename this to kvm_s390_pv_get_handle() for consistency?

Quoted 30 lines
arch/s390/kvm/kvm-s390.h +27 -0
+{
+	return kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu)
+{
+	return vcpu->arch.pv.handle;
+}
+#else
+static inline void kvm_s390_pv_unpin(struct kvm *kvm) {}
+static inline void kvm_s390_pv_dealloc_vm(struct kvm *kvm) {}
+static inline int kvm_s390_pv_alloc_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_destroy_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+					    u64 origin, u64 length) { return 0; }
+static inline int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr,
+				     unsigned long size,  unsigned long tweak)
+{ return 0; }
+static inline int kvm_s390_pv_verify(struct kvm *kvm) { return 0; }
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu) { return 0; }
+#endif
+
 /* implemented in interrupt.c */
 int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
 void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu);

(...)

arch/s390/kvm/kvm-s390.h

Quoted 9 lines
arch/s390/kvm/kvm-s390.h +9 -0
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc;
+	struct uv_cb_csc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CPU,
+		.header.len = sizeof(uvcb),
+	};
+
+	/* EEXIST and ENOENT? */

?

Quoted 20 lines
arch/s390/kvm/kvm-s390.h +20 -0
+	if (kvm_s390_pv_handle_cpu(vcpu))
+		return -EINVAL;
+
+	vcpu->arch.pv.stor_base = __get_free_pages(GFP_KERNEL,
+						   get_order(uv_info.guest_cpu_stor_len));
+	if (!vcpu->arch.pv.stor_base)
+		return -ENOMEM;
+
+	/* Input */
+	uvcb.guest_handle = kvm_s390_pv_handle(vcpu->kvm);
+	uvcb.num = vcpu->arch.sie_block->icpua;
+	uvcb.state_origin = (u64)vcpu->arch.sie_block;
+	uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VCPU_EVENT(vcpu, 3, "PROTVIRT CREATE VCPU: cpu %d handle %llx rc %x rrc %x",
+		   vcpu->vcpu_id, uvcb.cpu_handle, uvcb.header.rc,
+		   uvcb.header.rrc);
+
+	/* Output */

arch/s390/kvm/kvm-s390.h

Quoted 10 lines
arch/s390/kvm/kvm-s390.h +10 -0
+	vcpu->arch.pv.handle = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_cpu = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_config = kvm_s390_pv_handle(vcpu->kvm);
+	vcpu->arch.sie_block->sdf = 2;
+	if (!rc)
+		return 0;
+
+	kvm_s390_pv_destroy_cpu(vcpu);
+	return -EINVAL;
+}

(...)

Only a quick readthrough, as this patch is longish.

author maintainer
#34 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 15 lines · Cornelia Huck
Quoted 11 lines · On Thu, 24 Oct 2019 07:40:26 -0400 Janosch Frank <[email protected]> wrote:

Let's add a KVM interface to create and destroy protected VMs.

---
 arch/s390/include/asm/kvm_host.h |  24 +++-
 arch/s390/include/asm/uv.h       | 110 ++++++++++++++
 arch/s390/kvm/Makefile           |   2 +-
 arch/s390/kvm/kvm-s390.c         | 173 +++++++++++++++++++++-
 arch/s390/kvm/kvm-s390.h         |  47 ++++++
 arch/s390/kvm/pv.c               | 237 +++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h         |  33 +++++
Quoted 2 lines

Any new ioctls and caps probably want a mention in Documentation/virt/kvm/api.txt :)

Noted

Quoted 38 lines
Quoted 3 lines
 7 files changed, 622 insertions(+), 4 deletions(-)
 create mode 100644 arch/s390/kvm/pv.c

(...)

Quoted 32 lines
arch/s390/kvm/kvm-s390.c +28 -0
@@ -2157,6 +2164,96 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
 	return r;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VM_CREATE: {
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;
+
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_DESTROY: {
+		/* All VCPUs have to be destroyed before this call. */
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		r = kvm_s390_pv_destroy_vm(kvm);
+		if (!r)
Quoted 8 lines

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2199

Quoted 5 lines
arch/s390/kvm/kvm-s390.c +5 -0
+			kvm_s390_pv_dealloc_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}

Would be helpful to have some code that shows when/how these are called
- do you have any plans to post something soon?

Qemu patches will be in internal review soonish and afterwards I'll post them upstream

Quoted 5 lines

(...)

Quoted 2 lines
(quoted diff) +0 -0
@@ -2529,6 +2642,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
Quoted 7 lines

arch/s390/kvm/kvm-s390.c void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) · line 2646

Quoted 4 lines
arch/s390/kvm/kvm-s390.c +2 -0
 	if (vcpu->kvm->arch.use_cmma)
 		kvm_s390_vcpu_unsetup_cmma(vcpu);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_handle_cpu(vcpu))

I was a bit confused by that function name... maybe kvm_s390_pv_cpu_get_handle()?

Sure

Quoted 3 lines

Also, if this always returns 0 if the config option is off, you probably don't need to check for that option?

Hmm, if we decide to remove the config option altogether then it's not needed anyway and I think that's what Christian wants.

Quoted 4 lines
Quoted 4 lines
arch/s390/kvm/kvm-s390.c +1 -0
+		kvm_s390_pv_destroy_cpu(vcpu);
 	free_page((unsigned long)(vcpu->arch.sie_block));
 
Quoted 16 lines

arch/s390/kvm/kvm-s390.c void kvm_arch_destroy_vm(struct kvm *kvm) · line 2678

Quoted 11 lines
arch/s390/kvm/kvm-s390.c +4 -1
 	kvm_vcpu_uninit(vcpu);
@@ -2555,8 +2671,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 {
 	kvm_free_vcpus(kvm);
 	sca_dispose(kvm);
-	debug_unregister(kvm->arch.dbf);
 	kvm_s390_gisa_destroy(kvm);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);

It seems the pv vm can be either destroyed via the ioctl above or in the course of normal vm destruction. When is which way supposed to be used? Also, it seems kvm_s390_pv_destroy_vm() can fail -- can that be a problem in this code path?

On a reboot we need to tear down the protected VM and boot from unprotected mode again. If the VM shuts down we go through this cleanup path. If it fails the kernel will loose the memory that was allocated to start the VM.

Quoted 32 lines
Quoted 6 lines
arch/s390/kvm/kvm-s390.c +2 -0
+	}
+	debug_unregister(kvm->arch.dbf);
 	free_page((unsigned long)kvm->arch.sie_page2);
 	if (!kvm_is_ucontrol(kvm))
 		gmap_remove(kvm->arch.gmap);

(...)

Quoted 23 lines
arch/s390/kvm/kvm-s390.h +15 -0
index 6d9448dbd052..0d61dcc51f0e 100644
@@ -196,6 +196,53 @@ static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm)
 	return kvm->arch.user_cpu_state_ctrl != 0;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+/* implemented in pv.c */
+void kvm_s390_pv_unpin(struct kvm *kvm);
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm);
+int kvm_s390_pv_alloc_vm(struct kvm *kvm);
+int kvm_s390_pv_create_vm(struct kvm *kvm);
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_destroy_vm(struct kvm *kvm);
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length);
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak);
+int kvm_s390_pv_verify(struct kvm *kvm);
+
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm)
Quoted 8 lines

arch/s390/kvm/kvm-s390.h static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm) · line 218

Quoted 5 lines
arch/s390/kvm/kvm-s390.h +5 -0
+{
+	return !!kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm)

This function name is less confusing than the one below, but maybe also rename this to kvm_s390_pv_get_handle() for consistency?

kvm_s390_pv_kvm_handle?

Quoted 34 lines
Quoted 31 lines
arch/s390/kvm/kvm-s390.h +27 -0
+{
+	return kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu)
+{
+	return vcpu->arch.pv.handle;
+}
+#else
+static inline void kvm_s390_pv_unpin(struct kvm *kvm) {}
+static inline void kvm_s390_pv_dealloc_vm(struct kvm *kvm) {}
+static inline int kvm_s390_pv_alloc_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_destroy_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+					    u64 origin, u64 length) { return 0; }
+static inline int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr,
+				     unsigned long size,  unsigned long tweak)
+{ return 0; }
+static inline int kvm_s390_pv_verify(struct kvm *kvm) { return 0; }
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu) { return 0; }
+#endif
+
 /* implemented in interrupt.c */
 int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
 void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu);

(...)

Quoted 11 lines

arch/s390/kvm/kvm-s390.h

Quoted 9 lines
arch/s390/kvm/kvm-s390.h +9 -0
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc;
+	struct uv_cb_csc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CPU,
+		.header.len = sizeof(uvcb),
+	};
+
+	/* EEXIST and ENOENT? */

?

I was asking myself if EEXIST or ENOENT would be better error values than EINVAL.

Quoted 36 lines
Quoted 21 lines
arch/s390/kvm/kvm-s390.h +20 -0
+	if (kvm_s390_pv_handle_cpu(vcpu))
+		return -EINVAL;
+
+	vcpu->arch.pv.stor_base = __get_free_pages(GFP_KERNEL,
+						   get_order(uv_info.guest_cpu_stor_len));
+	if (!vcpu->arch.pv.stor_base)
+		return -ENOMEM;
+
+	/* Input */
+	uvcb.guest_handle = kvm_s390_pv_handle(vcpu->kvm);
+	uvcb.num = vcpu->arch.sie_block->icpua;
+	uvcb.state_origin = (u64)vcpu->arch.sie_block;
+	uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VCPU_EVENT(vcpu, 3, "PROTVIRT CREATE VCPU: cpu %d handle %llx rc %x rrc %x",
+		   vcpu->vcpu_id, uvcb.cpu_handle, uvcb.header.rc,
+		   uvcb.header.rrc);
+
+	/* Output */

arch/s390/kvm/kvm-s390.h

Quoted 10 lines
arch/s390/kvm/kvm-s390.h +10 -0
+	vcpu->arch.pv.handle = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_cpu = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_config = kvm_s390_pv_handle(vcpu->kvm);
+	vcpu->arch.sie_block->sdf = 2;
+	if (!rc)
+		return 0;
+
+	kvm_s390_pv_destroy_cpu(vcpu);
+	return -EINVAL;
+}

(...)

Only a quick readthrough, as this patch is longish.

#35 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 8 lines · On Fri, 8 Nov 2019 08:36:35 +0100 Janosch Frank <[email protected]> wrote:
Quoted 7 lines · Cornelia Huck
Quoted 4 lines · On Thu, 24 Oct 2019 07:40:26 -0400 Janosch Frank <[email protected]> wrote:
arch/s390/kvm/kvm-s390.c +0 -0
@@ -2157,6 +2164,96 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
 	return r;
 }
 
Quoted 16 lines
Quoted 8 lines
Quoted 8 lines
arch/s390/kvm/kvm-s390.c +8 -0
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VM_CREATE: {
Quoted 8 lines

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2182

Quoted 8 lines
arch/s390/kvm/kvm-s390.c +8 -0
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;
+
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);

Looking at this again: this function calls kvm_s390_vcpu_block_all() (which probably does not hurt), but then kvm_s390_vcpu_unblock_all()... don't we want to keep the block across pv_create_vm() as well?

Also, can you maybe skip calling this function if we use the esca already?

Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
arch/s390/kvm/kvm-s390.c +12 -0
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_DESTROY: {
+		/* All VCPUs have to be destroyed before this call. */
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		r = kvm_s390_pv_destroy_vm(kvm);
+		if (!r)
Quoted 8 lines

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2199

Quoted 5 lines
arch/s390/kvm/kvm-s390.c +5 -0
+			kvm_s390_pv_dealloc_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}  

Would be helpful to have some code that shows when/how these are called
- do you have any plans to post something soon?

Quoted 2 lines

Qemu patches will be in internal review soonish and afterwards I'll post them upstream

Great, looking forward to this :)

Quoted 14 lines
Quoted 6 lines

(...)

Quoted 2 lines
(quoted diff) +0 -0
@@ -2529,6 +2642,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
Quoted 7 lines

arch/s390/kvm/kvm-s390.c void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) · line 2646

Quoted 4 lines
arch/s390/kvm/kvm-s390.c +2 -0
 	if (vcpu->kvm->arch.use_cmma)
 		kvm_s390_vcpu_unsetup_cmma(vcpu);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_handle_cpu(vcpu))  

I was a bit confused by that function name... maybe kvm_s390_pv_cpu_get_handle()?

Quoted 8 lines

Sure

Quoted 3 lines

Also, if this always returns 0 if the config option is off, you probably don't need to check for that option?

Hmm, if we decide to remove the config option altogether then it's not needed anyway and I think that's what Christian wants.

That would be fine with me as well (I have not yet thought about all implications there, though.)

Quoted 22 lines
Quoted 5 lines
Quoted 5 lines
arch/s390/kvm/kvm-s390.c +1 -0
+		kvm_s390_pv_destroy_cpu(vcpu);
 	free_page((unsigned long)(vcpu->arch.sie_block));
 
Quoted 16 lines

arch/s390/kvm/kvm-s390.c void kvm_arch_destroy_vm(struct kvm *kvm) · line 2678

Quoted 11 lines
arch/s390/kvm/kvm-s390.c +4 -1
 	kvm_vcpu_uninit(vcpu);
@@ -2555,8 +2671,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 {
 	kvm_free_vcpus(kvm);
 	sca_dispose(kvm);
-	debug_unregister(kvm->arch.dbf);
 	kvm_s390_gisa_destroy(kvm);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);  

It seems the pv vm can be either destroyed via the ioctl above or in the course of normal vm destruction. When is which way supposed to be used? Also, it seems kvm_s390_pv_destroy_vm() can fail -- can that be a problem in this code path?

Quoted 4 lines

On a reboot we need to tear down the protected VM and boot from unprotected mode again. If the VM shuts down we go through this cleanup path. If it fails the kernel will loose the memory that was allocated to start the VM.

Shouldn't you at least log a moan in that case? Hopefully, this happens very rarely, but the dbf will be gone...

Quoted 42 lines
Quoted 33 lines
Quoted 7 lines
arch/s390/kvm/kvm-s390.c +2 -0
+	}
+	debug_unregister(kvm->arch.dbf);
 	free_page((unsigned long)kvm->arch.sie_page2);
 	if (!kvm_is_ucontrol(kvm))
 		gmap_remove(kvm->arch.gmap);  

(...)

Quoted 23 lines
arch/s390/kvm/kvm-s390.h +15 -0
index 6d9448dbd052..0d61dcc51f0e 100644
@@ -196,6 +196,53 @@ static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm)
 	return kvm->arch.user_cpu_state_ctrl != 0;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+/* implemented in pv.c */
+void kvm_s390_pv_unpin(struct kvm *kvm);
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm);
+int kvm_s390_pv_alloc_vm(struct kvm *kvm);
+int kvm_s390_pv_create_vm(struct kvm *kvm);
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_destroy_vm(struct kvm *kvm);
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length);
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak);
+int kvm_s390_pv_verify(struct kvm *kvm);
+
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm)
Quoted 8 lines

arch/s390/kvm/kvm-s390.h static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm) · line 218

Quoted 5 lines
arch/s390/kvm/kvm-s390.h +5 -0
+{
+	return !!kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm)  

This function name is less confusing than the one below, but maybe also rename this to kvm_s390_pv_get_handle() for consistency?

Quoted 1 line

kvm_s390_pv_kvm_handle?

kvm_s390_pv_kvm_get_handle() would mirror the cpu function :) </bikeshed>

Quoted 47 lines
Quoted 35 lines
Quoted 32 lines
arch/s390/kvm/kvm-s390.h +27 -0
+{
+	return kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu)
+{
+	return vcpu->arch.pv.handle;
+}
+#else
+static inline void kvm_s390_pv_unpin(struct kvm *kvm) {}
+static inline void kvm_s390_pv_dealloc_vm(struct kvm *kvm) {}
+static inline int kvm_s390_pv_alloc_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_destroy_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+					    u64 origin, u64 length) { return 0; }
+static inline int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr,
+				     unsigned long size,  unsigned long tweak)
+{ return 0; }
+static inline int kvm_s390_pv_verify(struct kvm *kvm) { return 0; }
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu) { return 0; }
+#endif
+
 /* implemented in interrupt.c */
 int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
 void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu);  

(...)

Quoted 11 lines

arch/s390/kvm/kvm-s390.h

Quoted 9 lines
arch/s390/kvm/kvm-s390.h +9 -0
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc;
+	struct uv_cb_csc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CPU,
+		.header.len = sizeof(uvcb),
+	};
+
+	/* EEXIST and ENOENT? */  

?

Quoted 2 lines

I was asking myself if EEXIST or ENOENT would be better error values than EINVAL.

EEXIST might be better, but I don't really like ENOENT.

Quoted 39 lines
Quoted 37 lines
Quoted 22 lines
arch/s390/kvm/kvm-s390.h +20 -0
+	if (kvm_s390_pv_handle_cpu(vcpu))
+		return -EINVAL;
+
+	vcpu->arch.pv.stor_base = __get_free_pages(GFP_KERNEL,
+						   get_order(uv_info.guest_cpu_stor_len));
+	if (!vcpu->arch.pv.stor_base)
+		return -ENOMEM;
+
+	/* Input */
+	uvcb.guest_handle = kvm_s390_pv_handle(vcpu->kvm);
+	uvcb.num = vcpu->arch.sie_block->icpua;
+	uvcb.state_origin = (u64)vcpu->arch.sie_block;
+	uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VCPU_EVENT(vcpu, 3, "PROTVIRT CREATE VCPU: cpu %d handle %llx rc %x rrc %x",
+		   vcpu->vcpu_id, uvcb.cpu_handle, uvcb.header.rc,
+		   uvcb.header.rrc);
+
+	/* Output */

arch/s390/kvm/kvm-s390.h

Quoted 10 lines
arch/s390/kvm/kvm-s390.h +10 -0
+	vcpu->arch.pv.handle = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_cpu = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_config = kvm_s390_pv_handle(vcpu->kvm);
+	vcpu->arch.sie_block->sdf = 2;
+	if (!rc)
+		return 0;
+
+	kvm_s390_pv_destroy_cpu(vcpu);
+	return -EINVAL;
+}  

(...)

Only a quick readthrough, as this patch is longish.

author maintainer
#36 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 23 lines · Cornelia Huck
Quoted 20 lines · On Fri, 8 Nov 2019 08:36:35 +0100 Janosch Frank <[email protected]> wrote:
Quoted 19 lines · Cornelia Huck
Quoted 16 lines · On Thu, 24 Oct 2019 07:40:26 -0400 Janosch Frank <[email protected]> wrote:
arch/s390/kvm/kvm-s390.c +12 -0
@@ -2157,6 +2164,96 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
 	return r;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VM_CREATE: {
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;
+
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2182

Quoted 4 lines
arch/s390/kvm/kvm-s390.c +4 -0
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);

Looking at this again: this function calls kvm_s390_vcpu_block_all() (which probably does not hurt), but then kvm_s390_vcpu_unblock_all()... don't we want to keep the block across pv_create_vm() as well?

Yeah

Quoted 3 lines

Also, can you maybe skip calling this function if we use the esca already?

Did I forget to include that in the patchset?
I extended sca_switch_to_extended() to just return in that case.

Quoted 75 lines
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
arch/s390/kvm/kvm-s390.c +12 -0
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_DESTROY: {
+		/* All VCPUs have to be destroyed before this call. */
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		r = kvm_s390_pv_destroy_vm(kvm);
+		if (!r)
Quoted 8 lines

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2199

Quoted 5 lines
arch/s390/kvm/kvm-s390.c +5 -0
+			kvm_s390_pv_dealloc_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}  

Would be helpful to have some code that shows when/how these are called
- do you have any plans to post something soon?

Quoted 2 lines

Qemu patches will be in internal review soonish and afterwards I'll post them upstream

Great, looking forward to this :)

Quoted 14 lines
Quoted 6 lines

(...)

Quoted 2 lines
(quoted diff) +0 -0
@@ -2529,6 +2642,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
Quoted 7 lines

arch/s390/kvm/kvm-s390.c void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) · line 2646

Quoted 4 lines
arch/s390/kvm/kvm-s390.c +2 -0
 	if (vcpu->kvm->arch.use_cmma)
 		kvm_s390_vcpu_unsetup_cmma(vcpu);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_handle_cpu(vcpu))  

I was a bit confused by that function name... maybe kvm_s390_pv_cpu_get_handle()?

Quoted 8 lines

Sure

Quoted 3 lines

Also, if this always returns 0 if the config option is off, you probably don't need to check for that option?

Hmm, if we decide to remove the config option altogether then it's not needed anyway and I think that's what Christian wants.

That would be fine with me as well (I have not yet thought about all implications there, though.)

Quoted 22 lines
Quoted 21 lines
Quoted 7 lines
arch/s390/kvm/kvm-s390.c +1 -0
+		kvm_s390_pv_destroy_cpu(vcpu);
 	free_page((unsigned long)(vcpu->arch.sie_block));
 
 	kvm_vcpu_uninit(vcpu);

arch/s390/kvm/kvm-s390.c void kvm_arch_destroy_vm(struct kvm *kvm) · line 2678

Quoted 9 lines
arch/s390/kvm/kvm-s390.c +4 -1
@@ -2555,8 +2671,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 {
 	kvm_free_vcpus(kvm);
 	sca_dispose(kvm);
-	debug_unregister(kvm->arch.dbf);
 	kvm_s390_gisa_destroy(kvm);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
+	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);  

It seems the pv vm can be either destroyed via the ioctl above or in the course of normal vm destruction. When is which way supposed to be used? Also, it seems kvm_s390_pv_destroy_vm() can fail -- can that be a problem in this code path?

Quoted 7 lines
Quoted 4 lines

On a reboot we need to tear down the protected VM and boot from unprotected mode again. If the VM shuts down we go through this cleanup path. If it fails the kernel will loose the memory that was allocated to start the VM.

Shouldn't you at least log a moan in that case? Hopefully, this happens very rarely, but the dbf will be gone...

That's why I created the uv dbf :-)
Well, it shouldn't happen at all so maybe a WARN will be a good option

Quoted 139 lines
Quoted 43 lines
Quoted 34 lines
Quoted 8 lines
arch/s390/kvm/kvm-s390.c +2 -0
+	}
+	debug_unregister(kvm->arch.dbf);
 	free_page((unsigned long)kvm->arch.sie_page2);
 	if (!kvm_is_ucontrol(kvm))
 		gmap_remove(kvm->arch.gmap);  

(...)

Quoted 23 lines
arch/s390/kvm/kvm-s390.h +15 -0
index 6d9448dbd052..0d61dcc51f0e 100644
@@ -196,6 +196,53 @@ static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm)
 	return kvm->arch.user_cpu_state_ctrl != 0;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+/* implemented in pv.c */
+void kvm_s390_pv_unpin(struct kvm *kvm);
+void kvm_s390_pv_dealloc_vm(struct kvm *kvm);
+int kvm_s390_pv_alloc_vm(struct kvm *kvm);
+int kvm_s390_pv_create_vm(struct kvm *kvm);
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_destroy_vm(struct kvm *kvm);
+int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu);
+int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length);
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak);
+int kvm_s390_pv_verify(struct kvm *kvm);
+
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm)
Quoted 8 lines

arch/s390/kvm/kvm-s390.h static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm) · line 218

Quoted 5 lines
arch/s390/kvm/kvm-s390.h +5 -0
+{
+	return !!kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm)  

This function name is less confusing than the one below, but maybe also rename this to kvm_s390_pv_get_handle() for consistency?

Quoted 1 line

kvm_s390_pv_kvm_handle?

kvm_s390_pv_kvm_get_handle() would mirror the cpu function :) </bikeshed>

Quoted 47 lines
Quoted 35 lines
Quoted 32 lines
arch/s390/kvm/kvm-s390.h +27 -0
+{
+	return kvm->arch.pv.handle;
+}
+
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu)
+{
+	return vcpu->arch.pv.handle;
+}
+#else
+static inline void kvm_s390_pv_unpin(struct kvm *kvm) {}
+static inline void kvm_s390_pv_dealloc_vm(struct kvm *kvm) {}
+static inline int kvm_s390_pv_alloc_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_destroy_vm(struct kvm *kvm) { return 0; }
+static inline int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu) { return 0; }
+static inline int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
+					    u64 origin, u64 length) { return 0; }
+static inline int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr,
+				     unsigned long size,  unsigned long tweak)
+{ return 0; }
+static inline int kvm_s390_pv_verify(struct kvm *kvm) { return 0; }
+static inline bool kvm_s390_pv_is_protected(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle(struct kvm *kvm) { return 0; }
+static inline u64 kvm_s390_pv_handle_cpu(struct kvm_vcpu *vcpu) { return 0; }
+#endif
+
 /* implemented in interrupt.c */
 int kvm_s390_handle_wait(struct kvm_vcpu *vcpu);
 void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu);  

(...)

Quoted 11 lines

arch/s390/kvm/kvm-s390.h

Quoted 9 lines
arch/s390/kvm/kvm-s390.h +9 -0
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc;
+	struct uv_cb_csc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CPU,
+		.header.len = sizeof(uvcb),
+	};
+
+	/* EEXIST and ENOENT? */  

?

Quoted 2 lines

I was asking myself if EEXIST or ENOENT would be better error values than EINVAL.

EEXIST might be better, but I don't really like ENOENT.

Quoted 39 lines
Quoted 37 lines
Quoted 22 lines
arch/s390/kvm/kvm-s390.h +20 -0
+	if (kvm_s390_pv_handle_cpu(vcpu))
+		return -EINVAL;
+
+	vcpu->arch.pv.stor_base = __get_free_pages(GFP_KERNEL,
+						   get_order(uv_info.guest_cpu_stor_len));
+	if (!vcpu->arch.pv.stor_base)
+		return -ENOMEM;
+
+	/* Input */
+	uvcb.guest_handle = kvm_s390_pv_handle(vcpu->kvm);
+	uvcb.num = vcpu->arch.sie_block->icpua;
+	uvcb.state_origin = (u64)vcpu->arch.sie_block;
+	uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base;
+
+	rc = uv_call(0, (u64)&uvcb);
+	VCPU_EVENT(vcpu, 3, "PROTVIRT CREATE VCPU: cpu %d handle %llx rc %x rrc %x",
+		   vcpu->vcpu_id, uvcb.cpu_handle, uvcb.header.rc,
+		   uvcb.header.rrc);
+
+	/* Output */

arch/s390/kvm/kvm-s390.h

Quoted 10 lines
arch/s390/kvm/kvm-s390.h +10 -0
+	vcpu->arch.pv.handle = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_cpu = uvcb.cpu_handle;
+	vcpu->arch.sie_block->pv_handle_config = kvm_s390_pv_handle(vcpu->kvm);
+	vcpu->arch.sie_block->sdf = 2;
+	if (!rc)
+		return 0;
+
+	kvm_s390_pv_destroy_cpu(vcpu);
+	return -EINVAL;
+}  

(...)

Only a quick readthrough, as this patch is longish.

#37 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 33 lines · On Mon, 11 Nov 2019 17:39:15 +0100 Janosch Frank <[email protected]> wrote:
Quoted 23 lines · Cornelia Huck
Quoted 20 lines · On Fri, 8 Nov 2019 08:36:35 +0100 Janosch Frank <[email protected]> wrote:
Quoted 19 lines · Cornelia Huck
Quoted 16 lines · On Thu, 24 Oct 2019 07:40:26 -0400 Janosch Frank <[email protected]> wrote:
arch/s390/kvm/kvm-s390.c +12 -0
@@ -2157,6 +2164,96 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
 	return r;
 }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {
+	case KVM_PV_VM_CREATE: {
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;
+
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines

arch/s390/kvm/kvm-s390.c static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2182

Quoted 4 lines
arch/s390/kvm/kvm-s390.c +4 -0
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);  

Looking at this again: this function calls kvm_s390_vcpu_block_all() (which probably does not hurt), but then kvm_s390_vcpu_unblock_all()... don't we want to keep the block across pv_create_vm() as well?

Quoted 8 lines

Yeah

Quoted 3 lines

Also, can you maybe skip calling this function if we use the esca already?

Did I forget to include that in the patchset?
I extended sca_switch_to_extended() to just return in that case.

If you did, I likely missed it; way too much stuff to review :)

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
arch/s390/kvm/kvm-s390.c +6 -0
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}

(...)

Quoted 21 lines
Quoted 21 lines
Quoted 20 lines
Quoted 7 lines
Quoted 7 lines
arch/s390/kvm/kvm-s390.c +1 -1
@@ -2555,8 +2671,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 {
 	kvm_free_vcpus(kvm);
 	sca_dispose(kvm);
-	debug_unregister(kvm->arch.dbf);
 	kvm_s390_gisa_destroy(kvm);
+	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
Quoted 8 lines

arch/s390/kvm/kvm-s390.c void kvm_arch_destroy_vm(struct kvm *kvm) · line 2678

Quoted 3 lines
arch/s390/kvm/kvm-s390.c +3 -0
+	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_destroy_vm(kvm);
+		kvm_s390_pv_dealloc_vm(kvm);    

It seems the pv vm can be either destroyed via the ioctl above or in the course of normal vm destruction. When is which way supposed to be used? Also, it seems kvm_s390_pv_destroy_vm() can fail -- can that be a problem in this code path?

On a reboot we need to tear down the protected VM and boot from unprotected mode again. If the VM shuts down we go through this cleanup path. If it fails the kernel will loose the memory that was allocated to start the VM.

Quoted 4 lines
Quoted 2 lines

Shouldn't you at least log a moan in that case? Hopefully, this happens very rarely, but the dbf will be gone...

That's why I created the uv dbf :-)

Again, way too easy to get lost in these changes :)

Quoted 1 line

Well, it shouldn't happen at all so maybe a WARN will be a good option

Yeah, if it this is one of these "should not happen" things, a WARN sounds good.

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
arch/s390/kvm/kvm-s390.c +2 -0
+	}
+	debug_unregister(kvm->arch.dbf);
 	free_page((unsigned long)kvm->arch.sie_page2);
 	if (!kvm_is_ucontrol(kvm))
 		gmap_remove(kvm->arch.gmap);    
#38 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

In reply to Cornelia Huck
Quoted 4 lines · Cornelia Huck
Quoted 1 line · On Fri, 8 Nov 2019 08:36:35 +0100 Janosch Frank <[email protected]> wrote:

On 11/7/19 5:29 PM, Cornelia Huck wrote:

[...]

Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

arch/s390/kvm/pv.c line 108

Quoted 10 lines
arch/s390/kvm/pv.c new file +9 -0
+int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu)
+{
+	int rc;
+	struct uv_cb_csc uvcb = {
+		.header.cmd = UVC_CMD_CREATE_SEC_CPU,
+		.header.len = sizeof(uvcb),
+	};
+
+	/* EEXIST and ENOENT? */  

?

Quoted 8 lines
Quoted 2 lines

I was asking myself if EEXIST or ENOENT would be better error values than EINVAL.

EEXIST might be better, but I don't really like ENOENT.

Quoted 3 lines
Quoted 3 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+	if (kvm_s390_pv_handle_cpu(vcpu))
+		return -EINVAL;

FWIW, I'd also vote for EEXIST here.

Thomas

#39 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

In reply to Janosch Frank
Quoted 1 line · Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

I agree with David, some more description here would be helpful.

Quoted 18 lines
---
  arch/s390/include/asm/kvm_host.h |  24 +++-
  arch/s390/include/asm/uv.h       | 110 ++++++++++++++
  arch/s390/kvm/Makefile           |   2 +-
  arch/s390/kvm/kvm-s390.c         | 173 +++++++++++++++++++++-
  arch/s390/kvm/kvm-s390.h         |  47 ++++++
  arch/s390/kvm/pv.c               | 237 +++++++++++++++++++++++++++++++
  include/uapi/linux/kvm.h         |  33 +++++
  7 files changed, 622 insertions(+), 4 deletions(-)

create mode 100644 arch/s390/kvm/pv.c

arch/s390/include/asm/kvm_host.h +0 -0
index 02f4c21c57f6..d4fd0f3af676 100644
@@ -155,7 +155,13 @@ struct kvm_s390_sie_block {
  	__u8	reserved08[4];		/* 0x0008 */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 164

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +7 -1
  #define PROG_IN_SIE (1<<0)
  	__u32	prog0c;			/* 0x000c */
-	__u8	reserved10[16];		/* 0x0010 */
+	union {
+		__u8	reserved10[16];		/* 0x0010 */
+		struct {
+			__u64	pv_handle_cpu;
+			__u64	pv_handle_config;
+		};
+	};

Why do you need to keep reserved10[] here? Simply replace it with the two new fields, and get rid of the union?

Quoted 4 lines
arch/s390/include/asm/kvm_host.h +4 -0
+/*
+ * Generic cmd executor for calls that only transport the cpu or guest
+ * handle and the command.
+ */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block {

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +10 -0
+static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
+{
+	int rc;
+	struct uv_cb_nodata uvcb = {
+		.header.cmd = cmd,
+		.header.len = sizeof(uvcb),
+		.handle = handle,
+	};
+
+	WARN(!handle, "No handle provided to Ultravisor call cmd %x\n", cmd);

If this is not supposed to happen, I thing you should return here instead of doing the uv_call() below? Or maybe even turn this into a BUG() statement?

Quoted 5 lines
arch/s390/include/asm/kvm_host.h +5 -0
+	rc = uv_call(0, (u64)&uvcb);
+	if (ret)
+		*ret = *(u32 *)&uvcb.header.rc;
+	return rc ? -EINVAL : 0;
+}

[...]

arch/s390/include/asm/kvm_host.h static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2173

Quoted 11 lines
arch/s390/include/asm/kvm_host.h +7 -0
@@ -2157,6 +2164,96 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
  	return r;
  }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+	void __user *argp = (void __user *)cmd->data;
+
+	switch (cmd->cmd) {

Why are you using curly braces for the case statements below? They do not seem to be necessary in most cases?

Quoted 28 lines
arch/s390/include/asm/kvm_host.h +28 -0
+	case KVM_PV_VM_CREATE: {
+		r = kvm_s390_pv_alloc_vm(kvm);
+		if (r)
+			break;
+
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		/* FMT 4 SIE needs esca */
+		r = sca_switch_to_extended(kvm);
+		if (!r)
+			r = kvm_s390_pv_create_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_DESTROY: {
+		/* All VCPUs have to be destroyed before this call. */
+		mutex_lock(&kvm->lock);
+		kvm_s390_vcpu_block_all(kvm);
+		r = kvm_s390_pv_destroy_vm(kvm);
+		if (!r)
+			kvm_s390_pv_dealloc_vm(kvm);
+		kvm_s390_vcpu_unblock_all(kvm);
+		mutex_unlock(&kvm->lock);
+		break;
+	}
+	case KVM_PV_VM_SET_SEC_PARMS: {
+		struct kvm_s390_pv_sec_parm parms = {};

arch/s390/include/asm/kvm_host.h static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2211

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +10 -0
+		void *hdr;
+
+		r = -EFAULT;
+		if (copy_from_user(&parms, argp, sizeof(parms)))
+			break;
+
+		/* Currently restricted to 8KB */
+		r = -EINVAL;
+		if (parms.length > PAGE_SIZE * 2)
+			break;

I think you should also check fr parms.length == 0 ... otherwise you'll get an unfriendly complaint from vmalloc().

Quoted 29 lines
arch/s390/include/asm/kvm_host.h +29 -0
+		r = -ENOMEM;
+		hdr = vmalloc(parms.length);
+		if (!hdr)
+			break;
+
+		r = -EFAULT;
+		if (!copy_from_user(hdr, (void __user *)parms.origin,
+				   parms.length))
+			r = kvm_s390_pv_set_sec_parms(kvm, hdr, parms.length);
+
+		vfree(hdr);
+		break;
+	}
+	case KVM_PV_VM_UNPACK: {
+		struct kvm_s390_pv_unp unp = {};
+
+		r = -EFAULT;
+		if (copy_from_user(&unp, argp, sizeof(unp)))
+			break;
+
+		r = kvm_s390_pv_unpack(kvm, unp.addr, unp.size, unp.tweak);
+		break;
+	}
+	case KVM_PV_VM_VERIFY: {
+		u32 ret;
+
+		r = -EINVAL;
+		if (!kvm_s390_pv_is_protected(kvm))
+			break;

arch/s390/include/asm/kvm_host.h static int kvm_s390_set_cmma_bits(struct kvm *kvm, · line 2250

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +10 -0
+
+		r = uv_cmd_nodata(kvm_s390_pv_handle(kvm),
+				  UVC_CMD_VERIFY_IMG,
+				  &ret);
+		VM_EVENT(kvm, 3, "PROTVIRT VERIFY: rc %x rrc %x",
+			 ret >> 16, ret & 0x0000ffff);
+		break;
+	}
+	default:
+		return -ENOTTY;

Is ENOTTY the right thing to return for an invalid cmd here? It might get confused with the ioctl not being available at all? Maybe EINVAL would be better?

Quoted 5 lines
arch/s390/include/asm/kvm_host.h +5 -0
+	}
+	return r;
+}
+#endif
+

[...]

arch/s390/include/asm/kvm_host.h long kvm_arch_vcpu_async_ioctl(struct file *filp, · line 4480

Quoted 11 lines
arch/s390/include/asm/kvm_host.h +7 -0
@@ -4338,6 +4471,28 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp,
  	return -ENOIOCTLCMD;
  }
 
+#ifdef CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST
+static int kvm_s390_handle_pv_vcpu(struct kvm_vcpu *vcpu,
+				   struct kvm_pv_cmd *cmd)
+{
+	int r = 0;
+
+	switch (cmd->cmd) {

Also no need for the curly braces of the case statements here?

arch/s390/include/asm/kvm_host.h long kvm_arch_vcpu_async_ioctl(struct file *filp, · line 4490

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +10 -0
+	case KVM_PV_VCPU_CREATE: {
+		r = kvm_s390_pv_create_cpu(vcpu);
+		break;
+	}
+	case KVM_PV_VCPU_DESTROY: {
+		r = kvm_s390_pv_destroy_cpu(vcpu);
+		break;
+	}
+	default:
+		r = -ENOTTY;

Or EINVAL?

arch/s390/include/asm/kvm_host.h long kvm_arch_vcpu_async_ioctl(struct file *filp, · line 4494

Quoted 4 lines
arch/s390/include/asm/kvm_host.h +4 -0
+	}
+	return r;
+}
+#endif

Thomas

#40 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

In reply to Janosch Frank
Quoted 4 lines · Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

---

[...]

arch/s390/kvm/pv.c line 210

Quoted 13 lines
arch/s390/kvm/pv.c new file +13 -0
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int i, rc = 0;
+	struct uv_cb_unp uvcb = {
+		.header.cmd = UVC_CMD_UNPACK_IMG,
+		.header.len = sizeof(uvcb),
+		.guest_handle = kvm_s390_pv_handle(kvm),
+		.tweak[0] = tweak
+	};
+
+	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
+		return -EINVAL;

Also check for size == 0 ?

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+

Remove one of the two empty lines, please.

Quoted 10 lines
arch/s390/kvm/pv.c new file +10 -0
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
+		 addr, size);
+	for (i = 0; i < size / PAGE_SIZE; i++) {
+		uvcb.gaddr = addr + i * PAGE_SIZE;
+		uvcb.tweak[1] = i * PAGE_SIZE;
+retry:
+		rc = uv_call(0, (u64)&uvcb);
+		if (!rc)
+			continue;
+		/* If not yet mapped fault and retry */

arch/s390/kvm/pv.c line 232

Quoted 10 lines
arch/s390/kvm/pv.c new file +10 -0
+		if (uvcb.header.rc == 0x10a) {
+			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+					FAULT_FLAG_WRITE);
+			if (rc)
+				return rc;
+			goto retry;
+		}
+		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+		break;

A break at the end of the for-loop ... that's really not what I'd expect.

Could you please invert the logic here, i.e.:

    if (uvcb.header.rc != 0x10a) {
        VM_EVENT(...)
        break;
    }
    rc = gmap_fault(...)
    ...

I think you might even get rid of that ugly "goto", too, that way?

arch/s390/kvm/pv.c line 237

Quoted 5 lines
arch/s390/kvm/pv.c new file +5 -0
+	}
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished with rc %x rrc %x",
+		 uvcb.header.rc, uvcb.header.rrc);
+	return rc;
+}

Thomas

author maintainer
#41 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

Quoted 6 lines · Thomas Huth
Quoted 4 lines · Janosch Frank

Let's add a KVM interface to create and destroy protected VMs.

---

[...]

Quoted 15 lines

arch/s390/kvm/pv.c line 210

Quoted 13 lines
arch/s390/kvm/pv.c new file +13 -0
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int i, rc = 0;
+	struct uv_cb_unp uvcb = {
+		.header.cmd = UVC_CMD_UNPACK_IMG,
+		.header.len = sizeof(uvcb),
+		.guest_handle = kvm_s390_pv_handle(kvm),
+		.tweak[0] = tweak
+	};
+
+	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
+		return -EINVAL;

Also check for size == 0 ?

Yep

Quoted 5 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+
+

Remove one of the two empty lines, please.

Yep

Quoted 26 lines
Quoted 11 lines
arch/s390/kvm/pv.c new file +10 -0
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
+		 addr, size);
+	for (i = 0; i < size / PAGE_SIZE; i++) {
+		uvcb.gaddr = addr + i * PAGE_SIZE;
+		uvcb.tweak[1] = i * PAGE_SIZE;
+retry:
+		rc = uv_call(0, (u64)&uvcb);
+		if (!rc)
+			continue;
+		/* If not yet mapped fault and retry */

arch/s390/kvm/pv.c line 232

Quoted 10 lines
arch/s390/kvm/pv.c new file +10 -0
+		if (uvcb.header.rc == 0x10a) {
+			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+					FAULT_FLAG_WRITE);
+			if (rc)
+				return rc;
+			goto retry;
+		}
+		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+		break;

A break at the end of the for-loop ... that's really not what I'd expect.

Could you please invert the logic here, i.e.:

Quoted 8 lines
    if (uvcb.header.rc != 0x10a) {
        VM_EVENT(...)
        break;
    }
    rc = gmap_fault(...)
    ...

I think you might even get rid of that ugly "goto", too, that way?

But without the goto we would increment i, no?
I'll try to find a solution, maybe using while, but then we need to manage i incrementation.

Quoted 9 lines

arch/s390/kvm/pv.c line 237

Quoted 6 lines
arch/s390/kvm/pv.c new file +5 -0
+	}
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished with rc %x rrc %x",
+		 uvcb.header.rc, uvcb.header.rrc);
+	return rc;
+}

Thomas

author maintainer
#42 lore raw

PATCH 1/1 Fix unpack

In reply to Thomas Huth

That should be easier to read :)

---
 arch/s390/kvm/pv.c | 60 +++++++++++++++++++++++++++-------------------
 1 file changed, 35 insertions(+), 25 deletions(-)
arch/s390/kvm/pv.c +35 -25
index 94cf16f40f25..fd73afb33b20 100644
@@ -195,43 +195,53 @@ int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
 	return 0;
 }
 
-int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
-		       unsigned long tweak)
+static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak[2])
 {
-	int i, rc = 0;
+	int rc;
 	struct uv_cb_unp uvcb = {
 		.header.cmd = UVC_CMD_UNPACK_IMG,
 		.header.len = sizeof(uvcb),
 		.guest_handle = kvm_s390_pv_handle(kvm),
-		.tweak[0] = tweak
+		.gaddr = addr,
+		.tweak[0] = tweak[0],
+		.tweak[1] = tweak[1],
 	};
 
-	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
-		return -EINVAL;
+	rc = uv_call(0, (u64)&uvcb);
+	if (!rc)
+		return rc;
+	if (uvcb.header.rc == 0x10a) {
+		/* If not yet mapped fault and retry */
+		rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+				FAULT_FLAG_WRITE);
+		if (!rc)
+			return -EAGAIN;
+	}
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+		 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+	return rc;
+}
 
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int rc = 0;
+	u64 tw[2] = {tweak, 0};
+
+	if (addr & ~PAGE_MASK || !size || size & ~PAGE_MASK)
+		return -EINVAL;
 
 	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
 		 addr, size);
-	for (i = 0; i < size / PAGE_SIZE; i++) {
-		uvcb.gaddr = addr + i * PAGE_SIZE;
-		uvcb.tweak[1] = i * PAGE_SIZE;
-retry:
-		rc = uv_call(0, (u64)&uvcb);
-		if (!rc)
+	while (tw[1] < size) {
+		rc = unpack_one(kvm, addr, tw);
+		if (rc == -EAGAIN)
 			continue;
-		/* If not yet mapped fault and retry */
-		if (uvcb.header.rc == 0x10a) {
-			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
-					FAULT_FLAG_WRITE);
-			if (rc)
-				return rc;
-			goto retry;
-		}
-		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
-			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
-		break;
+		if (rc)
+			break;
+		addr += PAGE_SIZE;
+		tw[1] += PAGE_SIZE;
 	}
-	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished with rc %x rrc %x",
-		 uvcb.header.rc, uvcb.header.rrc);
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished rc %x", rc);
 	return rc;
 }
Signature · 1 line
2.20.1
#43 lore raw

Re: [PATCH] Fix unpack

Quoted 81 lines · Janosch Frank

That should be easier to read :)

---
 arch/s390/kvm/pv.c | 60 +++++++++++++++++++++++++++-------------------
 1 file changed, 35 insertions(+), 25 deletions(-)
arch/s390/kvm/pv.c +30 -23
index 94cf16f40f25..fd73afb33b20 100644
@@ -195,43 +195,53 @@ int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
 	return 0;
 }
 
-int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
-		       unsigned long tweak)
+static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak[2])
 {
-	int i, rc = 0;
+	int rc;
 	struct uv_cb_unp uvcb = {
 		.header.cmd = UVC_CMD_UNPACK_IMG,
 		.header.len = sizeof(uvcb),
 		.guest_handle = kvm_s390_pv_handle(kvm),
-		.tweak[0] = tweak
+		.gaddr = addr,
+		.tweak[0] = tweak[0],
+		.tweak[1] = tweak[1],
 	};
 
-	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
-		return -EINVAL;
+	rc = uv_call(0, (u64)&uvcb);
+	if (!rc)
+		return rc;
+	if (uvcb.header.rc == 0x10a) {
+		/* If not yet mapped fault and retry */
+		rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+				FAULT_FLAG_WRITE);
+		if (!rc)
+			return -EAGAIN;
+	}
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+		 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+	return rc;
+}
 
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int rc = 0;
+	u64 tw[2] = {tweak, 0};
+
+	if (addr & ~PAGE_MASK || !size || size & ~PAGE_MASK)
+		return -EINVAL;
 
 	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
 		 addr, size);
-	for (i = 0; i < size / PAGE_SIZE; i++) {
-		uvcb.gaddr = addr + i * PAGE_SIZE;
-		uvcb.tweak[1] = i * PAGE_SIZE;
-retry:
-		rc = uv_call(0, (u64)&uvcb);
-		if (!rc)
+	while (tw[1] < size) {
+		rc = unpack_one(kvm, addr, tw);
+		if (rc == -EAGAIN)
 			continue;
-		/* If not yet mapped fault and retry */
-		if (uvcb.header.rc == 0x10a) {
-			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
-					FAULT_FLAG_WRITE);
-			if (rc)
-				return rc;
-			goto retry;
-		}
-		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
-			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
-		break;

arch/s390/kvm/pv.c int kvm_s390_pv_set_sec_parms(struct kvm *kvm, · line 247

Quoted 10 lines
arch/s390/kvm/pv.c +5 -2
+		if (rc)
+			break;
+		addr += PAGE_SIZE;
+		tw[1] += PAGE_SIZE;
 	}
-	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished with rc %x rrc %x",
-		 uvcb.header.rc, uvcb.header.rrc);
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished rc %x", rc);
 	return rc;
 }

Yes, I think that will be better, thanks!

Thomas

#44 lore raw

Re: [PATCH] Fix unpack

In reply to Janosch Frank

On Wed, 13 Nov 2019 09:03:06 -0500
Janosch Frank <[email protected]> wrote:

You seem to have dropped some cc:s :(

Quoted 80 lines

That should be easier to read :)

---
 arch/s390/kvm/pv.c | 60 +++++++++++++++++++++++++++-------------------
 1 file changed, 35 insertions(+), 25 deletions(-)
arch/s390/kvm/pv.c +30 -23
index 94cf16f40f25..fd73afb33b20 100644
@@ -195,43 +195,53 @@ int kvm_s390_pv_set_sec_parms(struct kvm *kvm,
 	return 0;
 }
 
-int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
-		       unsigned long tweak)
+static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak[2])
 {
-	int i, rc = 0;
+	int rc;
 	struct uv_cb_unp uvcb = {
 		.header.cmd = UVC_CMD_UNPACK_IMG,
 		.header.len = sizeof(uvcb),
 		.guest_handle = kvm_s390_pv_handle(kvm),
-		.tweak[0] = tweak
+		.gaddr = addr,
+		.tweak[0] = tweak[0],
+		.tweak[1] = tweak[1],
 	};
 
-	if (addr & ~PAGE_MASK || size & ~PAGE_MASK)
-		return -EINVAL;
+	rc = uv_call(0, (u64)&uvcb);
+	if (!rc)
+		return rc;
+	if (uvcb.header.rc == 0x10a) {
+		/* If not yet mapped fault and retry */
+		rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
+				FAULT_FLAG_WRITE);
+		if (!rc)
+			return -EAGAIN;
+	}
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
+		 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
+	return rc;
+}
 
+int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
+		       unsigned long tweak)
+{
+	int rc = 0;
+	u64 tw[2] = {tweak, 0};
+
+	if (addr & ~PAGE_MASK || !size || size & ~PAGE_MASK)
+		return -EINVAL;
 
 	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx",
 		 addr, size);
-	for (i = 0; i < size / PAGE_SIZE; i++) {
-		uvcb.gaddr = addr + i * PAGE_SIZE;
-		uvcb.tweak[1] = i * PAGE_SIZE;
-retry:
-		rc = uv_call(0, (u64)&uvcb);
-		if (!rc)
+	while (tw[1] < size) {
+		rc = unpack_one(kvm, addr, tw);
+		if (rc == -EAGAIN)
 			continue;
-		/* If not yet mapped fault and retry */
-		if (uvcb.header.rc == 0x10a) {
-			rc = gmap_fault(kvm->arch.gmap, uvcb.gaddr,
-					FAULT_FLAG_WRITE);
-			if (rc)
-				return rc;
-			goto retry;
-		}
-		VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx rc %x rrc %x",
-			 uvcb.gaddr, uvcb.header.rc, uvcb.header.rrc);
-		break;

arch/s390/kvm/pv.c int kvm_s390_pv_set_sec_parms(struct kvm *kvm, · line 247

Quoted 10 lines
arch/s390/kvm/pv.c +5 -2
+		if (rc)
+			break;
+		addr += PAGE_SIZE;
+		tw[1] += PAGE_SIZE;
 	}
-	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished with rc %x rrc %x",
-		 uvcb.header.rc, uvcb.header.rrc);
+	VM_EVENT(kvm, 3, "PROTVIRT VM UNPACK: finished rc %x", rc);
 	return rc;
 }

Yes, this looks more readable.

#45 lore raw

Re: [RFC 04/37] KVM: s390: protvirt: Add initial lifecycle handling

In reply to Janosch Frank
Quoted 8 lines · On Thu, 24 Oct 2019 07:40:26 -0400 Janosch Frank <[email protected]> wrote:
arch/s390/include/asm/uv.h +8 -0
+/*
+ * Generic cmd executor for calls that only transport the cpu or guest
+ * handle and the command.
+ */
+static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
+{
+	int rc;
+	struct uv_cb_nodata uvcb = {

arch/s390/include/asm/uv.h static inline int is_prot_virt_host(void) · line 279

Quoted 10 lines
arch/s390/include/asm/uv.h +10 -0
+		.header.cmd = cmd,
+		.header.len = sizeof(uvcb),
+		.handle = handle,
+	};
+
+	WARN(!handle, "No handle provided to Ultravisor call cmd %x\n", cmd);
+	rc = uv_call(0, (u64)&uvcb);
+	if (ret)
+		*ret = *(u32 *)&uvcb.header.rc;
+	return rc ? -EINVAL : 0;

Why go ahead with doing the uv call if it doesn't have a handle anyway? Or why warn, if you already know it is going to fail? I assume this can only happen if you have a logic error in the kvm code?

Quoted 1 line

+}

author maintainer
#46 lore raw

RFC 5/37 s390: KVM: Export PV handle to gmap

In reply to Janosch Frank

We need it in the next patch, when doing memory management for the guest in the kernel's fault handler, where otherwise we wouldn't have access to the handle.

---
 arch/s390/include/asm/gmap.h | 1 +
 arch/s390/kvm/pv.c           | 1 +
 2 files changed, 2 insertions(+)
arch/s390/include/asm/gmap.h +1 -0
index 37f96b6f0e61..6efc0b501227 100644
@@ -61,6 +61,7 @@ struct gmap {
 	spinlock_t shadow_lock;
 	struct gmap *parent;
 	unsigned long orig_asce;
+	unsigned long se_handle;
 	int edat_level;
 	bool removed;
 	bool initialized;
arch/s390/kvm/pv.c +1 -0
index 94cf16f40f25..80aecd5bea9e 100644
@@ -169,6 +169,7 @@ int kvm_s390_pv_create_vm(struct kvm *kvm)
 		kvm_s390_pv_dealloc_vm(kvm);
 		return -EINVAL;
 	}
+	kvm->arch.gmap->se_handle = uvcb.guest_handle;
 	return rc;
 }
 
Signature · 1 line
2.20.1
#47 lore raw

Re: [RFC 05/37] s390: KVM: Export PV handle to gmap

Quoted 28 lines · Janosch Frank

We need it in the next patch, when doing memory management for the guest in the kernel's fault handler, where otherwise we wouldn't have access to the handle.

---
  arch/s390/include/asm/gmap.h | 1 +
  arch/s390/kvm/pv.c           | 1 +
  2 files changed, 2 insertions(+)
arch/s390/include/asm/gmap.h +1 -0
index 37f96b6f0e61..6efc0b501227 100644
@@ -61,6 +61,7 @@ struct gmap {
  	spinlock_t shadow_lock;
  	struct gmap *parent;
  	unsigned long orig_asce;
+	unsigned long se_handle;
  	int edat_level;
  	bool removed;
  	bool initialized;
arch/s390/kvm/pv.c +0 -0
index 94cf16f40f25..80aecd5bea9e 100644

arch/s390/kvm/pv.c int kvm_s390_pv_create_vm(struct kvm *kvm) · line 175

Quoted 7 lines
arch/s390/kvm/pv.c +1 -0
@@ -169,6 +169,7 @@ int kvm_s390_pv_create_vm(struct kvm *kvm)
  		kvm_s390_pv_dealloc_vm(kvm);
  		return -EINVAL;
  	}
+	kvm->arch.gmap->se_handle = uvcb.guest_handle;
  	return rc;
  }
 

I'd suggest squashing that into the patch that needs it.

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#48 lore raw

RFC 6/37 s390: UV: Add import and export to UV library

In reply to Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
 arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +51 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/bug.h>
 #include <asm/page.h>
+#include <asm/gmap.h>
 
 #define UVC_RC_EXECUTED		0x0001
 #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
 	return rc ? -EINVAL : 0;
 }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;
+	return -EINVAL;
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;
+	return -EINVAL;
+}
+
 void setup_uv(void);
 void adjust_to_uv_max(unsigned long *vmax);
 #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
 static inline void setup_uv(void) {}
 static inline void adjust_to_uv_max(unsigned long *vmax) {}
 static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
Signature · 1 line
2.20.1
#49 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

Quoted 5 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

How does paging play along with pinning the pages (from uv_convert_to_secure() -> kvm_s390_pv_pin_page()) in a follow up patch? Can you paint me the bigger picture?

Just so I understand:

When a page is "secure", it is actually unencrypted but only the guest can access it. If the host accesses it, there is an exception.

When a page is "not secure", it is encrypted but only the host can read it. If the guest accesses it, there is an exception.

Based on these exceptions, you are able to request to convert back and forth.

Quoted 90 lines

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
  arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
  1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +51 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
  #include <linux/errno.h>
  #include <linux/bug.h>
  #include <asm/page.h>
+#include <asm/gmap.h>
 
  #define UVC_RC_EXECUTED		0x0001
  #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
  	return rc ? -EINVAL : 0;
  }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;
+	return -EINVAL;
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;
+	return -EINVAL;
+}
+
  void setup_uv(void);
  void adjust_to_uv_max(unsigned long *vmax);
  #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
  static inline void setup_uv(void) {}
  static inline void adjust_to_uv_max(unsigned long *vmax) {}
  static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
  #endif
 
  #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#50 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

Quoted 10 lines · David Hildenbrand
Quoted 5 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

How does paging play along with pinning the pages (from uv_convert_to_secure() -> kvm_s390_pv_pin_page()) in a follow up patch? Can you paint me the bigger picture?

That's a stale comment I should have removed before sending... The current patches do not support paging.

Quoted 11 lines

Just so I understand:

When a page is "secure", it is actually unencrypted but only the guest can access it. If the host accesses it, there is an exception.

When a page is "not secure", it is encrypted but only the host can read it. If the guest accesses it, there is an exception.

Based on these exceptions, you are able to request to convert back and forth.

Yes
Shared pages are the exception, because they are accessible to both parties.

Quoted 94 lines
Quoted 92 lines

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
  arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
  1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +51 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
  #include <linux/errno.h>
  #include <linux/bug.h>
  #include <asm/page.h>
+#include <asm/gmap.h>
 
  #define UVC_RC_EXECUTED		0x0001
  #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
  	return rc ? -EINVAL : 0;
  }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;
+	return -EINVAL;
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;
+	return -EINVAL;
+}
+
  void setup_uv(void);
  void adjust_to_uv_max(unsigned long *vmax);
  #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
  static inline void setup_uv(void) {}
  static inline void adjust_to_uv_max(unsigned long *vmax) {}
  static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
  #endif
 
  #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
#51 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

Quoted 8 lines · Janosch Frank
Quoted 7 lines · David Hildenbrand
Quoted 5 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Quoted 6 lines
Quoted 3 lines

How does paging play along with pinning the pages (from uv_convert_to_secure() -> kvm_s390_pv_pin_page()) in a follow up patch? Can you paint me the bigger picture?

That's a stale comment I should have removed before sending... The current patches do not support paging.

Note that once you pin you really have to disable the balloon in the QEMU (inhibit it).

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#52 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

Quoted 13 lines · David Hildenbrand
Quoted 12 lines · Janosch Frank
Quoted 10 lines · David Hildenbrand
Quoted 5 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

How does paging play along with pinning the pages (from uv_convert_to_secure() -> kvm_s390_pv_pin_page()) in a follow up patch? Can you paint me the bigger picture?

Quoted 6 lines
Quoted 2 lines

That's a stale comment I should have removed before sending... The current patches do not support paging.

Note that once you pin you really have to disable the balloon in the QEMU (inhibit it).

Yes, and you need the iommu for virtio.
We didn't yet fully discuss how to handle that.

#53 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

In reply to Janosch Frank
Quoted 39 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
 arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +6 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/bug.h>
 #include <asm/page.h>
+#include <asm/gmap.h>
 
 #define UVC_RC_EXECUTED		0x0001
 #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
 	return rc ? -EINVAL : 0;
 }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 297

Quoted 10 lines
arch/s390/include/asm/uv.h +10 -0
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;

As discussed on the KVM forum. We should also check for uvcb.header.rc != UVC_RC_EXECUTED
I know, we cant really do much if this fails, but we certainly want to know.

Quoted 20 lines
arch/s390/include/asm/uv.h +20 -0
+	return -EINVAL;
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 327

Quoted 10 lines
arch/s390/include/asm/uv.h +10 -0
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;

again, we should probably check for rc != UVC_RC_EXECUTED to detect any other problem.

Quoted 16 lines
arch/s390/include/asm/uv.h +5 -0
+	return -EINVAL;
+}
+
 void setup_uv(void);
 void adjust_to_uv_max(unsigned long *vmax);
 #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
 static inline void setup_uv(void) {}
 static inline void adjust_to_uv_max(unsigned long *vmax) {}
 static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
author maintainer
#54 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

Quoted 86 lines · Christian Borntraeger
Quoted 39 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
 arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +6 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/bug.h>
 #include <asm/page.h>
+#include <asm/gmap.h>
 
 #define UVC_RC_EXECUTED		0x0001
 #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
 	return rc ? -EINVAL : 0;
 }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 297

Quoted 10 lines
arch/s390/include/asm/uv.h +10 -0
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;

As discussed on the KVM forum. We should also check for uvcb.header.rc != UVC_RC_EXECUTED
I know, we cant really do much if this fails, but we certainly want to know.

Quoted 24 lines
arch/s390/include/asm/uv.h +24 -0
+	return -EINVAL;
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
Quoted 8 lines

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 327

Quoted 6 lines
arch/s390/include/asm/uv.h +6 -0
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;

again, we should probably check for rc != UVC_RC_EXECUTED to detect any other problem.

That's handled by the CC and the return below. CC == 1 means error
cc == 0 is success, that's why we return erly on cc == 0

Quoted 17 lines
Quoted 17 lines
arch/s390/include/asm/uv.h +5 -0
+	return -EINVAL;
+}
+
 void setup_uv(void);
 void adjust_to_uv_max(unsigned long *vmax);
 #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
 static inline void setup_uv(void) {}
 static inline void adjust_to_uv_max(unsigned long *vmax) {}
 static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
#55 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

Quoted 96 lines · Janosch Frank
Quoted 86 lines · Christian Borntraeger
Quoted 39 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
 arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +6 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/bug.h>
 #include <asm/page.h>
+#include <asm/gmap.h>
 
 #define UVC_RC_EXECUTED		0x0001
 #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
 	return rc ? -EINVAL : 0;
 }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 297

Quoted 10 lines
arch/s390/include/asm/uv.h +10 -0
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;

As discussed on the KVM forum. We should also check for uvcb.header.rc != UVC_RC_EXECUTED
I know, we cant really do much if this fails, but we certainly want to know.

Quoted 24 lines
arch/s390/include/asm/uv.h +24 -0
+	return -EINVAL;
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
Quoted 8 lines

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 327

Quoted 6 lines
arch/s390/include/asm/uv.h +6 -0
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;

again, we should probably check for rc != UVC_RC_EXECUTED to detect any other problem.

Quoted 3 lines

That's handled by the CC and the return below. CC == 1 means error
cc == 0 is success, that's why we return erly on cc == 0

Right, uv_call return depends on CC. Nevermind.

Reviewed-by on v1 6/37
#56 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

In reply to Janosch Frank
Quoted 6 lines · Janosch Frank

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Quoted 8 lines

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

After re-reading.

Quoted 81 lines
---
 arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +51 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/bug.h>
 #include <asm/page.h>
+#include <asm/gmap.h>
 
 #define UVC_RC_EXECUTED		0x0001
 #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
 	return rc ? -EINVAL : 0;
 }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;
+	return -EINVAL;
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;
+	return -EINVAL;
+}
+
 void setup_uv(void);
 void adjust_to_uv_max(unsigned long *vmax);
 #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
 static inline void setup_uv(void) {}
 static inline void adjust_to_uv_max(unsigned long *vmax) {}
 static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
#57 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

In reply to Janosch Frank
Quoted 40 lines · On Thu, 24 Oct 2019 07:40:28 -0400 Janosch Frank <[email protected]> wrote:

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
 arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +7 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/bug.h>
 #include <asm/page.h>
+#include <asm/gmap.h>
 
 #define UVC_RC_EXECUTED		0x0001
 #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
 	return rc ? -EINVAL : 0;
 }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported
+ */

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 298

Quoted 10 lines
arch/s390/include/asm/uv.h +10 -0
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;
+	return -EINVAL;

No possibility for other return codes here (e.g. -EFAULT)? (Asking because you look at a rc in the control block in the reverse function.)

Quoted 44 lines
arch/s390/include/asm/uv.h +34 -0
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;
+	return -EINVAL;
+}
+
 void setup_uv(void);
 void adjust_to_uv_max(unsigned long *vmax);
 #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
 static inline void setup_uv(void) {}
 static inline void adjust_to_uv_max(unsigned long *vmax) {}
 static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
author maintainer
#58 lore raw

Re: [RFC 06/37] s390: UV: Add import and export to UV library

Quoted 48 lines · Cornelia Huck
Quoted 45 lines · On Thu, 24 Oct 2019 07:40:28 -0400 Janosch Frank <[email protected]> wrote:

The convert to/from secure (or also "import/export") ultravisor calls are need for page management, i.e. paging, of secure execution VM.

Export encrypts a secure guest's page and makes it accessible to the guest for paging.

Import makes a page accessible to a secure guest.
On the first import of that page, the page will be cleared by the Ultravisor before it is given to the guest.

All following imports will decrypt a exported page and verify integrity before giving the page to the guest.

---
 arch/s390/include/asm/uv.h | 51 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
arch/s390/include/asm/uv.h +12 -0
index 0bfbafcca136..99cdd2034503 100644
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/bug.h>
 #include <asm/page.h>
+#include <asm/gmap.h>
 
 #define UVC_RC_EXECUTED		0x0001
 #define UVC_RC_INV_CMD		0x0002
@@ -279,6 +280,54 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret)
 	return rc ? -EINVAL : 0;
 }
 
+/*
+ * Requests the Ultravisor to encrypt a guest page and make it
+ * accessible to the host for paging (export).
+ *
+ * @paddr: Absolute host address of page to be exported
+ */
+static inline int uv_convert_from_secure(unsigned long paddr)
+{
+	struct uv_cb_cfs uvcb = {
+		.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
+		.header.len = sizeof(uvcb),
Quoted 8 lines

arch/s390/include/asm/uv.h static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) · line 298

Quoted 5 lines
arch/s390/include/asm/uv.h +5 -0
+		.paddr = paddr
+	};
+	if (!uv_call(0, (u64)&uvcb))
+		return 0;
+	return -EINVAL;

No possibility for other return codes here (e.g. -EFAULT)? (Asking because you look at a rc in the control block in the reverse function.)

Notice the "paddr" variable?
We work on physical memory for this UV call, all error codes that are defined are either input errors (not possible via the exception handlers), a KVM management error or an attack on the VM.

Quoted 46 lines
Quoted 45 lines
arch/s390/include/asm/uv.h +34 -0
+}
+
+/*
+ * Requests the Ultravisor to make a page accessible to a guest
+ * (import). If it's brought in the first time, it will be cleared. If
+ * it has been exported before, it will be decrypted and integrity
+ * checked.
+ *
+ * @handle: Ultravisor guest handle
+ * @gaddr: Guest 2 absolute address to be imported
+ */
+static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
+{
+	int cc;
+	struct uv_cb_cts uvcb = {
+		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
+		.header.len = sizeof(uvcb),
+		.guest_handle = gmap->se_handle,
+		.gaddr = gaddr
+	};
+
+	cc = uv_call(0, (u64)&uvcb);
+
+	if (!cc)
+		return 0;
+	if (uvcb.header.rc == 0x104)
+		return -EEXIST;
+	if (uvcb.header.rc == 0x10a)
+		return -EFAULT;
+	return -EINVAL;
+}
+
 void setup_uv(void);
 void adjust_to_uv_max(unsigned long *vmax);
 #else
@@ -286,6 +335,8 @@ void adjust_to_uv_max(unsigned long *vmax);
 static inline void setup_uv(void) {}
 static inline void adjust_to_uv_max(unsigned long *vmax) {}
 static inline int uv_cmd_nodata(u64 handle, u16 cmd, u32 *ret) { return 0; }
+static inline int uv_convert_from_secure(unsigned long paddr) { return 0; }
+static inline int uv_convert_to_secure(unsigned long handle, unsigned long gaddr) { return 0; }
 #endif
 
 #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) ||                          \
author maintainer
#59 lore raw

RFC 7/37 KVM: s390: protvirt: Secure memory is not mergeable

In reply to Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
 arch/s390/include/asm/gmap.h |  1 +
 arch/s390/kvm/kvm-s390.c     |  6 ++++++
 arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
 3 files changed, 25 insertions(+), 10 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
 void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
 			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
 #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
 		if (r)
 			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
 		mutex_lock(&kvm->lock);
 		kvm_s390_vcpu_block_all(kvm);
 		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +18 -10
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
 }
 EXPORT_SYMBOL_GPL(s390_enable_sie);
 
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags)) {
+			mm->context.uses_skeys = 0;
+			return -ENOMEM;
+		}
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+
 /*
  * Enable storage key handling from now on and initialize the storage
  * keys with the default key.
@@ -2593,7 +2610,6 @@ static const struct mm_walk_ops enable_skey_walk_ops = {
 int s390_enable_skey(void)
 {
 	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
 	int rc = 0;
 
 	down_write(&mm->mmap_sem);
@@ -2601,15 +2617,7 @@ int s390_enable_skey(void)
 		goto out_up;
 
 	mm->context.uses_skeys = 1;
-	for (vma = mm->mmap; vma; vma = vma->vm_next) {
-		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
-				MADV_UNMERGEABLE, &vma->vm_flags)) {
-			mm->context.uses_skeys = 0;
-			rc = -ENOMEM;
-			goto out_up;
-		}
-	}
-	mm->def_flags &= ~VM_MERGEABLE;
+	gmap_mark_unmergeable();
 
 	walk_page_range(mm, 0, TASK_SIZE, &enable_skey_walk_ops, NULL);
 
Signature · 1 line
2.20.1
#60 lore raw

Re: [RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 48 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
  arch/s390/include/asm/gmap.h |  1 +
  arch/s390/kvm/kvm-s390.c     |  6 ++++++
  arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
  3 files changed, 25 insertions(+), 10 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
  void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
  			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
  #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
  		if (r)
  			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
  		mutex_lock(&kvm->lock);
  		kvm_s390_vcpu_block_all(kvm);
  		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
  }
  EXPORT_SYMBOL_GPL(s390_enable_sie);

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2559

Quoted 10 lines
arch/s390/mm/gmap.c +9 -0
 
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags)) {
+			mm->context.uses_skeys = 0;

That skey setting does not make too much sense when coming via kvm_s390_handle_pv(). handle that in the caller?

Quoted 37 lines
arch/s390/mm/gmap.c +9 -10
+			return -ENOMEM;
+		}
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+
  /*
   * Enable storage key handling from now on and initialize the storage
   * keys with the default key.
@@ -2593,7 +2610,6 @@ static const struct mm_walk_ops enable_skey_walk_ops = {
  int s390_enable_skey(void)
  {
  	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
  	int rc = 0;
 
  	down_write(&mm->mmap_sem);
@@ -2601,15 +2617,7 @@ int s390_enable_skey(void)
  		goto out_up;
 
  	mm->context.uses_skeys = 1;
-	for (vma = mm->mmap; vma; vma = vma->vm_next) {
-		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
-				MADV_UNMERGEABLE, &vma->vm_flags)) {
-			mm->context.uses_skeys = 0;
-			rc = -ENOMEM;
-			goto out_up;
-		}
-	}
-	mm->def_flags &= ~VM_MERGEABLE;
+	gmap_mark_unmergeable();
 
  	walk_page_range(mm, 0, TASK_SIZE, &enable_skey_walk_ops, NULL);
 
Signature · 3 lines
Thanks,

David / dhildenb
maintainer
#61 lore raw

Re: [RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 50 lines · On Thu, 24 Oct 2019 18:07:14 +0200 David Hildenbrand <[email protected]> wrote:
Quoted 49 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
  arch/s390/include/asm/gmap.h |  1 +
  arch/s390/kvm/kvm-s390.c     |  6 ++++++
  arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
  3 files changed, 25 insertions(+), 10 deletions(-)
(quoted diff) +0 -0

b/arch/s390/include/asm/gmap.h index 6efc0b501227..eab6a2ec3599 100644 --- a/arch/s390/include/asm/gmap.h
+++ b/arch/s390/include/asm/gmap.h

(quoted diff) +1 -0
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *,
 unsigned long start, 
  void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long
 dirty_bitmap[4], unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
  #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm
 *kvm, struct kvm_pv_cmd *cmd) if (r)
  			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
  		mutex_lock(&kvm->lock);
  		kvm_s390_vcpu_block_all(kvm);
  		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
  }
  EXPORT_SYMBOL_GPL(s390_enable_sie);
 
Quoted 13 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2559

Quoted 10 lines
arch/s390/mm/gmap.c +9 -0
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags))
 {
+			mm->context.uses_skeys = 0;  

That skey setting does not make too much sense when coming via kvm_s390_handle_pv(). handle that in the caller?

protected guests run keyless; any attempt to use keys in the guest will result in an exception in the guest.

Quoted 42 lines
Quoted 40 lines
arch/s390/mm/gmap.c +8 -0
+			return -ENOMEM;
+		}
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+
  /*
   * Enable storage key handling from now on and initialize the

storage
* keys with the default key.

arch/s390/mm/gmap.c +1 -10
@@ -2593,7 +2610,6 @@ static const struct mm_walk_ops
 enable_skey_walk_ops = { int s390_enable_skey(void)
  {
  	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
  	int rc = 0;
 
  	down_write(&mm->mmap_sem);
@@ -2601,15 +2617,7 @@ int s390_enable_skey(void)
  		goto out_up;
 
  	mm->context.uses_skeys = 1;
-	for (vma = mm->mmap; vma; vma = vma->vm_next) {
-		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
-				MADV_UNMERGEABLE, &vma->vm_flags))
 {
-			mm->context.uses_skeys = 0;
-			rc = -ENOMEM;
-			goto out_up;
-		}
-	}
-	mm->def_flags &= ~VM_MERGEABLE;
+	gmap_mark_unmergeable();
 
  	walk_page_range(mm, 0, TASK_SIZE, &enable_skey_walk_ops,

NULL);

#62 lore raw

Re: [RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 67 lines · Claudio Imbrenda
Quoted 50 lines · On Thu, 24 Oct 2019 18:07:14 +0200 David Hildenbrand <[email protected]> wrote:
Quoted 49 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
   arch/s390/include/asm/gmap.h |  1 +
   arch/s390/kvm/kvm-s390.c     |  6 ++++++
   arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
   3 files changed, 25 insertions(+), 10 deletions(-)
(quoted diff) +0 -0

b/arch/s390/include/asm/gmap.h index 6efc0b501227..eab6a2ec3599 100644 --- a/arch/s390/include/asm/gmap.h
+++ b/arch/s390/include/asm/gmap.h

(quoted diff) +1 -0
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *,
 unsigned long start,
   void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long
 dirty_bitmap[4], unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
   #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm
 *kvm, struct kvm_pv_cmd *cmd) if (r)
   			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
   		mutex_lock(&kvm->lock);
   		kvm_s390_vcpu_block_all(kvm);
   		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
   }
   EXPORT_SYMBOL_GPL(s390_enable_sie);
 
Quoted 13 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2559

Quoted 10 lines
arch/s390/mm/gmap.c +9 -0
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags))
 {
+			mm->context.uses_skeys = 0;

That skey setting does not make too much sense when coming via kvm_s390_handle_pv(). handle that in the caller?

Quoted 2 lines

protected guests run keyless; any attempt to use keys in the guest will result in an exception in the guest.

still, this is the recovery path for the "mm->context.uses_skeys = 1;" in enable_skey_walk_ops() and confuses reader (like me).

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#63 lore raw

Re: [RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable

In reply to David Hildenbrand
Quoted 50 lines · David Hildenbrand
Quoted 49 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
  arch/s390/include/asm/gmap.h |  1 +
  arch/s390/kvm/kvm-s390.c     |  6 ++++++
  arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
  3 files changed, 25 insertions(+), 10 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
  void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
  			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
  #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
  		if (r)
  			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
  		mutex_lock(&kvm->lock);
  		kvm_s390_vcpu_block_all(kvm);
  		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
  }
  EXPORT_SYMBOL_GPL(s390_enable_sie);
 
Quoted 12 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2559

Quoted 9 lines
arch/s390/mm/gmap.c +9 -0
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags)) {
+			mm->context.uses_skeys = 0;

That skey setting does not make too much sense when coming via kvm_s390_handle_pv(). handle that in the caller?

Hmm, I think the name of that variable is just plain wrong. It should be "can_use_skeys" or "uses_unmergeable" (which would fit better into the mm context anyway) and then we could add a kvm->arch.uses_skeys to tell that we actually used them for migration checks, etc..

I had long discussions with Martin over these variable names a long time ago..

#64 lore raw

Re: [RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 64 lines · Janosch Frank
Quoted 50 lines · David Hildenbrand
Quoted 49 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
   arch/s390/include/asm/gmap.h |  1 +
   arch/s390/kvm/kvm-s390.c     |  6 ++++++
   arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
   3 files changed, 25 insertions(+), 10 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
   void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
   			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
   #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
   		if (r)
   			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
   		mutex_lock(&kvm->lock);
   		kvm_s390_vcpu_block_all(kvm);
   		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
   }
   EXPORT_SYMBOL_GPL(s390_enable_sie);
 
Quoted 12 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2559

Quoted 9 lines
arch/s390/mm/gmap.c +9 -0
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags)) {
+			mm->context.uses_skeys = 0;

That skey setting does not make too much sense when coming via kvm_s390_handle_pv(). handle that in the caller?

Quoted 8 lines

Hmm, I think the name of that variable is just plain wrong. It should be "can_use_skeys" or "uses_unmergeable" (which would fit better into the mm context anyway) and then we could add a kvm->arch.uses_skeys to tell that we actually used them for migration checks, etc..

I had long discussions with Martin over these variable names a long time ago..

uses_skeys is set during s390_enable_skey(). that is used when we

a) Call an skey instruction
b) Migrate skeys

So it should match "uses" or what am I missing?

If you look at the users of "mm_uses_skeys(mm)" I think "uses_unmergeable" would actually be misleading. (e.g., pgste_set_key()). it really means "somebody used skeys". The unmergable is just a required side effect.

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#65 lore raw

Re: [RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 79 lines · David Hildenbrand
Quoted 64 lines · Janosch Frank
Quoted 50 lines · David Hildenbrand
Quoted 49 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
   arch/s390/include/asm/gmap.h |  1 +
   arch/s390/kvm/kvm-s390.c     |  6 ++++++
   arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
   3 files changed, 25 insertions(+), 10 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
   void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
   			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
   #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
   		if (r)
   			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
   		mutex_lock(&kvm->lock);
   		kvm_s390_vcpu_block_all(kvm);
   		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
   }
   EXPORT_SYMBOL_GPL(s390_enable_sie);
 
Quoted 12 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2559

Quoted 9 lines
arch/s390/mm/gmap.c +9 -0
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags)) {
+			mm->context.uses_skeys = 0;

That skey setting does not make too much sense when coming via kvm_s390_handle_pv(). handle that in the caller?

Quoted 8 lines

Hmm, I think the name of that variable is just plain wrong. It should be "can_use_skeys" or "uses_unmergeable" (which would fit better into the mm context anyway) and then we could add a kvm->arch.uses_skeys to tell that we actually used them for migration checks, etc..

I had long discussions with Martin over these variable names a long time ago..

uses_skeys is set during s390_enable_skey(). that is used when we

a) Call an skey instruction
b) Migrate skeys

Quoted 6 lines

So it should match "uses" or what am I missing?

If you look at the users of "mm_uses_skeys(mm)" I think "uses_unmergeable" would actually be misleading. (e.g., pgste_set_key()). it really means "somebody used skeys". The unmergable is just a required side effect.

Hmm, we couldn't check struct kvm from pgtable.c anyway.
Oh well, I still don't like it very much but your arguments are better :-) Let's fix this.

#66 lore raw

Re: [RFC 07/37] KVM: s390: protvirt: Secure memory is not mergeable

In reply to Janosch Frank
Quoted 81 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
  arch/s390/include/asm/gmap.h |  1 +
  arch/s390/kvm/kvm-s390.c     |  6 ++++++
  arch/s390/mm/gmap.c          | 28 ++++++++++++++++++----------
  3 files changed, 25 insertions(+), 10 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
  void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
  			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
  #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
  		if (r)
  			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
  		mutex_lock(&kvm->lock);
  		kvm_s390_vcpu_block_all(kvm);
  		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +17 -1
index edcdca97e85e..bf365a09f900 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
  }
  EXPORT_SYMBOL_GPL(s390_enable_sie);
 
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags)) {
+			mm->context.uses_skeys = 0;
+			return -ENOMEM;
+		}
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+
  /*
   * Enable storage key handling from now on and initialize the storage
   * keys with the default key.
@@ -2593,7 +2610,6 @@ static const struct mm_walk_ops enable_skey_walk_ops = {
  int s390_enable_skey(void)
  {
  	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
  	int rc = 0;
 
  	down_write(&mm->mmap_sem);
@@ -2601,15 +2617,7 @@ int s390_enable_skey(void)
  		goto out_up;
 
  	mm->context.uses_skeys = 1;

arch/s390/mm/gmap.c int s390_enable_skey(void) · line 2620

Quoted 10 lines
arch/s390/mm/gmap.c +1 -9
-	for (vma = mm->mmap; vma; vma = vma->vm_next) {
-		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
-				MADV_UNMERGEABLE, &vma->vm_flags)) {
-			mm->context.uses_skeys = 0;
-			rc = -ENOMEM;
-			goto out_up;
-		}
-	}
-	mm->def_flags &= ~VM_MERGEABLE;
+	gmap_mark_unmergeable();

also, here we are now ignoring errors?

This looks broken.

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#67 lore raw

RFC v2 1/1 KVM: s390: protvirt: Secure memory is not mergeable

In reply to Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---
 arch/s390/include/asm/gmap.h |  1 +
 arch/s390/kvm/kvm-s390.c     |  6 ++++++
 arch/s390/mm/gmap.c          | 32 +++++++++++++++++++++-----------
 3 files changed, 28 insertions(+), 11 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
 void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
 			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
 #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
 		if (r)
 			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
 		mutex_lock(&kvm->lock);
 		kvm_s390_vcpu_block_all(kvm);
 		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +21 -11
index edcdca97e85e..faecdf81abdb 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
 }
 EXPORT_SYMBOL_GPL(s390_enable_sie);
 
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags))
+			return -ENOMEM;
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+
 /*
  * Enable storage key handling from now on and initialize the storage
  * keys with the default key.
@@ -2593,24 +2610,17 @@ static const struct mm_walk_ops enable_skey_walk_ops = {
 int s390_enable_skey(void)
 {
 	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
 	int rc = 0;
 
 	down_write(&mm->mmap_sem);
 	if (mm_uses_skeys(mm))
 		goto out_up;
 
-	mm->context.uses_skeys = 1;
-	for (vma = mm->mmap; vma; vma = vma->vm_next) {
-		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
-				MADV_UNMERGEABLE, &vma->vm_flags)) {
-			mm->context.uses_skeys = 0;
-			rc = -ENOMEM;
-			goto out_up;
-		}
-	}
-	mm->def_flags &= ~VM_MERGEABLE;
+	rc = gmap_mark_unmergeable();
+	if (rc)
+		goto out_up;
 
+	mm->context.uses_skeys = 1;
 	walk_page_range(mm, 0, TASK_SIZE, &enable_skey_walk_ops, NULL);
 
 out_up:
Signature · 1 line
2.20.1
Reviewed-by on v2 1/1
#68 lore raw

Re: [RFC v2] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 8 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

v2 Looks better.

Quoted 91 lines
---
 arch/s390/include/asm/gmap.h |  1 +
 arch/s390/kvm/kvm-s390.c     |  6 ++++++
 arch/s390/mm/gmap.c          | 32 +++++++++++++++++++++-----------
 3 files changed, 28 insertions(+), 11 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 6efc0b501227..eab6a2ec3599 100644
@@ -145,4 +145,5 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start,
 
 void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
 			     unsigned long gaddr, unsigned long vmaddr);
+int gmap_mark_unmergeable(void);
 #endif /* _ASM_S390_GMAP_H */
arch/s390/kvm/kvm-s390.c +6 -0
index 924132d92782..d1ba12f857e7 100644
@@ -2176,6 +2176,12 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
 		if (r)
 			break;
 
+		down_write(&current->mm->mmap_sem);
+		r = gmap_mark_unmergeable();
+		up_write(&current->mm->mmap_sem);
+		if (r)
+			break;
+
 		mutex_lock(&kvm->lock);
 		kvm_s390_vcpu_block_all(kvm);
 		/* FMT 4 SIE needs esca */
arch/s390/mm/gmap.c +21 -11
index edcdca97e85e..faecdf81abdb 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
 }
 EXPORT_SYMBOL_GPL(s390_enable_sie);
 
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags))
+			return -ENOMEM;
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+
 /*
  * Enable storage key handling from now on and initialize the storage
  * keys with the default key.
@@ -2593,24 +2610,17 @@ static const struct mm_walk_ops enable_skey_walk_ops = {
 int s390_enable_skey(void)
 {
 	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
 	int rc = 0;
 
 	down_write(&mm->mmap_sem);
 	if (mm_uses_skeys(mm))
 		goto out_up;
 
-	mm->context.uses_skeys = 1;
-	for (vma = mm->mmap; vma; vma = vma->vm_next) {
-		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
-				MADV_UNMERGEABLE, &vma->vm_flags)) {
-			mm->context.uses_skeys = 0;
-			rc = -ENOMEM;
-			goto out_up;
-		}
-	}
-	mm->def_flags &= ~VM_MERGEABLE;
+	rc = gmap_mark_unmergeable();
+	if (rc)
+		goto out_up;
 
+	mm->context.uses_skeys = 1;
 	walk_page_range(mm, 0, TASK_SIZE, &enable_skey_walk_ops, NULL);
 
 out_up:
#69 lore raw

Re: [RFC v2] KVM: s390: protvirt: Secure memory is not mergeable

In reply to Janosch Frank
Quoted 6 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

Patch itself looks good to me, but I do wonder: Is this really needed when pinning all encrypted pages currently?

Not sure about races between KSM and the pinning/encrypting thread, similar to paging, though ...

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#70 lore raw

Re: [RFC v2] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 5 lines · David Hildenbrand
Quoted 4 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Quoted 9 lines
Quoted 2 lines

Let's mark the guest pages as unmergeable when we transition to secure mode.

Patch itself looks good to me, but I do wonder: Is this really needed when pinning all encrypted pages currently?

Not sure about races between KSM and the pinning/encrypting thread, similar to paging, though ...

The pinning was added several months after I wrote the patch. Now that we have it, we really need to have another proper look at the whole topic.

Thanks for your review :-)

Reviewed-by
#71 lore raw

Re: [RFC v2] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 16 lines · Janosch Frank
Quoted 5 lines · David Hildenbrand
Quoted 4 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Quoted 9 lines
Quoted 2 lines

Let's mark the guest pages as unmergeable when we transition to secure mode.

Patch itself looks good to me, but I do wonder: Is this really needed when pinning all encrypted pages currently?

Not sure about races between KSM and the pinning/encrypting thread, similar to paging, though ...

Quoted 5 lines

The pinning was added several months after I wrote the patch. Now that we have it, we really need to have another proper look at the whole topic.

Thanks for your review :-)

I'd certainly prefer this patch (+some way to mlock) over pinning ;)

You can have

For this patch, if you end up needing it :)

Signature · 3 lines
Thanks,

David / dhildenb
Reviewed-by on v2 1/1
#72 lore raw

Re: [RFC v2] KVM: s390: protvirt: Secure memory is not mergeable

In reply to Janosch Frank
Quoted 9 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---

[...]

Quoted 5 lines
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..faecdf81abdb 100644

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2556

Quoted 9 lines
arch/s390/mm/gmap.c +6 -0
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
 }
 EXPORT_SYMBOL_GPL(s390_enable_sie);
 
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+

Please remove one of the two empty lines.

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2567

Quoted 11 lines
arch/s390/mm/gmap.c +11 -0
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags))
+			return -ENOMEM;
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+

[...]

Apart from the cosmetic nit, the patch looks fine to me.

author maintainer
#73 lore raw

Re: [RFC v2] KVM: s390: protvirt: Secure memory is not mergeable

Quoted 19 lines · Thomas Huth
Quoted 9 lines · Janosch Frank

KSM will not work on secure pages, because when the kernel reads a secure page, it will be encrypted and hence no two pages will look the same.

Let's mark the guest pages as unmergeable when we transition to secure mode.

---

[...]

Quoted 8 lines
arch/s390/mm/gmap.c +0 -0
index edcdca97e85e..faecdf81abdb 100644
@@ -2548,6 +2548,23 @@ int s390_enable_sie(void)
 }
 EXPORT_SYMBOL_GPL(s390_enable_sie);
 
Quoted 8 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2556

Quoted 6 lines
arch/s390/mm/gmap.c +6 -0
+int gmap_mark_unmergeable(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+

Please remove one of the two empty lines.

Already gone

Quoted 8 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2563

Quoted 8 lines
arch/s390/mm/gmap.c +7 -0
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (ksm_madvise(vma, vma->vm_start, vma->vm_end,
+				MADV_UNMERGEABLE, &vma->vm_flags))
+			return -ENOMEM;
+	}
+	mm->def_flags &= ~VM_MERGEABLE;
+

Also this one was removed

Quoted 10 lines

arch/s390/mm/gmap.c int s390_enable_sie(void) · line 2567

Quoted 4 lines
arch/s390/mm/gmap.c +4 -0
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gmap_mark_unmergeable);
+

[...]

Apart from the cosmetic nit, the patch looks fine to me.

Thanks!

author maintainer
#74 lore raw

RFC 8/37 KVM: s390: add missing include in gmap.h

In reply to Janosch Frank

From: Claudio Imbrenda <[email protected]>

gmap.h references radix trees, but does not include linux/radix-tree.h itself. Sources that include gmap.h but not also radix-tree.h will therefore fail to compile.

This simple patch adds the include for linux/radix-tree.h in gmap.h so that users of gmap.h will be able to compile.

---
 arch/s390/include/asm/gmap.h | 1 +
 1 file changed, 1 insertion(+)
arch/s390/include/asm/gmap.h +1 -0
index eab6a2ec3599..99b3eedda26e 100644
@@ -10,6 +10,7 @@
 #define _ASM_S390_GMAP_H
 
 #include <linux/refcount.h>
+#include <linux/radix-tree.h>
 
 /* Generic bits for GMAP notification on DAT table entry changes. */
 #define GMAP_NOTIFY_SHADOW	0x2
Signature · 1 line
2.20.1
#75 lore raw

Re: [RFC 08/37] KVM: s390: add missing include in gmap.h

Quoted 20 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

gmap.h references radix trees, but does not include linux/radix-tree.h itself. Sources that include gmap.h but not also radix-tree.h will therefore fail to compile.

This simple patch adds the include for linux/radix-tree.h in gmap.h so that users of gmap.h will be able to compile.

---
  arch/s390/include/asm/gmap.h | 1 +
  1 file changed, 1 insertion(+)
arch/s390/include/asm/gmap.h +0 -0
index eab6a2ec3599..99b3eedda26e 100644

arch/s390/include/asm/gmap.h line 16

Quoted 7 lines
arch/s390/include/asm/gmap.h +1 -0
@@ -10,6 +10,7 @@
  #define _ASM_S390_GMAP_H
 
  #include <linux/refcount.h>
+#include <linux/radix-tree.h>
 
  /* Generic bits for GMAP notification on DAT table entry changes. */
  #define GMAP_NOTIFY_SHADOW	0x2

Not sure if that's worth a separate patch, just squash it into the patch that needs it?

We usually don't care about includes as long as it compiles ...

Signature · 3 lines
Thanks,

David / dhildenb
Reviewed-by on v1 8/37
#76 lore raw

Re: [RFC 08/37] KVM: s390: add missing include in gmap.h

In reply to Janosch Frank
Quoted 20 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

gmap.h references radix trees, but does not include linux/radix-tree.h itself. Sources that include gmap.h but not also radix-tree.h will therefore fail to compile.

This simple patch adds the include for linux/radix-tree.h in gmap.h so that users of gmap.h will be able to compile.

---
 arch/s390/include/asm/gmap.h | 1 +
 1 file changed, 1 insertion(+)
arch/s390/include/asm/gmap.h +0 -0
index eab6a2ec3599..99b3eedda26e 100644

arch/s390/include/asm/gmap.h line 16

Quoted 7 lines
arch/s390/include/asm/gmap.h +1 -0
@@ -10,6 +10,7 @@
 #define _ASM_S390_GMAP_H
 
 #include <linux/refcount.h>
+#include <linux/radix-tree.h>
 
 /* Generic bits for GMAP notification on DAT table entry changes. */
 #define GMAP_NOTIFY_SHADOW	0x2
author maintainer
#77 lore raw

RFC 9/37 KVM: s390: protvirt: Implement on-demand pinning

In reply to Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

---
 arch/s390/include/asm/gmap.h |  1 +
 arch/s390/include/asm/uv.h   |  6 +++++
 arch/s390/kernel/uv.c        | 20 ++++++++++++++
 arch/s390/kvm/kvm-s390.c     |  2 ++
 arch/s390/kvm/pv.c           | 51 ++++++++++++++++++++++++++++++------
 5 files changed, 72 insertions(+), 8 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 99b3eedda26e..483f64427c0e 100644
@@ -63,6 +63,7 @@ struct gmap {
 	struct gmap *parent;
 	unsigned long orig_asce;
 	unsigned long se_handle;
+	struct page **pinned_pages;
 	int edat_level;
 	bool removed;
 	bool initialized;
arch/s390/include/asm/uv.h +6 -0
index 99cdd2034503..9ce9363aee1c 100644
@@ -298,6 +298,7 @@ static inline int uv_convert_from_secure(unsigned long paddr)
 	return -EINVAL;
 }
 
+int kvm_s390_pv_pin_page(struct gmap *gmap, unsigned long gpa);
 /*
  * Requests the Ultravisor to make a page accessible to a guest
  * (import). If it's brought in the first time, it will be cleared. If
@@ -317,6 +318,11 @@ static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
 		.gaddr = gaddr
 	};
 
+	down_read(&gmap->mm->mmap_sem);
+	cc = kvm_s390_pv_pin_page(gmap, gaddr);
+	up_read(&gmap->mm->mmap_sem);
+	if (cc)
+		return cc;
 	cc = uv_call(0, (u64)&uvcb);
 
 	if (!cc)
arch/s390/kernel/uv.c +20 -0
index f7778493e829..36554402b5c6 100644
@@ -98,4 +98,24 @@ void adjust_to_uv_max(unsigned long *vmax)
 	if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
 		*vmax = uv_info.max_sec_stor_addr;
 }
+
+int kvm_s390_pv_pin_page(struct gmap *gmap, unsigned long gpa)
+{
+	unsigned long hva, gfn = gpa / PAGE_SIZE;
+	int rc;
+
+	if (!gmap->pinned_pages)
+		return -EINVAL;
+	hva = __gmap_translate(gmap, gpa);
+	if (IS_ERR_VALUE(hva))
+		return -EFAULT;
+	if (gmap->pinned_pages[gfn])
+		return -EEXIST;
+	rc = get_user_pages_fast(hva, 1, FOLL_WRITE, gmap->pinned_pages + gfn);
+	if (rc < 0)
+		return rc;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_s390_pv_pin_page);
+
 #endif
arch/s390/kvm/kvm-s390.c +2 -0
index d1ba12f857e7..490fde080107 100644
@@ -2196,6 +2196,7 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
 		/* All VCPUs have to be destroyed before this call. */
 		mutex_lock(&kvm->lock);
 		kvm_s390_vcpu_block_all(kvm);
+		kvm_s390_pv_unpin(kvm);
 		r = kvm_s390_pv_destroy_vm(kvm);
 		if (!r)
 			kvm_s390_pv_dealloc_vm(kvm);
@@ -2680,6 +2681,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 	kvm_s390_gisa_destroy(kvm);
 	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
 	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_unpin(kvm);
 		kvm_s390_pv_destroy_vm(kvm);
 		kvm_s390_pv_dealloc_vm(kvm);
 	}
arch/s390/kvm/pv.c +43 -8

86 lines not on this page · raw message

Signature · 1 line
2.20.1
#78 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 4 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

Please explain why you do stuff. Why do we have to pin the hole guest memory? Why can't we mlock() the hole memory to avoid swapping in user space?

This really screams for a proper explanation.

Quoted 190 lines
---
  arch/s390/include/asm/gmap.h |  1 +
  arch/s390/include/asm/uv.h   |  6 +++++
  arch/s390/kernel/uv.c        | 20 ++++++++++++++
  arch/s390/kvm/kvm-s390.c     |  2 ++
  arch/s390/kvm/pv.c           | 51 ++++++++++++++++++++++++++++++------
  5 files changed, 72 insertions(+), 8 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 99b3eedda26e..483f64427c0e 100644
@@ -63,6 +63,7 @@ struct gmap {
  	struct gmap *parent;
  	unsigned long orig_asce;
  	unsigned long se_handle;
+	struct page **pinned_pages;
  	int edat_level;
  	bool removed;
  	bool initialized;
arch/s390/include/asm/uv.h +6 -0
index 99cdd2034503..9ce9363aee1c 100644
@@ -298,6 +298,7 @@ static inline int uv_convert_from_secure(unsigned long paddr)
  	return -EINVAL;
  }
 
+int kvm_s390_pv_pin_page(struct gmap *gmap, unsigned long gpa);
  /*
   * Requests the Ultravisor to make a page accessible to a guest
   * (import). If it's brought in the first time, it will be cleared. If
@@ -317,6 +318,11 @@ static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
  		.gaddr = gaddr
  	};
 
+	down_read(&gmap->mm->mmap_sem);
+	cc = kvm_s390_pv_pin_page(gmap, gaddr);
+	up_read(&gmap->mm->mmap_sem);
+	if (cc)
+		return cc;
  	cc = uv_call(0, (u64)&uvcb);
 
  	if (!cc)
arch/s390/kernel/uv.c +20 -0
index f7778493e829..36554402b5c6 100644
@@ -98,4 +98,24 @@ void adjust_to_uv_max(unsigned long *vmax)
  	if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
  		*vmax = uv_info.max_sec_stor_addr;
  }
+
+int kvm_s390_pv_pin_page(struct gmap *gmap, unsigned long gpa)
+{
+	unsigned long hva, gfn = gpa / PAGE_SIZE;
+	int rc;
+
+	if (!gmap->pinned_pages)
+		return -EINVAL;
+	hva = __gmap_translate(gmap, gpa);
+	if (IS_ERR_VALUE(hva))
+		return -EFAULT;
+	if (gmap->pinned_pages[gfn])
+		return -EEXIST;
+	rc = get_user_pages_fast(hva, 1, FOLL_WRITE, gmap->pinned_pages + gfn);
+	if (rc < 0)
+		return rc;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_s390_pv_pin_page);
+
  #endif
arch/s390/kvm/kvm-s390.c +2 -0
index d1ba12f857e7..490fde080107 100644
@@ -2196,6 +2196,7 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
  		/* All VCPUs have to be destroyed before this call. */
  		mutex_lock(&kvm->lock);
  		kvm_s390_vcpu_block_all(kvm);
+		kvm_s390_pv_unpin(kvm);
  		r = kvm_s390_pv_destroy_vm(kvm);
  		if (!r)
  			kvm_s390_pv_dealloc_vm(kvm);
@@ -2680,6 +2681,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
  	kvm_s390_gisa_destroy(kvm);
  	if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) &&
  	    kvm_s390_pv_is_protected(kvm)) {
+		kvm_s390_pv_unpin(kvm);
  		kvm_s390_pv_destroy_vm(kvm);
  		kvm_s390_pv_dealloc_vm(kvm);
  	}
arch/s390/kvm/pv.c +43 -8
index 80aecd5bea9e..383e660e2221 100644
@@ -15,8 +15,35 @@
  #include <asm/mman.h>
  #include "kvm-s390.h"
 
+static void unpin_destroy(struct page **pages, int nr)
+{
+	int i;
+	struct page *page;
+	u8 *val;
+
+	for (i = 0; i < nr; i++) {
+		page = pages[i];
+		if (!page)	/* page was never used */
+			continue;
+		val = (void *)page_to_phys(page);
+		READ_ONCE(*val);
+		put_page(page);
+	}
+}
+
+void kvm_s390_pv_unpin(struct kvm *kvm)
+{
+	unsigned long npages = kvm->arch.pv.guest_len / PAGE_SIZE;
+
+	mutex_lock(&kvm->slots_lock);
+	unpin_destroy(kvm->arch.gmap->pinned_pages, npages);
+	mutex_unlock(&kvm->slots_lock);
+}
+
  void kvm_s390_pv_dealloc_vm(struct kvm *kvm)
  {
+	vfree(kvm->arch.gmap->pinned_pages);
+	kvm->arch.gmap->pinned_pages = NULL;
  	vfree(kvm->arch.pv.stor_var);
  	free_pages(kvm->arch.pv.stor_base,
  		   get_order(uv_info.guest_base_stor_len));
@@ -28,7 +55,6 @@ int kvm_s390_pv_alloc_vm(struct kvm *kvm)
  	unsigned long base = uv_info.guest_base_stor_len;
  	unsigned long virt = uv_info.guest_virt_var_stor_len;
  	unsigned long npages = 0, vlen = 0;
-	struct kvm_memslots *slots;
  	struct kvm_memory_slot *memslot;
 
  	kvm->arch.pv.stor_var = NULL;
@@ -43,22 +69,26 @@ int kvm_s390_pv_alloc_vm(struct kvm *kvm)
  	 * Slots are sorted by GFN
  	 */
  	mutex_lock(&kvm->slots_lock);
-	slots = kvm_memslots(kvm);
-	memslot = slots->memslots;
+	memslot = kvm_memslots(kvm)->memslots;
  	npages = memslot->base_gfn + memslot->npages;
-
  	mutex_unlock(&kvm->slots_lock);
+
+	kvm->arch.gmap->pinned_pages = vzalloc(npages * sizeof(struct page *));
+	if (!kvm->arch.gmap->pinned_pages)
+		goto out_err;
  	kvm->arch.pv.guest_len = npages * PAGE_SIZE;
 
  	/* Allocate variable storage */
  	vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE);
  	vlen += uv_info.guest_virt_base_stor_len;
  	kvm->arch.pv.stor_var = vzalloc(vlen);
-	if (!kvm->arch.pv.stor_var) {
-		kvm_s390_pv_dealloc_vm(kvm);
-		return -ENOMEM;
-	}
+	if (!kvm->arch.pv.stor_var)
+		goto out_err;
  	return 0;
+
+out_err:
+	kvm_s390_pv_dealloc_vm(kvm);
+	return -ENOMEM;
  }
 
  int kvm_s390_pv_destroy_vm(struct kvm *kvm)
@@ -216,6 +246,11 @@ int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
  	for (i = 0; i < size / PAGE_SIZE; i++) {
  		uvcb.gaddr = addr + i * PAGE_SIZE;
  		uvcb.tweak[1] = i * PAGE_SIZE;
+		down_read(&kvm->mm->mmap_sem);
+		rc = kvm_s390_pv_pin_page(kvm->arch.gmap, uvcb.gaddr);
+		up_read(&kvm->mm->mmap_sem);
+		if (rc && (rc != -EEXIST))
+			break;
  retry:
  		rc = uv_call(0, (u64)&uvcb);
  		if (!rc)
Signature · 3 lines
Thanks,

David / dhildenb
#79 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 7 lines · David Hildenbrand
Quoted 4 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

Please explain why you do stuff. Why do we have to pin the hole guest memory? Why can't we mlock() the hole memory to avoid swapping in user space?

Basically we pin the guest for the same reason as AMD did it for their SEV. It is hard to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

Quoted 187 lines

This really screams for a proper explanation.

Quoted 183 lines

---
  arch/s390/include/asm/gmap.h |  1 + arch/s390/include/asm/uv.h   |  6 +++++ arch/s390/kernel/uv.c        | 20 ++++++++++++++ arch/s390/kvm/kvm-s390.c     |  2 ++
  arch/s390/kvm/pv.c           | 51 ++++++++++++++++++++++++++++++------

  5 files changed, 72 insertions(+), 8 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 99b3eedda26e..483f64427c0e 100644
@@ -63,6 +63,7 @@ struct gmap {
       struct gmap *parent;
       unsigned long orig_asce;
       unsigned long se_handle;
+    struct page **pinned_pages;
       int edat_level;
       bool removed;
       bool initialized;
arch/s390/include/asm/uv.h +0 -0
index 99cdd2034503..9ce9363aee1c 100644
@@ -298,6 +298,7 @@ static inline int uv_convert_from_secure(unsigned long paddr)
       return -EINVAL;
   }
   +int kvm_s390_pv_pin_page(struct gmap *gmap, unsigned long gpa);
   /*
    * Requests the Ultravisor to make a page accessible to a guest
    * (import). If it's brought in the first time, it will be cleared. If
@@ -317,6 +318,11 @@ static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)

          .gaddr = gaddr
      };
  +    down_read(&gmap->mm->mmap_sem);
+    cc = kvm_s390_pv_pin_page(gmap, gaddr);
+    up_read(&gmap->mm->mmap_sem);
+    if (cc)
+        return cc;
      cc = uv_call(0, (u64)&uvcb);
        if (!cc)

arch/s390/kernel/uv.c +20 -0
index f7778493e829..36554402b5c6 100644
@@ -98,4 +98,24 @@ void adjust_to_uv_max(unsigned long *vmax)
       if (prot_virt_host && *vmax > uv_info.max_sec_stor_addr)
           *vmax = uv_info.max_sec_stor_addr;
   }
+
+int kvm_s390_pv_pin_page(struct gmap *gmap, unsigned long gpa)
+{
+    unsigned long hva, gfn = gpa / PAGE_SIZE;
+    int rc;
+
+    if (!gmap->pinned_pages)
+        return -EINVAL;
+    hva = __gmap_translate(gmap, gpa);
+    if (IS_ERR_VALUE(hva))
+        return -EFAULT;
+    if (gmap->pinned_pages[gfn])
+        return -EEXIST;
+    rc = get_user_pages_fast(hva, 1, FOLL_WRITE, gmap->pinned_pages + gfn);
+    if (rc < 0)
+        return rc;
+    return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_s390_pv_pin_page);
+
   #endif
arch/s390/kvm/kvm-s390.c +1 -0
index d1ba12f857e7..490fde080107 100644
@@ -2196,6 +2196,7 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
           /* All VCPUs have to be destroyed before this call. */
           mutex_lock(&kvm->lock);
           kvm_s390_vcpu_block_all(kvm);
+        kvm_s390_pv_unpin(kvm);
           r = kvm_s390_pv_destroy_vm(kvm);
           if (!r)
               kvm_s390_pv_dealloc_vm(kvm);
@@ -2680,6 +2681,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
       kvm_s390_gisa_destroy(kvm);

      if (IS_ENABLED(CONFIG_KVM_S390_PROTECTED_VIRTUALIZATION_HOST) && kvm_s390_pv_is_protected(kvm)) {
+        kvm_s390_pv_unpin(kvm); kvm_s390_pv_destroy_vm(kvm); kvm_s390_pv_dealloc_vm(kvm);
      }

arch/s390/kvm/pv.c +42 -8
index 80aecd5bea9e..383e660e2221 100644
@@ -15,8 +15,35 @@
   #include <asm/mman.h>
   #include "kvm-s390.h"
   +static void unpin_destroy(struct page **pages, int nr)
+{
+    int i;
+    struct page *page;
+    u8 *val;
+
+    for (i = 0; i < nr; i++) {
+        page = pages[i];
+        if (!page)    /* page was never used */
+            continue;
+        val = (void *)page_to_phys(page);
+        READ_ONCE(*val);
+        put_page(page);
+    }
+}
+
+void kvm_s390_pv_unpin(struct kvm *kvm)
+{
+    unsigned long npages = kvm->arch.pv.guest_len / PAGE_SIZE;
+
+    mutex_lock(&kvm->slots_lock);
+    unpin_destroy(kvm->arch.gmap->pinned_pages, npages);
+    mutex_unlock(&kvm->slots_lock);
+}
+
   void kvm_s390_pv_dealloc_vm(struct kvm *kvm)
   {
+    vfree(kvm->arch.gmap->pinned_pages);
+    kvm->arch.gmap->pinned_pages = NULL;
       vfree(kvm->arch.pv.stor_var);
       free_pages(kvm->arch.pv.stor_base,
              get_order(uv_info.guest_base_stor_len));
@@ -28,7 +55,6 @@ int kvm_s390_pv_alloc_vm(struct kvm *kvm)
       unsigned long base = uv_info.guest_base_stor_len;
       unsigned long virt = uv_info.guest_virt_var_stor_len;
       unsigned long npages = 0, vlen = 0;
-    struct kvm_memslots *slots;
       struct kvm_memory_slot *memslot;
         kvm->arch.pv.stor_var = NULL;
@@ -43,22 +69,26 @@ int kvm_s390_pv_alloc_vm(struct kvm *kvm)
        * Slots are sorted by GFN
        */
       mutex_lock(&kvm->slots_lock);
-    slots = kvm_memslots(kvm);
-    memslot = slots->memslots;
+    memslot = kvm_memslots(kvm)->memslots;
       npages = memslot->base_gfn + memslot->npages;
-
       mutex_unlock(&kvm->slots_lock);
+
+    kvm->arch.gmap->pinned_pages = vzalloc(npages * sizeof(struct page *));
+    if (!kvm->arch.gmap->pinned_pages)
+        goto out_err;
       kvm->arch.pv.guest_len = npages * PAGE_SIZE;
         /* Allocate variable storage */
       vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE);
       vlen += uv_info.guest_virt_base_stor_len;
       kvm->arch.pv.stor_var = vzalloc(vlen);
-    if (!kvm->arch.pv.stor_var) {
-        kvm_s390_pv_dealloc_vm(kvm);
-        return -ENOMEM;
-    }
+    if (!kvm->arch.pv.stor_var)
+        goto out_err;
       return 0;
+
+out_err:
+    kvm_s390_pv_dealloc_vm(kvm);
+    return -ENOMEM;
   }
     int kvm_s390_pv_destroy_vm(struct kvm *kvm)
@@ -216,6 +246,11 @@ int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
       for (i = 0; i < size / PAGE_SIZE; i++) {
           uvcb.gaddr = addr + i * PAGE_SIZE;
           uvcb.tweak[1] = i * PAGE_SIZE;
+        down_read(&kvm->mm->mmap_sem);
+        rc = kvm_s390_pv_pin_page(kvm->arch.gmap, uvcb.gaddr);
+        up_read(&kvm->mm->mmap_sem);
+        if (rc && (rc != -EEXIST))
+            break;

  retry:
          rc = uv_call(0, (u64)&uvcb);
          if (!rc)

#80 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 4 lines · Christian Borntraeger
Quoted 1 line · David Hildenbrand

On 24.10.19 13:40, Janosch Frank wrote:

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

Please explain why you do stuff. Why do we have to pin the hole guest memory? Why can't we mlock() the hole memory to avoid swapping in user space?

Basically we pin the guest for the same reason as AMD did it for their SEV. It is hard

Pinning all guest memory is very ugly. What you want is "don't page", what you get is unmovable pages all over the place. I was hoping that you could get around this by having an automatic back-and-forth conversion in place (due to the special new exceptions).

Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

A proper automatic conversion should make this work. What am I missing?

Quoted 2 lines

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

I wonder if we could come up with an alternative (similar to how we override VM_MERGEABLE in the kernel) that can be called and ensured in the kernel. E.g., marking whole VMAs as "don't page" (I remember something like "special VMAs" like used for VDSOs that achieve exactly that, but I am absolutely no expert on that). That would be much nicer than pinning all pages and remembering what you pinned in huge page arrays ...

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#81 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 10 lines · David Hildenbrand
Quoted 9 lines · Christian Borntraeger
Quoted 6 lines · David Hildenbrand
Quoted 4 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

Please explain why you do stuff. Why do we have to pin the hole guest memory? Why can't we mlock() the hole memory to avoid swapping in user space?

Basically we pin the guest for the same reason as AMD did it for their SEV. It is hard

Pinning all guest memory is very ugly. What you want is "don't page", what you get is unmovable pages all over the place. I was hoping that you could get around this by having an automatic back-and-forth conversion in place (due to the special new exceptions).

Yes, that's one of the ideas that have been circulating.

Quoted 11 lines
Quoted 5 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

Quoted 6 lines

A proper automatic conversion should make this work. What am I missing?

Quoted 2 lines

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

Quoted 7 lines

I wonder if we could come up with an alternative (similar to how we override VM_MERGEABLE in the kernel) that can be called and ensured in the kernel. E.g., marking whole VMAs as "don't page" (I remember something like "special VMAs" like used for VDSOs that achieve exactly that, but I am absolutely no expert on that). That would be much nicer than pinning all pages and remembering what you pinned in huge page arrays ...

It might be more worthwhile to just accept one or two releases with pinning and fix the root of the problem than design a nice stopgap.

Btw. s390 is not alone with the problem and we'll try to have another discussion tomorrow with AMD to find a solution which works for more than one architecture.

#82 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Quoted 15 lines
Quoted 7 lines

A proper automatic conversion should make this work. What am I missing?

Quoted 2 lines

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

Quoted 7 lines

I wonder if we could come up with an alternative (similar to how we override VM_MERGEABLE in the kernel) that can be called and ensured in the kernel. E.g., marking whole VMAs as "don't page" (I remember something like "special VMAs" like used for VDSOs that achieve exactly that, but I am absolutely no expert on that). That would be much nicer than pinning all pages and remembering what you pinned in huge page arrays ...

Quoted 2 lines

It might be more worthwhile to just accept one or two releases with pinning and fix the root of the problem than design a nice stopgap.

Quite honestly, to me this feels like a prototype hack that deserves a proper solution first. The issue with this hack is that it affects user space (esp. MADV_DONTNEED no longer working correctly). It's not just something you once fix in the kernel and be done with it.

Quoted 4 lines

Btw. s390 is not alone with the problem and we'll try to have another discussion tomorrow with AMD to find a solution which works for more than one architecture.

Let me know if there was an interesting outcome.

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#83 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 23 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

Quoted 7 lines

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Restarting IO can be quite difficult with CCW, we might need to change request data...

Quoted 16 lines
Quoted 16 lines
Quoted 15 lines

A proper automatic conversion should make this work. What am I missing?

Quoted 2 lines

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

I wonder if we could come up with an alternative (similar to how we override VM_MERGEABLE in the kernel) that can be called and ensured in the kernel. E.g., marking whole VMAs as "don't page" (I remember something like "special VMAs" like used for VDSOs that achieve exactly that, but I am absolutely no expert on that). That would be much nicer than pinning all pages and remembering what you pinned in huge page arrays ...

Quoted 7 lines
Quoted 2 lines

It might be more worthwhile to just accept one or two releases with pinning and fix the root of the problem than design a nice stopgap.

Quite honestly, to me this feels like a prototype hack that deserves a proper solution first. The issue with this hack is that it affects user space (esp. MADV_DONTNEED no longer working correctly). It's not just something you once fix in the kernel and be done with it.

It is a hack, yes.
But we're not the only architecture to need it x86 pins all the memory at the start of the VM and that code is already upstream...

Quoted 7 lines
Quoted 4 lines

Btw. s390 is not alone with the problem and we'll try to have another discussion tomorrow with AMD to find a solution which works for more than one architecture.

Let me know if there was an interesting outcome.

#84 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 29 lines · Janosch Frank
Quoted 28 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

Quoted 5 lines
Quoted 2 lines

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Restarting IO can be quite difficult with CCW, we might need to change request data...

I am no I/O expert, so I can't comment if that would be possible :(

Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
Quoted 5 lines

A proper automatic conversion should make this work. What am I missing?

Quoted 2 lines

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

Quoted 7 lines

I wonder if we could come up with an alternative (similar to how we override VM_MERGEABLE in the kernel) that can be called and ensured in the kernel. E.g., marking whole VMAs as "don't page" (I remember something like "special VMAs" like used for VDSOs that achieve exactly that, but I am absolutely no expert on that). That would be much nicer than pinning all pages and remembering what you pinned in huge page arrays ...

It might be more worthwhile to just accept one or two releases with pinning and fix the root of the problem than design a nice stopgap.

Quoted 8 lines
Quoted 4 lines

Quite honestly, to me this feels like a prototype hack that deserves a proper solution first. The issue with this hack is that it affects user space (esp. MADV_DONTNEED no longer working correctly). It's not just something you once fix in the kernel and be done with it.

It is a hack, yes.
But we're not the only architecture to need it x86 pins all the memory at the start of the VM and that code is already upstream...

IMHO that doesn't make it any better. It is and remains a prototype hack in my opinion.

Signature · 3 lines
Thanks,

David / dhildenb
#85 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

In reply to David Hildenbrand
Quoted 21 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

Quoted 7 lines

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

We have looked into this. You would need to implement this in the low level handler for every I/O. DASD, FCP, PCI based NVME, iscsi. Where do you want to stop?
There is no proper global bounce buffer that works for everything.

Quoted 14 lines
Quoted 14 lines
Quoted 13 lines

A proper automatic conversion should make this work. What am I missing?

Quoted 2 lines

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

I wonder if we could come up with an alternative (similar to how we override VM_MERGEABLE in the kernel) that can be called and ensured in the kernel. E.g., marking whole VMAs as "don't page" (I remember something like "special VMAs" like used for VDSOs that achieve exactly that, but I am absolutely no expert on that). That would be much nicer than pinning all pages and remembering what you pinned in huge page arrays ...

Quoted 4 lines
Quoted 2 lines

It might be more worthwhile to just accept one or two releases with pinning and fix the root of the problem than design a nice stopgap.

Quite honestly, to me this feels like a prototype hack that deserves a proper solution first. The issue with this hack is that it affects user space (esp. MADV_DONTNEED no longer working correctly). It's not just something you once fix in the kernel and be done with it.

I disagree. Pinning is a valid initial version. I would find it strange to allow it for AMD SEV, but not allowing it for s390x.
As far as I can tell MADV_DONTNEED continues to work within the bounds of specification. In fact, it does work (or does not depending on your perspective :-) ) exactly in the same way as on hugetlbfs,which is also a way of pinning.

And yes, I am in full agreement that we must work on lifting that restriction.

Quoted 6 lines
Quoted 4 lines

Btw. s390 is not alone with the problem and we'll try to have another discussion tomorrow with AMD to find a solution which works for more than one architecture.

Let me know if there was an interesting outcome.

#86 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 30 lines · Christian Borntraeger
Quoted 27 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

Quoted 5 lines
Quoted 1 line

I assume that this is a corner case where we don't really have to care about performance in the first shot.

We have looked into this. You would need to implement this in the low level handler for every I/O. DASD, FCP, PCI based NVME, iscsi. Where do you want to stop?

If that's the real fix, we should do that. Maybe one can focus on the real use cases first. But I am no I/O expert, so my judgment might be completely wrong.

Quoted 16 lines

There is no proper global bounce buffer that works for everything.

Quoted 14 lines
Quoted 14 lines
Quoted 13 lines

A proper automatic conversion should make this work. What am I missing?

Quoted 2 lines

As we do not want to rely on the userspace to do the mlock this is now done in the kernel.

I wonder if we could come up with an alternative (similar to how we override VM_MERGEABLE in the kernel) that can be called and ensured in the kernel. E.g., marking whole VMAs as "don't page" (I remember something like "special VMAs" like used for VDSOs that achieve exactly that, but I am absolutely no expert on that). That would be much nicer than pinning all pages and remembering what you pinned in huge page arrays ...

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

It might be more worthwhile to just accept one or two releases with pinning and fix the root of the problem than design a nice stopgap.

Quite honestly, to me this feels like a prototype hack that deserves a proper solution first. The issue with this hack is that it affects user space (esp. MADV_DONTNEED no longer working correctly). It's not just something you once fix in the kernel and be done with it.

I disagree. Pinning is a valid initial version. I would find it strange to allow it for AMD SEV, but not allowing it for s390x.

"not allowing" is wrong. I don't like it, but I am not NACKing it. All I am saying is that this is for me a big fat "prototype" marker.

As a workaround, I would much rather want to see a "don't page" control (process/vma) than pinning every single page if "paging" is the only concern. Such an internal handling would not imply any real user space changes (as noted, like MADV_DONTNEED would see).

Quoted 2 lines

As far as I can tell MADV_DONTNEED continues to work within the bounds of specification. In fact, it does work (or does not depending on your

There is a reason why we disallow MADV_DONTNEED in QEMU when we have such vfio devices (e.g., balloon, postcopy live migration, ...). It does no longer work as specified for devices that pinned pages. You get inconsistent mappings.

Quoted 2 lines

perspective :-) ) exactly in the same way as on hugetlbfs,which is also a way of pinning.

MADV_DONTNEED is documented to not work on huge pages. That's a different story. You have to use FALLOC_FL_PUNCH_HOLE.

Signature · 3 lines
Thanks,

David / dhildenb
#87 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 33 lines · David Hildenbrand
Quoted 32 lines · Christian Borntraeger
Quoted 28 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Quoted 8 lines
Quoted 3 lines

We have looked into this. You would need to implement this in the low level handler for every I/O. DASD, FCP, PCI based NVME, iscsi. Where do you want to stop?

If that's the real fix, we should do that. Maybe one can focus on the real use cases first. But I am no I/O expert, so my judgment might be completely wrong.

Oh, and by the way, as discussed you really only have to care about accesses via "real" I/O devices (IOW, not via the CPU). When accessing via the CPU, you should have automatic conversion back and forth. As I am no expert on I/O, I have no idea how iscsi fits into this picture here (especially on s390x).

Signature · 3 lines
Thanks,

David / dhildenb
#88 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 43 lines · On Mon, 4 Nov 2019 15:42:11 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 33 lines · David Hildenbrand
Quoted 32 lines · Christian Borntraeger
Quoted 28 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Quoted 8 lines
Quoted 3 lines

We have looked into this. You would need to implement this in the low level handler for every I/O. DASD, FCP, PCI based NVME, iscsi. Where do you want to stop?

If that's the real fix, we should do that. Maybe one can focus on the real use cases first. But I am no I/O expert, so my judgment might be completely wrong.

Quoted 6 lines

Oh, and by the way, as discussed you really only have to care about accesses via "real" I/O devices (IOW, not via the CPU). When accessing via the CPU, you should have automatic conversion back and forth. As I am no expert on I/O, I have no idea how iscsi fits into this picture here (especially on s390x).

By "real" I/O devices, you mean things like channel devices, right? (So everything where you basically hand off control to a different kind of processor.)

For classic channel I/O (as used by dasd), I'd expect something like getting a check condition on a ccw if the CU or device cannot access the memory. You will know how far the channel program has progressed, and might be able to restart (from the beginning or from that point). Probably has a chance of working for a subset of channel programs.

For QDIO (as used by FCP), I have no idea how this is could work, as we have long-running channel programs there and any error basically kills the queues, which you would have to re-setup from the beginning.

For PCI devices, I have no idea how the instructions even act.

From my point of view, that error/restart approach looks nice on paper, but it seems hard to make it work in the general case (and I'm unsure if it's possible at all.)

#89 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 53 lines · Cornelia Huck
Quoted 43 lines · On Mon, 4 Nov 2019 15:42:11 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 33 lines · David Hildenbrand
Quoted 32 lines · Christian Borntraeger
Quoted 28 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Quoted 8 lines
Quoted 3 lines

We have looked into this. You would need to implement this in the low level handler for every I/O. DASD, FCP, PCI based NVME, iscsi. Where do you want to stop?

If that's the real fix, we should do that. Maybe one can focus on the real use cases first. But I am no I/O expert, so my judgment might be completely wrong.

Quoted 6 lines

Oh, and by the way, as discussed you really only have to care about accesses via "real" I/O devices (IOW, not via the CPU). When accessing via the CPU, you should have automatic conversion back and forth. As I am no expert on I/O, I have no idea how iscsi fits into this picture here (especially on s390x).

Quoted 3 lines

By "real" I/O devices, you mean things like channel devices, right? (So everything where you basically hand off control to a different kind of processor.)

Exactly.

Quoted 6 lines

For classic channel I/O (as used by dasd), I'd expect something like getting a check condition on a ccw if the CU or device cannot access the memory. You will know how far the channel program has progressed, and might be able to restart (from the beginning or from that point). Probably has a chance of working for a subset of channel programs.

Yeah, sound sane to me.

Quoted 10 lines

For QDIO (as used by FCP), I have no idea how this is could work, as we have long-running channel programs there and any error basically kills the queues, which you would have to re-setup from the beginning.

For PCI devices, I have no idea how the instructions even act.

From my point of view, that error/restart approach looks nice on paper,
but it seems hard to make it work in the general case (and I'm unsure if it's possible at all.)

Then I'm afraid whoever designed protected virtualization didn't properly consider concurrent I/O access to encrypted pages. It might not be easy to sort out, though, so I understand why the I/O part was designed that way :)

I was wondering if one could implement some kind of automatic conversion "back and forth" on I/O access (or even on any access within the HW). I mean, "basically" it's just encrypting/decrypting the page and updating the state by the UV (+ synchronization, lol). But yeah, the UV is involved, and would be triggered somehow via I/O access to these pages. Right now that conversion is performed via exceptions by the OS explicitly. Instead of passing exceptions, the UV could convert automatically. Smells like massive HW changes, if possible and desired at all.

I do wonder what would happen if you back your guest memory not on anonymous memory but on e.g., a file. Could be that this eliminates all options besides pinning and fixing I/O, because we're talking about writeback and not paging.

HOWEVER, reading https://lwn.net/Articles/787636/

"Kara talked mostly about the writeback code; in some cases, it will simply skip pages that are pinned. But there are cases where those pages must be written out — "somebody has called fsync(), and they expect something to be saved". In this case, pinned pages will be written, but they will not be marked clean at the end of the operation; they will still be write-protected in the page tables while writeback is underway, though."

So, sounds like you will get concurrent I/O access even without paging ... and that would leave fixing I/O the only option with the current HW design AFAIKS :/

Signature · 3 lines
Thanks,

David / dhildenb
#90 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

In reply to Cornelia Huck
Quoted 67 lines · Cornelia Huck
Quoted 43 lines · On Mon, 4 Nov 2019 15:42:11 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 33 lines · David Hildenbrand
Quoted 32 lines · Christian Borntraeger
Quoted 28 lines · David Hildenbrand
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

to synchronize page import/export with the I/O for paging. For example you can actually fault in a page that is currently under paging I/O. What do you do? import (so that the guest can run) or export (so that the I/O will work). As this turned out to be harder then we though we decided to defer paging to a later point in time.

Quoted 8 lines
Quoted 5 lines

I don't quite see the issue yet. If you page out, the page will automatically (on access) be converted to !secure/encrypted memory. If the UV/guest wants to access it, it will be automatically converted to secure/unencrypted memory. If you have concurrent access, it will be converted back and forth until one party is done.

IO does not trigger an export on an imported page, but an error condition in the IO subsystem. The page code does not read pages through

Ah, that makes it much clearer. Thanks!

Quoted 4 lines

the cpu, but often just asks the device to read directly and that's where everything goes wrong. We could bounce swapping, but chose to pin for now until we find a proper solution to that problem which nicely integrates into linux.

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Quoted 8 lines
Quoted 3 lines

We have looked into this. You would need to implement this in the low level handler for every I/O. DASD, FCP, PCI based NVME, iscsi. Where do you want to stop?

If that's the real fix, we should do that. Maybe one can focus on the real use cases first. But I am no I/O expert, so my judgment might be completely wrong.

Quoted 6 lines

Oh, and by the way, as discussed you really only have to care about accesses via "real" I/O devices (IOW, not via the CPU). When accessing via the CPU, you should have automatic conversion back and forth. As I am no expert on I/O, I have no idea how iscsi fits into this picture here (especially on s390x).

By "real" I/O devices, you mean things like channel devices, right? (So everything where you basically hand off control to a different kind of processor.)

For classic channel I/O (as used by dasd), I'd expect something like getting a check condition on a ccw if the CU or device cannot access the memory. You will know how far the channel program has progressed, and might be able to restart (from the beginning or from that point). Probably has a chance of working for a subset of channel programs.

For QDIO (as used by FCP), I have no idea how this is could work, as we have long-running channel programs there and any error basically kills the queues, which you would have to re-setup from the beginning.

Quoted 5 lines

For PCI devices, I have no idea how the instructions even act.

From my point of view, that error/restart approach looks nice on paper,
but it seems hard to make it work in the general case (and I'm unsure if it's possible at all.)

One thought: If all we do during an I/O request is read or write (or even a mixture), can we simply restart the whole I/O again, although we did partial reads/writes? This would eliminate the "know how far the channel program has progressed". On error, one would have to touch each involved page (e.g., try to read first byte to trigger a conversion) and restart the I/O. I can understand that this might sound simpler than it is (if it is even possible) and might still be problematic for QDIO as far as I understand. Just a thought.

Signature · 3 lines
Thanks,

David / dhildenb
#91 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 35 lines · On Mon, 4 Nov 2019 19:38:27 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 34 lines · Cornelia Huck
Quoted 20 lines · On Mon, 4 Nov 2019 15:42:11 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 10 lines · David Hildenbrand
Quoted 8 lines · Christian Borntraeger
Quoted 7 lines

How hard would it be to

1. Detect the error condition
2. Try a read on the affected page from the CPU (will will automatically convert to encrypted/!secure)
3. Restart the I/O

I assume that this is a corner case where we don't really have to care about performance in the first shot.

Quoted 8 lines
Quoted 3 lines

We have looked into this. You would need to implement this in the low level handler for every I/O. DASD, FCP, PCI based NVME, iscsi. Where do you want to stop?

If that's the real fix, we should do that. Maybe one can focus on the real use cases first. But I am no I/O expert, so my judgment might be completely wrong.

Quoted 6 lines

Oh, and by the way, as discussed you really only have to care about accesses via "real" I/O devices (IOW, not via the CPU). When accessing via the CPU, you should have automatic conversion back and forth. As I am no expert on I/O, I have no idea how iscsi fits into this picture here (especially on s390x).

By "real" I/O devices, you mean things like channel devices, right? (So everything where you basically hand off control to a different kind of processor.)

Quoted 5 lines
Quoted 5 lines

For classic channel I/O (as used by dasd), I'd expect something like getting a check condition on a ccw if the CU or device cannot access the memory. You will know how far the channel program has progressed, and might be able to restart (from the beginning or from that point). Probably has a chance of working for a subset of channel programs.

NB that there's more than simple reads/writes... could also be control commands, some of which do read/writes as well.

Quoted 11 lines
Quoted 10 lines

For QDIO (as used by FCP), I have no idea how this is could work, as we have long-running channel programs there and any error basically kills the queues, which you would have to re-setup from the beginning.

For PCI devices, I have no idea how the instructions even act.

From my point of view, that error/restart approach looks nice on paper,
but it seems hard to make it work in the general case (and I'm unsure if it's possible at all.)

Quoted 7 lines

One thought: If all we do during an I/O request is read or write (or even a mixture), can we simply restart the whole I/O again, although we did partial reads/writes? This would eliminate the "know how far the channel program has progressed". On error, one would have to touch each involved page (e.g., try to read first byte to trigger a conversion) and restart the I/O. I can understand that this might sound simpler than it is (if it is even possible)

Any control commands might have side effects, though. Problems there should be uncommon; there's still the _general_ case, though :(

Also, there's stuff like rewriting the channel program w/o prefetch, jumping with TIC, etc. Linux probably does not do the former, but at least the dasd driver uses NOP/TIC for error recovery.

Quoted 2 lines

and might still be problematic for QDIO as
far as I understand. Just a thought.

Yes, given that for QDIO, establishing the queues is simply one long-running channel program...

maintainer
#92 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

In reply to David Hildenbrand
Quoted 14 lines · On Thu, 31 Oct 2019 18:30:30 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 13 lines · Christian Borntraeger
Quoted 9 lines · David Hildenbrand
Quoted 4 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

Please explain why you do stuff. Why do we have to pin the hole guest memory? Why can't we mlock() the hole memory to avoid swapping in user space?

Quoted 7 lines
Quoted 2 lines

Basically we pin the guest for the same reason as AMD did it for their SEV. It is hard

Pinning all guest memory is very ugly. What you want is "don't page", what you get is unmovable pages all over the place. I was hoping that you could get around this by having an automatic back-and-forth conversion in place (due to the special new exceptions).

we're not pinning all of guest memory, btw, but only the pages that are actually used.

so if you have a *huge* guest, only the few pages used by the kernel and initrd are actually pinned at VM start. Then one by one the ones actually used when the guest is running get pinned on first use.

I don't need to add anything regarding the other points since the other have commented already :)

#93 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

Quoted 20 lines · Claudio Imbrenda
Quoted 17 lines · On Thu, 31 Oct 2019 18:30:30 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 15 lines · Christian Borntraeger
Quoted 9 lines · David Hildenbrand
Quoted 4 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

Please explain why you do stuff. Why do we have to pin the hole guest memory? Why can't we mlock() the hole memory to avoid swapping in user space?

Basically we pin the guest for the same reason as AMD did it for their SEV. It is hard

Quoted 7 lines
Quoted 4 lines

Pinning all guest memory is very ugly. What you want is "don't page", what you get is unmovable pages all over the place. I was hoping that you could get around this by having an automatic back-and-forth conversion in place (due to the special new exceptions).

we're not pinning all of guest memory, btw, but only the pages that are actually used.

Any longer-running guest will eventually touch all guest physical memory (e.g., page cache, page shuffling), so this is only an optimization for short running guests.

Signature · 3 lines
Thanks,

David / dhildenb
#94 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

In reply to Janosch Frank
Quoted 6 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

[...]

Quoted 9 lines
arch/s390/kvm/pv.c +1 -0
index 80aecd5bea9e..383e660e2221 100644
@@ -15,8 +15,35 @@
 #include <asm/mman.h>
 #include "kvm-s390.h"
 
+static void unpin_destroy(struct page **pages, int nr)

arch/s390/kvm/pv.c line 28

Quoted 10 lines
arch/s390/kvm/pv.c +10 -0
+{
+	int i;
+	struct page *page;
+	u8 *val;
+
+	for (i = 0; i < nr; i++) {
+		page = pages[i];
+		if (!page)	/* page was never used */
+			continue;
+		val = (void *)page_to_phys(page);

Why dont we call the convert from secure directly here to avoid the fault overhead?

arch/s390/kvm/pv.c line 30

Quoted 2 lines
arch/s390/kvm/pv.c +2 -0
+		READ_ONCE(*val);
+		put_page(page);

as we also do the export here (via implicit reading that page) this can take a while. I think we must add a cond_resched here.

Quoted 3 lines
arch/s390/kvm/pv.c +3 -0
+	}
+}
+

[...]

Quoted 27 lines
arch/s390/kvm/pv.c +10 -5
-
 	mutex_unlock(&kvm->slots_lock);
+
+	kvm->arch.gmap->pinned_pages = vzalloc(npages * sizeof(struct page *));
+	if (!kvm->arch.gmap->pinned_pages)
+		goto out_err;
 	kvm->arch.pv.guest_len = npages * PAGE_SIZE;
 
 	/* Allocate variable storage */
 	vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE);
 	vlen += uv_info.guest_virt_base_stor_len;
 	kvm->arch.pv.stor_var = vzalloc(vlen);
-	if (!kvm->arch.pv.stor_var) {
-		kvm_s390_pv_dealloc_vm(kvm);
-		return -ENOMEM;
-	}
+	if (!kvm->arch.pv.stor_var)
+		goto out_err;
 	return 0;
+
+out_err:
+	kvm_s390_pv_dealloc_vm(kvm);
+	return -ENOMEM;
 }
 
 int kvm_s390_pv_destroy_vm(struct kvm *kvm)

arch/s390/kvm/pv.c int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size, · line 251

Quoted 6 lines
arch/s390/kvm/pv.c +3 -0
@@ -216,6 +246,11 @@ int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size,
 	for (i = 0; i < size / PAGE_SIZE; i++) {
 		uvcb.gaddr = addr + i * PAGE_SIZE;
 		uvcb.tweak[1] = i * PAGE_SIZE;
+		down_read(&kvm->mm->mmap_sem);
+		rc = kvm_s390_pv_pin_page(kvm->arch.gmap, uvcb.gaddr);
+		up_read(&kvm->mm->mmap_sem);

Here we should also have a cond_resched();

Quoted 6 lines
arch/s390/kvm/pv.c +2 -0
+		if (rc && (rc != -EEXIST))
+			break;
 retry:
 		rc = uv_call(0, (u64)&uvcb);
 		if (!rc)
#95 lore raw

Re: [RFC 09/37] KVM: s390: protvirt: Implement on-demand pinning

In reply to Janosch Frank
Quoted 39 lines · Janosch Frank

From: Claudio Imbrenda <[email protected]>

Pin the guest pages when they are first accessed, instead of all at the same time when starting the guest.

---
  arch/s390/include/asm/gmap.h |  1 +
  arch/s390/include/asm/uv.h   |  6 +++++
  arch/s390/kernel/uv.c        | 20 ++++++++++++++
  arch/s390/kvm/kvm-s390.c     |  2 ++
  arch/s390/kvm/pv.c           | 51 ++++++++++++++++++++++++++++++------
  5 files changed, 72 insertions(+), 8 deletions(-)
arch/s390/include/asm/gmap.h +1 -0
index 99b3eedda26e..483f64427c0e 100644
@@ -63,6 +63,7 @@ struct gmap {
  	struct gmap *parent;
  	unsigned long orig_asce;
  	unsigned long se_handle;
+	struct page **pinned_pages;
  	int edat_level;
  	bool removed;
  	bool initialized;
arch/s390/include/asm/uv.h +1 -0
index 99cdd2034503..9ce9363aee1c 100644
@@ -298,6 +298,7 @@ static inline int uv_convert_from_secure(unsigned long paddr)
  	return -EINVAL;
  }
 
+int kvm_s390_pv_pin_page(struct gmap *gmap, unsigned long gpa);
  /*
   * Requests the Ultravisor to make a page accessible to a guest
   * (import). If it's brought in the first time, it will be cleared. If

arch/s390/include/asm/uv.h static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr) · line 326

Quoted 9 lines
arch/s390/include/asm/uv.h +5 -0
@@ -317,6 +318,11 @@ static inline int uv_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
  		.gaddr = gaddr
  	};
 
+	down_read(&gmap->mm->mmap_sem);
+	cc = kvm_s390_pv_pin_page(gmap, gaddr);
+	up_read(&gmap->mm->mmap_sem);
+	if (cc)
+		return cc;
  	cc = uv_call(0, (u64)&uvcb);

So, a theoretical question: Is any in-flight I/O from paging stopped when we try to pin the pages? I am no export on paging, but the comment from Christian ("you can actually fault in a page that is currently under paging I/O.") made me wonder.

Let's assume you could have parallel I/O on that page. You would do a uv_call() and convert the page to secure/unencrypted. The I/O can easily stumble over that (now inaccessible) page and report an error.

Or is any such race not possible because we are using get_user_pages_fast() vs. get_user_pages()? (then, I'd love to see a comment regarding that in the patch description)

Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#96 lore raw

RFC 10/37 s390: add (non)secure page access exceptions handlers

In reply to Janosch Frank

From: Vasily Gorbik <[email protected]>

Add exceptions handlers performing transparent transition of non-secure pages to secure (import) upon guest access and secure pages to non-secure (export) upon hypervisor access.

Current assumption is that guest pages are pinned.

---
 arch/s390/kernel/pgm_check.S |  4 +--
 arch/s390/mm/fault.c         | 64 ++++++++++++++++++++++++++++++++++++
 2 files changed, 66 insertions(+), 2 deletions(-)
arch/s390/kernel/pgm_check.S +2 -2
index 59dee9d3bebf..27ac4f324c70 100644
@@ -78,8 +78,8 @@ PGM_CHECK(do_dat_exception)		/* 39 */
 PGM_CHECK(do_dat_exception)		/* 3a */
 PGM_CHECK(do_dat_exception)		/* 3b */
 PGM_CHECK_DEFAULT			/* 3c */
-PGM_CHECK_DEFAULT			/* 3d */
-PGM_CHECK_DEFAULT			/* 3e */
+PGM_CHECK(do_secure_storage_access)	/* 3d */
+PGM_CHECK(do_non_secure_storage_access)	/* 3e */
 PGM_CHECK_DEFAULT			/* 3f */
 PGM_CHECK_DEFAULT			/* 40 */
 PGM_CHECK_DEFAULT			/* 41 */
arch/s390/mm/fault.c +64 -0

73 lines not on this page · raw message

Signature · 1 line
2.20.1
author maintainer
#97 lore raw

RFC 11/37 DOCUMENTATION: protvirt: Interrupt injection

In reply to Janosch Frank

Interrupt injection has changed a lot for protected guests, as KVM can't access the cpus' lowcores. New fields in the state description, like the interrupt injection control, and masked values safeguard the guest from KVM.

Let's add some documentation to the interrupt injection basics for protected guests.

---
 Documentation/virtual/kvm/s390-pv.txt | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
Documentation/virtual/kvm/s390-pv.txt +27 -0
index 86ed95f36759..e09f2dc5f164 100644
@@ -21,3 +21,30 @@ normally needed to be able to run a VM, some changes have been made in
 SIE behavior and fields have different meaning for a PVM. SIE exits
 are minimized as much as possible to improve speed and reduce exposed
 guest state.
+
+
+Interrupt injection:
+
+Interrupt injection is safeguarded by the Ultravisor and, as KVM lost
+access to the VCPUs' lowcores, is handled via the format 4 state
+description.
+
+Machine check, external, IO and restart interruptions each can be
+injected on SIE entry via a bit in the interrupt injection control
+field (offset 0x54). If the guest cpu is not enabled for the interrupt
+at the time of injection, a validity interception is recognized. The
+interrupt's data is transported via parts of the interception data
+block.
+
+Program and Service Call exceptions have another layer of
+safeguarding, they are only injectable, when instructions have
+intercepted into KVM and such an exception can be an emulation result.
+
+
+Mask notification interceptions:
+As a replacement for the lctl(g) and lpsw(e) interception, two new
+interception codes have been introduced. One which tells us that CRs
+0, 6 or 14 have been changed and therefore interrupt masking might
+have changed. And one for PSW bit 13 changes. The CRs and the PSW in
+the state description only contain the mask bits and no further info
+like the current instruction address.
Signature · 1 line
2.20.1
#98 lore raw

Re: [RFC 11/37] DOCUMENTATION: protvirt: Interrupt injection

Quoted 25 lines · On Thu, 24 Oct 2019 07:40:33 -0400 Janosch Frank <[email protected]> wrote:

Interrupt injection has changed a lot for protected guests, as KVM can't access the cpus' lowcores. New fields in the state description, like the interrupt injection control, and masked values safeguard the guest from KVM.

Let's add some documentation to the interrupt injection basics for protected guests.

---
 Documentation/virtual/kvm/s390-pv.txt | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
Documentation/virtual/kvm/s390-pv.txt +4 -0
index 86ed95f36759..e09f2dc5f164 100644
@@ -21,3 +21,30 @@ normally needed to be able to run a VM, some changes have been made in
 SIE behavior and fields have different meaning for a PVM. SIE exits
 are minimized as much as possible to improve speed and reduce exposed
 guest state.
+
+
+Interrupt injection:
+

Documentation/virtual/kvm/s390-pv.txt normally needed to be able to run a VM, some changes have been made in · line 37

Quoted 10 lines
Documentation/virtual/kvm/s390-pv.txt +10 -0
+Interrupt injection is safeguarded by the Ultravisor and, as KVM lost
+access to the VCPUs' lowcores, is handled via the format 4 state
+description.
+
+Machine check, external, IO and restart interruptions each can be
+injected on SIE entry via a bit in the interrupt injection control
+field (offset 0x54). If the guest cpu is not enabled for the interrupt
+at the time of injection, a validity interception is recognized. The
+interrupt's data is transported via parts of the interception data
+block.

"Data associated with the interrupt needs to be placed into the respective fields in the interception data block to be injected into the guest."

?

Documentation/virtual/kvm/s390-pv.txt normally needed to be able to run a VM, some changes have been made in · line 41

Quoted 4 lines
Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Program and Service Call exceptions have another layer of
+safeguarding, they are only injectable, when instructions have
+intercepted into KVM and such an exception can be an emulation result.

I find this sentence hard to parse... not sure if I understand it correctly.

"They can only be injected if the exception can be encountered during emulation of instructions that had been intercepted into KVM."

?

Documentation/virtual/kvm/s390-pv.txt normally needed to be able to run a VM, some changes have been made in · line 48

Quoted 7 lines
Documentation/virtual/kvm/s390-pv.txt +7 -0
+
+
+Mask notification interceptions:
+As a replacement for the lctl(g) and lpsw(e) interception, two new
+interception codes have been introduced. One which tells us that CRs
+0, 6 or 14 have been changed and therefore interrupt masking might
+have changed. And one for PSW bit 13 changes. The CRs and the PSW in

Might be helpful to mention that this bit covers machine checks, which do not get a separate bit in the control block :)

Documentation/virtual/kvm/s390-pv.txt normally needed to be able to run a VM, some changes have been made in · line 50

Quoted 2 lines
Documentation/virtual/kvm/s390-pv.txt +2 -0
+the state description only contain the mask bits and no further info
+like the current instruction address.

"The CRs and the PSW in the state description only contain the bits referring to interrupt masking; other fields like e.g. the current instruction address are zero."

?

maintainer
#99 lore raw

Re: [RFC 11/37] DOCUMENTATION: protvirt: Interrupt injection

Quoted 43 lines · On Thu, 14 Nov 2019 14:09:46 +0100 Cornelia Huck <[email protected]> wrote:
Quoted 32 lines · On Thu, 24 Oct 2019 07:40:33 -0400 Janosch Frank <[email protected]> wrote:

Interrupt injection has changed a lot for protected guests, as KVM can't access the cpus' lowcores. New fields in the state description, like the interrupt injection control, and masked values safeguard the guest from KVM.

Let's add some documentation to the interrupt injection basics for protected guests.

---
 Documentation/virtual/kvm/s390-pv.txt | 27

+++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)

(quoted diff) +0 -0

b/Documentation/virtual/kvm/s390-pv.txt index 86ed95f36759..e09f2dc5f164 100644 --- a/Documentation/virtual/kvm/s390-pv.txt +++ b/Documentation/virtual/kvm/s390-pv.txt @@ -21,3 +21,30 @@ normally needed to be able to run a VM, some changes have been made in SIE behavior and fields have different meaning for a PVM. SIE exits are minimized as much as possible to improve speed and reduce exposed guest state. +

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Interrupt injection:
+
+Interrupt injection is safeguarded by the Ultravisor and, as KVM

lost +access to the VCPUs' lowcores, is handled via the format 4 state +description.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Machine check, external, IO and restart interruptions each can be
+injected on SIE entry via a bit in the interrupt injection control
+field (offset 0x54). If the guest cpu is not enabled for the
Quoted 3 lines

interrupt +at the time of injection, a validity interception is recognized. The +interrupt's data is transported via parts of the interception data +block.

"Data associated with the interrupt needs to be placed into the respective fields in the interception data block to be injected into the guest."

Quoted 1 line

?

when a normal guest intercepts an exception, depending on the exception type, the parameters are saved in the state description at specified offsets, between 0xC0 amd 0xF8

to perform interrupt injection for secure guests, the same fields are used to specify the interrupt parameters that should be injected into the guest

Quoted 11 lines
Quoted 5 lines
Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Program and Service Call exceptions have another layer of
+safeguarding, they are only injectable, when instructions have
+intercepted into KVM and such an exception can be an emulation

result.

I find this sentence hard to parse... not sure if I understand it correctly.

"They can only be injected if the exception can be encountered during emulation of instructions that had been intercepted into KVM."

yes

Quoted 13 lines
Quoted 9 lines
Documentation/virtual/kvm/s390-pv.txt +5 -0
+
+
+Mask notification interceptions:
+As a replacement for the lctl(g) and lpsw(e) interception, two new
+interception codes have been introduced. One which tells us that

CRs +0, 6 or 14 have been changed and therefore interrupt masking might +have changed. And one for PSW bit 13 changes. The CRs and the PSW in

Might be helpful to mention that this bit covers machine checks, which do not get a separate bit in the control block :)

Quoted 6 lines
Quoted 2 lines

+the state description only contain the mask bits and no further info +like the current instruction address.

"The CRs and the PSW in the state description only contain the bits referring to interrupt masking; other fields like e.g. the current instruction address are zero."

wait state is saved too

CC is write only, and is only inspected by hardware/firmware when KVM/qemu is interpreting an instruction that expects a new CC to be set, and then only the expected CCs are allowed (e.g. if an instruction only allows CC 0 or 3, 2 cannot be specified)

#100 lore raw

Re: [RFC 11/37] DOCUMENTATION: protvirt: Interrupt injection

Quoted 48 lines · On Thu, 14 Nov 2019 14:25:00 +0100 Claudio Imbrenda <[email protected]>…
Quoted 43 lines · On Thu, 14 Nov 2019 14:09:46 +0100 Cornelia Huck <[email protected]> wrote:
Quoted 32 lines · On Thu, 24 Oct 2019 07:40:33 -0400 Janosch Frank <[email protected]> wrote:

Interrupt injection has changed a lot for protected guests, as KVM can't access the cpus' lowcores. New fields in the state description, like the interrupt injection control, and masked values safeguard the guest from KVM.

Let's add some documentation to the interrupt injection basics for protected guests.

---
 Documentation/virtual/kvm/s390-pv.txt | 27

+++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)

(quoted diff) +0 -0

b/Documentation/virtual/kvm/s390-pv.txt index 86ed95f36759..e09f2dc5f164 100644 --- a/Documentation/virtual/kvm/s390-pv.txt +++ b/Documentation/virtual/kvm/s390-pv.txt @@ -21,3 +21,30 @@ normally needed to be able to run a VM, some changes have been made in SIE behavior and fields have different meaning for a PVM. SIE exits are minimized as much as possible to improve speed and reduce exposed guest state. +

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Interrupt injection:
+
+Interrupt injection is safeguarded by the Ultravisor and, as KVM

lost +access to the VCPUs' lowcores, is handled via the format 4 state +description.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Machine check, external, IO and restart interruptions each can be
+injected on SIE entry via a bit in the interrupt injection control
+field (offset 0x54). If the guest cpu is not enabled for the
Quoted 3 lines

interrupt +at the time of injection, a validity interception is recognized. The +interrupt's data is transported via parts of the interception data +block.

"Data associated with the interrupt needs to be placed into the respective fields in the interception data block to be injected into the guest."

Quoted 1 line

?

Quoted 7 lines

when a normal guest intercepts an exception, depending on the exception type, the parameters are saved in the state description at specified offsets, between 0xC0 amd 0xF8

to perform interrupt injection for secure guests, the same fields are used to specify the interrupt parameters that should be injected into the guest

Ok, maybe add that as well.

Quoted 35 lines
Quoted 10 lines
Quoted 6 lines
Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Program and Service Call exceptions have another layer of
+safeguarding, they are only injectable, when instructions have
+intercepted into KVM and such an exception can be an emulation

result.

I find this sentence hard to parse... not sure if I understand it correctly.

Quoted 2 lines

"They can only be injected if the exception can be encountered during emulation of instructions that had been intercepted into KVM."

yes

Quoted 13 lines
Quoted 9 lines
Documentation/virtual/kvm/s390-pv.txt +5 -0
+
+
+Mask notification interceptions:
+As a replacement for the lctl(g) and lpsw(e) interception, two new
+interception codes have been introduced. One which tells us that

CRs +0, 6 or 14 have been changed and therefore interrupt masking might +have changed. And one for PSW bit 13 changes. The CRs and the PSW in

Might be helpful to mention that this bit covers machine checks, which do not get a separate bit in the control block :)

Quoted 6 lines
Quoted 2 lines

+the state description only contain the mask bits and no further info +like the current instruction address.

"The CRs and the PSW in the state description only contain the bits referring to interrupt masking; other fields like e.g. the current instruction address are zero."

Quoted 6 lines

wait state is saved too

CC is write only, and is only inspected by hardware/firmware when KVM/qemu is interpreting an instruction that expects a new CC to be set, and then only the expected CCs are allowed (e.g. if an instruction only allows CC 0 or 3, 2 cannot be specified)

So I'm wondering how much of that should go into the document... maybe just

"The CRs and the PSW in the state description contain less information than for normal guests: most information that does not refer to interrupt masking is not available to the hypervisor."

?

author maintainer
#101 lore raw

Re: [RFC 11/37] DOCUMENTATION: protvirt: Interrupt injection

Quoted 103 lines · Cornelia Huck
Quoted 48 lines · On Thu, 14 Nov 2019 14:25:00 +0100 Claudio Imbrenda <[email protected]>…
Quoted 43 lines · On Thu, 14 Nov 2019 14:09:46 +0100 Cornelia Huck <[email protected]> wrote:
Quoted 32 lines · On Thu, 24 Oct 2019 07:40:33 -0400 Janosch Frank <[email protected]> wrote:

Interrupt injection has changed a lot for protected guests, as KVM can't access the cpus' lowcores. New fields in the state description, like the interrupt injection control, and masked values safeguard the guest from KVM.

Let's add some documentation to the interrupt injection basics for protected guests.

---
 Documentation/virtual/kvm/s390-pv.txt | 27

+++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)

(quoted diff) +0 -0

b/Documentation/virtual/kvm/s390-pv.txt index 86ed95f36759..e09f2dc5f164 100644 --- a/Documentation/virtual/kvm/s390-pv.txt +++ b/Documentation/virtual/kvm/s390-pv.txt @@ -21,3 +21,30 @@ normally needed to be able to run a VM, some changes have been made in SIE behavior and fields have different meaning for a PVM. SIE exits are minimized as much as possible to improve speed and reduce exposed guest state. +

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Interrupt injection:
+
+Interrupt injection is safeguarded by the Ultravisor and, as KVM

lost +access to the VCPUs' lowcores, is handled via the format 4 state +description.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Machine check, external, IO and restart interruptions each can be
+injected on SIE entry via a bit in the interrupt injection control
+field (offset 0x54). If the guest cpu is not enabled for the
Quoted 3 lines

interrupt +at the time of injection, a validity interception is recognized. The +interrupt's data is transported via parts of the interception data +block.

"Data associated with the interrupt needs to be placed into the respective fields in the interception data block to be injected into the guest."

Quoted 1 line

?

Quoted 7 lines

when a normal guest intercepts an exception, depending on the exception type, the parameters are saved in the state description at specified offsets, between 0xC0 amd 0xF8

to perform interrupt injection for secure guests, the same fields are used to specify the interrupt parameters that should be injected into the guest

Ok, maybe add that as well.

Quoted 35 lines
Quoted 10 lines
Quoted 6 lines
Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+Program and Service Call exceptions have another layer of
+safeguarding, they are only injectable, when instructions have
+intercepted into KVM and such an exception can be an emulation

result.

I find this sentence hard to parse... not sure if I understand it correctly.

Quoted 2 lines

"They can only be injected if the exception can be encountered during emulation of instructions that had been intercepted into KVM."

yes

Quoted 13 lines
Quoted 9 lines
Documentation/virtual/kvm/s390-pv.txt +5 -0
+
+
+Mask notification interceptions:
+As a replacement for the lctl(g) and lpsw(e) interception, two new
+interception codes have been introduced. One which tells us that

CRs +0, 6 or 14 have been changed and therefore interrupt masking might +have changed. And one for PSW bit 13 changes. The CRs and the PSW in

Might be helpful to mention that this bit covers machine checks, which do not get a separate bit in the control block :)

Quoted 6 lines
Quoted 2 lines

+the state description only contain the mask bits and no further info +like the current instruction address.

"The CRs and the PSW in the state description only contain the bits referring to interrupt masking; other fields like e.g. the current instruction address are zero."

Quoted 6 lines

wait state is saved too

CC is write only, and is only inspected by hardware/firmware when KVM/qemu is interpreting an instruction that expects a new CC to be set, and then only the expected CCs are allowed (e.g. if an instruction only allows CC 0 or 3, 2 cannot be specified)

Quoted 9 lines

So I'm wondering how much of that should go into the document... maybe just

"The CRs and the PSW in the state description contain less information than for normal guests: most information that does not refer to interrupt masking is not available to the hypervisor."

?

I'm not liking that too much and I'm also asking myself it makes sense to fix documentation via mails. How about an etherpad?

author maintainer
#102 lore raw

RFC 12/37 KVM: s390: protvirt: Handle SE notification interceptions

In reply to Janosch Frank

Since KVM doesn't emulate any form of load control and load psw instructions anymore, we wouldn't get an interception if PSWs or CRs are changed in the guest. That means we can't inject IRQs right after the guest is enabled for them.

The new interception codes solve that problem by being a notification for changes to IRQ enablement relevant bits in CRs 0, 6 and 14, as well a the machine check mask bit in the PSW.

No special handling is needed for these interception codes, the KVM pre-run code will consult all necessary CRs and PSW bits and inject IRQs the guest is enabled for.

---
 arch/s390/include/asm/kvm_host.h |  2 ++
 arch/s390/kvm/intercept.c        | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)
arch/s390/include/asm/kvm_host.h +2 -0
index d4fd0f3af676..6cc3b73ca904 100644
@@ -210,6 +210,8 @@ struct kvm_s390_sie_block {
 #define ICPT_PARTEXEC	0x38
 #define ICPT_IOINST	0x40
 #define ICPT_KSS	0x5c
+#define ICPT_PV_MCHKR	0x60
+#define ICPT_PV_INT_EN	0x64
 	__u8	icptcode;		/* 0x0050 */
 	__u8	icptstatus;		/* 0x0051 */
 	__u16	ihcpu;			/* 0x0052 */
arch/s390/kvm/intercept.c +18 -0
index a389fa85cca2..acc1710fc472 100644
@@ -480,6 +480,24 @@ int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
 	case ICPT_KSS:
 		rc = kvm_s390_skey_check_enable(vcpu);
 		break;
+	case ICPT_PV_MCHKR:
+		/*
+		 * A protected guest changed PSW bit 13 to one and is now
+		 * enabled for interrupts. The pre-run code will check
+		 * the registers and inject pending MCHKs based on the
+		 * PSW and CRs. No additional work to do.
+		 */
+		rc = 0;
+		break;
+	case  ICPT_PV_INT_EN:
+		/*
+		 * A protected guest changed CR 0,6,14 and may now be
+		 * enabled for interrupts. The pre-run code will check
+		 * the registers and inject pending IRQs based on the
+		 * CRs. No additional work to do.
+		 */
+		rc = 0;
+	break;
 	default:
 		return -EOPNOTSUPP;
 	}
Signature · 1 line
2.20.1
#103 lore raw

Re: [RFC 12/37] KVM: s390: protvirt: Handle SE notification interceptions

Quoted 48 lines · Janosch Frank

Since KVM doesn't emulate any form of load control and load psw instructions anymore, we wouldn't get an interception if PSWs or CRs are changed in the guest. That means we can't inject IRQs right after the guest is enabled for them.

The new interception codes solve that problem by being a notification for changes to IRQ enablement relevant bits in CRs 0, 6 and 14, as well a the machine check mask bit in the PSW.

No special handling is needed for these interception codes, the KVM pre-run code will consult all necessary CRs and PSW bits and inject IRQs the guest is enabled for.

---
  arch/s390/include/asm/kvm_host.h |  2 ++
  arch/s390/kvm/intercept.c        | 18 ++++++++++++++++++
  2 files changed, 20 insertions(+)
arch/s390/include/asm/kvm_host.h +2 -0
index d4fd0f3af676..6cc3b73ca904 100644
@@ -210,6 +210,8 @@ struct kvm_s390_sie_block {
  #define ICPT_PARTEXEC	0x38
  #define ICPT_IOINST	0x40
  #define ICPT_KSS	0x5c
+#define ICPT_PV_MCHKR	0x60
+#define ICPT_PV_INT_EN	0x64
  	__u8	icptcode;		/* 0x0050 */
  	__u8	icptstatus;		/* 0x0051 */
  	__u16	ihcpu;			/* 0x0052 */
arch/s390/kvm/intercept.c +8 -0
index a389fa85cca2..acc1710fc472 100644
@@ -480,6 +480,24 @@ int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
  	case ICPT_KSS:
  		rc = kvm_s390_skey_check_enable(vcpu);
  		break;
+	case ICPT_PV_MCHKR:
+		/*
+		 * A protected guest changed PSW bit 13 to one and is now
+		 * enabled for interrupts. The pre-run code will check
+		 * the registers and inject pending MCHKs based on the
+		 * PSW and CRs. No additional work to do.
+		 */
+		rc = 0;

arch/s390/kvm/intercept.c int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu) · line 500

Quoted 10 lines
arch/s390/kvm/intercept.c +10 -0
+		break;
+	case  ICPT_PV_INT_EN:
+		/*
+		 * A protected guest changed CR 0,6,14 and may now be
+		 * enabled for interrupts. The pre-run code will check
+		 * the registers and inject pending IRQs based on the
+		 * CRs. No additional work to do.
+		 */
+		rc = 0;
+	break;

Wrong indentation.

Maybe simply

case ICPT_PV_MCHKR:
ICPT_PV_INT_EN:

	/*
	 * PSW bit 13 or a CR (0, 6, 14) changed and we might now be
         * able to deliver interrupts. pre-run code will take care of
         * this.
	 */
	rc = 0;
	break;
Signature · 3 lines
Thanks,

David / dhildenb
author maintainer
#104 lore raw

Re: [RFC 12/37] KVM: s390: protvirt: Handle SE notification interceptions

Quoted 66 lines · David Hildenbrand
Quoted 48 lines · Janosch Frank

Since KVM doesn't emulate any form of load control and load psw instructions anymore, we wouldn't get an interception if PSWs or CRs are changed in the guest. That means we can't inject IRQs right after the guest is enabled for them.

The new interception codes solve that problem by being a notification for changes to IRQ enablement relevant bits in CRs 0, 6 and 14, as well a the machine check mask bit in the PSW.

No special handling is needed for these interception codes, the KVM pre-run code will consult all necessary CRs and PSW bits and inject IRQs the guest is enabled for.

---
  arch/s390/include/asm/kvm_host.h |  2 ++
  arch/s390/kvm/intercept.c        | 18 ++++++++++++++++++
  2 files changed, 20 insertions(+)
arch/s390/include/asm/kvm_host.h +2 -0
index d4fd0f3af676..6cc3b73ca904 100644
@@ -210,6 +210,8 @@ struct kvm_s390_sie_block {
  #define ICPT_PARTEXEC	0x38
  #define ICPT_IOINST	0x40
  #define ICPT_KSS	0x5c
+#define ICPT_PV_MCHKR	0x60
+#define ICPT_PV_INT_EN	0x64
  	__u8	icptcode;		/* 0x0050 */
  	__u8	icptstatus;		/* 0x0051 */
  	__u16	ihcpu;			/* 0x0052 */
arch/s390/kvm/intercept.c +8 -0
index a389fa85cca2..acc1710fc472 100644
@@ -480,6 +480,24 @@ int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
  	case ICPT_KSS:
  		rc = kvm_s390_skey_check_enable(vcpu);
  		break;
+	case ICPT_PV_MCHKR:
+		/*
+		 * A protected guest changed PSW bit 13 to one and is now
+		 * enabled for interrupts. The pre-run code will check
+		 * the registers and inject pending MCHKs based on the
+		 * PSW and CRs. No additional work to do.
+		 */
+		rc = 0;

arch/s390/kvm/intercept.c int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu) · line 500

Quoted 10 lines
arch/s390/kvm/intercept.c +10 -0
+		break;
+	case  ICPT_PV_INT_EN:
+		/*
+		 * A protected guest changed CR 0,6,14 and may now be
+		 * enabled for interrupts. The pre-run code will check
+		 * the registers and inject pending IRQs based on the
+		 * CRs. No additional work to do.
+		 */
+		rc = 0;
+	break;

Wrong indentation.

Maybe simply

case ICPT_PV_MCHKR:
ICPT_PV_INT_EN:

Quoted 7 lines
	/*
	 * PSW bit 13 or a CR (0, 6, 14) changed and we might now be
          * able to deliver interrupts. pre-run code will take care of
          * this.
	 */
	rc = 0;
	break;

Sounds good, I'll fix it

#105 lore raw

Re: [RFC 12/37] KVM: s390: protvirt: Handle SE notification interceptions

In reply to Janosch Frank
Quoted 5 lines · On Thu, 24 Oct 2019 07:40:34 -0400 Janosch Frank <[email protected]> wrote:

Since KVM doesn't emulate any form of load control and load psw instructions anymore, we wouldn't get an interception if PSWs or CRs are changed in the guest. That means we can't inject IRQs right after the guest is enabled for them.

Quoted 7 lines

The new interception codes solve that problem by being a notification for changes to IRQ enablement relevant bits in CRs 0, 6 and 14, as well a the machine check mask bit in the PSW.

No special handling is needed for these interception codes, the KVM pre-run code will consult all necessary CRs and PSW bits and inject IRQs the guest is enabled for.

Just to clarify: The hypervisor can still access the relevant bits for pv guests, this is only about the notification, right?

Quoted 6 lines
---
 arch/s390/include/asm/kvm_host.h |  2 ++
 arch/s390/kvm/intercept.c        | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)
#106 lore raw

Re: [RFC 12/37] KVM: s390: protvirt: Handle SE notification interceptions

Quoted 12 lines · Cornelia Huck
Quoted 9 lines · On Thu, 24 Oct 2019 07:40:34 -0400 Janosch Frank <[email protected]> wrote:

Since KVM doesn't emulate any form of load control and load psw instructions anymore, we wouldn't get an interception if PSWs or CRs are changed in the guest. That means we can't inject IRQs right after the guest is enabled for them.

The new interception codes solve that problem by being a notification for changes to IRQ enablement relevant bits in CRs 0, 6 and 14, as well a the machine check mask bit in the PSW.

Quoted 7 lines
Quoted 3 lines

No special handling is needed for these interception codes, the KVM pre-run code will consult all necessary CRs and PSW bits and inject IRQs the guest is enabled for.

Just to clarify: The hypervisor can still access the relevant bits for pv guests, this is only about the notification, right?

Yes, the hypervisor (KVM) can always read the relevant PSW bits (I,E,M) and CR bits to decide if an interrupt can be delivered. All other bits of PSW and CRx are masked though.
This is a new intercept for notification as we do no longer get an IC4 (instruction to handle) for load control and friends so that we can re-check the bits.

Quoted 7 lines
Quoted 6 lines
---
 arch/s390/include/asm/kvm_host.h |  2 ++
 arch/s390/kvm/intercept.c        | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)
#107 lore raw

Re: [RFC 12/37] KVM: s390: protvirt: Handle SE notification interceptions

Quoted 21 lines · On Tue, 5 Nov 2019 19:15:19 +0100 Christian Borntraeger…
Quoted 12 lines · Cornelia Huck
Quoted 9 lines · On Thu, 24 Oct 2019 07:40:34 -0400 Janosch Frank <[email protected]> wrote:

Since KVM doesn't emulate any form of load control and load psw instructions anymore, we wouldn't get an interception if PSWs or CRs are changed in the guest. That means we can't inject IRQs right after the guest is enabled for them.

The new interception codes solve that problem by being a notification for changes to IRQ enablement relevant bits in CRs 0, 6 and 14, as well a the machine check mask bit in the PSW.

Quoted 7 lines
Quoted 3 lines

No special handling is needed for these interception codes, the KVM pre-run code will consult all necessary CRs and PSW bits and inject IRQs the guest is enabled for.

Just to clarify: The hypervisor can still access the relevant bits for pv guests, this is only about the notification, right?

Quoted 5 lines

Yes, the hypervisor (KVM) can always read the relevant PSW bits (I,E,M) and CR bits to decide if an interrupt can be delivered. All other bits of PSW and CRx are masked though.
This is a new intercept for notification as we do no longer get an IC4 (instruction to handle) for load control and friends so that we can re-check the bits.

Ok, thanks!

Quoted 8 lines
Quoted 7 lines
Quoted 6 lines
---
 arch/s390/include/asm/kvm_host.h |  2 ++
 arch/s390/kvm/intercept.c        | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)  
author maintainer
#108 lore raw

RFC 13/37 KVM: s390: protvirt: Add interruption injection controls

In reply to Janosch Frank

From: Michael Mueller <[email protected]>

Define the interruption injection codes and the related fields in the sie control block for PVM interruption injection.

---
 arch/s390/include/asm/kvm_host.h | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)
arch/s390/include/asm/kvm_host.h +21 -4
index 6cc3b73ca904..82443236d4cc 100644
@@ -215,7 +215,15 @@ struct kvm_s390_sie_block {
 	__u8	icptcode;		/* 0x0050 */
 	__u8	icptstatus;		/* 0x0051 */
 	__u16	ihcpu;			/* 0x0052 */
-	__u8	reserved54[2];		/* 0x0054 */
+	__u8	reserved54;		/* 0x0054 */
+#define IICTL_CODE_NONE		 0x00
+#define IICTL_CODE_MCHK		 0x01
+#define IICTL_CODE_EXT		 0x02
+#define IICTL_CODE_IO		 0x03
+#define IICTL_CODE_RESTART	 0x04
+#define IICTL_CODE_SPECIFICATION 0x10
+#define IICTL_CODE_OPERAND	 0x11
+	__u8	iictl;			/* 0x0055 */
 	__u16	ipa;			/* 0x0056 */
 	__u32	ipb;			/* 0x0058 */
 	__u32	scaoh;			/* 0x005c */
@@ -252,7 +260,8 @@ struct kvm_s390_sie_block {
 #define HPID_KVM	0x4
 #define HPID_VSIE	0x5
 	__u8	hpid;			/* 0x00b8 */
-	__u8	reservedb9[11];		/* 0x00b9 */
+	__u8	reservedb9[7];		/* 0x00b9 */
+	__u32	eiparams;		/* 0x00c0 */
 	__u16	extcpuaddr;		/* 0x00c4 */
 	__u16	eic;			/* 0x00c6 */
 	__u32	reservedc8;		/* 0x00c8 */
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {
 	__u8	oai;			/* 0x00e2 */
 	__u8	armid;			/* 0x00e3 */
 	__u8	reservede4[4];		/* 0x00e4 */
-	__u64	tecmc;			/* 0x00e8 */
-	__u8	reservedf0[12];		/* 0x00f0 */
+	union {
+		__u64	tecmc;		/* 0x00e8 */
+		struct {
+			__u16	subchannel_id;	/* 0x00e8 */
+			__u16	subchannel_nr;	/* 0x00ea */
+			__u32	io_int_parm;	/* 0x00ec */
+			__u32	io_int_word;	/* 0x00f0 */
+		};
+	} __packed;
+	__u8	reservedf4[8];		/* 0x00f4 */
 #define CRYCB_FORMAT_MASK 0x00000003
 #define CRYCB_FORMAT0 0x00000000
 #define CRYCB_FORMAT1 0x00000001
Signature · 1 line
2.20.1
#109 lore raw

Re: [RFC 13/37] KVM: s390: protvirt: Add interruption injection controls

Quoted 45 lines · Janosch Frank

From: Michael Mueller <[email protected]>

Define the interruption injection codes and the related fields in the sie control block for PVM interruption injection.

---
  arch/s390/include/asm/kvm_host.h | 25 +++++++++++++++++++++----
  1 file changed, 21 insertions(+), 4 deletions(-)
arch/s390/include/asm/kvm_host.h +11 -2
index 6cc3b73ca904..82443236d4cc 100644
@@ -215,7 +215,15 @@ struct kvm_s390_sie_block {
  	__u8	icptcode;		/* 0x0050 */
  	__u8	icptstatus;		/* 0x0051 */
  	__u16	ihcpu;			/* 0x0052 */
-	__u8	reserved54[2];		/* 0x0054 */
+	__u8	reserved54;		/* 0x0054 */
+#define IICTL_CODE_NONE		 0x00
+#define IICTL_CODE_MCHK		 0x01
+#define IICTL_CODE_EXT		 0x02
+#define IICTL_CODE_IO		 0x03
+#define IICTL_CODE_RESTART	 0x04
+#define IICTL_CODE_SPECIFICATION 0x10
+#define IICTL_CODE_OPERAND	 0x11
+	__u8	iictl;			/* 0x0055 */
  	__u16	ipa;			/* 0x0056 */
  	__u32	ipb;			/* 0x0058 */
  	__u32	scaoh;			/* 0x005c */
@@ -252,7 +260,8 @@ struct kvm_s390_sie_block {
  #define HPID_KVM	0x4
  #define HPID_VSIE	0x5
  	__u8	hpid;			/* 0x00b8 */
-	__u8	reservedb9[11];		/* 0x00b9 */
+	__u8	reservedb9[7];		/* 0x00b9 */
+	__u32	eiparams;		/* 0x00c0 */
  	__u16	extcpuaddr;		/* 0x00c4 */
  	__u16	eic;			/* 0x00c6 */
  	__u32	reservedc8;		/* 0x00c8 */
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {
  	__u8	oai;			/* 0x00e2 */
  	__u8	armid;			/* 0x00e3 */
  	__u8	reservede4[4];		/* 0x00e4 */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 287

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +8 -2
-	__u64	tecmc;			/* 0x00e8 */
-	__u8	reservedf0[12];		/* 0x00f0 */
+	union {
+		__u64	tecmc;		/* 0x00e8 */
+		struct {
+			__u16	subchannel_id;	/* 0x00e8 */
+			__u16	subchannel_nr;	/* 0x00ea */
+			__u32	io_int_parm;	/* 0x00ec */
+			__u32	io_int_word;	/* 0x00f0 */
+		};

I only wonder if we should give this member a fitting name, e.g., "ioparams"

Quoted 6 lines
arch/s390/include/asm/kvm_host.h +2 -0
+	} __packed;
+	__u8	reservedf4[8];		/* 0x00f4 */
  #define CRYCB_FORMAT_MASK 0x00000003
  #define CRYCB_FORMAT0 0x00000000
  #define CRYCB_FORMAT1 0x00000001
Signature · 3 lines
Thanks,

David / dhildenb
#110 lore raw

Re: [RFC 13/37] KVM: s390: protvirt: Add interruption injection controls

Quoted 8 lines · David Hildenbrand
Quoted 8 lines
(quoted diff) +0 -0
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {

      __u8    oai;            /* 0x00e2 */
      __u8    armid;            /* 0x00e3 */
      __u8    reservede4[4];        /* 0x00e4 */
-    __u64    tecmc;            /* 0x00e8 */
-    __u8    reservedf0[12];        /* 0x00f0 */
+    union {
+        __u64    tecmc;        /* 0x00e8 */

Quoted 8 lines
Quoted 6 lines

+        struct {
+            __u16    subchannel_id;    /* 0x00e8 */ +            __u16    subchannel_nr;    /* 0x00ea */ +            __u32    io_int_parm;    /* 0x00ec */ +            __u32    io_int_word;    /* 0x00f0 */ +        };

I only wonder if we should give this member a fitting name, e.g., "ioparams"

Do you see a real gain for that? We have a lot of other unnamed structs defined here as well.

Quoted 7 lines
Quoted 7 lines

+    } __packed;
+    __u8    reservedf4[8];        /* 0x00f4 */
  #define CRYCB_FORMAT_MASK 0x00000003
  #define CRYCB_FORMAT0 0x00000000
  #define CRYCB_FORMAT1 0x00000001

Thanks,
Michael

#111 lore raw

Re: [RFC 13/37] KVM: s390: protvirt: Add interruption injection controls

Quoted 21 lines · Michael Mueller
Quoted 9 lines · David Hildenbrand
Quoted 9 lines
(quoted diff) +0 -0
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {

      __u8    oai;            /* 0x00e2 */
      __u8    armid;            /* 0x00e3 */
      __u8    reservede4[4];        /* 0x00e4 */
-    __u64    tecmc;            /* 0x00e8 */
-    __u8    reservedf0[12];        /* 0x00f0 */
+    union {
+        __u64    tecmc;        /* 0x00e8 */
+        struct {

Quoted 8 lines
Quoted 5 lines

+            __u16    subchannel_id;    /* 0x00e8 */ +            __u16    subchannel_nr;    /* 0x00ea */ +            __u32    io_int_parm;    /* 0x00ec */ +            __u32    io_int_word;    /* 0x00f0 */ +        };

I only wonder if we should give this member a fitting name, e.g., "ioparams"

Quoted 2 lines

Do you see a real gain for that? We have a lot of other unnamed structs defined here as well.

I was wondering if we could just copy the whole struct when delivering the interrupt.

You could even reuse "struct kvm_s390_io_info" here to make that more clear.

Signature · 3 lines
Thanks,

David / dhildenb
#112 lore raw

Re: [RFC 13/37] KVM: s390: protvirt: Add interruption injection controls

Quoted 22 lines · David Hildenbrand
Quoted 21 lines · Michael Mueller
Quoted 17 lines · David Hildenbrand
Quoted 4 lines
arch/s390/include/asm/kvm_host.h +0 -0
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {
       __u8    oai;            /* 0x00e2 */
       __u8    armid;            /* 0x00e3 */
       __u8    reservede4[4];        /* 0x00e4 */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 287

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +8 -2
-    __u64    tecmc;            /* 0x00e8 */
-    __u8    reservedf0[12];        /* 0x00f0 */
+    union {
+        __u64    tecmc;        /* 0x00e8 */
+        struct {
+            __u16    subchannel_id;    /* 0x00e8 */
+            __u16    subchannel_nr;    /* 0x00ea */
+            __u32    io_int_parm;    /* 0x00ec */
+            __u32    io_int_word;    /* 0x00f0 */
+        };

I only wonder if we should give this member a fitting name, e.g., "ioparams"

Quoted 8 lines
Quoted 2 lines

Do you see a real gain for that? We have a lot of other unnamed structs defined here as well.

I was wondering if we could just copy the whole struct when delivering the interrupt.

You could even reuse "struct kvm_s390_io_info" here to make that more clear.

I want to keep it the way it is to have the fields in the SCB declaration explicit.

Thanks,
Michael

#113 lore raw

Re: [RFC 13/37] KVM: s390: protvirt: Add interruption injection controls

In reply to Janosch Frank
Quoted 47 lines · On Thu, 24 Oct 2019 07:40:35 -0400 Janosch Frank <[email protected]> wrote:

From: Michael Mueller <[email protected]>

Define the interruption injection codes and the related fields in the sie control block for PVM interruption injection.

---
 arch/s390/include/asm/kvm_host.h | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)
arch/s390/include/asm/kvm_host.h +11 -4
index 6cc3b73ca904..82443236d4cc 100644
@@ -215,7 +215,15 @@ struct kvm_s390_sie_block {
 	__u8	icptcode;		/* 0x0050 */
 	__u8	icptstatus;		/* 0x0051 */
 	__u16	ihcpu;			/* 0x0052 */
-	__u8	reserved54[2];		/* 0x0054 */
+	__u8	reserved54;		/* 0x0054 */
+#define IICTL_CODE_NONE		 0x00
+#define IICTL_CODE_MCHK		 0x01
+#define IICTL_CODE_EXT		 0x02
+#define IICTL_CODE_IO		 0x03
+#define IICTL_CODE_RESTART	 0x04
+#define IICTL_CODE_SPECIFICATION 0x10
+#define IICTL_CODE_OPERAND	 0x11
+	__u8	iictl;			/* 0x0055 */
 	__u16	ipa;			/* 0x0056 */
 	__u32	ipb;			/* 0x0058 */
 	__u32	scaoh;			/* 0x005c */
@@ -252,7 +260,8 @@ struct kvm_s390_sie_block {
 #define HPID_KVM	0x4
 #define HPID_VSIE	0x5
 	__u8	hpid;			/* 0x00b8 */
-	__u8	reservedb9[11];		/* 0x00b9 */
+	__u8	reservedb9[7];		/* 0x00b9 */
+	__u32	eiparams;		/* 0x00c0 */
 	__u16	extcpuaddr;		/* 0x00c4 */
 	__u16	eic;			/* 0x00c6 */
 	__u32	reservedc8;		/* 0x00c8 */
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {
 	__u8	oai;			/* 0x00e2 */
 	__u8	armid;			/* 0x00e3 */
 	__u8	reservede4[4];		/* 0x00e4 */
-	__u64	tecmc;			/* 0x00e8 */
-	__u8	reservedf0[12];		/* 0x00f0 */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 289

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +10 -0
+	union {
+		__u64	tecmc;		/* 0x00e8 */
+		struct {
+			__u16	subchannel_id;	/* 0x00e8 */
+			__u16	subchannel_nr;	/* 0x00ea */
+			__u32	io_int_parm;	/* 0x00ec */
+			__u32	io_int_word;	/* 0x00f0 */
+		};
+	} __packed;
+	__u8	reservedf4[8];		/* 0x00f4 */

IIUC, for protected guests, you won't get an interception for which tecmc would be valid anymore, but need to put the I/O interruption stuff at the same place, right?

My main issue is that this makes the control block definition a bit ugly, since the f0 value that's unused in the non-protvirt case is not obvious anymore; but I don't know how to express this without making it even uglier :(

Quoted 3 lines
arch/s390/include/asm/kvm_host.h +0 -0
 #define CRYCB_FORMAT_MASK 0x00000003
 #define CRYCB_FORMAT0 0x00000000
 #define CRYCB_FORMAT1 0x00000001
#114 lore raw

Re: [RFC 13/37] KVM: s390: protvirt: Add interruption injection controls

Quoted 56 lines · Cornelia Huck
Quoted 53 lines · On Thu, 24 Oct 2019 07:40:35 -0400 Janosch Frank <[email protected]> wrote:

From: Michael Mueller <[email protected]>

Define the interruption injection codes and the related fields in the sie control block for PVM interruption injection.

---
  arch/s390/include/asm/kvm_host.h | 25 +++++++++++++++++++++----
  1 file changed, 21 insertions(+), 4 deletions(-)
arch/s390/include/asm/kvm_host.h +17 -4
index 6cc3b73ca904..82443236d4cc 100644
@@ -215,7 +215,15 @@ struct kvm_s390_sie_block {
  	__u8	icptcode;		/* 0x0050 */
  	__u8	icptstatus;		/* 0x0051 */
  	__u16	ihcpu;			/* 0x0052 */
-	__u8	reserved54[2];		/* 0x0054 */
+	__u8	reserved54;		/* 0x0054 */
+#define IICTL_CODE_NONE		 0x00
+#define IICTL_CODE_MCHK		 0x01
+#define IICTL_CODE_EXT		 0x02
+#define IICTL_CODE_IO		 0x03
+#define IICTL_CODE_RESTART	 0x04
+#define IICTL_CODE_SPECIFICATION 0x10
+#define IICTL_CODE_OPERAND	 0x11
+	__u8	iictl;			/* 0x0055 */
  	__u16	ipa;			/* 0x0056 */
  	__u32	ipb;			/* 0x0058 */
  	__u32	scaoh;			/* 0x005c */
@@ -252,7 +260,8 @@ struct kvm_s390_sie_block {
  #define HPID_KVM	0x4
  #define HPID_VSIE	0x5
  	__u8	hpid;			/* 0x00b8 */
-	__u8	reservedb9[11];		/* 0x00b9 */
+	__u8	reservedb9[7];		/* 0x00b9 */
+	__u32	eiparams;		/* 0x00c0 */
  	__u16	extcpuaddr;		/* 0x00c4 */
  	__u16	eic;			/* 0x00c6 */
  	__u32	reservedc8;		/* 0x00c8 */
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {
  	__u8	oai;			/* 0x00e2 */
  	__u8	armid;			/* 0x00e3 */
  	__u8	reservede4[4];		/* 0x00e4 */
-	__u64	tecmc;			/* 0x00e8 */
-	__u8	reservedf0[12];		/* 0x00f0 */
+	union {
+		__u64	tecmc;		/* 0x00e8 */
+		struct {
+			__u16	subchannel_id;	/* 0x00e8 */
+			__u16	subchannel_nr;	/* 0x00ea */
+			__u32	io_int_parm;	/* 0x00ec */
Quoted 8 lines

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 289

Quoted 4 lines
arch/s390/include/asm/kvm_host.h +4 -0
+			__u32	io_int_word;	/* 0x00f0 */
+		};
+	} __packed;
+	__u8	reservedf4[8];		/* 0x00f4 */

IIUC, for protected guests, you won't get an interception for which tecmc would be valid anymore, but need to put the I/O interruption stuff at the same place, right?

Yes, the format 4 architecture defines this.

Quoted 5 lines

My main issue is that this makes the control block definition a bit ugly, since the f0 value that's unused in the non-protvirt case is not obvious anymore; but I don't know how to express this without making it even uglier :(

:)

Quoted 5 lines

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 292

Quoted 4 lines
arch/s390/include/asm/kvm_host.h +0 -0
  #define CRYCB_FORMAT_MASK 0x00000003
  #define CRYCB_FORMAT0 0x00000000
  #define CRYCB_FORMAT1 0x00000001

Thanks
Michael

#115 lore raw

Re: [RFC 13/37] KVM: s390: protvirt: Add interruption injection controls

In reply to Janosch Frank
Quoted 42 lines · Janosch Frank

From: Michael Mueller <[email protected]>

Define the interruption injection codes and the related fields in the sie control block for PVM interruption injection.

---
 arch/s390/include/asm/kvm_host.h | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)
arch/s390/include/asm/kvm_host.h +11 -2
index 6cc3b73ca904..82443236d4cc 100644
@@ -215,7 +215,15 @@ struct kvm_s390_sie_block {
 	__u8	icptcode;		/* 0x0050 */
 	__u8	icptstatus;		/* 0x0051 */
 	__u16	ihcpu;			/* 0x0052 */
-	__u8	reserved54[2];		/* 0x0054 */
+	__u8	reserved54;		/* 0x0054 */
+#define IICTL_CODE_NONE		 0x00
+#define IICTL_CODE_MCHK		 0x01
+#define IICTL_CODE_EXT		 0x02
+#define IICTL_CODE_IO		 0x03
+#define IICTL_CODE_RESTART	 0x04
+#define IICTL_CODE_SPECIFICATION 0x10
+#define IICTL_CODE_OPERAND	 0x11
+	__u8	iictl;			/* 0x0055 */
 	__u16	ipa;			/* 0x0056 */
 	__u32	ipb;			/* 0x0058 */
 	__u32	scaoh;			/* 0x005c */
@@ -252,7 +260,8 @@ struct kvm_s390_sie_block {
 #define HPID_KVM	0x4
 #define HPID_VSIE	0x5
 	__u8	hpid;			/* 0x00b8 */
-	__u8	reservedb9[11];		/* 0x00b9 */
+	__u8	reservedb9[7];		/* 0x00b9 */
+	__u32	eiparams;		/* 0x00c0 */
 	__u16	extcpuaddr;		/* 0x00c4 */
 	__u16	eic;			/* 0x00c6 */
 	__u32	reservedc8;		/* 0x00c8 */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 281

Quoted 7 lines
arch/s390/include/asm/kvm_host.h +2 -2
@@ -268,8 +277,16 @@ struct kvm_s390_sie_block {
 	__u8	oai;			/* 0x00e2 */
 	__u8	armid;			/* 0x00e3 */
 	__u8	reservede4[4];		/* 0x00e4 */
-	__u64	tecmc;			/* 0x00e8 */
-	__u8	reservedf0[12];		/* 0x00f0 */
+	union {
+		__u64	tecmc;		/* 0x00e8 */

I have to admit that I always have to think twice where the compiler might put the padding in this case. Maybe you could do that manually to make it obvious and wrap it in a struct, too:

                struct {
			__u64	tecmc;		/* 0x00e8 */
			__u8	reservedf0[4];	/* 0x00f0 */

};

?

Just my 0.02 €, though.

Thomas

Quoted 12 lines
arch/s390/include/asm/kvm_host.h +8 -0
+		struct {
+			__u16	subchannel_id;	/* 0x00e8 */
+			__u16	subchannel_nr;	/* 0x00ea */
+			__u32	io_int_parm;	/* 0x00ec */
+			__u32	io_int_word;	/* 0x00f0 */
+		};
+	} __packed;
+	__u8	reservedf4[8];		/* 0x00f4 */
 #define CRYCB_FORMAT_MASK 0x00000003
 #define CRYCB_FORMAT0 0x00000000
 #define CRYCB_FORMAT1 0x00000001
author maintainer
#116 lore raw

RFC 14/37 KVM: s390: protvirt: Implement interruption injection

In reply to Janosch Frank

From: Michael Mueller <[email protected]>

The patch implements interruption injection for the following list of interruption types:

- I/O

    __deliver_io (III)

- External

    __deliver_cpu_timer (IEI)
    __deliver_ckc (IEI)
    __deliver_emergency_signal (IEI)
    __deliver_external_call (IEI)
    __deliver_service (IEI)

- cpu restart
__deliver_restart (IRI)

---
 arch/s390/include/asm/kvm_host.h |  10 ++
 arch/s390/kvm/interrupt.c        | 182 +++++++++++++++++++++++--------
 2 files changed, 149 insertions(+), 43 deletions(-)
arch/s390/include/asm/kvm_host.h +10 -0
index 82443236d4cc..63fc32d38aa9 100644
@@ -496,6 +496,7 @@ enum irq_types {
 	IRQ_PEND_PFAULT_INIT,
 	IRQ_PEND_EXT_HOST,
 	IRQ_PEND_EXT_SERVICE,
+	IRQ_PEND_EXT_SERVICE_EV,
 	IRQ_PEND_EXT_TIMING,
 	IRQ_PEND_EXT_CPU_TIMER,
 	IRQ_PEND_EXT_CLOCK_COMP,
@@ -540,6 +541,7 @@ enum irq_types {
 			   (1UL << IRQ_PEND_EXT_TIMING)     | \
 			   (1UL << IRQ_PEND_EXT_HOST)       | \
 			   (1UL << IRQ_PEND_EXT_SERVICE)    | \
+			   (1UL << IRQ_PEND_EXT_SERVICE_EV) | \
 			   (1UL << IRQ_PEND_VIRTIO)         | \
 			   (1UL << IRQ_PEND_PFAULT_INIT)    | \
 			   (1UL << IRQ_PEND_PFAULT_DONE))
@@ -556,6 +558,13 @@ enum irq_types {
 #define IRQ_PEND_MCHK_MASK ((1UL << IRQ_PEND_MCHK_REP) | \
 			    (1UL << IRQ_PEND_MCHK_EX))
 
+#define IRQ_PEND_EXT_II_MASK ((1UL << IRQ_PEND_EXT_CPU_TIMER)  | \
+			      (1UL << IRQ_PEND_EXT_CLOCK_COMP) | \
+			      (1UL << IRQ_PEND_EXT_EMERGENCY)  | \
+			      (1UL << IRQ_PEND_EXT_EXTERNAL)   | \
+			      (1UL << IRQ_PEND_EXT_SERVICE)    | \
+			      (1UL << IRQ_PEND_EXT_SERVICE_EV))
+
 struct kvm_s390_interrupt_info {
 	struct list_head list;
 	u64	type;
@@ -614,6 +623,7 @@ struct kvm_s390_local_interrupt {
 
 struct kvm_s390_float_interrupt {
 	unsigned long pending_irqs;
+	unsigned long masked_irqs;
 	spinlock_t lock;
 	struct list_head lists[FIRQ_LIST_COUNT];
 	int counters[FIRQ_MAX_COUNT];
arch/s390/kvm/interrupt.c +139 -43

319 lines not on this page · raw message

Signature · 1 line
2.20.1
#117 lore raw

Re: [RFC 14/37] KVM: s390: protvirt: Implement interruption injection

Quoted 9 lines · Janosch Frank

From: Michael Mueller <[email protected]>

The patch implements interruption injection for the following list of interruption types:

- I/O

     __deliver_io (III)

- External

Quoted 8 lines
     __deliver_cpu_timer (IEI)
     __deliver_ckc (IEI)
     __deliver_emergency_signal (IEI)
     __deliver_external_call (IEI)
     __deliver_service (IEI)

- cpu restart
__deliver_restart (IRI)

What exactly is IRQ_PEND_EXT_SERVICE_EV? Can you add some comments whet the new interrupt does and why it is needed in this context? Thanks

Quoted 393 lines
---
  arch/s390/include/asm/kvm_host.h |  10 ++
  arch/s390/kvm/interrupt.c        | 182 +++++++++++++++++++++++--------
  2 files changed, 149 insertions(+), 43 deletions(-)
arch/s390/include/asm/kvm_host.h +10 -0
index 82443236d4cc..63fc32d38aa9 100644
@@ -496,6 +496,7 @@ enum irq_types {
  	IRQ_PEND_PFAULT_INIT,
  	IRQ_PEND_EXT_HOST,
  	IRQ_PEND_EXT_SERVICE,
+	IRQ_PEND_EXT_SERVICE_EV,
  	IRQ_PEND_EXT_TIMING,
  	IRQ_PEND_EXT_CPU_TIMER,
  	IRQ_PEND_EXT_CLOCK_COMP,
@@ -540,6 +541,7 @@ enum irq_types {
  			   (1UL << IRQ_PEND_EXT_TIMING)     | \
  			   (1UL << IRQ_PEND_EXT_HOST)       | \
  			   (1UL << IRQ_PEND_EXT_SERVICE)    | \
+			   (1UL << IRQ_PEND_EXT_SERVICE_EV) | \
  			   (1UL << IRQ_PEND_VIRTIO)         | \
  			   (1UL << IRQ_PEND_PFAULT_INIT)    | \
  			   (1UL << IRQ_PEND_PFAULT_DONE))
@@ -556,6 +558,13 @@ enum irq_types {
  #define IRQ_PEND_MCHK_MASK ((1UL << IRQ_PEND_MCHK_REP) | \
  			    (1UL << IRQ_PEND_MCHK_EX))
 
+#define IRQ_PEND_EXT_II_MASK ((1UL << IRQ_PEND_EXT_CPU_TIMER)  | \
+			      (1UL << IRQ_PEND_EXT_CLOCK_COMP) | \
+			      (1UL << IRQ_PEND_EXT_EMERGENCY)  | \
+			      (1UL << IRQ_PEND_EXT_EXTERNAL)   | \
+			      (1UL << IRQ_PEND_EXT_SERVICE)    | \
+			      (1UL << IRQ_PEND_EXT_SERVICE_EV))
+
  struct kvm_s390_interrupt_info {
  	struct list_head list;
  	u64	type;
@@ -614,6 +623,7 @@ struct kvm_s390_local_interrupt {
 
  struct kvm_s390_float_interrupt {
  	unsigned long pending_irqs;
+	unsigned long masked_irqs;
  	spinlock_t lock;
  	struct list_head lists[FIRQ_LIST_COUNT];
  	int counters[FIRQ_MAX_COUNT];
arch/s390/kvm/interrupt.c +139 -43
index 165dea4c7f19..c919dfe4dfd3 100644
@@ -324,8 +324,10 @@ static inline int gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
 
  static inline unsigned long pending_irqs_no_gisa(struct kvm_vcpu *vcpu)
  {
-	return vcpu->kvm->arch.float_int.pending_irqs |
-		vcpu->arch.local_int.pending_irqs;
+	unsigned long pending = vcpu->kvm->arch.float_int.pending_irqs | vcpu->arch.local_int.pending_irqs;
+
+	pending &= ~vcpu->kvm->arch.float_int.masked_irqs;
+	return pending;
  }
 
  static inline unsigned long pending_irqs(struct kvm_vcpu *vcpu)
@@ -383,10 +385,16 @@ static unsigned long deliverable_irqs(struct kvm_vcpu *vcpu)
  		__clear_bit(IRQ_PEND_EXT_CLOCK_COMP, &active_mask);
  	if (!(vcpu->arch.sie_block->gcr[0] & CR0_CPU_TIMER_SUBMASK))
  		__clear_bit(IRQ_PEND_EXT_CPU_TIMER, &active_mask);
-	if (!(vcpu->arch.sie_block->gcr[0] & CR0_SERVICE_SIGNAL_SUBMASK))
+	if (!(vcpu->arch.sie_block->gcr[0] & CR0_SERVICE_SIGNAL_SUBMASK)) {
  		__clear_bit(IRQ_PEND_EXT_SERVICE, &active_mask);
+		__clear_bit(IRQ_PEND_EXT_SERVICE_EV, &active_mask);
+	}
  	if (psw_mchk_disabled(vcpu))
  		active_mask &= ~IRQ_PEND_MCHK_MASK;
+	/* PV guest cpus can have a single interruption injected at a time. */
+	if (kvm_s390_pv_is_protected(vcpu->kvm) &&
+	    vcpu->arch.sie_block->iictl != IICTL_CODE_NONE)
+		active_mask &= ~(IRQ_PEND_EXT_II_MASK | IRQ_PEND_IO_MASK);
  	/*
  	 * Check both floating and local interrupt's cr14 because
  	 * bit IRQ_PEND_MCHK_REP could be set in both cases.
@@ -479,19 +487,23 @@ static void set_intercept_indicators(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;
-	int rc;
+	int rc = 0;
 
  	vcpu->stat.deliver_cputm++;
  	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_CPU_TIMER,
  					 0, 0);
-
-	rc  = put_guest_lc(vcpu, EXT_IRQ_CPU_TIMER,
-			   (u16 *)__LC_EXT_INT_CODE);
-	rc |= put_guest_lc(vcpu, 0, (u16 *)__LC_EXT_CPU_ADDR);
-	rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
-			     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-	rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
-			    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_EXT;
+		vcpu->arch.sie_block->eic = EXT_IRQ_CPU_TIMER;
+	} else {
+		rc  = put_guest_lc(vcpu, EXT_IRQ_CPU_TIMER,
+				   (u16 *)__LC_EXT_INT_CODE);
+		rc |= put_guest_lc(vcpu, 0, (u16 *)__LC_EXT_CPU_ADDR);
+		rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+				     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+		rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
+				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	}
  	clear_bit(IRQ_PEND_EXT_CPU_TIMER, &li->pending_irqs);
  	return rc ? -EFAULT : 0;
  }
@@ -499,19 +511,23 @@ static int __must_check __deliver_cpu_timer(struct kvm_vcpu *vcpu)
  static int __must_check __deliver_ckc(struct kvm_vcpu *vcpu)
  {
  	struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
-	int rc;
+	int rc = 0;
 
  	vcpu->stat.deliver_ckc++;
  	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_CLOCK_COMP,
  					 0, 0);
-
-	rc  = put_guest_lc(vcpu, EXT_IRQ_CLK_COMP,
-			   (u16 __user *)__LC_EXT_INT_CODE);
-	rc |= put_guest_lc(vcpu, 0, (u16 *)__LC_EXT_CPU_ADDR);
-	rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
-			     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-	rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
-			    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_EXT;
+		vcpu->arch.sie_block->eic = EXT_IRQ_CLK_COMP;
+	} else {
+		rc  = put_guest_lc(vcpu, EXT_IRQ_CLK_COMP,
+				   (u16 __user *)__LC_EXT_INT_CODE);
+		rc |= put_guest_lc(vcpu, 0, (u16 *)__LC_EXT_CPU_ADDR);
+		rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+				     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+		rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
+				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	}
  	clear_bit(IRQ_PEND_EXT_CLOCK_COMP, &li->pending_irqs);
  	return rc ? -EFAULT : 0;
  }
@@ -533,7 +549,6 @@ static int __must_check __deliver_pfault_init(struct kvm_vcpu *vcpu)
  	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id,
  					 KVM_S390_INT_PFAULT_INIT,
  					 0, ext.ext_params2);
-
  	rc  = put_guest_lc(vcpu, EXT_IRQ_CP_SERVICE, (u16 *) __LC_EXT_INT_CODE);
  	rc |= put_guest_lc(vcpu, PFAULT_INIT, (u16 *) __LC_EXT_CPU_ADDR);
  	rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
@@ -696,17 +711,21 @@ static int __must_check __deliver_machine_check(struct kvm_vcpu *vcpu)
  static int __must_check __deliver_restart(struct kvm_vcpu *vcpu)
  {
  	struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
-	int rc;
+	int rc = 0;
 
  	VCPU_EVENT(vcpu, 3, "%s", "deliver: cpu restart");
  	vcpu->stat.deliver_restart_signal++;
  	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_RESTART, 0, 0);
 
-	rc  = write_guest_lc(vcpu,
-			     offsetof(struct lowcore, restart_old_psw),
-			     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-	rc |= read_guest_lc(vcpu, offsetof(struct lowcore, restart_psw),
-			    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_RESTART;
+	} else {
+		rc  = write_guest_lc(vcpu,
+				     offsetof(struct lowcore, restart_old_psw),
+				     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+		rc |= read_guest_lc(vcpu, offsetof(struct lowcore, restart_psw),
+				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	}
  	clear_bit(IRQ_PEND_RESTART, &li->pending_irqs);
  	return rc ? -EFAULT : 0;
  }
@@ -748,6 +767,12 @@ static int __must_check __deliver_emergency_signal(struct kvm_vcpu *vcpu)
  	vcpu->stat.deliver_emergency_signal++;
  	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_EMERGENCY,
  					 cpu_addr, 0);
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_EXT;
+		vcpu->arch.sie_block->eic = EXT_IRQ_EMERGENCY_SIG;
+		vcpu->arch.sie_block->extcpuaddr = cpu_addr;
+		return 0;
+	}
 
  	rc  = put_guest_lc(vcpu, EXT_IRQ_EMERGENCY_SIG,
  			   (u16 *)__LC_EXT_INT_CODE);
@@ -776,6 +801,12 @@ static int __must_check __deliver_external_call(struct kvm_vcpu *vcpu)
  	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id,
  					 KVM_S390_INT_EXTERNAL_CALL,
  					 extcall.code, 0);
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_EXT;
+		vcpu->arch.sie_block->eic = EXT_IRQ_EXTERNAL_CALL;
+		vcpu->arch.sie_block->extcpuaddr = extcall.code;
+		return 0;
+	}
 
  	rc  = put_guest_lc(vcpu, EXT_IRQ_EXTERNAL_CALL,
  			   (u16 *)__LC_EXT_INT_CODE);
@@ -902,6 +933,31 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu)
  	return rc ? -EFAULT : 0;
  }
 
+#define SCCB_MASK 0xFFFFFFF8
+#define SCCB_EVENT_PENDING 0x3
+
+static int write_sclp(struct kvm_vcpu *vcpu, u32 parm)
+{
+	int rc;
+
+	if (kvm_s390_pv_handle_cpu(vcpu)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_EXT;
+		vcpu->arch.sie_block->eic = EXT_IRQ_SERVICE_SIG;
+		vcpu->arch.sie_block->eiparams = parm;
+		return 0;
+	}
+
+	rc  = put_guest_lc(vcpu, EXT_IRQ_SERVICE_SIG, (u16 *)__LC_EXT_INT_CODE);
+	rc |= put_guest_lc(vcpu, 0, (u16 *)__LC_EXT_CPU_ADDR);
+	rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+			     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
+			    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+	rc |= put_guest_lc(vcpu, parm,
+			   (u32 *)__LC_EXT_PARAMS);
+	return rc;
+}
+
  static int __must_check __deliver_service(struct kvm_vcpu *vcpu)
  {
  	struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
@@ -909,13 +965,17 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu)
  	int rc = 0;
 
  	spin_lock(&fi->lock);
-	if (!(test_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs))) {
+	if (test_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs) ||
+	    !(test_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs))) {
  		spin_unlock(&fi->lock);
  		return 0;
  	}
  	ext = fi->srv_signal;
  	memset(&fi->srv_signal, 0, sizeof(ext));
  	clear_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs);
+	clear_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs);
+	if (kvm_s390_pv_is_protected(vcpu->kvm))
+		set_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs);
  	spin_unlock(&fi->lock);
 
  	VCPU_EVENT(vcpu, 4, "deliver: sclp parameter 0x%x",
@@ -924,15 +984,33 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu)
  	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_SERVICE,
  					 ext.ext_params, 0);
 
-	rc  = put_guest_lc(vcpu, EXT_IRQ_SERVICE_SIG, (u16 *)__LC_EXT_INT_CODE);
-	rc |= put_guest_lc(vcpu, 0, (u16 *)__LC_EXT_CPU_ADDR);
-	rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
-			     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-	rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
-			    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-	rc |= put_guest_lc(vcpu, ext.ext_params,
-			   (u32 *)__LC_EXT_PARAMS);
+	rc = write_sclp(vcpu, ext.ext_params);
+	return rc ? -EFAULT : 0;
+}
 
+static int __must_check __deliver_service_ev(struct kvm_vcpu *vcpu)
+{
+	struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
+	struct kvm_s390_ext_info ext;
+	int rc = 0;
+
+	spin_lock(&fi->lock);
+	if (!(test_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs))) {
+		spin_unlock(&fi->lock);
+		return 0;
+	}
+	ext = fi->srv_signal;
+	/* only clear the event bit */
+	fi->srv_signal.ext_params &= ~SCCB_EVENT_PENDING;
+	clear_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs);
+	spin_unlock(&fi->lock);
+
+	VCPU_EVENT(vcpu, 4, "%s", "deliver: sclp parameter event");
+	vcpu->stat.deliver_service_signal++;
+	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_SERVICE,
+					 ext.ext_params, 0);
+
+	rc = write_sclp(vcpu, SCCB_EVENT_PENDING);
  	return rc ? -EFAULT : 0;
  }
 
@@ -1028,6 +1106,15 @@ static int __do_deliver_io(struct kvm_vcpu *vcpu, struct kvm_s390_io_info *io)
  {
  	int rc;
 
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_IO;
+		vcpu->arch.sie_block->subchannel_id = io->subchannel_id;
+		vcpu->arch.sie_block->subchannel_nr = io->subchannel_nr;
+		vcpu->arch.sie_block->io_int_parm = io->io_int_parm;
+		vcpu->arch.sie_block->io_int_word = io->io_int_word;
+		return 0;
+	}
+
  	rc  = put_guest_lc(vcpu, io->subchannel_id, (u16 *)__LC_SUBCHANNEL_ID);
  	rc |= put_guest_lc(vcpu, io->subchannel_nr, (u16 *)__LC_SUBCHANNEL_NR);
  	rc |= put_guest_lc(vcpu, io->io_int_parm, (u32 *)__LC_IO_INT_PARM);
@@ -1329,6 +1416,9 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
  		case IRQ_PEND_EXT_SERVICE:
  			rc = __deliver_service(vcpu);
  			break;
+		case IRQ_PEND_EXT_SERVICE_EV:
+			rc = __deliver_service_ev(vcpu);
+			break;
  		case IRQ_PEND_PFAULT_DONE:
  			rc = __deliver_pfault_done(vcpu);
  			break;
@@ -1421,7 +1511,7 @@ static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
  	if (kvm_get_vcpu_by_id(vcpu->kvm, src_id) == NULL)
  		return -EINVAL;
 
-	if (sclp.has_sigpif)
+	if (sclp.has_sigpif && !kvm_s390_pv_handle_cpu(vcpu))
  		return sca_inject_ext_call(vcpu, src_id);
 
  	if (test_and_set_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs))
@@ -1681,9 +1771,6 @@ struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
  	return inti;
  }
 
-#define SCCB_MASK 0xFFFFFFF8
-#define SCCB_EVENT_PENDING 0x3
-
  static int __inject_service(struct kvm *kvm,
  			     struct kvm_s390_interrupt_info *inti)
  {
@@ -1692,6 +1779,11 @@ static int __inject_service(struct kvm *kvm,
  	kvm->stat.inject_service_signal++;
  	spin_lock(&fi->lock);
  	fi->srv_signal.ext_params |= inti->ext.ext_params & SCCB_EVENT_PENDING;
+
+	/* We always allow events, track them separately from the sccb ints */
+	if (fi->srv_signal.ext_params & SCCB_EVENT_PENDING)
+		set_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs);
+
  	/*
  	 * Early versions of the QEMU s390 bios will inject several
  	 * service interrupts after another without handling a
@@ -1834,7 +1926,8 @@ static void __floating_irq_kick(struct kvm *kvm, u64 type)
  		break;
  	case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
  		if (!(type & KVM_S390_INT_IO_AI_MASK &&
-		      kvm->arch.gisa_int.origin))
+		      kvm->arch.gisa_int.origin) ||
+		      kvm_s390_pv_handle_cpu(dst_vcpu))
  			kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_IO_INT);
  		break;
  	default:
@@ -2082,6 +2175,8 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm)
 
  	spin_lock(&fi->lock);
  	fi->pending_irqs = 0;
+	if (!kvm_s390_pv_is_protected(kvm))
+		fi->masked_irqs = 0;
  	memset(&fi->srv_signal, 0, sizeof(fi->srv_signal));
  	memset(&fi->mchk, 0, sizeof(fi->mchk));
  	for (i = 0; i < FIRQ_LIST_COUNT; i++)
@@ -2146,7 +2241,8 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
  			n++;
  		}
  	}
-	if (test_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs)) {
+	if (test_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs) ||
+	    test_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs)) {
  		if (n == max_irqs) {
  			/* signal userspace to try again */
  			ret = -ENOMEM;
Signature · 3 lines
Thanks,

David / dhildenb
#118 lore raw

Re: [RFC 14/37] KVM: s390: protvirt: Implement interruption injection

Quoted 16 lines · David Hildenbrand
Quoted 15 lines · Janosch Frank

From: Michael Mueller <[email protected]>

The patch implements interruption injection for the following list of interruption types:

   - I/O
     __deliver_io (III)

   - External
     __deliver_cpu_timer (IEI)
     __deliver_ckc (IEI)
     __deliver_emergency_signal (IEI)
     __deliver_external_call (IEI)
     __deliver_service (IEI)

Quoted 4 lines
Quoted 2 lines

   - cpu restart
     __deliver_restart (IRI)

What exactly is IRQ_PEND_EXT_SERVICE_EV? Can you add some comments whet the new interrupt does and why it is needed in this context? Thanks

I did that code. What about the following add-on description.

The ultravisor does several checks on injected interrupts. For example it will check that for an sclp interrupt with an sccb address we had an servc exit and exit with a validity intercept.
As the hypervisor must avoid valitity intercepts we now mask invalid interrupts.

There are also sclp interrupts that only inject an event (e.g. an input event on the sclp consoles) those interrupts must not be masked.
Let us split out these "event interupts" from the normal sccb interrupts into IRQ_PEND_EXT_SERVICE_EV.

#119 lore raw

Re: [RFC 14/37] KVM: s390: protvirt: Implement interruption injection

Quoted 22 lines · Christian Borntraeger
Quoted 19 lines · David Hildenbrand
Quoted 17 lines · Janosch Frank

From: Michael Mueller <[email protected]>

The patch implements interruption injection for the following list of interruption types:

   - I/O
     __deliver_io (III)

   - External
     __deliver_cpu_timer (IEI)
     __deliver_ckc (IEI)
     __deliver_emergency_signal (IEI)
     __deliver_external_call (IEI)
     __deliver_service (IEI)

   - cpu restart
     __deliver_restart (IRI)

Quoted 8 lines
Quoted 1 line

What exactly is IRQ_PEND_EXT_SERVICE_EV? Can you add some comments whet the new interrupt does and why it is needed in this context? Thanks

I did that code. What about the following add-on description.

The ultravisor does several checks on injected interrupts. For example it will check that for an sclp interrupt with an sccb address we had an servc exit and exit with a validity intercept.
As the hypervisor must avoid valitity intercepts we now mask invalid interrupts.

s/valitity/validity/

Quoted 6 lines

There are also sclp interrupts that only inject an event (e.g. an input event on the sclp consoles) those interrupts must not be masked.
Let us split out these "event interupts" from the normal sccb interrupts into IRQ_PEND_EXT_SERVICE_EV.

Thanks for the clarification. From what I see, this is transparent from user space - we only track these interrupts separately internally.

Signature · 3 lines
Thanks,

David / dhildenb
#120 lore raw

Re: [RFC 14/37] KVM: s390: protvirt: Implement interruption injection

In reply to Janosch Frank
Quoted 21 lines · Janosch Frank

From: Michael Mueller <[email protected]>

The patch implements interruption injection for the following list of interruption types:

- I/O

    __deliver_io (III)

- External

    __deliver_cpu_timer (IEI)
    __deliver_ckc (IEI)
    __deliver_emergency_signal (IEI)
    __deliver_external_call (IEI)
    __deliver_service (IEI)

- cpu restart
__deliver_restart (IRI)

---

[...]

arch/s390/kvm/interrupt.c static inline int gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) · line 327

Quoted 11 lines
arch/s390/kvm/interrupt.c +1 -2
index 165dea4c7f19..c919dfe4dfd3 100644
@@ -324,8 +324,10 @@ static inline int gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
 
 static inline unsigned long pending_irqs_no_gisa(struct kvm_vcpu *vcpu)
 {
-	return vcpu->kvm->arch.float_int.pending_irqs |
-		vcpu->arch.local_int.pending_irqs;
+	unsigned long pending = vcpu->kvm->arch.float_int.pending_irqs | vcpu->arch.local_int.pending_irqs;

The line is now pretty long, way more than 80 columns ... maybe keep it on two lines?

Quoted 4 lines
arch/s390/kvm/interrupt.c +3 -0
+
+	pending &= ~vcpu->kvm->arch.float_int.masked_irqs;
+	return pending;
 }

[...]

arch/s390/kvm/interrupt.c static int __must_check __deliver_pfault_init(struct kvm_vcpu *vcpu) · line 554

Quoted 8 lines
arch/s390/kvm/interrupt.c +0 -1
@@ -533,7 +549,6 @@ static int __must_check __deliver_pfault_init(struct kvm_vcpu *vcpu)
 	trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id,
 					 KVM_S390_INT_PFAULT_INIT,
 					 0, ext.ext_params2);
-
 	rc  = put_guest_lc(vcpu, EXT_IRQ_CP_SERVICE, (u16 *) __LC_EXT_INT_CODE);
 	rc |= put_guest_lc(vcpu, PFAULT_INIT, (u16 *) __LC_EXT_CPU_ADDR);
 	rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,

I think you can drop this hunk.

Thomas

author maintainer
#121 lore raw

RFC 15/37 KVM: s390: protvirt: Add machine-check interruption injection controls

In reply to Janosch Frank

From: Michael Mueller <[email protected]>

The following fields are added to the sie control block type 4:
- Machine Check Interruption Code (mcic)
- External Damage Code (edc)
- Failing Storage Address (faddr)

---
 arch/s390/include/asm/kvm_host.h | 33 +++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)
arch/s390/include/asm/kvm_host.h +24 -9
index 63fc32d38aa9..0ab309b7bf4c 100644
@@ -261,16 +261,31 @@ struct kvm_s390_sie_block {
 #define HPID_VSIE	0x5
 	__u8	hpid;			/* 0x00b8 */
 	__u8	reservedb9[7];		/* 0x00b9 */
-	__u32	eiparams;		/* 0x00c0 */
-	__u16	extcpuaddr;		/* 0x00c4 */
-	__u16	eic;			/* 0x00c6 */
+	union {
+		struct {
+			__u32	eiparams;	/* 0x00c0 */
+			__u16	extcpuaddr;	/* 0x00c4 */
+			__u16	eic;		/* 0x00c6 */
+		};
+		__u64	mcic;			/* 0x00c0 */
+	} __packed;
 	__u32	reservedc8;		/* 0x00c8 */
-	__u16	pgmilc;			/* 0x00cc */
-	__u16	iprcc;			/* 0x00ce */
-	__u32	dxc;			/* 0x00d0 */
-	__u16	mcn;			/* 0x00d4 */
-	__u8	perc;			/* 0x00d6 */
-	__u8	peratmid;		/* 0x00d7 */
+	union {
+		struct {
+			__u16	pgmilc;		/* 0x00cc */
+			__u16	iprcc;		/* 0x00ce */
+		};
+		__u32	edc;			/* 0x00cc */
+	} __packed;
+	union {
+		struct {
+			__u32	dxc;		/* 0x00d0 */
+			__u16	mcn;		/* 0x00d4 */
+			__u8	perc;		/* 0x00d6 */
+			__u8	peratmid;	/* 0x00d7 */
+		};
+		__u64	faddr;			/* 0x00d0 */
+	} __packed;
 	__u64	peraddr;		/* 0x00d8 */
 	__u8	eai;			/* 0x00e0 */
 	__u8	peraid;			/* 0x00e1 */
Signature · 1 line
2.20.1
#122 lore raw

Re: [RFC 15/37] KVM: s390: protvirt: Add machine-check interruption injection controls

Quoted 44 lines · Janosch Frank

From: Michael Mueller <[email protected]>

The following fields are added to the sie control block type 4:
- Machine Check Interruption Code (mcic)
- External Damage Code (edc)
- Failing Storage Address (faddr)

---
 arch/s390/include/asm/kvm_host.h | 33 +++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)
arch/s390/include/asm/kvm_host.h +14 -9
index 63fc32d38aa9..0ab309b7bf4c 100644
@@ -261,16 +261,31 @@ struct kvm_s390_sie_block {
 #define HPID_VSIE	0x5
 	__u8	hpid;			/* 0x00b8 */
 	__u8	reservedb9[7];		/* 0x00b9 */
-	__u32	eiparams;		/* 0x00c0 */
-	__u16	extcpuaddr;		/* 0x00c4 */
-	__u16	eic;			/* 0x00c6 */
+	union {
+		struct {
+			__u32	eiparams;	/* 0x00c0 */
+			__u16	extcpuaddr;	/* 0x00c4 */
+			__u16	eic;		/* 0x00c6 */
+		};
+		__u64	mcic;			/* 0x00c0 */
+	} __packed;
 	__u32	reservedc8;		/* 0x00c8 */
-	__u16	pgmilc;			/* 0x00cc */
-	__u16	iprcc;			/* 0x00ce */
-	__u32	dxc;			/* 0x00d0 */
-	__u16	mcn;			/* 0x00d4 */
-	__u8	perc;			/* 0x00d6 */
-	__u8	peratmid;		/* 0x00d7 */
+	union {
+		struct {
+			__u16	pgmilc;		/* 0x00cc */
+			__u16	iprcc;		/* 0x00ce */
+		};
+		__u32	edc;			/* 0x00cc */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 288

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +10 -0
+	} __packed;
+	union {
+		struct {
+			__u32	dxc;		/* 0x00d0 */
+			__u16	mcn;		/* 0x00d4 */
+			__u8	perc;		/* 0x00d6 */
+			__u8	peratmid;	/* 0x00d7 */
+		};
+		__u64	faddr;			/* 0x00d0 */
+	} __packed;

Maybe drop the __packed keywords since the struct members are naturally aligned anyway?

Thomas

#123 lore raw

Re: [RFC 15/37] KVM: s390: protvirt: Add machine-check interruption injection controls

Quoted 59 lines · Thomas Huth
Quoted 44 lines · Janosch Frank

From: Michael Mueller <[email protected]>

The following fields are added to the sie control block type 4:
- Machine Check Interruption Code (mcic)
- External Damage Code (edc)
- Failing Storage Address (faddr)

---
  arch/s390/include/asm/kvm_host.h | 33 +++++++++++++++++++++++---------
  1 file changed, 24 insertions(+), 9 deletions(-)
arch/s390/include/asm/kvm_host.h +14 -9
index 63fc32d38aa9..0ab309b7bf4c 100644
@@ -261,16 +261,31 @@ struct kvm_s390_sie_block {
  #define HPID_VSIE	0x5
  	__u8	hpid;			/* 0x00b8 */
  	__u8	reservedb9[7];		/* 0x00b9 */
-	__u32	eiparams;		/* 0x00c0 */
-	__u16	extcpuaddr;		/* 0x00c4 */
-	__u16	eic;			/* 0x00c6 */
+	union {
+		struct {
+			__u32	eiparams;	/* 0x00c0 */
+			__u16	extcpuaddr;	/* 0x00c4 */
+			__u16	eic;		/* 0x00c6 */
+		};
+		__u64	mcic;			/* 0x00c0 */
+	} __packed;
  	__u32	reservedc8;		/* 0x00c8 */
-	__u16	pgmilc;			/* 0x00cc */
-	__u16	iprcc;			/* 0x00ce */
-	__u32	dxc;			/* 0x00d0 */
-	__u16	mcn;			/* 0x00d4 */
-	__u8	perc;			/* 0x00d6 */
-	__u8	peratmid;		/* 0x00d7 */
+	union {
+		struct {
+			__u16	pgmilc;		/* 0x00cc */
+			__u16	iprcc;		/* 0x00ce */
+		};
+		__u32	edc;			/* 0x00cc */

arch/s390/include/asm/kvm_host.h struct kvm_s390_sie_block { · line 288

Quoted 10 lines
arch/s390/include/asm/kvm_host.h +10 -0
+	} __packed;
+	union {
+		struct {
+			__u32	dxc;		/* 0x00d0 */
+			__u16	mcn;		/* 0x00d4 */
+			__u8	perc;		/* 0x00d6 */
+			__u8	peratmid;	/* 0x00d7 */
+		};
+		__u64	faddr;			/* 0x00d0 */
+	} __packed;

Maybe drop the __packed keywords since the struct members are naturally aligned anyway?

Quoted 2 lines

Thomas

Thanks, I will give it a try.

Michael

author maintainer
#124 lore raw

RFC 16/37 KVM: s390: protvirt: Implement machine-check interruption injection

In reply to Janosch Frank

From: Michael Mueller <[email protected]>

Similar to external interrupts, the hypervisor can inject machine checks by providing the right data in the interrupt injection controls.

---
 arch/s390/kvm/interrupt.c | 8 ++++++++
 1 file changed, 8 insertions(+)
arch/s390/kvm/interrupt.c +8 -0
index c919dfe4dfd3..1f87c7d3fa3e 100644
@@ -568,6 +568,14 @@ static int __write_machine_check(struct kvm_vcpu *vcpu,
 	union mci mci;
 	int rc;
 
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_MCHK;
+		vcpu->arch.sie_block->mcic = mchk->mcic;
+		vcpu->arch.sie_block->faddr = mchk->failing_storage_address;
+		vcpu->arch.sie_block->edc = mchk->ext_damage_code;
+		return 0;
+	}
+
 	mci.val = mchk->mcic;
 	/* take care of lazy register loading */
 	save_fpu_regs();
Signature · 1 line
2.20.1
#125 lore raw

Re: [RFC 16/37] KVM: s390: protvirt: Implement machine-check interruption injection

Quoted 16 lines · On Thu, 24 Oct 2019 07:40:38 -0400 Janosch Frank <[email protected]> wrote:

From: Michael Mueller <[email protected]>

Similar to external interrupts, the hypervisor can inject machine checks by providing the right data in the interrupt injection controls.

---
 arch/s390/kvm/interrupt.c | 8 ++++++++
 1 file changed, 8 insertions(+)
arch/s390/kvm/interrupt.c +0 -0
index c919dfe4dfd3..1f87c7d3fa3e 100644
@@ -568,6 +568,14 @@ static int __write_machine_check(struct kvm_vcpu *vcpu,
 	union mci mci;

arch/s390/kvm/interrupt.c static int __write_machine_check(struct kvm_vcpu *vcpu, · line 578

Quoted 10 lines
arch/s390/kvm/interrupt.c +8 -0
 	int rc;
 
+	if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+		vcpu->arch.sie_block->iictl = IICTL_CODE_MCHK;
+		vcpu->arch.sie_block->mcic = mchk->mcic;
+		vcpu->arch.sie_block->faddr = mchk->failing_storage_address;
+		vcpu->arch.sie_block->edc = mchk->ext_damage_code;
+		return 0;
+	}
+

The other stuff this function injects in the !pv case is inaccessible to the hypervisor in the pv case, right? (Registers, extended save area, ...) Maybe add a comment?

Quoted 3 lines
arch/s390/kvm/interrupt.c +0 -0
 	mci.val = mchk->mcic;
 	/* take care of lazy register loading */
 	save_fpu_regs();
author maintainer
#126 lore raw

RFC 17/37 DOCUMENTATION: protvirt: Instruction emulation

In reply to Janosch Frank

As guest memory is inaccessible and information about the guest's state is very limited, new ways for instruction emulation have been introduced.

With a bounce area for guest GRs and instruction data, guest state leaks can be limited by the Ultravisor. KVM now has to move instruction input and output through these areas.

---
 Documentation/virtual/kvm/s390-pv.txt | 47 +++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
Documentation/virtual/kvm/s390-pv.txt +47 -0
index e09f2dc5f164..cb08d78a7922 100644
@@ -48,3 +48,50 @@ interception codes have been introduced. One which tells us that CRs
 have changed. And one for PSW bit 13 changes. The CRs and the PSW in
 the state description only contain the mask bits and no further info
 like the current instruction address.
+
+
+Instruction emulation:
+With the format 4 state description the SIE instruction already
+interprets more instructions than it does with format 2. As it is not
+able to interpret all instruction, the SIE and the UV safeguard KVM's
+emulation inputs and outputs.
+
+Guest GRs and most of the instruction data, like IO data structures
+are filtered. Instruction data is copied to and from the Secure
+Instruction Data Area. Guest GRs are put into / retrieved from the
+Interception-Data block.
+
+The Interception-Data block from the state description's offset 0x380
+contains GRs 0 - 16. Only GR values needed to emulate an instruction
+will be copied into this area.
+
+The Interception Parameters state description field still contains the
+the bytes of the instruction text but with pre-set register
+values. I.e. each instruction always uses the same instruction text,
+to not leak guest instruction text.
+
+The Secure Instruction Data Area contains instruction storage
+data. Data for diag 500 is exempt from that and has to be moved
+through shared buffers to KVM.
+
+When SIE intercepts an instruction, it will only allow data and
+program interrupts for this instruction to be moved to the guest via
+the two data areas discussed before. Other data is ignored or results
+in validity interceptions.
+
+
+Instruction emulation interceptions:
+There are two types of SIE secure instruction intercepts. The normal
+and the notification type. Normal secure instruction intercepts will
+make the guest pending for instruction completion of the intercepted
+instruction type, i.e. on SIE entry it is attempted to complete
+emulation of the instruction with the data provided by KVM. That might
+be a program exception or instruction completion.
+
+The notification type intercepts inform KVM about guest environment
+changes due to guest instruction interpretation. Such an interception
+is recognized for the store prefix instruction and provides the new
+lowcore location for mapping change notification arming. Any KVM data
+in the data areas is ignored, program exceptions are not injected and
+execution continues on next SIE entry, as if no intercept had
+happened.
Signature · 1 line
2.20.1
#127 lore raw

Re: [RFC 17/37] DOCUMENTATION: protvirt: Instruction emulation

Quoted 18 lines · On Thu, 24 Oct 2019 07:40:39 -0400 Janosch Frank <[email protected]> wrote:

As guest memory is inaccessible and information about the guest's state is very limited, new ways for instruction emulation have been introduced.

With a bounce area for guest GRs and instruction data, guest state leaks can be limited by the Ultravisor. KVM now has to move instruction input and output through these areas.

---
 Documentation/virtual/kvm/s390-pv.txt | 47 +++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
Documentation/virtual/kvm/s390-pv.txt +0 -0
index e09f2dc5f164..cb08d78a7922 100644

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 54

Quoted 7 lines
Documentation/virtual/kvm/s390-pv.txt +4 -0
@@ -48,3 +48,50 @@ interception codes have been introduced. One which tells us that CRs
 have changed. And one for PSW bit 13 changes. The CRs and the PSW in
 the state description only contain the mask bits and no further info
 like the current instruction address.
+
+
+Instruction emulation:
+With the format 4 state description the SIE instruction already

s/description/description,/

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 56

Quoted 2 lines
Documentation/virtual/kvm/s390-pv.txt +2 -0
+interprets more instructions than it does with format 2. As it is not
+able to interpret all instruction, the SIE and the UV safeguard KVM's

s/instruction/instructions/

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 59

Quoted 3 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+emulation inputs and outputs.
+
+Guest GRs and most of the instruction data, like IO data structures

Hm, what 'IO data structures'?

Quoted 6 lines
Documentation/virtual/kvm/s390-pv.txt +6 -0
+are filtered. Instruction data is copied to and from the Secure
+Instruction Data Area. Guest GRs are put into / retrieved from the
+Interception-Data block.
+
+The Interception-Data block from the state description's offset 0x380
+contains GRs 0 - 16. Only GR values needed to emulate an instruction

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 75

Quoted 10 lines
Documentation/virtual/kvm/s390-pv.txt +10 -0
+will be copied into this area.
+
+The Interception Parameters state description field still contains the
+the bytes of the instruction text but with pre-set register
+values. I.e. each instruction always uses the same instruction text,
+to not leak guest instruction text.
+
+The Secure Instruction Data Area contains instruction storage
+data. Data for diag 500 is exempt from that and has to be moved
+through shared buffers to KVM.

I find this paragraph a bit confusing. What does that imply for diag 500 interception? Data is still present in gprs 1-4?

(Also, why only diag 500? Because it is the 'reserved for kvm' diagnose call?)

Quoted 7 lines
Documentation/virtual/kvm/s390-pv.txt +7 -0
+
+When SIE intercepts an instruction, it will only allow data and
+program interrupts for this instruction to be moved to the guest via
+the two data areas discussed before. Other data is ignored or results
+in validity interceptions.
+
+

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 92

Quoted 10 lines
Documentation/virtual/kvm/s390-pv.txt +10 -0
+Instruction emulation interceptions:
+There are two types of SIE secure instruction intercepts. The normal
+and the notification type. Normal secure instruction intercepts will
+make the guest pending for instruction completion of the intercepted
+instruction type, i.e. on SIE entry it is attempted to complete
+emulation of the instruction with the data provided by KVM. That might
+be a program exception or instruction completion.
+
+The notification type intercepts inform KVM about guest environment
+changes due to guest instruction interpretation. Such an interception

'interpretation by SIE' ?

Quoted 5 lines
Documentation/virtual/kvm/s390-pv.txt +5 -0
+is recognized for the store prefix instruction and provides the new
+lowcore location for mapping change notification arming. Any KVM data
+in the data areas is ignored, program exceptions are not injected and
+execution continues on next SIE entry, as if no intercept had
+happened.
maintainer
#128 lore raw

Re: [RFC 17/37] DOCUMENTATION: protvirt: Instruction emulation

Quoted 35 lines · On Thu, 14 Nov 2019 16:15:26 +0100 Cornelia Huck <[email protected]> wrote:
Quoted 22 lines · On Thu, 24 Oct 2019 07:40:39 -0400 Janosch Frank <[email protected]> wrote:

As guest memory is inaccessible and information about the guest's state is very limited, new ways for instruction emulation have been introduced.

With a bounce area for guest GRs and instruction data, guest state leaks can be limited by the Ultravisor. KVM now has to move instruction input and output through these areas.

---
 Documentation/virtual/kvm/s390-pv.txt | 47

+++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)

(quoted diff) +0 -0

b/Documentation/virtual/kvm/s390-pv.txt index e09f2dc5f164..cb08d78a7922 100644 --- a/Documentation/virtual/kvm/s390-pv.txt +++ b/Documentation/virtual/kvm/s390-pv.txt @@ -48,3 +48,50 @@ interception codes have been introduced. One which tells us that CRs have changed. And one for PSW bit 13 changes. The CRs and the PSW in the state description only contain the mask bits and no further info like the current instruction address. +

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 54

Quoted 3 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+
+Instruction emulation:
+With the format 4 state description the SIE instruction already  

s/description/description,/

Quoted 3 lines

+interprets more instructions than it does with format 2. As it is not +able to interpret all instruction, the SIE and the UV safeguard KVM's

Quoted 8 lines

s/instruction/instructions/

Quoted 4 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+emulation inputs and outputs.
+
+Guest GRs and most of the instruction data, like IO data

structures

Hm, what 'IO data structures'?

the various IRB and ORB of I/O instructions

Quoted 11 lines
Quoted 11 lines
Documentation/virtual/kvm/s390-pv.txt +5 -0
+are filtered. Instruction data is copied to and from the Secure
+Instruction Data Area. Guest GRs are put into / retrieved from the
+Interception-Data block.
+
+The Interception-Data block from the state description's offset

0x380 +contains GRs 0 - 16. Only GR values needed to emulate an instruction +will be copied into this area.

(quoted diff) +2 -0
+
+The Interception Parameters state description field still contains

the +the bytes of the instruction text but with pre-set register +values. I.e. each instruction always uses the same instruction

Quoted 8 lines

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 75

Quoted 5 lines

text, +to not leak guest instruction text.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+The Secure Instruction Data Area contains instruction storage
+data. Data for diag 500 is exempt from that and has to be moved
+through shared buffers to KVM.  

I find this paragraph a bit confusing. What does that imply for diag 500 interception? Data is still present in gprs 1-4?

no registers are leaked in the registers. registers are always only exposed through the state description.

Quoted 30 lines

(Also, why only diag 500? Because it is the 'reserved for kvm' diagnose call?)

Quoted 17 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+
+When SIE intercepts an instruction, it will only allow data and
+program interrupts for this instruction to be moved to the guest

via +the two data areas discussed before. Other data is ignored or results +in validity interceptions.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+
+Instruction emulation interceptions:
+There are two types of SIE secure instruction intercepts. The

normal +and the notification type. Normal secure instruction intercepts will +make the guest pending for instruction completion of the intercepted +instruction type, i.e. on SIE entry it is attempted to complete +emulation of the instruction with the data provided by KVM. That might +be a program exception or instruction completion. +

(quoted diff) +2 -0
+The notification type intercepts inform KVM about guest environment
+changes due to guest instruction interpretation. Such an
Quoted 1 line

interception

'interpretation by SIE' ?

Quoted 5 lines
(quoted diff) +2 -0
+is recognized for the store prefix instruction and provides the new
+lowcore location for mapping change notification arming. Any KVM

data +in the data areas is ignored, program exceptions are not injected and +execution continues on next SIE entry, as if no intercept had +happened.

#129 lore raw

Re: [RFC 17/37] DOCUMENTATION: protvirt: Instruction emulation

Quoted 40 lines · On Thu, 14 Nov 2019 16:20:24 +0100 Claudio Imbrenda <[email protected]>…
Quoted 37 lines · On Thu, 14 Nov 2019 16:15:26 +0100 Cornelia Huck <[email protected]> wrote:
Quoted 22 lines · On Thu, 24 Oct 2019 07:40:39 -0400 Janosch Frank <[email protected]> wrote:

As guest memory is inaccessible and information about the guest's state is very limited, new ways for instruction emulation have been introduced.

With a bounce area for guest GRs and instruction data, guest state leaks can be limited by the Ultravisor. KVM now has to move instruction input and output through these areas.

---
 Documentation/virtual/kvm/s390-pv.txt | 47

+++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)

(quoted diff) +0 -0

b/Documentation/virtual/kvm/s390-pv.txt index e09f2dc5f164..cb08d78a7922 100644 --- a/Documentation/virtual/kvm/s390-pv.txt +++ b/Documentation/virtual/kvm/s390-pv.txt @@ -48,3 +48,50 @@ interception codes have been introduced. One which tells us that CRs have changed. And one for PSW bit 13 changes. The CRs and the PSW in the state description only contain the mask bits and no further info like the current instruction address. +

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 54

Quoted 3 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+
+Instruction emulation:
+With the format 4 state description the SIE instruction already    

s/description/description,/

Quoted 3 lines

+interprets more instructions than it does with format 2. As it is not +able to interpret all instruction, the SIE and the UV safeguard KVM's

s/instruction/instructions/

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+emulation inputs and outputs.
+
+Guest GRs and most of the instruction data, like IO data

structures

Hm, what 'IO data structures'?

the various IRB and ORB of I/O instructions

Would be good to mention them as examples :)

Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
Documentation/virtual/kvm/s390-pv.txt +5 -0
+are filtered. Instruction data is copied to and from the Secure
+Instruction Data Area. Guest GRs are put into / retrieved from the
+Interception-Data block.
+
+The Interception-Data block from the state description's offset

0x380 +contains GRs 0 - 16. Only GR values needed to emulate an instruction +will be copied into this area.

(quoted diff) +2 -0
+
+The Interception Parameters state description field still contains

the +the bytes of the instruction text but with pre-set register +values. I.e. each instruction always uses the same instruction

Quoted 8 lines

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 75

Quoted 5 lines

text, +to not leak guest instruction text.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+The Secure Instruction Data Area contains instruction storage
+data. Data for diag 500 is exempt from that and has to be moved
+through shared buffers to KVM.    

I find this paragraph a bit confusing. What does that imply for diag 500 interception? Data is still present in gprs 1-4?

Quoted 2 lines

no registers are leaked in the registers. registers are always only exposed through the state description.

So, what is so special about diag 500, then?

Quoted 32 lines
Quoted 31 lines

(Also, why only diag 500? Because it is the 'reserved for kvm' diagnose call?)

Quoted 17 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+
+When SIE intercepts an instruction, it will only allow data and
+program interrupts for this instruction to be moved to the guest

via +the two data areas discussed before. Other data is ignored or results +in validity interceptions.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+
+Instruction emulation interceptions:
+There are two types of SIE secure instruction intercepts. The

normal +and the notification type. Normal secure instruction intercepts will +make the guest pending for instruction completion of the intercepted +instruction type, i.e. on SIE entry it is attempted to complete +emulation of the instruction with the data provided by KVM. That might +be a program exception or instruction completion. +

(quoted diff) +2 -0
+The notification type intercepts inform KVM about guest environment
+changes due to guest instruction interpretation. Such an
Quoted 1 line

interception

'interpretation by SIE' ?

Quoted 5 lines
(quoted diff) +2 -0
+is recognized for the store prefix instruction and provides the new
+lowcore location for mapping change notification arming. Any KVM

data +in the data areas is ignored, program exceptions are not injected and +execution continues on next SIE entry, as if no intercept had +happened.

author maintainer
#130 lore raw

Re: [RFC 17/37] DOCUMENTATION: protvirt: Instruction emulation

Quoted 75 lines · Cornelia Huck
Quoted 40 lines · On Thu, 14 Nov 2019 16:20:24 +0100 Claudio Imbrenda <[email protected]>…
Quoted 37 lines · On Thu, 14 Nov 2019 16:15:26 +0100 Cornelia Huck <[email protected]> wrote:
Quoted 22 lines · On Thu, 24 Oct 2019 07:40:39 -0400 Janosch Frank <[email protected]> wrote:

As guest memory is inaccessible and information about the guest's state is very limited, new ways for instruction emulation have been introduced.

With a bounce area for guest GRs and instruction data, guest state leaks can be limited by the Ultravisor. KVM now has to move instruction input and output through these areas.

---
 Documentation/virtual/kvm/s390-pv.txt | 47

+++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)

(quoted diff) +0 -0

b/Documentation/virtual/kvm/s390-pv.txt index e09f2dc5f164..cb08d78a7922 100644 --- a/Documentation/virtual/kvm/s390-pv.txt +++ b/Documentation/virtual/kvm/s390-pv.txt @@ -48,3 +48,50 @@ interception codes have been introduced. One which tells us that CRs have changed. And one for PSW bit 13 changes. The CRs and the PSW in the state description only contain the mask bits and no further info like the current instruction address. +

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 54

Quoted 3 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+
+Instruction emulation:
+With the format 4 state description the SIE instruction already    

s/description/description,/

Quoted 3 lines

+interprets more instructions than it does with format 2. As it is not +able to interpret all instruction, the SIE and the UV safeguard KVM's

s/instruction/instructions/

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+emulation inputs and outputs.
+
+Guest GRs and most of the instruction data, like IO data

structures

Hm, what 'IO data structures'?

the various IRB and ORB of I/O instructions

Would be good to mention them as examples :)

Quoted 21 lines
Quoted 20 lines
Quoted 13 lines
Documentation/virtual/kvm/s390-pv.txt +5 -0
+are filtered. Instruction data is copied to and from the Secure
+Instruction Data Area. Guest GRs are put into / retrieved from the
+Interception-Data block.
+
+The Interception-Data block from the state description's offset

0x380 +contains GRs 0 - 16. Only GR values needed to emulate an instruction +will be copied into this area.

(quoted diff) +2 -0
+
+The Interception Parameters state description field still contains

the +the bytes of the instruction text but with pre-set register +values. I.e. each instruction always uses the same instruction text, +to not leak guest instruction text.

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 75

Quoted 4 lines
Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+The Secure Instruction Data Area contains instruction storage
+data. Data for diag 500 is exempt from that and has to be moved
+through shared buffers to KVM.    

I find this paragraph a bit confusing. What does that imply for diag 500 interception? Data is still present in gprs 1-4?

Quoted 4 lines
Quoted 2 lines

no registers are leaked in the registers. registers are always only exposed through the state description.

So, what is so special about diag 500, then?

That's mostly a confusion on my side.
The SIDAD is 4k max, so we can only move IO "management" data over it like ORBs and stuff. My intention was to point out, that the data which is to be transferred (disk contents, etc.) can't go over the SIDAD but needs to be in a shared page.

diag500 was mostly a notification mechanism without a lot of data, right?

Quoted 34 lines
Quoted 33 lines
Quoted 32 lines

(Also, why only diag 500? Because it is the 'reserved for kvm' diagnose call?)

Quoted 17 lines
Documentation/virtual/kvm/s390-pv.txt +3 -0
+
+When SIE intercepts an instruction, it will only allow data and
+program interrupts for this instruction to be moved to the guest

via +the two data areas discussed before. Other data is ignored or results +in validity interceptions.

Documentation/virtual/kvm/s390-pv.txt +4 -0
+
+
+Instruction emulation interceptions:
+There are two types of SIE secure instruction intercepts. The

normal +and the notification type. Normal secure instruction intercepts will +make the guest pending for instruction completion of the intercepted +instruction type, i.e. on SIE entry it is attempted to complete +emulation of the instruction with the data provided by KVM. That might +be a program exception or instruction completion. +

(quoted diff) +2 -0
+The notification type intercepts inform KVM about guest environment
+changes due to guest instruction interpretation. Such an
Quoted 1 line

interception

'interpretation by SIE' ?

Quoted 5 lines
(quoted diff) +2 -0
+is recognized for the store prefix instruction and provides the new
+lowcore location for mapping change notification arming. Any KVM

data +in the data areas is ignored, program exceptions are not injected and +execution continues on next SIE entry, as if no intercept had +happened.

#131 lore raw

Re: [RFC 17/37] DOCUMENTATION: protvirt: Instruction emulation

Quoted 22 lines · On Thu, 14 Nov 2019 16:55:46 +0100 Janosch Frank <[email protected]> wrote:
Quoted 16 lines · Cornelia Huck
Quoted 13 lines · On Thu, 14 Nov 2019 16:20:24 +0100 Claudio Imbrenda <[email protected]>…
Quoted 9 lines · On Thu, 14 Nov 2019 16:15:26 +0100 Cornelia Huck <[email protected]> wrote:

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 75

Quoted 3 lines · On Thu, 24 Oct 2019 07:40:39 -0400 Janosch Frank <[email protected]> wrote:
Documentation/virtual/kvm/s390-pv.txt +3 -0
+The Secure Instruction Data Area contains instruction storage
+data. Data for diag 500 is exempt from that and has to be moved
+through shared buffers to KVM.      

I find this paragraph a bit confusing. What does that imply for diag 500 interception? Data is still present in gprs 1-4?

Quoted 4 lines
Quoted 2 lines

no registers are leaked in the registers. registers are always only exposed through the state description.

So, what is so special about diag 500, then?

Quoted 7 lines

That's mostly a confusion on my side.
The SIDAD is 4k max, so we can only move IO "management" data over it like ORBs and stuff. My intention was to point out, that the data which is to be transferred (disk contents, etc.) can't go over the SIDAD but needs to be in a shared page.

diag500 was mostly a notification mechanism without a lot of data, right?

Yes; the main information in there are the schid identifying the subchannel, the virtqueue number, and a cookie value, all of which fit into the registers.

So this goes via the sidad as well?

author maintainer
#132 lore raw

Re: [RFC 17/37] DOCUMENTATION: protvirt: Instruction emulation

Quoted 31 lines · Cornelia Huck
Quoted 28 lines · On Thu, 14 Nov 2019 16:55:46 +0100 Janosch Frank <[email protected]> wrote:
Quoted 16 lines · Cornelia Huck
Quoted 13 lines · On Thu, 14 Nov 2019 16:20:24 +0100 Claudio Imbrenda <[email protected]>…
Quoted 9 lines · On Thu, 14 Nov 2019 16:15:26 +0100 Cornelia Huck <[email protected]> wrote:

Documentation/virtual/kvm/s390-pv.txt interception codes have been introduced. One which tells us that CRs · line 75

Quoted 3 lines · On Thu, 24 Oct 2019 07:40:39 -0400 Janosch Frank <[email protected]> wrote:
Documentation/virtual/kvm/s390-pv.txt +3 -0
+The Secure Instruction Data Area contains instruction storage
+data. Data for diag 500 is exempt from that and has to be moved
+through shared buffers to KVM.      

I find this paragraph a bit confusing. What does that imply for diag 500 interception? Data is still present in gprs 1-4?

Quoted 4 lines
Quoted 2 lines

no registers are leaked in the registers. registers are always only exposed through the state description.

So, what is so special about diag 500, then?

That's mostly a confusion on my side.
The SIDAD is 4k max, so we can only move IO "management" data over it like ORBs and stuff. My intention was to point out, that the data which is to be transferred (disk contents, etc.) can't go over the SIDAD but needs to be in a shared page.

Quoted 8 lines
Quoted 1 line

diag500 was mostly a notification mechanism without a lot of data, right?

Yes; the main information in there are the schid identifying the subchannel, the virtqueue number, and a cookie value, all of which fit into the registers.

So this goes via the sidad as well?

Only referenced data goes over the SIDA, register values go into offset 0x380 of the SIE state description.

If an instruction has an address in a register, we will receive a bogus address and the referenced data in the SIDA.

SCLP has a code and an address as register values. We will get the code and a bogus address in the register area. The SCCB will be in the SIDA.

author maintainer
#133 lore raw

RFC 18/37 KVM: s390: protvirt: Handle spec exception loops

In reply to Janosch Frank

SIE intercept code 8 is used only on exception loops for protected guests. That means we need stop the guest when we see it.

---
 arch/s390/kvm/intercept.c | 7 +++++++
 1 file changed, 7 insertions(+)
arch/s390/kvm/intercept.c +7 -0
index acc1710fc472..b013a9c88d43 100644
@@ -231,6 +231,13 @@ static int handle_prog(struct kvm_vcpu *vcpu)
 
 	vcpu->stat.exit_program_interruption++;
 
+	/*
+	 * Intercept 8 indicates a loop of specification exceptions
+	 * for protected guests
+	 */
+	if (kvm_s390_pv_is_protected(vcpu->kvm))
+		return -EOPNOTSUPP;
+
 	if (guestdbg_enabled(vcpu) && per_event(vcpu)) {
 		rc = kvm_s390_handle_per_event(vcpu);
 		if (rc)
Signature · 1 line
2.20.1
Reviewed-by on v1 18/37
#134 lore raw

Re: [RFC 18/37] KVM: s390: protvirt: Handle spec exception loops

Quoted 16 lines · Janosch Frank

SIE intercept code 8 is used only on exception loops for protected guests. That means we need stop the guest when we see it.

---
 arch/s390/kvm/intercept.c | 7 +++++++
 1 file changed, 7 insertions(+)
arch/s390/kvm/intercept.c +0 -0
index acc1710fc472..b013a9c88d43 100644
@@ -231,6 +231,13 @@ static int handle_prog(struct kvm_vcpu *vcpu)
 
 	vcpu->stat.exit_program_interruption++;
 

arch/s390/kvm/intercept.c static int handle_prog(struct kvm_vcpu *vcpu) · line 243

Quoted 10 lines
arch/s390/kvm/intercept.c +7 -0
+	/*
+	 * Intercept 8 indicates a loop of specification exceptions
+	 * for protected guests
+	 */
+	if (kvm_s390_pv_is_protected(vcpu->kvm))
+		return -EOPNOTSUPP;
+
 	if (guestdbg_enabled(vcpu) && per_event(vcpu)) {
 		rc = kvm_s390_handle_per_event(vcpu);
 		if (rc)
author maintainer
#135 lore raw

RFC 19/37 KVM: s390: protvirt: Add new gprs location handling

In reply to Janosch Frank

Guest registers for protected guests are stored at offset 0x380.

---
 arch/s390/include/asm/kvm_host.h |  4 +++-
 arch/s390/kvm/kvm-s390.c         | 11 +++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)
arch/s390/include/asm/kvm_host.h +3 -1
index 0ab309b7bf4c..5deabf9734d9 100644
@@ -336,7 +336,9 @@ struct kvm_s390_itdb {
 struct sie_page {
 	struct kvm_s390_sie_block sie_block;
 	struct mcck_volatile_info mcck_info;	/* 0x0200 */
-	__u8 reserved218[1000];		/* 0x0218 */
+	__u8 reserved218[360];		/* 0x0218 */
+	__u64 pv_grregs[16];		/* 0x380 */
+	__u8 reserved400[512];
 	struct kvm_s390_itdb itdb;	/* 0x0600 */
 	__u8 reserved700[2304];		/* 0x0700 */
 };
arch/s390/kvm/kvm-s390.c +11 -0
index 490fde080107..97d3a81e5074 100644
@@ -3965,6 +3965,7 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
 static int __vcpu_run(struct kvm_vcpu *vcpu)
 {
 	int rc, exit_reason;
+	struct sie_page *sie_page = (struct sie_page *)vcpu->arch.sie_block;
 
 	/*
 	 * We try to hold kvm->srcu during most of vcpu_run (except when run-
@@ -3986,8 +3987,18 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
 		guest_enter_irqoff();
 		__disable_cpu_timer_accounting(vcpu);
 		local_irq_enable();
+		if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+			memcpy(sie_page->pv_grregs,
+			       vcpu->run->s.regs.gprs,
+			       sizeof(sie_page->pv_grregs));
+		}
 		exit_reason = sie64a(vcpu->arch.sie_block,
 				     vcpu->run->s.regs.gprs);
+		if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+			memcpy(vcpu->run->s.regs.gprs,
+			       sie_page->pv_grregs,
+			       sizeof(sie_page->pv_grregs));
+		}
 		local_irq_disable();
 		__enable_cpu_timer_accounting(vcpu);
 		guest_exit_irqoff();
Signature · 1 line
2.20.1
#136 lore raw

Re: [RFC 19/37] KVM: s390: protvirt: Add new gprs location handling

Quoted 41 lines · Janosch Frank

Guest registers for protected guests are stored at offset 0x380.

---
  arch/s390/include/asm/kvm_host.h |  4 +++-
  arch/s390/kvm/kvm-s390.c         | 11 +++++++++++
  2 files changed, 14 insertions(+), 1 deletion(-)
arch/s390/include/asm/kvm_host.h +3 -1
index 0ab309b7bf4c..5deabf9734d9 100644
@@ -336,7 +336,9 @@ struct kvm_s390_itdb {
  struct sie_page {
  	struct kvm_s390_sie_block sie_block;
  	struct mcck_volatile_info mcck_info;	/* 0x0200 */
-	__u8 reserved218[1000];		/* 0x0218 */
+	__u8 reserved218[360];		/* 0x0218 */
+	__u64 pv_grregs[16];		/* 0x380 */
+	__u8 reserved400[512];
  	struct kvm_s390_itdb itdb;	/* 0x0600 */
  	__u8 reserved700[2304];		/* 0x0700 */
  };
arch/s390/kvm/kvm-s390.c +3 -0
index 490fde080107..97d3a81e5074 100644
@@ -3965,6 +3965,7 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
  static int __vcpu_run(struct kvm_vcpu *vcpu)
  {
  	int rc, exit_reason;
+	struct sie_page *sie_page = (struct sie_page *)vcpu->arch.sie_block;
 
  	/*
  	 * We try to hold kvm->srcu during most of vcpu_run (except when run-
@@ -3986,8 +3987,18 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
  		guest_enter_irqoff();
  		__disable_cpu_timer_accounting(vcpu);
  		local_irq_enable();
+		if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+			memcpy(sie_page->pv_grregs,

arch/s390/kvm/kvm-s390.c static int __vcpu_run(struct kvm_vcpu *vcpu) · line 4001

Quoted 10 lines
arch/s390/kvm/kvm-s390.c +8 -0
+			       vcpu->run->s.regs.gprs,
+			       sizeof(sie_page->pv_grregs));
+		}
  		exit_reason = sie64a(vcpu->arch.sie_block,
  				     vcpu->run->s.regs.gprs);
+		if (kvm_s390_pv_is_protected(vcpu->kvm)) {
+			memcpy(vcpu->run->s.regs.gprs,
+			       sie_page->pv_grregs,
+			       sizeof(sie_page->pv_grregs));
+		}

sie64a will load/save gprs 0-13 from to vcpu->run->s.regs.gprs.

I would have assume that this is not required for prot virt, because the HW has direct access via the sie block?

1. Would it make sense to have a specialized sie64a() (or a parameter, e.g., if you pass in NULL in r3), that optimizes this loading/saving? Eventually we can also optimize which host registers to save/restore then.

2. Avoid this copying here. We have to store the state to vcpu->run->s.regs.gprs when returning to user space and restore the state when coming from user space.

Also, we access the GPRS from interception handlers, there we might use wrappers like

kvm_s390_set_gprs()
kvm_s390_get_gprs()

to route to the right location. There are multiple options to optimize this.

Signature · 3 lines
Thanks,

David / dhildenb