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