Commit e928e9cb authored by Michael Ellerman's avatar Michael Ellerman Committed by Alexander Graf

KVM: PPC: Book3S HV: Add fast real-mode H_RANDOM implementation.

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: default avatarMichael Ellerman <michael@ellerman.id.au>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
parent 99342cf8
...@@ -3573,3 +3573,20 @@ struct { ...@@ -3573,3 +3573,20 @@ struct {
@ar - access register number @ar - access register number
KVM handlers should exit to userspace with rc = -EREMOTE. 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.
...@@ -30,8 +30,6 @@ static inline int arch_has_random(void) ...@@ -30,8 +30,6 @@ static inline int arch_has_random(void)
return !!ppc_md.get_random_long; 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) static inline int arch_get_random_seed_long(unsigned long *v)
{ {
return 0; return 0;
...@@ -47,4 +45,13 @@ static inline int arch_has_random_seed(void) ...@@ -47,4 +45,13 @@ static inline int arch_has_random_seed(void)
#endif /* CONFIG_ARCH_RANDOM */ #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 */ #endif /* _ASM_POWERPC_ARCHRANDOM_H */
...@@ -302,6 +302,8 @@ static inline bool is_kvmppc_hv_enabled(struct kvm *kvm) ...@@ -302,6 +302,8 @@ static inline bool is_kvmppc_hv_enabled(struct kvm *kvm)
return kvm->arch.kvm_ops == kvmppc_hv_ops; return kvm->arch.kvm_ops == kvmppc_hv_ops;
} }
extern int kvmppc_hwrng_present(void);
/* /*
* Cuts out inst bits with ordering according to spec. * Cuts out inst bits with ordering according to spec.
* That means the leftmost bit is zero. All given bits are included. * That means the leftmost bit is zero. All given bits are included.
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <asm/cputable.h> #include <asm/cputable.h>
#include <asm/kvm_ppc.h> #include <asm/kvm_ppc.h>
#include <asm/kvm_book3s.h> #include <asm/kvm_book3s.h>
#include <asm/archrandom.h>
#define KVM_CMA_CHUNK_ORDER 18 #define KVM_CMA_CHUNK_ORDER 18
...@@ -169,3 +170,17 @@ int kvmppc_hcall_impl_hv_realmode(unsigned long cmd) ...@@ -169,3 +170,17 @@ int kvmppc_hcall_impl_hv_realmode(unsigned long cmd)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(kvmppc_hcall_impl_hv_realmode); 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;
}
...@@ -1839,6 +1839,121 @@ hcall_real_table: ...@@ -1839,6 +1839,121 @@ hcall_real_table:
.long 0 /* 0x12c */ .long 0 /* 0x12c */
.long 0 /* 0x130 */ .long 0 /* 0x130 */
.long DOTSYM(kvmppc_h_set_xdabr) - hcall_real_table .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 .globl hcall_real_table_end
hcall_real_table_end: hcall_real_table_end:
......
...@@ -529,6 +529,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) ...@@ -529,6 +529,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_PPC_RMA: case KVM_CAP_PPC_RMA:
r = 0; r = 0;
break; break;
case KVM_CAP_PPC_HWRNG:
r = kvmppc_hwrng_present();
break;
#endif #endif
case KVM_CAP_SYNC_MMU: case KVM_CAP_SYNC_MMU:
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
......
...@@ -24,12 +24,22 @@ ...@@ -24,12 +24,22 @@
struct powernv_rng { struct powernv_rng {
void __iomem *regs; void __iomem *regs;
void __iomem *regs_real;
unsigned long mask; unsigned long mask;
}; };
static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng); 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) static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val)
{ {
unsigned long parity; unsigned long parity;
...@@ -46,6 +56,17 @@ static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val) ...@@ -46,6 +56,17 @@ static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val)
return 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) int powernv_get_random_long(unsigned long *v)
{ {
struct powernv_rng *rng; struct powernv_rng *rng;
...@@ -80,12 +101,20 @@ static __init void rng_init_per_cpu(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) static __init int rng_create(struct device_node *dn)
{ {
struct powernv_rng *rng; struct powernv_rng *rng;
struct resource res;
unsigned long val; unsigned long val;
rng = kzalloc(sizeof(*rng), GFP_KERNEL); rng = kzalloc(sizeof(*rng), GFP_KERNEL);
if (!rng) if (!rng)
return -ENOMEM; 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); rng->regs = of_iomap(dn, 0);
if (!rng->regs) { if (!rng->regs) {
kfree(rng); kfree(rng);
......
...@@ -813,6 +813,7 @@ struct kvm_ppc_smmu_info { ...@@ -813,6 +813,7 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_MIPS_MSA 112 #define KVM_CAP_MIPS_MSA 112
#define KVM_CAP_S390_INJECT_IRQ 113 #define KVM_CAP_S390_INJECT_IRQ 113
#define KVM_CAP_S390_IRQ_STATE 114 #define KVM_CAP_S390_IRQ_STATE 114
#define KVM_CAP_PPC_HWRNG 115
#ifdef KVM_CAP_IRQ_ROUTING #ifdef KVM_CAP_IRQ_ROUTING
......
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