1/*
2 * QEMU guest-visible random functions
3 *
4 * Copyright 2019 Linaro, Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 */
11
12#include "qemu/osdep.h"
13#include "qemu/cutils.h"
14#include "qapi/error.h"
15#include "qemu/guest-random.h"
16#include "crypto/random.h"
17
18
19static __thread GRand *thread_rand;
20static bool deterministic;
21
22
23static int glib_random_bytes(void *buf, size_t len)
24{
25 GRand *rand = thread_rand;
26 size_t i;
27 uint32_t x;
28
29 if (unlikely(rand == NULL)) {
30 /* Thread not initialized for a cpu, or main w/o -seed. */
31 thread_rand = rand = g_rand_new();
32 }
33
34 for (i = 0; i + 4 <= len; i += 4) {
35 x = g_rand_int(rand);
36 __builtin_memcpy(buf + i, &x, 4);
37 }
38 if (i < len) {
39 x = g_rand_int(rand);
40 __builtin_memcpy(buf + i, &x, i - len);
41 }
42 return 0;
43}
44
45int qemu_guest_getrandom(void *buf, size_t len, Error **errp)
46{
47 if (unlikely(deterministic)) {
48 /* Deterministic implementation using Glib's Mersenne Twister. */
49 return glib_random_bytes(buf, len);
50 } else {
51 /* Non-deterministic implementation using crypto routines. */
52 return qcrypto_random_bytes(buf, len, errp);
53 }
54}
55
56void qemu_guest_getrandom_nofail(void *buf, size_t len)
57{
58 (void)qemu_guest_getrandom(buf, len, &error_fatal);
59}
60
61uint64_t qemu_guest_random_seed_thread_part1(void)
62{
63 if (deterministic) {
64 uint64_t ret;
65 glib_random_bytes(&ret, sizeof(ret));
66 return ret;
67 }
68 return 0;
69}
70
71void qemu_guest_random_seed_thread_part2(uint64_t seed)
72{
73 g_assert(thread_rand == NULL);
74 if (deterministic) {
75 thread_rand =
76 g_rand_new_with_seed_array((const guint32 *)&seed,
77 sizeof(seed) / sizeof(guint32));
78 }
79}
80
81int qemu_guest_random_seed_main(const char *optarg, Error **errp)
82{
83 unsigned long long seed;
84 if (parse_uint_full(optarg, &seed, 0)) {
85 error_setg(errp, "Invalid seed number: %s", optarg);
86 return -1;
87 } else {
88 deterministic = true;
89 qemu_guest_random_seed_thread_part2(seed);
90 return 0;
91 }
92}
93