]>
Commit | Line | Data |
---|---|---|
461a6a6f TH |
1 | /* |
2 | * QEMU model of the Xilinx BBRAM Battery Backed RAM | |
3 | * | |
4 | * Copyright (c) 2014-2021 Xilinx Inc. | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "qemu/osdep.h" | |
26 | #include "hw/nvram/xlnx-bbram.h" | |
27 | ||
28 | #include "qemu/error-report.h" | |
29 | #include "qemu/log.h" | |
30 | #include "qapi/error.h" | |
31 | #include "sysemu/blockdev.h" | |
32 | #include "migration/vmstate.h" | |
33 | #include "hw/qdev-properties.h" | |
34 | #include "hw/qdev-properties-system.h" | |
35 | #include "hw/nvram/xlnx-efuse.h" | |
36 | ||
37 | #ifndef XLNX_BBRAM_ERR_DEBUG | |
38 | #define XLNX_BBRAM_ERR_DEBUG 0 | |
39 | #endif | |
40 | ||
41 | REG32(BBRAM_STATUS, 0x0) | |
42 | FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1) | |
43 | FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1) | |
44 | FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1) | |
45 | FIELD(BBRAM_STATUS, PGM_MODE, 0, 1) | |
46 | REG32(BBRAM_CTRL, 0x4) | |
47 | FIELD(BBRAM_CTRL, ZEROIZE, 0, 1) | |
48 | REG32(PGM_MODE, 0x8) | |
49 | REG32(BBRAM_AES_CRC, 0xc) | |
50 | REG32(BBRAM_0, 0x10) | |
51 | REG32(BBRAM_1, 0x14) | |
52 | REG32(BBRAM_2, 0x18) | |
53 | REG32(BBRAM_3, 0x1c) | |
54 | REG32(BBRAM_4, 0x20) | |
55 | REG32(BBRAM_5, 0x24) | |
56 | REG32(BBRAM_6, 0x28) | |
57 | REG32(BBRAM_7, 0x2c) | |
58 | REG32(BBRAM_8, 0x30) | |
59 | REG32(BBRAM_SLVERR, 0x34) | |
60 | FIELD(BBRAM_SLVERR, ENABLE, 0, 1) | |
61 | REG32(BBRAM_ISR, 0x38) | |
62 | FIELD(BBRAM_ISR, APB_SLVERR, 0, 1) | |
63 | REG32(BBRAM_IMR, 0x3c) | |
64 | FIELD(BBRAM_IMR, APB_SLVERR, 0, 1) | |
65 | REG32(BBRAM_IER, 0x40) | |
66 | FIELD(BBRAM_IER, APB_SLVERR, 0, 1) | |
67 | REG32(BBRAM_IDR, 0x44) | |
68 | FIELD(BBRAM_IDR, APB_SLVERR, 0, 1) | |
69 | REG32(BBRAM_MSW_LOCK, 0x4c) | |
70 | FIELD(BBRAM_MSW_LOCK, VAL, 0, 1) | |
71 | ||
72 | #define R_MAX (R_BBRAM_MSW_LOCK + 1) | |
73 | ||
74 | #define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0) | |
75 | ||
76 | #define BBRAM_PGM_MAGIC 0x757bdf0d | |
77 | ||
78 | QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxBBRam *)0)->regs)); | |
79 | ||
80 | static bool bbram_msw_locked(XlnxBBRam *s) | |
81 | { | |
82 | return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0; | |
83 | } | |
84 | ||
85 | static bool bbram_pgm_enabled(XlnxBBRam *s) | |
86 | { | |
87 | return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0; | |
88 | } | |
89 | ||
90 | static void bbram_bdrv_error(XlnxBBRam *s, int rc, gchar *detail) | |
91 | { | |
92 | Error *errp; | |
93 | ||
94 | error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.", | |
95 | blk_name(s->blk), detail); | |
96 | error_report("%s", error_get_pretty(errp)); | |
97 | error_free(errp); | |
98 | ||
99 | g_free(detail); | |
100 | } | |
101 | ||
102 | static void bbram_bdrv_read(XlnxBBRam *s, Error **errp) | |
103 | { | |
104 | uint32_t *ram = &s->regs[R_BBRAM_0]; | |
105 | int nr = RAM_MAX; | |
106 | ||
107 | if (!s->blk) { | |
108 | return; | |
109 | } | |
110 | ||
111 | s->blk_ro = !blk_supports_write_perm(s->blk); | |
112 | if (!s->blk_ro) { | |
113 | int rc; | |
114 | ||
115 | rc = blk_set_perm(s->blk, | |
116 | (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), | |
117 | BLK_PERM_ALL, NULL); | |
118 | if (rc) { | |
119 | s->blk_ro = true; | |
120 | } | |
121 | } | |
122 | if (s->blk_ro) { | |
123 | warn_report("%s: Skip saving updates to read-only BBRAM backstore.", | |
124 | blk_name(s->blk)); | |
125 | } | |
126 | ||
127 | if (blk_pread(s->blk, 0, ram, nr) < 0) { | |
128 | error_setg(errp, | |
129 | "%s: Failed to read %u bytes from BBRAM backstore.", | |
130 | blk_name(s->blk), nr); | |
131 | return; | |
132 | } | |
133 | ||
134 | /* Convert from little-endian backstore for each 32-bit word */ | |
135 | nr /= 4; | |
136 | while (nr--) { | |
137 | ram[nr] = le32_to_cpu(ram[nr]); | |
138 | } | |
139 | } | |
140 | ||
141 | static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr) | |
142 | { | |
143 | uint32_t le32; | |
144 | unsigned offset; | |
145 | int rc; | |
146 | ||
147 | assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8); | |
148 | ||
149 | /* Backstore is always in little-endian */ | |
150 | le32 = cpu_to_le32(s->regs[hwaddr / 4]); | |
151 | ||
152 | /* Update zeroized flag */ | |
153 | if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) { | |
154 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0); | |
155 | } | |
156 | ||
157 | if (!s->blk || s->blk_ro) { | |
158 | return; | |
159 | } | |
160 | ||
161 | offset = hwaddr - A_BBRAM_0; | |
162 | rc = blk_pwrite(s->blk, offset, &le32, 4, 0); | |
163 | if (rc < 0) { | |
164 | bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset)); | |
165 | } | |
166 | } | |
167 | ||
168 | static void bbram_bdrv_zero(XlnxBBRam *s) | |
169 | { | |
170 | int rc; | |
171 | ||
172 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1); | |
173 | ||
174 | if (!s->blk || s->blk_ro) { | |
175 | return; | |
176 | } | |
177 | ||
178 | rc = blk_make_zero(s->blk, 0); | |
179 | if (rc < 0) { | |
180 | bbram_bdrv_error(s, rc, g_strdup("zeroizing")); | |
181 | } | |
182 | ||
183 | /* Restore bbram8 if it is non-zero */ | |
184 | if (s->regs[R_BBRAM_8]) { | |
185 | bbram_bdrv_sync(s, A_BBRAM_8); | |
186 | } | |
187 | } | |
188 | ||
189 | static void bbram_zeroize(XlnxBBRam *s) | |
190 | { | |
191 | int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */ | |
192 | ||
193 | memset(&s->regs[R_BBRAM_0], 0, nr); | |
194 | bbram_bdrv_zero(s); | |
195 | } | |
196 | ||
197 | static void bbram_update_irq(XlnxBBRam *s) | |
198 | { | |
199 | bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR]; | |
200 | ||
201 | qemu_set_irq(s->irq_bbram, pending); | |
202 | } | |
203 | ||
204 | static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64) | |
205 | { | |
206 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
207 | uint32_t val = val64; | |
208 | ||
209 | if (val & R_BBRAM_CTRL_ZEROIZE_MASK) { | |
210 | bbram_zeroize(s); | |
211 | /* The bit is self clearing */ | |
212 | s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK; | |
213 | } | |
214 | } | |
215 | ||
216 | static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64) | |
217 | { | |
218 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
219 | uint32_t val = val64; | |
220 | ||
221 | if (val == BBRAM_PGM_MAGIC) { | |
222 | bbram_zeroize(s); | |
223 | ||
224 | /* The status bit is cleared only by POR */ | |
225 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1); | |
226 | } | |
227 | } | |
228 | ||
229 | static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64) | |
230 | { | |
231 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
232 | uint32_t calc_crc; | |
233 | ||
234 | if (!bbram_pgm_enabled(s)) { | |
235 | /* We are not in programming mode, don't do anything */ | |
236 | return; | |
237 | } | |
238 | ||
239 | /* Perform the AES integrity check */ | |
240 | s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK; | |
241 | ||
242 | /* | |
243 | * Set check status. | |
244 | * | |
245 | * ZynqMP BBRAM check has a zero-u32 prepended; see: | |
246 | * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_bbramps_zynqmp.c#L311 | |
247 | */ | |
248 | calc_crc = xlnx_efuse_calc_crc(&s->regs[R_BBRAM_0], | |
249 | (R_BBRAM_8 - R_BBRAM_0), s->crc_zpads); | |
250 | ||
251 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS, | |
252 | (s->regs[R_BBRAM_AES_CRC] == calc_crc)); | |
253 | } | |
254 | ||
255 | static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64) | |
256 | { | |
257 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
258 | uint32_t original_data = *(uint32_t *) reg->data; | |
259 | ||
260 | if (bbram_pgm_enabled(s)) { | |
261 | return val64; | |
262 | } else { | |
263 | /* We are not in programming mode, don't do anything */ | |
264 | qemu_log_mask(LOG_GUEST_ERROR, | |
265 | "Not in programming mode, dropping the write\n"); | |
266 | return original_data; | |
267 | } | |
268 | } | |
269 | ||
270 | static void bbram_key_postw(RegisterInfo *reg, uint64_t val64) | |
271 | { | |
272 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
273 | ||
274 | bbram_bdrv_sync(s, reg->access->addr); | |
275 | } | |
276 | ||
277 | static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val) | |
278 | { | |
279 | return 0; | |
280 | } | |
281 | ||
282 | static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val) | |
283 | { | |
284 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
285 | ||
286 | return s->bbram8_wo ? bbram_wo_postr(reg, val) : val; | |
287 | } | |
288 | ||
289 | static bool bbram_r8_readonly(XlnxBBRam *s) | |
290 | { | |
291 | return !bbram_pgm_enabled(s) || bbram_msw_locked(s); | |
292 | } | |
293 | ||
294 | static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64) | |
295 | { | |
296 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
297 | ||
298 | if (bbram_r8_readonly(s)) { | |
299 | val64 = *(uint32_t *)reg->data; | |
300 | } | |
301 | ||
302 | return val64; | |
303 | } | |
304 | ||
305 | static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64) | |
306 | { | |
307 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
308 | ||
309 | if (!bbram_r8_readonly(s)) { | |
310 | bbram_bdrv_sync(s, A_BBRAM_8); | |
311 | } | |
312 | } | |
313 | ||
314 | static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64) | |
315 | { | |
316 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
317 | ||
318 | /* Never lock if bbram8 is wo; and, only POR can clear the lock */ | |
319 | if (s->bbram8_wo) { | |
320 | val64 = 0; | |
321 | } else { | |
322 | val64 |= s->regs[R_BBRAM_MSW_LOCK]; | |
323 | } | |
324 | ||
325 | return val64; | |
326 | } | |
327 | ||
328 | static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64) | |
329 | { | |
330 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
331 | ||
332 | bbram_update_irq(s); | |
333 | } | |
334 | ||
335 | static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64) | |
336 | { | |
337 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
338 | uint32_t val = val64; | |
339 | ||
340 | s->regs[R_BBRAM_IMR] &= ~val; | |
341 | bbram_update_irq(s); | |
342 | return 0; | |
343 | } | |
344 | ||
345 | static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64) | |
346 | { | |
347 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
348 | uint32_t val = val64; | |
349 | ||
350 | s->regs[R_BBRAM_IMR] |= val; | |
351 | bbram_update_irq(s); | |
352 | return 0; | |
353 | } | |
354 | ||
355 | static RegisterAccessInfo bbram_ctrl_regs_info[] = { | |
356 | { .name = "BBRAM_STATUS", .addr = A_BBRAM_STATUS, | |
357 | .rsvd = 0xee, | |
358 | .ro = 0x3ff, | |
359 | },{ .name = "BBRAM_CTRL", .addr = A_BBRAM_CTRL, | |
360 | .post_write = bbram_ctrl_postw, | |
361 | },{ .name = "PGM_MODE", .addr = A_PGM_MODE, | |
362 | .post_write = bbram_pgm_mode_postw, | |
363 | },{ .name = "BBRAM_AES_CRC", .addr = A_BBRAM_AES_CRC, | |
364 | .post_write = bbram_aes_crc_postw, | |
365 | .post_read = bbram_wo_postr, | |
366 | },{ .name = "BBRAM_0", .addr = A_BBRAM_0, | |
367 | .pre_write = bbram_key_prew, | |
368 | .post_write = bbram_key_postw, | |
369 | .post_read = bbram_wo_postr, | |
370 | },{ .name = "BBRAM_1", .addr = A_BBRAM_1, | |
371 | .pre_write = bbram_key_prew, | |
372 | .post_write = bbram_key_postw, | |
373 | .post_read = bbram_wo_postr, | |
374 | },{ .name = "BBRAM_2", .addr = A_BBRAM_2, | |
375 | .pre_write = bbram_key_prew, | |
376 | .post_write = bbram_key_postw, | |
377 | .post_read = bbram_wo_postr, | |
378 | },{ .name = "BBRAM_3", .addr = A_BBRAM_3, | |
379 | .pre_write = bbram_key_prew, | |
380 | .post_write = bbram_key_postw, | |
381 | .post_read = bbram_wo_postr, | |
382 | },{ .name = "BBRAM_4", .addr = A_BBRAM_4, | |
383 | .pre_write = bbram_key_prew, | |
384 | .post_write = bbram_key_postw, | |
385 | .post_read = bbram_wo_postr, | |
386 | },{ .name = "BBRAM_5", .addr = A_BBRAM_5, | |
387 | .pre_write = bbram_key_prew, | |
388 | .post_write = bbram_key_postw, | |
389 | .post_read = bbram_wo_postr, | |
390 | },{ .name = "BBRAM_6", .addr = A_BBRAM_6, | |
391 | .pre_write = bbram_key_prew, | |
392 | .post_write = bbram_key_postw, | |
393 | .post_read = bbram_wo_postr, | |
394 | },{ .name = "BBRAM_7", .addr = A_BBRAM_7, | |
395 | .pre_write = bbram_key_prew, | |
396 | .post_write = bbram_key_postw, | |
397 | .post_read = bbram_wo_postr, | |
398 | },{ .name = "BBRAM_8", .addr = A_BBRAM_8, | |
399 | .pre_write = bbram_r8_prew, | |
400 | .post_write = bbram_r8_postw, | |
401 | .post_read = bbram_r8_postr, | |
402 | },{ .name = "BBRAM_SLVERR", .addr = A_BBRAM_SLVERR, | |
403 | .rsvd = ~1, | |
404 | },{ .name = "BBRAM_ISR", .addr = A_BBRAM_ISR, | |
405 | .w1c = 0x1, | |
406 | .post_write = bbram_isr_postw, | |
407 | },{ .name = "BBRAM_IMR", .addr = A_BBRAM_IMR, | |
408 | .ro = 0x1, | |
409 | },{ .name = "BBRAM_IER", .addr = A_BBRAM_IER, | |
410 | .pre_write = bbram_ier_prew, | |
411 | },{ .name = "BBRAM_IDR", .addr = A_BBRAM_IDR, | |
412 | .pre_write = bbram_idr_prew, | |
413 | },{ .name = "BBRAM_MSW_LOCK", .addr = A_BBRAM_MSW_LOCK, | |
414 | .pre_write = bbram_msw_lock_prew, | |
415 | .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK, | |
416 | } | |
417 | }; | |
418 | ||
419 | static void bbram_ctrl_reset(DeviceState *dev) | |
420 | { | |
421 | XlnxBBRam *s = XLNX_BBRAM(dev); | |
422 | unsigned int i; | |
423 | ||
424 | for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { | |
425 | if (i < R_BBRAM_0 || i > R_BBRAM_8) { | |
426 | register_reset(&s->regs_info[i]); | |
427 | } | |
428 | } | |
429 | ||
430 | bbram_update_irq(s); | |
431 | } | |
432 | ||
433 | static const MemoryRegionOps bbram_ctrl_ops = { | |
434 | .read = register_read_memory, | |
435 | .write = register_write_memory, | |
436 | .endianness = DEVICE_LITTLE_ENDIAN, | |
437 | .valid = { | |
438 | .min_access_size = 4, | |
439 | .max_access_size = 4, | |
440 | }, | |
441 | }; | |
442 | ||
443 | static void bbram_ctrl_realize(DeviceState *dev, Error **errp) | |
444 | { | |
445 | XlnxBBRam *s = XLNX_BBRAM(dev); | |
446 | ||
447 | if (s->crc_zpads) { | |
448 | s->bbram8_wo = true; | |
449 | } | |
450 | ||
451 | bbram_bdrv_read(s, errp); | |
452 | } | |
453 | ||
454 | static void bbram_ctrl_init(Object *obj) | |
455 | { | |
456 | XlnxBBRam *s = XLNX_BBRAM(obj); | |
457 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
458 | RegisterInfoArray *reg_array; | |
459 | ||
460 | reg_array = | |
461 | register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, | |
462 | ARRAY_SIZE(bbram_ctrl_regs_info), | |
463 | s->regs_info, s->regs, | |
464 | &bbram_ctrl_ops, | |
465 | XLNX_BBRAM_ERR_DEBUG, | |
466 | R_MAX * 4); | |
467 | ||
468 | sysbus_init_mmio(sbd, ®_array->mem); | |
469 | sysbus_init_irq(sbd, &s->irq_bbram); | |
470 | } | |
471 | ||
472 | static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, | |
473 | void *opaque, Error **errp) | |
474 | { | |
475 | DeviceState *dev = DEVICE(obj); | |
476 | ||
477 | qdev_prop_drive.set(obj, v, name, opaque, errp); | |
478 | ||
479 | /* Fill initial data if backend is attached after realized */ | |
480 | if (dev->realized) { | |
481 | bbram_bdrv_read(XLNX_BBRAM(obj), errp); | |
482 | } | |
483 | } | |
484 | ||
485 | static void bbram_prop_get_drive(Object *obj, Visitor *v, const char *name, | |
486 | void *opaque, Error **errp) | |
487 | { | |
488 | qdev_prop_drive.get(obj, v, name, opaque, errp); | |
489 | } | |
490 | ||
491 | static void bbram_prop_release_drive(Object *obj, const char *name, | |
492 | void *opaque) | |
493 | { | |
494 | qdev_prop_drive.release(obj, name, opaque); | |
495 | } | |
496 | ||
497 | static const PropertyInfo bbram_prop_drive = { | |
498 | .name = "str", | |
499 | .description = "Node name or ID of a block device to use as BBRAM backend", | |
500 | .realized_set_allowed = true, | |
501 | .get = bbram_prop_get_drive, | |
502 | .set = bbram_prop_set_drive, | |
503 | .release = bbram_prop_release_drive, | |
504 | }; | |
505 | ||
506 | static const VMStateDescription vmstate_bbram_ctrl = { | |
507 | .name = TYPE_XLNX_BBRAM, | |
508 | .version_id = 1, | |
509 | .minimum_version_id = 1, | |
510 | .fields = (VMStateField[]) { | |
511 | VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX), | |
512 | VMSTATE_END_OF_LIST(), | |
513 | } | |
514 | }; | |
515 | ||
516 | static Property bbram_ctrl_props[] = { | |
517 | DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), | |
518 | DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), | |
519 | DEFINE_PROP_END_OF_LIST(), | |
520 | }; | |
521 | ||
522 | static void bbram_ctrl_class_init(ObjectClass *klass, void *data) | |
523 | { | |
524 | DeviceClass *dc = DEVICE_CLASS(klass); | |
525 | ||
526 | dc->reset = bbram_ctrl_reset; | |
527 | dc->realize = bbram_ctrl_realize; | |
528 | dc->vmsd = &vmstate_bbram_ctrl; | |
529 | device_class_set_props(dc, bbram_ctrl_props); | |
530 | } | |
531 | ||
532 | static const TypeInfo bbram_ctrl_info = { | |
533 | .name = TYPE_XLNX_BBRAM, | |
534 | .parent = TYPE_SYS_BUS_DEVICE, | |
535 | .instance_size = sizeof(XlnxBBRam), | |
536 | .class_init = bbram_ctrl_class_init, | |
537 | .instance_init = bbram_ctrl_init, | |
538 | }; | |
539 | ||
540 | static void bbram_ctrl_register_types(void) | |
541 | { | |
542 | type_register_static(&bbram_ctrl_info); | |
543 | } | |
544 | ||
545 | type_init(bbram_ctrl_register_types) |