| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2023, Microsoft Corporation.
- *
- * Authors: Microsoft Linux virtualization team
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <asm/mshyperv.h>
- #include "mshv_eventfd.h"
- #include "mshv.h"
- #include "mshv_root.h"
- /* called from the ioctl code, user wants to update the guest irq table */
- int mshv_update_routing_table(struct mshv_partition *partition,
- const struct mshv_user_irq_entry *ue,
- unsigned int numents)
- {
- struct mshv_girq_routing_table *new = NULL, *old;
- u32 i, nr_rt_entries = 0;
- int r = 0;
- if (numents == 0)
- goto swap_routes;
- for (i = 0; i < numents; i++) {
- if (ue[i].gsi >= MSHV_MAX_GUEST_IRQS)
- return -EINVAL;
- if (ue[i].address_hi)
- return -EINVAL;
- nr_rt_entries = max(nr_rt_entries, ue[i].gsi);
- }
- nr_rt_entries += 1;
- new = kzalloc_flex(*new, mshv_girq_info_tbl, nr_rt_entries,
- GFP_KERNEL_ACCOUNT);
- if (!new)
- return -ENOMEM;
- new->num_rt_entries = nr_rt_entries;
- for (i = 0; i < numents; i++) {
- struct mshv_guest_irq_ent *girq;
- girq = &new->mshv_girq_info_tbl[ue[i].gsi];
- /*
- * Allow only one to one mapping between GSI and MSI routing.
- */
- if (girq->guest_irq_num != 0) {
- r = -EINVAL;
- goto out;
- }
- girq->guest_irq_num = ue[i].gsi;
- girq->girq_addr_lo = ue[i].address_lo;
- girq->girq_addr_hi = ue[i].address_hi;
- girq->girq_irq_data = ue[i].data;
- girq->girq_entry_valid = true;
- }
- swap_routes:
- mutex_lock(&partition->pt_irq_lock);
- old = rcu_dereference_protected(partition->pt_girq_tbl, 1);
- rcu_assign_pointer(partition->pt_girq_tbl, new);
- mshv_irqfd_routing_update(partition);
- mutex_unlock(&partition->pt_irq_lock);
- synchronize_srcu_expedited(&partition->pt_irq_srcu);
- new = old;
- out:
- kfree(new);
- return r;
- }
- /* vm is going away, kfree the irq routing table */
- void mshv_free_routing_table(struct mshv_partition *partition)
- {
- struct mshv_girq_routing_table *rt =
- rcu_access_pointer(partition->pt_girq_tbl);
- kfree(rt);
- }
- struct mshv_guest_irq_ent
- mshv_ret_girq_entry(struct mshv_partition *partition, u32 irqnum)
- {
- struct mshv_guest_irq_ent entry = { 0 };
- struct mshv_girq_routing_table *girq_tbl;
- girq_tbl = srcu_dereference_check(partition->pt_girq_tbl,
- &partition->pt_irq_srcu,
- lockdep_is_held(&partition->pt_irq_lock));
- if (!girq_tbl || irqnum >= girq_tbl->num_rt_entries) {
- /*
- * Premature register_irqfd, setting valid_entry = 0
- * would ignore this entry anyway
- */
- entry.guest_irq_num = irqnum;
- return entry;
- }
- return girq_tbl->mshv_girq_info_tbl[irqnum];
- }
- void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent,
- struct mshv_lapic_irq *lirq)
- {
- memset(lirq, 0, sizeof(*lirq));
- if (!ent || !ent->girq_entry_valid)
- return;
- lirq->lapic_vector = ent->girq_irq_data & 0xFF;
- lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF;
- lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8;
- #if IS_ENABLED(CONFIG_X86)
- lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1;
- lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1;
- #elif IS_ENABLED(CONFIG_ARM64)
- lirq->lapic_control.asserted = 1;
- #endif
- }
|