]>
Commit | Line | Data |
---|---|---|
74fe55dc MZ |
1 | /* |
2 | * Copyright (C) 2017 ARM Ltd. | |
3 | * Author: Marc Zyngier <marc.zyngier@arm.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include <linux/interrupt.h> | |
19 | #include <linux/irqdomain.h> | |
20 | #include <linux/kvm_host.h> | |
196b1364 | 21 | #include <linux/irqchip/arm-gic-v3.h> |
74fe55dc MZ |
22 | |
23 | #include "vgic.h" | |
24 | ||
25 | /** | |
26 | * vgic_v4_init - Initialize the GICv4 data structures | |
27 | * @kvm: Pointer to the VM being initialized | |
28 | * | |
29 | * We may be called each time a vITS is created, or when the | |
30 | * vgic is initialized. This relies on kvm->lock to be | |
31 | * held. In both cases, the number of vcpus should now be | |
32 | * fixed. | |
33 | */ | |
34 | int vgic_v4_init(struct kvm *kvm) | |
35 | { | |
36 | struct vgic_dist *dist = &kvm->arch.vgic; | |
37 | struct kvm_vcpu *vcpu; | |
38 | int i, nr_vcpus, ret; | |
39 | ||
40 | if (dist->its_vm.vpes) | |
41 | return 0; | |
42 | ||
43 | nr_vcpus = atomic_read(&kvm->online_vcpus); | |
44 | ||
45 | dist->its_vm.vpes = kzalloc(sizeof(*dist->its_vm.vpes) * nr_vcpus, | |
46 | GFP_KERNEL); | |
47 | if (!dist->its_vm.vpes) | |
48 | return -ENOMEM; | |
49 | ||
50 | dist->its_vm.nr_vpes = nr_vcpus; | |
51 | ||
52 | kvm_for_each_vcpu(i, vcpu, kvm) | |
53 | dist->its_vm.vpes[i] = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe; | |
54 | ||
55 | ret = its_alloc_vcpu_irqs(&dist->its_vm); | |
56 | if (ret < 0) { | |
57 | kvm_err("VPE IRQ allocation failure\n"); | |
58 | kfree(dist->its_vm.vpes); | |
59 | dist->its_vm.nr_vpes = 0; | |
60 | dist->its_vm.vpes = NULL; | |
61 | return ret; | |
62 | } | |
63 | ||
64 | return ret; | |
65 | } | |
66 | ||
67 | /** | |
68 | * vgic_v4_teardown - Free the GICv4 data structures | |
69 | * @kvm: Pointer to the VM being destroyed | |
70 | * | |
71 | * Relies on kvm->lock to be held. | |
72 | */ | |
73 | void vgic_v4_teardown(struct kvm *kvm) | |
74 | { | |
75 | struct its_vm *its_vm = &kvm->arch.vgic.its_vm; | |
76 | ||
77 | if (!its_vm->vpes) | |
78 | return; | |
79 | ||
80 | its_free_vcpu_irqs(its_vm); | |
81 | kfree(its_vm->vpes); | |
82 | its_vm->nr_vpes = 0; | |
83 | its_vm->vpes = NULL; | |
84 | } | |
196b1364 MZ |
85 | |
86 | static struct vgic_its *vgic_get_its(struct kvm *kvm, | |
87 | struct kvm_kernel_irq_routing_entry *irq_entry) | |
88 | { | |
89 | struct kvm_msi msi = (struct kvm_msi) { | |
90 | .address_lo = irq_entry->msi.address_lo, | |
91 | .address_hi = irq_entry->msi.address_hi, | |
92 | .data = irq_entry->msi.data, | |
93 | .flags = irq_entry->msi.flags, | |
94 | .devid = irq_entry->msi.devid, | |
95 | }; | |
96 | ||
97 | return vgic_msi_to_its(kvm, &msi); | |
98 | } | |
99 | ||
100 | int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int virq, | |
101 | struct kvm_kernel_irq_routing_entry *irq_entry) | |
102 | { | |
103 | struct vgic_its *its; | |
104 | struct vgic_irq *irq; | |
105 | struct its_vlpi_map map; | |
106 | int ret; | |
107 | ||
108 | if (!vgic_supports_direct_msis(kvm)) | |
109 | return 0; | |
110 | ||
111 | /* | |
112 | * Get the ITS, and escape early on error (not a valid | |
113 | * doorbell for any of our vITSs). | |
114 | */ | |
115 | its = vgic_get_its(kvm, irq_entry); | |
116 | if (IS_ERR(its)) | |
117 | return 0; | |
118 | ||
119 | mutex_lock(&its->its_lock); | |
120 | ||
121 | /* Perform then actual DevID/EventID -> LPI translation. */ | |
122 | ret = vgic_its_resolve_lpi(kvm, its, irq_entry->msi.devid, | |
123 | irq_entry->msi.data, &irq); | |
124 | if (ret) | |
125 | goto out; | |
126 | ||
127 | /* | |
128 | * Emit the mapping request. If it fails, the ITS probably | |
129 | * isn't v4 compatible, so let's silently bail out. Holding | |
130 | * the ITS lock should ensure that nothing can modify the | |
131 | * target vcpu. | |
132 | */ | |
133 | map = (struct its_vlpi_map) { | |
134 | .vm = &kvm->arch.vgic.its_vm, | |
135 | .vpe = &irq->target_vcpu->arch.vgic_cpu.vgic_v3.its_vpe, | |
136 | .vintid = irq->intid, | |
137 | .properties = ((irq->priority & 0xfc) | | |
138 | (irq->enabled ? LPI_PROP_ENABLED : 0) | | |
139 | LPI_PROP_GROUP1), | |
140 | .db_enabled = true, | |
141 | }; | |
142 | ||
143 | ret = its_map_vlpi(virq, &map); | |
144 | if (ret) | |
145 | goto out; | |
146 | ||
147 | irq->hw = true; | |
148 | irq->host_irq = virq; | |
149 | ||
150 | out: | |
151 | mutex_unlock(&its->its_lock); | |
152 | return ret; | |
153 | } | |
154 | ||
155 | int kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int virq, | |
156 | struct kvm_kernel_irq_routing_entry *irq_entry) | |
157 | { | |
158 | struct vgic_its *its; | |
159 | struct vgic_irq *irq; | |
160 | int ret; | |
161 | ||
162 | if (!vgic_supports_direct_msis(kvm)) | |
163 | return 0; | |
164 | ||
165 | /* | |
166 | * Get the ITS, and escape early on error (not a valid | |
167 | * doorbell for any of our vITSs). | |
168 | */ | |
169 | its = vgic_get_its(kvm, irq_entry); | |
170 | if (IS_ERR(its)) | |
171 | return 0; | |
172 | ||
173 | mutex_lock(&its->its_lock); | |
174 | ||
175 | ret = vgic_its_resolve_lpi(kvm, its, irq_entry->msi.devid, | |
176 | irq_entry->msi.data, &irq); | |
177 | if (ret) | |
178 | goto out; | |
179 | ||
180 | WARN_ON(!(irq->hw && irq->host_irq == virq)); | |
181 | irq->hw = false; | |
182 | ret = its_unmap_vlpi(virq); | |
183 | ||
184 | out: | |
185 | mutex_unlock(&its->its_lock); | |
186 | return ret; | |
187 | } |