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
27
28#include "qemu/osdep.h"
29#include "cpu.h"
30#include "hw/sysbus.h"
31#include "qemu/log.h"
32#include "net/net.h"
33#include "hw/block/flash.h"
34#include "sysemu/sysemu.h"
35#include "hw/boards.h"
36#include "sysemu/device_tree.h"
37#include "exec/memory.h"
38#include "exec/address-spaces.h"
39#include "qemu/error-report.h"
40#include "qapi/error.h"
41#include "sysemu/qtest.h"
42#include "qemu/config-file.h"
43
44#include "hw/fdt_generic_util.h"
45#include "hw/fdt_generic_devices.h"
46
47#include "boot.h"
48
49#define VAL(name) qemu_fdt_getprop_cell(fdt_g, node_path, name, 0, false, NULL)
50
51#define IS_PETALINUX_MACHINE \
52 (!strcmp(MACHINE_GET_CLASS(machine)->name, MACHINE_NAME "-plnx"))
53
54#define QTEST_RUNNING (qtest_enabled() && qtest_driver())
55
56
57static void *fdt_g;
58
59static void
60microblaze_generic_fdt_reset(MicroBlazeCPU *cpu)
61{
62 CPUMBState *env = &cpu->env;
63
64 char node_path[DT_PATH_LENGTH];
65 qemu_devtree_get_node_by_name(fdt_g, node_path, "cpu");
66 int t;
67 int use_exc = 0;
68
69 env->pvr.regs[0] = 0;
70 env->pvr.regs[2] = PVR2_D_OPB_MASK \
71 | PVR2_D_LMB_MASK \
72 | PVR2_I_OPB_MASK \
73 | PVR2_I_LMB_MASK \
74 | 0;
75
76
77
78
79
80 if (VAL("xlnx,use-barrel")) {
81 env->pvr.regs[0] |= PVR0_USE_BARREL_MASK;
82 env->pvr.regs[2] |= PVR2_USE_BARREL_MASK;
83 }
84
85 if (VAL("xlnx,use-div")) {
86 env->pvr.regs[0] |= PVR0_USE_DIV_MASK;
87 env->pvr.regs[2] |= PVR2_USE_DIV_MASK;
88 }
89
90 t = VAL("xlnx,use-hw-mul");
91 if (t) {
92 env->pvr.regs[0] |= PVR0_USE_HW_MUL_MASK;
93 env->pvr.regs[2] |= PVR2_USE_HW_MUL_MASK;
94 if (t >= 2) {
95 env->pvr.regs[2] |= PVR2_USE_MUL64_MASK;
96 }
97 }
98
99 if (VAL("xlnx,use-msr-instr")) {
100 env->pvr.regs[2] |= PVR2_USE_MSR_INSTR;
101 }
102
103 if (VAL("xlnx,use-pcmp-instr")) {
104 env->pvr.regs[2] |= PVR2_USE_PCMP_INSTR;
105 }
106
107 if (VAL("xlnx,opcode-0x0-illegal")) {
108 env->pvr.regs[2] |= PVR2_OPCODE_0x0_ILL_MASK;
109 }
110
111 if (VAL("xlnx,unaligned-exceptions")) {
112 env->pvr.regs[2] |= PVR2_UNALIGNED_EXC_MASK;
113 use_exc = 1;
114 }
115
116 if (VAL("xlnx,ill-opcode-exception")) {
117 env->pvr.regs[2] |= PVR2_ILL_OPCODE_EXC_MASK;
118 use_exc = 1;
119 }
120
121 if (VAL("xlnx,iopb-bus-exception")) {
122 env->pvr.regs[2] |= PVR2_IOPB_BUS_EXC_MASK;
123 use_exc = 1;
124 }
125
126 if (VAL("xlnx,dopb-bus-exception")) {
127 env->pvr.regs[2] |= PVR2_DOPB_BUS_EXC_MASK;
128 use_exc = 1;
129 }
130
131 if (VAL("xlnx,div-zero-exception")) {
132 env->pvr.regs[2] |= PVR2_DIV_ZERO_EXC_MASK;
133 use_exc = 1;
134 }
135
136 env->pvr.regs[0] |= VAL("xlnx,pvr-user1") & 0xff;
137 env->pvr.regs[1] = VAL("xlnx,pvr-user2");
138
139
140 t = VAL("xlnx,use-mmu");
141 if (use_exc || t) {
142 env->pvr.regs[0] |= PVR0_USE_EXC_MASK ;
143 }
144
145 env->pvr.regs[11] = t << 30;
146 t = VAL("xlnx,mmu-zones");
147 env->pvr.regs[11] |= t << 17;
148 env->mmu.c_mmu_zones = t;
149
150 t = VAL("xlnx,mmu-tlb-access");
151 env->mmu.c_mmu_tlb_access = t;
152 env->pvr.regs[11] |= t << 22;
153
154 {
155 char *str;
156 const struct {
157 const char *name;
158 unsigned int arch;
159 } arch_lookup[] = {
160 {"virtex2", 0x4},
161 {"virtex2pro", 0x5},
162 {"spartan3", 0x6},
163 {"virtex4", 0x7},
164 {"virtex5", 0x8},
165 {"spartan3e", 0x9},
166 {"spartan3a", 0xa},
167 {"spartan3an", 0xb},
168 {"spartan3adsp", 0xc},
169 {"spartan6", 0xd},
170 {"virtex6", 0xe},
171 {"virtex7", 0xf},
172 {"kintex7", 0x10},
173 {"artix7", 0x11},
174 {"zynq7000", 0x12},
175 {"spartan2", 0xf0},
176 {NULL, 0},
177 };
178 unsigned int i = 0;
179
180 str = qemu_fdt_getprop(fdt_g, node_path, "xlnx,family", NULL, false, NULL);
181 while (arch_lookup[i].name && str) {
182 if (strcmp(arch_lookup[i].name, str) == 0) {
183 break;
184 }
185 i++;
186 }
187 if (!str || !arch_lookup[i].arch) {
188 env->pvr.regs[10] = 0x0c000000;
189 } else {
190 env->pvr.regs[10] = arch_lookup[i].arch << 24;
191 }
192 g_free(str);
193 }
194
195 {
196 env->pvr.regs[4] = PVR4_USE_ICACHE_MASK
197 | (21 << 26)
198 | (4 << 21)
199 | (11 << 16);
200 env->pvr.regs[6] = VAL("d-cache-baseaddr");
201 env->pvr.regs[7] = VAL("d-cache-highaddr");
202 env->pvr.regs[5] = PVR5_USE_DCACHE_MASK
203 | (21 << 26)
204 | (4 << 21)
205 | (11 << 16);
206
207 if (VAL("xlnx,dcache-use-writeback")) {
208 env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK;
209 }
210
211 env->pvr.regs[8] = VAL("i-cache-baseaddr");
212 env->pvr.regs[9] = VAL("i-cache-highaddr");
213 }
214 if (VAL("qemu,halt")) {
215 cpu_interrupt(ENV_GET_CPU(env), CPU_INTERRUPT_HALT);
216 }
217}
218
219static void secondary_cpu_reset(void *opaque)
220{
221 MicroBlazeCPU *cpu = MICROBLAZE_CPU(opaque);
222
223
224 microblaze_generic_fdt_reset(cpu);
225}
226
227#define LMB_BRAM_SIZE (128 * 1024)
228
229#define MACHINE_NAME "microblaze-fdt"
230
231#ifdef TARGET_WORDS_BIGENDIAN
232int endian = 1;
233#else
234int endian;
235#endif
236
237static void
238microblaze_generic_fdt_init(MachineState *machine)
239{
240 CPUState *cpu;
241 ram_addr_t ram_kernel_base = 0, ram_kernel_size = 0;
242 void *fdt = NULL;
243 const char *dtb_arg, *hw_dtb_arg;
244 QemuOpts *machine_opts;
245 int fdt_size;
246
247
248 char node_path[DT_PATH_LENGTH];
249 FDTMachineInfo *fdti;
250 MemoryRegion *main_mem;
251
252
253 char dma_path[DT_PATH_LENGTH] = { 0 };
254 uint32_t memory_phandle;
255
256
257 char **eth_paths;
258 char *phy_path;
259 char *mdio_path;
260 uint32_t n_eth;
261 uint32_t prop_val;
262
263 machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
264 if (!machine_opts) {
265 goto no_dtb_arg;
266 }
267 dtb_arg = qemu_opt_get(machine_opts, "dtb");
268 hw_dtb_arg = qemu_opt_get(machine_opts, "hw-dtb");
269 if (!dtb_arg && !hw_dtb_arg) {
270 goto no_dtb_arg;
271 }
272
273
274 if (!hw_dtb_arg) {
275 hw_dtb_arg = dtb_arg;
276 }
277
278 fdt = load_device_tree(hw_dtb_arg, &fdt_size);
279 if (!fdt) {
280 hw_error("Error: Unable to load Device Tree %s\n", hw_dtb_arg);
281 return;
282 }
283
284 if (IS_PETALINUX_MACHINE) {
285
286
287
288 add_to_compat_table(NULL, "compatible:simple-bus", NULL);
289 }
290
291
292 while (qemu_devtree_get_node_by_name(fdt, node_path, "memory")) {
293 qemu_fdt_add_subnode(fdt, "/memory@0");
294 qemu_fdt_setprop_cells(fdt, "/memory@0", "reg", 0, machine->ram_size);
295 }
296
297 if (!qemu_fdt_getprop(fdt, "/memory", "compatible", NULL, 0, NULL)) {
298 qemu_fdt_setprop_string(fdt, "/memory", "compatible",
299 "qemu:memory-region");
300 qemu_fdt_setprop_cells(fdt, "/memory", "qemu,ram", 1);
301 }
302
303 if (IS_PETALINUX_MACHINE) {
304
305
306
307
308 qemu_devtree_get_node_by_name(fdt, dma_path, "dma");
309
310 if (strcmp(dma_path, "") != 0) {
311 memory_phandle = qemu_fdt_check_phandle(fdt, node_path);
312
313 if (!memory_phandle) {
314 memory_phandle = qemu_fdt_alloc_phandle(fdt);
315
316 qemu_fdt_setprop_cells(fdt, "/memory", "linux,phandle",
317 memory_phandle);
318 qemu_fdt_setprop_cells(fdt, "/memory", "phandle",
319 memory_phandle);
320 }
321
322 if (!qemu_fdt_getprop(fdt, dma_path, "sg", NULL, 0, NULL)) {
323 qemu_fdt_setprop_phandle(fdt, dma_path, "sg", node_path);
324 }
325
326 if (!qemu_fdt_getprop(fdt, dma_path, "s2mm", NULL, 0, NULL)) {
327 qemu_fdt_setprop_phandle(fdt, dma_path, "s2mm", node_path);
328 }
329
330 if (!qemu_fdt_getprop(fdt, dma_path, "mm2s", NULL, 0, NULL)) {
331 qemu_fdt_setprop_phandle(fdt, dma_path, "mm2s", node_path);
332 }
333 }
334
335
336 n_eth = qemu_devtree_get_n_nodes_by_name(fdt, ð_paths, "ethernet");
337
338 while (n_eth--) {
339 mdio_path = qemu_devtree_get_child_by_name(fdt, eth_paths[n_eth],
340 "mdio");
341 if (mdio_path) {
342 phy_path = qemu_devtree_get_child_by_name(fdt, mdio_path,
343 "phy");
344 if (phy_path) {
345 prop_val = qemu_fdt_getprop_cell(fdt, phy_path, "reg", 0,
346 NULL, &error_abort);
347 qemu_fdt_setprop_cell(fdt, eth_paths[n_eth], "xlnx,phyaddr",
348 prop_val);
349 g_free(phy_path);
350 } else {
351 qemu_log_mask(LOG_GUEST_ERROR, "phy not found in %s",
352 mdio_path);
353 }
354 g_free(mdio_path);
355 }
356 g_free(eth_paths[n_eth]);
357 }
358 g_free(eth_paths);
359 }
360
361
362 fdti = fdt_generic_create_machine(fdt, NULL);
363 main_mem = MEMORY_REGION(object_resolve_path(node_path, NULL));
364
365 ram_kernel_base = object_property_get_int(OBJECT(main_mem), "addr", NULL);
366 ram_kernel_size = object_property_get_int(OBJECT(main_mem), "size", NULL);
367
368 if (!memory_region_is_mapped(main_mem)) {
369
370
371
372
373 memory_region_add_subregion(get_system_memory(), ram_kernel_base,
374 main_mem);
375
376 if (ram_kernel_base && IS_PETALINUX_MACHINE) {
377
378
379
380
381 MemoryRegion *hack_ram = g_new(MemoryRegion, 1);
382 memory_region_init_ram_nomigrate(hack_ram, NULL, "hack_ram",
383 0x1000, &error_abort);
384 vmstate_register_ram_global(hack_ram);
385 memory_region_add_subregion(get_system_memory(), 0, hack_ram);
386 }
387 }
388
389 fdt_init_destroy_fdti(fdti);
390
391 fdt_g = fdt;
392 microblaze_load_kernel(MICROBLAZE_CPU(first_cpu), ram_kernel_base,
393 ram_kernel_size, machine->initrd_filename, NULL,
394 microblaze_generic_fdt_reset, fdt, fdt_size);
395
396
397 cpu = CPU_NEXT(first_cpu);
398 while (cpu) {
399 qemu_register_reset(secondary_cpu_reset, cpu);
400 cpu = CPU_NEXT(cpu);
401 }
402
403 return;
404no_dtb_arg:
405 if (!QTEST_RUNNING) {
406 hw_error("DTB must be specified for %s machine model\n", MACHINE_NAME);
407 }
408 return;
409}
410
411static void microblaze_generic_fdt_machine_init(MachineClass *mc)
412{
413 mc->desc = "Microblaze device tree driven machine model";
414 mc->init = microblaze_generic_fdt_init;
415}
416
417static void microblaze_generic_fdt_plnx_machine_init(MachineClass *mc)
418{
419 mc->desc = "Microblaze device tree driven machine model for PetaLinux";
420 mc->init = microblaze_generic_fdt_init;
421}
422
423fdt_register_compatibility_opaque(pflash_cfi01_fdt_init, "compatible:cfi-flash",
424 0, &endian);
425
426DEFINE_MACHINE(MACHINE_NAME, microblaze_generic_fdt_machine_init)
427DEFINE_MACHINE(MACHINE_NAME "-plnx", microblaze_generic_fdt_plnx_machine_init)
428