Commit 19d31c5f authored by Jordan Niethe's avatar Jordan Niethe Committed by Michael Ellerman

KVM: PPC: Add support for nestedv2 guests

A series of hcalls have been added to the PAPR which allow a regular
guest partition to create and manage guest partitions of its own. KVM
already had an interface that allowed this on powernv platforms. This
existing interface will now be called "nestedv1". The newly added PAPR
interface will be called "nestedv2".  PHYP will support the nestedv2
interface. At this time the host side of the nestedv2 interface has not
been implemented on powernv but there is no technical reason why it
could not be added.

The nestedv1 interface is still supported.

Add support to KVM to utilize these hcalls to enable running nested
guests as a pseries guest on PHYP.

Overview of the new hcall usage:

- L1 and L0 negotiate capabilities with
  H_GUEST_{G,S}ET_CAPABILITIES()

- L1 requests the L0 create a L2 with
  H_GUEST_CREATE() and receives a handle to use in future hcalls

- L1 requests the L0 create a L2 vCPU with
  H_GUEST_CREATE_VCPU()

- L1 sets up the L2 using H_GUEST_SET and the
  H_GUEST_VCPU_RUN input buffer

- L1 requests the L0 runs the L2 vCPU using H_GUEST_VCPU_RUN()

- L2 returns to L1 with an exit reason and L1 reads the
  H_GUEST_VCPU_RUN output buffer populated by the L0

- L1 handles the exit using H_GET_STATE if necessary

- L1 reruns L2 vCPU with H_GUEST_VCPU_RUN

- L1 frees the L2 in the L0 with H_GUEST_DELETE()

Support for the new API is determined by trying
H_GUEST_GET_CAPABILITIES. On a successful return, use the nestedv2
interface.

Use the vcpu register state setters for tracking modified guest state
elements and copy the thread wide values into the H_GUEST_VCPU_RUN input
buffer immediately before running a L2. The guest wide
elements can not be added to the input buffer so send them with a
separate H_GUEST_SET call if necessary.

Make the vcpu register getter load the corresponding value from the real
host with H_GUEST_GET. To avoid unnecessarily calling H_GUEST_GET, track
which values have already been loaded between H_GUEST_VCPU_RUN calls. If
an element is present in the H_GUEST_VCPU_RUN output buffer it also does
not need to be loaded again.
Tested-by: default avatarSachin Sant <sachinp@linux.ibm.com>
Signed-off-by: default avatarVaibhav Jain <vaibhav@linux.ibm.com>
Signed-off-by: default avatarGautam Menghani <gautam@linux.ibm.com>
Signed-off-by: default avatarKautuk Consul <kconsul@linux.vnet.ibm.com>
Signed-off-by: default avatarAmit Machhiwal <amachhiw@linux.vnet.ibm.com>
Signed-off-by: default avatarJordan Niethe <jniethe5@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230914030600.16993-11-jniethe5@gmail.com
parent dfcaacc8
......@@ -5,6 +5,7 @@
#ifndef _ASM_POWERPC_GUEST_STATE_BUFFER_H
#define _ASM_POWERPC_GUEST_STATE_BUFFER_H
#include "asm/hvcall.h"
#include <linux/gfp.h>
#include <linux/bitmap.h>
#include <asm/plpar_wrappers.h>
......@@ -313,6 +314,8 @@ struct kvmppc_gs_buff *kvmppc_gsb_new(size_t size, unsigned long guest_id,
unsigned long vcpu_id, gfp_t flags);
void kvmppc_gsb_free(struct kvmppc_gs_buff *gsb);
void *kvmppc_gsb_put(struct kvmppc_gs_buff *gsb, size_t size);
int kvmppc_gsb_send(struct kvmppc_gs_buff *gsb, unsigned long flags);
int kvmppc_gsb_recv(struct kvmppc_gs_buff *gsb, unsigned long flags);
/**
* kvmppc_gsb_header() - the header of a guest state buffer
......@@ -901,4 +904,92 @@ static inline void kvmppc_gsm_reset(struct kvmppc_gs_msg *gsm)
kvmppc_gsbm_zero(&gsm->bitmap);
}
/**
* kvmppc_gsb_receive_data - flexibly update values from a guest state buffer
* @gsb: guest state buffer
* @gsm: guest state message
*
* Requests updated values for the guest state values included in the guest
* state message. The guest state message will then deserialize the guest state
* buffer.
*/
static inline int kvmppc_gsb_receive_data(struct kvmppc_gs_buff *gsb,
struct kvmppc_gs_msg *gsm)
{
int rc;
kvmppc_gsb_reset(gsb);
rc = kvmppc_gsm_fill_info(gsm, gsb);
if (rc < 0)
return rc;
rc = kvmppc_gsb_recv(gsb, gsm->flags);
if (rc < 0)
return rc;
rc = kvmppc_gsm_refresh_info(gsm, gsb);
if (rc < 0)
return rc;
return 0;
}
/**
* kvmppc_gsb_recv - receive a single guest state ID
* @gsb: guest state buffer
* @gsm: guest state message
* @iden: guest state identity
*/
static inline int kvmppc_gsb_receive_datum(struct kvmppc_gs_buff *gsb,
struct kvmppc_gs_msg *gsm, u16 iden)
{
int rc;
kvmppc_gsm_include(gsm, iden);
rc = kvmppc_gsb_receive_data(gsb, gsm);
if (rc < 0)
return rc;
kvmppc_gsm_reset(gsm);
return 0;
}
/**
* kvmppc_gsb_send_data - flexibly send values from a guest state buffer
* @gsb: guest state buffer
* @gsm: guest state message
*
* Sends the guest state values included in the guest state message.
*/
static inline int kvmppc_gsb_send_data(struct kvmppc_gs_buff *gsb,
struct kvmppc_gs_msg *gsm)
{
int rc;
kvmppc_gsb_reset(gsb);
rc = kvmppc_gsm_fill_info(gsm, gsb);
if (rc < 0)
return rc;
rc = kvmppc_gsb_send(gsb, gsm->flags);
return rc;
}
/**
* kvmppc_gsb_recv - send a single guest state ID
* @gsb: guest state buffer
* @gsm: guest state message
* @iden: guest state identity
*/
static inline int kvmppc_gsb_send_datum(struct kvmppc_gs_buff *gsb,
struct kvmppc_gs_msg *gsm, u16 iden)
{
int rc;
kvmppc_gsm_include(gsm, iden);
rc = kvmppc_gsb_send_data(gsb, gsm);
if (rc < 0)
return rc;
kvmppc_gsm_reset(gsm);
return 0;
}
#endif /* _ASM_POWERPC_GUEST_STATE_BUFFER_H */
......@@ -100,6 +100,18 @@
#define H_COP_HW -74
#define H_STATE -75
#define H_IN_USE -77
#define H_INVALID_ELEMENT_ID -79
#define H_INVALID_ELEMENT_SIZE -80
#define H_INVALID_ELEMENT_VALUE -81
#define H_INPUT_BUFFER_NOT_DEFINED -82
#define H_INPUT_BUFFER_TOO_SMALL -83
#define H_OUTPUT_BUFFER_NOT_DEFINED -84
#define H_OUTPUT_BUFFER_TOO_SMALL -85
#define H_PARTITION_PAGE_TABLE_NOT_DEFINED -86
#define H_GUEST_VCPU_STATE_NOT_HV_OWNED -87
#define H_UNSUPPORTED_FLAG_START -256
#define H_UNSUPPORTED_FLAG_END -511
#define H_MULTI_THREADS_ACTIVE -9005
......@@ -381,6 +393,15 @@
#define H_ENTER_NESTED 0xF804
#define H_TLB_INVALIDATE 0xF808
#define H_COPY_TOFROM_GUEST 0xF80C
#define H_GUEST_GET_CAPABILITIES 0x460
#define H_GUEST_SET_CAPABILITIES 0x464
#define H_GUEST_CREATE 0x470
#define H_GUEST_CREATE_VCPU 0x474
#define H_GUEST_GET_STATE 0x478
#define H_GUEST_SET_STATE 0x47C
#define H_GUEST_RUN_VCPU 0x480
#define H_GUEST_COPY_MEMORY 0x484
#define H_GUEST_DELETE 0x488
/* Flags for H_SVM_PAGE_IN */
#define H_PAGE_IN_SHARED 0x1
......@@ -467,6 +488,15 @@
#define H_RPTI_PAGE_1G 0x08
#define H_RPTI_PAGE_ALL (-1UL)
/* Flags for H_GUEST_{S,G}_STATE */
#define H_GUEST_FLAGS_WIDE (1UL<<(63-0))
/* Flag values used for H_{S,G}SET_GUEST_CAPABILITIES */
#define H_GUEST_CAP_COPY_MEM (1UL<<(63-0))
#define H_GUEST_CAP_POWER9 (1UL<<(63-1))
#define H_GUEST_CAP_POWER10 (1UL<<(63-2))
#define H_GUEST_CAP_BITMAP2 (1UL<<(63-63))
#ifndef __ASSEMBLY__
#include <linux/types.h>
......
......@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/kvm_host.h>
#include <asm/kvm_book3s_asm.h>
#include <asm/guest-state-buffer.h>
struct kvmppc_bat {
u64 raw;
......@@ -295,6 +296,7 @@ static inline void kvmppc_save_tm_sprs(struct kvm_vcpu *vcpu) {}
static inline void kvmppc_restore_tm_sprs(struct kvm_vcpu *vcpu) {}
#endif
extern unsigned long nested_capabilities;
long kvmhv_nested_init(void);
void kvmhv_nested_exit(void);
void kvmhv_vm_nested_init(struct kvm *kvm);
......@@ -316,6 +318,69 @@ long int kvmhv_nested_page_fault(struct kvm_vcpu *vcpu);
void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac);
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
extern struct static_key_false __kvmhv_is_nestedv2;
static inline bool kvmhv_is_nestedv2(void)
{
return static_branch_unlikely(&__kvmhv_is_nestedv2);
}
static inline bool kvmhv_is_nestedv1(void)
{
return !static_branch_likely(&__kvmhv_is_nestedv2);
}
#else
static inline bool kvmhv_is_nestedv2(void)
{
return false;
}
static inline bool kvmhv_is_nestedv1(void)
{
return false;
}
#endif
int __kvmhv_nestedv2_reload_ptregs(struct kvm_vcpu *vcpu, struct pt_regs *regs);
int __kvmhv_nestedv2_mark_dirty_ptregs(struct kvm_vcpu *vcpu, struct pt_regs *regs);
int __kvmhv_nestedv2_mark_dirty(struct kvm_vcpu *vcpu, u16 iden);
int __kvmhv_nestedv2_cached_reload(struct kvm_vcpu *vcpu, u16 iden);
static inline int kvmhv_nestedv2_reload_ptregs(struct kvm_vcpu *vcpu,
struct pt_regs *regs)
{
if (kvmhv_is_nestedv2())
return __kvmhv_nestedv2_reload_ptregs(vcpu, regs);
return 0;
}
static inline int kvmhv_nestedv2_mark_dirty_ptregs(struct kvm_vcpu *vcpu,
struct pt_regs *regs)
{
if (kvmhv_is_nestedv2())
return __kvmhv_nestedv2_mark_dirty_ptregs(vcpu, regs);
return 0;
}
static inline int kvmhv_nestedv2_mark_dirty(struct kvm_vcpu *vcpu, u16 iden)
{
if (kvmhv_is_nestedv2())
return __kvmhv_nestedv2_mark_dirty(vcpu, iden);
return 0;
}
static inline int kvmhv_nestedv2_cached_reload(struct kvm_vcpu *vcpu, u16 iden)
{
if (kvmhv_is_nestedv2())
return __kvmhv_nestedv2_cached_reload(vcpu, iden);
return 0;
}
extern int kvm_irq_bypass;
static inline struct kvmppc_vcpu_book3s *to_book3s(struct kvm_vcpu *vcpu)
......@@ -335,60 +400,72 @@ static inline struct kvmppc_vcpu_book3s *to_book3s(struct kvm_vcpu *vcpu)
static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
{
vcpu->arch.regs.gpr[num] = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_GPR(num));
}
static inline ulong kvmppc_get_gpr(struct kvm_vcpu *vcpu, int num)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_GPR(num)) < 0);
return vcpu->arch.regs.gpr[num];
}
static inline void kvmppc_set_cr(struct kvm_vcpu *vcpu, u32 val)
{
vcpu->arch.regs.ccr = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_CR);
}
static inline u32 kvmppc_get_cr(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_CR) < 0);
return vcpu->arch.regs.ccr;
}
static inline void kvmppc_set_xer(struct kvm_vcpu *vcpu, ulong val)
{
vcpu->arch.regs.xer = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_XER);
}
static inline ulong kvmppc_get_xer(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_XER) < 0);
return vcpu->arch.regs.xer;
}
static inline void kvmppc_set_ctr(struct kvm_vcpu *vcpu, ulong val)
{
vcpu->arch.regs.ctr = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_CTR);
}
static inline ulong kvmppc_get_ctr(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_CTR) < 0);
return vcpu->arch.regs.ctr;
}
static inline void kvmppc_set_lr(struct kvm_vcpu *vcpu, ulong val)
{
vcpu->arch.regs.link = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_LR);
}
static inline ulong kvmppc_get_lr(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_LR) < 0);
return vcpu->arch.regs.link;
}
static inline void kvmppc_set_pc(struct kvm_vcpu *vcpu, ulong val)
{
vcpu->arch.regs.nip = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_NIA);
}
static inline ulong kvmppc_get_pc(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_NIA) < 0);
return vcpu->arch.regs.nip;
}
......@@ -405,27 +482,32 @@ static inline ulong kvmppc_get_fault_dar(struct kvm_vcpu *vcpu)
static inline u64 kvmppc_get_fpr(struct kvm_vcpu *vcpu, int i)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_VSRS(i)) < 0);
return vcpu->arch.fp.fpr[i][TS_FPROFFSET];
}
static inline void kvmppc_set_fpr(struct kvm_vcpu *vcpu, int i, u64 val)
{
vcpu->arch.fp.fpr[i][TS_FPROFFSET] = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_VSRS(i));
}
static inline u64 kvmppc_get_fpscr(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_FPSCR) < 0);
return vcpu->arch.fp.fpscr;
}
static inline void kvmppc_set_fpscr(struct kvm_vcpu *vcpu, u64 val)
{
vcpu->arch.fp.fpscr = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_FPSCR);
}
static inline u64 kvmppc_get_vsx_fpr(struct kvm_vcpu *vcpu, int i, int j)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_VSRS(i)) < 0);
return vcpu->arch.fp.fpr[i][j];
}
......@@ -433,11 +515,13 @@ static inline void kvmppc_set_vsx_fpr(struct kvm_vcpu *vcpu, int i, int j,
u64 val)
{
vcpu->arch.fp.fpr[i][j] = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_VSRS(i));
}
#ifdef CONFIG_ALTIVEC
static inline void kvmppc_get_vsx_vr(struct kvm_vcpu *vcpu, int i, vector128 *v)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_VSRS(32 + i)) < 0);
*v = vcpu->arch.vr.vr[i];
}
......@@ -445,75 +529,86 @@ static inline void kvmppc_set_vsx_vr(struct kvm_vcpu *vcpu, int i,
vector128 *val)
{
vcpu->arch.vr.vr[i] = *val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_VSRS(32 + i));
}
static inline u32 kvmppc_get_vscr(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_VSCR) < 0);
return vcpu->arch.vr.vscr.u[3];
}
static inline void kvmppc_set_vscr(struct kvm_vcpu *vcpu, u32 val)
{
vcpu->arch.vr.vscr.u[3] = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_VSCR);
}
#endif
#define KVMPPC_BOOK3S_VCPU_ACCESSOR_SET(reg, size) \
#define KVMPPC_BOOK3S_VCPU_ACCESSOR_SET(reg, size, iden) \
static inline void kvmppc_set_##reg(struct kvm_vcpu *vcpu, u##size val) \
{ \
\
vcpu->arch.reg = val; \
kvmhv_nestedv2_mark_dirty(vcpu, iden); \
}
#define KVMPPC_BOOK3S_VCPU_ACCESSOR_GET(reg, size) \
#define KVMPPC_BOOK3S_VCPU_ACCESSOR_GET(reg, size, iden) \
static inline u##size kvmppc_get_##reg(struct kvm_vcpu *vcpu) \
{ \
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, iden) < 0); \
return vcpu->arch.reg; \
}
#define KVMPPC_BOOK3S_VCPU_ACCESSOR(reg, size) \
KVMPPC_BOOK3S_VCPU_ACCESSOR_SET(reg, size) \
KVMPPC_BOOK3S_VCPU_ACCESSOR_GET(reg, size) \
#define KVMPPC_BOOK3S_VCPU_ACCESSOR(reg, size, iden) \
KVMPPC_BOOK3S_VCPU_ACCESSOR_SET(reg, size, iden) \
KVMPPC_BOOK3S_VCPU_ACCESSOR_GET(reg, size, iden) \
KVMPPC_BOOK3S_VCPU_ACCESSOR(pid, 32)
KVMPPC_BOOK3S_VCPU_ACCESSOR(tar, 64)
KVMPPC_BOOK3S_VCPU_ACCESSOR(ebbhr, 64)
KVMPPC_BOOK3S_VCPU_ACCESSOR(ebbrr, 64)
KVMPPC_BOOK3S_VCPU_ACCESSOR(bescr, 64)
KVMPPC_BOOK3S_VCPU_ACCESSOR(ic, 64)
KVMPPC_BOOK3S_VCPU_ACCESSOR(vrsave, 64)
KVMPPC_BOOK3S_VCPU_ACCESSOR(pid, 32, KVMPPC_GSID_PIDR)
KVMPPC_BOOK3S_VCPU_ACCESSOR(tar, 64, KVMPPC_GSID_TAR)
KVMPPC_BOOK3S_VCPU_ACCESSOR(ebbhr, 64, KVMPPC_GSID_EBBHR)
KVMPPC_BOOK3S_VCPU_ACCESSOR(ebbrr, 64, KVMPPC_GSID_EBBRR)
KVMPPC_BOOK3S_VCPU_ACCESSOR(bescr, 64, KVMPPC_GSID_BESCR)
KVMPPC_BOOK3S_VCPU_ACCESSOR(ic, 64, KVMPPC_GSID_IC)
KVMPPC_BOOK3S_VCPU_ACCESSOR(vrsave, 64, KVMPPC_GSID_VRSAVE)
#define KVMPPC_BOOK3S_VCORE_ACCESSOR_SET(reg, size) \
#define KVMPPC_BOOK3S_VCORE_ACCESSOR_SET(reg, size, iden) \
static inline void kvmppc_set_##reg(struct kvm_vcpu *vcpu, u##size val) \
{ \
vcpu->arch.vcore->reg = val; \
kvmhv_nestedv2_mark_dirty(vcpu, iden); \
}
#define KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(reg, size) \
#define KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(reg, size, iden) \
static inline u##size kvmppc_get_##reg(struct kvm_vcpu *vcpu) \
{ \
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, iden) < 0); \
return vcpu->arch.vcore->reg; \
}
#define KVMPPC_BOOK3S_VCORE_ACCESSOR(reg, size) \
KVMPPC_BOOK3S_VCORE_ACCESSOR_SET(reg, size) \
KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(reg, size) \
#define KVMPPC_BOOK3S_VCORE_ACCESSOR(reg, size, iden) \
KVMPPC_BOOK3S_VCORE_ACCESSOR_SET(reg, size, iden) \
KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(reg, size, iden) \
KVMPPC_BOOK3S_VCORE_ACCESSOR(vtb, 64)
KVMPPC_BOOK3S_VCORE_ACCESSOR(tb_offset, 64)
KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(arch_compat, 32)
KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(lpcr, 64)
KVMPPC_BOOK3S_VCORE_ACCESSOR(vtb, 64, KVMPPC_GSID_VTB)
KVMPPC_BOOK3S_VCORE_ACCESSOR(tb_offset, 64, KVMPPC_GSID_TB_OFFSET)
KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(arch_compat, 32, KVMPPC_GSID_LOGICAL_PVR)
KVMPPC_BOOK3S_VCORE_ACCESSOR_GET(lpcr, 64, KVMPPC_GSID_LPCR)
static inline u64 kvmppc_get_dec_expires(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_TB_OFFSET) < 0);
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_DEC_EXPIRY_TB) < 0);
return vcpu->arch.dec_expires;
}
static inline void kvmppc_set_dec_expires(struct kvm_vcpu *vcpu, u64 val)
{
vcpu->arch.dec_expires = val;
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_TB_OFFSET) < 0);
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_DEC_EXPIRY_TB);
}
/* Expiry time of vcpu DEC relative to host TB */
......
......@@ -677,6 +677,12 @@ static inline pte_t *find_kvm_host_pte(struct kvm *kvm, unsigned long mmu_seq,
extern pte_t *find_kvm_nested_guest_pte(struct kvm *kvm, unsigned long lpid,
unsigned long ea, unsigned *hshift);
int kvmhv_nestedv2_vcpu_create(struct kvm_vcpu *vcpu, struct kvmhv_nestedv2_io *io);
void kvmhv_nestedv2_vcpu_free(struct kvm_vcpu *vcpu, struct kvmhv_nestedv2_io *io);
int kvmhv_nestedv2_flush_vcpu(struct kvm_vcpu *vcpu, u64 time_limit);
int kvmhv_nestedv2_set_ptbl_entry(unsigned long lpid, u64 dw0, u64 dw1);
int kvmhv_nestedv2_parse_output(struct kvm_vcpu *vcpu);
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
#endif /* __ASM_KVM_BOOK3S_64_H__ */
......@@ -25,6 +25,7 @@
#include <asm/cacheflush.h>
#include <asm/hvcall.h>
#include <asm/mce.h>
#include <asm/guest-state-buffer.h>
#define __KVM_HAVE_ARCH_VCPU_DEBUGFS
......@@ -509,6 +510,23 @@ union xive_tma_w01 {
__be64 w01;
};
/* Nestedv2 H_GUEST_RUN_VCPU configuration */
struct kvmhv_nestedv2_config {
struct kvmppc_gs_buff_info vcpu_run_output_cfg;
struct kvmppc_gs_buff_info vcpu_run_input_cfg;
u64 vcpu_run_output_size;
};
/* Nestedv2 L1<->L0 communication state */
struct kvmhv_nestedv2_io {
struct kvmhv_nestedv2_config cfg;
struct kvmppc_gs_buff *vcpu_run_output;
struct kvmppc_gs_buff *vcpu_run_input;
struct kvmppc_gs_msg *vcpu_message;
struct kvmppc_gs_msg *vcore_message;
struct kvmppc_gs_bitmap valids;
};
struct kvm_vcpu_arch {
ulong host_stack;
u32 host_pid;
......@@ -829,6 +847,8 @@ struct kvm_vcpu_arch {
u64 nested_hfscr; /* HFSCR that the L1 requested for the nested guest */
u32 nested_vcpu_id;
gpa_t nested_io_gpr;
/* For nested APIv2 guests*/
struct kvmhv_nestedv2_io nestedv2_io;
#endif
#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING
......
......@@ -615,6 +615,42 @@ static inline bool kvmhv_on_pseries(void)
{
return false;
}
#endif
#ifndef CONFIG_PPC_BOOK3S
static inline bool kvmhv_is_nestedv2(void)
{
return false;
}
static inline bool kvmhv_is_nestedv1(void)
{
return false;
}
static inline int kvmhv_nestedv2_reload_ptregs(struct kvm_vcpu *vcpu,
struct pt_regs *regs)
{
return 0;
}
static inline int kvmhv_nestedv2_mark_dirty_ptregs(struct kvm_vcpu *vcpu,
struct pt_regs *regs)
{
return 0;
}
static inline int kvmhv_nestedv2_mark_dirty(struct kvm_vcpu *vcpu, u16 iden)
{
return 0;
}
static inline int kvmhv_nestedv2_cached_reload(struct kvm_vcpu *vcpu, u16 iden)
{
return 0;
}
#endif
#ifdef CONFIG_KVM_XICS
......@@ -939,27 +975,32 @@ static inline void kvmppc_set_##reg(struct kvm_vcpu *vcpu, ulong val) \
mtspr(bookehv_spr, val); \
} \
#define KVMPPC_VCPU_SHARED_REGS_ACCESSOR_GET(reg, size) \
#define KVMPPC_VCPU_SHARED_REGS_ACCESSOR_GET(reg, size, iden) \
static inline u##size kvmppc_get_##reg(struct kvm_vcpu *vcpu) \
{ \
if (iden) \
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, iden) < 0); \
if (kvmppc_shared_big_endian(vcpu)) \
return be##size##_to_cpu(vcpu->arch.shared->reg); \
else \
return le##size##_to_cpu(vcpu->arch.shared->reg); \
} \
#define KVMPPC_VCPU_SHARED_REGS_ACCESSOR_SET(reg, size) \
#define KVMPPC_VCPU_SHARED_REGS_ACCESSOR_SET(reg, size, iden) \
static inline void kvmppc_set_##reg(struct kvm_vcpu *vcpu, u##size val) \
{ \
if (kvmppc_shared_big_endian(vcpu)) \
vcpu->arch.shared->reg = cpu_to_be##size(val); \
else \
vcpu->arch.shared->reg = cpu_to_le##size(val); \
\
if (iden) \
kvmhv_nestedv2_mark_dirty(vcpu, iden); \
} \
#define KVMPPC_VCPU_SHARED_REGS_ACCESSOR(reg, size) \
KVMPPC_VCPU_SHARED_REGS_ACCESSOR_GET(reg, size) \
KVMPPC_VCPU_SHARED_REGS_ACCESSOR_SET(reg, size) \
#define KVMPPC_VCPU_SHARED_REGS_ACCESSOR(reg, size, iden) \
KVMPPC_VCPU_SHARED_REGS_ACCESSOR_GET(reg, size, iden) \
KVMPPC_VCPU_SHARED_REGS_ACCESSOR_SET(reg, size, iden) \
#define KVMPPC_BOOKE_HV_SPRNG_ACCESSOR(reg, bookehv_spr) \
KVMPPC_BOOKE_HV_SPRNG_ACCESSOR_GET(reg, bookehv_spr) \
......@@ -967,39 +1008,40 @@ static inline void kvmppc_set_##reg(struct kvm_vcpu *vcpu, u##size val) \
#ifdef CONFIG_KVM_BOOKE_HV
#define KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(reg, size, bookehv_spr) \
#define KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(reg, size, bookehv_spr, iden) \
KVMPPC_BOOKE_HV_SPRNG_ACCESSOR(reg, bookehv_spr) \
#else
#define KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(reg, size, bookehv_spr) \
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(reg, size) \
#define KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(reg, size, bookehv_spr, iden) \
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(reg, size, iden) \
#endif
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(critical, 64)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg0, 64, SPRN_GSPRG0)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg1, 64, SPRN_GSPRG1)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg2, 64, SPRN_GSPRG2)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg3, 64, SPRN_GSPRG3)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(srr0, 64, SPRN_GSRR0)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(srr1, 64, SPRN_GSRR1)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(dar, 64, SPRN_GDEAR)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(esr, 64, SPRN_GESR)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR_GET(msr, 64)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(critical, 64, 0)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg0, 64, SPRN_GSPRG0, KVMPPC_GSID_SPRG0)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg1, 64, SPRN_GSPRG1, KVMPPC_GSID_SPRG1)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg2, 64, SPRN_GSPRG2, KVMPPC_GSID_SPRG2)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(sprg3, 64, SPRN_GSPRG3, KVMPPC_GSID_SPRG3)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(srr0, 64, SPRN_GSRR0, KVMPPC_GSID_SRR0)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(srr1, 64, SPRN_GSRR1, KVMPPC_GSID_SRR1)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(dar, 64, SPRN_GDEAR, KVMPPC_GSID_DAR)
KVMPPC_BOOKE_HV_SPRNG_OR_VCPU_SHARED_REGS_ACCESSOR(esr, 64, SPRN_GESR, 0)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR_GET(msr, 64, KVMPPC_GSID_MSR)
static inline void kvmppc_set_msr_fast(struct kvm_vcpu *vcpu, u64 val)
{
if (kvmppc_shared_big_endian(vcpu))
vcpu->arch.shared->msr = cpu_to_be64(val);
else
vcpu->arch.shared->msr = cpu_to_le64(val);
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_MSR);
}
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(dsisr, 32)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(int_pending, 32)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg4, 64)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg5, 64)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg6, 64)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg7, 64)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(dsisr, 32, KVMPPC_GSID_DSISR)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(int_pending, 32, 0)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg4, 64, 0)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg5, 64, 0)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg6, 64, 0)
KVMPPC_VCPU_SHARED_REGS_ACCESSOR(sprg7, 64, 0)
static inline u32 kvmppc_get_sr(struct kvm_vcpu *vcpu, int nr)
{
......
......@@ -6,6 +6,7 @@
#include <linux/string.h>
#include <linux/irqflags.h>
#include <linux/delay.h>
#include <asm/hvcall.h>
#include <asm/paca.h>
......@@ -343,6 +344,212 @@ static inline long plpar_get_cpu_characteristics(struct h_cpu_char_result *p)
return rc;
}
static inline long plpar_guest_create(unsigned long flags, unsigned long *guest_id)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
unsigned long token;
long rc;
token = -1UL;
do {
rc = plpar_hcall(H_GUEST_CREATE, retbuf, flags, token);
if (rc == H_SUCCESS)
*guest_id = retbuf[0];
if (rc == H_BUSY) {
token = retbuf[0];
cond_resched();
}
if (H_IS_LONG_BUSY(rc)) {
token = retbuf[0];
msleep(get_longbusy_msecs(rc));
rc = H_BUSY;
}
} while (rc == H_BUSY);
return rc;
}
static inline long plpar_guest_create_vcpu(unsigned long flags,
unsigned long guest_id,
unsigned long vcpu_id)
{
long rc;
do {
rc = plpar_hcall_norets(H_GUEST_CREATE_VCPU, 0, guest_id, vcpu_id);
if (rc == H_BUSY)
cond_resched();
if (H_IS_LONG_BUSY(rc)) {
msleep(get_longbusy_msecs(rc));
rc = H_BUSY;
}
} while (rc == H_BUSY);
return rc;
}
static inline long plpar_guest_set_state(unsigned long flags,
unsigned long guest_id,
unsigned long vcpu_id,
unsigned long data_buffer,
unsigned long data_size,
unsigned long *failed_index)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
long rc;
while (true) {
rc = plpar_hcall(H_GUEST_SET_STATE, retbuf, flags, guest_id,
vcpu_id, data_buffer, data_size);
if (rc == H_BUSY) {
cpu_relax();
continue;
}
if (H_IS_LONG_BUSY(rc)) {
mdelay(get_longbusy_msecs(rc));
continue;
}
if (rc == H_INVALID_ELEMENT_ID)
*failed_index = retbuf[0];
else if (rc == H_INVALID_ELEMENT_SIZE)
*failed_index = retbuf[0];
else if (rc == H_INVALID_ELEMENT_VALUE)
*failed_index = retbuf[0];
break;
}
return rc;
}
static inline long plpar_guest_get_state(unsigned long flags,
unsigned long guest_id,
unsigned long vcpu_id,
unsigned long data_buffer,
unsigned long data_size,
unsigned long *failed_index)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
long rc;
while (true) {
rc = plpar_hcall(H_GUEST_GET_STATE, retbuf, flags, guest_id,
vcpu_id, data_buffer, data_size);
if (rc == H_BUSY) {
cpu_relax();
continue;
}
if (H_IS_LONG_BUSY(rc)) {
mdelay(get_longbusy_msecs(rc));
continue;
}
if (rc == H_INVALID_ELEMENT_ID)
*failed_index = retbuf[0];
else if (rc == H_INVALID_ELEMENT_SIZE)
*failed_index = retbuf[0];
else if (rc == H_INVALID_ELEMENT_VALUE)
*failed_index = retbuf[0];
break;
}
return rc;
}
static inline long plpar_guest_run_vcpu(unsigned long flags, unsigned long guest_id,
unsigned long vcpu_id, int *trap,
unsigned long *failed_index)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
long rc;
rc = plpar_hcall(H_GUEST_RUN_VCPU, retbuf, flags, guest_id, vcpu_id);
if (rc == H_SUCCESS)
*trap = retbuf[0];
else if (rc == H_INVALID_ELEMENT_ID)
*failed_index = retbuf[0];
else if (rc == H_INVALID_ELEMENT_SIZE)
*failed_index = retbuf[0];
else if (rc == H_INVALID_ELEMENT_VALUE)
*failed_index = retbuf[0];
return rc;
}
static inline long plpar_guest_delete(unsigned long flags, u64 guest_id)
{
long rc;
do {
rc = plpar_hcall_norets(H_GUEST_DELETE, flags, guest_id);
if (rc == H_BUSY)
cond_resched();
if (H_IS_LONG_BUSY(rc)) {
msleep(get_longbusy_msecs(rc));
rc = H_BUSY;
}
} while (rc == H_BUSY);
return rc;
}
static inline long plpar_guest_set_capabilities(unsigned long flags,
unsigned long capabilities)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
long rc;
do {
rc = plpar_hcall(H_GUEST_SET_CAPABILITIES, retbuf, flags, capabilities);
if (rc == H_BUSY)
cond_resched();
if (H_IS_LONG_BUSY(rc)) {
msleep(get_longbusy_msecs(rc));
rc = H_BUSY;
}
} while (rc == H_BUSY);
return rc;
}
static inline long plpar_guest_get_capabilities(unsigned long flags,
unsigned long *capabilities)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
long rc;
do {
rc = plpar_hcall(H_GUEST_GET_CAPABILITIES, retbuf, flags);
if (rc == H_BUSY)
cond_resched();
if (H_IS_LONG_BUSY(rc)) {
msleep(get_longbusy_msecs(rc));
rc = H_BUSY;
}
} while (rc == H_BUSY);
if (rc == H_SUCCESS)
*capabilities = retbuf[0];
return rc;
}
/*
* Wrapper to H_RPT_INVALIDATE hcall that handles return values appropriately
*
......@@ -407,6 +614,62 @@ static inline long pseries_rpt_invalidate(u64 pid, u64 target, u64 type,
return 0;
}
static inline long plpar_guest_create_vcpu(unsigned long flags,
unsigned long guest_id,
unsigned long vcpu_id)
{
return 0;
}
static inline long plpar_guest_get_state(unsigned long flags,
unsigned long guest_id,
unsigned long vcpu_id,
unsigned long data_buffer,
unsigned long data_size,
unsigned long *failed_index)
{
return 0;
}
static inline long plpar_guest_set_state(unsigned long flags,
unsigned long guest_id,
unsigned long vcpu_id,
unsigned long data_buffer,
unsigned long data_size,
unsigned long *failed_index)
{
return 0;
}
static inline long plpar_guest_run_vcpu(unsigned long flags, unsigned long guest_id,
unsigned long vcpu_id, int *trap,
unsigned long *failed_index)
{
return 0;
}
static inline long plpar_guest_create(unsigned long flags, unsigned long *guest_id)
{
return 0;
}
static inline long plpar_guest_delete(unsigned long flags, u64 guest_id)
{
return 0;
}
static inline long plpar_guest_get_capabilities(unsigned long flags,
unsigned long *capabilities)
{
return 0;
}
static inline long plpar_guest_set_capabilities(unsigned long flags,
unsigned long capabilities)
{
return 0;
}
#endif /* CONFIG_PPC_PSERIES */
#endif /* _ASM_POWERPC_PLPAR_WRAPPERS_H */
......@@ -87,6 +87,7 @@ kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
book3s_hv_ras.o \
book3s_hv_builtin.o \
book3s_hv_p9_perf.o \
book3s_hv_nestedv2.o \
guest-state-buffer.o \
$(kvm-book3s_64-builtin-tm-objs-y) \
$(kvm-book3s_64-builtin-xics-objs-y)
......
......@@ -393,7 +393,7 @@ static void kvmppc_set_pvr_hv(struct kvm_vcpu *vcpu, u32 pvr)
static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat)
{
unsigned long host_pcr_bit = 0, guest_pcr_bit = 0;
unsigned long host_pcr_bit = 0, guest_pcr_bit = 0, cap = 0;
struct kvmppc_vcore *vc = vcpu->arch.vcore;
/* We can (emulate) our own architecture version and anything older */
......@@ -424,9 +424,11 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat)
break;
case PVR_ARCH_300:
guest_pcr_bit = PCR_ARCH_300;
cap = H_GUEST_CAP_POWER9;
break;
case PVR_ARCH_31:
guest_pcr_bit = PCR_ARCH_31;
cap = H_GUEST_CAP_POWER10;
break;
default:
return -EINVAL;
......@@ -437,8 +439,14 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat)
if (guest_pcr_bit > host_pcr_bit)
return -EINVAL;
if (kvmhv_on_pseries() && kvmhv_is_nestedv2()) {
if (!(cap & nested_capabilities))
return -EINVAL;
}
spin_lock(&vc->lock);
vc->arch_compat = arch_compat;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_LOGICAL_PVR);
/*
* Set all PCR bits for which guest_pcr_bit <= bit < host_pcr_bit
* Also set all reserved PCR bits
......@@ -2187,6 +2195,7 @@ static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 new_lpcr,
}
vc->lpcr = new_lpcr;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_LPCR);
spin_unlock(&vc->lock);
}
......@@ -2927,8 +2936,14 @@ static int kvmppc_core_vcpu_create_hv(struct kvm_vcpu *vcpu)
vcpu->arch.shared_big_endian = false;
#endif
#endif
kvmppc_set_mmcr_hv(vcpu, 0, MMCR0_FC);
if (kvmhv_is_nestedv2()) {
err = kvmhv_nestedv2_vcpu_create(vcpu, &vcpu->arch.nestedv2_io);
if (err < 0)
return err;
}
kvmppc_set_mmcr_hv(vcpu, 0, MMCR0_FC);
if (cpu_has_feature(CPU_FTR_ARCH_31)) {
kvmppc_set_mmcr_hv(vcpu, 0, kvmppc_get_mmcr_hv(vcpu, 0) | MMCR0_PMCCEXT);
kvmppc_set_mmcra_hv(vcpu, MMCRA_BHRB_DISABLE);
......@@ -3084,6 +3099,8 @@ static void kvmppc_core_vcpu_free_hv(struct kvm_vcpu *vcpu)
unpin_vpa(vcpu->kvm, &vcpu->arch.slb_shadow);
unpin_vpa(vcpu->kvm, &vcpu->arch.vpa);
spin_unlock(&vcpu->arch.vpa_update_lock);
if (kvmhv_is_nestedv2())
kvmhv_nestedv2_vcpu_free(vcpu, &vcpu->arch.nestedv2_io);
}
static int kvmppc_core_check_requests_hv(struct kvm_vcpu *vcpu)
......@@ -4048,6 +4065,55 @@ static void vcpu_vpa_increment_dispatch(struct kvm_vcpu *vcpu)
}
}
static int kvmhv_vcpu_entry_nestedv2(struct kvm_vcpu *vcpu, u64 time_limit,
unsigned long lpcr, u64 *tb)
{
struct kvmhv_nestedv2_io *io;
unsigned long msr, i;
int trap;
long rc;
io = &vcpu->arch.nestedv2_io;
msr = mfmsr();
kvmppc_msr_hard_disable_set_facilities(vcpu, msr);
if (lazy_irq_pending())
return 0;
rc = kvmhv_nestedv2_flush_vcpu(vcpu, time_limit);
if (rc < 0)
return -EINVAL;
accumulate_time(vcpu, &vcpu->arch.in_guest);
rc = plpar_guest_run_vcpu(0, vcpu->kvm->arch.lpid, vcpu->vcpu_id,
&trap, &i);
if (rc != H_SUCCESS) {
pr_err("KVM Guest Run VCPU hcall failed\n");
if (rc == H_INVALID_ELEMENT_ID)
pr_err("KVM: Guest Run VCPU invalid element id at %ld\n", i);
else if (rc == H_INVALID_ELEMENT_SIZE)
pr_err("KVM: Guest Run VCPU invalid element size at %ld\n", i);
else if (rc == H_INVALID_ELEMENT_VALUE)
pr_err("KVM: Guest Run VCPU invalid element value at %ld\n", i);
return -EINVAL;
}
accumulate_time(vcpu, &vcpu->arch.guest_exit);
*tb = mftb();
kvmppc_gsm_reset(io->vcpu_message);
kvmppc_gsm_reset(io->vcore_message);
kvmppc_gsbm_zero(&io->valids);
rc = kvmhv_nestedv2_parse_output(vcpu);
if (rc < 0)
return -EINVAL;
timer_rearm_host_dec(*tb);
return trap;
}
/* call our hypervisor to load up HV regs and go */
static int kvmhv_vcpu_entry_p9_nested(struct kvm_vcpu *vcpu, u64 time_limit, unsigned long lpcr, u64 *tb)
{
......@@ -4165,7 +4231,10 @@ static int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu, u64 time_limit,
vcpu_vpa_increment_dispatch(vcpu);
if (kvmhv_on_pseries()) {
if (kvmhv_is_nestedv1())
trap = kvmhv_vcpu_entry_p9_nested(vcpu, time_limit, lpcr, tb);
else
trap = kvmhv_vcpu_entry_nestedv2(vcpu, time_limit, lpcr, tb);
/* H_CEDE has to be handled now, not later */
if (trap == BOOK3S_INTERRUPT_SYSCALL && !nested &&
......@@ -5145,6 +5214,14 @@ void kvmppc_update_lpcr(struct kvm *kvm, unsigned long lpcr, unsigned long mask)
if (++cores_done >= kvm->arch.online_vcores)
break;
}
if (kvmhv_is_nestedv2()) {
struct kvm_vcpu *vcpu;
kvm_for_each_vcpu(i, vcpu, kvm) {
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_LPCR);
}
}
}
void kvmppc_setup_partition_table(struct kvm *kvm)
......@@ -5411,15 +5488,43 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
/* Allocate the guest's logical partition ID */
if (!kvmhv_is_nestedv2()) {
lpid = kvmppc_alloc_lpid();
if ((long)lpid < 0)
return -ENOMEM;
kvm->arch.lpid = lpid;
}
kvmppc_alloc_host_rm_ops();
kvmhv_vm_nested_init(kvm);
if (kvmhv_is_nestedv2()) {
long rc;
unsigned long guest_id;
rc = plpar_guest_create(0, &guest_id);
if (rc != H_SUCCESS)
pr_err("KVM: Create Guest hcall failed, rc=%ld\n", rc);
switch (rc) {
case H_PARAMETER:
case H_FUNCTION:
case H_STATE:
return -EINVAL;
case H_NOT_ENOUGH_RESOURCES:
case H_ABORTED:
return -ENOMEM;
case H_AUTHORITY:
return -EPERM;
case H_NOT_AVAILABLE:
return -EBUSY;
}
kvm->arch.lpid = guest_id;
}
/*
* Since we don't flush the TLB when tearing down a VM,
* and this lpid might have previously been used,
......@@ -5489,6 +5594,9 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
lpcr |= LPCR_HAIL;
ret = kvmppc_init_vm_radix(kvm);
if (ret) {
if (kvmhv_is_nestedv2())
plpar_guest_delete(0, kvm->arch.lpid);
else
kvmppc_free_lpid(kvm->arch.lpid);
return ret;
}
......@@ -5579,9 +5687,13 @@ static void kvmppc_core_destroy_vm_hv(struct kvm *kvm)
kvm->arch.process_table = 0;
if (kvm->arch.secure_guest)
uv_svm_terminate(kvm->arch.lpid);
if (!kvmhv_is_nestedv2())
kvmhv_set_ptbl_entry(kvm->arch.lpid, 0, 0);
}
if (kvmhv_is_nestedv2())
plpar_guest_delete(0, kvm->arch.lpid);
else
kvmppc_free_lpid(kvm->arch.lpid);
kvmppc_free_pimap(kvm);
......@@ -5994,6 +6106,8 @@ static int kvmhv_enable_nested(struct kvm *kvm)
return -ENODEV;
if (!radix_enabled())
return -ENODEV;
if (kvmhv_is_nestedv2())
return -ENODEV;
/* kvm == NULL means the caller is testing if the capability exists */
if (kvm)
......
......@@ -3,6 +3,8 @@
/*
* Privileged (non-hypervisor) host registers to save.
*/
#include "asm/guest-state-buffer.h"
struct p9_host_os_sprs {
unsigned long iamr;
unsigned long amr;
......@@ -54,67 +56,73 @@ void accumulate_time(struct kvm_vcpu *vcpu, struct kvmhv_tb_accumulator *next);
static inline void __kvmppc_set_msr_hv(struct kvm_vcpu *vcpu, u64 val)
{
vcpu->arch.shregs.msr = val;
kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_MSR);
}
static inline u64 __kvmppc_get_msr_hv(struct kvm_vcpu *vcpu)
{
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, KVMPPC_GSID_MSR) < 0);
return vcpu->arch.shregs.msr;
}
#define KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_SET(reg, size) \
#define KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_SET(reg, size, iden) \
static inline void kvmppc_set_##reg ##_hv(struct kvm_vcpu *vcpu, u##size val) \
{ \
vcpu->arch.reg = val; \
kvmhv_nestedv2_mark_dirty(vcpu, iden); \
}
#define KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_GET(reg, size) \
#define KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_GET(reg, size, iden) \
static inline u##size kvmppc_get_##reg ##_hv(struct kvm_vcpu *vcpu) \
{ \
kvmhv_nestedv2_cached_reload(vcpu, iden); \
return vcpu->arch.reg; \
}
#define KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(reg, size) \
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_SET(reg, size) \
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_GET(reg, size) \
#define KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(reg, size, iden) \
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_SET(reg, size, iden) \
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR_GET(reg, size, iden) \
#define KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_SET(reg, size) \
#define KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_SET(reg, size, iden) \
static inline void kvmppc_set_##reg ##_hv(struct kvm_vcpu *vcpu, int i, u##size val) \
{ \
vcpu->arch.reg[i] = val; \
kvmhv_nestedv2_mark_dirty(vcpu, iden(i)); \
}
#define KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_GET(reg, size) \
#define KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_GET(reg, size, iden) \
static inline u##size kvmppc_get_##reg ##_hv(struct kvm_vcpu *vcpu, int i) \
{ \
WARN_ON(kvmhv_nestedv2_cached_reload(vcpu, iden(i)) < 0); \
return vcpu->arch.reg[i]; \
}
#define KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(reg, size) \
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_SET(reg, size) \
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_GET(reg, size) \
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(mmcra, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(hfscr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(fscr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dscr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(purr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(spurr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(amr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(uamor, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(siar, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(sdar, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(iamr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawr0, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawr1, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawrx0, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawrx1, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(ciabr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(wort, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(ppr, 64)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(ctrl, 64)
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(mmcr, 64)
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(sier, 64)
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(pmc, 32)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(pspb, 32)
#define KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(reg, size, iden) \
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_SET(reg, size, iden) \
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR_GET(reg, size, iden) \
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(mmcra, 64, KVMPPC_GSID_MMCRA)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(hfscr, 64, KVMPPC_GSID_HFSCR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(fscr, 64, KVMPPC_GSID_FSCR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dscr, 64, KVMPPC_GSID_DSCR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(purr, 64, KVMPPC_GSID_PURR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(spurr, 64, KVMPPC_GSID_SPURR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(amr, 64, KVMPPC_GSID_AMR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(uamor, 64, KVMPPC_GSID_UAMOR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(siar, 64, KVMPPC_GSID_SIAR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(sdar, 64, KVMPPC_GSID_SDAR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(iamr, 64, KVMPPC_GSID_IAMR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawr0, 64, KVMPPC_GSID_DAWR0)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawr1, 64, KVMPPC_GSID_DAWR1)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawrx0, 64, KVMPPC_GSID_DAWRX0)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(dawrx1, 64, KVMPPC_GSID_DAWRX1)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(ciabr, 64, KVMPPC_GSID_CIABR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(wort, 64, KVMPPC_GSID_WORT)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(ppr, 64, KVMPPC_GSID_PPR)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(ctrl, 64, KVMPPC_GSID_CTRL);
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(mmcr, 64, KVMPPC_GSID_MMCR)
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(sier, 64, KVMPPC_GSID_SIER)
KVMPPC_BOOK3S_HV_VCPU_ARRAY_ACCESSOR(pmc, 32, KVMPPC_GSID_PMC)
KVMPPC_BOOK3S_HV_VCPU_ACCESSOR(pspb, 32, KVMPPC_GSID_PSPB)
......@@ -428,10 +428,12 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
return vcpu->arch.trap;
}
unsigned long nested_capabilities;
long kvmhv_nested_init(void)
{
long int ptb_order;
unsigned long ptcr;
unsigned long ptcr, host_capabilities;
long rc;
if (!kvmhv_on_pseries())
......@@ -439,6 +441,29 @@ long kvmhv_nested_init(void)
if (!radix_enabled())
return -ENODEV;
rc = plpar_guest_get_capabilities(0, &host_capabilities);
if (rc == H_SUCCESS) {
unsigned long capabilities = 0;
if (cpu_has_feature(CPU_FTR_ARCH_31))
capabilities |= H_GUEST_CAP_POWER10;
if (cpu_has_feature(CPU_FTR_ARCH_300))
capabilities |= H_GUEST_CAP_POWER9;
nested_capabilities = capabilities & host_capabilities;
rc = plpar_guest_set_capabilities(0, nested_capabilities);
if (rc != H_SUCCESS) {
pr_err("kvm-hv: Could not configure parent hypervisor capabilities (rc=%ld)",
rc);
return -ENODEV;
}
static_branch_enable(&__kvmhv_is_nestedv2);
return 0;
}
pr_info("kvm-hv: nestedv2 get capabilities hcall failed, falling back to nestedv1 (rc=%ld)\n",
rc);
/* Partition table entry is 1<<4 bytes in size, hence the 4. */
ptb_order = KVM_MAX_NESTED_GUESTS_SHIFT + 4;
/* Minimum partition table size is 1<<12 bytes */
......@@ -507,10 +532,15 @@ void kvmhv_set_ptbl_entry(u64 lpid, u64 dw0, u64 dw1)
return;
}
if (kvmhv_is_nestedv1()) {
pseries_partition_tb[lpid].patb0 = cpu_to_be64(dw0);
pseries_partition_tb[lpid].patb1 = cpu_to_be64(dw1);
/* L0 will do the necessary barriers */
kvmhv_flush_lpid(lpid);
}
if (kvmhv_is_nestedv2())
kvmhv_nestedv2_set_ptbl_entry(lpid, dw0, dw1);
}
static void kvmhv_set_nested_ptbl(struct kvm_nested_guest *gp)
......
This diff is collapsed.
......@@ -92,7 +92,8 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
vcpu->arch.mmio_host_swabbed = 0;
emulated = EMULATE_FAIL;
vcpu->arch.regs.msr = vcpu->arch.shared->msr;
vcpu->arch.regs.msr = kvmppc_get_msr(vcpu);
kvmhv_nestedv2_reload_ptregs(vcpu, &vcpu->arch.regs);
if (analyse_instr(&op, &vcpu->arch.regs, inst) == 0) {
int type = op.type & INSTR_TYPE_MASK;
int size = GETSIZE(op.type);
......@@ -357,6 +358,7 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
}
trace_kvm_ppc_instr(ppc_inst_val(inst), kvmppc_get_pc(vcpu), emulated);
kvmhv_nestedv2_mark_dirty_ptregs(vcpu, &vcpu->arch.regs);
/* Advance past emulated instruction. */
if (emulated != EMULATE_FAIL)
......
......@@ -569,3 +569,53 @@ int kvmppc_gsm_refresh_info(struct kvmppc_gs_msg *gsm,
return gsm->ops->refresh_info(gsm, gsb);
}
EXPORT_SYMBOL_GPL(kvmppc_gsm_refresh_info);
/**
* kvmppc_gsb_send - send all elements in the buffer to the hypervisor.
* @gsb: guest state buffer
* @flags: guest wide or thread wide
*
* Performs the H_GUEST_SET_STATE hcall for the guest state buffer.
*/
int kvmppc_gsb_send(struct kvmppc_gs_buff *gsb, unsigned long flags)
{
unsigned long hflags = 0;
unsigned long i;
int rc;
if (kvmppc_gsb_nelems(gsb) == 0)
return 0;
if (flags & KVMPPC_GS_FLAGS_WIDE)
hflags |= H_GUEST_FLAGS_WIDE;
rc = plpar_guest_set_state(hflags, gsb->guest_id, gsb->vcpu_id,
__pa(gsb->hdr), gsb->capacity, &i);
return rc;
}
EXPORT_SYMBOL_GPL(kvmppc_gsb_send);
/**
* kvmppc_gsb_recv - request all elements in the buffer have their value
* updated.
* @gsb: guest state buffer
* @flags: guest wide or thread wide
*
* Performs the H_GUEST_GET_STATE hcall for the guest state buffer.
* After returning from the hcall the guest state elements that were
* present in the buffer will have updated values from the hypervisor.
*/
int kvmppc_gsb_recv(struct kvmppc_gs_buff *gsb, unsigned long flags)
{
unsigned long hflags = 0;
unsigned long i;
int rc;
if (flags & KVMPPC_GS_FLAGS_WIDE)
hflags |= H_GUEST_FLAGS_WIDE;
rc = plpar_guest_get_state(hflags, gsb->guest_id, gsb->vcpu_id,
__pa(gsb->hdr), gsb->capacity, &i);
return rc;
}
EXPORT_SYMBOL_GPL(kvmppc_gsb_recv);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment