.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2016,2017 ARM Limited, All Rights Reserved. |
---|
3 | 4 | * 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 | 5 | */ |
---|
17 | 6 | |
---|
18 | 7 | #include <linux/interrupt.h> |
---|
.. | .. |
---|
96 | 85 | |
---|
97 | 86 | static struct irq_domain *gic_domain; |
---|
98 | 87 | static const struct irq_domain_ops *vpe_domain_ops; |
---|
| 88 | +static const struct irq_domain_ops *sgi_domain_ops; |
---|
| 89 | + |
---|
| 90 | +static bool has_v4_1(void) |
---|
| 91 | +{ |
---|
| 92 | + return !!sgi_domain_ops; |
---|
| 93 | +} |
---|
| 94 | + |
---|
| 95 | +static int its_alloc_vcpu_sgis(struct its_vpe *vpe, int idx) |
---|
| 96 | +{ |
---|
| 97 | + char *name; |
---|
| 98 | + int sgi_base; |
---|
| 99 | + |
---|
| 100 | + if (!has_v4_1()) |
---|
| 101 | + return 0; |
---|
| 102 | + |
---|
| 103 | + name = kasprintf(GFP_KERNEL, "GICv4-sgi-%d", task_pid_nr(current)); |
---|
| 104 | + if (!name) |
---|
| 105 | + goto err; |
---|
| 106 | + |
---|
| 107 | + vpe->fwnode = irq_domain_alloc_named_id_fwnode(name, idx); |
---|
| 108 | + if (!vpe->fwnode) |
---|
| 109 | + goto err; |
---|
| 110 | + |
---|
| 111 | + kfree(name); |
---|
| 112 | + name = NULL; |
---|
| 113 | + |
---|
| 114 | + vpe->sgi_domain = irq_domain_create_linear(vpe->fwnode, 16, |
---|
| 115 | + sgi_domain_ops, vpe); |
---|
| 116 | + if (!vpe->sgi_domain) |
---|
| 117 | + goto err; |
---|
| 118 | + |
---|
| 119 | + sgi_base = __irq_domain_alloc_irqs(vpe->sgi_domain, -1, 16, |
---|
| 120 | + NUMA_NO_NODE, vpe, |
---|
| 121 | + false, NULL); |
---|
| 122 | + if (sgi_base <= 0) |
---|
| 123 | + goto err; |
---|
| 124 | + |
---|
| 125 | + return 0; |
---|
| 126 | + |
---|
| 127 | +err: |
---|
| 128 | + if (vpe->sgi_domain) |
---|
| 129 | + irq_domain_remove(vpe->sgi_domain); |
---|
| 130 | + if (vpe->fwnode) |
---|
| 131 | + irq_domain_free_fwnode(vpe->fwnode); |
---|
| 132 | + kfree(name); |
---|
| 133 | + return -ENOMEM; |
---|
| 134 | +} |
---|
99 | 135 | |
---|
100 | 136 | int its_alloc_vcpu_irqs(struct its_vm *vm) |
---|
101 | 137 | { |
---|
.. | .. |
---|
123 | 159 | if (vpe_base_irq <= 0) |
---|
124 | 160 | goto err; |
---|
125 | 161 | |
---|
126 | | - for (i = 0; i < vm->nr_vpes; i++) |
---|
| 162 | + for (i = 0; i < vm->nr_vpes; i++) { |
---|
| 163 | + int ret; |
---|
127 | 164 | vm->vpes[i]->irq = vpe_base_irq + i; |
---|
| 165 | + ret = its_alloc_vcpu_sgis(vm->vpes[i], i); |
---|
| 166 | + if (ret) |
---|
| 167 | + goto err; |
---|
| 168 | + } |
---|
128 | 169 | |
---|
129 | 170 | return 0; |
---|
130 | 171 | |
---|
.. | .. |
---|
137 | 178 | return -ENOMEM; |
---|
138 | 179 | } |
---|
139 | 180 | |
---|
| 181 | +static void its_free_sgi_irqs(struct its_vm *vm) |
---|
| 182 | +{ |
---|
| 183 | + int i; |
---|
| 184 | + |
---|
| 185 | + if (!has_v4_1()) |
---|
| 186 | + return; |
---|
| 187 | + |
---|
| 188 | + for (i = 0; i < vm->nr_vpes; i++) { |
---|
| 189 | + unsigned int irq = irq_find_mapping(vm->vpes[i]->sgi_domain, 0); |
---|
| 190 | + |
---|
| 191 | + if (WARN_ON(!irq)) |
---|
| 192 | + continue; |
---|
| 193 | + |
---|
| 194 | + irq_domain_free_irqs(irq, 16); |
---|
| 195 | + irq_domain_remove(vm->vpes[i]->sgi_domain); |
---|
| 196 | + irq_domain_free_fwnode(vm->vpes[i]->fwnode); |
---|
| 197 | + } |
---|
| 198 | +} |
---|
| 199 | + |
---|
140 | 200 | void its_free_vcpu_irqs(struct its_vm *vm) |
---|
141 | 201 | { |
---|
| 202 | + its_free_sgi_irqs(vm); |
---|
142 | 203 | irq_domain_free_irqs(vm->vpes[0]->irq, vm->nr_vpes); |
---|
143 | 204 | irq_domain_remove(vm->domain); |
---|
144 | 205 | irq_domain_free_fwnode(vm->fwnode); |
---|
.. | .. |
---|
149 | 210 | return irq_set_vcpu_affinity(vpe->irq, info); |
---|
150 | 211 | } |
---|
151 | 212 | |
---|
152 | | -int its_schedule_vpe(struct its_vpe *vpe, bool on) |
---|
| 213 | +int its_make_vpe_non_resident(struct its_vpe *vpe, bool db) |
---|
153 | 214 | { |
---|
154 | | - struct its_cmd_info info; |
---|
| 215 | + struct irq_desc *desc = irq_to_desc(vpe->irq); |
---|
| 216 | + struct its_cmd_info info = { }; |
---|
| 217 | + int ret; |
---|
155 | 218 | |
---|
156 | 219 | WARN_ON(preemptible()); |
---|
157 | 220 | |
---|
158 | | - info.cmd_type = on ? SCHEDULE_VPE : DESCHEDULE_VPE; |
---|
| 221 | + info.cmd_type = DESCHEDULE_VPE; |
---|
| 222 | + if (has_v4_1()) { |
---|
| 223 | + /* GICv4.1 can directly deal with doorbells */ |
---|
| 224 | + info.req_db = db; |
---|
| 225 | + } else { |
---|
| 226 | + /* Undo the nested disable_irq() calls... */ |
---|
| 227 | + while (db && irqd_irq_disabled(&desc->irq_data)) |
---|
| 228 | + enable_irq(vpe->irq); |
---|
| 229 | + } |
---|
159 | 230 | |
---|
160 | | - return its_send_vpe_cmd(vpe, &info); |
---|
| 231 | + ret = its_send_vpe_cmd(vpe, &info); |
---|
| 232 | + if (!ret) |
---|
| 233 | + vpe->resident = false; |
---|
| 234 | + |
---|
| 235 | + vpe->ready = false; |
---|
| 236 | + |
---|
| 237 | + return ret; |
---|
161 | 238 | } |
---|
| 239 | + |
---|
| 240 | +int its_make_vpe_resident(struct its_vpe *vpe, bool g0en, bool g1en) |
---|
| 241 | +{ |
---|
| 242 | + struct its_cmd_info info = { }; |
---|
| 243 | + int ret; |
---|
| 244 | + |
---|
| 245 | + WARN_ON(preemptible()); |
---|
| 246 | + |
---|
| 247 | + info.cmd_type = SCHEDULE_VPE; |
---|
| 248 | + if (has_v4_1()) { |
---|
| 249 | + info.g0en = g0en; |
---|
| 250 | + info.g1en = g1en; |
---|
| 251 | + } else { |
---|
| 252 | + /* Disabled the doorbell, as we're about to enter the guest */ |
---|
| 253 | + disable_irq_nosync(vpe->irq); |
---|
| 254 | + } |
---|
| 255 | + |
---|
| 256 | + ret = its_send_vpe_cmd(vpe, &info); |
---|
| 257 | + if (!ret) |
---|
| 258 | + vpe->resident = true; |
---|
| 259 | + |
---|
| 260 | + return ret; |
---|
| 261 | +} |
---|
| 262 | + |
---|
| 263 | +int its_commit_vpe(struct its_vpe *vpe) |
---|
| 264 | +{ |
---|
| 265 | + struct its_cmd_info info = { |
---|
| 266 | + .cmd_type = COMMIT_VPE, |
---|
| 267 | + }; |
---|
| 268 | + int ret; |
---|
| 269 | + |
---|
| 270 | + WARN_ON(preemptible()); |
---|
| 271 | + |
---|
| 272 | + ret = its_send_vpe_cmd(vpe, &info); |
---|
| 273 | + if (!ret) |
---|
| 274 | + vpe->ready = true; |
---|
| 275 | + |
---|
| 276 | + return ret; |
---|
| 277 | +} |
---|
| 278 | + |
---|
162 | 279 | |
---|
163 | 280 | int its_invall_vpe(struct its_vpe *vpe) |
---|
164 | 281 | { |
---|
.. | .. |
---|
222 | 339 | return irq_set_vcpu_affinity(irq, &info); |
---|
223 | 340 | } |
---|
224 | 341 | |
---|
225 | | -int its_init_v4(struct irq_domain *domain, const struct irq_domain_ops *ops) |
---|
| 342 | +int its_prop_update_vsgi(int irq, u8 priority, bool group) |
---|
| 343 | +{ |
---|
| 344 | + struct its_cmd_info info = { |
---|
| 345 | + .cmd_type = PROP_UPDATE_VSGI, |
---|
| 346 | + { |
---|
| 347 | + .priority = priority, |
---|
| 348 | + .group = group, |
---|
| 349 | + }, |
---|
| 350 | + }; |
---|
| 351 | + |
---|
| 352 | + return irq_set_vcpu_affinity(irq, &info); |
---|
| 353 | +} |
---|
| 354 | + |
---|
| 355 | +int its_init_v4(struct irq_domain *domain, |
---|
| 356 | + const struct irq_domain_ops *vpe_ops, |
---|
| 357 | + const struct irq_domain_ops *sgi_ops) |
---|
226 | 358 | { |
---|
227 | 359 | if (domain) { |
---|
228 | 360 | pr_info("ITS: Enabling GICv4 support\n"); |
---|
229 | 361 | gic_domain = domain; |
---|
230 | | - vpe_domain_ops = ops; |
---|
| 362 | + vpe_domain_ops = vpe_ops; |
---|
| 363 | + sgi_domain_ops = sgi_ops; |
---|
231 | 364 | return 0; |
---|
232 | 365 | } |
---|
233 | 366 | |
---|