qemu/hw/misc/allwinner-sid.c
<<
>>
Prefs
   1/*
   2 * Allwinner Security ID emulation
   3 *
   4 * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
   5 *
   6 * This program is free software: you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the 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,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "qemu/units.h"
  22#include "hw/sysbus.h"
  23#include "migration/vmstate.h"
  24#include "qemu/log.h"
  25#include "qemu/module.h"
  26#include "qemu/guest-random.h"
  27#include "qapi/error.h"
  28#include "hw/qdev-properties.h"
  29#include "hw/qdev-properties-system.h"
  30#include "hw/misc/allwinner-sid.h"
  31#include "trace.h"
  32
  33/* SID register offsets */
  34enum {
  35    REG_PRCTL = 0x40,   /* Control */
  36    REG_RDKEY = 0x60,   /* Read Key */
  37};
  38
  39/* SID register flags */
  40enum {
  41    REG_PRCTL_WRITE   = 0x0002, /* Unknown write flag */
  42    REG_PRCTL_OP_LOCK = 0xAC00, /* Lock operation */
  43};
  44
  45static uint64_t allwinner_sid_read(void *opaque, hwaddr offset,
  46                                   unsigned size)
  47{
  48    const AwSidState *s = AW_SID(opaque);
  49    uint64_t val = 0;
  50
  51    switch (offset) {
  52    case REG_PRCTL:    /* Control */
  53        val = s->control;
  54        break;
  55    case REG_RDKEY:    /* Read Key */
  56        val = s->rdkey;
  57        break;
  58    default:
  59        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
  60                      __func__, (uint32_t)offset);
  61        return 0;
  62    }
  63
  64    trace_allwinner_sid_read(offset, val, size);
  65
  66    return val;
  67}
  68
  69static void allwinner_sid_write(void *opaque, hwaddr offset,
  70                                uint64_t val, unsigned size)
  71{
  72    AwSidState *s = AW_SID(opaque);
  73
  74    trace_allwinner_sid_write(offset, val, size);
  75
  76    switch (offset) {
  77    case REG_PRCTL:    /* Control */
  78        s->control = val;
  79
  80        if ((s->control & REG_PRCTL_OP_LOCK) &&
  81            (s->control & REG_PRCTL_WRITE)) {
  82            uint32_t id = s->control >> 16;
  83
  84            if (id <= sizeof(QemuUUID) - sizeof(s->rdkey)) {
  85                s->rdkey = ldl_be_p(&s->identifier.data[id]);
  86            }
  87        }
  88        s->control &= ~REG_PRCTL_WRITE;
  89        break;
  90    case REG_RDKEY:    /* Read Key */
  91        break;
  92    default:
  93        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
  94                      __func__, (uint32_t)offset);
  95        break;
  96    }
  97}
  98
  99static const MemoryRegionOps allwinner_sid_ops = {
 100    .read = allwinner_sid_read,
 101    .write = allwinner_sid_write,
 102    .endianness = DEVICE_NATIVE_ENDIAN,
 103    .valid = {
 104        .min_access_size = 4,
 105        .max_access_size = 4,
 106    },
 107    .impl.min_access_size = 4,
 108};
 109
 110static void allwinner_sid_reset(DeviceState *dev)
 111{
 112    AwSidState *s = AW_SID(dev);
 113
 114    /* Set default values for registers */
 115    s->control = 0;
 116    s->rdkey = 0;
 117}
 118
 119static void allwinner_sid_init(Object *obj)
 120{
 121    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 122    AwSidState *s = AW_SID(obj);
 123
 124    /* Memory mapping */
 125    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sid_ops, s,
 126                           TYPE_AW_SID, 1 * KiB);
 127    sysbus_init_mmio(sbd, &s->iomem);
 128}
 129
 130static Property allwinner_sid_properties[] = {
 131    DEFINE_PROP_UUID_NODEFAULT("identifier", AwSidState, identifier),
 132    DEFINE_PROP_END_OF_LIST()
 133};
 134
 135static const VMStateDescription allwinner_sid_vmstate = {
 136    .name = "allwinner-sid",
 137    .version_id = 1,
 138    .minimum_version_id = 1,
 139    .fields = (VMStateField[]) {
 140        VMSTATE_UINT32(control, AwSidState),
 141        VMSTATE_UINT32(rdkey, AwSidState),
 142        VMSTATE_UINT8_ARRAY_V(identifier.data, AwSidState, sizeof(QemuUUID), 1),
 143        VMSTATE_END_OF_LIST()
 144    }
 145};
 146
 147static void allwinner_sid_class_init(ObjectClass *klass, void *data)
 148{
 149    DeviceClass *dc = DEVICE_CLASS(klass);
 150
 151    dc->reset = allwinner_sid_reset;
 152    dc->vmsd = &allwinner_sid_vmstate;
 153    device_class_set_props(dc, allwinner_sid_properties);
 154}
 155
 156static const TypeInfo allwinner_sid_info = {
 157    .name          = TYPE_AW_SID,
 158    .parent        = TYPE_SYS_BUS_DEVICE,
 159    .instance_init = allwinner_sid_init,
 160    .instance_size = sizeof(AwSidState),
 161    .class_init    = allwinner_sid_class_init,
 162};
 163
 164static void allwinner_sid_register(void)
 165{
 166    type_register_static(&allwinner_sid_info);
 167}
 168
 169type_init(allwinner_sid_register)
 170