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