18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GICv3 ITS emulation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015,2016 ARM Ltd. 68c2ecf20Sopenharmony_ci * Author: Andre Przywara <andre.przywara@arm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/cpu.h> 108c2ecf20Sopenharmony_ci#include <linux/kvm.h> 118c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <linux/list_sort.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic-v3.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/kvm_emulate.h> 208c2ecf20Sopenharmony_ci#include <asm/kvm_arm.h> 218c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "vgic.h" 248c2ecf20Sopenharmony_ci#include "vgic-mmio.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int vgic_its_save_tables_v0(struct vgic_its *its); 278c2ecf20Sopenharmony_cistatic int vgic_its_restore_tables_v0(struct vgic_its *its); 288c2ecf20Sopenharmony_cistatic int vgic_its_commit_v0(struct vgic_its *its); 298c2ecf20Sopenharmony_cistatic int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq, 308c2ecf20Sopenharmony_ci struct kvm_vcpu *filter_vcpu, bool needs_inv); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Creates a new (reference to a) struct vgic_irq for a given LPI. 348c2ecf20Sopenharmony_ci * If this LPI is already mapped on another ITS, we increase its refcount 358c2ecf20Sopenharmony_ci * and return a pointer to the existing structure. 368c2ecf20Sopenharmony_ci * If this is a "new" LPI, we allocate and initialize a new struct vgic_irq. 378c2ecf20Sopenharmony_ci * This function returns a pointer to the _unlocked_ structure. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid, 408c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 438c2ecf20Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intid), *oldirq; 448c2ecf20Sopenharmony_ci unsigned long flags; 458c2ecf20Sopenharmony_ci int ret; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* In this case there is no put, since we keep the reference. */ 488c2ecf20Sopenharmony_ci if (irq) 498c2ecf20Sopenharmony_ci return irq; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci irq = kzalloc(sizeof(struct vgic_irq), GFP_KERNEL); 528c2ecf20Sopenharmony_ci if (!irq) 538c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&irq->lpi_list); 568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&irq->ap_list); 578c2ecf20Sopenharmony_ci raw_spin_lock_init(&irq->irq_lock); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci irq->config = VGIC_CONFIG_EDGE; 608c2ecf20Sopenharmony_ci kref_init(&irq->refcount); 618c2ecf20Sopenharmony_ci irq->intid = intid; 628c2ecf20Sopenharmony_ci irq->target_vcpu = vcpu; 638c2ecf20Sopenharmony_ci irq->group = 1; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dist->lpi_list_lock, flags); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * There could be a race with another vgic_add_lpi(), so we need to 698c2ecf20Sopenharmony_ci * check that we don't add a second list entry with the same LPI. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci list_for_each_entry(oldirq, &dist->lpi_list_head, lpi_list) { 728c2ecf20Sopenharmony_ci if (oldirq->intid != intid) 738c2ecf20Sopenharmony_ci continue; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Someone was faster with adding this LPI, lets use that. */ 768c2ecf20Sopenharmony_ci kfree(irq); 778c2ecf20Sopenharmony_ci irq = oldirq; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * This increases the refcount, the caller is expected to 818c2ecf20Sopenharmony_ci * call vgic_put_irq() on the returned pointer once it's 828c2ecf20Sopenharmony_ci * finished with the IRQ. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci vgic_get_irq_kref(irq); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci goto out_unlock; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci list_add_tail(&irq->lpi_list, &dist->lpi_list_head); 908c2ecf20Sopenharmony_ci dist->lpi_list_count++; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciout_unlock: 938c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * We "cache" the configuration table entries in our struct vgic_irq's. 978c2ecf20Sopenharmony_ci * However we only have those structs for mapped IRQs, so we read in 988c2ecf20Sopenharmony_ci * the respective config data from memory here upon mapping the LPI. 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * Should any of these fail, behave as if we couldn't create the LPI 1018c2ecf20Sopenharmony_ci * by dropping the refcount and returning the error. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci ret = update_lpi_config(kvm, irq, NULL, false); 1048c2ecf20Sopenharmony_ci if (ret) { 1058c2ecf20Sopenharmony_ci vgic_put_irq(kvm, irq); 1068c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ret = vgic_v3_lpi_sync_pending_status(kvm, irq); 1108c2ecf20Sopenharmony_ci if (ret) { 1118c2ecf20Sopenharmony_ci vgic_put_irq(kvm, irq); 1128c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return irq; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct its_device { 1198c2ecf20Sopenharmony_ci struct list_head dev_list; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* the head for the list of ITTEs */ 1228c2ecf20Sopenharmony_ci struct list_head itt_head; 1238c2ecf20Sopenharmony_ci u32 num_eventid_bits; 1248c2ecf20Sopenharmony_ci gpa_t itt_addr; 1258c2ecf20Sopenharmony_ci u32 device_id; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define COLLECTION_NOT_MAPPED ((u32)~0) 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct its_collection { 1318c2ecf20Sopenharmony_ci struct list_head coll_list; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci u32 collection_id; 1348c2ecf20Sopenharmony_ci u32 target_addr; 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define its_is_collection_mapped(coll) ((coll) && \ 1388c2ecf20Sopenharmony_ci ((coll)->target_addr != COLLECTION_NOT_MAPPED)) 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistruct its_ite { 1418c2ecf20Sopenharmony_ci struct list_head ite_list; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci struct vgic_irq *irq; 1448c2ecf20Sopenharmony_ci struct its_collection *collection; 1458c2ecf20Sopenharmony_ci u32 event_id; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistruct vgic_translation_cache_entry { 1498c2ecf20Sopenharmony_ci struct list_head entry; 1508c2ecf20Sopenharmony_ci phys_addr_t db; 1518c2ecf20Sopenharmony_ci u32 devid; 1528c2ecf20Sopenharmony_ci u32 eventid; 1538c2ecf20Sopenharmony_ci struct vgic_irq *irq; 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/** 1578c2ecf20Sopenharmony_ci * struct vgic_its_abi - ITS abi ops and settings 1588c2ecf20Sopenharmony_ci * @cte_esz: collection table entry size 1598c2ecf20Sopenharmony_ci * @dte_esz: device table entry size 1608c2ecf20Sopenharmony_ci * @ite_esz: interrupt translation table entry size 1618c2ecf20Sopenharmony_ci * @save tables: save the ITS tables into guest RAM 1628c2ecf20Sopenharmony_ci * @restore_tables: restore the ITS internal structs from tables 1638c2ecf20Sopenharmony_ci * stored in guest RAM 1648c2ecf20Sopenharmony_ci * @commit: initialize the registers which expose the ABI settings, 1658c2ecf20Sopenharmony_ci * especially the entry sizes 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistruct vgic_its_abi { 1688c2ecf20Sopenharmony_ci int cte_esz; 1698c2ecf20Sopenharmony_ci int dte_esz; 1708c2ecf20Sopenharmony_ci int ite_esz; 1718c2ecf20Sopenharmony_ci int (*save_tables)(struct vgic_its *its); 1728c2ecf20Sopenharmony_ci int (*restore_tables)(struct vgic_its *its); 1738c2ecf20Sopenharmony_ci int (*commit)(struct vgic_its *its); 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#define ABI_0_ESZ 8 1778c2ecf20Sopenharmony_ci#define ESZ_MAX ABI_0_ESZ 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic const struct vgic_its_abi its_table_abi_versions[] = { 1808c2ecf20Sopenharmony_ci [0] = { 1818c2ecf20Sopenharmony_ci .cte_esz = ABI_0_ESZ, 1828c2ecf20Sopenharmony_ci .dte_esz = ABI_0_ESZ, 1838c2ecf20Sopenharmony_ci .ite_esz = ABI_0_ESZ, 1848c2ecf20Sopenharmony_ci .save_tables = vgic_its_save_tables_v0, 1858c2ecf20Sopenharmony_ci .restore_tables = vgic_its_restore_tables_v0, 1868c2ecf20Sopenharmony_ci .commit = vgic_its_commit_v0, 1878c2ecf20Sopenharmony_ci }, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci#define NR_ITS_ABIS ARRAY_SIZE(its_table_abi_versions) 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciinline const struct vgic_its_abi *vgic_its_get_abi(struct vgic_its *its) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return &its_table_abi_versions[its->abi_rev]; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int vgic_its_set_abi(struct vgic_its *its, u32 rev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci its->abi_rev = rev; 2028c2ecf20Sopenharmony_ci abi = vgic_its_get_abi(its); 2038c2ecf20Sopenharmony_ci return abi->commit(its); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * Find and returns a device in the device table for an ITS. 2088c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_cistatic struct its_device *find_its_device(struct vgic_its *its, u32 device_id) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct its_device *device; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci list_for_each_entry(device, &its->device_list, dev_list) 2158c2ecf20Sopenharmony_ci if (device_id == device->device_id) 2168c2ecf20Sopenharmony_ci return device; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return NULL; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* 2228c2ecf20Sopenharmony_ci * Find and returns an interrupt translation table entry (ITTE) for a given 2238c2ecf20Sopenharmony_ci * Device ID/Event ID pair on an ITS. 2248c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistatic struct its_ite *find_ite(struct vgic_its *its, u32 device_id, 2278c2ecf20Sopenharmony_ci u32 event_id) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct its_device *device; 2308c2ecf20Sopenharmony_ci struct its_ite *ite; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci device = find_its_device(its, device_id); 2338c2ecf20Sopenharmony_ci if (device == NULL) 2348c2ecf20Sopenharmony_ci return NULL; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci list_for_each_entry(ite, &device->itt_head, ite_list) 2378c2ecf20Sopenharmony_ci if (ite->event_id == event_id) 2388c2ecf20Sopenharmony_ci return ite; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return NULL; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* To be used as an iterator this macro misses the enclosing parentheses */ 2448c2ecf20Sopenharmony_ci#define for_each_lpi_its(dev, ite, its) \ 2458c2ecf20Sopenharmony_ci list_for_each_entry(dev, &(its)->device_list, dev_list) \ 2468c2ecf20Sopenharmony_ci list_for_each_entry(ite, &(dev)->itt_head, ite_list) 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci#define GIC_LPI_OFFSET 8192 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#define VITS_TYPER_IDBITS 16 2518c2ecf20Sopenharmony_ci#define VITS_TYPER_DEVBITS 16 2528c2ecf20Sopenharmony_ci#define VITS_DTE_MAX_DEVID_OFFSET (BIT(14) - 1) 2538c2ecf20Sopenharmony_ci#define VITS_ITE_MAX_EVENTID_OFFSET (BIT(16) - 1) 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci * Finds and returns a collection in the ITS collection table. 2578c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic struct its_collection *find_collection(struct vgic_its *its, int coll_id) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct its_collection *collection; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci list_for_each_entry(collection, &its->collection_list, coll_list) { 2648c2ecf20Sopenharmony_ci if (coll_id == collection->collection_id) 2658c2ecf20Sopenharmony_ci return collection; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return NULL; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci#define LPI_PROP_ENABLE_BIT(p) ((p) & LPI_PROP_ENABLED) 2728c2ecf20Sopenharmony_ci#define LPI_PROP_PRIORITY(p) ((p) & 0xfc) 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * Reads the configuration data for a given LPI from guest memory and 2768c2ecf20Sopenharmony_ci * updates the fields in struct vgic_irq. 2778c2ecf20Sopenharmony_ci * If filter_vcpu is not NULL, applies only if the IRQ is targeting this 2788c2ecf20Sopenharmony_ci * VCPU. Unconditionally applies if filter_vcpu is NULL. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistatic int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq, 2818c2ecf20Sopenharmony_ci struct kvm_vcpu *filter_vcpu, bool needs_inv) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci u64 propbase = GICR_PROPBASER_ADDRESS(kvm->arch.vgic.propbaser); 2848c2ecf20Sopenharmony_ci u8 prop; 2858c2ecf20Sopenharmony_ci int ret; 2868c2ecf20Sopenharmony_ci unsigned long flags; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = kvm_read_guest_lock(kvm, propbase + irq->intid - GIC_LPI_OFFSET, 2898c2ecf20Sopenharmony_ci &prop, 1); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (ret) 2928c2ecf20Sopenharmony_ci return ret; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!filter_vcpu || filter_vcpu == irq->target_vcpu) { 2978c2ecf20Sopenharmony_ci irq->priority = LPI_PROP_PRIORITY(prop); 2988c2ecf20Sopenharmony_ci irq->enabled = LPI_PROP_ENABLE_BIT(prop); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!irq->hw) { 3018c2ecf20Sopenharmony_ci vgic_queue_irq_unlock(kvm, irq, flags); 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (irq->hw) 3098c2ecf20Sopenharmony_ci return its_prop_update_vlpi(irq->host_irq, prop, needs_inv); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * Create a snapshot of the current LPIs targeting @vcpu, so that we can 3168c2ecf20Sopenharmony_ci * enumerate those LPIs without holding any lock. 3178c2ecf20Sopenharmony_ci * Returns their number and puts the kmalloc'ed array into intid_ptr. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ciint vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 3228c2ecf20Sopenharmony_ci struct vgic_irq *irq; 3238c2ecf20Sopenharmony_ci unsigned long flags; 3248c2ecf20Sopenharmony_ci u32 *intids; 3258c2ecf20Sopenharmony_ci int irq_count, i = 0; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * There is an obvious race between allocating the array and LPIs 3298c2ecf20Sopenharmony_ci * being mapped/unmapped. If we ended up here as a result of a 3308c2ecf20Sopenharmony_ci * command, we're safe (locks are held, preventing another 3318c2ecf20Sopenharmony_ci * command). If coming from another path (such as enabling LPIs), 3328c2ecf20Sopenharmony_ci * we must be careful not to overrun the array. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci irq_count = READ_ONCE(dist->lpi_list_count); 3358c2ecf20Sopenharmony_ci intids = kmalloc_array(irq_count, sizeof(intids[0]), GFP_KERNEL); 3368c2ecf20Sopenharmony_ci if (!intids) 3378c2ecf20Sopenharmony_ci return -ENOMEM; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dist->lpi_list_lock, flags); 3408c2ecf20Sopenharmony_ci list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) { 3418c2ecf20Sopenharmony_ci if (i == irq_count) 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci /* We don't need to "get" the IRQ, as we hold the list lock. */ 3448c2ecf20Sopenharmony_ci if (vcpu && irq->target_vcpu != vcpu) 3458c2ecf20Sopenharmony_ci continue; 3468c2ecf20Sopenharmony_ci intids[i++] = irq->intid; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci *intid_ptr = intids; 3518c2ecf20Sopenharmony_ci return i; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int update_affinity(struct vgic_irq *irq, struct kvm_vcpu *vcpu) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci int ret = 0; 3578c2ecf20Sopenharmony_ci unsigned long flags; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 3608c2ecf20Sopenharmony_ci irq->target_vcpu = vcpu; 3618c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (irq->hw) { 3648c2ecf20Sopenharmony_ci struct its_vlpi_map map; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = its_get_vlpi(irq->host_irq, &map); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (map.vpe) 3718c2ecf20Sopenharmony_ci atomic_dec(&map.vpe->vlpi_count); 3728c2ecf20Sopenharmony_ci map.vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe; 3738c2ecf20Sopenharmony_ci atomic_inc(&map.vpe->vlpi_count); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ret = its_map_vlpi(irq->host_irq, &map); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* 3828c2ecf20Sopenharmony_ci * Promotes the ITS view of affinity of an ITTE (which redistributor this LPI 3838c2ecf20Sopenharmony_ci * is targeting) to the VGIC's view, which deals with target VCPUs. 3848c2ecf20Sopenharmony_ci * Needs to be called whenever either the collection for a LPIs has 3858c2ecf20Sopenharmony_ci * changed or the collection itself got retargeted. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_cistatic void update_affinity_ite(struct kvm *kvm, struct its_ite *ite) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!its_is_collection_mapped(ite->collection)) 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, ite->collection->target_addr); 3958c2ecf20Sopenharmony_ci update_affinity(ite->irq, vcpu); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* 3998c2ecf20Sopenharmony_ci * Updates the target VCPU for every LPI targeting this collection. 4008c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic void update_affinity_collection(struct kvm *kvm, struct vgic_its *its, 4038c2ecf20Sopenharmony_ci struct its_collection *coll) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct its_device *device; 4068c2ecf20Sopenharmony_ci struct its_ite *ite; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for_each_lpi_its(device, ite, its) { 4098c2ecf20Sopenharmony_ci if (!ite->collection || coll != ite->collection) 4108c2ecf20Sopenharmony_ci continue; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci update_affinity_ite(kvm, ite); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic u32 max_lpis_propbaser(u64 propbaser) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci int nr_idbits = (propbaser & 0x1f) + 1; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 1U << min(nr_idbits, INTERRUPT_ID_BITS_ITS); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* 4248c2ecf20Sopenharmony_ci * Sync the pending table pending bit of LPIs targeting @vcpu 4258c2ecf20Sopenharmony_ci * with our own data structures. This relies on the LPI being 4268c2ecf20Sopenharmony_ci * mapped before. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_cistatic int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci gpa_t pendbase = GICR_PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser); 4318c2ecf20Sopenharmony_ci struct vgic_irq *irq; 4328c2ecf20Sopenharmony_ci int last_byte_offset = -1; 4338c2ecf20Sopenharmony_ci int ret = 0; 4348c2ecf20Sopenharmony_ci u32 *intids; 4358c2ecf20Sopenharmony_ci int nr_irqs, i; 4368c2ecf20Sopenharmony_ci unsigned long flags; 4378c2ecf20Sopenharmony_ci u8 pendmask; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci nr_irqs = vgic_copy_lpi_list(vcpu->kvm, vcpu, &intids); 4408c2ecf20Sopenharmony_ci if (nr_irqs < 0) 4418c2ecf20Sopenharmony_ci return nr_irqs; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) { 4448c2ecf20Sopenharmony_ci int byte_offset, bit_nr; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci byte_offset = intids[i] / BITS_PER_BYTE; 4478c2ecf20Sopenharmony_ci bit_nr = intids[i] % BITS_PER_BYTE; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * For contiguously allocated LPIs chances are we just read 4518c2ecf20Sopenharmony_ci * this very same byte in the last iteration. Reuse that. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (byte_offset != last_byte_offset) { 4548c2ecf20Sopenharmony_ci ret = kvm_read_guest_lock(vcpu->kvm, 4558c2ecf20Sopenharmony_ci pendbase + byte_offset, 4568c2ecf20Sopenharmony_ci &pendmask, 1); 4578c2ecf20Sopenharmony_ci if (ret) { 4588c2ecf20Sopenharmony_ci kfree(intids); 4598c2ecf20Sopenharmony_ci return ret; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci last_byte_offset = byte_offset; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, NULL, intids[i]); 4658c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 4668c2ecf20Sopenharmony_ci irq->pending_latch = pendmask & (1U << bit_nr); 4678c2ecf20Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 4688c2ecf20Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci kfree(intids); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return ret; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_typer(struct kvm *kvm, 4778c2ecf20Sopenharmony_ci struct vgic_its *its, 4788c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 4818c2ecf20Sopenharmony_ci u64 reg = GITS_TYPER_PLPIS; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * We use linear CPU numbers for redistributor addressing, 4858c2ecf20Sopenharmony_ci * so GITS_TYPER.PTA is 0. 4868c2ecf20Sopenharmony_ci * Also we force all PROPBASER registers to be the same, so 4878c2ecf20Sopenharmony_ci * CommonLPIAff is 0 as well. 4888c2ecf20Sopenharmony_ci * To avoid memory waste in the guest, we keep the number of IDBits and 4898c2ecf20Sopenharmony_ci * DevBits low - as least for the time being. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci reg |= GIC_ENCODE_SZ(VITS_TYPER_DEVBITS, 5) << GITS_TYPER_DEVBITS_SHIFT; 4928c2ecf20Sopenharmony_ci reg |= GIC_ENCODE_SZ(VITS_TYPER_IDBITS, 5) << GITS_TYPER_IDBITS_SHIFT; 4938c2ecf20Sopenharmony_ci reg |= GIC_ENCODE_SZ(abi->ite_esz, 4) << GITS_TYPER_ITT_ENTRY_SIZE_SHIFT; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return extract_bytes(reg, addr & 7, len); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_iidr(struct kvm *kvm, 4998c2ecf20Sopenharmony_ci struct vgic_its *its, 5008c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci u32 val; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci val = (its->abi_rev << GITS_IIDR_REV_SHIFT) & GITS_IIDR_REV_MASK; 5058c2ecf20Sopenharmony_ci val |= (PRODUCT_ID_KVM << GITS_IIDR_PRODUCTID_SHIFT) | IMPLEMENTER_ARM; 5068c2ecf20Sopenharmony_ci return val; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int vgic_mmio_uaccess_write_its_iidr(struct kvm *kvm, 5108c2ecf20Sopenharmony_ci struct vgic_its *its, 5118c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 5128c2ecf20Sopenharmony_ci unsigned long val) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci u32 rev = GITS_IIDR_REV(val); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (rev >= NR_ITS_ABIS) 5178c2ecf20Sopenharmony_ci return -EINVAL; 5188c2ecf20Sopenharmony_ci return vgic_its_set_abi(its, rev); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm, 5228c2ecf20Sopenharmony_ci struct vgic_its *its, 5238c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci switch (addr & 0xffff) { 5268c2ecf20Sopenharmony_ci case GITS_PIDR0: 5278c2ecf20Sopenharmony_ci return 0x92; /* part number, bits[7:0] */ 5288c2ecf20Sopenharmony_ci case GITS_PIDR1: 5298c2ecf20Sopenharmony_ci return 0xb4; /* part number, bits[11:8] */ 5308c2ecf20Sopenharmony_ci case GITS_PIDR2: 5318c2ecf20Sopenharmony_ci return GIC_PIDR2_ARCH_GICv3 | 0x0b; 5328c2ecf20Sopenharmony_ci case GITS_PIDR4: 5338c2ecf20Sopenharmony_ci return 0x40; /* This is a 64K software visible page */ 5348c2ecf20Sopenharmony_ci /* The following are the ID registers for (any) GIC. */ 5358c2ecf20Sopenharmony_ci case GITS_CIDR0: 5368c2ecf20Sopenharmony_ci return 0x0d; 5378c2ecf20Sopenharmony_ci case GITS_CIDR1: 5388c2ecf20Sopenharmony_ci return 0xf0; 5398c2ecf20Sopenharmony_ci case GITS_CIDR2: 5408c2ecf20Sopenharmony_ci return 0x05; 5418c2ecf20Sopenharmony_ci case GITS_CIDR3: 5428c2ecf20Sopenharmony_ci return 0xb1; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic struct vgic_irq *__vgic_its_check_cache(struct vgic_dist *dist, 5498c2ecf20Sopenharmony_ci phys_addr_t db, 5508c2ecf20Sopenharmony_ci u32 devid, u32 eventid) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct vgic_translation_cache_entry *cte; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci list_for_each_entry(cte, &dist->lpi_translation_cache, entry) { 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * If we hit a NULL entry, there is nothing after this 5578c2ecf20Sopenharmony_ci * point. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci if (!cte->irq) 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (cte->db != db || cte->devid != devid || 5638c2ecf20Sopenharmony_ci cte->eventid != eventid) 5648c2ecf20Sopenharmony_ci continue; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* 5678c2ecf20Sopenharmony_ci * Move this entry to the head, as it is the most 5688c2ecf20Sopenharmony_ci * recently used. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_ci if (!list_is_first(&cte->entry, &dist->lpi_translation_cache)) 5718c2ecf20Sopenharmony_ci list_move(&cte->entry, &dist->lpi_translation_cache); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return cte->irq; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return NULL; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic struct vgic_irq *vgic_its_check_cache(struct kvm *kvm, phys_addr_t db, 5808c2ecf20Sopenharmony_ci u32 devid, u32 eventid) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 5838c2ecf20Sopenharmony_ci struct vgic_irq *irq; 5848c2ecf20Sopenharmony_ci unsigned long flags; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dist->lpi_list_lock, flags); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci irq = __vgic_its_check_cache(dist, db, devid, eventid); 5898c2ecf20Sopenharmony_ci if (irq) 5908c2ecf20Sopenharmony_ci vgic_get_irq_kref(irq); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return irq; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic void vgic_its_cache_translation(struct kvm *kvm, struct vgic_its *its, 5988c2ecf20Sopenharmony_ci u32 devid, u32 eventid, 5998c2ecf20Sopenharmony_ci struct vgic_irq *irq) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 6028c2ecf20Sopenharmony_ci struct vgic_translation_cache_entry *cte; 6038c2ecf20Sopenharmony_ci unsigned long flags; 6048c2ecf20Sopenharmony_ci phys_addr_t db; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* Do not cache a directly injected interrupt */ 6078c2ecf20Sopenharmony_ci if (irq->hw) 6088c2ecf20Sopenharmony_ci return; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dist->lpi_list_lock, flags); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (unlikely(list_empty(&dist->lpi_translation_cache))) 6138c2ecf20Sopenharmony_ci goto out; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * We could have raced with another CPU caching the same 6178c2ecf20Sopenharmony_ci * translation behind our back, so let's check it is not in 6188c2ecf20Sopenharmony_ci * already 6198c2ecf20Sopenharmony_ci */ 6208c2ecf20Sopenharmony_ci db = its->vgic_its_base + GITS_TRANSLATER; 6218c2ecf20Sopenharmony_ci if (__vgic_its_check_cache(dist, db, devid, eventid)) 6228c2ecf20Sopenharmony_ci goto out; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* Always reuse the last entry (LRU policy) */ 6258c2ecf20Sopenharmony_ci cte = list_last_entry(&dist->lpi_translation_cache, 6268c2ecf20Sopenharmony_ci typeof(*cte), entry); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* 6298c2ecf20Sopenharmony_ci * Caching the translation implies having an extra reference 6308c2ecf20Sopenharmony_ci * to the interrupt, so drop the potential reference on what 6318c2ecf20Sopenharmony_ci * was in the cache, and increment it on the new interrupt. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ci if (cte->irq) 6348c2ecf20Sopenharmony_ci __vgic_put_lpi_locked(kvm, cte->irq); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci vgic_get_irq_kref(irq); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci cte->db = db; 6398c2ecf20Sopenharmony_ci cte->devid = devid; 6408c2ecf20Sopenharmony_ci cte->eventid = eventid; 6418c2ecf20Sopenharmony_ci cte->irq = irq; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* Move the new translation to the head of the list */ 6448c2ecf20Sopenharmony_ci list_move(&cte->entry, &dist->lpi_translation_cache); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ciout: 6478c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_civoid vgic_its_invalidate_cache(struct kvm *kvm) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 6538c2ecf20Sopenharmony_ci struct vgic_translation_cache_entry *cte; 6548c2ecf20Sopenharmony_ci unsigned long flags; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dist->lpi_list_lock, flags); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci list_for_each_entry(cte, &dist->lpi_translation_cache, entry) { 6598c2ecf20Sopenharmony_ci /* 6608c2ecf20Sopenharmony_ci * If we hit a NULL entry, there is nothing after this 6618c2ecf20Sopenharmony_ci * point. 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci if (!cte->irq) 6648c2ecf20Sopenharmony_ci break; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci __vgic_put_lpi_locked(kvm, cte->irq); 6678c2ecf20Sopenharmony_ci cte->irq = NULL; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags); 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ciint vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its, 6748c2ecf20Sopenharmony_ci u32 devid, u32 eventid, struct vgic_irq **irq) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 6778c2ecf20Sopenharmony_ci struct its_ite *ite; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!its->enabled) 6808c2ecf20Sopenharmony_ci return -EBUSY; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci ite = find_ite(its, devid, eventid); 6838c2ecf20Sopenharmony_ci if (!ite || !its_is_collection_mapped(ite->collection)) 6848c2ecf20Sopenharmony_ci return E_ITS_INT_UNMAPPED_INTERRUPT; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, ite->collection->target_addr); 6878c2ecf20Sopenharmony_ci if (!vcpu) 6888c2ecf20Sopenharmony_ci return E_ITS_INT_UNMAPPED_INTERRUPT; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (!vcpu->arch.vgic_cpu.lpis_enabled) 6918c2ecf20Sopenharmony_ci return -EBUSY; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci vgic_its_cache_translation(kvm, its, devid, eventid, ite->irq); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci *irq = ite->irq; 6968c2ecf20Sopenharmony_ci return 0; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistruct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci u64 address; 7028c2ecf20Sopenharmony_ci struct kvm_io_device *kvm_io_dev; 7038c2ecf20Sopenharmony_ci struct vgic_io_device *iodev; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (!vgic_has_its(kvm)) 7068c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (!(msi->flags & KVM_MSI_VALID_DEVID)) 7098c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci address = (u64)msi->address_hi << 32 | msi->address_lo; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, address); 7148c2ecf20Sopenharmony_ci if (!kvm_io_dev) 7158c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (kvm_io_dev->ops != &kvm_io_gic_ops) 7188c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci iodev = container_of(kvm_io_dev, struct vgic_io_device, dev); 7218c2ecf20Sopenharmony_ci if (iodev->iodev_type != IODEV_ITS) 7228c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci return iodev->its; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci/* 7288c2ecf20Sopenharmony_ci * Find the target VCPU and the LPI number for a given devid/eventid pair 7298c2ecf20Sopenharmony_ci * and make this IRQ pending, possibly injecting it. 7308c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 7318c2ecf20Sopenharmony_ci * Returns 0 on success, a positive error value for any ITS mapping 7328c2ecf20Sopenharmony_ci * related errors and negative error values for generic errors. 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_cistatic int vgic_its_trigger_msi(struct kvm *kvm, struct vgic_its *its, 7358c2ecf20Sopenharmony_ci u32 devid, u32 eventid) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct vgic_irq *irq = NULL; 7388c2ecf20Sopenharmony_ci unsigned long flags; 7398c2ecf20Sopenharmony_ci int err; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci err = vgic_its_resolve_lpi(kvm, its, devid, eventid, &irq); 7428c2ecf20Sopenharmony_ci if (err) 7438c2ecf20Sopenharmony_ci return err; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (irq->hw) 7468c2ecf20Sopenharmony_ci return irq_set_irqchip_state(irq->host_irq, 7478c2ecf20Sopenharmony_ci IRQCHIP_STATE_PENDING, true); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 7508c2ecf20Sopenharmony_ci irq->pending_latch = true; 7518c2ecf20Sopenharmony_ci vgic_queue_irq_unlock(kvm, irq, flags); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ciint vgic_its_inject_cached_translation(struct kvm *kvm, struct kvm_msi *msi) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct vgic_irq *irq; 7598c2ecf20Sopenharmony_ci unsigned long flags; 7608c2ecf20Sopenharmony_ci phys_addr_t db; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci db = (u64)msi->address_hi << 32 | msi->address_lo; 7638c2ecf20Sopenharmony_ci irq = vgic_its_check_cache(kvm, db, msi->devid, msi->data); 7648c2ecf20Sopenharmony_ci if (!irq) 7658c2ecf20Sopenharmony_ci return -EWOULDBLOCK; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 7688c2ecf20Sopenharmony_ci irq->pending_latch = true; 7698c2ecf20Sopenharmony_ci vgic_queue_irq_unlock(kvm, irq, flags); 7708c2ecf20Sopenharmony_ci vgic_put_irq(kvm, irq); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci/* 7768c2ecf20Sopenharmony_ci * Queries the KVM IO bus framework to get the ITS pointer from the given 7778c2ecf20Sopenharmony_ci * doorbell address. 7788c2ecf20Sopenharmony_ci * We then call vgic_its_trigger_msi() with the decoded data. 7798c2ecf20Sopenharmony_ci * According to the KVM_SIGNAL_MSI API description returns 1 on success. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_ciint vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct vgic_its *its; 7848c2ecf20Sopenharmony_ci int ret; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (!vgic_its_inject_cached_translation(kvm, msi)) 7878c2ecf20Sopenharmony_ci return 1; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci its = vgic_msi_to_its(kvm, msi); 7908c2ecf20Sopenharmony_ci if (IS_ERR(its)) 7918c2ecf20Sopenharmony_ci return PTR_ERR(its); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci mutex_lock(&its->its_lock); 7948c2ecf20Sopenharmony_ci ret = vgic_its_trigger_msi(kvm, its, msi->devid, msi->data); 7958c2ecf20Sopenharmony_ci mutex_unlock(&its->its_lock); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (ret < 0) 7988c2ecf20Sopenharmony_ci return ret; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* 8018c2ecf20Sopenharmony_ci * KVM_SIGNAL_MSI demands a return value > 0 for success and 0 8028c2ecf20Sopenharmony_ci * if the guest has blocked the MSI. So we map any LPI mapping 8038c2ecf20Sopenharmony_ci * related error to that. 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci if (ret) 8068c2ecf20Sopenharmony_ci return 0; 8078c2ecf20Sopenharmony_ci else 8088c2ecf20Sopenharmony_ci return 1; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* Requires the its_lock to be held. */ 8128c2ecf20Sopenharmony_cistatic void its_free_ite(struct kvm *kvm, struct its_ite *ite) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci list_del(&ite->ite_list); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* This put matches the get in vgic_add_lpi. */ 8178c2ecf20Sopenharmony_ci if (ite->irq) { 8188c2ecf20Sopenharmony_ci if (ite->irq->hw) 8198c2ecf20Sopenharmony_ci WARN_ON(its_unmap_vlpi(ite->irq->host_irq)); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci vgic_put_irq(kvm, ite->irq); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci kfree(ite); 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic u64 its_cmd_mask_field(u64 *its_cmd, int word, int shift, int size) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT_ULL(size) - 1); 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci#define its_cmd_get_command(cmd) its_cmd_mask_field(cmd, 0, 0, 8) 8338c2ecf20Sopenharmony_ci#define its_cmd_get_deviceid(cmd) its_cmd_mask_field(cmd, 0, 32, 32) 8348c2ecf20Sopenharmony_ci#define its_cmd_get_size(cmd) (its_cmd_mask_field(cmd, 1, 0, 5) + 1) 8358c2ecf20Sopenharmony_ci#define its_cmd_get_id(cmd) its_cmd_mask_field(cmd, 1, 0, 32) 8368c2ecf20Sopenharmony_ci#define its_cmd_get_physical_id(cmd) its_cmd_mask_field(cmd, 1, 32, 32) 8378c2ecf20Sopenharmony_ci#define its_cmd_get_collection(cmd) its_cmd_mask_field(cmd, 2, 0, 16) 8388c2ecf20Sopenharmony_ci#define its_cmd_get_ittaddr(cmd) (its_cmd_mask_field(cmd, 2, 8, 44) << 8) 8398c2ecf20Sopenharmony_ci#define its_cmd_get_target_addr(cmd) its_cmd_mask_field(cmd, 2, 16, 32) 8408c2ecf20Sopenharmony_ci#define its_cmd_get_validbit(cmd) its_cmd_mask_field(cmd, 2, 63, 1) 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci/* 8438c2ecf20Sopenharmony_ci * The DISCARD command frees an Interrupt Translation Table Entry (ITTE). 8448c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its, 8478c2ecf20Sopenharmony_ci u64 *its_cmd) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci u32 device_id = its_cmd_get_deviceid(its_cmd); 8508c2ecf20Sopenharmony_ci u32 event_id = its_cmd_get_id(its_cmd); 8518c2ecf20Sopenharmony_ci struct its_ite *ite; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci ite = find_ite(its, device_id, event_id); 8548c2ecf20Sopenharmony_ci if (ite && its_is_collection_mapped(ite->collection)) { 8558c2ecf20Sopenharmony_ci /* 8568c2ecf20Sopenharmony_ci * Though the spec talks about removing the pending state, we 8578c2ecf20Sopenharmony_ci * don't bother here since we clear the ITTE anyway and the 8588c2ecf20Sopenharmony_ci * pending state is a property of the ITTE struct. 8598c2ecf20Sopenharmony_ci */ 8608c2ecf20Sopenharmony_ci vgic_its_invalidate_cache(kvm); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci its_free_ite(kvm, ite); 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci return E_ITS_DISCARD_UNMAPPED_INTERRUPT; 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci/* 8708c2ecf20Sopenharmony_ci * The MOVI command moves an ITTE to a different collection. 8718c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 8728c2ecf20Sopenharmony_ci */ 8738c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its, 8748c2ecf20Sopenharmony_ci u64 *its_cmd) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci u32 device_id = its_cmd_get_deviceid(its_cmd); 8778c2ecf20Sopenharmony_ci u32 event_id = its_cmd_get_id(its_cmd); 8788c2ecf20Sopenharmony_ci u32 coll_id = its_cmd_get_collection(its_cmd); 8798c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 8808c2ecf20Sopenharmony_ci struct its_ite *ite; 8818c2ecf20Sopenharmony_ci struct its_collection *collection; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci ite = find_ite(its, device_id, event_id); 8848c2ecf20Sopenharmony_ci if (!ite) 8858c2ecf20Sopenharmony_ci return E_ITS_MOVI_UNMAPPED_INTERRUPT; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (!its_is_collection_mapped(ite->collection)) 8888c2ecf20Sopenharmony_ci return E_ITS_MOVI_UNMAPPED_COLLECTION; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci collection = find_collection(its, coll_id); 8918c2ecf20Sopenharmony_ci if (!its_is_collection_mapped(collection)) 8928c2ecf20Sopenharmony_ci return E_ITS_MOVI_UNMAPPED_COLLECTION; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci ite->collection = collection; 8958c2ecf20Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, collection->target_addr); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci vgic_its_invalidate_cache(kvm); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return update_affinity(ite->irq, vcpu); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/* 9038c2ecf20Sopenharmony_ci * Check whether an ID can be stored into the corresponding guest table. 9048c2ecf20Sopenharmony_ci * For a direct table this is pretty easy, but gets a bit nasty for 9058c2ecf20Sopenharmony_ci * indirect tables. We check whether the resulting guest physical address 9068c2ecf20Sopenharmony_ci * is actually valid (covered by a memslot and guest accessible). 9078c2ecf20Sopenharmony_ci * For this we have to read the respective first level entry. 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_cistatic bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id, 9108c2ecf20Sopenharmony_ci gpa_t *eaddr) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci int l1_tbl_size = GITS_BASER_NR_PAGES(baser) * SZ_64K; 9138c2ecf20Sopenharmony_ci u64 indirect_ptr, type = GITS_BASER_TYPE(baser); 9148c2ecf20Sopenharmony_ci phys_addr_t base = GITS_BASER_ADDR_48_to_52(baser); 9158c2ecf20Sopenharmony_ci int esz = GITS_BASER_ENTRY_SIZE(baser); 9168c2ecf20Sopenharmony_ci int index, idx; 9178c2ecf20Sopenharmony_ci gfn_t gfn; 9188c2ecf20Sopenharmony_ci bool ret; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci switch (type) { 9218c2ecf20Sopenharmony_ci case GITS_BASER_TYPE_DEVICE: 9228c2ecf20Sopenharmony_ci if (id >= BIT_ULL(VITS_TYPER_DEVBITS)) 9238c2ecf20Sopenharmony_ci return false; 9248c2ecf20Sopenharmony_ci break; 9258c2ecf20Sopenharmony_ci case GITS_BASER_TYPE_COLLECTION: 9268c2ecf20Sopenharmony_ci /* as GITS_TYPER.CIL == 0, ITS supports 16-bit collection ID */ 9278c2ecf20Sopenharmony_ci if (id >= BIT_ULL(16)) 9288c2ecf20Sopenharmony_ci return false; 9298c2ecf20Sopenharmony_ci break; 9308c2ecf20Sopenharmony_ci default: 9318c2ecf20Sopenharmony_ci return false; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (!(baser & GITS_BASER_INDIRECT)) { 9358c2ecf20Sopenharmony_ci phys_addr_t addr; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (id >= (l1_tbl_size / esz)) 9388c2ecf20Sopenharmony_ci return false; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci addr = base + id * esz; 9418c2ecf20Sopenharmony_ci gfn = addr >> PAGE_SHIFT; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (eaddr) 9448c2ecf20Sopenharmony_ci *eaddr = addr; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci goto out; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* calculate and check the index into the 1st level */ 9508c2ecf20Sopenharmony_ci index = id / (SZ_64K / esz); 9518c2ecf20Sopenharmony_ci if (index >= (l1_tbl_size / sizeof(u64))) 9528c2ecf20Sopenharmony_ci return false; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* Each 1st level entry is represented by a 64-bit value. */ 9558c2ecf20Sopenharmony_ci if (kvm_read_guest_lock(its->dev->kvm, 9568c2ecf20Sopenharmony_ci base + index * sizeof(indirect_ptr), 9578c2ecf20Sopenharmony_ci &indirect_ptr, sizeof(indirect_ptr))) 9588c2ecf20Sopenharmony_ci return false; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci indirect_ptr = le64_to_cpu(indirect_ptr); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* check the valid bit of the first level entry */ 9638c2ecf20Sopenharmony_ci if (!(indirect_ptr & BIT_ULL(63))) 9648c2ecf20Sopenharmony_ci return false; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* Mask the guest physical address and calculate the frame number. */ 9678c2ecf20Sopenharmony_ci indirect_ptr &= GENMASK_ULL(51, 16); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* Find the address of the actual entry */ 9708c2ecf20Sopenharmony_ci index = id % (SZ_64K / esz); 9718c2ecf20Sopenharmony_ci indirect_ptr += index * esz; 9728c2ecf20Sopenharmony_ci gfn = indirect_ptr >> PAGE_SHIFT; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (eaddr) 9758c2ecf20Sopenharmony_ci *eaddr = indirect_ptr; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ciout: 9788c2ecf20Sopenharmony_ci idx = srcu_read_lock(&its->dev->kvm->srcu); 9798c2ecf20Sopenharmony_ci ret = kvm_is_visible_gfn(its->dev->kvm, gfn); 9808c2ecf20Sopenharmony_ci srcu_read_unlock(&its->dev->kvm->srcu, idx); 9818c2ecf20Sopenharmony_ci return ret; 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic int vgic_its_alloc_collection(struct vgic_its *its, 9858c2ecf20Sopenharmony_ci struct its_collection **colp, 9868c2ecf20Sopenharmony_ci u32 coll_id) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct its_collection *collection; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (!vgic_its_check_id(its, its->baser_coll_table, coll_id, NULL)) 9918c2ecf20Sopenharmony_ci return E_ITS_MAPC_COLLECTION_OOR; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci collection = kzalloc(sizeof(*collection), GFP_KERNEL); 9948c2ecf20Sopenharmony_ci if (!collection) 9958c2ecf20Sopenharmony_ci return -ENOMEM; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci collection->collection_id = coll_id; 9988c2ecf20Sopenharmony_ci collection->target_addr = COLLECTION_NOT_MAPPED; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci list_add_tail(&collection->coll_list, &its->collection_list); 10018c2ecf20Sopenharmony_ci *colp = collection; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic void vgic_its_free_collection(struct vgic_its *its, u32 coll_id) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct its_collection *collection; 10098c2ecf20Sopenharmony_ci struct its_device *device; 10108c2ecf20Sopenharmony_ci struct its_ite *ite; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* 10138c2ecf20Sopenharmony_ci * Clearing the mapping for that collection ID removes the 10148c2ecf20Sopenharmony_ci * entry from the list. If there wasn't any before, we can 10158c2ecf20Sopenharmony_ci * go home early. 10168c2ecf20Sopenharmony_ci */ 10178c2ecf20Sopenharmony_ci collection = find_collection(its, coll_id); 10188c2ecf20Sopenharmony_ci if (!collection) 10198c2ecf20Sopenharmony_ci return; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci for_each_lpi_its(device, ite, its) 10228c2ecf20Sopenharmony_ci if (ite->collection && 10238c2ecf20Sopenharmony_ci ite->collection->collection_id == coll_id) 10248c2ecf20Sopenharmony_ci ite->collection = NULL; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci list_del(&collection->coll_list); 10278c2ecf20Sopenharmony_ci kfree(collection); 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci/* Must be called with its_lock mutex held */ 10318c2ecf20Sopenharmony_cistatic struct its_ite *vgic_its_alloc_ite(struct its_device *device, 10328c2ecf20Sopenharmony_ci struct its_collection *collection, 10338c2ecf20Sopenharmony_ci u32 event_id) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct its_ite *ite; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci ite = kzalloc(sizeof(*ite), GFP_KERNEL); 10388c2ecf20Sopenharmony_ci if (!ite) 10398c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci ite->event_id = event_id; 10428c2ecf20Sopenharmony_ci ite->collection = collection; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci list_add_tail(&ite->ite_list, &device->itt_head); 10458c2ecf20Sopenharmony_ci return ite; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/* 10498c2ecf20Sopenharmony_ci * The MAPTI and MAPI commands map LPIs to ITTEs. 10508c2ecf20Sopenharmony_ci * Must be called with its_lock mutex held. 10518c2ecf20Sopenharmony_ci */ 10528c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its, 10538c2ecf20Sopenharmony_ci u64 *its_cmd) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci u32 device_id = its_cmd_get_deviceid(its_cmd); 10568c2ecf20Sopenharmony_ci u32 event_id = its_cmd_get_id(its_cmd); 10578c2ecf20Sopenharmony_ci u32 coll_id = its_cmd_get_collection(its_cmd); 10588c2ecf20Sopenharmony_ci struct its_ite *ite; 10598c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu = NULL; 10608c2ecf20Sopenharmony_ci struct its_device *device; 10618c2ecf20Sopenharmony_ci struct its_collection *collection, *new_coll = NULL; 10628c2ecf20Sopenharmony_ci struct vgic_irq *irq; 10638c2ecf20Sopenharmony_ci int lpi_nr; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci device = find_its_device(its, device_id); 10668c2ecf20Sopenharmony_ci if (!device) 10678c2ecf20Sopenharmony_ci return E_ITS_MAPTI_UNMAPPED_DEVICE; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (event_id >= BIT_ULL(device->num_eventid_bits)) 10708c2ecf20Sopenharmony_ci return E_ITS_MAPTI_ID_OOR; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (its_cmd_get_command(its_cmd) == GITS_CMD_MAPTI) 10738c2ecf20Sopenharmony_ci lpi_nr = its_cmd_get_physical_id(its_cmd); 10748c2ecf20Sopenharmony_ci else 10758c2ecf20Sopenharmony_ci lpi_nr = event_id; 10768c2ecf20Sopenharmony_ci if (lpi_nr < GIC_LPI_OFFSET || 10778c2ecf20Sopenharmony_ci lpi_nr >= max_lpis_propbaser(kvm->arch.vgic.propbaser)) 10788c2ecf20Sopenharmony_ci return E_ITS_MAPTI_PHYSICALID_OOR; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* If there is an existing mapping, behavior is UNPREDICTABLE. */ 10818c2ecf20Sopenharmony_ci if (find_ite(its, device_id, event_id)) 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci collection = find_collection(its, coll_id); 10858c2ecf20Sopenharmony_ci if (!collection) { 10868c2ecf20Sopenharmony_ci int ret = vgic_its_alloc_collection(its, &collection, coll_id); 10878c2ecf20Sopenharmony_ci if (ret) 10888c2ecf20Sopenharmony_ci return ret; 10898c2ecf20Sopenharmony_ci new_coll = collection; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci ite = vgic_its_alloc_ite(device, collection, event_id); 10938c2ecf20Sopenharmony_ci if (IS_ERR(ite)) { 10948c2ecf20Sopenharmony_ci if (new_coll) 10958c2ecf20Sopenharmony_ci vgic_its_free_collection(its, coll_id); 10968c2ecf20Sopenharmony_ci return PTR_ERR(ite); 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (its_is_collection_mapped(collection)) 11008c2ecf20Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, collection->target_addr); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci irq = vgic_add_lpi(kvm, lpi_nr, vcpu); 11038c2ecf20Sopenharmony_ci if (IS_ERR(irq)) { 11048c2ecf20Sopenharmony_ci if (new_coll) 11058c2ecf20Sopenharmony_ci vgic_its_free_collection(its, coll_id); 11068c2ecf20Sopenharmony_ci its_free_ite(kvm, ite); 11078c2ecf20Sopenharmony_ci return PTR_ERR(irq); 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci ite->irq = irq; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci return 0; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci/* Requires the its_lock to be held. */ 11158c2ecf20Sopenharmony_cistatic void vgic_its_free_device(struct kvm *kvm, struct its_device *device) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci struct its_ite *ite, *temp; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* 11208c2ecf20Sopenharmony_ci * The spec says that unmapping a device with still valid 11218c2ecf20Sopenharmony_ci * ITTEs associated is UNPREDICTABLE. We remove all ITTEs, 11228c2ecf20Sopenharmony_ci * since we cannot leave the memory unreferenced. 11238c2ecf20Sopenharmony_ci */ 11248c2ecf20Sopenharmony_ci list_for_each_entry_safe(ite, temp, &device->itt_head, ite_list) 11258c2ecf20Sopenharmony_ci its_free_ite(kvm, ite); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci vgic_its_invalidate_cache(kvm); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci list_del(&device->dev_list); 11308c2ecf20Sopenharmony_ci kfree(device); 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci/* its lock must be held */ 11348c2ecf20Sopenharmony_cistatic void vgic_its_free_device_list(struct kvm *kvm, struct vgic_its *its) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct its_device *cur, *temp; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci list_for_each_entry_safe(cur, temp, &its->device_list, dev_list) 11398c2ecf20Sopenharmony_ci vgic_its_free_device(kvm, cur); 11408c2ecf20Sopenharmony_ci} 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci/* its lock must be held */ 11438c2ecf20Sopenharmony_cistatic void vgic_its_free_collection_list(struct kvm *kvm, struct vgic_its *its) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct its_collection *cur, *temp; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci list_for_each_entry_safe(cur, temp, &its->collection_list, coll_list) 11488c2ecf20Sopenharmony_ci vgic_its_free_collection(its, cur->collection_id); 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci/* Must be called with its_lock mutex held */ 11528c2ecf20Sopenharmony_cistatic struct its_device *vgic_its_alloc_device(struct vgic_its *its, 11538c2ecf20Sopenharmony_ci u32 device_id, gpa_t itt_addr, 11548c2ecf20Sopenharmony_ci u8 num_eventid_bits) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci struct its_device *device; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci device = kzalloc(sizeof(*device), GFP_KERNEL); 11598c2ecf20Sopenharmony_ci if (!device) 11608c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci device->device_id = device_id; 11638c2ecf20Sopenharmony_ci device->itt_addr = itt_addr; 11648c2ecf20Sopenharmony_ci device->num_eventid_bits = num_eventid_bits; 11658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&device->itt_head); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci list_add_tail(&device->dev_list, &its->device_list); 11688c2ecf20Sopenharmony_ci return device; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci/* 11728c2ecf20Sopenharmony_ci * MAPD maps or unmaps a device ID to Interrupt Translation Tables (ITTs). 11738c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 11748c2ecf20Sopenharmony_ci */ 11758c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its, 11768c2ecf20Sopenharmony_ci u64 *its_cmd) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci u32 device_id = its_cmd_get_deviceid(its_cmd); 11798c2ecf20Sopenharmony_ci bool valid = its_cmd_get_validbit(its_cmd); 11808c2ecf20Sopenharmony_ci u8 num_eventid_bits = its_cmd_get_size(its_cmd); 11818c2ecf20Sopenharmony_ci gpa_t itt_addr = its_cmd_get_ittaddr(its_cmd); 11828c2ecf20Sopenharmony_ci struct its_device *device; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (!vgic_its_check_id(its, its->baser_device_table, device_id, NULL)) 11858c2ecf20Sopenharmony_ci return E_ITS_MAPD_DEVICE_OOR; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if (valid && num_eventid_bits > VITS_TYPER_IDBITS) 11888c2ecf20Sopenharmony_ci return E_ITS_MAPD_ITTSIZE_OOR; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci device = find_its_device(its, device_id); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* 11938c2ecf20Sopenharmony_ci * The spec says that calling MAPD on an already mapped device 11948c2ecf20Sopenharmony_ci * invalidates all cached data for this device. We implement this 11958c2ecf20Sopenharmony_ci * by removing the mapping and re-establishing it. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci if (device) 11988c2ecf20Sopenharmony_ci vgic_its_free_device(kvm, device); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* 12018c2ecf20Sopenharmony_ci * The spec does not say whether unmapping a not-mapped device 12028c2ecf20Sopenharmony_ci * is an error, so we are done in any case. 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci if (!valid) 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci device = vgic_its_alloc_device(its, device_id, itt_addr, 12088c2ecf20Sopenharmony_ci num_eventid_bits); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(device); 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci/* 12148c2ecf20Sopenharmony_ci * The MAPC command maps collection IDs to redistributors. 12158c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its, 12188c2ecf20Sopenharmony_ci u64 *its_cmd) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci u16 coll_id; 12218c2ecf20Sopenharmony_ci u32 target_addr; 12228c2ecf20Sopenharmony_ci struct its_collection *collection; 12238c2ecf20Sopenharmony_ci bool valid; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci valid = its_cmd_get_validbit(its_cmd); 12268c2ecf20Sopenharmony_ci coll_id = its_cmd_get_collection(its_cmd); 12278c2ecf20Sopenharmony_ci target_addr = its_cmd_get_target_addr(its_cmd); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (target_addr >= atomic_read(&kvm->online_vcpus)) 12308c2ecf20Sopenharmony_ci return E_ITS_MAPC_PROCNUM_OOR; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci if (!valid) { 12338c2ecf20Sopenharmony_ci vgic_its_free_collection(its, coll_id); 12348c2ecf20Sopenharmony_ci vgic_its_invalidate_cache(kvm); 12358c2ecf20Sopenharmony_ci } else { 12368c2ecf20Sopenharmony_ci collection = find_collection(its, coll_id); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (!collection) { 12398c2ecf20Sopenharmony_ci int ret; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ret = vgic_its_alloc_collection(its, &collection, 12428c2ecf20Sopenharmony_ci coll_id); 12438c2ecf20Sopenharmony_ci if (ret) 12448c2ecf20Sopenharmony_ci return ret; 12458c2ecf20Sopenharmony_ci collection->target_addr = target_addr; 12468c2ecf20Sopenharmony_ci } else { 12478c2ecf20Sopenharmony_ci collection->target_addr = target_addr; 12488c2ecf20Sopenharmony_ci update_affinity_collection(kvm, its, collection); 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci/* 12568c2ecf20Sopenharmony_ci * The CLEAR command removes the pending state for a particular LPI. 12578c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 12588c2ecf20Sopenharmony_ci */ 12598c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_clear(struct kvm *kvm, struct vgic_its *its, 12608c2ecf20Sopenharmony_ci u64 *its_cmd) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci u32 device_id = its_cmd_get_deviceid(its_cmd); 12638c2ecf20Sopenharmony_ci u32 event_id = its_cmd_get_id(its_cmd); 12648c2ecf20Sopenharmony_ci struct its_ite *ite; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci ite = find_ite(its, device_id, event_id); 12688c2ecf20Sopenharmony_ci if (!ite) 12698c2ecf20Sopenharmony_ci return E_ITS_CLEAR_UNMAPPED_INTERRUPT; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci ite->irq->pending_latch = false; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (ite->irq->hw) 12748c2ecf20Sopenharmony_ci return irq_set_irqchip_state(ite->irq->host_irq, 12758c2ecf20Sopenharmony_ci IRQCHIP_STATE_PENDING, false); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci return 0; 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci/* 12818c2ecf20Sopenharmony_ci * The INV command syncs the configuration bits from the memory table. 12828c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 12838c2ecf20Sopenharmony_ci */ 12848c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its, 12858c2ecf20Sopenharmony_ci u64 *its_cmd) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci u32 device_id = its_cmd_get_deviceid(its_cmd); 12888c2ecf20Sopenharmony_ci u32 event_id = its_cmd_get_id(its_cmd); 12898c2ecf20Sopenharmony_ci struct its_ite *ite; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci ite = find_ite(its, device_id, event_id); 12938c2ecf20Sopenharmony_ci if (!ite) 12948c2ecf20Sopenharmony_ci return E_ITS_INV_UNMAPPED_INTERRUPT; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci return update_lpi_config(kvm, ite->irq, NULL, true); 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci/* 13008c2ecf20Sopenharmony_ci * The INVALL command requests flushing of all IRQ data in this collection. 13018c2ecf20Sopenharmony_ci * Find the VCPU mapped to that collection, then iterate over the VM's list 13028c2ecf20Sopenharmony_ci * of mapped LPIs and update the configuration for each IRQ which targets 13038c2ecf20Sopenharmony_ci * the specified vcpu. The configuration will be read from the in-memory 13048c2ecf20Sopenharmony_ci * configuration table. 13058c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 13068c2ecf20Sopenharmony_ci */ 13078c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its, 13088c2ecf20Sopenharmony_ci u64 *its_cmd) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci u32 coll_id = its_cmd_get_collection(its_cmd); 13118c2ecf20Sopenharmony_ci struct its_collection *collection; 13128c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 13138c2ecf20Sopenharmony_ci struct vgic_irq *irq; 13148c2ecf20Sopenharmony_ci u32 *intids; 13158c2ecf20Sopenharmony_ci int irq_count, i; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci collection = find_collection(its, coll_id); 13188c2ecf20Sopenharmony_ci if (!its_is_collection_mapped(collection)) 13198c2ecf20Sopenharmony_ci return E_ITS_INVALL_UNMAPPED_COLLECTION; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, collection->target_addr); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci irq_count = vgic_copy_lpi_list(kvm, vcpu, &intids); 13248c2ecf20Sopenharmony_ci if (irq_count < 0) 13258c2ecf20Sopenharmony_ci return irq_count; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci for (i = 0; i < irq_count; i++) { 13288c2ecf20Sopenharmony_ci irq = vgic_get_irq(kvm, NULL, intids[i]); 13298c2ecf20Sopenharmony_ci if (!irq) 13308c2ecf20Sopenharmony_ci continue; 13318c2ecf20Sopenharmony_ci update_lpi_config(kvm, irq, vcpu, false); 13328c2ecf20Sopenharmony_ci vgic_put_irq(kvm, irq); 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci kfree(intids); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (vcpu->arch.vgic_cpu.vgic_v3.its_vpe.its_vm) 13388c2ecf20Sopenharmony_ci its_invall_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci return 0; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci/* 13448c2ecf20Sopenharmony_ci * The MOVALL command moves the pending state of all IRQs targeting one 13458c2ecf20Sopenharmony_ci * redistributor to another. We don't hold the pending state in the VCPUs, 13468c2ecf20Sopenharmony_ci * but in the IRQs instead, so there is really not much to do for us here. 13478c2ecf20Sopenharmony_ci * However the spec says that no IRQ must target the old redistributor 13488c2ecf20Sopenharmony_ci * afterwards, so we make sure that no LPI is using the associated target_vcpu. 13498c2ecf20Sopenharmony_ci * This command affects all LPIs in the system that target that redistributor. 13508c2ecf20Sopenharmony_ci */ 13518c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its, 13528c2ecf20Sopenharmony_ci u64 *its_cmd) 13538c2ecf20Sopenharmony_ci{ 13548c2ecf20Sopenharmony_ci u32 target1_addr = its_cmd_get_target_addr(its_cmd); 13558c2ecf20Sopenharmony_ci u32 target2_addr = its_cmd_mask_field(its_cmd, 3, 16, 32); 13568c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu1, *vcpu2; 13578c2ecf20Sopenharmony_ci struct vgic_irq *irq; 13588c2ecf20Sopenharmony_ci u32 *intids; 13598c2ecf20Sopenharmony_ci int irq_count, i; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (target1_addr >= atomic_read(&kvm->online_vcpus) || 13628c2ecf20Sopenharmony_ci target2_addr >= atomic_read(&kvm->online_vcpus)) 13638c2ecf20Sopenharmony_ci return E_ITS_MOVALL_PROCNUM_OOR; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci if (target1_addr == target2_addr) 13668c2ecf20Sopenharmony_ci return 0; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci vcpu1 = kvm_get_vcpu(kvm, target1_addr); 13698c2ecf20Sopenharmony_ci vcpu2 = kvm_get_vcpu(kvm, target2_addr); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci irq_count = vgic_copy_lpi_list(kvm, vcpu1, &intids); 13728c2ecf20Sopenharmony_ci if (irq_count < 0) 13738c2ecf20Sopenharmony_ci return irq_count; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci for (i = 0; i < irq_count; i++) { 13768c2ecf20Sopenharmony_ci irq = vgic_get_irq(kvm, NULL, intids[i]); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci update_affinity(irq, vcpu2); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci vgic_put_irq(kvm, irq); 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci vgic_its_invalidate_cache(kvm); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci kfree(intids); 13868c2ecf20Sopenharmony_ci return 0; 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci/* 13908c2ecf20Sopenharmony_ci * The INT command injects the LPI associated with that DevID/EvID pair. 13918c2ecf20Sopenharmony_ci * Must be called with the its_lock mutex held. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_cistatic int vgic_its_cmd_handle_int(struct kvm *kvm, struct vgic_its *its, 13948c2ecf20Sopenharmony_ci u64 *its_cmd) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci u32 msi_data = its_cmd_get_id(its_cmd); 13978c2ecf20Sopenharmony_ci u64 msi_devid = its_cmd_get_deviceid(its_cmd); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci return vgic_its_trigger_msi(kvm, its, msi_devid, msi_data); 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci/* 14038c2ecf20Sopenharmony_ci * This function is called with the its_cmd lock held, but the ITS data 14048c2ecf20Sopenharmony_ci * structure lock dropped. 14058c2ecf20Sopenharmony_ci */ 14068c2ecf20Sopenharmony_cistatic int vgic_its_handle_command(struct kvm *kvm, struct vgic_its *its, 14078c2ecf20Sopenharmony_ci u64 *its_cmd) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci int ret = -ENODEV; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci mutex_lock(&its->its_lock); 14128c2ecf20Sopenharmony_ci switch (its_cmd_get_command(its_cmd)) { 14138c2ecf20Sopenharmony_ci case GITS_CMD_MAPD: 14148c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_mapd(kvm, its, its_cmd); 14158c2ecf20Sopenharmony_ci break; 14168c2ecf20Sopenharmony_ci case GITS_CMD_MAPC: 14178c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_mapc(kvm, its, its_cmd); 14188c2ecf20Sopenharmony_ci break; 14198c2ecf20Sopenharmony_ci case GITS_CMD_MAPI: 14208c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_mapi(kvm, its, its_cmd); 14218c2ecf20Sopenharmony_ci break; 14228c2ecf20Sopenharmony_ci case GITS_CMD_MAPTI: 14238c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_mapi(kvm, its, its_cmd); 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci case GITS_CMD_MOVI: 14268c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_movi(kvm, its, its_cmd); 14278c2ecf20Sopenharmony_ci break; 14288c2ecf20Sopenharmony_ci case GITS_CMD_DISCARD: 14298c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_discard(kvm, its, its_cmd); 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci case GITS_CMD_CLEAR: 14328c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_clear(kvm, its, its_cmd); 14338c2ecf20Sopenharmony_ci break; 14348c2ecf20Sopenharmony_ci case GITS_CMD_MOVALL: 14358c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_movall(kvm, its, its_cmd); 14368c2ecf20Sopenharmony_ci break; 14378c2ecf20Sopenharmony_ci case GITS_CMD_INT: 14388c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_int(kvm, its, its_cmd); 14398c2ecf20Sopenharmony_ci break; 14408c2ecf20Sopenharmony_ci case GITS_CMD_INV: 14418c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_inv(kvm, its, its_cmd); 14428c2ecf20Sopenharmony_ci break; 14438c2ecf20Sopenharmony_ci case GITS_CMD_INVALL: 14448c2ecf20Sopenharmony_ci ret = vgic_its_cmd_handle_invall(kvm, its, its_cmd); 14458c2ecf20Sopenharmony_ci break; 14468c2ecf20Sopenharmony_ci case GITS_CMD_SYNC: 14478c2ecf20Sopenharmony_ci /* we ignore this command: we are in sync all of the time */ 14488c2ecf20Sopenharmony_ci ret = 0; 14498c2ecf20Sopenharmony_ci break; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci mutex_unlock(&its->its_lock); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci return ret; 14548c2ecf20Sopenharmony_ci} 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_cistatic u64 vgic_sanitise_its_baser(u64 reg) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci reg = vgic_sanitise_field(reg, GITS_BASER_SHAREABILITY_MASK, 14598c2ecf20Sopenharmony_ci GITS_BASER_SHAREABILITY_SHIFT, 14608c2ecf20Sopenharmony_ci vgic_sanitise_shareability); 14618c2ecf20Sopenharmony_ci reg = vgic_sanitise_field(reg, GITS_BASER_INNER_CACHEABILITY_MASK, 14628c2ecf20Sopenharmony_ci GITS_BASER_INNER_CACHEABILITY_SHIFT, 14638c2ecf20Sopenharmony_ci vgic_sanitise_inner_cacheability); 14648c2ecf20Sopenharmony_ci reg = vgic_sanitise_field(reg, GITS_BASER_OUTER_CACHEABILITY_MASK, 14658c2ecf20Sopenharmony_ci GITS_BASER_OUTER_CACHEABILITY_SHIFT, 14668c2ecf20Sopenharmony_ci vgic_sanitise_outer_cacheability); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci /* We support only one (ITS) page size: 64K */ 14698c2ecf20Sopenharmony_ci reg = (reg & ~GITS_BASER_PAGE_SIZE_MASK) | GITS_BASER_PAGE_SIZE_64K; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci return reg; 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic u64 vgic_sanitise_its_cbaser(u64 reg) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci reg = vgic_sanitise_field(reg, GITS_CBASER_SHAREABILITY_MASK, 14778c2ecf20Sopenharmony_ci GITS_CBASER_SHAREABILITY_SHIFT, 14788c2ecf20Sopenharmony_ci vgic_sanitise_shareability); 14798c2ecf20Sopenharmony_ci reg = vgic_sanitise_field(reg, GITS_CBASER_INNER_CACHEABILITY_MASK, 14808c2ecf20Sopenharmony_ci GITS_CBASER_INNER_CACHEABILITY_SHIFT, 14818c2ecf20Sopenharmony_ci vgic_sanitise_inner_cacheability); 14828c2ecf20Sopenharmony_ci reg = vgic_sanitise_field(reg, GITS_CBASER_OUTER_CACHEABILITY_MASK, 14838c2ecf20Sopenharmony_ci GITS_CBASER_OUTER_CACHEABILITY_SHIFT, 14848c2ecf20Sopenharmony_ci vgic_sanitise_outer_cacheability); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci /* Sanitise the physical address to be 64k aligned. */ 14878c2ecf20Sopenharmony_ci reg &= ~GENMASK_ULL(15, 12); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci return reg; 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_cbaser(struct kvm *kvm, 14938c2ecf20Sopenharmony_ci struct vgic_its *its, 14948c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci return extract_bytes(its->cbaser, addr & 7, len); 14978c2ecf20Sopenharmony_ci} 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_cistatic void vgic_mmio_write_its_cbaser(struct kvm *kvm, struct vgic_its *its, 15008c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 15018c2ecf20Sopenharmony_ci unsigned long val) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci /* When GITS_CTLR.Enable is 1, this register is RO. */ 15048c2ecf20Sopenharmony_ci if (its->enabled) 15058c2ecf20Sopenharmony_ci return; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci mutex_lock(&its->cmd_lock); 15088c2ecf20Sopenharmony_ci its->cbaser = update_64bit_reg(its->cbaser, addr & 7, len, val); 15098c2ecf20Sopenharmony_ci its->cbaser = vgic_sanitise_its_cbaser(its->cbaser); 15108c2ecf20Sopenharmony_ci its->creadr = 0; 15118c2ecf20Sopenharmony_ci /* 15128c2ecf20Sopenharmony_ci * CWRITER is architecturally UNKNOWN on reset, but we need to reset 15138c2ecf20Sopenharmony_ci * it to CREADR to make sure we start with an empty command buffer. 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_ci its->cwriter = its->creadr; 15168c2ecf20Sopenharmony_ci mutex_unlock(&its->cmd_lock); 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci#define ITS_CMD_BUFFER_SIZE(baser) ((((baser) & 0xff) + 1) << 12) 15208c2ecf20Sopenharmony_ci#define ITS_CMD_SIZE 32 15218c2ecf20Sopenharmony_ci#define ITS_CMD_OFFSET(reg) ((reg) & GENMASK(19, 5)) 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci/* Must be called with the cmd_lock held. */ 15248c2ecf20Sopenharmony_cistatic void vgic_its_process_commands(struct kvm *kvm, struct vgic_its *its) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci gpa_t cbaser; 15278c2ecf20Sopenharmony_ci u64 cmd_buf[4]; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci /* Commands are only processed when the ITS is enabled. */ 15308c2ecf20Sopenharmony_ci if (!its->enabled) 15318c2ecf20Sopenharmony_ci return; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci cbaser = GITS_CBASER_ADDRESS(its->cbaser); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci while (its->cwriter != its->creadr) { 15368c2ecf20Sopenharmony_ci int ret = kvm_read_guest_lock(kvm, cbaser + its->creadr, 15378c2ecf20Sopenharmony_ci cmd_buf, ITS_CMD_SIZE); 15388c2ecf20Sopenharmony_ci /* 15398c2ecf20Sopenharmony_ci * If kvm_read_guest() fails, this could be due to the guest 15408c2ecf20Sopenharmony_ci * programming a bogus value in CBASER or something else going 15418c2ecf20Sopenharmony_ci * wrong from which we cannot easily recover. 15428c2ecf20Sopenharmony_ci * According to section 6.3.2 in the GICv3 spec we can just 15438c2ecf20Sopenharmony_ci * ignore that command then. 15448c2ecf20Sopenharmony_ci */ 15458c2ecf20Sopenharmony_ci if (!ret) 15468c2ecf20Sopenharmony_ci vgic_its_handle_command(kvm, its, cmd_buf); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci its->creadr += ITS_CMD_SIZE; 15498c2ecf20Sopenharmony_ci if (its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser)) 15508c2ecf20Sopenharmony_ci its->creadr = 0; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci/* 15558c2ecf20Sopenharmony_ci * By writing to CWRITER the guest announces new commands to be processed. 15568c2ecf20Sopenharmony_ci * To avoid any races in the first place, we take the its_cmd lock, which 15578c2ecf20Sopenharmony_ci * protects our ring buffer variables, so that there is only one user 15588c2ecf20Sopenharmony_ci * per ITS handling commands at a given time. 15598c2ecf20Sopenharmony_ci */ 15608c2ecf20Sopenharmony_cistatic void vgic_mmio_write_its_cwriter(struct kvm *kvm, struct vgic_its *its, 15618c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 15628c2ecf20Sopenharmony_ci unsigned long val) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci u64 reg; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if (!its) 15678c2ecf20Sopenharmony_ci return; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci mutex_lock(&its->cmd_lock); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci reg = update_64bit_reg(its->cwriter, addr & 7, len, val); 15728c2ecf20Sopenharmony_ci reg = ITS_CMD_OFFSET(reg); 15738c2ecf20Sopenharmony_ci if (reg >= ITS_CMD_BUFFER_SIZE(its->cbaser)) { 15748c2ecf20Sopenharmony_ci mutex_unlock(&its->cmd_lock); 15758c2ecf20Sopenharmony_ci return; 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci its->cwriter = reg; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci vgic_its_process_commands(kvm, its); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci mutex_unlock(&its->cmd_lock); 15828c2ecf20Sopenharmony_ci} 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_cwriter(struct kvm *kvm, 15858c2ecf20Sopenharmony_ci struct vgic_its *its, 15868c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci return extract_bytes(its->cwriter, addr & 0x7, len); 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_creadr(struct kvm *kvm, 15928c2ecf20Sopenharmony_ci struct vgic_its *its, 15938c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci return extract_bytes(its->creadr, addr & 0x7, len); 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic int vgic_mmio_uaccess_write_its_creadr(struct kvm *kvm, 15998c2ecf20Sopenharmony_ci struct vgic_its *its, 16008c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 16018c2ecf20Sopenharmony_ci unsigned long val) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci u32 cmd_offset; 16048c2ecf20Sopenharmony_ci int ret = 0; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci mutex_lock(&its->cmd_lock); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci if (its->enabled) { 16098c2ecf20Sopenharmony_ci ret = -EBUSY; 16108c2ecf20Sopenharmony_ci goto out; 16118c2ecf20Sopenharmony_ci } 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci cmd_offset = ITS_CMD_OFFSET(val); 16148c2ecf20Sopenharmony_ci if (cmd_offset >= ITS_CMD_BUFFER_SIZE(its->cbaser)) { 16158c2ecf20Sopenharmony_ci ret = -EINVAL; 16168c2ecf20Sopenharmony_ci goto out; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci its->creadr = cmd_offset; 16208c2ecf20Sopenharmony_ciout: 16218c2ecf20Sopenharmony_ci mutex_unlock(&its->cmd_lock); 16228c2ecf20Sopenharmony_ci return ret; 16238c2ecf20Sopenharmony_ci} 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci#define BASER_INDEX(addr) (((addr) / sizeof(u64)) & 0x7) 16268c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_baser(struct kvm *kvm, 16278c2ecf20Sopenharmony_ci struct vgic_its *its, 16288c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci u64 reg; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci switch (BASER_INDEX(addr)) { 16338c2ecf20Sopenharmony_ci case 0: 16348c2ecf20Sopenharmony_ci reg = its->baser_device_table; 16358c2ecf20Sopenharmony_ci break; 16368c2ecf20Sopenharmony_ci case 1: 16378c2ecf20Sopenharmony_ci reg = its->baser_coll_table; 16388c2ecf20Sopenharmony_ci break; 16398c2ecf20Sopenharmony_ci default: 16408c2ecf20Sopenharmony_ci reg = 0; 16418c2ecf20Sopenharmony_ci break; 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci return extract_bytes(reg, addr & 7, len); 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci#define GITS_BASER_RO_MASK (GENMASK_ULL(52, 48) | GENMASK_ULL(58, 56)) 16488c2ecf20Sopenharmony_cistatic void vgic_mmio_write_its_baser(struct kvm *kvm, 16498c2ecf20Sopenharmony_ci struct vgic_its *its, 16508c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 16518c2ecf20Sopenharmony_ci unsigned long val) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 16548c2ecf20Sopenharmony_ci u64 entry_size, table_type; 16558c2ecf20Sopenharmony_ci u64 reg, *regptr, clearbits = 0; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci /* When GITS_CTLR.Enable is 1, we ignore write accesses. */ 16588c2ecf20Sopenharmony_ci if (its->enabled) 16598c2ecf20Sopenharmony_ci return; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci switch (BASER_INDEX(addr)) { 16628c2ecf20Sopenharmony_ci case 0: 16638c2ecf20Sopenharmony_ci regptr = &its->baser_device_table; 16648c2ecf20Sopenharmony_ci entry_size = abi->dte_esz; 16658c2ecf20Sopenharmony_ci table_type = GITS_BASER_TYPE_DEVICE; 16668c2ecf20Sopenharmony_ci break; 16678c2ecf20Sopenharmony_ci case 1: 16688c2ecf20Sopenharmony_ci regptr = &its->baser_coll_table; 16698c2ecf20Sopenharmony_ci entry_size = abi->cte_esz; 16708c2ecf20Sopenharmony_ci table_type = GITS_BASER_TYPE_COLLECTION; 16718c2ecf20Sopenharmony_ci clearbits = GITS_BASER_INDIRECT; 16728c2ecf20Sopenharmony_ci break; 16738c2ecf20Sopenharmony_ci default: 16748c2ecf20Sopenharmony_ci return; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci reg = update_64bit_reg(*regptr, addr & 7, len, val); 16788c2ecf20Sopenharmony_ci reg &= ~GITS_BASER_RO_MASK; 16798c2ecf20Sopenharmony_ci reg &= ~clearbits; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci reg |= (entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT; 16828c2ecf20Sopenharmony_ci reg |= table_type << GITS_BASER_TYPE_SHIFT; 16838c2ecf20Sopenharmony_ci reg = vgic_sanitise_its_baser(reg); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci *regptr = reg; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci if (!(reg & GITS_BASER_VALID)) { 16888c2ecf20Sopenharmony_ci /* Take the its_lock to prevent a race with a save/restore */ 16898c2ecf20Sopenharmony_ci mutex_lock(&its->its_lock); 16908c2ecf20Sopenharmony_ci switch (table_type) { 16918c2ecf20Sopenharmony_ci case GITS_BASER_TYPE_DEVICE: 16928c2ecf20Sopenharmony_ci vgic_its_free_device_list(kvm, its); 16938c2ecf20Sopenharmony_ci break; 16948c2ecf20Sopenharmony_ci case GITS_BASER_TYPE_COLLECTION: 16958c2ecf20Sopenharmony_ci vgic_its_free_collection_list(kvm, its); 16968c2ecf20Sopenharmony_ci break; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci mutex_unlock(&its->its_lock); 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci} 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_its_ctlr(struct kvm *vcpu, 17038c2ecf20Sopenharmony_ci struct vgic_its *its, 17048c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 17058c2ecf20Sopenharmony_ci{ 17068c2ecf20Sopenharmony_ci u32 reg = 0; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci mutex_lock(&its->cmd_lock); 17098c2ecf20Sopenharmony_ci if (its->creadr == its->cwriter) 17108c2ecf20Sopenharmony_ci reg |= GITS_CTLR_QUIESCENT; 17118c2ecf20Sopenharmony_ci if (its->enabled) 17128c2ecf20Sopenharmony_ci reg |= GITS_CTLR_ENABLE; 17138c2ecf20Sopenharmony_ci mutex_unlock(&its->cmd_lock); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci return reg; 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_cistatic void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its, 17198c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 17208c2ecf20Sopenharmony_ci unsigned long val) 17218c2ecf20Sopenharmony_ci{ 17228c2ecf20Sopenharmony_ci mutex_lock(&its->cmd_lock); 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci /* 17258c2ecf20Sopenharmony_ci * It is UNPREDICTABLE to enable the ITS if any of the CBASER or 17268c2ecf20Sopenharmony_ci * device/collection BASER are invalid 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_ci if (!its->enabled && (val & GITS_CTLR_ENABLE) && 17298c2ecf20Sopenharmony_ci (!(its->baser_device_table & GITS_BASER_VALID) || 17308c2ecf20Sopenharmony_ci !(its->baser_coll_table & GITS_BASER_VALID) || 17318c2ecf20Sopenharmony_ci !(its->cbaser & GITS_CBASER_VALID))) 17328c2ecf20Sopenharmony_ci goto out; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci its->enabled = !!(val & GITS_CTLR_ENABLE); 17358c2ecf20Sopenharmony_ci if (!its->enabled) 17368c2ecf20Sopenharmony_ci vgic_its_invalidate_cache(kvm); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci /* 17398c2ecf20Sopenharmony_ci * Try to process any pending commands. This function bails out early 17408c2ecf20Sopenharmony_ci * if the ITS is disabled or no commands have been queued. 17418c2ecf20Sopenharmony_ci */ 17428c2ecf20Sopenharmony_ci vgic_its_process_commands(kvm, its); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ciout: 17458c2ecf20Sopenharmony_ci mutex_unlock(&its->cmd_lock); 17468c2ecf20Sopenharmony_ci} 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci#define REGISTER_ITS_DESC(off, rd, wr, length, acc) \ 17498c2ecf20Sopenharmony_ci{ \ 17508c2ecf20Sopenharmony_ci .reg_offset = off, \ 17518c2ecf20Sopenharmony_ci .len = length, \ 17528c2ecf20Sopenharmony_ci .access_flags = acc, \ 17538c2ecf20Sopenharmony_ci .its_read = rd, \ 17548c2ecf20Sopenharmony_ci .its_write = wr, \ 17558c2ecf20Sopenharmony_ci} 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci#define REGISTER_ITS_DESC_UACCESS(off, rd, wr, uwr, length, acc)\ 17588c2ecf20Sopenharmony_ci{ \ 17598c2ecf20Sopenharmony_ci .reg_offset = off, \ 17608c2ecf20Sopenharmony_ci .len = length, \ 17618c2ecf20Sopenharmony_ci .access_flags = acc, \ 17628c2ecf20Sopenharmony_ci .its_read = rd, \ 17638c2ecf20Sopenharmony_ci .its_write = wr, \ 17648c2ecf20Sopenharmony_ci .uaccess_its_write = uwr, \ 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_cistatic void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its, 17688c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, unsigned long val) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci /* Ignore */ 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic struct vgic_register_region its_registers[] = { 17748c2ecf20Sopenharmony_ci REGISTER_ITS_DESC(GITS_CTLR, 17758c2ecf20Sopenharmony_ci vgic_mmio_read_its_ctlr, vgic_mmio_write_its_ctlr, 4, 17768c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 17778c2ecf20Sopenharmony_ci REGISTER_ITS_DESC_UACCESS(GITS_IIDR, 17788c2ecf20Sopenharmony_ci vgic_mmio_read_its_iidr, its_mmio_write_wi, 17798c2ecf20Sopenharmony_ci vgic_mmio_uaccess_write_its_iidr, 4, 17808c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 17818c2ecf20Sopenharmony_ci REGISTER_ITS_DESC(GITS_TYPER, 17828c2ecf20Sopenharmony_ci vgic_mmio_read_its_typer, its_mmio_write_wi, 8, 17838c2ecf20Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 17848c2ecf20Sopenharmony_ci REGISTER_ITS_DESC(GITS_CBASER, 17858c2ecf20Sopenharmony_ci vgic_mmio_read_its_cbaser, vgic_mmio_write_its_cbaser, 8, 17868c2ecf20Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 17878c2ecf20Sopenharmony_ci REGISTER_ITS_DESC(GITS_CWRITER, 17888c2ecf20Sopenharmony_ci vgic_mmio_read_its_cwriter, vgic_mmio_write_its_cwriter, 8, 17898c2ecf20Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 17908c2ecf20Sopenharmony_ci REGISTER_ITS_DESC_UACCESS(GITS_CREADR, 17918c2ecf20Sopenharmony_ci vgic_mmio_read_its_creadr, its_mmio_write_wi, 17928c2ecf20Sopenharmony_ci vgic_mmio_uaccess_write_its_creadr, 8, 17938c2ecf20Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 17948c2ecf20Sopenharmony_ci REGISTER_ITS_DESC(GITS_BASER, 17958c2ecf20Sopenharmony_ci vgic_mmio_read_its_baser, vgic_mmio_write_its_baser, 0x40, 17968c2ecf20Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 17978c2ecf20Sopenharmony_ci REGISTER_ITS_DESC(GITS_IDREGS_BASE, 17988c2ecf20Sopenharmony_ci vgic_mmio_read_its_idregs, its_mmio_write_wi, 0x30, 17998c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 18008c2ecf20Sopenharmony_ci}; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci/* This is called on setting the LPI enable bit in the redistributor. */ 18038c2ecf20Sopenharmony_civoid vgic_enable_lpis(struct kvm_vcpu *vcpu) 18048c2ecf20Sopenharmony_ci{ 18058c2ecf20Sopenharmony_ci if (!(vcpu->arch.vgic_cpu.pendbaser & GICR_PENDBASER_PTZ)) 18068c2ecf20Sopenharmony_ci its_sync_lpi_pending_table(vcpu); 18078c2ecf20Sopenharmony_ci} 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_cistatic int vgic_register_its_iodev(struct kvm *kvm, struct vgic_its *its, 18108c2ecf20Sopenharmony_ci u64 addr) 18118c2ecf20Sopenharmony_ci{ 18128c2ecf20Sopenharmony_ci struct vgic_io_device *iodev = &its->iodev; 18138c2ecf20Sopenharmony_ci int ret; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci mutex_lock(&kvm->slots_lock); 18168c2ecf20Sopenharmony_ci if (!IS_VGIC_ADDR_UNDEF(its->vgic_its_base)) { 18178c2ecf20Sopenharmony_ci ret = -EBUSY; 18188c2ecf20Sopenharmony_ci goto out; 18198c2ecf20Sopenharmony_ci } 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci its->vgic_its_base = addr; 18228c2ecf20Sopenharmony_ci iodev->regions = its_registers; 18238c2ecf20Sopenharmony_ci iodev->nr_regions = ARRAY_SIZE(its_registers); 18248c2ecf20Sopenharmony_ci kvm_iodevice_init(&iodev->dev, &kvm_io_gic_ops); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci iodev->base_addr = its->vgic_its_base; 18278c2ecf20Sopenharmony_ci iodev->iodev_type = IODEV_ITS; 18288c2ecf20Sopenharmony_ci iodev->its = its; 18298c2ecf20Sopenharmony_ci ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, iodev->base_addr, 18308c2ecf20Sopenharmony_ci KVM_VGIC_V3_ITS_SIZE, &iodev->dev); 18318c2ecf20Sopenharmony_ciout: 18328c2ecf20Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci return ret; 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci/* Default is 16 cached LPIs per vcpu */ 18388c2ecf20Sopenharmony_ci#define LPI_DEFAULT_PCPU_CACHE_SIZE 16 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_civoid vgic_lpi_translation_cache_init(struct kvm *kvm) 18418c2ecf20Sopenharmony_ci{ 18428c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 18438c2ecf20Sopenharmony_ci unsigned int sz; 18448c2ecf20Sopenharmony_ci int i; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (!list_empty(&dist->lpi_translation_cache)) 18478c2ecf20Sopenharmony_ci return; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci sz = atomic_read(&kvm->online_vcpus) * LPI_DEFAULT_PCPU_CACHE_SIZE; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci for (i = 0; i < sz; i++) { 18528c2ecf20Sopenharmony_ci struct vgic_translation_cache_entry *cte; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci /* An allocation failure is not fatal */ 18558c2ecf20Sopenharmony_ci cte = kzalloc(sizeof(*cte), GFP_KERNEL); 18568c2ecf20Sopenharmony_ci if (WARN_ON(!cte)) 18578c2ecf20Sopenharmony_ci break; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cte->entry); 18608c2ecf20Sopenharmony_ci list_add(&cte->entry, &dist->lpi_translation_cache); 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci} 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_civoid vgic_lpi_translation_cache_destroy(struct kvm *kvm) 18658c2ecf20Sopenharmony_ci{ 18668c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 18678c2ecf20Sopenharmony_ci struct vgic_translation_cache_entry *cte, *tmp; 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci vgic_its_invalidate_cache(kvm); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci list_for_each_entry_safe(cte, tmp, 18728c2ecf20Sopenharmony_ci &dist->lpi_translation_cache, entry) { 18738c2ecf20Sopenharmony_ci list_del(&cte->entry); 18748c2ecf20Sopenharmony_ci kfree(cte); 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci} 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci#define INITIAL_BASER_VALUE \ 18798c2ecf20Sopenharmony_ci (GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb) | \ 18808c2ecf20Sopenharmony_ci GIC_BASER_CACHEABILITY(GITS_BASER, OUTER, SameAsInner) | \ 18818c2ecf20Sopenharmony_ci GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable) | \ 18828c2ecf20Sopenharmony_ci GITS_BASER_PAGE_SIZE_64K) 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci#define INITIAL_PROPBASER_VALUE \ 18858c2ecf20Sopenharmony_ci (GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWb) | \ 18868c2ecf20Sopenharmony_ci GIC_BASER_CACHEABILITY(GICR_PROPBASER, OUTER, SameAsInner) | \ 18878c2ecf20Sopenharmony_ci GIC_BASER_SHAREABILITY(GICR_PROPBASER, InnerShareable)) 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_cistatic int vgic_its_create(struct kvm_device *dev, u32 type) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci struct vgic_its *its; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci if (type != KVM_DEV_TYPE_ARM_VGIC_ITS) 18948c2ecf20Sopenharmony_ci return -ENODEV; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci its = kzalloc(sizeof(struct vgic_its), GFP_KERNEL); 18978c2ecf20Sopenharmony_ci if (!its) 18988c2ecf20Sopenharmony_ci return -ENOMEM; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci if (vgic_initialized(dev->kvm)) { 19018c2ecf20Sopenharmony_ci int ret = vgic_v4_init(dev->kvm); 19028c2ecf20Sopenharmony_ci if (ret < 0) { 19038c2ecf20Sopenharmony_ci kfree(its); 19048c2ecf20Sopenharmony_ci return ret; 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci vgic_lpi_translation_cache_init(dev->kvm); 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci mutex_init(&its->its_lock); 19118c2ecf20Sopenharmony_ci mutex_init(&its->cmd_lock); 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci its->vgic_its_base = VGIC_ADDR_UNDEF; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&its->device_list); 19168c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&its->collection_list); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci dev->kvm->arch.vgic.msis_require_devid = true; 19198c2ecf20Sopenharmony_ci dev->kvm->arch.vgic.has_its = true; 19208c2ecf20Sopenharmony_ci its->enabled = false; 19218c2ecf20Sopenharmony_ci its->dev = dev; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci its->baser_device_table = INITIAL_BASER_VALUE | 19248c2ecf20Sopenharmony_ci ((u64)GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT); 19258c2ecf20Sopenharmony_ci its->baser_coll_table = INITIAL_BASER_VALUE | 19268c2ecf20Sopenharmony_ci ((u64)GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT); 19278c2ecf20Sopenharmony_ci dev->kvm->arch.vgic.propbaser = INITIAL_PROPBASER_VALUE; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci dev->private = its; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci return vgic_its_set_abi(its, NR_ITS_ABIS - 1); 19328c2ecf20Sopenharmony_ci} 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_cistatic void vgic_its_destroy(struct kvm_device *kvm_dev) 19358c2ecf20Sopenharmony_ci{ 19368c2ecf20Sopenharmony_ci struct kvm *kvm = kvm_dev->kvm; 19378c2ecf20Sopenharmony_ci struct vgic_its *its = kvm_dev->private; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci mutex_lock(&its->its_lock); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci vgic_its_free_device_list(kvm, its); 19428c2ecf20Sopenharmony_ci vgic_its_free_collection_list(kvm, its); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci mutex_unlock(&its->its_lock); 19458c2ecf20Sopenharmony_ci kfree(its); 19468c2ecf20Sopenharmony_ci kfree(kvm_dev);/* alloc by kvm_ioctl_create_device, free by .destroy */ 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic int vgic_its_has_attr_regs(struct kvm_device *dev, 19508c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 19518c2ecf20Sopenharmony_ci{ 19528c2ecf20Sopenharmony_ci const struct vgic_register_region *region; 19538c2ecf20Sopenharmony_ci gpa_t offset = attr->attr; 19548c2ecf20Sopenharmony_ci int align; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci align = (offset < GITS_TYPER) || (offset >= GITS_PIDR4) ? 0x3 : 0x7; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (offset & align) 19598c2ecf20Sopenharmony_ci return -EINVAL; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci region = vgic_find_mmio_region(its_registers, 19628c2ecf20Sopenharmony_ci ARRAY_SIZE(its_registers), 19638c2ecf20Sopenharmony_ci offset); 19648c2ecf20Sopenharmony_ci if (!region) 19658c2ecf20Sopenharmony_ci return -ENXIO; 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci return 0; 19688c2ecf20Sopenharmony_ci} 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_cistatic int vgic_its_attr_regs_access(struct kvm_device *dev, 19718c2ecf20Sopenharmony_ci struct kvm_device_attr *attr, 19728c2ecf20Sopenharmony_ci u64 *reg, bool is_write) 19738c2ecf20Sopenharmony_ci{ 19748c2ecf20Sopenharmony_ci const struct vgic_register_region *region; 19758c2ecf20Sopenharmony_ci struct vgic_its *its; 19768c2ecf20Sopenharmony_ci gpa_t addr, offset; 19778c2ecf20Sopenharmony_ci unsigned int len; 19788c2ecf20Sopenharmony_ci int align, ret = 0; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci its = dev->private; 19818c2ecf20Sopenharmony_ci offset = attr->attr; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci /* 19848c2ecf20Sopenharmony_ci * Although the spec supports upper/lower 32-bit accesses to 19858c2ecf20Sopenharmony_ci * 64-bit ITS registers, the userspace ABI requires 64-bit 19868c2ecf20Sopenharmony_ci * accesses to all 64-bit wide registers. We therefore only 19878c2ecf20Sopenharmony_ci * support 32-bit accesses to GITS_CTLR, GITS_IIDR and GITS ID 19888c2ecf20Sopenharmony_ci * registers 19898c2ecf20Sopenharmony_ci */ 19908c2ecf20Sopenharmony_ci if ((offset < GITS_TYPER) || (offset >= GITS_PIDR4)) 19918c2ecf20Sopenharmony_ci align = 0x3; 19928c2ecf20Sopenharmony_ci else 19938c2ecf20Sopenharmony_ci align = 0x7; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci if (offset & align) 19968c2ecf20Sopenharmony_ci return -EINVAL; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci mutex_lock(&dev->kvm->lock); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base)) { 20018c2ecf20Sopenharmony_ci ret = -ENXIO; 20028c2ecf20Sopenharmony_ci goto out; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci region = vgic_find_mmio_region(its_registers, 20068c2ecf20Sopenharmony_ci ARRAY_SIZE(its_registers), 20078c2ecf20Sopenharmony_ci offset); 20088c2ecf20Sopenharmony_ci if (!region) { 20098c2ecf20Sopenharmony_ci ret = -ENXIO; 20108c2ecf20Sopenharmony_ci goto out; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci if (!lock_all_vcpus(dev->kvm)) { 20148c2ecf20Sopenharmony_ci ret = -EBUSY; 20158c2ecf20Sopenharmony_ci goto out; 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci addr = its->vgic_its_base + offset; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci len = region->access_flags & VGIC_ACCESS_64bit ? 8 : 4; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci if (is_write) { 20238c2ecf20Sopenharmony_ci if (region->uaccess_its_write) 20248c2ecf20Sopenharmony_ci ret = region->uaccess_its_write(dev->kvm, its, addr, 20258c2ecf20Sopenharmony_ci len, *reg); 20268c2ecf20Sopenharmony_ci else 20278c2ecf20Sopenharmony_ci region->its_write(dev->kvm, its, addr, len, *reg); 20288c2ecf20Sopenharmony_ci } else { 20298c2ecf20Sopenharmony_ci *reg = region->its_read(dev->kvm, its, addr, len); 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci unlock_all_vcpus(dev->kvm); 20328c2ecf20Sopenharmony_ciout: 20338c2ecf20Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 20348c2ecf20Sopenharmony_ci return ret; 20358c2ecf20Sopenharmony_ci} 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_cistatic u32 compute_next_devid_offset(struct list_head *h, 20388c2ecf20Sopenharmony_ci struct its_device *dev) 20398c2ecf20Sopenharmony_ci{ 20408c2ecf20Sopenharmony_ci struct its_device *next; 20418c2ecf20Sopenharmony_ci u32 next_offset; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci if (list_is_last(&dev->dev_list, h)) 20448c2ecf20Sopenharmony_ci return 0; 20458c2ecf20Sopenharmony_ci next = list_next_entry(dev, dev_list); 20468c2ecf20Sopenharmony_ci next_offset = next->device_id - dev->device_id; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci return min_t(u32, next_offset, VITS_DTE_MAX_DEVID_OFFSET); 20498c2ecf20Sopenharmony_ci} 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_cistatic u32 compute_next_eventid_offset(struct list_head *h, struct its_ite *ite) 20528c2ecf20Sopenharmony_ci{ 20538c2ecf20Sopenharmony_ci struct its_ite *next; 20548c2ecf20Sopenharmony_ci u32 next_offset; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci if (list_is_last(&ite->ite_list, h)) 20578c2ecf20Sopenharmony_ci return 0; 20588c2ecf20Sopenharmony_ci next = list_next_entry(ite, ite_list); 20598c2ecf20Sopenharmony_ci next_offset = next->event_id - ite->event_id; 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci return min_t(u32, next_offset, VITS_ITE_MAX_EVENTID_OFFSET); 20628c2ecf20Sopenharmony_ci} 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci/** 20658c2ecf20Sopenharmony_ci * entry_fn_t - Callback called on a table entry restore path 20668c2ecf20Sopenharmony_ci * @its: its handle 20678c2ecf20Sopenharmony_ci * @id: id of the entry 20688c2ecf20Sopenharmony_ci * @entry: pointer to the entry 20698c2ecf20Sopenharmony_ci * @opaque: pointer to an opaque data 20708c2ecf20Sopenharmony_ci * 20718c2ecf20Sopenharmony_ci * Return: < 0 on error, 0 if last element was identified, id offset to next 20728c2ecf20Sopenharmony_ci * element otherwise 20738c2ecf20Sopenharmony_ci */ 20748c2ecf20Sopenharmony_citypedef int (*entry_fn_t)(struct vgic_its *its, u32 id, void *entry, 20758c2ecf20Sopenharmony_ci void *opaque); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci/** 20788c2ecf20Sopenharmony_ci * scan_its_table - Scan a contiguous table in guest RAM and applies a function 20798c2ecf20Sopenharmony_ci * to each entry 20808c2ecf20Sopenharmony_ci * 20818c2ecf20Sopenharmony_ci * @its: its handle 20828c2ecf20Sopenharmony_ci * @base: base gpa of the table 20838c2ecf20Sopenharmony_ci * @size: size of the table in bytes 20848c2ecf20Sopenharmony_ci * @esz: entry size in bytes 20858c2ecf20Sopenharmony_ci * @start_id: the ID of the first entry in the table 20868c2ecf20Sopenharmony_ci * (non zero for 2d level tables) 20878c2ecf20Sopenharmony_ci * @fn: function to apply on each entry 20888c2ecf20Sopenharmony_ci * 20898c2ecf20Sopenharmony_ci * Return: < 0 on error, 0 if last element was identified, 1 otherwise 20908c2ecf20Sopenharmony_ci * (the last element may not be found on second level tables) 20918c2ecf20Sopenharmony_ci */ 20928c2ecf20Sopenharmony_cistatic int scan_its_table(struct vgic_its *its, gpa_t base, int size, u32 esz, 20938c2ecf20Sopenharmony_ci int start_id, entry_fn_t fn, void *opaque) 20948c2ecf20Sopenharmony_ci{ 20958c2ecf20Sopenharmony_ci struct kvm *kvm = its->dev->kvm; 20968c2ecf20Sopenharmony_ci unsigned long len = size; 20978c2ecf20Sopenharmony_ci int id = start_id; 20988c2ecf20Sopenharmony_ci gpa_t gpa = base; 20998c2ecf20Sopenharmony_ci char entry[ESZ_MAX]; 21008c2ecf20Sopenharmony_ci int ret; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci memset(entry, 0, esz); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci while (true) { 21058c2ecf20Sopenharmony_ci int next_offset; 21068c2ecf20Sopenharmony_ci size_t byte_offset; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci ret = kvm_read_guest_lock(kvm, gpa, entry, esz); 21098c2ecf20Sopenharmony_ci if (ret) 21108c2ecf20Sopenharmony_ci return ret; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci next_offset = fn(its, id, entry, opaque); 21138c2ecf20Sopenharmony_ci if (next_offset <= 0) 21148c2ecf20Sopenharmony_ci return next_offset; 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci byte_offset = next_offset * esz; 21178c2ecf20Sopenharmony_ci if (byte_offset >= len) 21188c2ecf20Sopenharmony_ci break; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci id += next_offset; 21218c2ecf20Sopenharmony_ci gpa += byte_offset; 21228c2ecf20Sopenharmony_ci len -= byte_offset; 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci return 1; 21258c2ecf20Sopenharmony_ci} 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci/** 21288c2ecf20Sopenharmony_ci * vgic_its_save_ite - Save an interrupt translation entry at @gpa 21298c2ecf20Sopenharmony_ci */ 21308c2ecf20Sopenharmony_cistatic int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev, 21318c2ecf20Sopenharmony_ci struct its_ite *ite, gpa_t gpa, int ite_esz) 21328c2ecf20Sopenharmony_ci{ 21338c2ecf20Sopenharmony_ci struct kvm *kvm = its->dev->kvm; 21348c2ecf20Sopenharmony_ci u32 next_offset; 21358c2ecf20Sopenharmony_ci u64 val; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci next_offset = compute_next_eventid_offset(&dev->itt_head, ite); 21388c2ecf20Sopenharmony_ci val = ((u64)next_offset << KVM_ITS_ITE_NEXT_SHIFT) | 21398c2ecf20Sopenharmony_ci ((u64)ite->irq->intid << KVM_ITS_ITE_PINTID_SHIFT) | 21408c2ecf20Sopenharmony_ci ite->collection->collection_id; 21418c2ecf20Sopenharmony_ci val = cpu_to_le64(val); 21428c2ecf20Sopenharmony_ci return kvm_write_guest_lock(kvm, gpa, &val, ite_esz); 21438c2ecf20Sopenharmony_ci} 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci/** 21468c2ecf20Sopenharmony_ci * vgic_its_restore_ite - restore an interrupt translation entry 21478c2ecf20Sopenharmony_ci * @event_id: id used for indexing 21488c2ecf20Sopenharmony_ci * @ptr: pointer to the ITE entry 21498c2ecf20Sopenharmony_ci * @opaque: pointer to the its_device 21508c2ecf20Sopenharmony_ci */ 21518c2ecf20Sopenharmony_cistatic int vgic_its_restore_ite(struct vgic_its *its, u32 event_id, 21528c2ecf20Sopenharmony_ci void *ptr, void *opaque) 21538c2ecf20Sopenharmony_ci{ 21548c2ecf20Sopenharmony_ci struct its_device *dev = (struct its_device *)opaque; 21558c2ecf20Sopenharmony_ci struct its_collection *collection; 21568c2ecf20Sopenharmony_ci struct kvm *kvm = its->dev->kvm; 21578c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu = NULL; 21588c2ecf20Sopenharmony_ci u64 val; 21598c2ecf20Sopenharmony_ci u64 *p = (u64 *)ptr; 21608c2ecf20Sopenharmony_ci struct vgic_irq *irq; 21618c2ecf20Sopenharmony_ci u32 coll_id, lpi_id; 21628c2ecf20Sopenharmony_ci struct its_ite *ite; 21638c2ecf20Sopenharmony_ci u32 offset; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci val = *p; 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci val = le64_to_cpu(val); 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci coll_id = val & KVM_ITS_ITE_ICID_MASK; 21708c2ecf20Sopenharmony_ci lpi_id = (val & KVM_ITS_ITE_PINTID_MASK) >> KVM_ITS_ITE_PINTID_SHIFT; 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci if (!lpi_id) 21738c2ecf20Sopenharmony_ci return 1; /* invalid entry, no choice but to scan next entry */ 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci if (lpi_id < VGIC_MIN_LPI) 21768c2ecf20Sopenharmony_ci return -EINVAL; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci offset = val >> KVM_ITS_ITE_NEXT_SHIFT; 21798c2ecf20Sopenharmony_ci if (event_id + offset >= BIT_ULL(dev->num_eventid_bits)) 21808c2ecf20Sopenharmony_ci return -EINVAL; 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci collection = find_collection(its, coll_id); 21838c2ecf20Sopenharmony_ci if (!collection) 21848c2ecf20Sopenharmony_ci return -EINVAL; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci ite = vgic_its_alloc_ite(dev, collection, event_id); 21878c2ecf20Sopenharmony_ci if (IS_ERR(ite)) 21888c2ecf20Sopenharmony_ci return PTR_ERR(ite); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (its_is_collection_mapped(collection)) 21918c2ecf20Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, collection->target_addr); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci irq = vgic_add_lpi(kvm, lpi_id, vcpu); 21948c2ecf20Sopenharmony_ci if (IS_ERR(irq)) 21958c2ecf20Sopenharmony_ci return PTR_ERR(irq); 21968c2ecf20Sopenharmony_ci ite->irq = irq; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci return offset; 21998c2ecf20Sopenharmony_ci} 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_cistatic int vgic_its_ite_cmp(void *priv, const struct list_head *a, 22028c2ecf20Sopenharmony_ci const struct list_head *b) 22038c2ecf20Sopenharmony_ci{ 22048c2ecf20Sopenharmony_ci struct its_ite *itea = container_of(a, struct its_ite, ite_list); 22058c2ecf20Sopenharmony_ci struct its_ite *iteb = container_of(b, struct its_ite, ite_list); 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci if (itea->event_id < iteb->event_id) 22088c2ecf20Sopenharmony_ci return -1; 22098c2ecf20Sopenharmony_ci else 22108c2ecf20Sopenharmony_ci return 1; 22118c2ecf20Sopenharmony_ci} 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_cistatic int vgic_its_save_itt(struct vgic_its *its, struct its_device *device) 22148c2ecf20Sopenharmony_ci{ 22158c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 22168c2ecf20Sopenharmony_ci gpa_t base = device->itt_addr; 22178c2ecf20Sopenharmony_ci struct its_ite *ite; 22188c2ecf20Sopenharmony_ci int ret; 22198c2ecf20Sopenharmony_ci int ite_esz = abi->ite_esz; 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci list_sort(NULL, &device->itt_head, vgic_its_ite_cmp); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci list_for_each_entry(ite, &device->itt_head, ite_list) { 22248c2ecf20Sopenharmony_ci gpa_t gpa = base + ite->event_id * ite_esz; 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci /* 22278c2ecf20Sopenharmony_ci * If an LPI carries the HW bit, this means that this 22288c2ecf20Sopenharmony_ci * interrupt is controlled by GICv4, and we do not 22298c2ecf20Sopenharmony_ci * have direct access to that state. Let's simply fail 22308c2ecf20Sopenharmony_ci * the save operation... 22318c2ecf20Sopenharmony_ci */ 22328c2ecf20Sopenharmony_ci if (ite->irq->hw) 22338c2ecf20Sopenharmony_ci return -EACCES; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci ret = vgic_its_save_ite(its, device, ite, gpa, ite_esz); 22368c2ecf20Sopenharmony_ci if (ret) 22378c2ecf20Sopenharmony_ci return ret; 22388c2ecf20Sopenharmony_ci } 22398c2ecf20Sopenharmony_ci return 0; 22408c2ecf20Sopenharmony_ci} 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci/** 22438c2ecf20Sopenharmony_ci * vgic_its_restore_itt - restore the ITT of a device 22448c2ecf20Sopenharmony_ci * 22458c2ecf20Sopenharmony_ci * @its: its handle 22468c2ecf20Sopenharmony_ci * @dev: device handle 22478c2ecf20Sopenharmony_ci * 22488c2ecf20Sopenharmony_ci * Return 0 on success, < 0 on error 22498c2ecf20Sopenharmony_ci */ 22508c2ecf20Sopenharmony_cistatic int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev) 22518c2ecf20Sopenharmony_ci{ 22528c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 22538c2ecf20Sopenharmony_ci gpa_t base = dev->itt_addr; 22548c2ecf20Sopenharmony_ci int ret; 22558c2ecf20Sopenharmony_ci int ite_esz = abi->ite_esz; 22568c2ecf20Sopenharmony_ci size_t max_size = BIT_ULL(dev->num_eventid_bits) * ite_esz; 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci ret = scan_its_table(its, base, max_size, ite_esz, 0, 22598c2ecf20Sopenharmony_ci vgic_its_restore_ite, dev); 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci /* scan_its_table returns +1 if all ITEs are invalid */ 22628c2ecf20Sopenharmony_ci if (ret > 0) 22638c2ecf20Sopenharmony_ci ret = 0; 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci return ret; 22668c2ecf20Sopenharmony_ci} 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci/** 22698c2ecf20Sopenharmony_ci * vgic_its_save_dte - Save a device table entry at a given GPA 22708c2ecf20Sopenharmony_ci * 22718c2ecf20Sopenharmony_ci * @its: ITS handle 22728c2ecf20Sopenharmony_ci * @dev: ITS device 22738c2ecf20Sopenharmony_ci * @ptr: GPA 22748c2ecf20Sopenharmony_ci */ 22758c2ecf20Sopenharmony_cistatic int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev, 22768c2ecf20Sopenharmony_ci gpa_t ptr, int dte_esz) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci struct kvm *kvm = its->dev->kvm; 22798c2ecf20Sopenharmony_ci u64 val, itt_addr_field; 22808c2ecf20Sopenharmony_ci u32 next_offset; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci itt_addr_field = dev->itt_addr >> 8; 22838c2ecf20Sopenharmony_ci next_offset = compute_next_devid_offset(&its->device_list, dev); 22848c2ecf20Sopenharmony_ci val = (1ULL << KVM_ITS_DTE_VALID_SHIFT | 22858c2ecf20Sopenharmony_ci ((u64)next_offset << KVM_ITS_DTE_NEXT_SHIFT) | 22868c2ecf20Sopenharmony_ci (itt_addr_field << KVM_ITS_DTE_ITTADDR_SHIFT) | 22878c2ecf20Sopenharmony_ci (dev->num_eventid_bits - 1)); 22888c2ecf20Sopenharmony_ci val = cpu_to_le64(val); 22898c2ecf20Sopenharmony_ci return kvm_write_guest_lock(kvm, ptr, &val, dte_esz); 22908c2ecf20Sopenharmony_ci} 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci/** 22938c2ecf20Sopenharmony_ci * vgic_its_restore_dte - restore a device table entry 22948c2ecf20Sopenharmony_ci * 22958c2ecf20Sopenharmony_ci * @its: its handle 22968c2ecf20Sopenharmony_ci * @id: device id the DTE corresponds to 22978c2ecf20Sopenharmony_ci * @ptr: kernel VA where the 8 byte DTE is located 22988c2ecf20Sopenharmony_ci * @opaque: unused 22998c2ecf20Sopenharmony_ci * 23008c2ecf20Sopenharmony_ci * Return: < 0 on error, 0 if the dte is the last one, id offset to the 23018c2ecf20Sopenharmony_ci * next dte otherwise 23028c2ecf20Sopenharmony_ci */ 23038c2ecf20Sopenharmony_cistatic int vgic_its_restore_dte(struct vgic_its *its, u32 id, 23048c2ecf20Sopenharmony_ci void *ptr, void *opaque) 23058c2ecf20Sopenharmony_ci{ 23068c2ecf20Sopenharmony_ci struct its_device *dev; 23078c2ecf20Sopenharmony_ci gpa_t itt_addr; 23088c2ecf20Sopenharmony_ci u8 num_eventid_bits; 23098c2ecf20Sopenharmony_ci u64 entry = *(u64 *)ptr; 23108c2ecf20Sopenharmony_ci bool valid; 23118c2ecf20Sopenharmony_ci u32 offset; 23128c2ecf20Sopenharmony_ci int ret; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci entry = le64_to_cpu(entry); 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci valid = entry >> KVM_ITS_DTE_VALID_SHIFT; 23178c2ecf20Sopenharmony_ci num_eventid_bits = (entry & KVM_ITS_DTE_SIZE_MASK) + 1; 23188c2ecf20Sopenharmony_ci itt_addr = ((entry & KVM_ITS_DTE_ITTADDR_MASK) 23198c2ecf20Sopenharmony_ci >> KVM_ITS_DTE_ITTADDR_SHIFT) << 8; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci if (!valid) 23228c2ecf20Sopenharmony_ci return 1; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci /* dte entry is valid */ 23258c2ecf20Sopenharmony_ci offset = (entry & KVM_ITS_DTE_NEXT_MASK) >> KVM_ITS_DTE_NEXT_SHIFT; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci dev = vgic_its_alloc_device(its, id, itt_addr, num_eventid_bits); 23288c2ecf20Sopenharmony_ci if (IS_ERR(dev)) 23298c2ecf20Sopenharmony_ci return PTR_ERR(dev); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci ret = vgic_its_restore_itt(its, dev); 23328c2ecf20Sopenharmony_ci if (ret) { 23338c2ecf20Sopenharmony_ci vgic_its_free_device(its->dev->kvm, dev); 23348c2ecf20Sopenharmony_ci return ret; 23358c2ecf20Sopenharmony_ci } 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci return offset; 23388c2ecf20Sopenharmony_ci} 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_cistatic int vgic_its_device_cmp(void *priv, const struct list_head *a, 23418c2ecf20Sopenharmony_ci const struct list_head *b) 23428c2ecf20Sopenharmony_ci{ 23438c2ecf20Sopenharmony_ci struct its_device *deva = container_of(a, struct its_device, dev_list); 23448c2ecf20Sopenharmony_ci struct its_device *devb = container_of(b, struct its_device, dev_list); 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci if (deva->device_id < devb->device_id) 23478c2ecf20Sopenharmony_ci return -1; 23488c2ecf20Sopenharmony_ci else 23498c2ecf20Sopenharmony_ci return 1; 23508c2ecf20Sopenharmony_ci} 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci/** 23538c2ecf20Sopenharmony_ci * vgic_its_save_device_tables - Save the device table and all ITT 23548c2ecf20Sopenharmony_ci * into guest RAM 23558c2ecf20Sopenharmony_ci * 23568c2ecf20Sopenharmony_ci * L1/L2 handling is hidden by vgic_its_check_id() helper which directly 23578c2ecf20Sopenharmony_ci * returns the GPA of the device entry 23588c2ecf20Sopenharmony_ci */ 23598c2ecf20Sopenharmony_cistatic int vgic_its_save_device_tables(struct vgic_its *its) 23608c2ecf20Sopenharmony_ci{ 23618c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 23628c2ecf20Sopenharmony_ci u64 baser = its->baser_device_table; 23638c2ecf20Sopenharmony_ci struct its_device *dev; 23648c2ecf20Sopenharmony_ci int dte_esz = abi->dte_esz; 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci if (!(baser & GITS_BASER_VALID)) 23678c2ecf20Sopenharmony_ci return 0; 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci list_sort(NULL, &its->device_list, vgic_its_device_cmp); 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci list_for_each_entry(dev, &its->device_list, dev_list) { 23728c2ecf20Sopenharmony_ci int ret; 23738c2ecf20Sopenharmony_ci gpa_t eaddr; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci if (!vgic_its_check_id(its, baser, 23768c2ecf20Sopenharmony_ci dev->device_id, &eaddr)) 23778c2ecf20Sopenharmony_ci return -EINVAL; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci ret = vgic_its_save_itt(its, dev); 23808c2ecf20Sopenharmony_ci if (ret) 23818c2ecf20Sopenharmony_ci return ret; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci ret = vgic_its_save_dte(its, dev, eaddr, dte_esz); 23848c2ecf20Sopenharmony_ci if (ret) 23858c2ecf20Sopenharmony_ci return ret; 23868c2ecf20Sopenharmony_ci } 23878c2ecf20Sopenharmony_ci return 0; 23888c2ecf20Sopenharmony_ci} 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci/** 23918c2ecf20Sopenharmony_ci * handle_l1_dte - callback used for L1 device table entries (2 stage case) 23928c2ecf20Sopenharmony_ci * 23938c2ecf20Sopenharmony_ci * @its: its handle 23948c2ecf20Sopenharmony_ci * @id: index of the entry in the L1 table 23958c2ecf20Sopenharmony_ci * @addr: kernel VA 23968c2ecf20Sopenharmony_ci * @opaque: unused 23978c2ecf20Sopenharmony_ci * 23988c2ecf20Sopenharmony_ci * L1 table entries are scanned by steps of 1 entry 23998c2ecf20Sopenharmony_ci * Return < 0 if error, 0 if last dte was found when scanning the L2 24008c2ecf20Sopenharmony_ci * table, +1 otherwise (meaning next L1 entry must be scanned) 24018c2ecf20Sopenharmony_ci */ 24028c2ecf20Sopenharmony_cistatic int handle_l1_dte(struct vgic_its *its, u32 id, void *addr, 24038c2ecf20Sopenharmony_ci void *opaque) 24048c2ecf20Sopenharmony_ci{ 24058c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 24068c2ecf20Sopenharmony_ci int l2_start_id = id * (SZ_64K / abi->dte_esz); 24078c2ecf20Sopenharmony_ci u64 entry = *(u64 *)addr; 24088c2ecf20Sopenharmony_ci int dte_esz = abi->dte_esz; 24098c2ecf20Sopenharmony_ci gpa_t gpa; 24108c2ecf20Sopenharmony_ci int ret; 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci entry = le64_to_cpu(entry); 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci if (!(entry & KVM_ITS_L1E_VALID_MASK)) 24158c2ecf20Sopenharmony_ci return 1; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci gpa = entry & KVM_ITS_L1E_ADDR_MASK; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci ret = scan_its_table(its, gpa, SZ_64K, dte_esz, 24208c2ecf20Sopenharmony_ci l2_start_id, vgic_its_restore_dte, NULL); 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci return ret; 24238c2ecf20Sopenharmony_ci} 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci/** 24268c2ecf20Sopenharmony_ci * vgic_its_restore_device_tables - Restore the device table and all ITT 24278c2ecf20Sopenharmony_ci * from guest RAM to internal data structs 24288c2ecf20Sopenharmony_ci */ 24298c2ecf20Sopenharmony_cistatic int vgic_its_restore_device_tables(struct vgic_its *its) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 24328c2ecf20Sopenharmony_ci u64 baser = its->baser_device_table; 24338c2ecf20Sopenharmony_ci int l1_esz, ret; 24348c2ecf20Sopenharmony_ci int l1_tbl_size = GITS_BASER_NR_PAGES(baser) * SZ_64K; 24358c2ecf20Sopenharmony_ci gpa_t l1_gpa; 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci if (!(baser & GITS_BASER_VALID)) 24388c2ecf20Sopenharmony_ci return 0; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci l1_gpa = GITS_BASER_ADDR_48_to_52(baser); 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci if (baser & GITS_BASER_INDIRECT) { 24438c2ecf20Sopenharmony_ci l1_esz = GITS_LVL1_ENTRY_SIZE; 24448c2ecf20Sopenharmony_ci ret = scan_its_table(its, l1_gpa, l1_tbl_size, l1_esz, 0, 24458c2ecf20Sopenharmony_ci handle_l1_dte, NULL); 24468c2ecf20Sopenharmony_ci } else { 24478c2ecf20Sopenharmony_ci l1_esz = abi->dte_esz; 24488c2ecf20Sopenharmony_ci ret = scan_its_table(its, l1_gpa, l1_tbl_size, l1_esz, 0, 24498c2ecf20Sopenharmony_ci vgic_its_restore_dte, NULL); 24508c2ecf20Sopenharmony_ci } 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci /* scan_its_table returns +1 if all entries are invalid */ 24538c2ecf20Sopenharmony_ci if (ret > 0) 24548c2ecf20Sopenharmony_ci ret = 0; 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci return ret; 24578c2ecf20Sopenharmony_ci} 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_cistatic int vgic_its_save_cte(struct vgic_its *its, 24608c2ecf20Sopenharmony_ci struct its_collection *collection, 24618c2ecf20Sopenharmony_ci gpa_t gpa, int esz) 24628c2ecf20Sopenharmony_ci{ 24638c2ecf20Sopenharmony_ci u64 val; 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci val = (1ULL << KVM_ITS_CTE_VALID_SHIFT | 24668c2ecf20Sopenharmony_ci ((u64)collection->target_addr << KVM_ITS_CTE_RDBASE_SHIFT) | 24678c2ecf20Sopenharmony_ci collection->collection_id); 24688c2ecf20Sopenharmony_ci val = cpu_to_le64(val); 24698c2ecf20Sopenharmony_ci return kvm_write_guest_lock(its->dev->kvm, gpa, &val, esz); 24708c2ecf20Sopenharmony_ci} 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_cistatic int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz) 24738c2ecf20Sopenharmony_ci{ 24748c2ecf20Sopenharmony_ci struct its_collection *collection; 24758c2ecf20Sopenharmony_ci struct kvm *kvm = its->dev->kvm; 24768c2ecf20Sopenharmony_ci u32 target_addr, coll_id; 24778c2ecf20Sopenharmony_ci u64 val; 24788c2ecf20Sopenharmony_ci int ret; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci BUG_ON(esz > sizeof(val)); 24818c2ecf20Sopenharmony_ci ret = kvm_read_guest_lock(kvm, gpa, &val, esz); 24828c2ecf20Sopenharmony_ci if (ret) 24838c2ecf20Sopenharmony_ci return ret; 24848c2ecf20Sopenharmony_ci val = le64_to_cpu(val); 24858c2ecf20Sopenharmony_ci if (!(val & KVM_ITS_CTE_VALID_MASK)) 24868c2ecf20Sopenharmony_ci return 0; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci target_addr = (u32)(val >> KVM_ITS_CTE_RDBASE_SHIFT); 24898c2ecf20Sopenharmony_ci coll_id = val & KVM_ITS_CTE_ICID_MASK; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci if (target_addr != COLLECTION_NOT_MAPPED && 24928c2ecf20Sopenharmony_ci target_addr >= atomic_read(&kvm->online_vcpus)) 24938c2ecf20Sopenharmony_ci return -EINVAL; 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci collection = find_collection(its, coll_id); 24968c2ecf20Sopenharmony_ci if (collection) 24978c2ecf20Sopenharmony_ci return -EEXIST; 24988c2ecf20Sopenharmony_ci ret = vgic_its_alloc_collection(its, &collection, coll_id); 24998c2ecf20Sopenharmony_ci if (ret) 25008c2ecf20Sopenharmony_ci return ret; 25018c2ecf20Sopenharmony_ci collection->target_addr = target_addr; 25028c2ecf20Sopenharmony_ci return 1; 25038c2ecf20Sopenharmony_ci} 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci/** 25068c2ecf20Sopenharmony_ci * vgic_its_save_collection_table - Save the collection table into 25078c2ecf20Sopenharmony_ci * guest RAM 25088c2ecf20Sopenharmony_ci */ 25098c2ecf20Sopenharmony_cistatic int vgic_its_save_collection_table(struct vgic_its *its) 25108c2ecf20Sopenharmony_ci{ 25118c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 25128c2ecf20Sopenharmony_ci u64 baser = its->baser_coll_table; 25138c2ecf20Sopenharmony_ci gpa_t gpa = GITS_BASER_ADDR_48_to_52(baser); 25148c2ecf20Sopenharmony_ci struct its_collection *collection; 25158c2ecf20Sopenharmony_ci u64 val; 25168c2ecf20Sopenharmony_ci size_t max_size, filled = 0; 25178c2ecf20Sopenharmony_ci int ret, cte_esz = abi->cte_esz; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci if (!(baser & GITS_BASER_VALID)) 25208c2ecf20Sopenharmony_ci return 0; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci list_for_each_entry(collection, &its->collection_list, coll_list) { 25258c2ecf20Sopenharmony_ci ret = vgic_its_save_cte(its, collection, gpa, cte_esz); 25268c2ecf20Sopenharmony_ci if (ret) 25278c2ecf20Sopenharmony_ci return ret; 25288c2ecf20Sopenharmony_ci gpa += cte_esz; 25298c2ecf20Sopenharmony_ci filled += cte_esz; 25308c2ecf20Sopenharmony_ci } 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci if (filled == max_size) 25338c2ecf20Sopenharmony_ci return 0; 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci /* 25368c2ecf20Sopenharmony_ci * table is not fully filled, add a last dummy element 25378c2ecf20Sopenharmony_ci * with valid bit unset 25388c2ecf20Sopenharmony_ci */ 25398c2ecf20Sopenharmony_ci val = 0; 25408c2ecf20Sopenharmony_ci BUG_ON(cte_esz > sizeof(val)); 25418c2ecf20Sopenharmony_ci ret = kvm_write_guest_lock(its->dev->kvm, gpa, &val, cte_esz); 25428c2ecf20Sopenharmony_ci return ret; 25438c2ecf20Sopenharmony_ci} 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci/** 25468c2ecf20Sopenharmony_ci * vgic_its_restore_collection_table - reads the collection table 25478c2ecf20Sopenharmony_ci * in guest memory and restores the ITS internal state. Requires the 25488c2ecf20Sopenharmony_ci * BASER registers to be restored before. 25498c2ecf20Sopenharmony_ci */ 25508c2ecf20Sopenharmony_cistatic int vgic_its_restore_collection_table(struct vgic_its *its) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 25538c2ecf20Sopenharmony_ci u64 baser = its->baser_coll_table; 25548c2ecf20Sopenharmony_ci int cte_esz = abi->cte_esz; 25558c2ecf20Sopenharmony_ci size_t max_size, read = 0; 25568c2ecf20Sopenharmony_ci gpa_t gpa; 25578c2ecf20Sopenharmony_ci int ret; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci if (!(baser & GITS_BASER_VALID)) 25608c2ecf20Sopenharmony_ci return 0; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci gpa = GITS_BASER_ADDR_48_to_52(baser); 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci while (read < max_size) { 25678c2ecf20Sopenharmony_ci ret = vgic_its_restore_cte(its, gpa, cte_esz); 25688c2ecf20Sopenharmony_ci if (ret <= 0) 25698c2ecf20Sopenharmony_ci break; 25708c2ecf20Sopenharmony_ci gpa += cte_esz; 25718c2ecf20Sopenharmony_ci read += cte_esz; 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci if (ret > 0) 25758c2ecf20Sopenharmony_ci return 0; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci return ret; 25788c2ecf20Sopenharmony_ci} 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci/** 25818c2ecf20Sopenharmony_ci * vgic_its_save_tables_v0 - Save the ITS tables into guest ARM 25828c2ecf20Sopenharmony_ci * according to v0 ABI 25838c2ecf20Sopenharmony_ci */ 25848c2ecf20Sopenharmony_cistatic int vgic_its_save_tables_v0(struct vgic_its *its) 25858c2ecf20Sopenharmony_ci{ 25868c2ecf20Sopenharmony_ci int ret; 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci ret = vgic_its_save_device_tables(its); 25898c2ecf20Sopenharmony_ci if (ret) 25908c2ecf20Sopenharmony_ci return ret; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci return vgic_its_save_collection_table(its); 25938c2ecf20Sopenharmony_ci} 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci/** 25968c2ecf20Sopenharmony_ci * vgic_its_restore_tables_v0 - Restore the ITS tables from guest RAM 25978c2ecf20Sopenharmony_ci * to internal data structs according to V0 ABI 25988c2ecf20Sopenharmony_ci * 25998c2ecf20Sopenharmony_ci */ 26008c2ecf20Sopenharmony_cistatic int vgic_its_restore_tables_v0(struct vgic_its *its) 26018c2ecf20Sopenharmony_ci{ 26028c2ecf20Sopenharmony_ci int ret; 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci ret = vgic_its_restore_collection_table(its); 26058c2ecf20Sopenharmony_ci if (ret) 26068c2ecf20Sopenharmony_ci return ret; 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci return vgic_its_restore_device_tables(its); 26098c2ecf20Sopenharmony_ci} 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_cistatic int vgic_its_commit_v0(struct vgic_its *its) 26128c2ecf20Sopenharmony_ci{ 26138c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci abi = vgic_its_get_abi(its); 26168c2ecf20Sopenharmony_ci its->baser_coll_table &= ~GITS_BASER_ENTRY_SIZE_MASK; 26178c2ecf20Sopenharmony_ci its->baser_device_table &= ~GITS_BASER_ENTRY_SIZE_MASK; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci its->baser_coll_table |= (GIC_ENCODE_SZ(abi->cte_esz, 5) 26208c2ecf20Sopenharmony_ci << GITS_BASER_ENTRY_SIZE_SHIFT); 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci its->baser_device_table |= (GIC_ENCODE_SZ(abi->dte_esz, 5) 26238c2ecf20Sopenharmony_ci << GITS_BASER_ENTRY_SIZE_SHIFT); 26248c2ecf20Sopenharmony_ci return 0; 26258c2ecf20Sopenharmony_ci} 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_cistatic void vgic_its_reset(struct kvm *kvm, struct vgic_its *its) 26288c2ecf20Sopenharmony_ci{ 26298c2ecf20Sopenharmony_ci /* We need to keep the ABI specific field values */ 26308c2ecf20Sopenharmony_ci its->baser_coll_table &= ~GITS_BASER_VALID; 26318c2ecf20Sopenharmony_ci its->baser_device_table &= ~GITS_BASER_VALID; 26328c2ecf20Sopenharmony_ci its->cbaser = 0; 26338c2ecf20Sopenharmony_ci its->creadr = 0; 26348c2ecf20Sopenharmony_ci its->cwriter = 0; 26358c2ecf20Sopenharmony_ci its->enabled = 0; 26368c2ecf20Sopenharmony_ci vgic_its_free_device_list(kvm, its); 26378c2ecf20Sopenharmony_ci vgic_its_free_collection_list(kvm, its); 26388c2ecf20Sopenharmony_ci} 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_cistatic int vgic_its_has_attr(struct kvm_device *dev, 26418c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 26428c2ecf20Sopenharmony_ci{ 26438c2ecf20Sopenharmony_ci switch (attr->group) { 26448c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: 26458c2ecf20Sopenharmony_ci switch (attr->attr) { 26468c2ecf20Sopenharmony_ci case KVM_VGIC_ITS_ADDR_TYPE: 26478c2ecf20Sopenharmony_ci return 0; 26488c2ecf20Sopenharmony_ci } 26498c2ecf20Sopenharmony_ci break; 26508c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: 26518c2ecf20Sopenharmony_ci switch (attr->attr) { 26528c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_CTRL_INIT: 26538c2ecf20Sopenharmony_ci return 0; 26548c2ecf20Sopenharmony_ci case KVM_DEV_ARM_ITS_CTRL_RESET: 26558c2ecf20Sopenharmony_ci return 0; 26568c2ecf20Sopenharmony_ci case KVM_DEV_ARM_ITS_SAVE_TABLES: 26578c2ecf20Sopenharmony_ci return 0; 26588c2ecf20Sopenharmony_ci case KVM_DEV_ARM_ITS_RESTORE_TABLES: 26598c2ecf20Sopenharmony_ci return 0; 26608c2ecf20Sopenharmony_ci } 26618c2ecf20Sopenharmony_ci break; 26628c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ITS_REGS: 26638c2ecf20Sopenharmony_ci return vgic_its_has_attr_regs(dev, attr); 26648c2ecf20Sopenharmony_ci } 26658c2ecf20Sopenharmony_ci return -ENXIO; 26668c2ecf20Sopenharmony_ci} 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_cistatic int vgic_its_ctrl(struct kvm *kvm, struct vgic_its *its, u64 attr) 26698c2ecf20Sopenharmony_ci{ 26708c2ecf20Sopenharmony_ci const struct vgic_its_abi *abi = vgic_its_get_abi(its); 26718c2ecf20Sopenharmony_ci int ret = 0; 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci if (attr == KVM_DEV_ARM_VGIC_CTRL_INIT) /* Nothing to do */ 26748c2ecf20Sopenharmony_ci return 0; 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci mutex_lock(&kvm->lock); 26778c2ecf20Sopenharmony_ci mutex_lock(&its->its_lock); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci if (!lock_all_vcpus(kvm)) { 26808c2ecf20Sopenharmony_ci mutex_unlock(&its->its_lock); 26818c2ecf20Sopenharmony_ci mutex_unlock(&kvm->lock); 26828c2ecf20Sopenharmony_ci return -EBUSY; 26838c2ecf20Sopenharmony_ci } 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci switch (attr) { 26868c2ecf20Sopenharmony_ci case KVM_DEV_ARM_ITS_CTRL_RESET: 26878c2ecf20Sopenharmony_ci vgic_its_reset(kvm, its); 26888c2ecf20Sopenharmony_ci break; 26898c2ecf20Sopenharmony_ci case KVM_DEV_ARM_ITS_SAVE_TABLES: 26908c2ecf20Sopenharmony_ci ret = abi->save_tables(its); 26918c2ecf20Sopenharmony_ci break; 26928c2ecf20Sopenharmony_ci case KVM_DEV_ARM_ITS_RESTORE_TABLES: 26938c2ecf20Sopenharmony_ci ret = abi->restore_tables(its); 26948c2ecf20Sopenharmony_ci break; 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci unlock_all_vcpus(kvm); 26988c2ecf20Sopenharmony_ci mutex_unlock(&its->its_lock); 26998c2ecf20Sopenharmony_ci mutex_unlock(&kvm->lock); 27008c2ecf20Sopenharmony_ci return ret; 27018c2ecf20Sopenharmony_ci} 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_cistatic int vgic_its_set_attr(struct kvm_device *dev, 27048c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci struct vgic_its *its = dev->private; 27078c2ecf20Sopenharmony_ci int ret; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci switch (attr->group) { 27108c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: { 27118c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 27128c2ecf20Sopenharmony_ci unsigned long type = (unsigned long)attr->attr; 27138c2ecf20Sopenharmony_ci u64 addr; 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci if (type != KVM_VGIC_ITS_ADDR_TYPE) 27168c2ecf20Sopenharmony_ci return -ENODEV; 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci if (copy_from_user(&addr, uaddr, sizeof(addr))) 27198c2ecf20Sopenharmony_ci return -EFAULT; 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci ret = vgic_check_ioaddr(dev->kvm, &its->vgic_its_base, 27228c2ecf20Sopenharmony_ci addr, SZ_64K); 27238c2ecf20Sopenharmony_ci if (ret) 27248c2ecf20Sopenharmony_ci return ret; 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci return vgic_register_its_iodev(dev->kvm, its, addr); 27278c2ecf20Sopenharmony_ci } 27288c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: 27298c2ecf20Sopenharmony_ci return vgic_its_ctrl(dev->kvm, its, attr->attr); 27308c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ITS_REGS: { 27318c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 27328c2ecf20Sopenharmony_ci u64 reg; 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci if (get_user(reg, uaddr)) 27358c2ecf20Sopenharmony_ci return -EFAULT; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci return vgic_its_attr_regs_access(dev, attr, ®, true); 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci } 27408c2ecf20Sopenharmony_ci return -ENXIO; 27418c2ecf20Sopenharmony_ci} 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_cistatic int vgic_its_get_attr(struct kvm_device *dev, 27448c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 27458c2ecf20Sopenharmony_ci{ 27468c2ecf20Sopenharmony_ci switch (attr->group) { 27478c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: { 27488c2ecf20Sopenharmony_ci struct vgic_its *its = dev->private; 27498c2ecf20Sopenharmony_ci u64 addr = its->vgic_its_base; 27508c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 27518c2ecf20Sopenharmony_ci unsigned long type = (unsigned long)attr->attr; 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci if (type != KVM_VGIC_ITS_ADDR_TYPE) 27548c2ecf20Sopenharmony_ci return -ENODEV; 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci if (copy_to_user(uaddr, &addr, sizeof(addr))) 27578c2ecf20Sopenharmony_ci return -EFAULT; 27588c2ecf20Sopenharmony_ci break; 27598c2ecf20Sopenharmony_ci } 27608c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ITS_REGS: { 27618c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 27628c2ecf20Sopenharmony_ci u64 reg; 27638c2ecf20Sopenharmony_ci int ret; 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci ret = vgic_its_attr_regs_access(dev, attr, ®, false); 27668c2ecf20Sopenharmony_ci if (ret) 27678c2ecf20Sopenharmony_ci return ret; 27688c2ecf20Sopenharmony_ci return put_user(reg, uaddr); 27698c2ecf20Sopenharmony_ci } 27708c2ecf20Sopenharmony_ci default: 27718c2ecf20Sopenharmony_ci return -ENXIO; 27728c2ecf20Sopenharmony_ci } 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci return 0; 27758c2ecf20Sopenharmony_ci} 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_cistatic struct kvm_device_ops kvm_arm_vgic_its_ops = { 27788c2ecf20Sopenharmony_ci .name = "kvm-arm-vgic-its", 27798c2ecf20Sopenharmony_ci .create = vgic_its_create, 27808c2ecf20Sopenharmony_ci .destroy = vgic_its_destroy, 27818c2ecf20Sopenharmony_ci .set_attr = vgic_its_set_attr, 27828c2ecf20Sopenharmony_ci .get_attr = vgic_its_get_attr, 27838c2ecf20Sopenharmony_ci .has_attr = vgic_its_has_attr, 27848c2ecf20Sopenharmony_ci}; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ciint kvm_vgic_register_its_device(void) 27878c2ecf20Sopenharmony_ci{ 27888c2ecf20Sopenharmony_ci return kvm_register_device_ops(&kvm_arm_vgic_its_ops, 27898c2ecf20Sopenharmony_ci KVM_DEV_TYPE_ARM_VGIC_ITS); 27908c2ecf20Sopenharmony_ci} 2791