Skip to content

Commit

Permalink
KVM: x86: Modify struct kvm_nested_state to have explicit fields for …
Browse files Browse the repository at this point in the history
…data

Improve the KVM_{GET,SET}_NESTED_STATE structs by detailing the format
of VMX nested state data in a struct.

In order to avoid changing the ioctl values of
KVM_{GET,SET}_NESTED_STATE, there is a need to preserve
sizeof(struct kvm_nested_state). This is done by defining the data
struct as "data.vmx[0]". It was the most elegant way I found to
preserve struct size while still keeping struct readable and easy to
maintain. It does have a misfortunate side-effect that now it has to be
accessed as "data.vmx[0]" rather than just "data.vmx".

Because we are already modifying these structs, I also modified the
following:
* Define the "format" field values as macros.
* Rename vmcs_pa to vmcs12_pa for better readability.

Signed-off-by: Liran Alon <liran.alon@oracle.com>
[Remove SVM stubs, add KVM_STATE_NESTED_VMX_VMCS12_SIZE. - Paolo]
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Liran Alon authored and bonzini committed Jun 19, 2019
1 parent 76e3bcd commit 6ca00df
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 85 deletions.
46 changes: 31 additions & 15 deletions Documentation/virtual/kvm/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3857,43 +3857,59 @@ Type: vcpu ioctl
Parameters: struct kvm_nested_state (in/out)
Returns: 0 on success, -1 on error
Errors:
E2BIG: the total state size (including the fixed-size part of struct
kvm_nested_state) exceeds the value of 'size' specified by
E2BIG: the total state size exceeds the value of 'size' specified by
the user; the size required will be written into size.

struct kvm_nested_state {
__u16 flags;
__u16 format;
__u32 size;

union {
struct kvm_vmx_nested_state vmx;
struct kvm_svm_nested_state svm;
struct kvm_vmx_nested_state_hdr vmx;
struct kvm_svm_nested_state_hdr svm;

/* Pad the header to 128 bytes. */
__u8 pad[120];
};
__u8 data[0];
} hdr;

union {
struct kvm_vmx_nested_state_data vmx[0];
struct kvm_svm_nested_state_data svm[0];
} data;
};

#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
#define KVM_STATE_NESTED_EVMCS 0x00000004

#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002
#define KVM_STATE_NESTED_FORMAT_VMX 0
#define KVM_STATE_NESTED_FORMAT_SVM 1

struct kvm_vmx_nested_state {
#define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000

#define KVM_STATE_NESTED_VMX_SMM_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_VMX_SMM_VMXON 0x00000002

struct kvm_vmx_nested_state_hdr {
__u64 vmxon_pa;
__u64 vmcs_pa;
__u64 vmcs12_pa;

struct {
__u16 flags;
} smm;
};

struct kvm_vmx_nested_state_data {
__u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
__u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
};

This ioctl copies the vcpu's nested virtualization state from the kernel to
userspace.

The maximum size of the state, including the fixed-size part of struct
kvm_nested_state, can be retrieved by passing KVM_CAP_NESTED_STATE to
the KVM_CHECK_EXTENSION ioctl().
The maximum size of the state can be retrieved by passing KVM_CAP_NESTED_STATE
to the KVM_CHECK_EXTENSION ioctl().

4.115 KVM_SET_NESTED_STATE

Expand All @@ -3903,8 +3919,8 @@ Type: vcpu ioctl
Parameters: struct kvm_nested_state (in)
Returns: 0 on success, -1 on error

This copies the vcpu's kvm_nested_state struct from userspace to the kernel. For
the definition of struct kvm_nested_state, see KVM_GET_NESTED_STATE.
This copies the vcpu's kvm_nested_state struct from userspace to the kernel.
For the definition of struct kvm_nested_state, see KVM_GET_NESTED_STATE.

4.116 KVM_(UN)REGISTER_COALESCED_MMIO

Expand Down
33 changes: 22 additions & 11 deletions arch/x86/include/uapi/asm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,16 +383,26 @@ struct kvm_sync_regs {
#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2)
#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3)

#define KVM_STATE_NESTED_FORMAT_VMX 0
#define KVM_STATE_NESTED_FORMAT_SVM 1 /* unused */

#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
#define KVM_STATE_NESTED_EVMCS 0x00000004

#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002

struct kvm_vmx_nested_state {
#define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000

struct kvm_vmx_nested_state_data {
__u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
__u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
};

struct kvm_vmx_nested_state_hdr {
__u64 vmxon_pa;
__u64 vmcs_pa;
__u64 vmcs12_pa;

struct {
__u16 flags;
Expand All @@ -401,24 +411,25 @@ struct kvm_vmx_nested_state {

/* for KVM_CAP_NESTED_STATE */
struct kvm_nested_state {
/* KVM_STATE_* flags */
__u16 flags;

/* 0 for VMX, 1 for SVM. */
__u16 format;

/* 128 for SVM, 128 + VMCS size for VMX. */
__u32 size;

union {
/* VMXON, VMCS */
struct kvm_vmx_nested_state vmx;
struct kvm_vmx_nested_state_hdr vmx;

/* Pad the header to 128 bytes. */
__u8 pad[120];
};
} hdr;

__u8 data[0];
/*
* Define data region as 0 bytes to preserve backwards-compatability
* to old definition of kvm_nested_state in order to avoid changing
* KVM_{GET,PUT}_NESTED_STATE ioctl values.
*/
union {
struct kvm_vmx_nested_state_data vmx[0];
} data;
};

#endif /* _ASM_X86_KVM_H */
79 changes: 44 additions & 35 deletions arch/x86/kvm/vmx/nested.c
Original file line number Diff line number Diff line change
Expand Up @@ -5226,14 +5226,16 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,
struct vmcs12 *vmcs12;
struct kvm_nested_state kvm_state = {
.flags = 0,
.format = 0,
.format = KVM_STATE_NESTED_FORMAT_VMX,
.size = sizeof(kvm_state),
.vmx.vmxon_pa = -1ull,
.vmx.vmcs_pa = -1ull,
.hdr.vmx.vmxon_pa = -1ull,
.hdr.vmx.vmcs12_pa = -1ull,
};
struct kvm_vmx_nested_state_data __user *user_vmx_nested_state =
&user_kvm_nested_state->data.vmx[0];

if (!vcpu)
return kvm_state.size + 2 * VMCS12_SIZE;
return kvm_state.size + sizeof(*user_vmx_nested_state);

vmx = to_vmx(vcpu);
vmcs12 = get_vmcs12(vcpu);
Expand All @@ -5243,23 +5245,23 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,

if (nested_vmx_allowed(vcpu) &&
(vmx->nested.vmxon || vmx->nested.smm.vmxon)) {
kvm_state.vmx.vmxon_pa = vmx->nested.vmxon_ptr;
kvm_state.vmx.vmcs_pa = vmx->nested.current_vmptr;
kvm_state.hdr.vmx.vmxon_pa = vmx->nested.vmxon_ptr;
kvm_state.hdr.vmx.vmcs12_pa = vmx->nested.current_vmptr;

if (vmx_has_valid_vmcs12(vcpu)) {
kvm_state.size += VMCS12_SIZE;
kvm_state.size += sizeof(user_vmx_nested_state->vmcs12);

if (is_guest_mode(vcpu) &&
nested_cpu_has_shadow_vmcs(vmcs12) &&
vmcs12->vmcs_link_pointer != -1ull)
kvm_state.size += VMCS12_SIZE;
kvm_state.size += sizeof(user_vmx_nested_state->shadow_vmcs12);
}

if (vmx->nested.smm.vmxon)
kvm_state.vmx.smm.flags |= KVM_STATE_NESTED_SMM_VMXON;
kvm_state.hdr.vmx.smm.flags |= KVM_STATE_NESTED_SMM_VMXON;

if (vmx->nested.smm.guest_mode)
kvm_state.vmx.smm.flags |= KVM_STATE_NESTED_SMM_GUEST_MODE;
kvm_state.hdr.vmx.smm.flags |= KVM_STATE_NESTED_SMM_GUEST_MODE;

if (is_guest_mode(vcpu)) {
kvm_state.flags |= KVM_STATE_NESTED_GUEST_MODE;
Expand Down Expand Up @@ -5294,16 +5296,19 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,
copy_shadow_to_vmcs12(vmx);
}

BUILD_BUG_ON(sizeof(user_vmx_nested_state->vmcs12) < VMCS12_SIZE);
BUILD_BUG_ON(sizeof(user_vmx_nested_state->shadow_vmcs12) < VMCS12_SIZE);

/*
* Copy over the full allocated size of vmcs12 rather than just the size
* of the struct.
*/
if (copy_to_user(user_kvm_nested_state->data, vmcs12, VMCS12_SIZE))
if (copy_to_user(user_vmx_nested_state->vmcs12, vmcs12, VMCS12_SIZE))
return -EFAULT;

if (nested_cpu_has_shadow_vmcs(vmcs12) &&
vmcs12->vmcs_link_pointer != -1ull) {
if (copy_to_user(user_kvm_nested_state->data + VMCS12_SIZE,
if (copy_to_user(user_vmx_nested_state->shadow_vmcs12,
get_shadow_vmcs12(vcpu), VMCS12_SIZE))
return -EFAULT;
}
Expand Down Expand Up @@ -5331,33 +5336,35 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
struct vcpu_vmx *vmx = to_vmx(vcpu);
struct vmcs12 *vmcs12;
u32 exit_qual;
struct kvm_vmx_nested_state_data __user *user_vmx_nested_state =
&user_kvm_nested_state->data.vmx[0];
int ret;

if (kvm_state->format != 0)
if (kvm_state->format != KVM_STATE_NESTED_FORMAT_VMX)
return -EINVAL;

if (!nested_vmx_allowed(vcpu))
return kvm_state->vmx.vmxon_pa == -1ull ? 0 : -EINVAL;
return kvm_state->hdr.vmx.vmxon_pa == -1ull ? 0 : -EINVAL;

if (kvm_state->vmx.vmxon_pa == -1ull) {
if (kvm_state->vmx.smm.flags)
if (kvm_state->hdr.vmx.vmxon_pa == -1ull) {
if (kvm_state->hdr.vmx.smm.flags)
return -EINVAL;

if (kvm_state->vmx.vmcs_pa != -1ull)
if (kvm_state->hdr.vmx.vmcs12_pa != -1ull)
return -EINVAL;

vmx_leave_nested(vcpu);
return 0;
}

if (!page_address_valid(vcpu, kvm_state->vmx.vmxon_pa))
if (!page_address_valid(vcpu, kvm_state->hdr.vmx.vmxon_pa))
return -EINVAL;

if ((kvm_state->vmx.smm.flags & KVM_STATE_NESTED_SMM_GUEST_MODE) &&
if ((kvm_state->hdr.vmx.smm.flags & KVM_STATE_NESTED_SMM_GUEST_MODE) &&
(kvm_state->flags & KVM_STATE_NESTED_GUEST_MODE))
return -EINVAL;

if (kvm_state->vmx.smm.flags &
if (kvm_state->hdr.vmx.smm.flags &
~(KVM_STATE_NESTED_SMM_GUEST_MODE | KVM_STATE_NESTED_SMM_VMXON))
return -EINVAL;

Expand All @@ -5366,21 +5373,21 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
* nor can VMLAUNCH/VMRESUME be pending. Outside SMM, SMM flags
* must be zero.
*/
if (is_smm(vcpu) ? kvm_state->flags : kvm_state->vmx.smm.flags)
if (is_smm(vcpu) ? kvm_state->flags : kvm_state->hdr.vmx.smm.flags)
return -EINVAL;

if ((kvm_state->vmx.smm.flags & KVM_STATE_NESTED_SMM_GUEST_MODE) &&
!(kvm_state->vmx.smm.flags & KVM_STATE_NESTED_SMM_VMXON))
if ((kvm_state->hdr.vmx.smm.flags & KVM_STATE_NESTED_SMM_GUEST_MODE) &&
!(kvm_state->hdr.vmx.smm.flags & KVM_STATE_NESTED_SMM_VMXON))
return -EINVAL;

vmx_leave_nested(vcpu);
if (kvm_state->vmx.vmxon_pa == -1ull)
if (kvm_state->hdr.vmx.vmxon_pa == -1ull)
return 0;

if (kvm_state->flags & KVM_STATE_NESTED_EVMCS)
nested_enable_evmcs(vcpu, NULL);

vmx->nested.vmxon_ptr = kvm_state->vmx.vmxon_pa;
vmx->nested.vmxon_ptr = kvm_state->hdr.vmx.vmxon_pa;
ret = enter_vmx_operation(vcpu);
if (ret)
return ret;
Expand All @@ -5389,12 +5396,12 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
if (kvm_state->size < sizeof(*kvm_state) + sizeof(*vmcs12))
return 0;

if (kvm_state->vmx.vmcs_pa != -1ull) {
if (kvm_state->vmx.vmcs_pa == kvm_state->vmx.vmxon_pa ||
!page_address_valid(vcpu, kvm_state->vmx.vmcs_pa))
if (kvm_state->hdr.vmx.vmcs12_pa != -1ull) {
if (kvm_state->hdr.vmx.vmcs12_pa == kvm_state->hdr.vmx.vmxon_pa ||
!page_address_valid(vcpu, kvm_state->hdr.vmx.vmcs12_pa))
return -EINVAL;

set_current_vmptr(vmx, kvm_state->vmx.vmcs_pa);
set_current_vmptr(vmx, kvm_state->hdr.vmx.vmcs12_pa);
} else if (kvm_state->flags & KVM_STATE_NESTED_EVMCS) {
/*
* Sync eVMCS upon entry as we may not have
Expand All @@ -5405,16 +5412,16 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
return -EINVAL;
}

if (kvm_state->vmx.smm.flags & KVM_STATE_NESTED_SMM_VMXON) {
if (kvm_state->hdr.vmx.smm.flags & KVM_STATE_NESTED_SMM_VMXON) {
vmx->nested.smm.vmxon = true;
vmx->nested.vmxon = false;

if (kvm_state->vmx.smm.flags & KVM_STATE_NESTED_SMM_GUEST_MODE)
if (kvm_state->hdr.vmx.smm.flags & KVM_STATE_NESTED_SMM_GUEST_MODE)
vmx->nested.smm.guest_mode = true;
}

vmcs12 = get_vmcs12(vcpu);
if (copy_from_user(vmcs12, user_kvm_nested_state->data, sizeof(*vmcs12)))
if (copy_from_user(vmcs12, user_vmx_nested_state->vmcs12, sizeof(*vmcs12)))
return -EFAULT;

if (vmcs12->hdr.revision_id != VMCS12_REVISION)
Expand All @@ -5431,12 +5438,14 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
vmcs12->vmcs_link_pointer != -1ull) {
struct vmcs12 *shadow_vmcs12 = get_shadow_vmcs12(vcpu);

if (kvm_state->size < sizeof(*kvm_state) + VMCS12_SIZE + sizeof(*vmcs12))
if (kvm_state->size <
sizeof(*kvm_state) +
sizeof(user_vmx_nested_state->vmcs12) + sizeof(*shadow_vmcs12))
goto error_guest_mode;

if (copy_from_user(shadow_vmcs12,
user_kvm_nested_state->data + VMCS12_SIZE,
sizeof(*vmcs12))) {
user_vmx_nested_state->shadow_vmcs12,
sizeof(*shadow_vmcs12))) {
ret = -EFAULT;
goto error_guest_mode;
}
Expand Down
5 changes: 3 additions & 2 deletions arch/x86/kvm/vmx/vmcs12.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,10 @@ struct __packed vmcs12 {
/*
* VMCS12_SIZE is the number of bytes L1 should allocate for the VMXON region
* and any VMCS region. Although only sizeof(struct vmcs12) are used by the
* current implementation, 4K are reserved to avoid future complications.
* current implementation, 4K are reserved to avoid future complications and
* to preserve userspace ABI.
*/
#define VMCS12_SIZE 0x1000
#define VMCS12_SIZE KVM_STATE_NESTED_VMX_VMCS_SIZE

/*
* VMCS12_MAX_FIELD_INDEX is the highest index value used in any
Expand Down
2 changes: 1 addition & 1 deletion tools/arch/x86/include/uapi/asm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ struct kvm_sync_regs {

struct kvm_vmx_nested_state {
__u64 vmxon_pa;
__u64 vmcs_pa;
__u64 vmcs12_pa;

struct {
__u16 flags;
Expand Down
Loading

0 comments on commit 6ca00df

Please sign in to comment.