1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include "qemu/osdep.h"
27#include "hw/sysbus.h"
28#include "hw/ptimer.h"
29#include "qemu/bitops.h"
30#include "qapi/error.h"
31#include "sysemu/dma.h"
32#include "qemu/log.h"
33#include "qemu/main-loop.h"
34#include "hw/core/cpu.h"
35#include "migration/vmstate.h"
36#include "hw/qdev-properties.h"
37#include "hw/qdev-properties-system.h"
38#include "sysemu/reset.h"
39#include "sysemu/blockdev.h"
40#include "sysemu/block-backend.h"
41
42#include "hw/misc/xlnx-zynqmp-pmufw-cfg.h"
43
44#ifndef XILINX_ZYNQMP_BOOT_DEBUG
45#define XILINX_ZYNQMP_BOOT_DEBUG 0
46#endif
47
48#define TYPE_XILINX_ZYNQMP_BOOT "xlnx,zynqmp-boot"
49
50#define XILINX_ZYNQMP_BOOT(obj) \
51 OBJECT_CHECK(ZynqMPBoot, (obj), TYPE_XILINX_ZYNQMP_BOOT)
52
53
54#define IPI_BUFFER_BASEADDR 0xFF990000U
55#define IPI_BUFFER_RPU_0_BASE (IPI_BUFFER_BASEADDR + 0x0U)
56#define IPI_BUFFER_RPU_1_BASE (IPI_BUFFER_BASEADDR + 0x200U)
57#define IPI_BUFFER_APU_BASE (IPI_BUFFER_BASEADDR + 0x400U)
58#define IPI_BUFFER_PMU_BASE (IPI_BUFFER_BASEADDR + 0xE00U)
59
60#define IPI_BUFFER_TARGET_PMU_OFFSET 0x1C0U
61
62#define IPI_BUFFER_REQ_OFFSET 0x0U
63#define IPI_BUFFER_RESP_OFFSET 0x20U
64
65
66#define IPI_BASEADDR 0XFF300000
67#define IPI_APU_IXR_PMU_0_MASK (1 << 16)
68
69#define IPI_TRIG_OFFSET 0
70#define IPI_OBS_OFFSET 4
71
72
73#define PM_INT_NUM 0
74#define IPI_PMU_PM_INT_MASK (IPI_APU_IXR_PMU_0_MASK << PM_INT_NUM)
75
76#define IPI_APU_MASK 1U
77
78#define PAYLOAD_ARG_CNT 6
79#define PM_SET_CONFIGURATION 2
80
81#define CPU_NONE 0xFFFFFFFF
82
83#define DB_PRINT_L(lvl, fmt, args...) do { \
84 if (XILINX_ZYNQMP_BOOT_DEBUG >= lvl) { \
85 qemu_log("%s: " fmt, __func__, ## args); \
86 } \
87} while (0);
88
89#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
90
91typedef enum {
92 STATE_WAIT_RST = 0,
93 STATE_WAIT_PMUFW,
94 STATE_PMUFW_SETCFG,
95 STATE_WAIT_PMUFW_READY,
96 STATE_RELEASE_CPU,
97 STATE_DONE,
98} BootState;
99
100typedef struct ZynqMPBoot {
101 SysBusDevice parent_obj;
102
103 MemoryRegion *dma_mr;
104 AddressSpace *dma_as;
105
106 ptimer_state *ptimer;
107
108 BootState state;
109
110
111 bool n_reset;
112
113 bool boot_ready;
114
115 struct {
116 uint32_t cpu_num;
117 bool use_pmufw;
118 bool load_pmufw_cfg;
119 } cfg;
120
121 BlockBackend *blk;
122 uint8_t *cfg_buf;
123 uint32_t cfg_size;
124 unsigned char *buf;
125} ZynqMPBoot;
126
127static const MemTxAttrs mattr_secure = { .secure = true };
128
129static void boot_store32(ZynqMPBoot *s, uint64_t addr, uint32_t v)
130{
131 address_space_write(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
132}
133
134static uint32_t boot_load32(ZynqMPBoot *s, uint64_t addr)
135{
136 uint32_t v;
137 address_space_read(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
138 return v;
139}
140
141
142
143
144static bool pm_ipi_ready(ZynqMPBoot *s)
145{
146 uint32_t r;
147
148 r = boot_load32(s, IPI_BASEADDR + IPI_OBS_OFFSET);
149 r &= IPI_PMU_PM_INT_MASK;
150 return !r;
151}
152
153
154
155
156static void pm_ipi_send(ZynqMPBoot *s,
157 uint32_t payload[PAYLOAD_ARG_CNT])
158{
159 unsigned int i;
160 unsigned int offset = 0;
161 uintptr_t buffer_base = IPI_BUFFER_APU_BASE +
162 IPI_BUFFER_TARGET_PMU_OFFSET +
163 IPI_BUFFER_REQ_OFFSET;
164
165 assert(pm_ipi_ready(s));
166
167
168 for (i = 0; i < PAYLOAD_ARG_CNT; i++) {
169 boot_store32(s, buffer_base + offset, payload[i]);
170 offset += 4;
171 }
172
173 boot_store32(s, IPI_BASEADDR + IPI_TRIG_OFFSET, IPI_PMU_PM_INT_MASK);
174}
175
176static void release_cpu_set_pc(CPUState *cpu, run_on_cpu_data arg)
177{
178 cpu_set_pc(cpu, arg.target_ptr);
179}
180
181static void release_cpu(ZynqMPBoot *s)
182{
183 CPUState *cpu = qemu_get_cpu(s->cfg.cpu_num);
184 CPUClass *cc = CPU_GET_CLASS(cpu);
185 vaddr pc = 0;
186 uint32_t r;
187
188 DB_PRINT("Starting CPU#%d release\n", s->cfg.cpu_num)
189
190
191
192
193 if (cc->get_pc) {
194 pc = cc->get_pc(cpu);
195 }
196 if (s->cfg.cpu_num < 4) {
197
198 r = boot_load32(s, 0xfd1a0104);
199 r &= ~(1 << s->cfg.cpu_num);
200 boot_store32(s, 0xfd1a0104, 0x80000000 | r);
201 } else {
202
203 }
204 if (cc->set_pc) {
205 DB_PRINT("Setting CPU#%d PC to 0x%" PRIx64 "\n", s->cfg.cpu_num, pc)
206 run_on_cpu(cpu, release_cpu_set_pc, RUN_ON_CPU_TARGET_PTR(pc));
207 }
208}
209
210static bool check_for_pmufw(ZynqMPBoot *s)
211{
212 uint32_t r;
213
214 r = boot_load32(s, 0xFFD80000);
215 return r & (1 << 4);
216}
217
218static void roll_timer(ZynqMPBoot *s)
219{
220 ptimer_set_limit(s->ptimer, 200000, 1);
221 ptimer_run(s->ptimer, 1);
222}
223
224static void boot_sequence(void *opaque)
225{
226 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
227 uint32_t pay[6] = {};
228
229 switch (s->state) {
230 case STATE_WAIT_PMUFW:
231 if (!s->cfg.use_pmufw) {
232 s->state = STATE_RELEASE_CPU;
233 boot_sequence(s);
234 return;
235 }
236
237 if (!check_for_pmufw(s)) {
238 roll_timer(s);
239 return;
240 }
241
242 if (s->cfg.load_pmufw_cfg) {
243 s->state = STATE_PMUFW_SETCFG;
244 } else {
245 s->state = STATE_RELEASE_CPU;
246 }
247 boot_sequence(s);
248 break;
249
250 case STATE_PMUFW_SETCFG:
251 if (!pm_ipi_ready(s)) {
252 roll_timer(s);
253 return;
254 }
255
256 s->cfg_size = s->blk ? blk_getlength(s->blk) :
257 sizeof(pmufw_cfg);
258 s->cfg_buf = g_new0(uint8_t, s->cfg_size);
259 if (s->blk) {
260 blk_pread(s->blk, 0, s->cfg_size, s->cfg_buf, 0);
261 } else {
262 memcpy(s->cfg_buf, pmufw_cfg, s->cfg_size);
263 }
264
265
266 s->buf = g_malloc(s->cfg_size);
267 address_space_read(s->dma_as, 0, mattr_secure,
268 s->buf, s->cfg_size);
269 address_space_write(s->dma_as, 0, mattr_secure,
270 (void *) s->cfg_buf, s->cfg_size);
271 pay[0] = PM_SET_CONFIGURATION;
272 pay[1] = 0;
273 pm_ipi_send(s, pay);
274 s->state = STATE_WAIT_PMUFW_READY;
275 boot_sequence(s);
276 break;
277
278 case STATE_WAIT_PMUFW_READY:
279 if (!pm_ipi_ready(s)) {
280 roll_timer(s);
281 return;
282 }
283
284
285 address_space_write(s->dma_as, 0, mattr_secure,
286 s->buf, s->cfg_size);
287 g_free(s->buf);
288 s->buf = NULL;
289 g_free(s->cfg_buf);
290 s->cfg_buf = NULL;
291
292 s->state = STATE_RELEASE_CPU;
293 boot_sequence(s);
294 break;
295
296 case STATE_RELEASE_CPU:
297 if (s->cfg.cpu_num != CPU_NONE) {
298 release_cpu(s);
299 }
300 s->state = STATE_DONE;
301 s->boot_ready = false;
302 break;
303
304 case STATE_DONE:
305 case STATE_WAIT_RST:
306
307 g_assert_not_reached();
308 break;
309 };
310}
311
312static void irq_handler(void *opaque, int irq, int level)
313{
314 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
315
316 if (!s->n_reset && level) {
317 s->boot_ready = true;
318 }
319 s->n_reset = level;
320}
321
322static void zynqmp_boot_reset(void *opaque)
323{
324 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
325
326 if (s->boot_ready) {
327
328 DB_PRINT("Starting the boot sequence\n");
329 s->state = STATE_WAIT_PMUFW;
330 ptimer_transaction_begin(s->ptimer);
331 boot_sequence(s);
332 ptimer_transaction_commit(s->ptimer);
333 }
334}
335
336static void zynqmp_boot_realize(DeviceState *dev, Error **errp)
337{
338 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(dev);
339
340 if (s->cfg.cpu_num > 3 && s->cfg.cpu_num != CPU_NONE) {
341 error_setg(errp, "cpu-num %u is out of range\n", s->cfg.cpu_num);
342 }
343
344 s->dma_as = s->dma_mr ? address_space_init_shareable(s->dma_mr, NULL)
345 : &address_space_memory;
346
347 qemu_register_reset_loader(zynqmp_boot_reset, dev);
348
349 s->ptimer = ptimer_init(boot_sequence, s, PTIMER_POLICY_LEGACY);
350 ptimer_transaction_begin(s->ptimer);
351 ptimer_set_freq(s->ptimer, 1000000);
352 ptimer_transaction_commit(s->ptimer);
353}
354
355static void zynqmp_boot_unrealize(DeviceState *dev)
356{
357 qemu_unregister_reset_loader(zynqmp_boot_reset, dev);
358}
359
360static void zynqmp_boot_init(Object *obj)
361{
362 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(obj);
363
364 qdev_init_gpio_in(DEVICE(obj), irq_handler, 1);
365 object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
366 (Object **)&s->dma_mr,
367 qdev_prop_allow_set_link_before_realize,
368 OBJ_PROP_LINK_STRONG);
369}
370
371static Property zynqmp_boot_props[] = {
372 DEFINE_PROP_UINT32("cpu-num", ZynqMPBoot, cfg.cpu_num, CPU_NONE),
373 DEFINE_PROP_BOOL("use-pmufw", ZynqMPBoot, cfg.use_pmufw, false),
374 DEFINE_PROP_BOOL("load-pmufw-cfg", ZynqMPBoot, cfg.load_pmufw_cfg, true),
375 DEFINE_PROP_DRIVE("drive", ZynqMPBoot, blk),
376 DEFINE_PROP_END_OF_LIST(),
377};
378
379static void zynqmp_boot_class_init(ObjectClass *klass, void *data)
380{
381 DeviceClass *dc = DEVICE_CLASS(klass);
382
383 dc->realize = zynqmp_boot_realize;
384 device_class_set_props(dc, zynqmp_boot_props);
385 dc->unrealize = zynqmp_boot_unrealize;
386}
387
388static const TypeInfo zynqmp_boot_info = {
389 .name = TYPE_XILINX_ZYNQMP_BOOT,
390 .parent = TYPE_SYS_BUS_DEVICE,
391 .instance_size = sizeof(ZynqMPBoot),
392 .class_init = zynqmp_boot_class_init,
393 .instance_init = zynqmp_boot_init,
394};
395
396static void zynqmp_boot_register_types(void)
397{
398 type_register_static(&zynqmp_boot_info);
399}
400
401type_init(zynqmp_boot_register_types)
402