| .. | .. |
|---|
| 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 | |
|---|