]> Git Repo - J-linux.git/commitdiff
Merge tag 'kvm-s390-next-5.19-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorPaolo Bonzini <[email protected]>
Tue, 7 Jun 2022 16:28:53 +0000 (12:28 -0400)
committerPaolo Bonzini <[email protected]>
Tue, 7 Jun 2022 16:28:53 +0000 (12:28 -0400)
KVM: s390: pvdump and selftest improvements

- add an interface to provide a hypervisor dump for secure guests
- improve selftests to show tests

1  2 
Documentation/virt/kvm/api.rst
arch/s390/kvm/kvm-s390.c

index 11e00a46c610f913455ae86df942d1e98709a0a4,0dd6d23c32ee7e915b894fe57f9db680c2b2c2a1..94d73ec52abad17bc6968cd2380cf0015a2f013c
@@@ -5127,7 -5127,15 +5127,15 @@@ into ESA mode. This reset is a superse
        __u32 reserved[3];
    };
  
- cmd values:
+ **Ultravisor return codes**
+ The Ultravisor return (reason) codes are provided by the kernel if a
+ Ultravisor call has been executed to achieve the results expected by
+ the command. Therefore they are independent of the IOCTL return
+ code. If KVM changes `rc`, its value will always be greater than 0
+ hence setting it to 0 before issuing a PV command is advised to be
+ able to detect a change of `rc`.
+ **cmd values:**
  
  KVM_PV_ENABLE
    Allocate memory and register the VM with the Ultravisor, thereby
    =====      =============================
  
  KVM_PV_DISABLE
    Deregister the VM from the Ultravisor and reclaim the memory that
    had been donated to the Ultravisor, making it usable by the kernel
    again.  All registered VCPUs are converted back to non-protected
@@@ -5160,6 -5167,117 +5167,117 @@@ KVM_PV_VM_VERIF
    Verify the integrity of the unpacked image. Only if this succeeds,
    KVM is allowed to start protected VCPUs.
  
+ KVM_PV_INFO
+   :Capability: KVM_CAP_S390_PROTECTED_DUMP
+   Presents an API that provides Ultravisor related data to userspace
+   via subcommands. len_max is the size of the user space buffer,
+   len_written is KVM's indication of how much bytes of that buffer
+   were actually written to. len_written can be used to determine the
+   valid fields if more response fields are added in the future.
+   ::
+      enum pv_cmd_info_id {
+       KVM_PV_INFO_VM,
+       KVM_PV_INFO_DUMP,
+      };
+      struct kvm_s390_pv_info_header {
+       __u32 id;
+       __u32 len_max;
+       __u32 len_written;
+       __u32 reserved;
+      };
+      struct kvm_s390_pv_info {
+       struct kvm_s390_pv_info_header header;
+       struct kvm_s390_pv_info_dump dump;
+       struct kvm_s390_pv_info_vm vm;
+      };
+ **subcommands:**
+   KVM_PV_INFO_VM
+     This subcommand provides basic Ultravisor information for PV
+     hosts. These values are likely also exported as files in the sysfs
+     firmware UV query interface but they are more easily available to
+     programs in this API.
+     The installed calls and feature_indication members provide the
+     installed UV calls and the UV's other feature indications.
+     The max_* members provide information about the maximum number of PV
+     vcpus, PV guests and PV guest memory size.
+     ::
+       struct kvm_s390_pv_info_vm {
+       __u64 inst_calls_list[4];
+       __u64 max_cpus;
+       __u64 max_guests;
+       __u64 max_guest_addr;
+       __u64 feature_indication;
+       };
+   KVM_PV_INFO_DUMP
+     This subcommand provides information related to dumping PV guests.
+     ::
+       struct kvm_s390_pv_info_dump {
+       __u64 dump_cpu_buffer_len;
+       __u64 dump_config_mem_buffer_per_1m;
+       __u64 dump_config_finalize_len;
+       };
+ KVM_PV_DUMP
+   :Capability: KVM_CAP_S390_PROTECTED_DUMP
+   Presents an API that provides calls which facilitate dumping a
+   protected VM.
+   ::
+     struct kvm_s390_pv_dmp {
+       __u64 subcmd;
+       __u64 buff_addr;
+       __u64 buff_len;
+       __u64 gaddr;            /* For dump storage state */
+     };
+   **subcommands:**
+   KVM_PV_DUMP_INIT
+     Initializes the dump process of a protected VM. If this call does
+     not succeed all other subcommands will fail with -EINVAL. This
+     subcommand will return -EINVAL if a dump process has not yet been
+     completed.
+     Not all PV vms can be dumped, the owner needs to set `dump
+     allowed` PCF bit 34 in the SE header to allow dumping.
+   KVM_PV_DUMP_CONFIG_STOR_STATE
+      Stores `buff_len` bytes of tweak component values starting with
+      the 1MB block specified by the absolute guest address
+      (`gaddr`). `buff_len` needs to be `conf_dump_storage_state_len`
+      aligned and at least >= the `conf_dump_storage_state_len` value
+      provided by the dump uv_info data. buff_user might be written to
+      even if an error rc is returned. For instance if we encounter a
+      fault after writing the first page of data.
+   KVM_PV_DUMP_COMPLETE
+     If the subcommand succeeds it completes the dump process and lets
+     KVM_PV_DUMP_INIT be called again.
+     On success `conf_dump_finalize_len` bytes of completion data will be
+     stored to the `buff_addr`. The completion data contains a key
+     derivation seed, IV, tweak nonce and encryption keys as well as an
+     authentication tag all of which are needed to decrypt the dump at a
+     later time.
  4.126 KVM_X86_SET_MSR_FILTER
  ----------------------------
  
@@@ -5802,6 -5920,32 +5920,32 @@@ of CPUID leaf 0xD on the host
  
  This ioctl injects an event channel interrupt directly to the guest vCPU.
  
+ 4.136 KVM_S390_PV_CPU_COMMAND
+ -----------------------------
+ :Capability: KVM_CAP_S390_PROTECTED_DUMP
+ :Architectures: s390
+ :Type: vcpu ioctl
+ :Parameters: none
+ :Returns: 0 on success, < 0 on error
+ This ioctl closely mirrors `KVM_S390_PV_COMMAND` but handles requests
+ for vcpus. It re-uses the kvm_s390_pv_dmp struct and hence also shares
+ the command ids.
+ **command:**
+ KVM_PV_DUMP
+   Presents an API that provides calls which facilitate dumping a vcpu
+   of a protected VM.
+ **subcommand:**
+ KVM_PV_DUMP_CPU
+   Provides encrypted dump data like register values.
+   The length of the returned data is provided by uv_info.guest_cpu_stor_len.
  5. The kvm_run structure
  ========================
  
@@@ -5869,8 -6013,6 +6013,8 @@@ affect the device's behavior. Current d
    #define KVM_RUN_X86_SMM     (1 << 0)
    /* x86, set if bus lock detected in VM */
    #define KVM_RUN_BUS_LOCK    (1 << 1)
 +  /* arm64, set for KVM_EXIT_DEBUG */
 +  #define KVM_DEBUG_ARCH_HSR_HIGH_VALID  (1 << 0)
  
  ::
  
@@@ -7956,6 -8098,20 +8100,20 @@@ should adjust CPUID leaf 0xA to reflec
  When enabled, KVM will exit to userspace with KVM_EXIT_SYSTEM_EVENT of
  type KVM_SYSTEM_EVENT_SUSPEND to process the guest suspend request.
  
+ 8.37 KVM_CAP_S390_PROTECTED_DUMP
+ --------------------------------
+ :Capability: KVM_CAP_S390_PROTECTED_DUMP
+ :Architectures: s390
+ :Type: vm
+ This capability indicates that KVM and the Ultravisor support dumping
+ PV guests. The `KVM_PV_DUMP` command is available for the
+ `KVM_S390_PV_COMMAND` ioctl and the `KVM_PV_INFO` command provides
+ dump related UV data. Also the vcpu ioctl `KVM_S390_PV_CPU_COMMAND` is
+ available and supports the `KVM_PV_DUMP_CPU` subcommand.
  9. Known KVM API problems
  =========================
  
diff --combined arch/s390/kvm/kvm-s390.c
index 8fcb561416893fa2292e00cf99ff3ec872816f91,d1a32eb3cf5d0011ad02674b3fd59ca77225cc17..ff457a77e22b80d9cb219bb0138fd5edd4ff9f05
@@@ -606,6 -606,26 +606,26 @@@ int kvm_vm_ioctl_check_extension(struc
        case KVM_CAP_S390_PROTECTED:
                r = is_prot_virt_host();
                break;
+       case KVM_CAP_S390_PROTECTED_DUMP: {
+               u64 pv_cmds_dump[] = {
+                       BIT_UVC_CMD_DUMP_INIT,
+                       BIT_UVC_CMD_DUMP_CONFIG_STOR_STATE,
+                       BIT_UVC_CMD_DUMP_CPU,
+                       BIT_UVC_CMD_DUMP_COMPLETE,
+               };
+               int i;
+               r = is_prot_virt_host();
+               for (i = 0; i < ARRAY_SIZE(pv_cmds_dump); i++) {
+                       if (!test_bit_inv(pv_cmds_dump[i],
+                                         (unsigned long *)&uv_info.inst_calls_list)) {
+                               r = 0;
+                               break;
+                       }
+               }
+               break;
+       }
        default:
                r = 0;
        }
@@@ -1332,7 -1352,8 +1352,7 @@@ static int kvm_s390_set_processor_feat(
                mutex_unlock(&kvm->lock);
                return -EBUSY;
        }
 -      bitmap_copy(kvm->arch.cpu_feat, (unsigned long *) data.feat,
 -                  KVM_S390_VM_CPU_FEAT_NR_BITS);
 +      bitmap_from_arr64(kvm->arch.cpu_feat, data.feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
        mutex_unlock(&kvm->lock);
        VM_EVENT(kvm, 3, "SET: guest feat: 0x%16.16llx.0x%16.16llx.0x%16.16llx",
                         data.feat[0],
@@@ -1503,7 -1524,8 +1523,7 @@@ static int kvm_s390_get_processor_feat(
  {
        struct kvm_s390_vm_cpu_feat data;
  
 -      bitmap_copy((unsigned long *) data.feat, kvm->arch.cpu_feat,
 -                  KVM_S390_VM_CPU_FEAT_NR_BITS);
 +      bitmap_to_arr64(data.feat, kvm->arch.cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
        if (copy_to_user((void __user *)attr->addr, &data, sizeof(data)))
                return -EFAULT;
        VM_EVENT(kvm, 3, "GET: guest feat: 0x%16.16llx.0x%16.16llx.0x%16.16llx",
@@@ -1518,7 -1540,9 +1538,7 @@@ static int kvm_s390_get_machine_feat(st
  {
        struct kvm_s390_vm_cpu_feat data;
  
 -      bitmap_copy((unsigned long *) data.feat,
 -                  kvm_s390_available_cpu_feat,
 -                  KVM_S390_VM_CPU_FEAT_NR_BITS);
 +      bitmap_to_arr64(data.feat, kvm_s390_available_cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
        if (copy_to_user((void __user *)attr->addr, &data, sizeof(data)))
                return -EFAULT;
        VM_EVENT(kvm, 3, "GET: host feat:  0x%16.16llx.0x%16.16llx.0x%16.16llx",
@@@ -2220,6 -2244,115 +2240,115 @@@ static int kvm_s390_cpus_to_pv(struct k
        return r;
  }
  
+ /*
+  * Here we provide user space with a direct interface to query UV
+  * related data like UV maxima and available features as well as
+  * feature specific data.
+  *
+  * To facilitate future extension of the data structures we'll try to
+  * write data up to the maximum requested length.
+  */
+ static ssize_t kvm_s390_handle_pv_info(struct kvm_s390_pv_info *info)
+ {
+       ssize_t len_min;
+       switch (info->header.id) {
+       case KVM_PV_INFO_VM: {
+               len_min =  sizeof(info->header) + sizeof(info->vm);
+               if (info->header.len_max < len_min)
+                       return -EINVAL;
+               memcpy(info->vm.inst_calls_list,
+                      uv_info.inst_calls_list,
+                      sizeof(uv_info.inst_calls_list));
+               /* It's max cpuid not max cpus, so it's off by one */
+               info->vm.max_cpus = uv_info.max_guest_cpu_id + 1;
+               info->vm.max_guests = uv_info.max_num_sec_conf;
+               info->vm.max_guest_addr = uv_info.max_sec_stor_addr;
+               info->vm.feature_indication = uv_info.uv_feature_indications;
+               return len_min;
+       }
+       case KVM_PV_INFO_DUMP: {
+               len_min =  sizeof(info->header) + sizeof(info->dump);
+               if (info->header.len_max < len_min)
+                       return -EINVAL;
+               info->dump.dump_cpu_buffer_len = uv_info.guest_cpu_stor_len;
+               info->dump.dump_config_mem_buffer_per_1m = uv_info.conf_dump_storage_state_len;
+               info->dump.dump_config_finalize_len = uv_info.conf_dump_finalize_len;
+               return len_min;
+       }
+       default:
+               return -EINVAL;
+       }
+ }
+ static int kvm_s390_pv_dmp(struct kvm *kvm, struct kvm_pv_cmd *cmd,
+                          struct kvm_s390_pv_dmp dmp)
+ {
+       int r = -EINVAL;
+       void __user *result_buff = (void __user *)dmp.buff_addr;
+       switch (dmp.subcmd) {
+       case KVM_PV_DUMP_INIT: {
+               if (kvm->arch.pv.dumping)
+                       break;
+               /*
+                * Block SIE entry as concurrent dump UVCs could lead
+                * to validities.
+                */
+               kvm_s390_vcpu_block_all(kvm);
+               r = uv_cmd_nodata(kvm_s390_pv_get_handle(kvm),
+                                 UVC_CMD_DUMP_INIT, &cmd->rc, &cmd->rrc);
+               KVM_UV_EVENT(kvm, 3, "PROTVIRT DUMP INIT: rc %x rrc %x",
+                            cmd->rc, cmd->rrc);
+               if (!r) {
+                       kvm->arch.pv.dumping = true;
+               } else {
+                       kvm_s390_vcpu_unblock_all(kvm);
+                       r = -EINVAL;
+               }
+               break;
+       }
+       case KVM_PV_DUMP_CONFIG_STOR_STATE: {
+               if (!kvm->arch.pv.dumping)
+                       break;
+               /*
+                * gaddr is an output parameter since we might stop
+                * early. As dmp will be copied back in our caller, we
+                * don't need to do it ourselves.
+                */
+               r = kvm_s390_pv_dump_stor_state(kvm, result_buff, &dmp.gaddr, dmp.buff_len,
+                                               &cmd->rc, &cmd->rrc);
+               break;
+       }
+       case KVM_PV_DUMP_COMPLETE: {
+               if (!kvm->arch.pv.dumping)
+                       break;
+               r = -EINVAL;
+               if (dmp.buff_len < uv_info.conf_dump_finalize_len)
+                       break;
+               r = kvm_s390_pv_dump_complete(kvm, result_buff,
+                                             &cmd->rc, &cmd->rrc);
+               break;
+       }
+       default:
+               r = -ENOTTY;
+               break;
+       }
+       return r;
+ }
  static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
  {
        int r = 0;
                             cmd->rc, cmd->rrc);
                break;
        }
+       case KVM_PV_INFO: {
+               struct kvm_s390_pv_info info = {};
+               ssize_t data_len;
+               /*
+                * No need to check the VM protection here.
+                *
+                * Maybe user space wants to query some of the data
+                * when the VM is still unprotected. If we see the
+                * need to fence a new data command we can still
+                * return an error in the info handler.
+                */
+               r = -EFAULT;
+               if (copy_from_user(&info, argp, sizeof(info.header)))
+                       break;
+               r = -EINVAL;
+               if (info.header.len_max < sizeof(info.header))
+                       break;
+               data_len = kvm_s390_handle_pv_info(&info);
+               if (data_len < 0) {
+                       r = data_len;
+                       break;
+               }
+               /*
+                * If a data command struct is extended (multiple
+                * times) this can be used to determine how much of it
+                * is valid.
+                */
+               info.header.len_written = data_len;
+               r = -EFAULT;
+               if (copy_to_user(argp, &info, data_len))
+                       break;
+               r = 0;
+               break;
+       }
+       case KVM_PV_DUMP: {
+               struct kvm_s390_pv_dmp dmp;
+               r = -EINVAL;
+               if (!kvm_s390_pv_is_protected(kvm))
+                       break;
+               r = -EFAULT;
+               if (copy_from_user(&dmp, argp, sizeof(dmp)))
+                       break;
+               r = kvm_s390_pv_dmp(kvm, cmd, dmp);
+               if (r)
+                       break;
+               if (copy_to_user(argp, &dmp, sizeof(dmp))) {
+                       r = -EFAULT;
+                       break;
+               }
+               break;
+       }
        default:
                r = -ENOTTY;
        }
@@@ -4473,6 -4668,15 +4664,15 @@@ int kvm_arch_vcpu_ioctl_run(struct kvm_
        struct kvm_run *kvm_run = vcpu->run;
        int rc;
  
+       /*
+        * Running a VM while dumping always has the potential to
+        * produce inconsistent dump data. But for PV vcpus a SIE
+        * entry while dumping could also lead to a fatal validity
+        * intercept which we absolutely want to avoid.
+        */
+       if (vcpu->kvm->arch.pv.dumping)
+               return -EINVAL;
        if (kvm_run->immediate_exit)
                return -EINTR;
  
@@@ -4912,6 -5116,48 +5112,48 @@@ long kvm_arch_vcpu_async_ioctl(struct f
        return -ENOIOCTLCMD;
  }
  
+ static int kvm_s390_handle_pv_vcpu_dump(struct kvm_vcpu *vcpu,
+                                       struct kvm_pv_cmd *cmd)
+ {
+       struct kvm_s390_pv_dmp dmp;
+       void *data;
+       int ret;
+       /* Dump initialization is a prerequisite */
+       if (!vcpu->kvm->arch.pv.dumping)
+               return -EINVAL;
+       if (copy_from_user(&dmp, (__u8 __user *)cmd->data, sizeof(dmp)))
+               return -EFAULT;
+       /* We only handle this subcmd right now */
+       if (dmp.subcmd != KVM_PV_DUMP_CPU)
+               return -EINVAL;
+       /* CPU dump length is the same as create cpu storage donation. */
+       if (dmp.buff_len != uv_info.guest_cpu_stor_len)
+               return -EINVAL;
+       data = kvzalloc(uv_info.guest_cpu_stor_len, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       ret = kvm_s390_pv_dump_cpu(vcpu, data, &cmd->rc, &cmd->rrc);
+       VCPU_EVENT(vcpu, 3, "PROTVIRT DUMP CPU %d rc %x rrc %x",
+                  vcpu->vcpu_id, cmd->rc, cmd->rrc);
+       if (ret)
+               ret = -EINVAL;
+       /* On success copy over the dump data */
+       if (!ret && copy_to_user((__u8 __user *)dmp.buff_addr, data, uv_info.guest_cpu_stor_len))
+               ret = -EFAULT;
+       kvfree(data);
+       return ret;
+ }
  long kvm_arch_vcpu_ioctl(struct file *filp,
                         unsigned int ioctl, unsigned long arg)
  {
                r = kvm_s390_get_irq_state(vcpu,
                                           (__u8 __user *)  irq_state.buf,
                                           irq_state.len);
+               break;
+       }
+       case KVM_S390_PV_CPU_COMMAND: {
+               struct kvm_pv_cmd cmd;
+               r = -EINVAL;
+               if (!is_prot_virt_host())
+                       break;
+               r = -EFAULT;
+               if (copy_from_user(&cmd, argp, sizeof(cmd)))
+                       break;
+               r = -EINVAL;
+               if (cmd.flags)
+                       break;
+               /* We only handle this cmd right now */
+               if (cmd.cmd != KVM_PV_DUMP)
+                       break;
+               r = kvm_s390_handle_pv_vcpu_dump(vcpu, &cmd);
+               /* Always copy over UV rc / rrc data */
+               if (copy_to_user((__u8 __user *)argp, &cmd.rc,
+                                sizeof(cmd.rc) + sizeof(cmd.rrc)))
+                       r = -EFAULT;
                break;
        }
        default:
This page took 0.089602 seconds and 4 git commands to generate.