diff options
author | Paul Mackerras <paulus@samba.org> | 2013-04-17 16:32:26 -0400 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2013-04-26 14:27:34 -0400 |
commit | 8b78645c93b5d469e8006d68dbc92edc2640c654 (patch) | |
tree | 5f558213cc4bd2677069232d7dc1a78c96aee165 | |
parent | d19bd86204f85d42873e07bb64a27587fc380b5b (diff) |
KVM: PPC: Book3S: Facilities to save/restore XICS presentation ctrler state
This adds the ability for userspace to save and restore the state
of the XICS interrupt presentation controllers (ICPs) via the
KVM_GET/SET_ONE_REG interface. Since there is one ICP per vcpu, we
simply define a new 64-bit register in the ONE_REG space for the ICP
state. The state includes the CPU priority setting, the pending IPI
priority, and the priority and source number of any pending external
interrupt.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
-rw-r--r-- | Documentation/virtual/kvm/api.txt | 1 | ||||
-rw-r--r-- | arch/powerpc/include/asm/kvm_ppc.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/uapi/asm/kvm.h | 12 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s.c | 19 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_xics.c | 90 |
5 files changed, 124 insertions, 0 deletions
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index fb308be8521b..c09d1832e935 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt | |||
@@ -1808,6 +1808,7 @@ registers, find a list below: | |||
1808 | PPC | KVM_REG_PPC_TLB2PS | 32 | 1808 | PPC | KVM_REG_PPC_TLB2PS | 32 |
1809 | PPC | KVM_REG_PPC_TLB3PS | 32 | 1809 | PPC | KVM_REG_PPC_TLB3PS | 32 |
1810 | PPC | KVM_REG_PPC_EPTCFG | 32 | 1810 | PPC | KVM_REG_PPC_EPTCFG | 32 |
1811 | PPC | KVM_REG_PPC_ICP_STATE | 64 | ||
1811 | 1812 | ||
1812 | ARM registers are mapped using the lower 32 bits. The upper 16 of that | 1813 | ARM registers are mapped using the lower 32 bits. The upper 16 of that |
1813 | is the register group type, or coprocessor number: | 1814 | is the register group type, or coprocessor number: |
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index cfaa47995c0e..d7339df19259 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h | |||
@@ -313,6 +313,8 @@ extern void kvmppc_xics_free_icp(struct kvm_vcpu *vcpu); | |||
313 | extern int kvmppc_xics_create_icp(struct kvm_vcpu *vcpu, unsigned long server); | 313 | extern int kvmppc_xics_create_icp(struct kvm_vcpu *vcpu, unsigned long server); |
314 | extern int kvm_vm_ioctl_xics_irq(struct kvm *kvm, struct kvm_irq_level *args); | 314 | extern int kvm_vm_ioctl_xics_irq(struct kvm *kvm, struct kvm_irq_level *args); |
315 | extern int kvmppc_xics_hcall(struct kvm_vcpu *vcpu, u32 cmd); | 315 | extern int kvmppc_xics_hcall(struct kvm_vcpu *vcpu, u32 cmd); |
316 | extern u64 kvmppc_xics_get_icp(struct kvm_vcpu *vcpu); | ||
317 | extern int kvmppc_xics_set_icp(struct kvm_vcpu *vcpu, u64 icpval); | ||
316 | #else | 318 | #else |
317 | static inline int kvmppc_xics_enabled(struct kvm_vcpu *vcpu) | 319 | static inline int kvmppc_xics_enabled(struct kvm_vcpu *vcpu) |
318 | { return 0; } | 320 | { return 0; } |
diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index eb9e25c194ad..427b9aca2a0f 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h | |||
@@ -390,6 +390,18 @@ struct kvm_get_htab_header { | |||
390 | __u16 n_invalid; | 390 | __u16 n_invalid; |
391 | }; | 391 | }; |
392 | 392 | ||
393 | /* Per-vcpu XICS interrupt controller state */ | ||
394 | #define KVM_REG_PPC_ICP_STATE (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x8c) | ||
395 | |||
396 | #define KVM_REG_PPC_ICP_CPPR_SHIFT 56 /* current proc priority */ | ||
397 | #define KVM_REG_PPC_ICP_CPPR_MASK 0xff | ||
398 | #define KVM_REG_PPC_ICP_XISR_SHIFT 32 /* interrupt status field */ | ||
399 | #define KVM_REG_PPC_ICP_XISR_MASK 0xffffff | ||
400 | #define KVM_REG_PPC_ICP_MFRR_SHIFT 24 /* pending IPI priority */ | ||
401 | #define KVM_REG_PPC_ICP_MFRR_MASK 0xff | ||
402 | #define KVM_REG_PPC_ICP_PPRI_SHIFT 16 /* pending irq priority */ | ||
403 | #define KVM_REG_PPC_ICP_PPRI_MASK 0xff | ||
404 | |||
393 | /* Device control API: PPC-specific devices */ | 405 | /* Device control API: PPC-specific devices */ |
394 | #define KVM_DEV_MPIC_GRP_MISC 1 | 406 | #define KVM_DEV_MPIC_GRP_MISC 1 |
395 | #define KVM_DEV_MPIC_BASE_ADDR 0 /* 64-bit */ | 407 | #define KVM_DEV_MPIC_BASE_ADDR 0 /* 64-bit */ |
diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index 1a4d787df507..700df6f1d32c 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c | |||
@@ -535,6 +535,15 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) | |||
535 | &opcode, sizeof(u32)); | 535 | &opcode, sizeof(u32)); |
536 | break; | 536 | break; |
537 | } | 537 | } |
538 | #ifdef CONFIG_KVM_XICS | ||
539 | case KVM_REG_PPC_ICP_STATE: | ||
540 | if (!vcpu->arch.icp) { | ||
541 | r = -ENXIO; | ||
542 | break; | ||
543 | } | ||
544 | val = get_reg_val(reg->id, kvmppc_xics_get_icp(vcpu)); | ||
545 | break; | ||
546 | #endif /* CONFIG_KVM_XICS */ | ||
538 | default: | 547 | default: |
539 | r = -EINVAL; | 548 | r = -EINVAL; |
540 | break; | 549 | break; |
@@ -597,6 +606,16 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) | |||
597 | vcpu->arch.vscr.u[3] = set_reg_val(reg->id, val); | 606 | vcpu->arch.vscr.u[3] = set_reg_val(reg->id, val); |
598 | break; | 607 | break; |
599 | #endif /* CONFIG_ALTIVEC */ | 608 | #endif /* CONFIG_ALTIVEC */ |
609 | #ifdef CONFIG_KVM_XICS | ||
610 | case KVM_REG_PPC_ICP_STATE: | ||
611 | if (!vcpu->arch.icp) { | ||
612 | r = -ENXIO; | ||
613 | break; | ||
614 | } | ||
615 | r = kvmppc_xics_set_icp(vcpu, | ||
616 | set_reg_val(reg->id, val)); | ||
617 | break; | ||
618 | #endif /* CONFIG_KVM_XICS */ | ||
600 | default: | 619 | default: |
601 | r = -EINVAL; | 620 | r = -EINVAL; |
602 | break; | 621 | break; |
diff --git a/arch/powerpc/kvm/book3s_xics.c b/arch/powerpc/kvm/book3s_xics.c index 9fb2d3909c46..ee841ed8a690 100644 --- a/arch/powerpc/kvm/book3s_xics.c +++ b/arch/powerpc/kvm/book3s_xics.c | |||
@@ -954,6 +954,96 @@ int kvmppc_xics_create_icp(struct kvm_vcpu *vcpu, unsigned long server_num) | |||
954 | return 0; | 954 | return 0; |
955 | } | 955 | } |
956 | 956 | ||
957 | u64 kvmppc_xics_get_icp(struct kvm_vcpu *vcpu) | ||
958 | { | ||
959 | struct kvmppc_icp *icp = vcpu->arch.icp; | ||
960 | union kvmppc_icp_state state; | ||
961 | |||
962 | if (!icp) | ||
963 | return 0; | ||
964 | state = icp->state; | ||
965 | return ((u64)state.cppr << KVM_REG_PPC_ICP_CPPR_SHIFT) | | ||
966 | ((u64)state.xisr << KVM_REG_PPC_ICP_XISR_SHIFT) | | ||
967 | ((u64)state.mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT) | | ||
968 | ((u64)state.pending_pri << KVM_REG_PPC_ICP_PPRI_SHIFT); | ||
969 | } | ||
970 | |||
971 | int kvmppc_xics_set_icp(struct kvm_vcpu *vcpu, u64 icpval) | ||
972 | { | ||
973 | struct kvmppc_icp *icp = vcpu->arch.icp; | ||
974 | struct kvmppc_xics *xics = vcpu->kvm->arch.xics; | ||
975 | union kvmppc_icp_state old_state, new_state; | ||
976 | struct kvmppc_ics *ics; | ||
977 | u8 cppr, mfrr, pending_pri; | ||
978 | u32 xisr; | ||
979 | u16 src; | ||
980 | bool resend; | ||
981 | |||
982 | if (!icp || !xics) | ||
983 | return -ENOENT; | ||
984 | |||
985 | cppr = icpval >> KVM_REG_PPC_ICP_CPPR_SHIFT; | ||
986 | xisr = (icpval >> KVM_REG_PPC_ICP_XISR_SHIFT) & | ||
987 | KVM_REG_PPC_ICP_XISR_MASK; | ||
988 | mfrr = icpval >> KVM_REG_PPC_ICP_MFRR_SHIFT; | ||
989 | pending_pri = icpval >> KVM_REG_PPC_ICP_PPRI_SHIFT; | ||
990 | |||
991 | /* Require the new state to be internally consistent */ | ||
992 | if (xisr == 0) { | ||
993 | if (pending_pri != 0xff) | ||
994 | return -EINVAL; | ||
995 | } else if (xisr == XICS_IPI) { | ||
996 | if (pending_pri != mfrr || pending_pri >= cppr) | ||
997 | return -EINVAL; | ||
998 | } else { | ||
999 | if (pending_pri >= mfrr || pending_pri >= cppr) | ||
1000 | return -EINVAL; | ||
1001 | ics = kvmppc_xics_find_ics(xics, xisr, &src); | ||
1002 | if (!ics) | ||
1003 | return -EINVAL; | ||
1004 | } | ||
1005 | |||
1006 | new_state.raw = 0; | ||
1007 | new_state.cppr = cppr; | ||
1008 | new_state.xisr = xisr; | ||
1009 | new_state.mfrr = mfrr; | ||
1010 | new_state.pending_pri = pending_pri; | ||
1011 | |||
1012 | /* | ||
1013 | * Deassert the CPU interrupt request. | ||
1014 | * icp_try_update will reassert it if necessary. | ||
1015 | */ | ||
1016 | kvmppc_book3s_dequeue_irqprio(icp->vcpu, | ||
1017 | BOOK3S_INTERRUPT_EXTERNAL_LEVEL); | ||
1018 | |||
1019 | /* | ||
1020 | * Note that if we displace an interrupt from old_state.xisr, | ||
1021 | * we don't mark it as rejected. We expect userspace to set | ||
1022 | * the state of the interrupt sources to be consistent with | ||
1023 | * the ICP states (either before or afterwards, which doesn't | ||
1024 | * matter). We do handle resends due to CPPR becoming less | ||
1025 | * favoured because that is necessary to end up with a | ||
1026 | * consistent state in the situation where userspace restores | ||
1027 | * the ICS states before the ICP states. | ||
1028 | */ | ||
1029 | do { | ||
1030 | old_state = ACCESS_ONCE(icp->state); | ||
1031 | |||
1032 | if (new_state.mfrr <= old_state.mfrr) { | ||
1033 | resend = false; | ||
1034 | new_state.need_resend = old_state.need_resend; | ||
1035 | } else { | ||
1036 | resend = old_state.need_resend; | ||
1037 | new_state.need_resend = 0; | ||
1038 | } | ||
1039 | } while (!icp_try_update(icp, old_state, new_state, false)); | ||
1040 | |||
1041 | if (resend) | ||
1042 | icp_check_resend(xics, icp); | ||
1043 | |||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
957 | /* -- ioctls -- */ | 1047 | /* -- ioctls -- */ |
958 | 1048 | ||
959 | int kvm_vm_ioctl_xics_irq(struct kvm *kvm, struct kvm_irq_level *args) | 1049 | int kvm_vm_ioctl_xics_irq(struct kvm *kvm, struct kvm_irq_level *args) |