xref: /kvmtool/arm/gic.c (revision 69b9a17ac8a372f87c69ebf1cc6580963c07b441)
17c0e8b0cSWill Deacon #include "kvm/fdt.h"
27c0e8b0cSWill Deacon #include "kvm/kvm.h"
37c0e8b0cSWill Deacon #include "kvm/virtio.h"
47c0e8b0cSWill Deacon 
57c0e8b0cSWill Deacon #include "arm-common/gic.h"
67c0e8b0cSWill Deacon 
77c0e8b0cSWill Deacon #include <linux/byteorder.h>
87c0e8b0cSWill Deacon #include <linux/kvm.h>
97c0e8b0cSWill Deacon 
10*69b9a17aSMarc Zyngier static int gic_fd = -1;
11*69b9a17aSMarc Zyngier 
12*69b9a17aSMarc Zyngier static int gic__create_device(struct kvm *kvm)
13*69b9a17aSMarc Zyngier {
14*69b9a17aSMarc Zyngier 	int err;
15*69b9a17aSMarc Zyngier 	u64 cpu_if_addr = ARM_GIC_CPUI_BASE;
16*69b9a17aSMarc Zyngier 	u64 dist_addr = ARM_GIC_DIST_BASE;
17*69b9a17aSMarc Zyngier 	struct kvm_create_device gic_device = {
18*69b9a17aSMarc Zyngier 		.type	= KVM_DEV_TYPE_ARM_VGIC_V2,
19*69b9a17aSMarc Zyngier 	};
20*69b9a17aSMarc Zyngier 	struct kvm_device_attr cpu_if_attr = {
21*69b9a17aSMarc Zyngier 		.group	= KVM_DEV_ARM_VGIC_GRP_ADDR,
22*69b9a17aSMarc Zyngier 		.attr	= KVM_VGIC_V2_ADDR_TYPE_CPU,
23*69b9a17aSMarc Zyngier 		.addr	= (u64)(unsigned long)&cpu_if_addr,
24*69b9a17aSMarc Zyngier 	};
25*69b9a17aSMarc Zyngier 	struct kvm_device_attr dist_attr = {
26*69b9a17aSMarc Zyngier 		.group	= KVM_DEV_ARM_VGIC_GRP_ADDR,
27*69b9a17aSMarc Zyngier 		.attr	= KVM_VGIC_V2_ADDR_TYPE_DIST,
28*69b9a17aSMarc Zyngier 		.addr	= (u64)(unsigned long)&dist_addr,
29*69b9a17aSMarc Zyngier 	};
30*69b9a17aSMarc Zyngier 
31*69b9a17aSMarc Zyngier 	err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &gic_device);
32*69b9a17aSMarc Zyngier 	if (err)
33*69b9a17aSMarc Zyngier 		return err;
34*69b9a17aSMarc Zyngier 
35*69b9a17aSMarc Zyngier 	gic_fd = gic_device.fd;
36*69b9a17aSMarc Zyngier 
37*69b9a17aSMarc Zyngier 	err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &cpu_if_attr);
38*69b9a17aSMarc Zyngier 	if (err)
39*69b9a17aSMarc Zyngier 		goto out_err;
40*69b9a17aSMarc Zyngier 
41*69b9a17aSMarc Zyngier 	err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &dist_attr);
42*69b9a17aSMarc Zyngier 	if (err)
43*69b9a17aSMarc Zyngier 		goto out_err;
44*69b9a17aSMarc Zyngier 
45*69b9a17aSMarc Zyngier 	return 0;
46*69b9a17aSMarc Zyngier 
47*69b9a17aSMarc Zyngier out_err:
48*69b9a17aSMarc Zyngier 	close(gic_fd);
49*69b9a17aSMarc Zyngier 	gic_fd = -1;
50*69b9a17aSMarc Zyngier 	return err;
51*69b9a17aSMarc Zyngier }
52*69b9a17aSMarc Zyngier 
53*69b9a17aSMarc Zyngier static int gic__create_irqchip(struct kvm *kvm)
547c0e8b0cSWill Deacon {
557c0e8b0cSWill Deacon 	int err;
56aa7a0e79SWill Deacon 	struct kvm_arm_device_addr gic_addr[] = {
577c0e8b0cSWill Deacon 		[0] = {
58aa7a0e79SWill Deacon 			.id = KVM_VGIC_V2_ADDR_TYPE_DIST |
59aa7a0e79SWill Deacon 			(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT),
607c0e8b0cSWill Deacon 			.addr = ARM_GIC_DIST_BASE,
617c0e8b0cSWill Deacon 		},
627c0e8b0cSWill Deacon 		[1] = {
63aa7a0e79SWill Deacon 			.id = KVM_VGIC_V2_ADDR_TYPE_CPU |
64aa7a0e79SWill Deacon 			(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT),
657c0e8b0cSWill Deacon 			.addr = ARM_GIC_CPUI_BASE,
667c0e8b0cSWill Deacon 		}
677c0e8b0cSWill Deacon 	};
687c0e8b0cSWill Deacon 
697c0e8b0cSWill Deacon 	err = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP);
707c0e8b0cSWill Deacon 	if (err)
717c0e8b0cSWill Deacon 		return err;
727c0e8b0cSWill Deacon 
73aa7a0e79SWill Deacon 	err = ioctl(kvm->vm_fd, KVM_ARM_SET_DEVICE_ADDR, &gic_addr[0]);
747c0e8b0cSWill Deacon 	if (err)
757c0e8b0cSWill Deacon 		return err;
767c0e8b0cSWill Deacon 
77aa7a0e79SWill Deacon 	err = ioctl(kvm->vm_fd, KVM_ARM_SET_DEVICE_ADDR, &gic_addr[1]);
787c0e8b0cSWill Deacon 	return err;
797c0e8b0cSWill Deacon }
807c0e8b0cSWill Deacon 
81*69b9a17aSMarc Zyngier int gic__create(struct kvm *kvm)
82*69b9a17aSMarc Zyngier {
83*69b9a17aSMarc Zyngier 	int err;
84*69b9a17aSMarc Zyngier 
85*69b9a17aSMarc Zyngier 	if (kvm->nrcpus > GIC_MAX_CPUS) {
86*69b9a17aSMarc Zyngier 		pr_warning("%d CPUS greater than maximum of %d -- truncating\n",
87*69b9a17aSMarc Zyngier 				kvm->nrcpus, GIC_MAX_CPUS);
88*69b9a17aSMarc Zyngier 		kvm->nrcpus = GIC_MAX_CPUS;
89*69b9a17aSMarc Zyngier 	}
90*69b9a17aSMarc Zyngier 
91*69b9a17aSMarc Zyngier 	/* Try the new way first, and fallback on legacy method otherwise */
92*69b9a17aSMarc Zyngier 	err = gic__create_device(kvm);
93*69b9a17aSMarc Zyngier 	if (err)
94*69b9a17aSMarc Zyngier 		err = gic__create_irqchip(kvm);
95*69b9a17aSMarc Zyngier 
96*69b9a17aSMarc Zyngier 	return err;
97*69b9a17aSMarc Zyngier }
98*69b9a17aSMarc Zyngier 
997c0e8b0cSWill Deacon void gic__generate_fdt_nodes(void *fdt, u32 phandle)
1007c0e8b0cSWill Deacon {
1017c0e8b0cSWill Deacon 	u64 reg_prop[] = {
1027c0e8b0cSWill Deacon 		cpu_to_fdt64(ARM_GIC_DIST_BASE), cpu_to_fdt64(ARM_GIC_DIST_SIZE),
1037c0e8b0cSWill Deacon 		cpu_to_fdt64(ARM_GIC_CPUI_BASE), cpu_to_fdt64(ARM_GIC_CPUI_SIZE),
1047c0e8b0cSWill Deacon 	};
1057c0e8b0cSWill Deacon 
1067c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, "intc"));
1077c0e8b0cSWill Deacon 	_FDT(fdt_property_string(fdt, "compatible", "arm,cortex-a15-gic"));
1087c0e8b0cSWill Deacon 	_FDT(fdt_property_cell(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS));
1097c0e8b0cSWill Deacon 	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
1107c0e8b0cSWill Deacon 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
1117c0e8b0cSWill Deacon 	_FDT(fdt_property_cell(fdt, "phandle", phandle));
1127c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
1137c0e8b0cSWill Deacon }
1147c0e8b0cSWill Deacon 
1157c0e8b0cSWill Deacon #define KVM_IRQCHIP_IRQ(x) (KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT) |\
1167c0e8b0cSWill Deacon 			   ((x) & KVM_ARM_IRQ_NUM_MASK)
1177c0e8b0cSWill Deacon 
1187c0e8b0cSWill Deacon void kvm__irq_line(struct kvm *kvm, int irq, int level)
1197c0e8b0cSWill Deacon {
1207c0e8b0cSWill Deacon 	struct kvm_irq_level irq_level = {
1217c0e8b0cSWill Deacon 		.irq	= KVM_IRQCHIP_IRQ(irq),
1227c0e8b0cSWill Deacon 		.level	= !!level,
1237c0e8b0cSWill Deacon 	};
1247c0e8b0cSWill Deacon 
1257c0e8b0cSWill Deacon 	if (irq < GIC_SPI_IRQ_BASE || irq > GIC_MAX_IRQ)
1267c0e8b0cSWill Deacon 		pr_warning("Ignoring invalid GIC IRQ %d", irq);
1277c0e8b0cSWill Deacon 	else if (ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level) < 0)
1287c0e8b0cSWill Deacon 		pr_warning("Could not KVM_IRQ_LINE for irq %d", irq);
1297c0e8b0cSWill Deacon }
1307c0e8b0cSWill Deacon 
1317c0e8b0cSWill Deacon void kvm__irq_trigger(struct kvm *kvm, int irq)
1327c0e8b0cSWill Deacon {
1337c0e8b0cSWill Deacon 	kvm__irq_line(kvm, irq, VIRTIO_IRQ_HIGH);
1347c0e8b0cSWill Deacon 	kvm__irq_line(kvm, irq, VIRTIO_IRQ_LOW);
1357c0e8b0cSWill Deacon }
136