xref: /openbmc/qemu/hw/misc/imx7_snvs.c (revision 2df1eb27)
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 
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 
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 
69 static void imx7_snvs_reset(DeviceState *dev)
70 {
71     IMX7SNVSState *s = IMX7_SNVS(dev);
72 
73     s->lpcr = 0;
74 }
75 
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 
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 
146 static void imx7_snvs_class_init(ObjectClass *klass, void *data)
147 {
148     DeviceClass *dc = DEVICE_CLASS(klass);
149 
150     dc->reset = 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 
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