xref: /qemu/hw/loongarch/virt-fdt-build.c (revision 6ff5da16000f908140723e164d33a0b51a6c4162)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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