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 */ 34 enum { 35 REG_PRCTL = 0x40, /* Control */ 36 REG_RDKEY = 0x60, /* Read Key */ 37 }; 38 39 /* SID register flags */ 40 enum { 41 REG_PRCTL_WRITE = 0x0002, /* Unknown write flag */ 42 REG_PRCTL_OP_LOCK = 0xAC00, /* Lock operation */ 43 }; 44 45 static 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 69 static 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 99 static 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 110 static 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 119 static 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 130 static Property allwinner_sid_properties[] = { 131 DEFINE_PROP_UUID_NODEFAULT("identifier", AwSidState, identifier), 132 DEFINE_PROP_END_OF_LIST() 133 }; 134 135 static const VMStateDescription allwinner_sid_vmstate = { 136 .name = "allwinner-sid", 137 .version_id = 1, 138 .minimum_version_id = 1, 139 .fields = (const 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 147 static void allwinner_sid_class_init(ObjectClass *klass, void *data) 148 { 149 DeviceClass *dc = DEVICE_CLASS(klass); 150 151 device_class_set_legacy_reset(dc, allwinner_sid_reset); 152 dc->vmsd = &allwinner_sid_vmstate; 153 device_class_set_props(dc, allwinner_sid_properties); 154 } 155 156 static 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 164 static void allwinner_sid_register(void) 165 { 166 type_register_static(&allwinner_sid_info); 167 } 168 169 type_init(allwinner_sid_register) 170