]>
Commit | Line | Data |
---|---|---|
8d8404f1 RH |
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" | |
8d8404f1 RH |
13 | #include "qemu/cutils.h" |
14 | #include "qapi/error.h" | |
15 | #include "qemu/guest-random.h" | |
16 | #include "crypto/random.h" | |
878ec29b | 17 | #include "sysemu/replay.h" |
8d8404f1 RH |
18 | |
19 | ||
20 | static __thread GRand *thread_rand; | |
21 | static bool deterministic; | |
22 | ||
23 | ||
24 | static int glib_random_bytes(void *buf, size_t len) | |
25 | { | |
26 | GRand *rand = thread_rand; | |
27 | size_t i; | |
28 | uint32_t x; | |
29 | ||
30 | if (unlikely(rand == NULL)) { | |
31 | /* Thread not initialized for a cpu, or main w/o -seed. */ | |
32 | thread_rand = rand = g_rand_new(); | |
33 | } | |
34 | ||
35 | for (i = 0; i + 4 <= len; i += 4) { | |
36 | x = g_rand_int(rand); | |
37 | __builtin_memcpy(buf + i, &x, 4); | |
38 | } | |
39 | if (i < len) { | |
40 | x = g_rand_int(rand); | |
e28ffe90 | 41 | __builtin_memcpy(buf + i, &x, len - i); |
8d8404f1 RH |
42 | } |
43 | return 0; | |
44 | } | |
45 | ||
46 | int qemu_guest_getrandom(void *buf, size_t len, Error **errp) | |
47 | { | |
878ec29b PD |
48 | int ret; |
49 | if (replay_mode == REPLAY_MODE_PLAY) { | |
50 | return replay_read_random(buf, len); | |
51 | } | |
8d8404f1 RH |
52 | if (unlikely(deterministic)) { |
53 | /* Deterministic implementation using Glib's Mersenne Twister. */ | |
878ec29b | 54 | ret = glib_random_bytes(buf, len); |
8d8404f1 RH |
55 | } else { |
56 | /* Non-deterministic implementation using crypto routines. */ | |
878ec29b PD |
57 | ret = qcrypto_random_bytes(buf, len, errp); |
58 | } | |
59 | if (replay_mode == REPLAY_MODE_RECORD) { | |
60 | replay_save_random(ret, buf, len); | |
8d8404f1 | 61 | } |
878ec29b | 62 | return ret; |
8d8404f1 RH |
63 | } |
64 | ||
65 | void qemu_guest_getrandom_nofail(void *buf, size_t len) | |
66 | { | |
11259e9a | 67 | (void)qemu_guest_getrandom(buf, len, &error_fatal); |
8d8404f1 RH |
68 | } |
69 | ||
70 | uint64_t qemu_guest_random_seed_thread_part1(void) | |
71 | { | |
72 | if (deterministic) { | |
73 | uint64_t ret; | |
74 | glib_random_bytes(&ret, sizeof(ret)); | |
75 | return ret; | |
76 | } | |
77 | return 0; | |
78 | } | |
79 | ||
80 | void qemu_guest_random_seed_thread_part2(uint64_t seed) | |
81 | { | |
82 | g_assert(thread_rand == NULL); | |
83 | if (deterministic) { | |
84 | thread_rand = | |
85 | g_rand_new_with_seed_array((const guint32 *)&seed, | |
86 | sizeof(seed) / sizeof(guint32)); | |
87 | } | |
88 | } | |
89 | ||
90 | int qemu_guest_random_seed_main(const char *optarg, Error **errp) | |
91 | { | |
92 | unsigned long long seed; | |
93 | if (parse_uint_full(optarg, &seed, 0)) { | |
94 | error_setg(errp, "Invalid seed number: %s", optarg); | |
95 | return -1; | |
96 | } else { | |
97 | deterministic = true; | |
98 | qemu_guest_random_seed_thread_part2(seed); | |
99 | return 0; | |
100 | } | |
101 | } |