diff --git a/drivers/virt/haven/hh_irq_lend.c b/drivers/virt/haven/hh_irq_lend.c index 9fbbb8c205dd..76fd1f7fdc58 100644 --- a/drivers/virt/haven/hh_irq_lend.c +++ b/drivers/virt/haven/hh_irq_lend.c @@ -141,17 +141,14 @@ int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, int ret, virq; unsigned long flags; struct hh_irq_entry *entry; - struct irq_data *irq_data; if (label >= HH_IRQ_LABEL_MAX || !on_release) return -EINVAL; entry = &hh_irq_entries[label]; - irq_data = irq_get_irq_data(irq); - if (!irq_data) + if (hh_rm_irq_to_virq(irq, &virq)) return -EINVAL; - virq = irq_data->hwirq; spin_lock_irqsave(&hh_irq_lend_lock, flags); if (entry->state != HH_IRQ_STATE_NONE) { @@ -245,17 +242,18 @@ EXPORT_SYMBOL(hh_irq_wait_for_lend); * hh_irq_accept: Register to receive interrupts with a lent vIRQ * @label: vIRQ high-level label * @irq: Linux IRQ# to associate vIRQ with. If don't care, use -1 + * @type: IRQ flags to use when allowing RM to choose the IRQ. If irq parameter + * is specified, then type is unused. * * Returns the Linux IRQ# that vIRQ was registered to on success. * Returns <0 on error * This function is not thread-safe w.r.t. IRQ lend state. Do not race with * with hh_irq_release or another hh_irq_accept with same label. */ -int hh_irq_accept(enum hh_irq_label label, int irq) +int hh_irq_accept(enum hh_irq_label label, int irq, int type) { struct hh_irq_entry *entry; - const struct irq_data *irq_data; - int virq; + u32 virq; if (label >= HH_IRQ_LABEL_MAX) return -EINVAL; @@ -266,10 +264,8 @@ int hh_irq_accept(enum hh_irq_label label, int irq) return -EINVAL; if (irq != -1) { - irq_data = irq_get_irq_data(irq); - if (!irq_data) + if (hh_rm_irq_to_virq(irq, &virq)) return -EINVAL; - virq = irq_data->hwirq; } else virq = -1; @@ -278,8 +274,7 @@ int hh_irq_accept(enum hh_irq_label label, int irq) return virq; if (irq == -1) - irq = hh_rm_virq_to_linux_irq(virq - 32, GIC_SPI, - IRQ_TYPE_LEVEL_HIGH); + irq = hh_rm_virq_to_irq(virq, type); return irq; } diff --git a/drivers/virt/haven/hh_rm_core.c b/drivers/virt/haven/hh_rm_core.c index 330acc9c5a85..f1c0ae48dd0c 100644 --- a/drivers/virt/haven/hh_rm_core.c +++ b/drivers/virt/haven/hh_rm_core.c @@ -60,6 +60,7 @@ static DEFINE_MUTEX(hh_rm_send_lock); static DEFINE_IDA(hh_rm_free_virq_ida); static struct device_node *hh_rm_intc; +static struct irq_domain *hh_rm_irq_domain; static u32 hh_rm_base_virq; SRCU_NOTIFIER_HEAD_STATIC(hh_rm_notifier); @@ -624,19 +625,54 @@ out: return ret; } -int hh_rm_virq_to_linux_irq(u32 virq, u32 type, u32 trigger) +/** + * hh_rm_virq_to_irq: Get a Linux IRQ from a Haven-compatible vIRQ + * @virq: Haven-compatible vIRQ + * @type: IRQ trigger type (IRQ_TYPE_EDGE_RISING) + * + * Returns the mapped Linux IRQ# at Haven's IRQ domain (i.e. GIC SPI) + */ +int hh_rm_virq_to_irq(u32 virq, u32 type) { struct irq_fwspec fwspec = {}; + if (virq < 32 || virq >= GIC_V3_SPI_MAX) { + pr_warn("%s: expecting an SPI from RM, but got GIC IRQ %d\n", + __func__, virq); + } + fwspec.fwnode = of_node_to_fwnode(hh_rm_intc); fwspec.param_count = 3; - fwspec.param[0] = type; - fwspec.param[1] = virq; - fwspec.param[2] = trigger; + fwspec.param[0] = GIC_SPI; + fwspec.param[1] = virq - 32; + fwspec.param[2] = type; return irq_create_fwspec_mapping(&fwspec); } -EXPORT_SYMBOL(hh_rm_virq_to_linux_irq); +EXPORT_SYMBOL(hh_rm_virq_to_irq); + +/** + * hh_rm_irq_to_virq: Get a Haven-compatible vIRQ from a Linux IRQ + * @irq: Linux-assigned IRQ# + * @virq: out value where Haven-compatible vIRQ is stored + * + * Returns 0 upon success, -EINVAL if the Linux IRQ could not be mapped to + * a Haven vIRQ (i.e., the IRQ does not correspond to any GIC-level IRQ) + */ +int hh_rm_irq_to_virq(int irq, u32 *virq) +{ + struct irq_data *irq_data; + + irq_data = irq_domain_get_irq_data(hh_rm_irq_domain, irq); + if (!irq_data) + return -EINVAL; + + if (virq) + *virq = irq_data->hwirq; + + return 0; +} +EXPORT_SYMBOL(hh_rm_irq_to_virq); static int hh_rm_get_irq(struct hh_vm_get_hyp_res_resp_entry *res_entry) { @@ -673,8 +709,7 @@ static int hh_rm_get_irq(struct hh_vm_get_hyp_res_resp_entry *res_entry) return -EINVAL; } - return hh_rm_virq_to_linux_irq(virq - 32, GIC_SPI, - IRQ_TYPE_EDGE_RISING); + return hh_rm_virq_to_irq(virq, IRQ_TYPE_EDGE_RISING); err: ida_free(&hh_rm_free_virq_ida, virq - 32); @@ -838,6 +873,11 @@ static int hh_rm_drv_probe(struct platform_device *pdev) dev_err(dev, "Failed to get the IRQ parent node\n"); return -ENXIO; } + hh_rm_irq_domain = irq_find_host(hh_rm_intc); + if (!hh_rm_irq_domain) { + dev_err(dev, "Failed to get IRQ domain associated with RM\n"); + return -ENXIO; + } hh_rm_msgq_desc = hh_msgq_register(HH_MSGQ_LABEL_RM); if (IS_ERR_OR_NULL(hh_rm_msgq_desc)) diff --git a/drivers/virt/haven/hh_rm_drv_private.h b/drivers/virt/haven/hh_rm_drv_private.h index 243879f4f905..36f990b24189 100644 --- a/drivers/virt/haven/hh_rm_drv_private.h +++ b/drivers/virt/haven/hh_rm_drv_private.h @@ -281,7 +281,6 @@ struct hh_mem_notify_req_payload { /* End Message ID headers */ /* Common function declerations */ -int hh_rm_virq_to_linux_irq(u32 virq, u32 type, u32 trigger); int hh_update_vm_prop_table(enum hh_vm_names vm_name, struct hh_vm_property *vm_prop); void *hh_rm_call(hh_rm_msgid_t message_id, diff --git a/drivers/virt/haven/hh_rm_iface.c b/drivers/virt/haven/hh_rm_iface.c index 896ba49fc195..8d7446836cd9 100644 --- a/drivers/virt/haven/hh_rm_iface.c +++ b/drivers/virt/haven/hh_rm_iface.c @@ -373,7 +373,6 @@ static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) return ret; } -EXPORT_SYMBOL(hh_rm_vm_irq_release); /** * hh_rm_vm_irq_release_notify: Release IRQ back to a VM and notify that it has diff --git a/include/linux/haven/hh_irq_lend.h b/include/linux/haven/hh_irq_lend.h index 2815e6c97be2..f2737395b28d 100644 --- a/include/linux/haven/hh_irq_lend.h +++ b/include/linux/haven/hh_irq_lend.h @@ -26,7 +26,7 @@ int hh_irq_reclaim(enum hh_irq_label label); int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, hh_irq_handle_fn on_lend, void *data); -int hh_irq_accept(enum hh_irq_label label, int hw_irq); +int hh_irq_accept(enum hh_irq_label label, int irq, int type); int hh_irq_release(enum hh_irq_label label); #endif diff --git a/include/linux/haven/hh_rm_drv.h b/include/linux/haven/hh_rm_drv.h index 9dad98a13fcb..dc11541af543 100644 --- a/include/linux/haven/hh_rm_drv.h +++ b/include/linux/haven/hh_rm_drv.h @@ -183,6 +183,9 @@ int hh_rm_register_notifier(struct notifier_block *nb); int hh_rm_unregister_notifier(struct notifier_block *nb); /* Client APIs for IRQ management */ +int hh_rm_virq_to_irq(u32 virq, u32 type); +int hh_rm_irq_to_virq(int irq, u32 *virq); + int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq); int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label, hh_virq_handle_t *virq_handle); @@ -227,6 +230,7 @@ int hh_rm_mem_lend(u8 mem_type, u8 flags, hh_label_t label, int hh_rm_mem_notify(hh_memparcel_handle_t handle, u8 flags, hh_label_t mem_info_tag, struct hh_notify_vmid_desc *vmid_desc); + #else /* RM client register notifications APIs */ static inline int hh_rm_register_notifier(struct notifier_block *nb) @@ -240,6 +244,16 @@ static inline int hh_rm_unregister_notifier(struct notifier_block *nb) } /* Client APIs for IRQ management */ +static inline int hh_rm_virq_to_irq(u32 virq) +{ + return -EINVAL; +} + +static inline int hh_rm_irq_to_virq(int irq, u32 *virq) +{ + return -EINVAL; +} + static inline int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) { return -EINVAL;