xref: /kvmtool/riscv/aia.c (revision 100fade1ac13f242d6fce9a3a7eab3f1f92f144b)
1328f0879SAnup Patel #include "kvm/devices.h"
2328f0879SAnup Patel #include "kvm/fdt.h"
3328f0879SAnup Patel #include "kvm/ioeventfd.h"
4328f0879SAnup Patel #include "kvm/ioport.h"
5328f0879SAnup Patel #include "kvm/kvm.h"
6328f0879SAnup Patel #include "kvm/kvm-cpu.h"
7328f0879SAnup Patel #include "kvm/irq.h"
8328f0879SAnup Patel #include "kvm/util.h"
9328f0879SAnup Patel 
10328f0879SAnup Patel static int aia_fd = -1;
11328f0879SAnup Patel 
12328f0879SAnup Patel static u32 aia_mode = KVM_DEV_RISCV_AIA_MODE_EMUL;
13328f0879SAnup Patel static struct kvm_device_attr aia_mode_attr = {
14328f0879SAnup Patel 	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
15328f0879SAnup Patel 	.attr	= KVM_DEV_RISCV_AIA_CONFIG_MODE,
16328f0879SAnup Patel };
17328f0879SAnup Patel 
18328f0879SAnup Patel static u32 aia_nr_ids = 0;
19328f0879SAnup Patel static struct kvm_device_attr aia_nr_ids_attr = {
20328f0879SAnup Patel 	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
21328f0879SAnup Patel 	.attr	= KVM_DEV_RISCV_AIA_CONFIG_IDS,
22328f0879SAnup Patel };
23328f0879SAnup Patel 
24328f0879SAnup Patel static u32 aia_nr_sources = 0;
25328f0879SAnup Patel static struct kvm_device_attr aia_nr_sources_attr = {
26328f0879SAnup Patel 	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
27328f0879SAnup Patel 	.attr	= KVM_DEV_RISCV_AIA_CONFIG_SRCS,
28328f0879SAnup Patel };
29328f0879SAnup Patel 
30328f0879SAnup Patel static u32 aia_hart_bits = 0;
31328f0879SAnup Patel static struct kvm_device_attr aia_hart_bits_attr = {
32328f0879SAnup Patel 	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
33328f0879SAnup Patel 	.attr	= KVM_DEV_RISCV_AIA_CONFIG_HART_BITS,
34328f0879SAnup Patel };
35328f0879SAnup Patel 
36328f0879SAnup Patel static u32 aia_nr_harts = 0;
37328f0879SAnup Patel 
38328f0879SAnup Patel #define IRQCHIP_AIA_NR			0
39328f0879SAnup Patel 
40328f0879SAnup Patel #define AIA_IMSIC_BASE			RISCV_IRQCHIP
41328f0879SAnup Patel #define AIA_IMSIC_ADDR(__hart)		\
42328f0879SAnup Patel 	(AIA_IMSIC_BASE + (__hart) * KVM_DEV_RISCV_IMSIC_SIZE)
43328f0879SAnup Patel #define AIA_IMSIC_SIZE			\
44328f0879SAnup Patel 	(aia_nr_harts * KVM_DEV_RISCV_IMSIC_SIZE)
45328f0879SAnup Patel #define AIA_APLIC_ADDR			\
46328f0879SAnup Patel 	(AIA_IMSIC_BASE + AIA_IMSIC_SIZE)
47328f0879SAnup Patel 
aia__generate_fdt_node(void * fdt,struct kvm * kvm)48328f0879SAnup Patel static void aia__generate_fdt_node(void *fdt, struct kvm *kvm)
49328f0879SAnup Patel {
50328f0879SAnup Patel 	u32 i;
51328f0879SAnup Patel 	char name[64];
52328f0879SAnup Patel 	u32 reg_cells[4], *irq_cells;
53328f0879SAnup Patel 
54328f0879SAnup Patel 	irq_cells = calloc(aia_nr_harts * 2, sizeof(u32));
55328f0879SAnup Patel 	if (!irq_cells)
56328f0879SAnup Patel 		die("Failed to alloc irq_cells");
57328f0879SAnup Patel 
58328f0879SAnup Patel 	sprintf(name, "imsics@%08x", (u32)AIA_IMSIC_BASE);
59328f0879SAnup Patel 	_FDT(fdt_begin_node(fdt, name));
60328f0879SAnup Patel 	_FDT(fdt_property_string(fdt, "compatible", "riscv,imsics"));
61328f0879SAnup Patel 	reg_cells[0] = 0;
62328f0879SAnup Patel 	reg_cells[1] = cpu_to_fdt32(AIA_IMSIC_BASE);
63328f0879SAnup Patel 	reg_cells[2] = 0;
64328f0879SAnup Patel 	reg_cells[3] = cpu_to_fdt32(AIA_IMSIC_SIZE);
65328f0879SAnup Patel 	_FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
66328f0879SAnup Patel 	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 0));
67328f0879SAnup Patel 	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
68328f0879SAnup Patel 	_FDT(fdt_property(fdt, "msi-controller", NULL, 0));
69328f0879SAnup Patel 	_FDT(fdt_property_cell(fdt, "riscv,num-ids", aia_nr_ids));
70328f0879SAnup Patel 	_FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_IMSIC));
71328f0879SAnup Patel 	for (i = 0; i < aia_nr_harts; i++) {
72328f0879SAnup Patel 		irq_cells[2*i + 0] = cpu_to_fdt32(PHANDLE_CPU_INTC_BASE + i);
73328f0879SAnup Patel 		irq_cells[2*i + 1] = cpu_to_fdt32(9);
74328f0879SAnup Patel 	}
75328f0879SAnup Patel 	_FDT(fdt_property(fdt, "interrupts-extended", irq_cells,
76328f0879SAnup Patel 			  sizeof(u32) * aia_nr_harts * 2));
77328f0879SAnup Patel 	_FDT(fdt_end_node(fdt));
78328f0879SAnup Patel 
79328f0879SAnup Patel 	free(irq_cells);
80328f0879SAnup Patel 
81328f0879SAnup Patel 	/* Skip APLIC node if we have no interrupt sources */
82328f0879SAnup Patel 	if (!aia_nr_sources)
83328f0879SAnup Patel 		return;
84328f0879SAnup Patel 
85328f0879SAnup Patel 	sprintf(name, "aplic@%08x", (u32)AIA_APLIC_ADDR);
86328f0879SAnup Patel 	_FDT(fdt_begin_node(fdt, name));
87328f0879SAnup Patel 	_FDT(fdt_property_string(fdt, "compatible", "riscv,aplic"));
88328f0879SAnup Patel 	reg_cells[0] = 0;
89328f0879SAnup Patel 	reg_cells[1] = cpu_to_fdt32(AIA_APLIC_ADDR);
90328f0879SAnup Patel 	reg_cells[2] = 0;
91328f0879SAnup Patel 	reg_cells[3] = cpu_to_fdt32(KVM_DEV_RISCV_APLIC_SIZE);
92328f0879SAnup Patel 	_FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
93328f0879SAnup Patel 	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 2));
94328f0879SAnup Patel 	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
95328f0879SAnup Patel 	_FDT(fdt_property_cell(fdt, "riscv,num-sources", aia_nr_sources));
96328f0879SAnup Patel 	_FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_APLIC));
97328f0879SAnup Patel 	_FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_AIA_IMSIC));
98328f0879SAnup Patel 	_FDT(fdt_end_node(fdt));
99328f0879SAnup Patel }
100328f0879SAnup Patel 
aia__irq_routing_init(struct kvm * kvm)101328f0879SAnup Patel static int aia__irq_routing_init(struct kvm *kvm)
102328f0879SAnup Patel {
103328f0879SAnup Patel 	int r;
104328f0879SAnup Patel 	int irqlines = aia_nr_sources + 1;
105328f0879SAnup Patel 
106328f0879SAnup Patel 	/* Skip this if we have no interrupt sources */
107328f0879SAnup Patel 	if (!aia_nr_sources)
108328f0879SAnup Patel 		return 0;
109328f0879SAnup Patel 
110328f0879SAnup Patel 	/*
111328f0879SAnup Patel 	 * This describes the default routing that the kernel uses without
112328f0879SAnup Patel 	 * any routing explicitly set up via KVM_SET_GSI_ROUTING. So we
113328f0879SAnup Patel 	 * don't need to commit these setting right now. The first actual
114328f0879SAnup Patel 	 * user (MSI routing) will engage these mappings then.
115328f0879SAnup Patel 	 */
116328f0879SAnup Patel 	for (next_gsi = 0; next_gsi < irqlines; next_gsi++) {
117328f0879SAnup Patel 		r = irq__allocate_routing_entry();
118328f0879SAnup Patel 		if (r)
119328f0879SAnup Patel 			return r;
120328f0879SAnup Patel 
121328f0879SAnup Patel 		irq_routing->entries[irq_routing->nr++] =
122328f0879SAnup Patel 			(struct kvm_irq_routing_entry) {
123328f0879SAnup Patel 				.gsi = next_gsi,
124328f0879SAnup Patel 				.type = KVM_IRQ_ROUTING_IRQCHIP,
125328f0879SAnup Patel 				.u.irqchip.irqchip = IRQCHIP_AIA_NR,
126328f0879SAnup Patel 				.u.irqchip.pin = next_gsi,
127328f0879SAnup Patel 		};
128328f0879SAnup Patel 	}
129328f0879SAnup Patel 
130328f0879SAnup Patel 	return 0;
131328f0879SAnup Patel }
132328f0879SAnup Patel 
aia__init(struct kvm * kvm)133328f0879SAnup Patel static int aia__init(struct kvm *kvm)
134328f0879SAnup Patel {
135328f0879SAnup Patel 	int i, ret;
136328f0879SAnup Patel 	u64 aia_addr = 0;
137328f0879SAnup Patel 	struct kvm_device_attr aia_addr_attr = {
138328f0879SAnup Patel 		.group	= KVM_DEV_RISCV_AIA_GRP_ADDR,
139328f0879SAnup Patel 		.addr	= (u64)(unsigned long)&aia_addr,
140328f0879SAnup Patel 	};
141328f0879SAnup Patel 	struct kvm_device_attr aia_init_attr = {
142328f0879SAnup Patel 		.group	= KVM_DEV_RISCV_AIA_GRP_CTRL,
143328f0879SAnup Patel 		.attr	= KVM_DEV_RISCV_AIA_CTRL_INIT,
144328f0879SAnup Patel 	};
145328f0879SAnup Patel 
146328f0879SAnup Patel 	/* Setup global device attribute variables */
147328f0879SAnup Patel 	aia_mode_attr.addr = (u64)(unsigned long)&aia_mode;
148328f0879SAnup Patel 	aia_nr_ids_attr.addr = (u64)(unsigned long)&aia_nr_ids;
149328f0879SAnup Patel 	aia_nr_sources_attr.addr = (u64)(unsigned long)&aia_nr_sources;
150328f0879SAnup Patel 	aia_hart_bits_attr.addr = (u64)(unsigned long)&aia_hart_bits;
151328f0879SAnup Patel 
152328f0879SAnup Patel 	/* Do nothing if AIA device not created */
153328f0879SAnup Patel 	if (aia_fd < 0)
154328f0879SAnup Patel 		return 0;
155328f0879SAnup Patel 
156328f0879SAnup Patel 	/* Set/Get AIA device config parameters */
157328f0879SAnup Patel 	ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_mode_attr);
158328f0879SAnup Patel 	if (ret)
159328f0879SAnup Patel 		return ret;
160328f0879SAnup Patel 	ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_nr_ids_attr);
161328f0879SAnup Patel 	if (ret)
162328f0879SAnup Patel 		return ret;
163328f0879SAnup Patel 	aia_nr_sources = irq__get_nr_allocated_lines();
164328f0879SAnup Patel 	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_nr_sources_attr);
165328f0879SAnup Patel 	if (ret)
166328f0879SAnup Patel 		return ret;
167*100fade1SAndrew Jones 	aia_hart_bits = fls_long(kvm->nrcpus - 1);
168328f0879SAnup Patel 	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_hart_bits_attr);
169328f0879SAnup Patel 	if (ret)
170328f0879SAnup Patel 		return ret;
171328f0879SAnup Patel 
172328f0879SAnup Patel 	/* Save number of HARTs for FDT generation */
173328f0879SAnup Patel 	aia_nr_harts = kvm->nrcpus;
174328f0879SAnup Patel 
175328f0879SAnup Patel 	/* Set AIA device addresses */
176328f0879SAnup Patel 	aia_addr = AIA_APLIC_ADDR;
177328f0879SAnup Patel 	aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_APLIC;
178328f0879SAnup Patel 	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
179328f0879SAnup Patel 	if (ret)
180328f0879SAnup Patel 		return ret;
181328f0879SAnup Patel 	for (i = 0; i < kvm->nrcpus; i++) {
182328f0879SAnup Patel 		aia_addr = AIA_IMSIC_ADDR(i);
183328f0879SAnup Patel 		aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_IMSIC(i);
184328f0879SAnup Patel 		ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
185328f0879SAnup Patel 		if (ret)
186328f0879SAnup Patel 			return ret;
187328f0879SAnup Patel 	}
188328f0879SAnup Patel 
189328f0879SAnup Patel 	/* Setup default IRQ routing */
190328f0879SAnup Patel 	aia__irq_routing_init(kvm);
191328f0879SAnup Patel 
192328f0879SAnup Patel 	/* Initialize the AIA device */
193328f0879SAnup Patel 	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_init_attr);
194328f0879SAnup Patel 	if (ret)
195328f0879SAnup Patel 		return ret;
196328f0879SAnup Patel 
197328f0879SAnup Patel 	/* Mark IRQFD as ready */
198328f0879SAnup Patel 	riscv_irqchip_irqfd_ready = true;
199328f0879SAnup Patel 
200328f0879SAnup Patel 	return 0;
201328f0879SAnup Patel }
202328f0879SAnup Patel late_init(aia__init);
203328f0879SAnup Patel 
aia__create(struct kvm * kvm)204328f0879SAnup Patel void aia__create(struct kvm *kvm)
205328f0879SAnup Patel {
206328f0879SAnup Patel 	int err;
207328f0879SAnup Patel 	struct kvm_create_device aia_device = {
208328f0879SAnup Patel 		.type = KVM_DEV_TYPE_RISCV_AIA,
209328f0879SAnup Patel 		.flags = 0,
210328f0879SAnup Patel 	};
211328f0879SAnup Patel 
212328f0879SAnup Patel 	if (kvm->cfg.arch.ext_disabled[KVM_RISCV_ISA_EXT_SSAIA])
213328f0879SAnup Patel 		return;
214328f0879SAnup Patel 
215328f0879SAnup Patel 	err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &aia_device);
216328f0879SAnup Patel 	if (err)
217328f0879SAnup Patel 		return;
218328f0879SAnup Patel 	aia_fd = aia_device.fd;
219328f0879SAnup Patel 
220328f0879SAnup Patel 	riscv_irqchip = IRQCHIP_AIA;
221328f0879SAnup Patel 	riscv_irqchip_inkernel = true;
222328f0879SAnup Patel 	riscv_irqchip_trigger = NULL;
223328f0879SAnup Patel 	riscv_irqchip_generate_fdt_node = aia__generate_fdt_node;
224328f0879SAnup Patel 	riscv_irqchip_phandle = PHANDLE_AIA_APLIC;
225328f0879SAnup Patel 	riscv_irqchip_msi_phandle = PHANDLE_AIA_IMSIC;
226328f0879SAnup Patel 	riscv_irqchip_line_sensing = true;
227328f0879SAnup Patel }
228