npcm7xx_rng.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. * Nuvoton NPCM7xx Random Number Generator.
  3. *
  4. * Copyright 2020 Google LLC
  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
  8. * Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * for more details.
  15. */
  16. #include "qemu/osdep.h"
  17. #include "hw/misc/npcm7xx_rng.h"
  18. #include "migration/vmstate.h"
  19. #include "qemu/bitops.h"
  20. #include "qemu/guest-random.h"
  21. #include "qemu/log.h"
  22. #include "qemu/module.h"
  23. #include "qemu/units.h"
  24. #include "trace.h"
  25. #define NPCM7XX_RNG_REGS_SIZE (4 * KiB)
  26. #define NPCM7XX_RNGCS (0x00)
  27. #define NPCM7XX_RNGCS_CLKP(rv) extract32(rv, 2, 4)
  28. #define NPCM7XX_RNGCS_DVALID BIT(1)
  29. #define NPCM7XX_RNGCS_RNGE BIT(0)
  30. #define NPCM7XX_RNGD (0x04)
  31. #define NPCM7XX_RNGMODE (0x08)
  32. #define NPCM7XX_RNGMODE_NORMAL (0x02)
  33. static bool npcm7xx_rng_is_enabled(NPCM7xxRNGState *s)
  34. {
  35. return (s->rngcs & NPCM7XX_RNGCS_RNGE) &&
  36. (s->rngmode == NPCM7XX_RNGMODE_NORMAL);
  37. }
  38. static uint64_t npcm7xx_rng_read(void *opaque, hwaddr offset, unsigned size)
  39. {
  40. NPCM7xxRNGState *s = opaque;
  41. uint64_t value = 0;
  42. switch (offset) {
  43. case NPCM7XX_RNGCS:
  44. /*
  45. * If the RNG is enabled, but we don't have any valid random data, try
  46. * obtaining some and update the DVALID bit accordingly.
  47. */
  48. if (!npcm7xx_rng_is_enabled(s)) {
  49. s->rngcs &= ~NPCM7XX_RNGCS_DVALID;
  50. } else if (!(s->rngcs & NPCM7XX_RNGCS_DVALID)) {
  51. uint8_t byte = 0;
  52. if (qemu_guest_getrandom(&byte, sizeof(byte), NULL) == 0) {
  53. s->rngd = byte;
  54. s->rngcs |= NPCM7XX_RNGCS_DVALID;
  55. }
  56. }
  57. value = s->rngcs;
  58. break;
  59. case NPCM7XX_RNGD:
  60. if (npcm7xx_rng_is_enabled(s) && s->rngcs & NPCM7XX_RNGCS_DVALID) {
  61. s->rngcs &= ~NPCM7XX_RNGCS_DVALID;
  62. value = s->rngd;
  63. s->rngd = 0;
  64. }
  65. break;
  66. case NPCM7XX_RNGMODE:
  67. value = s->rngmode;
  68. break;
  69. default:
  70. qemu_log_mask(LOG_GUEST_ERROR,
  71. "%s: read from invalid offset 0x%" HWADDR_PRIx "\n",
  72. DEVICE(s)->canonical_path, offset);
  73. break;
  74. }
  75. trace_npcm7xx_rng_read(offset, value, size);
  76. return value;
  77. }
  78. static void npcm7xx_rng_write(void *opaque, hwaddr offset, uint64_t value,
  79. unsigned size)
  80. {
  81. NPCM7xxRNGState *s = opaque;
  82. trace_npcm7xx_rng_write(offset, value, size);
  83. switch (offset) {
  84. case NPCM7XX_RNGCS:
  85. s->rngcs &= NPCM7XX_RNGCS_DVALID;
  86. s->rngcs |= value & ~NPCM7XX_RNGCS_DVALID;
  87. break;
  88. case NPCM7XX_RNGD:
  89. qemu_log_mask(LOG_GUEST_ERROR,
  90. "%s: write to read-only register @ 0x%" HWADDR_PRIx "\n",
  91. DEVICE(s)->canonical_path, offset);
  92. break;
  93. case NPCM7XX_RNGMODE:
  94. s->rngmode = value;
  95. break;
  96. default:
  97. qemu_log_mask(LOG_GUEST_ERROR,
  98. "%s: write to invalid offset 0x%" HWADDR_PRIx "\n",
  99. DEVICE(s)->canonical_path, offset);
  100. break;
  101. }
  102. }
  103. static const MemoryRegionOps npcm7xx_rng_ops = {
  104. .read = npcm7xx_rng_read,
  105. .write = npcm7xx_rng_write,
  106. .endianness = DEVICE_LITTLE_ENDIAN,
  107. .valid = {
  108. .min_access_size = 1,
  109. .max_access_size = 4,
  110. .unaligned = false,
  111. },
  112. };
  113. static void npcm7xx_rng_enter_reset(Object *obj, ResetType type)
  114. {
  115. NPCM7xxRNGState *s = NPCM7XX_RNG(obj);
  116. s->rngcs = 0;
  117. s->rngd = 0;
  118. s->rngmode = 0;
  119. }
  120. static void npcm7xx_rng_init(Object *obj)
  121. {
  122. NPCM7xxRNGState *s = NPCM7XX_RNG(obj);
  123. memory_region_init_io(&s->iomem, obj, &npcm7xx_rng_ops, s, "regs",
  124. NPCM7XX_RNG_REGS_SIZE);
  125. sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
  126. }
  127. static const VMStateDescription vmstate_npcm7xx_rng = {
  128. .name = "npcm7xx-rng",
  129. .version_id = 0,
  130. .minimum_version_id = 0,
  131. .fields = (VMStateField[]) {
  132. VMSTATE_UINT8(rngcs, NPCM7xxRNGState),
  133. VMSTATE_UINT8(rngd, NPCM7xxRNGState),
  134. VMSTATE_UINT8(rngmode, NPCM7xxRNGState),
  135. VMSTATE_END_OF_LIST(),
  136. },
  137. };
  138. static void npcm7xx_rng_class_init(ObjectClass *klass, void *data)
  139. {
  140. ResettableClass *rc = RESETTABLE_CLASS(klass);
  141. DeviceClass *dc = DEVICE_CLASS(klass);
  142. dc->desc = "NPCM7xx Random Number Generator";
  143. dc->vmsd = &vmstate_npcm7xx_rng;
  144. rc->phases.enter = npcm7xx_rng_enter_reset;
  145. }
  146. static const TypeInfo npcm7xx_rng_types[] = {
  147. {
  148. .name = TYPE_NPCM7XX_RNG,
  149. .parent = TYPE_SYS_BUS_DEVICE,
  150. .instance_size = sizeof(NPCM7xxRNGState),
  151. .class_init = npcm7xx_rng_class_init,
  152. .instance_init = npcm7xx_rng_init,
  153. },
  154. };
  155. DEFINE_TYPES(npcm7xx_rng_types);