KVM: PPC: Book3S HV: Implement H_SVM_INIT_ABORT hcall

Implement the H_SVM_INIT_ABORT hcall which the Ultravisor can use to
abort an SVM after it has issued the H_SVM_INIT_START and before the
H_SVM_INIT_DONE hcalls. This hcall could be used when Ultravisor
encounters security violations or other errors when starting an SVM.

Note that this hcall is different from UV_SVM_TERMINATE ucall which
is used by HV to terminate/cleanup an VM that has becore secure.

The H_SVM_INIT_ABORT basically undoes operations that were done
since the H_SVM_INIT_START hcall - i.e page-out all the VM pages back
to normal memory, and terminate the SVM.

(If we do not bring the pages back to normal memory, the text/data
of the VM would be stuck in secure memory and since the SVM did not
go secure, its MSR_S bit will be clear and the VM wont be able to
access its pages even to do a clean exit).

Based on patches and discussion with Paul Mackerras, Ram Pai and
Bharata Rao.

Signed-off-by: Ram Pai <linuxram@linux.ibm.com>
Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.ibm.com>
Signed-off-by: Bharata B Rao <bharata@linux.ibm.com>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
This commit is contained in:
Sukadev Bhattiprolu 2020-01-06 18:02:37 -08:00 committed by Paul Mackerras
parent ce477a7a1c
commit 3a43970d55
6 changed files with 99 additions and 0 deletions

View File

@ -948,6 +948,66 @@ Use cases
up its internal state for this virtual machine.
H_SVM_INIT_ABORT
----------------
Abort the process of securing an SVM.
Syntax
~~~~~~
.. code-block:: c
uint64_t hypercall(const uint64_t H_SVM_INIT_ABORT)
Return values
~~~~~~~~~~~~~
One of the following values:
* H_PARAMETER on successfully cleaning up the state,
Hypervisor will return this value to the
**guest**, to indicate that the underlying
UV_ESM ultracall failed.
* H_STATE if called after a VM has gone secure (i.e
H_SVM_INIT_DONE hypercall was successful).
* H_UNSUPPORTED if called from a wrong context (e.g. from a
normal VM).
Description
~~~~~~~~~~~
Abort the process of securing a virtual machine. This call must
be made after a prior call to ``H_SVM_INIT_START`` hypercall and
before a call to ``H_SVM_INIT_DONE``.
On entry into this hypercall the non-volatile GPRs and FPRs are
expected to contain the values they had at the time the VM issued
the UV_ESM ultracall. Further ``SRR0`` is expected to contain the
address of the instruction after the ``UV_ESM`` ultracall and ``SRR1``
the MSR value with which to return to the VM.
This hypercall will cleanup any partial state that was established for
the VM since the prior ``H_SVM_INIT_START`` hypercall, including paging
out pages that were paged-into secure memory, and issue the
``UV_SVM_TERMINATE`` ultracall to terminate the VM.
After the partial state is cleaned up, control returns to the VM
(**not Ultravisor**), at the address specified in ``SRR0`` with the
MSR values set to the value in ``SRR1``.
Use cases
~~~~~~~~~
If after a successful call to ``H_SVM_INIT_START``, the Ultravisor
encounters an error while securing a virtual machine, either due
to lack of resources or because the VM's security information could
not be validated, Ultravisor informs the Hypervisor about it.
Hypervisor should use this call to clean up any internal state for
this virtual machine and return to the VM.
H_SVM_PAGE_IN
-------------

View File

@ -350,6 +350,7 @@
#define H_SVM_PAGE_OUT 0xEF04
#define H_SVM_INIT_START 0xEF08
#define H_SVM_INIT_DONE 0xEF0C
#define H_SVM_INIT_ABORT 0xEF14
/* Values for 2nd argument to H_SET_MODE */
#define H_SET_MODE_RESOURCE_SET_CIABR 1

View File

@ -19,6 +19,7 @@ unsigned long kvmppc_h_svm_page_out(struct kvm *kvm,
unsigned long kvmppc_h_svm_init_start(struct kvm *kvm);
unsigned long kvmppc_h_svm_init_done(struct kvm *kvm);
int kvmppc_send_page_to_uv(struct kvm *kvm, unsigned long gfn);
unsigned long kvmppc_h_svm_init_abort(struct kvm *kvm);
void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *free,
struct kvm *kvm, bool skip_page_out);
#else
@ -62,6 +63,11 @@ static inline unsigned long kvmppc_h_svm_init_done(struct kvm *kvm)
return H_UNSUPPORTED;
}
static inline unsigned long kvmppc_h_svm_init_abort(struct kvm *kvm)
{
return H_UNSUPPORTED;
}
static inline int kvmppc_send_page_to_uv(struct kvm *kvm, unsigned long gfn)
{
return -EFAULT;

View File

@ -278,6 +278,7 @@ struct kvm_resize_hpt;
/* Flag values for kvm_arch.secure_guest */
#define KVMPPC_SECURE_INIT_START 0x1 /* H_SVM_INIT_START has been called */
#define KVMPPC_SECURE_INIT_DONE 0x2 /* H_SVM_INIT_DONE completed */
#define KVMPPC_SECURE_INIT_ABORT 0x4 /* H_SVM_INIT_ABORT issued */
struct kvm_arch {
unsigned int lpid;

View File

@ -1091,6 +1091,9 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
case H_SVM_INIT_DONE:
ret = kvmppc_h_svm_init_done(vcpu->kvm);
break;
case H_SVM_INIT_ABORT:
ret = kvmppc_h_svm_init_abort(vcpu->kvm);
break;
default:
return RESUME_HOST;

View File

@ -286,6 +286,34 @@ void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *free,
}
}
unsigned long kvmppc_h_svm_init_abort(struct kvm *kvm)
{
int srcu_idx;
struct kvm_memory_slot *memslot;
/*
* Expect to be called only after INIT_START and before INIT_DONE.
* If INIT_DONE was completed, use normal VM termination sequence.
*/
if (!(kvm->arch.secure_guest & KVMPPC_SECURE_INIT_START))
return H_UNSUPPORTED;
if (kvm->arch.secure_guest & KVMPPC_SECURE_INIT_DONE)
return H_STATE;
srcu_idx = srcu_read_lock(&kvm->srcu);
kvm_for_each_memslot(memslot, kvm_memslots(kvm))
kvmppc_uvmem_drop_pages(memslot, kvm, false);
srcu_read_unlock(&kvm->srcu, srcu_idx);
kvm->arch.secure_guest = 0;
uv_svm_terminate(kvm->arch.lpid);
return H_PARAMETER;
}
/*
* Get a free device PFN from the pool
*