mshv_irq.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2023, Microsoft Corporation.
  4. *
  5. * Authors: Microsoft Linux virtualization team
  6. */
  7. #include <linux/kernel.h>
  8. #include <linux/module.h>
  9. #include <linux/slab.h>
  10. #include <asm/mshyperv.h>
  11. #include "mshv_eventfd.h"
  12. #include "mshv.h"
  13. #include "mshv_root.h"
  14. /* called from the ioctl code, user wants to update the guest irq table */
  15. int mshv_update_routing_table(struct mshv_partition *partition,
  16. const struct mshv_user_irq_entry *ue,
  17. unsigned int numents)
  18. {
  19. struct mshv_girq_routing_table *new = NULL, *old;
  20. u32 i, nr_rt_entries = 0;
  21. int r = 0;
  22. if (numents == 0)
  23. goto swap_routes;
  24. for (i = 0; i < numents; i++) {
  25. if (ue[i].gsi >= MSHV_MAX_GUEST_IRQS)
  26. return -EINVAL;
  27. if (ue[i].address_hi)
  28. return -EINVAL;
  29. nr_rt_entries = max(nr_rt_entries, ue[i].gsi);
  30. }
  31. nr_rt_entries += 1;
  32. new = kzalloc_flex(*new, mshv_girq_info_tbl, nr_rt_entries,
  33. GFP_KERNEL_ACCOUNT);
  34. if (!new)
  35. return -ENOMEM;
  36. new->num_rt_entries = nr_rt_entries;
  37. for (i = 0; i < numents; i++) {
  38. struct mshv_guest_irq_ent *girq;
  39. girq = &new->mshv_girq_info_tbl[ue[i].gsi];
  40. /*
  41. * Allow only one to one mapping between GSI and MSI routing.
  42. */
  43. if (girq->guest_irq_num != 0) {
  44. r = -EINVAL;
  45. goto out;
  46. }
  47. girq->guest_irq_num = ue[i].gsi;
  48. girq->girq_addr_lo = ue[i].address_lo;
  49. girq->girq_addr_hi = ue[i].address_hi;
  50. girq->girq_irq_data = ue[i].data;
  51. girq->girq_entry_valid = true;
  52. }
  53. swap_routes:
  54. mutex_lock(&partition->pt_irq_lock);
  55. old = rcu_dereference_protected(partition->pt_girq_tbl, 1);
  56. rcu_assign_pointer(partition->pt_girq_tbl, new);
  57. mshv_irqfd_routing_update(partition);
  58. mutex_unlock(&partition->pt_irq_lock);
  59. synchronize_srcu_expedited(&partition->pt_irq_srcu);
  60. new = old;
  61. out:
  62. kfree(new);
  63. return r;
  64. }
  65. /* vm is going away, kfree the irq routing table */
  66. void mshv_free_routing_table(struct mshv_partition *partition)
  67. {
  68. struct mshv_girq_routing_table *rt =
  69. rcu_access_pointer(partition->pt_girq_tbl);
  70. kfree(rt);
  71. }
  72. struct mshv_guest_irq_ent
  73. mshv_ret_girq_entry(struct mshv_partition *partition, u32 irqnum)
  74. {
  75. struct mshv_guest_irq_ent entry = { 0 };
  76. struct mshv_girq_routing_table *girq_tbl;
  77. girq_tbl = srcu_dereference_check(partition->pt_girq_tbl,
  78. &partition->pt_irq_srcu,
  79. lockdep_is_held(&partition->pt_irq_lock));
  80. if (!girq_tbl || irqnum >= girq_tbl->num_rt_entries) {
  81. /*
  82. * Premature register_irqfd, setting valid_entry = 0
  83. * would ignore this entry anyway
  84. */
  85. entry.guest_irq_num = irqnum;
  86. return entry;
  87. }
  88. return girq_tbl->mshv_girq_info_tbl[irqnum];
  89. }
  90. void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent,
  91. struct mshv_lapic_irq *lirq)
  92. {
  93. memset(lirq, 0, sizeof(*lirq));
  94. if (!ent || !ent->girq_entry_valid)
  95. return;
  96. lirq->lapic_vector = ent->girq_irq_data & 0xFF;
  97. lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF;
  98. lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8;
  99. #if IS_ENABLED(CONFIG_X86)
  100. lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1;
  101. lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1;
  102. #elif IS_ENABLED(CONFIG_ARM64)
  103. lirq->lapic_control.asserted = 1;
  104. #endif
  105. }