master
c 598 lines 22.5 KB
Raw
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * Copyright (c) 2025 Loongson Technology Corporation Limited
4 */
5 #include "qemu/osdep.h"
6 #include "qemu/error-report.h"
7 #include "qemu/guest-random.h"
8 #include <libfdt.h>
9 #include "hw/acpi/generic_event_device.h"
10 #include "hw/core/sysbus-fdt.h"
11 #include "hw/intc/loongarch_extioi.h"
12 #include "hw/core/loader.h"
13 #include "hw/loongarch/virt.h"
14 #include "hw/pci-host/gpex.h"
15 #include "system/device_tree.h"
16 #include "system/reset.h"
17 #include "target/loongarch/cpu.h"
18
19 #define FDT_IRQ_TYPE_EDGE_RISING 1
20 #define FDT_IRQ_TYPE_EDGE_FALLING 2
21 #define FDT_IRQ_TYPE_LEVEL_HIGH 4
22 #define FDT_IRQ_TYPE_LEVEL_LOW 8
23
24 static void create_fdt(LoongArchVirtMachineState *lvms)
25 {
26 MachineState *ms = MACHINE(lvms);
27 uint8_t rng_seed[32];
28 int ret;
29
30 ms->fdt = create_device_tree(&lvms->fdt_size);
31 if (!ms->fdt) {
32 error_report("create_device_tree() failed");
33 exit(1);
34 }
35
36 /* Header */
37 qemu_fdt_setprop_string(ms->fdt, "/", "compatible",
38 "linux,dummy-loongson3");
39 qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2);
40 qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2);
41 qemu_fdt_add_subnode(ms->fdt, "/chosen");
42
43 if (ms->kernel_cmdline && *ms->kernel_cmdline) {
44 ret = qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs",
45 ms->kernel_cmdline);
46 if (ret < 0) {
47 error_report("set /chosen/bootargs failed");
48 exit(1);
49 }
50 }
51
52 if (lvms->bootinfo.initrd_addr) {
53 ret = qemu_fdt_setprop_cell(ms->fdt, "/chosen", "linux,initrd-start",
54 lvms->bootinfo.initrd_addr);
55 if (ret < 0) {
56 error_report("set /chosen/linux,initrd-start failed");
57 exit(1);
58 }
59
60 ret = qemu_fdt_setprop_cell(ms->fdt, "/chosen", "linux,initrd-end",
61 lvms->bootinfo.initrd_addr +
62 lvms->bootinfo.initrd_size);
63 if (ret < 0) {
64 error_report("set /chosen/linux,initrd-end failed");
65 exit(1);
66 }
67 }
68
69 /* Pass seed to RNG */
70 qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
71 qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
72 }
73
74 static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms)
75 {
76 int num;
77 MachineState *ms = MACHINE(lvms);
78 MachineClass *mc = MACHINE_GET_CLASS(ms);
79 const CPUArchIdList *possible_cpus;
80 LoongArchCPU *cpu;
81 CPUState *cs;
82 char *nodename, *map_path;
83
84 qemu_fdt_add_subnode(ms->fdt, "/cpus");
85 qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
86 qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
87
88 /* cpu nodes */
89 possible_cpus = mc->possible_cpu_arch_ids(ms);
90 for (num = 0; num < possible_cpus->len; num++) {
91 cs = possible_cpus->cpus[num].cpu;
92 if (cs == NULL) {
93 continue;
94 }
95
96 nodename = g_strdup_printf("/cpus/cpu@%d", num);
97 cpu = LOONGARCH_CPU(cs);
98
99 qemu_fdt_add_subnode(ms->fdt, nodename);
100 qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
101 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
102 cpu->dtb_compatible);
103 if (possible_cpus->cpus[num].props.has_node_id) {
104 qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id",
105 possible_cpus->cpus[num].props.node_id);
106 }
107 qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
108 qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
109 qemu_fdt_alloc_phandle(ms->fdt));
110 g_free(nodename);
111 }
112
113 /*cpu map */
114 qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map");
115 for (num = 0; num < possible_cpus->len; num++) {
116 cs = possible_cpus->cpus[num].cpu;
117 if (cs == NULL) {
118 continue;
119 }
120
121 nodename = g_strdup_printf("/cpus/cpu@%d", num);
122 if (ms->smp.threads > 1) {
123 map_path = g_strdup_printf(
124 "/cpus/cpu-map/socket%d/core%d/thread%d",
125 num / (ms->smp.cores * ms->smp.threads),
126 (num / ms->smp.threads) % ms->smp.cores,
127 num % ms->smp.threads);
128 } else {
129 map_path = g_strdup_printf(
130 "/cpus/cpu-map/socket%d/core%d",
131 num / ms->smp.cores,
132 num % ms->smp.cores);
133 }
134 qemu_fdt_add_path(ms->fdt, map_path);
135 qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", nodename);
136
137 g_free(map_path);
138 g_free(nodename);
139 }
140 }
141
142 static void fdt_add_memory_node(MachineState *ms,
143 uint64_t base, uint64_t size, int node_id)
144 {
145 char *nodename = g_strdup_printf("/memory@%" PRIx64, base);
146
147 qemu_fdt_add_subnode(ms->fdt, nodename);
148 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base,
149 size >> 32, size);
150 qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory");
151
152 if (ms->numa_state && ms->numa_state->num_nodes) {
153 qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id);
154 }
155
156 g_free(nodename);
157 }
158
159 static void fdt_add_memory_nodes(MachineState *ms)
160 {
161 hwaddr base, size, ram_size, gap;
162 int i, nb_numa_nodes, nodes;
163 NodeInfo *numa_info;
164
165 ram_size = ms->ram_size;
166 base = VIRT_LOWMEM_BASE;
167 gap = VIRT_LOWMEM_SIZE;
168 nodes = nb_numa_nodes = ms->numa_state->num_nodes;
169 numa_info = ms->numa_state->nodes;
170 if (!nodes) {
171 nodes = 1;
172 }
173
174 for (i = 0; i < nodes; i++) {
175 if (nb_numa_nodes) {
176 size = numa_info[i].node_mem;
177 } else {
178 size = ram_size;
179 }
180
181 /*
182 * memory for the node splited into two part
183 * lowram: [base, +gap)
184 * highram: [VIRT_HIGHMEM_BASE, +(len - gap))
185 */
186 if (size >= gap) {
187 fdt_add_memory_node(ms, base, gap, i);
188 size -= gap;
189 base = VIRT_HIGHMEM_BASE;
190 gap = ram_size - VIRT_LOWMEM_SIZE;
191 }
192
193 if (size) {
194 fdt_add_memory_node(ms, base, size, i);
195 base += size;
196 gap -= size;
197 }
198 }
199 }
200
201 static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms)
202 {
203 char *nodename;
204 hwaddr base = VIRT_FWCFG_BASE;
205 const MachineState *ms = MACHINE(lvms);
206
207 nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base);
208 qemu_fdt_add_subnode(ms->fdt, nodename);
209 qemu_fdt_setprop_string(ms->fdt, nodename,
210 "compatible", "qemu,fw-cfg-mmio");
211 qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
212 2, base, 2, 0x18);
213 qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0);
214 g_free(nodename);
215 }
216
217 static void fdt_add_flash_node(LoongArchVirtMachineState *lvms)
218 {
219 MachineState *ms = MACHINE(lvms);
220 char *nodename;
221 MemoryRegion *flash_mem;
222
223 hwaddr flash0_base;
224 hwaddr flash0_size;
225
226 hwaddr flash1_base;
227 hwaddr flash1_size;
228
229 flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
230 flash0_base = flash_mem->addr;
231 flash0_size = memory_region_size(flash_mem);
232
233 flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
234 flash1_base = flash_mem->addr;
235 flash1_size = memory_region_size(flash_mem);
236
237 nodename = g_strdup_printf("/flash@%" PRIx64, flash0_base);
238 qemu_fdt_add_subnode(ms->fdt, nodename);
239 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash");
240 qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
241 2, flash0_base, 2, flash0_size,
242 2, flash1_base, 2, flash1_size);
243 qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4);
244 g_free(nodename);
245 }
246
247 static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms,
248 uint32_t *cpuintc_phandle)
249 {
250 MachineState *ms = MACHINE(lvms);
251 char *nodename;
252
253 *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
254 nodename = g_strdup_printf("/cpuic");
255 qemu_fdt_add_subnode(ms->fdt, nodename);
256 qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle);
257 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
258 "loongson,cpu-interrupt-controller");
259 qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
260 qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
261 g_free(nodename);
262 }
263
264 static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms,
265 uint32_t *cpuintc_phandle,
266 uint32_t *eiointc_phandle)
267 {
268 MachineState *ms = MACHINE(lvms);
269 char *nodename;
270 hwaddr extioi_base = APIC_BASE;
271 hwaddr extioi_size = EXTIOI_SIZE;
272
273 *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
274 nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base);
275 qemu_fdt_add_subnode(ms->fdt, nodename);
276 qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle);
277 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
278 "loongson,ls2k2000-eiointc");
279 qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
280 qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
281 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
282 *cpuintc_phandle);
283 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3);
284 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0,
285 extioi_base, 0x0, extioi_size);
286 g_free(nodename);
287 }
288
289 static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms,
290 uint32_t *eiointc_phandle,
291 uint32_t *pch_pic_phandle)
292 {
293 MachineState *ms = MACHINE(lvms);
294 char *nodename;
295 hwaddr pch_pic_base = VIRT_PCH_REG_BASE;
296 hwaddr pch_pic_size = VIRT_PCH_REG_SIZE;
297
298 *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
299 nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base);
300 qemu_fdt_add_subnode(ms->fdt, nodename);
301 qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle);
302 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
303 "loongson,pch-pic-1.0");
304 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0,
305 pch_pic_base, 0, pch_pic_size);
306 qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
307 qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2);
308 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
309 *eiointc_phandle);
310 qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0);
311 g_free(nodename);
312 }
313
314 static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms,
315 uint32_t *eiointc_phandle,
316 uint32_t *pch_msi_phandle)
317 {
318 MachineState *ms = MACHINE(lvms);
319 char *nodename;
320 hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW;
321 hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE;
322
323 *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt);
324 nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base);
325 qemu_fdt_add_subnode(ms->fdt, nodename);
326 qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle);
327 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
328 "loongson,pch-msi-1.0");
329 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg",
330 0, pch_msi_base,
331 0, pch_msi_size);
332 qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
333 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
334 *eiointc_phandle);
335 qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec",
336 VIRT_PCH_PIC_IRQ_NUM);
337 qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs",
338 EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM);
339 g_free(nodename);
340 }
341
342 static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
343 char *nodename,
344 uint32_t *pch_pic_phandle)
345 {
346 int pin, dev;
347 uint32_t irq_map_stride = 0;
348 uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 10] = {};
349 uint32_t *irq_map = full_irq_map;
350 const MachineState *ms = MACHINE(lvms);
351 uint32_t pin_mask;
352 uint32_t devfn_mask;
353
354 /*
355 * This code creates a standard swizzle of interrupts such that
356 * each device's first interrupt is based on it's PCI_SLOT number.
357 * (See pci_swizzle_map_irq_fn())
358 *
359 * We only need one entry per interrupt in the table (not one per
360 * possible slot) seeing the interrupt-map-mask will allow the table
361 * to wrap to any number of devices.
362 */
363
364 for (dev = 0; dev < PCI_NUM_PINS; dev++) {
365 int devfn = PCI_DEVFN(dev, 0);
366
367 for (pin = 0; pin < PCI_NUM_PINS; pin++) {
368 int irq_nr = VIRT_DEVICE_IRQS + \
369 ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
370 int i = 0;
371
372 uint32_t map[] = {
373 devfn << 8, 0, 0, /* devfn */
374 pin + 1, /* PCI pin */
375 *pch_pic_phandle, /* interrupt controller handle */
376 irq_nr, /* irq number */
377 FDT_IRQ_TYPE_LEVEL_HIGH }; /* irq trigger level */
378
379 if (!irq_map_stride) {
380 irq_map_stride = sizeof(map) / sizeof(uint32_t);
381 }
382
383 /* Convert map to big endian */
384 for (i = 0; i < irq_map_stride; i++) {
385 irq_map[i] = cpu_to_be32(map[i]);
386 }
387
388 irq_map += irq_map_stride;
389 }
390 }
391
392 qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map,
393 PCI_NUM_PINS * PCI_NUM_PINS *
394 irq_map_stride * sizeof(uint32_t));
395
396 /* Only need to match the pci slot bit */
397 devfn_mask = PCI_DEVFN((PCI_NUM_PINS - 1), 0) << 8;
398 /* The pci interrupt only needs to match the specified low bit */
399 pin_mask = (1 << ((PCI_NUM_PINS - 1))) - 1;
400
401 qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask",
402 devfn_mask, 0, 0, /* address cells */
403 pin_mask);
404 }
405
406 static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms,
407 uint32_t *pch_pic_phandle,
408 uint32_t *pch_msi_phandle)
409 {
410 char *nodename;
411 hwaddr base_mmio, base_mmio_high;
412 hwaddr size_mmio, size_mmio_high;
413 hwaddr base_pio = lvms->gpex.pio.base;
414 hwaddr size_pio = lvms->gpex.pio.size;
415 hwaddr base_pcie = lvms->gpex.ecam.base;
416 hwaddr size_pcie = lvms->gpex.ecam.size;
417 hwaddr base = base_pcie;
418 const MachineState *ms = MACHINE(lvms);
419
420 nodename = g_strdup_printf("/pcie@%" PRIx64, base);
421 qemu_fdt_add_subnode(ms->fdt, nodename);
422 qemu_fdt_setprop_string(ms->fdt, nodename,
423 "compatible", "pci-host-ecam-generic");
424 qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci");
425 qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3);
426 qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2);
427 qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0);
428 qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0,
429 PCIE_MMCFG_BUS(size_pcie - 1));
430 qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0);
431 qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
432 2, base_pcie, 2, size_pcie);
433 if (lvms->highmem_mmio) {
434 base_mmio_high = lvms->gpex.mmio64.base;
435 size_mmio_high = lvms->gpex.mmio64.size;
436 base_mmio = lvms->gpex.mmio32.base;
437 size_mmio = lvms->gpex.mmio32.size;
438 qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
439 1, FDT_PCI_RANGE_IOPORT,
440 2, VIRT_PCI_IO_OFFSET,
441 2, base_pio, 2, size_pio,
442 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
443 2, base_mmio, 2, size_mmio,
444 1, FDT_PCI_RANGE_MMIO_64BIT,
445 2, base_mmio_high,
446 2, base_mmio_high, 2, size_mmio_high);
447 } else {
448 base_mmio = lvms->gpex.mmio64.base;
449 size_mmio = lvms->gpex.mmio64.size;
450 qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
451 1, FDT_PCI_RANGE_IOPORT,
452 2, VIRT_PCI_IO_OFFSET,
453 2, base_pio, 2, size_pio,
454 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
455 2, base_mmio, 2, size_mmio);
456 }
457 qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map",
458 0, *pch_msi_phandle, 0, 0x10000);
459
460 qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
461 fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle);
462 g_free(nodename);
463 }
464
465 static void fdt_add_uart_node(LoongArchVirtMachineState *lvms,
466 uint32_t *pch_pic_phandle, hwaddr base,
467 int irq, bool chosen)
468 {
469 char *nodename;
470 hwaddr size = VIRT_UART_SIZE;
471 MachineState *ms = MACHINE(lvms);
472
473 nodename = g_strdup_printf("/serial@%" PRIx64, base);
474 qemu_fdt_add_subnode(ms->fdt, nodename);
475 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a");
476 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size);
477 qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000);
478 if (chosen) {
479 qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
480 }
481 qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq,
482 FDT_IRQ_TYPE_LEVEL_HIGH);
483 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
484 *pch_pic_phandle);
485 g_free(nodename);
486 }
487
488 static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms,
489 uint32_t *pch_pic_phandle)
490 {
491 char *nodename;
492 hwaddr base = VIRT_RTC_REG_BASE;
493 hwaddr size = VIRT_RTC_LEN;
494 MachineState *ms = MACHINE(lvms);
495
496 nodename = g_strdup_printf("/rtc@%" PRIx64, base);
497 qemu_fdt_add_subnode(ms->fdt, nodename);
498 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
499 "loongson,ls7a-rtc");
500 qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
501 qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
502 VIRT_RTC_IRQ - VIRT_GSI_BASE ,
503 FDT_IRQ_TYPE_LEVEL_HIGH);
504 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
505 *pch_pic_phandle);
506 g_free(nodename);
507 }
508
509 static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms)
510 {
511 char *name;
512 uint32_t ged_handle;
513 MachineState *ms = MACHINE(lvms);
514 hwaddr base = VIRT_GED_REG_ADDR;
515 hwaddr size = ACPI_GED_REG_COUNT;
516
517 ged_handle = qemu_fdt_alloc_phandle(ms->fdt);
518 name = g_strdup_printf("/ged@%" PRIx64, base);
519 qemu_fdt_add_subnode(ms->fdt, name);
520 qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon");
521 qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size);
522 /* 8 bit registers */
523 qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0);
524 qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1);
525 qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle);
526 ged_handle = qemu_fdt_get_phandle(ms->fdt, name);
527 g_free(name);
528
529 name = g_strdup_printf("/reboot");
530 qemu_fdt_add_subnode(ms->fdt, name);
531 qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot");
532 qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
533 qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET);
534 qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE);
535 g_free(name);
536
537 name = g_strdup_printf("/poweroff");
538 qemu_fdt_add_subnode(ms->fdt, name);
539 qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff");
540 qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
541 qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL);
542 qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN |
543 (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS));
544 g_free(name);
545 }
546
547 void virt_fdt_setup(LoongArchVirtMachineState *lvms)
548 {
549 MachineState *machine = MACHINE(lvms);
550 uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle;
551 int i;
552
553 create_fdt(lvms);
554 fdt_add_cpu_nodes(lvms);
555 fdt_add_memory_nodes(machine);
556 fdt_add_fw_cfg_node(lvms);
557 fdt_add_flash_node(lvms);
558
559 /* Add cpu interrupt-controller */
560 fdt_add_cpuic_node(lvms, &cpuintc_phandle);
561 /* Add Extend I/O Interrupt Controller node */
562 fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle);
563 /* Add PCH PIC node */
564 fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle);
565 /* Add PCH MSI node */
566 fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle);
567 /* Add pcie node */
568 fdt_add_pcie_node(lvms, &pch_pic_phandle, &pch_msi_phandle);
569
570 /*
571 * Create uart fdt node in reverse order so that they appear
572 * in the finished device tree lowest address first
573 */
574 for (i = VIRT_UART_COUNT; i-- > 0;) {
575 hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE;
576 int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE;
577 fdt_add_uart_node(lvms, &pch_pic_phandle, base, irq, i == 0);
578 }
579
580 fdt_add_rtc_node(lvms, &pch_pic_phandle);
581 fdt_add_ged_reset(lvms);
582 platform_bus_add_all_fdt_nodes(machine->fdt, "/platic",
583 VIRT_PLATFORM_BUS_BASEADDRESS,
584 VIRT_PLATFORM_BUS_SIZE,
585 VIRT_PLATFORM_BUS_IRQ);
586
587 /*
588 * Since lowmem region starts from 0 and Linux kernel legacy start address
589 * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer
590 * access. FDT size limit with 1 MiB.
591 * Put the FDT into the memory map as a ROM image: this will ensure
592 * the FDT is copied again upon reset, even if addr points into RAM.
593 */
594 rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE,
595 &address_space_memory);
596 qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds,
597 rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size));
598 }