diff mbox

[v2,2/2] ppc/spapr_hcall: Implement H_RANDOM hypercall in QEMU

Message ID 1441046762-5788-3-git-send-email-thuth@redhat.com
State New
Headers show

Commit Message

Thomas Huth Aug. 31, 2015, 6:46 p.m. UTC
The PAPR interface provides a hypercall to pass high-quality
hardware generated random numbers to guests. So let's provide
this call in QEMU, too, so that guests that do not support
virtio-rnd yet can get good random numbers, too.
Please note that this hypercall should provide "good" random data
instead of pseudo-random, so the function uses the RngBackend to
retrieve the values instead of using a "simple" library function
like rand() or g_random_int(). Since there are multiple RngBackends
available, the user must select an appropriate backend via the
"h-random" property of the the machine state to enable it, e.g.

 qemu-system-ppc64 -M pseries,h-random=rng-random ...

to use the /dev/random backend, or "h-random=rng-egd" to use the
Entropy Gathering Daemon instead.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 hw/ppc/spapr.c         | 36 +++++++++++++++++++++---
 hw/ppc/spapr_hcall.c   | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr.h |  2 ++
 3 files changed, 109 insertions(+), 4 deletions(-)

Comments

David Gibson Sept. 1, 2015, 12:47 a.m. UTC | #1
On Mon, Aug 31, 2015 at 08:46:02PM +0200, Thomas Huth wrote:
> The PAPR interface provides a hypercall to pass high-quality
> hardware generated random numbers to guests. So let's provide
> this call in QEMU, too, so that guests that do not support
> virtio-rnd yet can get good random numbers, too.
> Please note that this hypercall should provide "good" random data
> instead of pseudo-random, so the function uses the RngBackend to
> retrieve the values instead of using a "simple" library function
> like rand() or g_random_int(). Since there are multiple RngBackends
> available, the user must select an appropriate backend via the
> "h-random" property of the the machine state to enable it, e.g.
> 
>  qemu-system-ppc64 -M pseries,h-random=rng-random ...

I think it would be a better idea to require that the backend RNG be
constructed with -object, then give its id to the pseries code.  That
matches what's needed for virtio-rng more closely, and also makes it
simpler to supply parameters to the RNG backend, if necessary.

There's also a wart in that whatever the user specifies by way of
backend, it will be silently overridden if KVM does implement the
H_RANDOM call.  I'm not sure how best to handle that.

> to use the /dev/random backend, or "h-random=rng-egd" to use the
> Entropy Gathering Daemon instead.
> 
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
>  hw/ppc/spapr.c         | 36 +++++++++++++++++++++---
>  hw/ppc/spapr_hcall.c   | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/spapr.h |  2 ++
>  3 files changed, 109 insertions(+), 4 deletions(-)
> 
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index bc3a112..3db87b5 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -312,7 +312,8 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
>                                     hwaddr kernel_size,
>                                     bool little_endian,
>                                     const char *kernel_cmdline,
> -                                   uint32_t epow_irq)
> +                                   uint32_t epow_irq,
> +                                   bool enable_h_random)
>  {
>      void *fdt;
>      uint32_t start_prop = cpu_to_be32(initrd_base);
> @@ -466,7 +467,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
>  
>      _FDT((fdt_end_node(fdt)));
>  
> -    if (kvmppc_hwrng_present()) {
> +    if (enable_h_random) {
>          _FDT(fdt_begin_node(fdt, "ibm,platform-facilities"));
>  
>          _FDT(fdt_property_string(fdt, "name", "ibm,platform-facilities"));
> @@ -1472,7 +1473,7 @@ static void ppc_spapr_init(MachineState *machine)
>      uint32_t initrd_base = 0;
>      long kernel_size = 0, initrd_size = 0;
>      long load_limit, fw_size;
> -    bool kernel_le = false;
> +    bool kernel_le = false, enable_h_random;

I'd prefer to have the new variable on a new line - this way makes it
very easy to miss the initializer on the old one.

>      char *filename;
>  
>      msi_supported = true;
> @@ -1699,6 +1700,10 @@ static void ppc_spapr_init(MachineState *machine)
>      }
>      g_free(filename);
>  
> +    /* Enable H_RANDOM hypercall if requested by user or supported by kernel */
> +    enable_h_random = kvmppc_hwrng_present() ||
> +                      !spapr_h_random_init(spapr->h_random_type);
> +
>      /* FIXME: Should register things through the MachineState's qdev
>       * interface, this is a legacy from the sPAPREnvironment structure
>       * which predated MachineState but had a similar function */
> @@ -1710,7 +1715,8 @@ static void ppc_spapr_init(MachineState *machine)
>      spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
>                                              kernel_size, kernel_le,
>                                              kernel_cmdline,
> -                                            spapr->check_exception_irq);
> +                                            spapr->check_exception_irq,
> +                                            enable_h_random);
>      assert(spapr->fdt_skel != NULL);
>  
>      /* used by RTAS */
> @@ -1810,6 +1816,21 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp)
>      spapr->kvm_type = g_strdup(value);
>  }
>  
> +static char *spapr_get_h_random_type(Object *obj, Error **errp)
> +{
> +    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
> +
> +    return g_strdup(spapr->h_random_type);
> +}
> +
> +static void spapr_set_h_random_type(Object *obj, const char *val, Error **errp)
> +{
> +    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
> +
> +    g_free(spapr->h_random_type);
> +    spapr->h_random_type = g_strdup(val);
> +}
> +
>  static void spapr_machine_initfn(Object *obj)
>  {
>      object_property_add_str(obj, "kvm-type",
> @@ -1817,6 +1838,13 @@ static void spapr_machine_initfn(Object *obj)
>      object_property_set_description(obj, "kvm-type",
>                                      "Specifies the KVM virtualization mode (HV, PR)",
>                                      NULL);
> +
> +    object_property_add_str(obj, "h-random", spapr_get_h_random_type,
> +                            spapr_set_h_random_type, NULL);
> +    object_property_set_description(obj, "h-random",
> +                                    "Select RNG backend for H_RANDOM hypercall "
> +                                    "(rng-random, rng-egd)",
> +                                    NULL);
>  }
>  
>  static void ppc_cpu_do_nmi_on_cpu(void *arg)
> diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
> index 652ddf6..ff9d4fd 100644
> --- a/hw/ppc/spapr_hcall.c
> +++ b/hw/ppc/spapr_hcall.c
> @@ -1,4 +1,8 @@
>  #include "sysemu/sysemu.h"
> +#include "sysemu/rng.h"
> +#include "sysemu/rng-random.h"
> +#include "qom/object_interfaces.h"
> +#include "qemu/error-report.h"
>  #include "cpu.h"
>  #include "helper_regs.h"
>  #include "hw/ppc/spapr.h"
> @@ -929,6 +933,77 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
>      return H_SUCCESS;
>  }
>  
> +typedef struct HRandomData {
> +    QemuSemaphore sem;
> +    union {
> +        uint64_t v64;
> +        uint8_t v8[8];
> +    } val;
> +    int received;
> +} HRandomData;
> +
> +static RndRandom *hrandom_rng;

Couldn't you avoid the new global by looking this up through the
sPAPRMachineState?

> +
> +static void random_recv(void *dest, const void *src, size_t size)
> +{
> +    HRandomData *hrcrdp = dest;
> +
> +    if (src && size > 0) {
> +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);

I'd be happier with an assert() ensuring that size doesn't exceed the
buffer space we have left.

> +        hrcrdp->received += size;
> +    }
> +    qemu_sem_post(&hrcrdp->sem);

Could you avoid a few wakeups by only posting the semaphore once the
buffer is filled?

> +}
> +
> +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
> +                             target_ulong opcode, target_ulong *args)
> +{
> +    HRandomData hrcrd;
> +
> +    if (!hrandom_rng) {
> +        return H_HARDWARE;
> +    }
> +
> +    qemu_sem_init(&hrcrd.sem, 0);
> +    hrcrd.val.v64 = 0;
> +    hrcrd.received = 0;
> +
> +    qemu_mutex_unlock_iothread();
> +    while (hrcrd.received < 8) {
> +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
> +                                    8 - hrcrd.received, random_recv, &hrcrd);
> +        qemu_sem_wait(&hrcrd.sem);
> +    }
> +    qemu_mutex_lock_iothread();
> +
> +    qemu_sem_destroy(&hrcrd.sem);
> +    args[0] = hrcrd.val.v64;
> +
> +    return H_SUCCESS;
> +}
> +
> +int spapr_h_random_init(const char *backend_type)
> +{
> +    Error *local_err = NULL;
> +
> +    if (!backend_type) {
> +        return -1;
> +    }
> +
> +    hrandom_rng = RNG_RANDOM(object_new(backend_type));
> +    user_creatable_complete(OBJECT(hrandom_rng), &local_err);
> +    if (local_err) {
> +        error_printf("Failed to initialize backend for H_RANDOM:\n  %s\n",
> +                     error_get_pretty(local_err));
> +        object_unref(OBJECT(hrandom_rng));
> +        return -1;
> +    }
> +
> +    spapr_register_hypercall(H_RANDOM, h_random);
> +
> +    return 0;
> +}
> +
>  static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
>  static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1];
>  
> diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
> index ab8906f..de17624 100644
> --- a/include/hw/ppc/spapr.h
> +++ b/include/hw/ppc/spapr.h
> @@ -76,6 +76,7 @@ struct sPAPRMachineState {
>  
>      /*< public >*/
>      char *kvm_type;
> +    char *h_random_type;
>  };
>  
>  #define H_SUCCESS         0
> @@ -592,6 +593,7 @@ int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
>  void spapr_pci_switch_vga(bool big_endian);
>  void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
>  void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
> +int spapr_h_random_init(const char *backend_type);
>  
>  /* rtas-configure-connector state */
>  struct sPAPRConfigureConnectorState {
Thomas Huth Sept. 1, 2015, 11:03 a.m. UTC | #2
On 01/09/15 02:47, David Gibson wrote:
> On Mon, Aug 31, 2015 at 08:46:02PM +0200, Thomas Huth wrote:
>> The PAPR interface provides a hypercall to pass high-quality
>> hardware generated random numbers to guests. So let's provide
>> this call in QEMU, too, so that guests that do not support
>> virtio-rnd yet can get good random numbers, too.
>> Please note that this hypercall should provide "good" random data
>> instead of pseudo-random, so the function uses the RngBackend to
>> retrieve the values instead of using a "simple" library function
>> like rand() or g_random_int(). Since there are multiple RngBackends
>> available, the user must select an appropriate backend via the
>> "h-random" property of the the machine state to enable it, e.g.
>>
>>  qemu-system-ppc64 -M pseries,h-random=rng-random ...
> 
> I think it would be a better idea to require that the backend RNG be
> constructed with -object, then give its id to the pseries code.  That
> matches what's needed for virtio-rng more closely, and also makes it
> simpler to supply parameters to the RNG backend, if necessary.

Ok, sounds like a good idea, will rework my patch accordingly.

> There's also a wart in that whatever the user specifies by way of
> backend, it will be silently overridden if KVM does implement the
> H_RANDOM call.  I'm not sure how best to handle that.

We could either print a warning message if we detect that the in-kernel
implementation is available and the user still tries to use the QEMU
implementation - or we simply do not enable the in-kernel hypercall via
kvmppc_enable_hcall() if the user tried to use the QEMU implementation.
What would you prefer?

>> to use the /dev/random backend, or "h-random=rng-egd" to use the
>> Entropy Gathering Daemon instead.
>>
>> Signed-off-by: Thomas Huth <thuth@redhat.com>
>> ---
>>  hw/ppc/spapr.c         | 36 +++++++++++++++++++++---
>>  hw/ppc/spapr_hcall.c   | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/spapr.h |  2 ++
>>  3 files changed, 109 insertions(+), 4 deletions(-)
>>
>> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
>> index bc3a112..3db87b5 100644
>> --- a/hw/ppc/spapr.c
>> +++ b/hw/ppc/spapr.c
...
>> @@ -1472,7 +1473,7 @@ static void ppc_spapr_init(MachineState *machine)
>>      uint32_t initrd_base = 0;
>>      long kernel_size = 0, initrd_size = 0;
>>      long load_limit, fw_size;
>> -    bool kernel_le = false;
>> +    bool kernel_le = false, enable_h_random;
> 
> I'd prefer to have the new variable on a new line - this way makes it
> very easy to miss the initializer on the old one.

Ok.

...
>> diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
>> index 652ddf6..ff9d4fd 100644
>> --- a/hw/ppc/spapr_hcall.c
>> +++ b/hw/ppc/spapr_hcall.c
>> @@ -1,4 +1,8 @@
>>  #include "sysemu/sysemu.h"
>> +#include "sysemu/rng.h"
>> +#include "sysemu/rng-random.h"
>> +#include "qom/object_interfaces.h"
>> +#include "qemu/error-report.h"
>>  #include "cpu.h"
>>  #include "helper_regs.h"
>>  #include "hw/ppc/spapr.h"
>> @@ -929,6 +933,77 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
>>      return H_SUCCESS;
>>  }
>>  
>> +typedef struct HRandomData {
>> +    QemuSemaphore sem;
>> +    union {
>> +        uint64_t v64;
>> +        uint8_t v8[8];
>> +    } val;
>> +    int received;
>> +} HRandomData;
>> +
>> +static RndRandom *hrandom_rng;
> 
> Couldn't you avoid the new global by looking this up through the
> sPAPRMachineState?

Sure.

>> +static void random_recv(void *dest, const void *src, size_t size)
>> +{
>> +    HRandomData *hrcrdp = dest;
>> +
>> +    if (src && size > 0) {
>> +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
> 
> I'd be happier with an assert() ensuring that size doesn't exceed the
> buffer space we have left.

That's a different issue - the if-statement above seems to be necassary
because the callback sometimes seems to be called with size = 0 or src =
NULL, so I had to add this here to avoid crashes.
But I can also add an assert(hrcrdp->received + size <= 8) here if you
like, just to be sure.

>> +        hrcrdp->received += size;
>> +    }
>> +    qemu_sem_post(&hrcrdp->sem);
> 
> Could you avoid a few wakeups by only posting the semaphore once the
> buffer is filled?

Then the callback functions has to trigger the next
rng_backend_request_entropy() ... not sure yet whether I like that
idea... but I can have a try.

 Thomas
Amit Shah Sept. 2, 2015, 5:34 a.m. UTC | #3
On (Mon) 31 Aug 2015 [20:46:02], Thomas Huth wrote:
> The PAPR interface provides a hypercall to pass high-quality
> hardware generated random numbers to guests. So let's provide
> this call in QEMU, too, so that guests that do not support
> virtio-rnd yet can get good random numbers, too.

virtio-rng, not rnd.

Can you elaborate what you mean by 'guests that do not support
virtio-rng yet'?  The Linux kernel has had the virtio-rng driver since
2.6.26, so I'm assuming that's not the thing you're alluding to.

Not saying this hypercall isn't a good idea, just asking why.  I think
there's are valid reasons like the driver fails to load, or the driver
is compiled out, or simply is loaded too late in the boot cycle.

> Please note that this hypercall should provide "good" random data
> instead of pseudo-random, so the function uses the RngBackend to
> retrieve the values instead of using a "simple" library function
> like rand() or g_random_int(). Since there are multiple RngBackends
> available, the user must select an appropriate backend via the
> "h-random" property of the the machine state to enable it, e.g.
> 
>  qemu-system-ppc64 -M pseries,h-random=rng-random ...
> 
> to use the /dev/random backend, or "h-random=rng-egd" to use the
> Entropy Gathering Daemon instead.

I was going to suggest using -object here, but already I see you and
David have reached an agreement for that.

Out of curiosity: what does the host kernel use for its source when
going the hypercall route?

> +static void random_recv(void *dest, const void *src, size_t size)
> +{
> +    HRandomData *hrcrdp = dest;
> +
> +    if (src && size > 0) {
> +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
> +        hrcrdp->received += size;
> +    }
> +    qemu_sem_post(&hrcrdp->sem);
> +}
> +
> +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
> +                             target_ulong opcode, target_ulong *args)
> +{
> +    HRandomData hrcrd;
> +
> +    if (!hrandom_rng) {
> +        return H_HARDWARE;
> +    }
> +
> +    qemu_sem_init(&hrcrd.sem, 0);
> +    hrcrd.val.v64 = 0;
> +    hrcrd.received = 0;
> +
> +    qemu_mutex_unlock_iothread();
> +    while (hrcrd.received < 8) {
> +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
> +                                    8 - hrcrd.received, random_recv, &hrcrd);
> +        qemu_sem_wait(&hrcrd.sem);
> +    }

Is it possible for a second hypercall to arrive while the first is
waiting for the backend to provide data?

> +    qemu_mutex_lock_iothread();
> +
> +    qemu_sem_destroy(&hrcrd.sem);
> +    args[0] = hrcrd.val.v64;
> +
> +    return H_SUCCESS;
> +}

		Amit
David Gibson Sept. 2, 2015, 7:48 a.m. UTC | #4
On Wed, Sep 02, 2015 at 11:04:12AM +0530, Amit Shah wrote:
> On (Mon) 31 Aug 2015 [20:46:02], Thomas Huth wrote:
> > The PAPR interface provides a hypercall to pass high-quality
> > hardware generated random numbers to guests. So let's provide
> > this call in QEMU, too, so that guests that do not support
> > virtio-rnd yet can get good random numbers, too.
> 
> virtio-rng, not rnd.
> 
> Can you elaborate what you mean by 'guests that do not support
> virtio-rng yet'?  The Linux kernel has had the virtio-rng driver since
> 2.6.26, so I'm assuming that's not the thing you're alluding to.
> 
> Not saying this hypercall isn't a good idea, just asking why.  I think
> there's are valid reasons like the driver fails to load, or the driver
> is compiled out, or simply is loaded too late in the boot cycle.

Yeah, I think we'd be talking about guests that just don't have it
configured, although I suppose it's possible someone out there is
using something earlier than 2.6.26 as well.  Note that H_RANDOM has
been supported under PowerVM for a long time, and PowerVM doesn't have
any virtio support.  So it is plausible that there are guests out
there with with H_RANDOM support but no virtio-rng support, although I
don't know of any examples specifically.  RHEL6 had virtio support,
including virtio-rng more or less by accident (since it was only
supported under PowerVM).  SLES may not have made the same fortunate
error - I don't have a system handy to check.

> > Please note that this hypercall should provide "good" random data
> > instead of pseudo-random, so the function uses the RngBackend to
> > retrieve the values instead of using a "simple" library function
> > like rand() or g_random_int(). Since there are multiple RngBackends
> > available, the user must select an appropriate backend via the
> > "h-random" property of the the machine state to enable it, e.g.
> > 
> >  qemu-system-ppc64 -M pseries,h-random=rng-random ...
> > 
> > to use the /dev/random backend, or "h-random=rng-egd" to use the
> > Entropy Gathering Daemon instead.
> 
> I was going to suggest using -object here, but already I see you and
> David have reached an agreement for that.
> 
> Out of curiosity: what does the host kernel use for its source when
> going the hypercall route?

I believe it draws from the same entropy pool as /dev/random.

> > +static void random_recv(void *dest, const void *src, size_t size)
> > +{
> > +    HRandomData *hrcrdp = dest;
> > +
> > +    if (src && size > 0) {
> > +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
> > +        hrcrdp->received += size;
> > +    }
> > +    qemu_sem_post(&hrcrdp->sem);
> > +}
> > +
> > +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
> > +                             target_ulong opcode, target_ulong *args)
> > +{
> > +    HRandomData hrcrd;
> > +
> > +    if (!hrandom_rng) {
> > +        return H_HARDWARE;
> > +    }
> > +
> > +    qemu_sem_init(&hrcrd.sem, 0);
> > +    hrcrd.val.v64 = 0;
> > +    hrcrd.received = 0;
> > +
> > +    qemu_mutex_unlock_iothread();
> > +    while (hrcrd.received < 8) {
> > +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
> > +                                    8 - hrcrd.received, random_recv, &hrcrd);
> > +        qemu_sem_wait(&hrcrd.sem);
> > +    }
> 
> Is it possible for a second hypercall to arrive while the first is
> waiting for the backend to provide data?

Yes it is.  The hypercall itself is synchronous, but you could get
concurrent calls from different guest CPUs.  Hence the need for
iothread unlocking.

> > +    qemu_mutex_lock_iothread();
> > +
> > +    qemu_sem_destroy(&hrcrd.sem);
> > +    args[0] = hrcrd.val.v64;
> > +
> > +    return H_SUCCESS;
> > +}
> 
> 		Amit
>
Thomas Huth Sept. 2, 2015, 8:58 a.m. UTC | #5
On 02/09/15 09:48, David Gibson wrote:
> On Wed, Sep 02, 2015 at 11:04:12AM +0530, Amit Shah wrote:
>> On (Mon) 31 Aug 2015 [20:46:02], Thomas Huth wrote:
>>> The PAPR interface provides a hypercall to pass high-quality
>>> hardware generated random numbers to guests. So let's provide
>>> this call in QEMU, too, so that guests that do not support
>>> virtio-rnd yet can get good random numbers, too.
>>
>> virtio-rng, not rnd.

Oh, sorry, I'll fix the description.

>> Can you elaborate what you mean by 'guests that do not support
>> virtio-rng yet'?  The Linux kernel has had the virtio-rng driver since
>> 2.6.26, so I'm assuming that's not the thing you're alluding to.
>>
>> Not saying this hypercall isn't a good idea, just asking why.  I think
>> there's are valid reasons like the driver fails to load, or the driver
>> is compiled out, or simply is loaded too late in the boot cycle.
> 
> Yeah, I think we'd be talking about guests that just don't have it
> configured, although I suppose it's possible someone out there is
> using something earlier than 2.6.26 as well.  Note that H_RANDOM has
> been supported under PowerVM for a long time, and PowerVM doesn't have
> any virtio support.  So it is plausible that there are guests out
> there with with H_RANDOM support but no virtio-rng support, although I
> don't know of any examples specifically.  RHEL6 had virtio support,
> including virtio-rng more or less by accident (since it was only
> supported under PowerVM).  SLES may not have made the same fortunate
> error - I don't have a system handy to check.

Right, thanks David, I couldn't have explained it better.

>>> Please note that this hypercall should provide "good" random data
>>> instead of pseudo-random, so the function uses the RngBackend to
>>> retrieve the values instead of using a "simple" library function
>>> like rand() or g_random_int(). Since there are multiple RngBackends
>>> available, the user must select an appropriate backend via the
>>> "h-random" property of the the machine state to enable it, e.g.
>>>
>>>  qemu-system-ppc64 -M pseries,h-random=rng-random ...
>>>
>>> to use the /dev/random backend, or "h-random=rng-egd" to use the
>>> Entropy Gathering Daemon instead.
>>
>> I was going to suggest using -object here, but already I see you and
>> David have reached an agreement for that.
>>
>> Out of curiosity: what does the host kernel use for its source when
>> going the hypercall route?
> 
> I believe it draws from the same entropy pool as /dev/random.

The H_RANDOM handler in the kernel uses powernv_get_random_real_mode()
in arch/powerpc/platforms/powernv/rng.c ... that seems to be a
powernv-only pool (but it is also used to feed the normal kernel entropy
pool, I think), but I am not an expert here so I might be wrong.

>>> +static void random_recv(void *dest, const void *src, size_t size)
>>> +{
>>> +    HRandomData *hrcrdp = dest;
>>> +
>>> +    if (src && size > 0) {
>>> +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
>>> +        hrcrdp->received += size;
>>> +    }
>>> +    qemu_sem_post(&hrcrdp->sem);
>>> +}
>>> +
>>> +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>>> +                             target_ulong opcode, target_ulong *args)
>>> +{
>>> +    HRandomData hrcrd;
>>> +
>>> +    if (!hrandom_rng) {
>>> +        return H_HARDWARE;
>>> +    }
>>> +
>>> +    qemu_sem_init(&hrcrd.sem, 0);
>>> +    hrcrd.val.v64 = 0;
>>> +    hrcrd.received = 0;
>>> +
>>> +    qemu_mutex_unlock_iothread();
>>> +    while (hrcrd.received < 8) {
>>> +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
>>> +                                    8 - hrcrd.received, random_recv, &hrcrd);
>>> +        qemu_sem_wait(&hrcrd.sem);
>>> +    }
>>
>> Is it possible for a second hypercall to arrive while the first is
>> waiting for the backend to provide data?
> 
> Yes it is.  The hypercall itself is synchronous, but you could get
> concurrent calls from different guest CPUs.  Hence the need for
> iothread unlocking.

BQL and semaphore handling should be ok, I think, but one remaining
question is: Can the RngBackend deal with multiple requests in flight
from different vCPUs? Or is it limited to one consumer only? Amit, do
you know this?

 Thomas
Amit Shah Sept. 2, 2015, 10:02 a.m. UTC | #6
On (Wed) 02 Sep 2015 [17:48:01], David Gibson wrote:
> On Wed, Sep 02, 2015 at 11:04:12AM +0530, Amit Shah wrote:
> > On (Mon) 31 Aug 2015 [20:46:02], Thomas Huth wrote:
> > > The PAPR interface provides a hypercall to pass high-quality
> > > hardware generated random numbers to guests. So let's provide
> > > this call in QEMU, too, so that guests that do not support
> > > virtio-rnd yet can get good random numbers, too.
> > 
> > virtio-rng, not rnd.
> > 
> > Can you elaborate what you mean by 'guests that do not support
> > virtio-rng yet'?  The Linux kernel has had the virtio-rng driver since
> > 2.6.26, so I'm assuming that's not the thing you're alluding to.
> > 
> > Not saying this hypercall isn't a good idea, just asking why.  I think
> > there's are valid reasons like the driver fails to load, or the driver
> > is compiled out, or simply is loaded too late in the boot cycle.
> 
> Yeah, I think we'd be talking about guests that just don't have it
> configured, although I suppose it's possible someone out there is
> using something earlier than 2.6.26 as well.  Note that H_RANDOM has
> been supported under PowerVM for a long time, and PowerVM doesn't have
> any virtio support.  So it is plausible that there are guests out
> there with with H_RANDOM support but no virtio-rng support, although I
> don't know of any examples specifically.  RHEL6 had virtio support,
> including virtio-rng more or less by accident (since it was only
> supported under PowerVM).  SLES may not have made the same fortunate
> error - I don't have a system handy to check.

RHEL6 also used 2.6.32, which means it inherited from upstream.  But
you're right that x86 didn't have a device for virtio-rng then.

> > > Please note that this hypercall should provide "good" random data
> > > instead of pseudo-random, so the function uses the RngBackend to
> > > retrieve the values instead of using a "simple" library function
> > > like rand() or g_random_int(). Since there are multiple RngBackends
> > > available, the user must select an appropriate backend via the
> > > "h-random" property of the the machine state to enable it, e.g.
> > > 
> > >  qemu-system-ppc64 -M pseries,h-random=rng-random ...
> > > 
> > > to use the /dev/random backend, or "h-random=rng-egd" to use the
> > > Entropy Gathering Daemon instead.
> > 
> > I was going to suggest using -object here, but already I see you and
> > David have reached an agreement for that.
> > 
> > Out of curiosity: what does the host kernel use for its source when
> > going the hypercall route?
> 
> I believe it draws from the same entropy pool as /dev/random.

OK - I'll take a look there as well.

> > > +static void random_recv(void *dest, const void *src, size_t size)
> > > +{
> > > +    HRandomData *hrcrdp = dest;
> > > +
> > > +    if (src && size > 0) {
> > > +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
> > > +        hrcrdp->received += size;
> > > +    }
> > > +    qemu_sem_post(&hrcrdp->sem);
> > > +}
> > > +
> > > +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
> > > +                             target_ulong opcode, target_ulong *args)
> > > +{
> > > +    HRandomData hrcrd;
> > > +
> > > +    if (!hrandom_rng) {
> > > +        return H_HARDWARE;
> > > +    }
> > > +
> > > +    qemu_sem_init(&hrcrd.sem, 0);
> > > +    hrcrd.val.v64 = 0;
> > > +    hrcrd.received = 0;
> > > +
> > > +    qemu_mutex_unlock_iothread();
> > > +    while (hrcrd.received < 8) {
> > > +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
> > > +                                    8 - hrcrd.received, random_recv, &hrcrd);
> > > +        qemu_sem_wait(&hrcrd.sem);
> > > +    }
> > 
> > Is it possible for a second hypercall to arrive while the first is
> > waiting for the backend to provide data?
> 
> Yes it is.  The hypercall itself is synchronous, but you could get
> concurrent calls from different guest CPUs.  Hence the need for
> iothread unlocking.

OK, thanks!


		Amit
Amit Shah Sept. 2, 2015, 10:06 a.m. UTC | #7
On (Wed) 02 Sep 2015 [10:58:57], Thomas Huth wrote:
> On 02/09/15 09:48, David Gibson wrote:
> > On Wed, Sep 02, 2015 at 11:04:12AM +0530, Amit Shah wrote:
> >> On (Mon) 31 Aug 2015 [20:46:02], Thomas Huth wrote:
> >>> The PAPR interface provides a hypercall to pass high-quality
> >>> hardware generated random numbers to guests. So let's provide
> >>> this call in QEMU, too, so that guests that do not support
> >>> virtio-rnd yet can get good random numbers, too.
> >>
> >> virtio-rng, not rnd.
> 
> Oh, sorry, I'll fix the description.

Thanks.  (It's that way in patch 0 too.)

> >> Can you elaborate what you mean by 'guests that do not support
> >> virtio-rng yet'?  The Linux kernel has had the virtio-rng driver since
> >> 2.6.26, so I'm assuming that's not the thing you're alluding to.
> >>
> >> Not saying this hypercall isn't a good idea, just asking why.  I think
> >> there's are valid reasons like the driver fails to load, or the driver
> >> is compiled out, or simply is loaded too late in the boot cycle.
> > 
> > Yeah, I think we'd be talking about guests that just don't have it
> > configured, although I suppose it's possible someone out there is
> > using something earlier than 2.6.26 as well.  Note that H_RANDOM has
> > been supported under PowerVM for a long time, and PowerVM doesn't have
> > any virtio support.  So it is plausible that there are guests out
> > there with with H_RANDOM support but no virtio-rng support, although I
> > don't know of any examples specifically.  RHEL6 had virtio support,
> > including virtio-rng more or less by accident (since it was only
> > supported under PowerVM).  SLES may not have made the same fortunate
> > error - I don't have a system handy to check.
> 
> Right, thanks David, I couldn't have explained it better.
> 
> >>> Please note that this hypercall should provide "good" random data
> >>> instead of pseudo-random, so the function uses the RngBackend to
> >>> retrieve the values instead of using a "simple" library function
> >>> like rand() or g_random_int(). Since there are multiple RngBackends
> >>> available, the user must select an appropriate backend via the
> >>> "h-random" property of the the machine state to enable it, e.g.
> >>>
> >>>  qemu-system-ppc64 -M pseries,h-random=rng-random ...
> >>>
> >>> to use the /dev/random backend, or "h-random=rng-egd" to use the
> >>> Entropy Gathering Daemon instead.
> >>
> >> I was going to suggest using -object here, but already I see you and
> >> David have reached an agreement for that.
> >>
> >> Out of curiosity: what does the host kernel use for its source when
> >> going the hypercall route?
> > 
> > I believe it draws from the same entropy pool as /dev/random.
> 
> The H_RANDOM handler in the kernel uses powernv_get_random_real_mode()
> in arch/powerpc/platforms/powernv/rng.c ... that seems to be a
> powernv-only pool (but it is also used to feed the normal kernel entropy
> pool, I think), but I am not an expert here so I might be wrong.

Thanks for the pointer, I'm going to take a look.

> >>> +static void random_recv(void *dest, const void *src, size_t size)
> >>> +{
> >>> +    HRandomData *hrcrdp = dest;
> >>> +
> >>> +    if (src && size > 0) {
> >>> +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
> >>> +        hrcrdp->received += size;
> >>> +    }
> >>> +    qemu_sem_post(&hrcrdp->sem);
> >>> +}
> >>> +
> >>> +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
> >>> +                             target_ulong opcode, target_ulong *args)
> >>> +{
> >>> +    HRandomData hrcrd;
> >>> +
> >>> +    if (!hrandom_rng) {
> >>> +        return H_HARDWARE;
> >>> +    }
> >>> +
> >>> +    qemu_sem_init(&hrcrd.sem, 0);
> >>> +    hrcrd.val.v64 = 0;
> >>> +    hrcrd.received = 0;
> >>> +
> >>> +    qemu_mutex_unlock_iothread();
> >>> +    while (hrcrd.received < 8) {
> >>> +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
> >>> +                                    8 - hrcrd.received, random_recv, &hrcrd);
> >>> +        qemu_sem_wait(&hrcrd.sem);
> >>> +    }
> >>
> >> Is it possible for a second hypercall to arrive while the first is
> >> waiting for the backend to provide data?
> > 
> > Yes it is.  The hypercall itself is synchronous, but you could get
> > concurrent calls from different guest CPUs.  Hence the need for
> > iothread unlocking.
> 
> BQL and semaphore handling should be ok, I think, but one remaining
> question is: Can the RngBackend deal with multiple requests in flight
> from different vCPUs? Or is it limited to one consumer only? Amit, do
> you know this?

It's not limited by one consumer, it should work fine for the way
you're using it.  For virtio-rng, though, I've had this feeling for a
while that it won't do the right thing (ie it will source more bytes
than asked for), which bothers me.  One of the things I want to look
at later..


		Amit
Michael Ellerman Sept. 3, 2015, 1:21 a.m. UTC | #8
On Wed, 2015-09-02 at 17:48 +1000, David Gibson wrote:
> On Wed, Sep 02, 2015 at 11:04:12AM +0530, Amit Shah wrote:
> > On (Mon) 31 Aug 2015 [20:46:02], Thomas Huth wrote:
> > > The PAPR interface provides a hypercall to pass high-quality
> > > hardware generated random numbers to guests. So let's provide
> > > this call in QEMU, too, so that guests that do not support
> > > virtio-rnd yet can get good random numbers, too.
> > 
> > virtio-rng, not rnd.
> > 
> > Can you elaborate what you mean by 'guests that do not support
> > virtio-rng yet'?  The Linux kernel has had the virtio-rng driver since
> > 2.6.26, so I'm assuming that's not the thing you're alluding to.
> > 
> > Not saying this hypercall isn't a good idea, just asking why.  I think
> > there's are valid reasons like the driver fails to load, or the driver
> > is compiled out, or simply is loaded too late in the boot cycle.
> 
> Yeah, I think we'd be talking about guests that just don't have it
> configured, although I suppose it's possible someone out there is
> using something earlier than 2.6.26 as well.  Note that H_RANDOM has
> been supported under PowerVM for a long time, and PowerVM doesn't have
> any virtio support.  So it is plausible that there are guests out
> there with with H_RANDOM support but no virtio-rng support, although I
> don't know of any examples specifically.  RHEL6 had virtio support,
> including virtio-rng more or less by accident (since it was only
> supported under PowerVM).  SLES may not have made the same fortunate
> error - I don't have a system handy to check.

There also could be folks who want to run non-Linux operating systems, which
don't have a virtio-rng driver, crazy I know :)

cheers
David Gibson Sept. 3, 2015, 2:17 a.m. UTC | #9
On Thu, Sep 03, 2015 at 11:21:24AM +1000, Michael Ellerman wrote:
> On Wed, 2015-09-02 at 17:48 +1000, David Gibson wrote:
> > On Wed, Sep 02, 2015 at 11:04:12AM +0530, Amit Shah wrote:
> > > On (Mon) 31 Aug 2015 [20:46:02], Thomas Huth wrote:
> > > > The PAPR interface provides a hypercall to pass high-quality
> > > > hardware generated random numbers to guests. So let's provide
> > > > this call in QEMU, too, so that guests that do not support
> > > > virtio-rnd yet can get good random numbers, too.
> > > 
> > > virtio-rng, not rnd.
> > > 
> > > Can you elaborate what you mean by 'guests that do not support
> > > virtio-rng yet'?  The Linux kernel has had the virtio-rng driver since
> > > 2.6.26, so I'm assuming that's not the thing you're alluding to.
> > > 
> > > Not saying this hypercall isn't a good idea, just asking why.  I think
> > > there's are valid reasons like the driver fails to load, or the driver
> > > is compiled out, or simply is loaded too late in the boot cycle.
> > 
> > Yeah, I think we'd be talking about guests that just don't have it
> > configured, although I suppose it's possible someone out there is
> > using something earlier than 2.6.26 as well.  Note that H_RANDOM has
> > been supported under PowerVM for a long time, and PowerVM doesn't have
> > any virtio support.  So it is plausible that there are guests out
> > there with with H_RANDOM support but no virtio-rng support, although I
> > don't know of any examples specifically.  RHEL6 had virtio support,
> > including virtio-rng more or less by accident (since it was only
> > supported under PowerVM).  SLES may not have made the same fortunate
> > error - I don't have a system handy to check.
> 
> There also could be folks who want to run non-Linux operating systems, which
> don't have a virtio-rng driver, crazy I know :)

Well, yes.  Although I don't have any concrete examples of those, either..
Thomas Huth Sept. 7, 2015, 3:05 p.m. UTC | #10
On 01/09/15 02:47, David Gibson wrote:
> On Mon, Aug 31, 2015 at 08:46:02PM +0200, Thomas Huth wrote:
>> The PAPR interface provides a hypercall to pass high-quality
>> hardware generated random numbers to guests. So let's provide
>> this call in QEMU, too, so that guests that do not support
>> virtio-rnd yet can get good random numbers, too.
>> Please note that this hypercall should provide "good" random data
>> instead of pseudo-random, so the function uses the RngBackend to
>> retrieve the values instead of using a "simple" library function
>> like rand() or g_random_int(). Since there are multiple RngBackends
>> available, the user must select an appropriate backend via the
>> "h-random" property of the the machine state to enable it, e.g.
>>
>>  qemu-system-ppc64 -M pseries,h-random=rng-random ...
...
>> diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
>> index 652ddf6..ff9d4fd 100644
>> --- a/hw/ppc/spapr_hcall.c
>> +++ b/hw/ppc/spapr_hcall.c
>> @@ -1,4 +1,8 @@
>>  #include "sysemu/sysemu.h"
>> +#include "sysemu/rng.h"
>> +#include "sysemu/rng-random.h"
>> +#include "qom/object_interfaces.h"
>> +#include "qemu/error-report.h"
>>  #include "cpu.h"
>>  #include "helper_regs.h"
>>  #include "hw/ppc/spapr.h"
>> @@ -929,6 +933,77 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
>>      return H_SUCCESS;
>>  }
>>  
>> +typedef struct HRandomData {
>> +    QemuSemaphore sem;
>> +    union {
>> +        uint64_t v64;
>> +        uint8_t v8[8];
>> +    } val;
>> +    int received;
>> +} HRandomData;
>> +
>> +static RndRandom *hrandom_rng;
> 
> Couldn't you avoid the new global by looking this up through the
> sPAPRMachineState?
> 
>> +
>> +static void random_recv(void *dest, const void *src, size_t size)
>> +{
>> +    HRandomData *hrcrdp = dest;
>> +
>> +    if (src && size > 0) {
>> +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
> 
> I'd be happier with an assert() ensuring that size doesn't exceed the
> buffer space we have left.
> 
>> +        hrcrdp->received += size;
>> +    }
>> +    qemu_sem_post(&hrcrdp->sem);
> 
> Could you avoid a few wakeups by only posting the semaphore once the
> buffer is filled?

I tried that now, but calling rng_backend_request_entropy() from within
the callback function does not work (since entropy_available() in
rng-random.c clears the callback function variable after having called
the callback).

And since you normally seem get 8 bytes in the first shot already anyway
when using a good random number generator source, I think it's best to
simply keep the logic as I've currently got it - at least that's easiest
to understand when reading the source code.

>> +}
>> +
>> +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>> +                             target_ulong opcode, target_ulong *args)
>> +{
>> +    HRandomData hrcrd;
>> +
>> +    if (!hrandom_rng) {
>> +        return H_HARDWARE;
>> +    }
>> +
>> +    qemu_sem_init(&hrcrd.sem, 0);
>> +    hrcrd.val.v64 = 0;
>> +    hrcrd.received = 0;
>> +
>> +    qemu_mutex_unlock_iothread();
>> +    while (hrcrd.received < 8) {
>> +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
>> +                                    8 - hrcrd.received, random_recv, &hrcrd);
>> +        qemu_sem_wait(&hrcrd.sem);
>> +    }
>> +    qemu_mutex_lock_iothread();
>> +
>> +    qemu_sem_destroy(&hrcrd.sem);
>> +    args[0] = hrcrd.val.v64;
>> +
>> +    return H_SUCCESS;
>> +}

I'll post a new version with the other changes soon.

 Thomas
David Gibson Sept. 8, 2015, 1:14 a.m. UTC | #11
On Mon, Sep 07, 2015 at 05:05:48PM +0200, Thomas Huth wrote:
> On 01/09/15 02:47, David Gibson wrote:
> > On Mon, Aug 31, 2015 at 08:46:02PM +0200, Thomas Huth wrote:
> >> The PAPR interface provides a hypercall to pass high-quality
> >> hardware generated random numbers to guests. So let's provide
> >> this call in QEMU, too, so that guests that do not support
> >> virtio-rnd yet can get good random numbers, too.
> >> Please note that this hypercall should provide "good" random data
> >> instead of pseudo-random, so the function uses the RngBackend to
> >> retrieve the values instead of using a "simple" library function
> >> like rand() or g_random_int(). Since there are multiple RngBackends
> >> available, the user must select an appropriate backend via the
> >> "h-random" property of the the machine state to enable it, e.g.
> >>
> >>  qemu-system-ppc64 -M pseries,h-random=rng-random ...
> ...
> >> diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
> >> index 652ddf6..ff9d4fd 100644
> >> --- a/hw/ppc/spapr_hcall.c
> >> +++ b/hw/ppc/spapr_hcall.c
> >> @@ -1,4 +1,8 @@
> >>  #include "sysemu/sysemu.h"
> >> +#include "sysemu/rng.h"
> >> +#include "sysemu/rng-random.h"
> >> +#include "qom/object_interfaces.h"
> >> +#include "qemu/error-report.h"
> >>  #include "cpu.h"
> >>  #include "helper_regs.h"
> >>  #include "hw/ppc/spapr.h"
> >> @@ -929,6 +933,77 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
> >>      return H_SUCCESS;
> >>  }
> >>  
> >> +typedef struct HRandomData {
> >> +    QemuSemaphore sem;
> >> +    union {
> >> +        uint64_t v64;
> >> +        uint8_t v8[8];
> >> +    } val;
> >> +    int received;
> >> +} HRandomData;
> >> +
> >> +static RndRandom *hrandom_rng;
> > 
> > Couldn't you avoid the new global by looking this up through the
> > sPAPRMachineState?
> > 
> >> +
> >> +static void random_recv(void *dest, const void *src, size_t size)
> >> +{
> >> +    HRandomData *hrcrdp = dest;
> >> +
> >> +    if (src && size > 0) {
> >> +        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
> > 
> > I'd be happier with an assert() ensuring that size doesn't exceed the
> > buffer space we have left.
> > 
> >> +        hrcrdp->received += size;
> >> +    }
> >> +    qemu_sem_post(&hrcrdp->sem);
> > 
> > Could you avoid a few wakeups by only posting the semaphore once the
> > buffer is filled?
> 
> I tried that now, but calling rng_backend_request_entropy() from within
> the callback function does not work (since entropy_available() in
> rng-random.c clears the callback function variable after having called
> the callback).

Ah, ok.  I'd missed the fact that rng_backend_request_entropy() was
being called again - I'd assumed that after the first call the backend
would just keep notifying until you had as much data as requested.

> And since you normally seem get 8 bytes in the first shot already anyway
> when using a good random number generator source, I think it's best to
> simply keep the logic as I've currently got it - at least that's easiest
> to understand when reading the source code.

Yes, I concur.

> 
> >> +}
> >> +
> >> +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
> >> +                             target_ulong opcode, target_ulong *args)
> >> +{
> >> +    HRandomData hrcrd;
> >> +
> >> +    if (!hrandom_rng) {
> >> +        return H_HARDWARE;
> >> +    }
> >> +
> >> +    qemu_sem_init(&hrcrd.sem, 0);
> >> +    hrcrd.val.v64 = 0;
> >> +    hrcrd.received = 0;
> >> +
> >> +    qemu_mutex_unlock_iothread();
> >> +    while (hrcrd.received < 8) {
> >> +        rng_backend_request_entropy((RngBackend *)hrandom_rng,
> >> +                                    8 - hrcrd.received, random_recv, &hrcrd);
> >> +        qemu_sem_wait(&hrcrd.sem);
> >> +    }
> >> +    qemu_mutex_lock_iothread();
> >> +
> >> +    qemu_sem_destroy(&hrcrd.sem);
> >> +    args[0] = hrcrd.val.v64;
> >> +
> >> +    return H_SUCCESS;
> >> +}
> 
> I'll post a new version with the other changes soon.
> 
>  Thomas
> 
>
diff mbox

Patch

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index bc3a112..3db87b5 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -312,7 +312,8 @@  static void *spapr_create_fdt_skel(hwaddr initrd_base,
                                    hwaddr kernel_size,
                                    bool little_endian,
                                    const char *kernel_cmdline,
-                                   uint32_t epow_irq)
+                                   uint32_t epow_irq,
+                                   bool enable_h_random)
 {
     void *fdt;
     uint32_t start_prop = cpu_to_be32(initrd_base);
@@ -466,7 +467,7 @@  static void *spapr_create_fdt_skel(hwaddr initrd_base,
 
     _FDT((fdt_end_node(fdt)));
 
-    if (kvmppc_hwrng_present()) {
+    if (enable_h_random) {
         _FDT(fdt_begin_node(fdt, "ibm,platform-facilities"));
 
         _FDT(fdt_property_string(fdt, "name", "ibm,platform-facilities"));
@@ -1472,7 +1473,7 @@  static void ppc_spapr_init(MachineState *machine)
     uint32_t initrd_base = 0;
     long kernel_size = 0, initrd_size = 0;
     long load_limit, fw_size;
-    bool kernel_le = false;
+    bool kernel_le = false, enable_h_random;
     char *filename;
 
     msi_supported = true;
@@ -1699,6 +1700,10 @@  static void ppc_spapr_init(MachineState *machine)
     }
     g_free(filename);
 
+    /* Enable H_RANDOM hypercall if requested by user or supported by kernel */
+    enable_h_random = kvmppc_hwrng_present() ||
+                      !spapr_h_random_init(spapr->h_random_type);
+
     /* FIXME: Should register things through the MachineState's qdev
      * interface, this is a legacy from the sPAPREnvironment structure
      * which predated MachineState but had a similar function */
@@ -1710,7 +1715,8 @@  static void ppc_spapr_init(MachineState *machine)
     spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
                                             kernel_size, kernel_le,
                                             kernel_cmdline,
-                                            spapr->check_exception_irq);
+                                            spapr->check_exception_irq,
+                                            enable_h_random);
     assert(spapr->fdt_skel != NULL);
 
     /* used by RTAS */
@@ -1810,6 +1816,21 @@  static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp)
     spapr->kvm_type = g_strdup(value);
 }
 
+static char *spapr_get_h_random_type(Object *obj, Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+    return g_strdup(spapr->h_random_type);
+}
+
+static void spapr_set_h_random_type(Object *obj, const char *val, Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+    g_free(spapr->h_random_type);
+    spapr->h_random_type = g_strdup(val);
+}
+
 static void spapr_machine_initfn(Object *obj)
 {
     object_property_add_str(obj, "kvm-type",
@@ -1817,6 +1838,13 @@  static void spapr_machine_initfn(Object *obj)
     object_property_set_description(obj, "kvm-type",
                                     "Specifies the KVM virtualization mode (HV, PR)",
                                     NULL);
+
+    object_property_add_str(obj, "h-random", spapr_get_h_random_type,
+                            spapr_set_h_random_type, NULL);
+    object_property_set_description(obj, "h-random",
+                                    "Select RNG backend for H_RANDOM hypercall "
+                                    "(rng-random, rng-egd)",
+                                    NULL);
 }
 
 static void ppc_cpu_do_nmi_on_cpu(void *arg)
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 652ddf6..ff9d4fd 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1,4 +1,8 @@ 
 #include "sysemu/sysemu.h"
+#include "sysemu/rng.h"
+#include "sysemu/rng-random.h"
+#include "qom/object_interfaces.h"
+#include "qemu/error-report.h"
 #include "cpu.h"
 #include "helper_regs.h"
 #include "hw/ppc/spapr.h"
@@ -929,6 +933,77 @@  static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
     return H_SUCCESS;
 }
 
+typedef struct HRandomData {
+    QemuSemaphore sem;
+    union {
+        uint64_t v64;
+        uint8_t v8[8];
+    } val;
+    int received;
+} HRandomData;
+
+static RndRandom *hrandom_rng;
+
+static void random_recv(void *dest, const void *src, size_t size)
+{
+    HRandomData *hrcrdp = dest;
+
+    if (src && size > 0) {
+        memcpy(&hrcrdp->val.v8[hrcrdp->received], src, size);
+        hrcrdp->received += size;
+    }
+    qemu_sem_post(&hrcrdp->sem);
+}
+
+static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                             target_ulong opcode, target_ulong *args)
+{
+    HRandomData hrcrd;
+
+    if (!hrandom_rng) {
+        return H_HARDWARE;
+    }
+
+    qemu_sem_init(&hrcrd.sem, 0);
+    hrcrd.val.v64 = 0;
+    hrcrd.received = 0;
+
+    qemu_mutex_unlock_iothread();
+    while (hrcrd.received < 8) {
+        rng_backend_request_entropy((RngBackend *)hrandom_rng,
+                                    8 - hrcrd.received, random_recv, &hrcrd);
+        qemu_sem_wait(&hrcrd.sem);
+    }
+    qemu_mutex_lock_iothread();
+
+    qemu_sem_destroy(&hrcrd.sem);
+    args[0] = hrcrd.val.v64;
+
+    return H_SUCCESS;
+}
+
+int spapr_h_random_init(const char *backend_type)
+{
+    Error *local_err = NULL;
+
+    if (!backend_type) {
+        return -1;
+    }
+
+    hrandom_rng = RNG_RANDOM(object_new(backend_type));
+    user_creatable_complete(OBJECT(hrandom_rng), &local_err);
+    if (local_err) {
+        error_printf("Failed to initialize backend for H_RANDOM:\n  %s\n",
+                     error_get_pretty(local_err));
+        object_unref(OBJECT(hrandom_rng));
+        return -1;
+    }
+
+    spapr_register_hypercall(H_RANDOM, h_random);
+
+    return 0;
+}
+
 static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
 static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1];
 
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index ab8906f..de17624 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -76,6 +76,7 @@  struct sPAPRMachineState {
 
     /*< public >*/
     char *kvm_type;
+    char *h_random_type;
 };
 
 #define H_SUCCESS         0
@@ -592,6 +593,7 @@  int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
 void spapr_pci_switch_vga(bool big_endian);
 void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
 void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
+int spapr_h_random_init(const char *backend_type);
 
 /* rtas-configure-connector state */
 struct sPAPRConfigureConnectorState {