| /* | |
| * QEMU guest-visible random functions | |
| * | |
| * Copyright 2019 Linaro, Ltd. | |
| * | |
| * This program is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License as published by the Free | |
| * Software Foundation; either version 2 of the License, or (at your option) | |
| * any later version. | |
| */ | |
| #include "qemu/osdep.h" | |
| #include "qemu/cutils.h" | |
| #include "qapi/error.h" | |
| #include "qemu/guest-random.h" | |
| #include "crypto/random.h" | |
| #include "exec/replay-core.h" | |
| static __thread GRand *thread_rand; | |
| static bool deterministic; | |
| static int glib_random_bytes(void *buf, size_t len) | |
| { | |
| GRand *rand = thread_rand; | |
| size_t i; | |
| uint32_t x; | |
| if (unlikely(rand == NULL)) { | |
| /* Thread not initialized for a cpu, or main w/o -seed. */ | |
| thread_rand = rand = g_rand_new(); | |
| } | |
| for (i = 0; i + 4 <= len; i += 4) { | |
| x = g_rand_int(rand); | |
| __builtin_memcpy(buf + i, &x, 4); | |
| } | |
| if (i < len) { | |
| x = g_rand_int(rand); | |
| __builtin_memcpy(buf + i, &x, len - i); | |
| } | |
| return 0; | |
| } | |
| int qemu_guest_getrandom(void *buf, size_t len, Error **errp) | |
| { | |
| int ret; | |
| if (replay_mode == REPLAY_MODE_PLAY) { | |
| return replay_read_random(buf, len); | |
| } | |
| if (unlikely(deterministic)) { | |
| /* Deterministic implementation using Glib's Mersenne Twister. */ | |
| ret = glib_random_bytes(buf, len); | |
| } else { | |
| /* Non-deterministic implementation using crypto routines. */ | |
| ret = qcrypto_random_bytes(buf, len, errp); | |
| } | |
| if (replay_mode == REPLAY_MODE_RECORD) { | |
| replay_save_random(ret, buf, len); | |
| } | |
| return ret; | |
| } | |
| void qemu_guest_getrandom_nofail(void *buf, size_t len) | |
| { | |
| (void)qemu_guest_getrandom(buf, len, &error_fatal); | |
| } | |
| uint64_t qemu_guest_random_seed_thread_part1(void) | |
| { | |
| if (deterministic) { | |
| uint64_t ret; | |
| glib_random_bytes(&ret, sizeof(ret)); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| void qemu_guest_random_seed_thread_part2(uint64_t seed) | |
| { | |
| g_assert(thread_rand == NULL); | |
| if (deterministic) { | |
| thread_rand = | |
| g_rand_new_with_seed_array((const guint32 *)&seed, | |
| sizeof(seed) / sizeof(guint32)); | |
| } | |
| } | |
| int qemu_guest_random_seed_main(const char *seedstr, Error **errp) | |
| { | |
| uint64_t seed; | |
| if (parse_uint_full(seedstr, 0, &seed)) { | |
| error_setg(errp, "Invalid seed number: %s", seedstr); | |
| return -1; | |
| } else { | |
| deterministic = true; | |
| qemu_guest_random_seed_thread_part2(seed); | |
| return 0; | |
| } | |
| } |