17c0e8b0cSWill Deacon #include "kvm/devices.h" 27c0e8b0cSWill Deacon #include "kvm/fdt.h" 37c0e8b0cSWill Deacon #include "kvm/kvm.h" 47c0e8b0cSWill Deacon #include "kvm/kvm-cpu.h" 57c0e8b0cSWill Deacon #include "kvm/virtio-mmio.h" 67c0e8b0cSWill Deacon 77c0e8b0cSWill Deacon #include "arm-common/gic.h" 816242d27SWill Deacon #include "arm-common/pci.h" 97c0e8b0cSWill Deacon 107c0e8b0cSWill Deacon #include <stdbool.h> 117c0e8b0cSWill Deacon 127c0e8b0cSWill Deacon #include <linux/byteorder.h> 137c0e8b0cSWill Deacon #include <linux/kernel.h> 147c0e8b0cSWill Deacon #include <linux/sizes.h> 15c2dad402SAnup Patel #include <linux/psci.h> 167c0e8b0cSWill Deacon 17ba27ff46SWill Deacon static void dump_fdt(const char *dtb_file, void *fdt) 187c0e8b0cSWill Deacon { 197c0e8b0cSWill Deacon int count, fd; 207c0e8b0cSWill Deacon 21ba27ff46SWill Deacon fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666); 227c0e8b0cSWill Deacon if (fd < 0) 23ba27ff46SWill Deacon die("Failed to write dtb to %s", dtb_file); 247c0e8b0cSWill Deacon 257c0e8b0cSWill Deacon count = write(fd, fdt, FDT_MAX_SIZE); 267c0e8b0cSWill Deacon if (count < 0) 277c0e8b0cSWill Deacon die_perror("Failed to dump dtb"); 287c0e8b0cSWill Deacon 29e1c7c62aSAndre Przywara pr_debug("Wrote %d bytes to dtb %s", count, dtb_file); 307c0e8b0cSWill Deacon close(fd); 317c0e8b0cSWill Deacon } 327c0e8b0cSWill Deacon 33cc874a22SWill Deacon #define CPU_NAME_MAX_LEN 15 34beff7ae0SMarc Zyngier static void generate_cpu_nodes(void *fdt, struct kvm *kvm) 35beff7ae0SMarc Zyngier { 36beff7ae0SMarc Zyngier int cpu; 37beff7ae0SMarc Zyngier 38beff7ae0SMarc Zyngier _FDT(fdt_begin_node(fdt, "cpus")); 39beff7ae0SMarc Zyngier _FDT(fdt_property_cell(fdt, "#address-cells", 0x1)); 40beff7ae0SMarc Zyngier _FDT(fdt_property_cell(fdt, "#size-cells", 0x0)); 41beff7ae0SMarc Zyngier 42beff7ae0SMarc Zyngier for (cpu = 0; cpu < kvm->nrcpus; ++cpu) { 43beff7ae0SMarc Zyngier char cpu_name[CPU_NAME_MAX_LEN]; 44d06bc640SMarc Zyngier struct kvm_cpu *vcpu = kvm->cpus[cpu]; 45d06bc640SMarc Zyngier unsigned long mpidr = kvm_cpu__get_vcpu_mpidr(vcpu); 46beff7ae0SMarc Zyngier 47d06bc640SMarc Zyngier mpidr &= ARM_MPIDR_HWID_BITMASK; 48d06bc640SMarc Zyngier snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%lx", mpidr); 49beff7ae0SMarc Zyngier 50beff7ae0SMarc Zyngier _FDT(fdt_begin_node(fdt, cpu_name)); 51beff7ae0SMarc Zyngier _FDT(fdt_property_string(fdt, "device_type", "cpu")); 52d06bc640SMarc Zyngier _FDT(fdt_property_string(fdt, "compatible", vcpu->cpu_compatible)); 53beff7ae0SMarc Zyngier 54beff7ae0SMarc Zyngier if (kvm->nrcpus > 1) 55beff7ae0SMarc Zyngier _FDT(fdt_property_string(fdt, "enable-method", "psci")); 56beff7ae0SMarc Zyngier 57d06bc640SMarc Zyngier _FDT(fdt_property_cell(fdt, "reg", mpidr)); 58beff7ae0SMarc Zyngier _FDT(fdt_end_node(fdt)); 59beff7ae0SMarc Zyngier } 60beff7ae0SMarc Zyngier 61beff7ae0SMarc Zyngier _FDT(fdt_end_node(fdt)); 62beff7ae0SMarc Zyngier } 63beff7ae0SMarc Zyngier 642bfd9ac3SAndre Przywara static void generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type) 657c0e8b0cSWill Deacon { 667c0e8b0cSWill Deacon u32 irq_prop[] = { 677c0e8b0cSWill Deacon cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI), 682454c7dcSWill Deacon cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE), 692bfd9ac3SAndre Przywara cpu_to_fdt32(irq_type) 707c0e8b0cSWill Deacon }; 717c0e8b0cSWill Deacon 727c0e8b0cSWill Deacon _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop))); 737c0e8b0cSWill Deacon } 747c0e8b0cSWill Deacon 75c2dad402SAnup Patel struct psci_fns { 76c2dad402SAnup Patel u32 cpu_suspend; 77c2dad402SAnup Patel u32 cpu_off; 78c2dad402SAnup Patel u32 cpu_on; 79c2dad402SAnup Patel u32 migrate; 80c2dad402SAnup Patel }; 81c2dad402SAnup Patel 82c2dad402SAnup Patel static struct psci_fns psci_0_1_fns = { 83c2dad402SAnup Patel .cpu_suspend = KVM_PSCI_FN_CPU_SUSPEND, 84c2dad402SAnup Patel .cpu_off = KVM_PSCI_FN_CPU_OFF, 85c2dad402SAnup Patel .cpu_on = KVM_PSCI_FN_CPU_ON, 86c2dad402SAnup Patel .migrate = KVM_PSCI_FN_MIGRATE, 87c2dad402SAnup Patel }; 88c2dad402SAnup Patel 89c2dad402SAnup Patel static struct psci_fns psci_0_2_aarch32_fns = { 90c2dad402SAnup Patel .cpu_suspend = PSCI_0_2_FN_CPU_SUSPEND, 91c2dad402SAnup Patel .cpu_off = PSCI_0_2_FN_CPU_OFF, 92c2dad402SAnup Patel .cpu_on = PSCI_0_2_FN_CPU_ON, 93c2dad402SAnup Patel .migrate = PSCI_0_2_FN_MIGRATE, 94c2dad402SAnup Patel }; 95c2dad402SAnup Patel 96c2dad402SAnup Patel static struct psci_fns psci_0_2_aarch64_fns = { 97c2dad402SAnup Patel .cpu_suspend = PSCI_0_2_FN64_CPU_SUSPEND, 98c2dad402SAnup Patel .cpu_off = PSCI_0_2_FN_CPU_OFF, 99c2dad402SAnup Patel .cpu_on = PSCI_0_2_FN64_CPU_ON, 100c2dad402SAnup Patel .migrate = PSCI_0_2_FN64_MIGRATE, 101c2dad402SAnup Patel }; 102c2dad402SAnup Patel 1037c0e8b0cSWill Deacon static int setup_fdt(struct kvm *kvm) 1047c0e8b0cSWill Deacon { 1057c0e8b0cSWill Deacon struct device_header *dev_hdr; 1067c0e8b0cSWill Deacon u8 staging_fdt[FDT_MAX_SIZE]; 1077c0e8b0cSWill Deacon u64 mem_reg_prop[] = { 1087c0e8b0cSWill Deacon cpu_to_fdt64(kvm->arch.memory_guest_start), 1097c0e8b0cSWill Deacon cpu_to_fdt64(kvm->ram_size), 1107c0e8b0cSWill Deacon }; 111c2dad402SAnup Patel struct psci_fns *fns; 1127c0e8b0cSWill Deacon void *fdt = staging_fdt; 1137c0e8b0cSWill Deacon void *fdt_dest = guest_flat_to_host(kvm, 1147c0e8b0cSWill Deacon kvm->arch.dtb_guest_start); 1152454c7dcSWill Deacon void (*generate_mmio_fdt_nodes)(void *, struct device_header *, 1162bfd9ac3SAndre Przywara void (*)(void *, u8, enum irq_type)); 1170063d50cSAndre Przywara void (*generate_cpu_peripheral_fdt_nodes)(void *, struct kvm *) 1187c0e8b0cSWill Deacon = kvm->cpus[0]->generate_fdt_nodes; 1197c0e8b0cSWill Deacon 1207c0e8b0cSWill Deacon /* Create new tree without a reserve map */ 1217c0e8b0cSWill Deacon _FDT(fdt_create(fdt, FDT_MAX_SIZE)); 1227c0e8b0cSWill Deacon _FDT(fdt_finish_reservemap(fdt)); 1237c0e8b0cSWill Deacon 1247c0e8b0cSWill Deacon /* Header */ 1257c0e8b0cSWill Deacon _FDT(fdt_begin_node(fdt, "")); 126cd9a8066SAndre Przywara _FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC)); 1277c0e8b0cSWill Deacon _FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt")); 1287c0e8b0cSWill Deacon _FDT(fdt_property_cell(fdt, "#address-cells", 0x2)); 1297c0e8b0cSWill Deacon _FDT(fdt_property_cell(fdt, "#size-cells", 0x2)); 1307c0e8b0cSWill Deacon 1317c0e8b0cSWill Deacon /* /chosen */ 1327c0e8b0cSWill Deacon _FDT(fdt_begin_node(fdt, "chosen")); 1339afefd6cSJulien Thierry 1345e4b563dSAndre Przywara /* Pass on our amended command line to a Linux kernel only. */ 1359afefd6cSJulien Thierry if (kvm->cfg.firmware_filename) { 1369afefd6cSJulien Thierry if (kvm->cfg.kernel_cmdline) 1375e4b563dSAndre Przywara _FDT(fdt_property_string(fdt, "bootargs", 1385e4b563dSAndre Przywara kvm->cfg.kernel_cmdline)); 1399afefd6cSJulien Thierry } else 1409afefd6cSJulien Thierry _FDT(fdt_property_string(fdt, "bootargs", 1419afefd6cSJulien Thierry kvm->cfg.real_cmdline)); 1429afefd6cSJulien Thierry 14384d77496SWill Deacon _FDT(fdt_property_u64(fdt, "kaslr-seed", kvm->cfg.arch.kaslr_seed)); 14456e45ea4SAndre Przywara _FDT(fdt_property_string(fdt, "stdout-path", "serial0")); 1457c0e8b0cSWill Deacon 1467c0e8b0cSWill Deacon /* Initrd */ 1477c0e8b0cSWill Deacon if (kvm->arch.initrd_size != 0) { 148bd33695dSAndre Przywara u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start); 149bd33695dSAndre Przywara u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start + 1507c0e8b0cSWill Deacon kvm->arch.initrd_size); 1517c0e8b0cSWill Deacon 1527c0e8b0cSWill Deacon _FDT(fdt_property(fdt, "linux,initrd-start", 1537c0e8b0cSWill Deacon &ird_st_prop, sizeof(ird_st_prop))); 1547c0e8b0cSWill Deacon _FDT(fdt_property(fdt, "linux,initrd-end", 1557c0e8b0cSWill Deacon &ird_end_prop, sizeof(ird_end_prop))); 1567c0e8b0cSWill Deacon } 1577c0e8b0cSWill Deacon _FDT(fdt_end_node(fdt)); 1587c0e8b0cSWill Deacon 1597c0e8b0cSWill Deacon /* Memory */ 1607c0e8b0cSWill Deacon _FDT(fdt_begin_node(fdt, "memory")); 1617c0e8b0cSWill Deacon _FDT(fdt_property_string(fdt, "device_type", "memory")); 1627c0e8b0cSWill Deacon _FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop))); 1637c0e8b0cSWill Deacon _FDT(fdt_end_node(fdt)); 1647c0e8b0cSWill Deacon 1657c0e8b0cSWill Deacon /* CPU and peripherals (interrupt controller, timers, etc) */ 166beff7ae0SMarc Zyngier generate_cpu_nodes(fdt, kvm); 1672454c7dcSWill Deacon if (generate_cpu_peripheral_fdt_nodes) 1680063d50cSAndre Przywara generate_cpu_peripheral_fdt_nodes(fdt, kvm); 1697c0e8b0cSWill Deacon 1707c0e8b0cSWill Deacon /* Virtio MMIO devices */ 1717c0e8b0cSWill Deacon dev_hdr = device__first_dev(DEVICE_BUS_MMIO); 1727c0e8b0cSWill Deacon while (dev_hdr) { 1732454c7dcSWill Deacon generate_mmio_fdt_nodes = dev_hdr->data; 174*6b74f68fSAlexandru Elisei if (generate_mmio_fdt_nodes) { 1752454c7dcSWill Deacon generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop); 176*6b74f68fSAlexandru Elisei } else { 177*6b74f68fSAlexandru Elisei pr_debug("Missing FDT node generator for MMIO device %d", 178*6b74f68fSAlexandru Elisei dev_hdr->dev_num); 179*6b74f68fSAlexandru Elisei } 1807c0e8b0cSWill Deacon dev_hdr = device__next_dev(dev_hdr); 1817c0e8b0cSWill Deacon } 1827c0e8b0cSWill Deacon 183ed7b31c9SWill Deacon /* IOPORT devices (!) */ 184ed7b31c9SWill Deacon dev_hdr = device__first_dev(DEVICE_BUS_IOPORT); 185ed7b31c9SWill Deacon while (dev_hdr) { 186ed7b31c9SWill Deacon generate_mmio_fdt_nodes = dev_hdr->data; 187ed7b31c9SWill Deacon generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop); 188ed7b31c9SWill Deacon dev_hdr = device__next_dev(dev_hdr); 189ed7b31c9SWill Deacon } 190ed7b31c9SWill Deacon 19116242d27SWill Deacon /* PCI host controller */ 1920063d50cSAndre Przywara pci__generate_fdt_nodes(fdt); 19316242d27SWill Deacon 19461076240SWill Deacon /* PSCI firmware */ 19561076240SWill Deacon _FDT(fdt_begin_node(fdt, "psci")); 196c2dad402SAnup Patel if (kvm__supports_extension(kvm, KVM_CAP_ARM_PSCI_0_2)) { 197c2dad402SAnup Patel const char compatible[] = "arm,psci-0.2\0arm,psci"; 198c2dad402SAnup Patel _FDT(fdt_property(fdt, "compatible", 199c2dad402SAnup Patel compatible, sizeof(compatible))); 200c2dad402SAnup Patel if (kvm->cfg.arch.aarch32_guest) 201c2dad402SAnup Patel fns = &psci_0_2_aarch32_fns; 202c2dad402SAnup Patel else 203c2dad402SAnup Patel fns = &psci_0_2_aarch64_fns; 204c2dad402SAnup Patel } else { 20561076240SWill Deacon _FDT(fdt_property_string(fdt, "compatible", "arm,psci")); 206c2dad402SAnup Patel fns = &psci_0_1_fns; 207c2dad402SAnup Patel } 20861076240SWill Deacon _FDT(fdt_property_string(fdt, "method", "hvc")); 209c2dad402SAnup Patel _FDT(fdt_property_cell(fdt, "cpu_suspend", fns->cpu_suspend)); 210c2dad402SAnup Patel _FDT(fdt_property_cell(fdt, "cpu_off", fns->cpu_off)); 211c2dad402SAnup Patel _FDT(fdt_property_cell(fdt, "cpu_on", fns->cpu_on)); 212c2dad402SAnup Patel _FDT(fdt_property_cell(fdt, "migrate", fns->migrate)); 21361076240SWill Deacon _FDT(fdt_end_node(fdt)); 21461076240SWill Deacon 21556e45ea4SAndre Przywara if (fdt_stdout_path) { 21656e45ea4SAndre Przywara _FDT(fdt_begin_node(fdt, "aliases")); 21756e45ea4SAndre Przywara _FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path)); 21856e45ea4SAndre Przywara _FDT(fdt_end_node(fdt)); 21956e45ea4SAndre Przywara 22056e45ea4SAndre Przywara free(fdt_stdout_path); 22156e45ea4SAndre Przywara fdt_stdout_path = NULL; 22256e45ea4SAndre Przywara } 22356e45ea4SAndre Przywara 2247c0e8b0cSWill Deacon /* Finalise. */ 2257c0e8b0cSWill Deacon _FDT(fdt_end_node(fdt)); 2267c0e8b0cSWill Deacon _FDT(fdt_finish(fdt)); 2277c0e8b0cSWill Deacon 2287c0e8b0cSWill Deacon _FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE)); 2297c0e8b0cSWill Deacon _FDT(fdt_pack(fdt_dest)); 2307c0e8b0cSWill Deacon 231ba27ff46SWill Deacon if (kvm->cfg.arch.dump_dtb_filename) 232ba27ff46SWill Deacon dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest); 2337c0e8b0cSWill Deacon return 0; 2347c0e8b0cSWill Deacon } 2357c0e8b0cSWill Deacon late_init(setup_fdt); 236