1 /*
2 * IMX7 Secure Non-Volatile Storage
3 *
4 * Copyright (c) 2018, Impinj, Inc.
5 *
6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
10 *
11 * Bare minimum emulation code needed to support being able to shut
12 * down linux guest gracefully.
13 */
14
15 #include "qemu/osdep.h"
16 #include "qemu/bitops.h"
17 #include "qemu/timer.h"
18 #include "migration/vmstate.h"
19 #include "hw/misc/imx7_snvs.h"
20 #include "qemu/cutils.h"
21 #include "qemu/module.h"
22 #include "sysemu/sysemu.h"
23 #include "sysemu/rtc.h"
24 #include "sysemu/runstate.h"
25 #include "trace.h"
26
27 #define RTC_FREQ 32768ULL
28
29 static const VMStateDescription vmstate_imx7_snvs = {
30 .name = TYPE_IMX7_SNVS,
31 .version_id = 1,
32 .minimum_version_id = 1,
33 .fields = (const VMStateField[]) {
34 VMSTATE_UINT64(tick_offset, IMX7SNVSState),
35 VMSTATE_UINT64(lpcr, IMX7SNVSState),
36 VMSTATE_END_OF_LIST()
37 }
38 };
39
imx7_snvs_get_count(IMX7SNVSState * s)40 static uint64_t imx7_snvs_get_count(IMX7SNVSState *s)
41 {
42 uint64_t ticks = muldiv64(qemu_clock_get_ns(rtc_clock), RTC_FREQ,
43 NANOSECONDS_PER_SECOND);
44 return s->tick_offset + ticks;
45 }
46
imx7_snvs_read(void * opaque,hwaddr offset,unsigned size)47 static uint64_t imx7_snvs_read(void *opaque, hwaddr offset, unsigned size)
48 {
49 IMX7SNVSState *s = IMX7_SNVS(opaque);
50 uint64_t ret = 0;
51
52 switch (offset) {
53 case SNVS_LPSRTCMR:
54 ret = extract64(imx7_snvs_get_count(s), 32, 15);
55 break;
56 case SNVS_LPSRTCLR:
57 ret = extract64(imx7_snvs_get_count(s), 0, 32);
58 break;
59 case SNVS_LPCR:
60 ret = s->lpcr;
61 break;
62 }
63
64 trace_imx7_snvs_read(offset, ret, size);
65
66 return ret;
67 }
68
imx7_snvs_reset(DeviceState * dev)69 static void imx7_snvs_reset(DeviceState *dev)
70 {
71 IMX7SNVSState *s = IMX7_SNVS(dev);
72
73 s->lpcr = 0;
74 }
75
imx7_snvs_write(void * opaque,hwaddr offset,uint64_t v,unsigned size)76 static void imx7_snvs_write(void *opaque, hwaddr offset,
77 uint64_t v, unsigned size)
78 {
79 trace_imx7_snvs_write(offset, v, size);
80
81 IMX7SNVSState *s = IMX7_SNVS(opaque);
82
83 uint64_t new_value = 0, snvs_count = 0;
84
85 if (offset == SNVS_LPSRTCMR || offset == SNVS_LPSRTCLR) {
86 snvs_count = imx7_snvs_get_count(s);
87 }
88
89 switch (offset) {
90 case SNVS_LPSRTCMR:
91 new_value = deposit64(snvs_count, 32, 32, v);
92 break;
93 case SNVS_LPSRTCLR:
94 new_value = deposit64(snvs_count, 0, 32, v);
95 break;
96 case SNVS_LPCR: {
97 s->lpcr = v;
98
99 const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN;
100
101 if ((v & mask) == mask) {
102 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
103 }
104 break;
105 }
106 }
107
108 if (offset == SNVS_LPSRTCMR || offset == SNVS_LPSRTCLR) {
109 s->tick_offset += new_value - snvs_count;
110 }
111 }
112
113 static const struct MemoryRegionOps imx7_snvs_ops = {
114 .read = imx7_snvs_read,
115 .write = imx7_snvs_write,
116 .endianness = DEVICE_NATIVE_ENDIAN,
117 .impl = {
118 /*
119 * Our device would not work correctly if the guest was doing
120 * unaligned access. This might not be a limitation on the real
121 * device but in practice there is no reason for a guest to access
122 * this device unaligned.
123 */
124 .min_access_size = 4,
125 .max_access_size = 4,
126 .unaligned = false,
127 },
128 };
129
imx7_snvs_init(Object * obj)130 static void imx7_snvs_init(Object *obj)
131 {
132 SysBusDevice *sd = SYS_BUS_DEVICE(obj);
133 IMX7SNVSState *s = IMX7_SNVS(obj);
134 struct tm tm;
135
136 memory_region_init_io(&s->mmio, obj, &imx7_snvs_ops, s,
137 TYPE_IMX7_SNVS, 0x1000);
138
139 sysbus_init_mmio(sd, &s->mmio);
140
141 qemu_get_timedate(&tm, 0);
142 s->tick_offset = mktimegm(&tm) -
143 qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
144 }
145
imx7_snvs_class_init(ObjectClass * klass,void * data)146 static void imx7_snvs_class_init(ObjectClass *klass, void *data)
147 {
148 DeviceClass *dc = DEVICE_CLASS(klass);
149
150 device_class_set_legacy_reset(dc, imx7_snvs_reset);
151 dc->vmsd = &vmstate_imx7_snvs;
152 dc->desc = "i.MX7 Secure Non-Volatile Storage Module";
153 }
154
155 static const TypeInfo imx7_snvs_info = {
156 .name = TYPE_IMX7_SNVS,
157 .parent = TYPE_SYS_BUS_DEVICE,
158 .instance_size = sizeof(IMX7SNVSState),
159 .instance_init = imx7_snvs_init,
160 .class_init = imx7_snvs_class_init,
161 };
162
imx7_snvs_register_type(void)163 static void imx7_snvs_register_type(void)
164 {
165 type_register_static(&imx7_snvs_info);
166 }
167 type_init(imx7_snvs_register_type)
168