KVM: PPC: Book3S HV: Add fast real-mode H_RANDOM implementation.
authorMichael Ellerman <michael@ellerman.id.au>
Fri, 20 Mar 2015 09:39:41 +0000 (20:39 +1100)
committerAlexander Graf <agraf@suse.de>
Tue, 21 Apr 2015 13:21:29 +0000 (15:21 +0200)
Some PowerNV systems include a hardware random-number generator.
This HWRNG is present on POWER7+ and POWER8 chips and is capable of
generating one 64-bit random number every microsecond.  The random
numbers are produced by sampling a set of 64 unstable high-frequency
oscillators and are almost completely entropic.

PAPR defines an H_RANDOM hypercall which guests can use to obtain one
64-bit random sample from the HWRNG.  This adds a real-mode
implementation of the H_RANDOM hypercall.  This hypercall was
implemented in real mode because the latency of reading the HWRNG is
generally small compared to the latency of a guest exit and entry for
all the threads in the same virtual core.

Userspace can detect the presence of the HWRNG and the H_RANDOM
implementation by querying the KVM_CAP_PPC_HWRNG capability.  The
H_RANDOM hypercall implementation will only be invoked when the guest
does an H_RANDOM hypercall if userspace first enables the in-kernel
H_RANDOM implementation using the KVM_CAP_PPC_ENABLE_HCALL capability.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
Documentation/virtual/kvm/api.txt
arch/powerpc/include/asm/archrandom.h
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/kvm/book3s_hv_builtin.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/powerpc.c
arch/powerpc/platforms/powernv/rng.c
include/uapi/linux/kvm.h

index bc9f6fe44e27614c2f26f155385fe0d5d13d2e3b..9fa2bf8c3f6f13282c92c33671b5ad9a3eaa1d0a 100644 (file)
@@ -3573,3 +3573,20 @@ struct {
 @ar   - access register number
 
 KVM handlers should exit to userspace with rc = -EREMOTE.
+
+
+8. Other capabilities.
+----------------------
+
+This section lists capabilities that give information about other
+features of the KVM implementation.
+
+8.1 KVM_CAP_PPC_HWRNG
+
+Architectures: ppc
+
+This capability, if KVM_CHECK_EXTENSION indicates that it is
+available, means that that the kernel has an implementation of the
+H_RANDOM hypercall backed by a hardware random-number generator.
+If present, the kernel H_RANDOM handler can be enabled for guest use
+with the KVM_CAP_PPC_ENABLE_HCALL capability.
index bde5311036384c891463de556bb75e62b6d39a7c..0cc6eedc4780325fbf4241738f4694c84d5248b9 100644 (file)
@@ -30,8 +30,6 @@ static inline int arch_has_random(void)
        return !!ppc_md.get_random_long;
 }
 
-int powernv_get_random_long(unsigned long *v);
-
 static inline int arch_get_random_seed_long(unsigned long *v)
 {
        return 0;
@@ -47,4 +45,13 @@ static inline int arch_has_random_seed(void)
 
 #endif /* CONFIG_ARCH_RANDOM */
 
+#ifdef CONFIG_PPC_POWERNV
+int powernv_hwrng_present(void);
+int powernv_get_random_long(unsigned long *v);
+int powernv_get_random_real_mode(unsigned long *v);
+#else
+static inline int powernv_hwrng_present(void) { return 0; }
+static inline int powernv_get_random_real_mode(unsigned long *v) { return 0; }
+#endif
+
 #endif /* _ASM_POWERPC_ARCHRANDOM_H */
index 46bf652c91697c384e8a5323ad5ed428014f9980..b8475daad88471006369ad99c25d6c51efbe7501 100644 (file)
@@ -302,6 +302,8 @@ static inline bool is_kvmppc_hv_enabled(struct kvm *kvm)
        return kvm->arch.kvm_ops == kvmppc_hv_ops;
 }
 
+extern int kvmppc_hwrng_present(void);
+
 /*
  * Cuts out inst bits with ordering according to spec.
  * That means the leftmost bit is zero. All given bits are included.
index 1f083ff8a61a065cd7aa2dc2901000224e515e2e..1954a1c4b1f913c5d542bf571c2a503fb6d4c144 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/cputable.h>
 #include <asm/kvm_ppc.h>
 #include <asm/kvm_book3s.h>
+#include <asm/archrandom.h>
 
 #define KVM_CMA_CHUNK_ORDER    18
 
@@ -169,3 +170,17 @@ int kvmppc_hcall_impl_hv_realmode(unsigned long cmd)
        return 0;
 }
 EXPORT_SYMBOL_GPL(kvmppc_hcall_impl_hv_realmode);
+
+int kvmppc_hwrng_present(void)
+{
+       return powernv_hwrng_present();
+}
+EXPORT_SYMBOL_GPL(kvmppc_hwrng_present);
+
+long kvmppc_h_random(struct kvm_vcpu *vcpu)
+{
+       if (powernv_get_random_real_mode(&vcpu->arch.gpr[4]))
+               return H_SUCCESS;
+
+       return H_HARDWARE;
+}
index 6cbf1630cb70c9d8b306a7628c91a3c9acf35785..0814ca122fcde68aa721b941c57f881d1cae7850 100644 (file)
@@ -1839,6 +1839,121 @@ hcall_real_table:
        .long   0               /* 0x12c */
        .long   0               /* 0x130 */
        .long   DOTSYM(kvmppc_h_set_xdabr) - hcall_real_table
+       .long   0               /* 0x138 */
+       .long   0               /* 0x13c */
+       .long   0               /* 0x140 */
+       .long   0               /* 0x144 */
+       .long   0               /* 0x148 */
+       .long   0               /* 0x14c */
+       .long   0               /* 0x150 */
+       .long   0               /* 0x154 */
+       .long   0               /* 0x158 */
+       .long   0               /* 0x15c */
+       .long   0               /* 0x160 */
+       .long   0               /* 0x164 */
+       .long   0               /* 0x168 */
+       .long   0               /* 0x16c */
+       .long   0               /* 0x170 */
+       .long   0               /* 0x174 */
+       .long   0               /* 0x178 */
+       .long   0               /* 0x17c */
+       .long   0               /* 0x180 */
+       .long   0               /* 0x184 */
+       .long   0               /* 0x188 */
+       .long   0               /* 0x18c */
+       .long   0               /* 0x190 */
+       .long   0               /* 0x194 */
+       .long   0               /* 0x198 */
+       .long   0               /* 0x19c */
+       .long   0               /* 0x1a0 */
+       .long   0               /* 0x1a4 */
+       .long   0               /* 0x1a8 */
+       .long   0               /* 0x1ac */
+       .long   0               /* 0x1b0 */
+       .long   0               /* 0x1b4 */
+       .long   0               /* 0x1b8 */
+       .long   0               /* 0x1bc */
+       .long   0               /* 0x1c0 */
+       .long   0               /* 0x1c4 */
+       .long   0               /* 0x1c8 */
+       .long   0               /* 0x1cc */
+       .long   0               /* 0x1d0 */
+       .long   0               /* 0x1d4 */
+       .long   0               /* 0x1d8 */
+       .long   0               /* 0x1dc */
+       .long   0               /* 0x1e0 */
+       .long   0               /* 0x1e4 */
+       .long   0               /* 0x1e8 */
+       .long   0               /* 0x1ec */
+       .long   0               /* 0x1f0 */
+       .long   0               /* 0x1f4 */
+       .long   0               /* 0x1f8 */
+       .long   0               /* 0x1fc */
+       .long   0               /* 0x200 */
+       .long   0               /* 0x204 */
+       .long   0               /* 0x208 */
+       .long   0               /* 0x20c */
+       .long   0               /* 0x210 */
+       .long   0               /* 0x214 */
+       .long   0               /* 0x218 */
+       .long   0               /* 0x21c */
+       .long   0               /* 0x220 */
+       .long   0               /* 0x224 */
+       .long   0               /* 0x228 */
+       .long   0               /* 0x22c */
+       .long   0               /* 0x230 */
+       .long   0               /* 0x234 */
+       .long   0               /* 0x238 */
+       .long   0               /* 0x23c */
+       .long   0               /* 0x240 */
+       .long   0               /* 0x244 */
+       .long   0               /* 0x248 */
+       .long   0               /* 0x24c */
+       .long   0               /* 0x250 */
+       .long   0               /* 0x254 */
+       .long   0               /* 0x258 */
+       .long   0               /* 0x25c */
+       .long   0               /* 0x260 */
+       .long   0               /* 0x264 */
+       .long   0               /* 0x268 */
+       .long   0               /* 0x26c */
+       .long   0               /* 0x270 */
+       .long   0               /* 0x274 */
+       .long   0               /* 0x278 */
+       .long   0               /* 0x27c */
+       .long   0               /* 0x280 */
+       .long   0               /* 0x284 */
+       .long   0               /* 0x288 */
+       .long   0               /* 0x28c */
+       .long   0               /* 0x290 */
+       .long   0               /* 0x294 */
+       .long   0               /* 0x298 */
+       .long   0               /* 0x29c */
+       .long   0               /* 0x2a0 */
+       .long   0               /* 0x2a4 */
+       .long   0               /* 0x2a8 */
+       .long   0               /* 0x2ac */
+       .long   0               /* 0x2b0 */
+       .long   0               /* 0x2b4 */
+       .long   0               /* 0x2b8 */
+       .long   0               /* 0x2bc */
+       .long   0               /* 0x2c0 */
+       .long   0               /* 0x2c4 */
+       .long   0               /* 0x2c8 */
+       .long   0               /* 0x2cc */
+       .long   0               /* 0x2d0 */
+       .long   0               /* 0x2d4 */
+       .long   0               /* 0x2d8 */
+       .long   0               /* 0x2dc */
+       .long   0               /* 0x2e0 */
+       .long   0               /* 0x2e4 */
+       .long   0               /* 0x2e8 */
+       .long   0               /* 0x2ec */
+       .long   0               /* 0x2f0 */
+       .long   0               /* 0x2f4 */
+       .long   0               /* 0x2f8 */
+       .long   0               /* 0x2fc */
+       .long   DOTSYM(kvmppc_h_random) - hcall_real_table
        .globl  hcall_real_table_end
 hcall_real_table_end:
 
index 24bfe401373e44aad58268c95caf7e2a5e09198e..55a4763d6d115d38877eac0871b3ba21e4fa65c4 100644 (file)
@@ -529,6 +529,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
        case KVM_CAP_PPC_RMA:
                r = 0;
                break;
+       case KVM_CAP_PPC_HWRNG:
+               r = kvmppc_hwrng_present();
+               break;
 #endif
        case KVM_CAP_SYNC_MMU:
 #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
index 80db43944afefe927cec6e09ed088bffb635f45d..6eb808ff637e712e038138488c1dad53ca926264 100644 (file)
 
 struct powernv_rng {
        void __iomem *regs;
+       void __iomem *regs_real;
        unsigned long mask;
 };
 
 static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng);
 
 
+int powernv_hwrng_present(void)
+{
+       struct powernv_rng *rng;
+
+       rng = get_cpu_var(powernv_rng);
+       put_cpu_var(rng);
+       return rng != NULL;
+}
+
 static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val)
 {
        unsigned long parity;
@@ -46,6 +56,17 @@ static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val)
        return val;
 }
 
+int powernv_get_random_real_mode(unsigned long *v)
+{
+       struct powernv_rng *rng;
+
+       rng = raw_cpu_read(powernv_rng);
+
+       *v = rng_whiten(rng, in_rm64(rng->regs_real));
+
+       return 1;
+}
+
 int powernv_get_random_long(unsigned long *v)
 {
        struct powernv_rng *rng;
@@ -80,12 +101,20 @@ static __init void rng_init_per_cpu(struct powernv_rng *rng,
 static __init int rng_create(struct device_node *dn)
 {
        struct powernv_rng *rng;
+       struct resource res;
        unsigned long val;
 
        rng = kzalloc(sizeof(*rng), GFP_KERNEL);
        if (!rng)
                return -ENOMEM;
 
+       if (of_address_to_resource(dn, 0, &res)) {
+               kfree(rng);
+               return -ENXIO;
+       }
+
+       rng->regs_real = (void __iomem *)res.start;
+
        rng->regs = of_iomap(dn, 0);
        if (!rng->regs) {
                kfree(rng);
index f574d7be7631e5795f26a8934391b8b6f1473f8e..4b60056776d1484f4c6c43b6c4a1ef47a2592a61 100644 (file)
@@ -813,6 +813,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_MIPS_MSA 112
 #define KVM_CAP_S390_INJECT_IRQ 113
 #define KVM_CAP_S390_IRQ_STATE 114
+#define KVM_CAP_PPC_HWRNG 115
 
 #ifdef KVM_CAP_IRQ_ROUTING