Browse Source
* v7M: ignore writes to CONTROL.SPSEL from Thread mode * KVM: Enable in-kernel timers with user space gic * aspeed: Register all watchdogs * hw/misc: Add Exynos4210 Pseudo Random Number Generator -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJZZKg/AAoJEDwlJe0UNgzeolIQAIQibd+vcPj/PX/6aF9lz+cP 3PGXLhpm0ks1oCU7JH5MYSjl4JhJjnxdQdwl88KBhgWyn60D7txKbiqSMdHon7rT dkohZrywOMz9TKbGvhKk0mN+0uAUAv0kB7gc7qU0ei1yFMTjGLBWt8NqMdg4HRCr cj5xtiYnc0WiXgkpZkMZD1J1svY6AG4s7s5wclUpFhSefY31+Myj9GU7ehwZL0t7 uyi8oN8OosS1KVPTzYTYdIMqC0R5+8VqR9mV1+ZUIHYG/QwlhO8tqe/C0tIh1sDQ qJYEGR3QoxPsThFepBnLKbqr8ME2EnLBZtAdtu8xqj0CSsUCeumrvY8IbvbpzZ9t YN/VxzaFC8DP2POk+lYa+hyN4gJLfh1ktXynt936YE2hpNz0ZnNttEN85XQV3Iom r5XPgWk5o/tq98DuWLFK4BzW8B4a1abR0asGvwM3zP1r+8QYOssjzNCjeYXrH6g6 aVTLLAi4FZV+nGmXRMAs1PTWaCiAzVg+3xqX40FSg0BsWQcsya/RwaO/Z1SkF9Uy O5n3hfhUYsMuRKCLe7s6I01725coBus85DcTCCAzj9iBjTekuO8gTOoR1+wFeSTS 5N0DZUoT/x+nyXvNs+mqefiCSBCArwfsd/j7a5/p4+7fq//X2H2PereYNjLeTrXR yQwZmY7FOQYmyyMUm61B =ejgi -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170711' into staging target-arm queue: * v7M: ignore writes to CONTROL.SPSEL from Thread mode * KVM: Enable in-kernel timers with user space gic * aspeed: Register all watchdogs * hw/misc: Add Exynos4210 Pseudo Random Number Generator # gpg: Signature made Tue 11 Jul 2017 11:28:15 BST # gpg: using RSA key 0x3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20170711: target-arm: v7M: ignore writes to CONTROL.SPSEL from Thread mode ARM: KVM: Enable in-kernel timers with user space gic aspeed: Register all watchdogs hw/misc: Add Exynos4210 Pseudo Random Number Generator Signed-off-by: Peter Maydell <peter.maydell@linaro.org>pull/55/head
12 changed files with 394 additions and 13 deletions
@ -0,0 +1,277 @@ |
|||
/*
|
|||
* Exynos4210 Pseudo Random Nubmer Generator Emulation |
|||
* |
|||
* Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org> |
|||
* |
|||
* 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. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but WITHOUT |
|||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|||
* for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "crypto/random.h" |
|||
#include "hw/sysbus.h" |
|||
#include "qemu/log.h" |
|||
|
|||
#define DEBUG_EXYNOS_RNG 0 |
|||
|
|||
#define DPRINTF(fmt, ...) \ |
|||
do { \ |
|||
if (DEBUG_EXYNOS_RNG) { \ |
|||
printf("exynos4210_rng: " fmt, ## __VA_ARGS__); \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define TYPE_EXYNOS4210_RNG "exynos4210.rng" |
|||
#define EXYNOS4210_RNG(obj) \ |
|||
OBJECT_CHECK(Exynos4210RngState, (obj), TYPE_EXYNOS4210_RNG) |
|||
|
|||
/*
|
|||
* Exynos4220, PRNG, only polling mode is supported. |
|||
*/ |
|||
|
|||
/* RNG_CONTROL_1 register bitfields, reset value: 0x0 */ |
|||
#define EXYNOS4210_RNG_CONTROL_1_PRNG 0x8 |
|||
#define EXYNOS4210_RNG_CONTROL_1_START_INIT BIT(4) |
|||
/* RNG_STATUS register bitfields, reset value: 0x1 */ |
|||
#define EXYNOS4210_RNG_STATUS_PRNG_ERROR BIT(7) |
|||
#define EXYNOS4210_RNG_STATUS_PRNG_DONE BIT(5) |
|||
#define EXYNOS4210_RNG_STATUS_MSG_DONE BIT(4) |
|||
#define EXYNOS4210_RNG_STATUS_PARTIAL_DONE BIT(3) |
|||
#define EXYNOS4210_RNG_STATUS_PRNG_BUSY BIT(2) |
|||
#define EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE BIT(1) |
|||
#define EXYNOS4210_RNG_STATUS_BUFFER_READY BIT(0) |
|||
#define EXYNOS4210_RNG_STATUS_WRITE_MASK (EXYNOS4210_RNG_STATUS_PRNG_DONE \ |
|||
| EXYNOS4210_RNG_STATUS_MSG_DONE \ |
|||
| EXYNOS4210_RNG_STATUS_PARTIAL_DONE) |
|||
|
|||
#define EXYNOS4210_RNG_CONTROL_1 0x0 |
|||
#define EXYNOS4210_RNG_STATUS 0x10 |
|||
#define EXYNOS4210_RNG_SEED_IN 0x140 |
|||
#define EXYNOS4210_RNG_SEED_IN_OFFSET(n) (EXYNOS4210_RNG_SEED_IN + (n * 0x4)) |
|||
#define EXYNOS4210_RNG_PRNG 0x160 |
|||
#define EXYNOS4210_RNG_PRNG_OFFSET(n) (EXYNOS4210_RNG_PRNG + (n * 0x4)) |
|||
|
|||
#define EXYNOS4210_RNG_PRNG_NUM 5 |
|||
|
|||
#define EXYNOS4210_RNG_REGS_MEM_SIZE 0x200 |
|||
|
|||
typedef struct Exynos4210RngState { |
|||
SysBusDevice parent_obj; |
|||
MemoryRegion iomem; |
|||
|
|||
int32_t randr_value[EXYNOS4210_RNG_PRNG_NUM]; |
|||
/* bits from 0 to EXYNOS4210_RNG_PRNG_NUM if given seed register was set */ |
|||
uint32_t seed_set; |
|||
|
|||
/* Register values */ |
|||
uint32_t reg_control; |
|||
uint32_t reg_status; |
|||
} Exynos4210RngState; |
|||
|
|||
static bool exynos4210_rng_seed_ready(const Exynos4210RngState *s) |
|||
{ |
|||
uint32_t mask = MAKE_64BIT_MASK(0, EXYNOS4210_RNG_PRNG_NUM); |
|||
|
|||
/* Return true if all the seed-set bits are set. */ |
|||
return (s->seed_set & mask) == mask; |
|||
} |
|||
|
|||
static void exynos4210_rng_set_seed(Exynos4210RngState *s, unsigned int i, |
|||
uint64_t val) |
|||
{ |
|||
/*
|
|||
* We actually ignore the seed and always generate true random numbers. |
|||
* Theoretically this should not match the device as Exynos has |
|||
* a Pseudo Random Number Generator but testing shown that it always |
|||
* generates random numbers regardless of the seed value. |
|||
*/ |
|||
s->seed_set |= BIT(i); |
|||
|
|||
/* If all seeds were written, update the status to reflect it */ |
|||
if (exynos4210_rng_seed_ready(s)) { |
|||
s->reg_status |= EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE; |
|||
} else { |
|||
s->reg_status &= ~EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE; |
|||
} |
|||
} |
|||
|
|||
static void exynos4210_rng_run_engine(Exynos4210RngState *s) |
|||
{ |
|||
Error *err = NULL; |
|||
int ret; |
|||
|
|||
/* Seed set? */ |
|||
if ((s->reg_status & EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE) == 0) { |
|||
goto out; |
|||
} |
|||
|
|||
/* PRNG engine chosen? */ |
|||
if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_PRNG) == 0) { |
|||
goto out; |
|||
} |
|||
|
|||
/* PRNG engine started? */ |
|||
if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_START_INIT) == 0) { |
|||
goto out; |
|||
} |
|||
|
|||
/* Get randoms */ |
|||
ret = qcrypto_random_bytes((uint8_t *)s->randr_value, |
|||
sizeof(s->randr_value), &err); |
|||
if (!ret) { |
|||
/* Notify that PRNG is ready */ |
|||
s->reg_status |= EXYNOS4210_RNG_STATUS_PRNG_DONE; |
|||
} else { |
|||
error_report_err(err); |
|||
} |
|||
|
|||
out: |
|||
/* Always clear start engine bit */ |
|||
s->reg_control &= ~EXYNOS4210_RNG_CONTROL_1_START_INIT; |
|||
} |
|||
|
|||
static uint64_t exynos4210_rng_read(void *opaque, hwaddr offset, |
|||
unsigned size) |
|||
{ |
|||
Exynos4210RngState *s = (Exynos4210RngState *)opaque; |
|||
uint32_t val = 0; |
|||
|
|||
assert(size == 4); |
|||
|
|||
switch (offset) { |
|||
case EXYNOS4210_RNG_CONTROL_1: |
|||
val = s->reg_control; |
|||
break; |
|||
|
|||
case EXYNOS4210_RNG_STATUS: |
|||
val = s->reg_status; |
|||
break; |
|||
|
|||
case EXYNOS4210_RNG_PRNG_OFFSET(0): |
|||
case EXYNOS4210_RNG_PRNG_OFFSET(1): |
|||
case EXYNOS4210_RNG_PRNG_OFFSET(2): |
|||
case EXYNOS4210_RNG_PRNG_OFFSET(3): |
|||
case EXYNOS4210_RNG_PRNG_OFFSET(4): |
|||
val = s->randr_value[(offset - EXYNOS4210_RNG_PRNG_OFFSET(0)) / 4]; |
|||
DPRINTF("returning random @0x%" HWADDR_PRIx ": 0x%" PRIx32 "\n", |
|||
offset, val); |
|||
break; |
|||
|
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, |
|||
"%s: bad read offset 0x%" HWADDR_PRIx "\n", |
|||
__func__, offset); |
|||
} |
|||
|
|||
return val; |
|||
} |
|||
|
|||
static void exynos4210_rng_write(void *opaque, hwaddr offset, |
|||
uint64_t val, unsigned size) |
|||
{ |
|||
Exynos4210RngState *s = (Exynos4210RngState *)opaque; |
|||
|
|||
assert(size == 4); |
|||
|
|||
switch (offset) { |
|||
case EXYNOS4210_RNG_CONTROL_1: |
|||
DPRINTF("RNG_CONTROL_1 = 0x%" PRIx64 "\n", val); |
|||
s->reg_control = val; |
|||
exynos4210_rng_run_engine(s); |
|||
break; |
|||
|
|||
case EXYNOS4210_RNG_STATUS: |
|||
/* For clearing status fields */ |
|||
s->reg_status &= ~EXYNOS4210_RNG_STATUS_WRITE_MASK; |
|||
s->reg_status |= val & EXYNOS4210_RNG_STATUS_WRITE_MASK; |
|||
break; |
|||
|
|||
case EXYNOS4210_RNG_SEED_IN_OFFSET(0): |
|||
case EXYNOS4210_RNG_SEED_IN_OFFSET(1): |
|||
case EXYNOS4210_RNG_SEED_IN_OFFSET(2): |
|||
case EXYNOS4210_RNG_SEED_IN_OFFSET(3): |
|||
case EXYNOS4210_RNG_SEED_IN_OFFSET(4): |
|||
exynos4210_rng_set_seed(s, |
|||
(offset - EXYNOS4210_RNG_SEED_IN_OFFSET(0)) / 4, |
|||
val); |
|||
break; |
|||
|
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, |
|||
"%s: bad write offset 0x%" HWADDR_PRIx "\n", |
|||
__func__, offset); |
|||
} |
|||
} |
|||
|
|||
static const MemoryRegionOps exynos4210_rng_ops = { |
|||
.read = exynos4210_rng_read, |
|||
.write = exynos4210_rng_write, |
|||
.endianness = DEVICE_NATIVE_ENDIAN, |
|||
}; |
|||
|
|||
static void exynos4210_rng_reset(DeviceState *dev) |
|||
{ |
|||
Exynos4210RngState *s = EXYNOS4210_RNG(dev); |
|||
|
|||
s->reg_control = 0; |
|||
s->reg_status = EXYNOS4210_RNG_STATUS_BUFFER_READY; |
|||
memset(s->randr_value, 0, sizeof(s->randr_value)); |
|||
s->seed_set = 0; |
|||
} |
|||
|
|||
static void exynos4210_rng_init(Object *obj) |
|||
{ |
|||
Exynos4210RngState *s = EXYNOS4210_RNG(obj); |
|||
SysBusDevice *dev = SYS_BUS_DEVICE(obj); |
|||
|
|||
memory_region_init_io(&s->iomem, obj, &exynos4210_rng_ops, s, |
|||
TYPE_EXYNOS4210_RNG, EXYNOS4210_RNG_REGS_MEM_SIZE); |
|||
sysbus_init_mmio(dev, &s->iomem); |
|||
} |
|||
|
|||
static const VMStateDescription exynos4210_rng_vmstate = { |
|||
.name = TYPE_EXYNOS4210_RNG, |
|||
.version_id = 1, |
|||
.minimum_version_id = 1, |
|||
.fields = (VMStateField[]) { |
|||
VMSTATE_INT32_ARRAY(randr_value, Exynos4210RngState, |
|||
EXYNOS4210_RNG_PRNG_NUM), |
|||
VMSTATE_UINT32(seed_set, Exynos4210RngState), |
|||
VMSTATE_UINT32(reg_status, Exynos4210RngState), |
|||
VMSTATE_UINT32(reg_control, Exynos4210RngState), |
|||
VMSTATE_END_OF_LIST() |
|||
} |
|||
}; |
|||
|
|||
static void exynos4210_rng_class_init(ObjectClass *klass, void *data) |
|||
{ |
|||
DeviceClass *dc = DEVICE_CLASS(klass); |
|||
|
|||
dc->reset = exynos4210_rng_reset; |
|||
dc->vmsd = &exynos4210_rng_vmstate; |
|||
} |
|||
|
|||
static const TypeInfo exynos4210_rng_info = { |
|||
.name = TYPE_EXYNOS4210_RNG, |
|||
.parent = TYPE_SYS_BUS_DEVICE, |
|||
.instance_size = sizeof(Exynos4210RngState), |
|||
.instance_init = exynos4210_rng_init, |
|||
.class_init = exynos4210_rng_class_init, |
|||
}; |
|||
|
|||
static void exynos4210_rng_register(void) |
|||
{ |
|||
type_register_static(&exynos4210_rng_info); |
|||
} |
|||
|
|||
type_init(exynos4210_rng_register) |
|||
Loading…
Reference in new issue