1*7c9aac00SAnup Patel #include "kvm/devices.h" 2*7c9aac00SAnup Patel #include "kvm/fdt.h" 3*7c9aac00SAnup Patel #include "kvm/kvm.h" 4*7c9aac00SAnup Patel #include "kvm/kvm-cpu.h" 5*7c9aac00SAnup Patel 6*7c9aac00SAnup Patel #include <stdbool.h> 7*7c9aac00SAnup Patel 8*7c9aac00SAnup Patel #include <linux/byteorder.h> 9*7c9aac00SAnup Patel #include <linux/kernel.h> 10*7c9aac00SAnup Patel #include <linux/sizes.h> 11*7c9aac00SAnup Patel 12*7c9aac00SAnup Patel static void dump_fdt(const char *dtb_file, void *fdt) 13*7c9aac00SAnup Patel { 14*7c9aac00SAnup Patel int count, fd; 15*7c9aac00SAnup Patel 16*7c9aac00SAnup Patel fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666); 17*7c9aac00SAnup Patel if (fd < 0) 18*7c9aac00SAnup Patel die("Failed to write dtb to %s", dtb_file); 19*7c9aac00SAnup Patel 20*7c9aac00SAnup Patel count = write(fd, fdt, FDT_MAX_SIZE); 21*7c9aac00SAnup Patel if (count < 0) 22*7c9aac00SAnup Patel die_perror("Failed to dump dtb"); 23*7c9aac00SAnup Patel 24*7c9aac00SAnup Patel pr_debug("Wrote %d bytes to dtb %s", count, dtb_file); 25*7c9aac00SAnup Patel close(fd); 26*7c9aac00SAnup Patel } 27*7c9aac00SAnup Patel 28*7c9aac00SAnup Patel #define CPU_NAME_MAX_LEN 15 29*7c9aac00SAnup Patel #define CPU_ISA_MAX_LEN 128 30*7c9aac00SAnup Patel static void generate_cpu_nodes(void *fdt, struct kvm *kvm) 31*7c9aac00SAnup Patel { 32*7c9aac00SAnup Patel int cpu, pos, i, index, valid_isa_len; 33*7c9aac00SAnup Patel const char *valid_isa_order = "IEMAFDQCLBJTPVNSUHKORWXYZG"; 34*7c9aac00SAnup Patel 35*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "cpus")); 36*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#address-cells", 0x1)); 37*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#size-cells", 0x0)); 38*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "timebase-frequency", 39*7c9aac00SAnup Patel kvm->cpus[0]->riscv_timebase)); 40*7c9aac00SAnup Patel 41*7c9aac00SAnup Patel for (cpu = 0; cpu < kvm->nrcpus; ++cpu) { 42*7c9aac00SAnup Patel char cpu_name[CPU_NAME_MAX_LEN]; 43*7c9aac00SAnup Patel char cpu_isa[CPU_ISA_MAX_LEN]; 44*7c9aac00SAnup Patel struct kvm_cpu *vcpu = kvm->cpus[cpu]; 45*7c9aac00SAnup Patel 46*7c9aac00SAnup Patel snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%x", cpu); 47*7c9aac00SAnup Patel 48*7c9aac00SAnup Patel snprintf(cpu_isa, CPU_ISA_MAX_LEN, "rv%ld", vcpu->riscv_xlen); 49*7c9aac00SAnup Patel pos = strlen(cpu_isa); 50*7c9aac00SAnup Patel valid_isa_len = strlen(valid_isa_order); 51*7c9aac00SAnup Patel for (i = 0; i < valid_isa_len; i++) { 52*7c9aac00SAnup Patel index = valid_isa_order[i] - 'A'; 53*7c9aac00SAnup Patel if (vcpu->riscv_isa & (1 << (index))) 54*7c9aac00SAnup Patel cpu_isa[pos++] = 'a' + index; 55*7c9aac00SAnup Patel } 56*7c9aac00SAnup Patel cpu_isa[pos] = '\0'; 57*7c9aac00SAnup Patel 58*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, cpu_name)); 59*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "device_type", "cpu")); 60*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "riscv")); 61*7c9aac00SAnup Patel if (vcpu->riscv_xlen == 64) 62*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "mmu-type", 63*7c9aac00SAnup Patel "riscv,sv48")); 64*7c9aac00SAnup Patel else 65*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "mmu-type", 66*7c9aac00SAnup Patel "riscv,sv32")); 67*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "riscv,isa", cpu_isa)); 68*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "reg", cpu)); 69*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "status", "okay")); 70*7c9aac00SAnup Patel 71*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "interrupt-controller")); 72*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "riscv,cpu-intc")); 73*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#interrupt-cells", 1)); 74*7c9aac00SAnup Patel _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0)); 75*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "phandle", 76*7c9aac00SAnup Patel PHANDLE_CPU_INTC_BASE + cpu)); 77*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 78*7c9aac00SAnup Patel 79*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 80*7c9aac00SAnup Patel } 81*7c9aac00SAnup Patel 82*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 83*7c9aac00SAnup Patel } 84*7c9aac00SAnup Patel 85*7c9aac00SAnup Patel static int setup_fdt(struct kvm *kvm) 86*7c9aac00SAnup Patel { 87*7c9aac00SAnup Patel struct device_header *dev_hdr; 88*7c9aac00SAnup Patel u8 staging_fdt[FDT_MAX_SIZE]; 89*7c9aac00SAnup Patel u64 mem_reg_prop[] = { 90*7c9aac00SAnup Patel cpu_to_fdt64(kvm->arch.memory_guest_start), 91*7c9aac00SAnup Patel cpu_to_fdt64(kvm->ram_size), 92*7c9aac00SAnup Patel }; 93*7c9aac00SAnup Patel void *fdt = staging_fdt; 94*7c9aac00SAnup Patel void *fdt_dest = guest_flat_to_host(kvm, 95*7c9aac00SAnup Patel kvm->arch.dtb_guest_start); 96*7c9aac00SAnup Patel void (*generate_mmio_fdt_nodes)(void *, struct device_header *, 97*7c9aac00SAnup Patel void (*)(void *, u8, enum irq_type)); 98*7c9aac00SAnup Patel 99*7c9aac00SAnup Patel /* Create new tree without a reserve map */ 100*7c9aac00SAnup Patel _FDT(fdt_create(fdt, FDT_MAX_SIZE)); 101*7c9aac00SAnup Patel _FDT(fdt_finish_reservemap(fdt)); 102*7c9aac00SAnup Patel 103*7c9aac00SAnup Patel /* Header */ 104*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "")); 105*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt")); 106*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#address-cells", 0x2)); 107*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#size-cells", 0x2)); 108*7c9aac00SAnup Patel 109*7c9aac00SAnup Patel /* /chosen */ 110*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "chosen")); 111*7c9aac00SAnup Patel 112*7c9aac00SAnup Patel /* Pass on our amended command line to a Linux kernel only. */ 113*7c9aac00SAnup Patel if (kvm->cfg.firmware_filename) { 114*7c9aac00SAnup Patel if (kvm->cfg.kernel_cmdline) 115*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "bootargs", 116*7c9aac00SAnup Patel kvm->cfg.kernel_cmdline)); 117*7c9aac00SAnup Patel } else 118*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "bootargs", 119*7c9aac00SAnup Patel kvm->cfg.real_cmdline)); 120*7c9aac00SAnup Patel 121*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "stdout-path", "serial0")); 122*7c9aac00SAnup Patel 123*7c9aac00SAnup Patel /* Initrd */ 124*7c9aac00SAnup Patel if (kvm->arch.initrd_size != 0) { 125*7c9aac00SAnup Patel u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start); 126*7c9aac00SAnup Patel u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start + 127*7c9aac00SAnup Patel kvm->arch.initrd_size); 128*7c9aac00SAnup Patel 129*7c9aac00SAnup Patel _FDT(fdt_property(fdt, "linux,initrd-start", 130*7c9aac00SAnup Patel &ird_st_prop, sizeof(ird_st_prop))); 131*7c9aac00SAnup Patel _FDT(fdt_property(fdt, "linux,initrd-end", 132*7c9aac00SAnup Patel &ird_end_prop, sizeof(ird_end_prop))); 133*7c9aac00SAnup Patel } 134*7c9aac00SAnup Patel 135*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 136*7c9aac00SAnup Patel 137*7c9aac00SAnup Patel /* Memory */ 138*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "memory")); 139*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "device_type", "memory")); 140*7c9aac00SAnup Patel _FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop))); 141*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 142*7c9aac00SAnup Patel 143*7c9aac00SAnup Patel /* CPUs */ 144*7c9aac00SAnup Patel generate_cpu_nodes(fdt, kvm); 145*7c9aac00SAnup Patel 146*7c9aac00SAnup Patel /* Simple Bus */ 147*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "smb")); 148*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "compatible", "simple-bus")); 149*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#address-cells", 0x2)); 150*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "#size-cells", 0x2)); 151*7c9aac00SAnup Patel _FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_PLIC)); 152*7c9aac00SAnup Patel _FDT(fdt_property(fdt, "ranges", NULL, 0)); 153*7c9aac00SAnup Patel 154*7c9aac00SAnup Patel /* Virtio MMIO devices */ 155*7c9aac00SAnup Patel dev_hdr = device__first_dev(DEVICE_BUS_MMIO); 156*7c9aac00SAnup Patel while (dev_hdr) { 157*7c9aac00SAnup Patel generate_mmio_fdt_nodes = dev_hdr->data; 158*7c9aac00SAnup Patel generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop); 159*7c9aac00SAnup Patel dev_hdr = device__next_dev(dev_hdr); 160*7c9aac00SAnup Patel } 161*7c9aac00SAnup Patel 162*7c9aac00SAnup Patel /* IOPORT devices */ 163*7c9aac00SAnup Patel dev_hdr = device__first_dev(DEVICE_BUS_IOPORT); 164*7c9aac00SAnup Patel while (dev_hdr) { 165*7c9aac00SAnup Patel generate_mmio_fdt_nodes = dev_hdr->data; 166*7c9aac00SAnup Patel generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop); 167*7c9aac00SAnup Patel dev_hdr = device__next_dev(dev_hdr); 168*7c9aac00SAnup Patel } 169*7c9aac00SAnup Patel 170*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 171*7c9aac00SAnup Patel 172*7c9aac00SAnup Patel if (fdt_stdout_path) { 173*7c9aac00SAnup Patel _FDT(fdt_begin_node(fdt, "aliases")); 174*7c9aac00SAnup Patel _FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path)); 175*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 176*7c9aac00SAnup Patel 177*7c9aac00SAnup Patel free(fdt_stdout_path); 178*7c9aac00SAnup Patel fdt_stdout_path = NULL; 179*7c9aac00SAnup Patel } 180*7c9aac00SAnup Patel 181*7c9aac00SAnup Patel /* Finalise. */ 182*7c9aac00SAnup Patel _FDT(fdt_end_node(fdt)); 183*7c9aac00SAnup Patel _FDT(fdt_finish(fdt)); 184*7c9aac00SAnup Patel 185*7c9aac00SAnup Patel _FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE)); 186*7c9aac00SAnup Patel _FDT(fdt_pack(fdt_dest)); 187*7c9aac00SAnup Patel 188*7c9aac00SAnup Patel if (kvm->cfg.arch.dump_dtb_filename) 189*7c9aac00SAnup Patel dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest); 190*7c9aac00SAnup Patel return 0; 191*7c9aac00SAnup Patel } 192*7c9aac00SAnup Patel late_init(setup_fdt); 193