| /* |
| * 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; |
| } |
| } |