aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnup Patel <anup.patel@wdc.com>2021-11-19 18:15:13 +0530
committerWill Deacon <will@kernel.org>2021-12-14 15:05:30 +0000
commit7c9aac003925e52121b34c48cf81564c4077b26c (patch)
tree8c66b3e33ed461e88b3d957cb2c4e91d2c083a15
parent762224e47cc2f8f5192d014caee3315ad4129fc9 (diff)
downloadkvmtool-7c9aac003925e52121b34c48cf81564c4077b26c.tar.gz
riscv: Generate FDT at runtime for Guest/VM
We generate FDT at runtime for RISC-V Guest/VM so that KVMTOOL users don't have to pass FDT separately via command-line parameters. Also, we provide "--dump-dtb <filename>" command-line option to dump generated FDT into a file for debugging purpose. Signed-off-by: Atish Patra <atish.patra@wdc.com> Signed-off-by: Anup Patel <anup.patel@wdc.com> Link: https://lore.kernel.org/r/20211119124515.89439-7-anup.patel@wdc.com Signed-off-by: Will Deacon <will@kernel.org>
-rw-r--r--Makefile1
-rw-r--r--riscv/fdt.c192
-rw-r--r--riscv/include/kvm/fdt-arch.h4
-rw-r--r--riscv/include/kvm/kvm-arch.h2
-rw-r--r--riscv/include/kvm/kvm-config-arch.h6
-rw-r--r--riscv/plic.c50
6 files changed, 255 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 0d7c7ec8..31778532 100644
--- a/Makefile
+++ b/Makefile
@@ -200,6 +200,7 @@ endif
ifeq ($(ARCH),riscv)
DEFINES += -DCONFIG_RISCV
ARCH_INCLUDE := riscv/include
+ OBJS += riscv/fdt.o
OBJS += riscv/ioport.o
OBJS += riscv/irq.o
OBJS += riscv/kvm.o
diff --git a/riscv/fdt.c b/riscv/fdt.c
new file mode 100644
index 00000000..6527ef77
--- /dev/null
+++ b/riscv/fdt.c
@@ -0,0 +1,192 @@
+#include "kvm/devices.h"
+#include "kvm/fdt.h"
+#include "kvm/kvm.h"
+#include "kvm/kvm-cpu.h"
+
+#include <stdbool.h>
+
+#include <linux/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+
+static void dump_fdt(const char *dtb_file, void *fdt)
+{
+ int count, fd;
+
+ fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666);
+ if (fd < 0)
+ die("Failed to write dtb to %s", dtb_file);
+
+ count = write(fd, fdt, FDT_MAX_SIZE);
+ if (count < 0)
+ die_perror("Failed to dump dtb");
+
+ pr_debug("Wrote %d bytes to dtb %s", count, dtb_file);
+ close(fd);
+}
+
+#define CPU_NAME_MAX_LEN 15
+#define CPU_ISA_MAX_LEN 128
+static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
+{
+ int cpu, pos, i, index, valid_isa_len;
+ const char *valid_isa_order = "IEMAFDQCLBJTPVNSUHKORWXYZG";
+
+ _FDT(fdt_begin_node(fdt, "cpus"));
+ _FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
+ _FDT(fdt_property_cell(fdt, "#size-cells", 0x0));
+ _FDT(fdt_property_cell(fdt, "timebase-frequency",
+ kvm->cpus[0]->riscv_timebase));
+
+ for (cpu = 0; cpu < kvm->nrcpus; ++cpu) {
+ char cpu_name[CPU_NAME_MAX_LEN];
+ char cpu_isa[CPU_ISA_MAX_LEN];
+ struct kvm_cpu *vcpu = kvm->cpus[cpu];
+
+ snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%x", cpu);
+
+ snprintf(cpu_isa, CPU_ISA_MAX_LEN, "rv%ld", vcpu->riscv_xlen);
+ pos = strlen(cpu_isa);
+ valid_isa_len = strlen(valid_isa_order);
+ for (i = 0; i < valid_isa_len; i++) {
+ index = valid_isa_order[i] - 'A';
+ if (vcpu->riscv_isa & (1 << (index)))
+ cpu_isa[pos++] = 'a' + index;
+ }
+ cpu_isa[pos] = '\0';
+
+ _FDT(fdt_begin_node(fdt, cpu_name));
+ _FDT(fdt_property_string(fdt, "device_type", "cpu"));
+ _FDT(fdt_property_string(fdt, "compatible", "riscv"));
+ if (vcpu->riscv_xlen == 64)
+ _FDT(fdt_property_string(fdt, "mmu-type",
+ "riscv,sv48"));
+ else
+ _FDT(fdt_property_string(fdt, "mmu-type",
+ "riscv,sv32"));
+ _FDT(fdt_property_string(fdt, "riscv,isa", cpu_isa));
+ _FDT(fdt_property_cell(fdt, "reg", cpu));
+ _FDT(fdt_property_string(fdt, "status", "okay"));
+
+ _FDT(fdt_begin_node(fdt, "interrupt-controller"));
+ _FDT(fdt_property_string(fdt, "compatible", "riscv,cpu-intc"));
+ _FDT(fdt_property_cell(fdt, "#interrupt-cells", 1));
+ _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
+ _FDT(fdt_property_cell(fdt, "phandle",
+ PHANDLE_CPU_INTC_BASE + cpu));
+ _FDT(fdt_end_node(fdt));
+
+ _FDT(fdt_end_node(fdt));
+ }
+
+ _FDT(fdt_end_node(fdt));
+}
+
+static int setup_fdt(struct kvm *kvm)
+{
+ struct device_header *dev_hdr;
+ u8 staging_fdt[FDT_MAX_SIZE];
+ u64 mem_reg_prop[] = {
+ cpu_to_fdt64(kvm->arch.memory_guest_start),
+ cpu_to_fdt64(kvm->ram_size),
+ };
+ void *fdt = staging_fdt;
+ void *fdt_dest = guest_flat_to_host(kvm,
+ kvm->arch.dtb_guest_start);
+ void (*generate_mmio_fdt_nodes)(void *, struct device_header *,
+ void (*)(void *, u8, enum irq_type));
+
+ /* Create new tree without a reserve map */
+ _FDT(fdt_create(fdt, FDT_MAX_SIZE));
+ _FDT(fdt_finish_reservemap(fdt));
+
+ /* Header */
+ _FDT(fdt_begin_node(fdt, ""));
+ _FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt"));
+ _FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
+ _FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
+
+ /* /chosen */
+ _FDT(fdt_begin_node(fdt, "chosen"));
+
+ /* Pass on our amended command line to a Linux kernel only. */
+ if (kvm->cfg.firmware_filename) {
+ if (kvm->cfg.kernel_cmdline)
+ _FDT(fdt_property_string(fdt, "bootargs",
+ kvm->cfg.kernel_cmdline));
+ } else
+ _FDT(fdt_property_string(fdt, "bootargs",
+ kvm->cfg.real_cmdline));
+
+ _FDT(fdt_property_string(fdt, "stdout-path", "serial0"));
+
+ /* Initrd */
+ if (kvm->arch.initrd_size != 0) {
+ u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
+ u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
+ kvm->arch.initrd_size);
+
+ _FDT(fdt_property(fdt, "linux,initrd-start",
+ &ird_st_prop, sizeof(ird_st_prop)));
+ _FDT(fdt_property(fdt, "linux,initrd-end",
+ &ird_end_prop, sizeof(ird_end_prop)));
+ }
+
+ _FDT(fdt_end_node(fdt));
+
+ /* Memory */
+ _FDT(fdt_begin_node(fdt, "memory"));
+ _FDT(fdt_property_string(fdt, "device_type", "memory"));
+ _FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
+ _FDT(fdt_end_node(fdt));
+
+ /* CPUs */
+ generate_cpu_nodes(fdt, kvm);
+
+ /* Simple Bus */
+ _FDT(fdt_begin_node(fdt, "smb"));
+ _FDT(fdt_property_string(fdt, "compatible", "simple-bus"));
+ _FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
+ _FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
+ _FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_PLIC));
+ _FDT(fdt_property(fdt, "ranges", NULL, 0));
+
+ /* Virtio MMIO devices */
+ dev_hdr = device__first_dev(DEVICE_BUS_MMIO);
+ while (dev_hdr) {
+ generate_mmio_fdt_nodes = dev_hdr->data;
+ generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop);
+ dev_hdr = device__next_dev(dev_hdr);
+ }
+
+ /* IOPORT devices */
+ dev_hdr = device__first_dev(DEVICE_BUS_IOPORT);
+ while (dev_hdr) {
+ generate_mmio_fdt_nodes = dev_hdr->data;
+ generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop);
+ dev_hdr = device__next_dev(dev_hdr);
+ }
+
+ _FDT(fdt_end_node(fdt));
+
+ if (fdt_stdout_path) {
+ _FDT(fdt_begin_node(fdt, "aliases"));
+ _FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path));
+ _FDT(fdt_end_node(fdt));
+
+ free(fdt_stdout_path);
+ fdt_stdout_path = NULL;
+ }
+
+ /* Finalise. */
+ _FDT(fdt_end_node(fdt));
+ _FDT(fdt_finish(fdt));
+
+ _FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
+ _FDT(fdt_pack(fdt_dest));
+
+ if (kvm->cfg.arch.dump_dtb_filename)
+ dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest);
+ return 0;
+}
+late_init(setup_fdt);
diff --git a/riscv/include/kvm/fdt-arch.h b/riscv/include/kvm/fdt-arch.h
index 9450fc50..f7548e80 100644
--- a/riscv/include/kvm/fdt-arch.h
+++ b/riscv/include/kvm/fdt-arch.h
@@ -1,4 +1,8 @@
#ifndef KVM__KVM_FDT_H
#define KVM__KVM_FDT_H
+enum phandles {PHANDLE_RESERVED = 0, PHANDLE_PLIC, PHANDLES_MAX};
+
+#define PHANDLE_CPU_INTC_BASE PHANDLES_MAX
+
#endif /* KVM__KVM_FDT_H */
diff --git a/riscv/include/kvm/kvm-arch.h b/riscv/include/kvm/kvm-arch.h
index 25b80b3f..d0be9655 100644
--- a/riscv/include/kvm/kvm-arch.h
+++ b/riscv/include/kvm/kvm-arch.h
@@ -80,6 +80,8 @@ static inline bool riscv_addr_in_ioport_region(u64 phys_addr)
enum irq_type;
+void plic__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type);
+
void plic__irq_trig(struct kvm *kvm, int irq, int level, bool edge);
#endif /* KVM__KVM_ARCH_H */
diff --git a/riscv/include/kvm/kvm-config-arch.h b/riscv/include/kvm/kvm-config-arch.h
index 60c73336..526fca26 100644
--- a/riscv/include/kvm/kvm-config-arch.h
+++ b/riscv/include/kvm/kvm-config-arch.h
@@ -4,6 +4,12 @@
#include "kvm/parse-options.h"
struct kvm_config_arch {
+ const char *dump_dtb_filename;
};
+#define OPT_ARCH_RUN(pfx, cfg) \
+ pfx, \
+ OPT_STRING('\0', "dump-dtb", &(cfg)->dump_dtb_filename, \
+ ".dtb file", "Dump generated .dtb to specified file"),
+
#endif /* KVM__KVM_CONFIG_ARCH_H */
diff --git a/riscv/plic.c b/riscv/plic.c
index 6d64e8b5..62422863 100644
--- a/riscv/plic.c
+++ b/riscv/plic.c
@@ -1,5 +1,6 @@
#include "kvm/devices.h"
+#include "kvm/fdt.h"
#include "kvm/ioeventfd.h"
#include "kvm/ioport.h"
#include "kvm/kvm.h"
@@ -463,6 +464,54 @@ static void plic__mmio_callback(struct kvm_cpu *vcpu,
}
}
+void plic__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
+{
+ u32 irq_prop[] = {
+ cpu_to_fdt32(irq)
+ };
+
+ _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
+}
+
+static void plic__generate_fdt_node(void *fdt,
+ struct device_header *dev_hdr,
+ void (*generate_irq_prop)(void *fdt,
+ u8 irq,
+ enum irq_type))
+{
+ u32 i;
+ u32 reg_cells[4], *irq_cells;
+
+ reg_cells[0] = 0;
+ reg_cells[1] = cpu_to_fdt32(RISCV_PLIC);
+ reg_cells[2] = 0;
+ reg_cells[3] = cpu_to_fdt32(RISCV_PLIC_SIZE);
+
+ irq_cells = calloc(plic.num_context * 2, sizeof(u32));
+ if (!irq_cells)
+ die("Failed to alloc irq_cells");
+
+ _FDT(fdt_begin_node(fdt, "interrupt-controller@0c000000"));
+ _FDT(fdt_property_string(fdt, "compatible", "riscv,plic0"));
+ _FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
+ _FDT(fdt_property_cell(fdt, "#interrupt-cells", 1));
+ _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
+ _FDT(fdt_property_cell(fdt, "riscv,max-priority", plic.max_prio));
+ _FDT(fdt_property_cell(fdt, "riscv,ndev", MAX_DEVICES - 1));
+ _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_PLIC));
+ for (i = 0; i < (plic.num_context / 2); i++) {
+ irq_cells[4*i + 0] = cpu_to_fdt32(PHANDLE_CPU_INTC_BASE + i);
+ irq_cells[4*i + 1] = cpu_to_fdt32(0xffffffff);
+ irq_cells[4*i + 2] = cpu_to_fdt32(PHANDLE_CPU_INTC_BASE + i);
+ irq_cells[4*i + 3] = cpu_to_fdt32(9);
+ }
+ _FDT(fdt_property(fdt, "interrupts-extended", irq_cells,
+ sizeof(u32) * plic.num_context * 2));
+ _FDT(fdt_end_node(fdt));
+
+ free(irq_cells);
+}
+
static int plic__init(struct kvm *kvm)
{
u32 i;
@@ -472,6 +521,7 @@ static int plic__init(struct kvm *kvm)
plic.kvm = kvm;
plic.dev_hdr = (struct device_header) {
.bus_type = DEVICE_BUS_MMIO,
+ .data = plic__generate_fdt_node,
};
plic.num_irq = MAX_DEVICES;