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