17c9aac00SAnup Patel #include "kvm/devices.h" 27c9aac00SAnup Patel #include "kvm/fdt.h" 37c9aac00SAnup Patel #include "kvm/kvm.h" 47c9aac00SAnup Patel #include "kvm/kvm-cpu.h" 57c9aac00SAnup Patel 67c9aac00SAnup Patel #include <stdbool.h> 77c9aac00SAnup Patel 87c9aac00SAnup Patel #include <linux/byteorder.h> 97c9aac00SAnup Patel #include <linux/kernel.h> 107c9aac00SAnup Patel #include <linux/sizes.h> 117c9aac00SAnup Patel 127c9aac00SAnup Patel static void dump_fdt(const char *dtb_file, void *fdt) 137c9aac00SAnup Patel { 147c9aac00SAnup Patel int count, fd; 157c9aac00SAnup Patel 167c9aac00SAnup Patel fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666); 177c9aac00SAnup Patel if (fd < 0) 187c9aac00SAnup Patel die("Failed to write dtb to %s", dtb_file); 197c9aac00SAnup Patel 207c9aac00SAnup Patel count = write(fd, fdt, FDT_MAX_SIZE); 217c9aac00SAnup Patel if (count < 0) 227c9aac00SAnup Patel die_perror("Failed to dump dtb"); 237c9aac00SAnup Patel 247c9aac00SAnup Patel pr_debug("Wrote %d bytes to dtb %s", count, dtb_file); 257c9aac00SAnup Patel close(fd); 267c9aac00SAnup Patel } 277c9aac00SAnup Patel 287c9aac00SAnup Patel #define CPU_NAME_MAX_LEN 15 297c9aac00SAnup Patel #define CPU_ISA_MAX_LEN 128 307c9aac00SAnup Patel static void generate_cpu_nodes(void *fdt, struct kvm *kvm) 317c9aac00SAnup Patel { 327c9aac00SAnup Patel int cpu, pos, i, index, valid_isa_len; 337c9aac00SAnup Patel const char *valid_isa_order = "IEMAFDQCLBJTPVNSUHKORWXYZG"; 347c9aac00SAnup Patel 357c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "cpus")); 367c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#address-cells", 0x1)); 377c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#size-cells", 0x0)); 387c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "timebase-frequency", 397c9aac00SAnup Patel kvm->cpus[0]->riscv_timebase)); 407c9aac00SAnup Patel 417c9aac00SAnup Patel for (cpu = 0; cpu < kvm->nrcpus; ++cpu) { 427c9aac00SAnup Patel char cpu_name[CPU_NAME_MAX_LEN]; 437c9aac00SAnup Patel char cpu_isa[CPU_ISA_MAX_LEN]; 447c9aac00SAnup Patel struct kvm_cpu *vcpu = kvm->cpus[cpu]; 457c9aac00SAnup Patel 467c9aac00SAnup Patel snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%x", cpu); 477c9aac00SAnup Patel 487c9aac00SAnup Patel snprintf(cpu_isa, CPU_ISA_MAX_LEN, "rv%ld", vcpu->riscv_xlen); 497c9aac00SAnup Patel pos = strlen(cpu_isa); 507c9aac00SAnup Patel valid_isa_len = strlen(valid_isa_order); 517c9aac00SAnup Patel for (i = 0; i < valid_isa_len; i++) { 527c9aac00SAnup Patel index = valid_isa_order[i] - 'A'; 537c9aac00SAnup Patel if (vcpu->riscv_isa & (1 << (index))) 547c9aac00SAnup Patel cpu_isa[pos++] = 'a' + index; 557c9aac00SAnup Patel } 567c9aac00SAnup Patel cpu_isa[pos] = '\0'; 577c9aac00SAnup Patel 587c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, cpu_name)); 597c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "device_type", "cpu")); 607c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "riscv")); 617c9aac00SAnup Patel if (vcpu->riscv_xlen == 64) 627c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "mmu-type", 637c9aac00SAnup Patel "riscv,sv48")); 647c9aac00SAnup Patel else 657c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "mmu-type", 667c9aac00SAnup Patel "riscv,sv32")); 677c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "riscv,isa", cpu_isa)); 687c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "reg", cpu)); 697c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "status", "okay")); 707c9aac00SAnup Patel 717c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "interrupt-controller")); 727c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "riscv,cpu-intc")); 737c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#interrupt-cells", 1)); 747c9aac00SAnup Patel _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0)); 757c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "phandle", 767c9aac00SAnup Patel PHANDLE_CPU_INTC_BASE + cpu)); 777c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 787c9aac00SAnup Patel 797c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 807c9aac00SAnup Patel } 817c9aac00SAnup Patel 827c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 837c9aac00SAnup Patel } 847c9aac00SAnup Patel 857c9aac00SAnup Patel static int setup_fdt(struct kvm *kvm) 867c9aac00SAnup Patel { 877c9aac00SAnup Patel struct device_header *dev_hdr; 887c9aac00SAnup Patel u8 staging_fdt[FDT_MAX_SIZE]; 897c9aac00SAnup Patel u64 mem_reg_prop[] = { 907c9aac00SAnup Patel cpu_to_fdt64(kvm->arch.memory_guest_start), 917c9aac00SAnup Patel cpu_to_fdt64(kvm->ram_size), 927c9aac00SAnup Patel }; 937c9aac00SAnup Patel void *fdt = staging_fdt; 947c9aac00SAnup Patel void *fdt_dest = guest_flat_to_host(kvm, 957c9aac00SAnup Patel kvm->arch.dtb_guest_start); 967c9aac00SAnup Patel void (*generate_mmio_fdt_nodes)(void *, struct device_header *, 977c9aac00SAnup Patel void (*)(void *, u8, enum irq_type)); 987c9aac00SAnup Patel 997c9aac00SAnup Patel /* Create new tree without a reserve map */ 1007c9aac00SAnup Patel _FDT(fdt_create(fdt, FDT_MAX_SIZE)); 1017c9aac00SAnup Patel _FDT(fdt_finish_reservemap(fdt)); 1027c9aac00SAnup Patel 1037c9aac00SAnup Patel /* Header */ 1047c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "")); 1057c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt")); 1067c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#address-cells", 0x2)); 1077c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#size-cells", 0x2)); 1087c9aac00SAnup Patel 1097c9aac00SAnup Patel /* /chosen */ 1107c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "chosen")); 1117c9aac00SAnup Patel 1127c9aac00SAnup Patel /* Pass on our amended command line to a Linux kernel only. */ 1137c9aac00SAnup Patel if (kvm->cfg.firmware_filename) { 1147c9aac00SAnup Patel if (kvm->cfg.kernel_cmdline) 1157c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "bootargs", 1167c9aac00SAnup Patel kvm->cfg.kernel_cmdline)); 1177c9aac00SAnup Patel } else 1187c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "bootargs", 1197c9aac00SAnup Patel kvm->cfg.real_cmdline)); 1207c9aac00SAnup Patel 1217c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "stdout-path", "serial0")); 1227c9aac00SAnup Patel 1237c9aac00SAnup Patel /* Initrd */ 1247c9aac00SAnup Patel if (kvm->arch.initrd_size != 0) { 1257c9aac00SAnup Patel u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start); 1267c9aac00SAnup Patel u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start + 1277c9aac00SAnup Patel kvm->arch.initrd_size); 1287c9aac00SAnup Patel 1297c9aac00SAnup Patel _FDT(fdt_property(fdt, "linux,initrd-start", 1307c9aac00SAnup Patel &ird_st_prop, sizeof(ird_st_prop))); 1317c9aac00SAnup Patel _FDT(fdt_property(fdt, "linux,initrd-end", 1327c9aac00SAnup Patel &ird_end_prop, sizeof(ird_end_prop))); 1337c9aac00SAnup Patel } 1347c9aac00SAnup Patel 1357c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 1367c9aac00SAnup Patel 1377c9aac00SAnup Patel /* Memory */ 1387c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "memory")); 1397c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "device_type", "memory")); 1407c9aac00SAnup Patel _FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop))); 1417c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 1427c9aac00SAnup Patel 1437c9aac00SAnup Patel /* CPUs */ 1447c9aac00SAnup Patel generate_cpu_nodes(fdt, kvm); 1457c9aac00SAnup Patel 1467c9aac00SAnup Patel /* Simple Bus */ 1477c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "smb")); 1487c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "simple-bus")); 1497c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#address-cells", 0x2)); 1507c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#size-cells", 0x2)); 1517c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_PLIC)); 1527c9aac00SAnup Patel _FDT(fdt_property(fdt, "ranges", NULL, 0)); 1537c9aac00SAnup Patel 1547c9aac00SAnup Patel /* Virtio MMIO devices */ 1557c9aac00SAnup Patel dev_hdr = device__first_dev(DEVICE_BUS_MMIO); 1567c9aac00SAnup Patel while (dev_hdr) { 1577c9aac00SAnup Patel generate_mmio_fdt_nodes = dev_hdr->data; 1587c9aac00SAnup Patel generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop); 1597c9aac00SAnup Patel dev_hdr = device__next_dev(dev_hdr); 1607c9aac00SAnup Patel } 1617c9aac00SAnup Patel 1627c9aac00SAnup Patel /* IOPORT devices */ 1637c9aac00SAnup Patel dev_hdr = device__first_dev(DEVICE_BUS_IOPORT); 1647c9aac00SAnup Patel while (dev_hdr) { 1657c9aac00SAnup Patel generate_mmio_fdt_nodes = dev_hdr->data; 1667c9aac00SAnup Patel generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop); 1677c9aac00SAnup Patel dev_hdr = device__next_dev(dev_hdr); 1687c9aac00SAnup Patel } 1697c9aac00SAnup Patel 170*cdd7d8ccSAnup Patel /* PCI host controller */ 171*cdd7d8ccSAnup Patel pci__generate_fdt_nodes(fdt); 172*cdd7d8ccSAnup Patel 1737c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 1747c9aac00SAnup Patel 1757c9aac00SAnup Patel if (fdt_stdout_path) { 1767c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "aliases")); 1777c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path)); 1787c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 1797c9aac00SAnup Patel 1807c9aac00SAnup Patel free(fdt_stdout_path); 1817c9aac00SAnup Patel fdt_stdout_path = NULL; 1827c9aac00SAnup Patel } 1837c9aac00SAnup Patel 1847c9aac00SAnup Patel /* Finalise. */ 1857c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 1867c9aac00SAnup Patel _FDT(fdt_finish(fdt)); 1877c9aac00SAnup Patel 1887c9aac00SAnup Patel _FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE)); 1897c9aac00SAnup Patel _FDT(fdt_pack(fdt_dest)); 1907c9aac00SAnup Patel 1917c9aac00SAnup Patel if (kvm->cfg.arch.dump_dtb_filename) 1927c9aac00SAnup Patel dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest); 1937c9aac00SAnup Patel return 0; 1947c9aac00SAnup Patel } 1957c9aac00SAnup Patel late_init(setup_fdt); 196