diff mbox series

[v17,11/35] virt: gunyah: Translate gh_rm_hyp_resource into gunyah_resource

Message ID 20240222-gunyah-v17-11-1e9da6763d38@quicinc.com
State New
Headers show
Series Drivers for Gunyah hypervisor | expand

Commit Message

Elliot Berman Feb. 22, 2024, 11:16 p.m. UTC
When booting a Gunyah virtual machine, the host VM may gain capabilities
to interact with resources for the guest virtual machine. Examples of
such resources are vCPUs or message queues. To use those resources, we
need to translate the RM response into a gunyah_resource structure which
are useful to Linux drivers. Presently, Linux drivers need only to know
the type of resource, the capability ID, and an interrupt.

On ARM64 systems, the interrupt reported by Gunyah is the GIC interrupt
ID number and always a SPI or extended SPI.

Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
---
 arch/arm64/include/asm/gunyah.h | 36 ++++++++++++++++++++++
 drivers/virt/gunyah/rsc_mgr.c   | 67 +++++++++++++++++++++++++++++++++++++++++
 drivers/virt/gunyah/rsc_mgr.h   |  5 +++
 include/linux/gunyah.h          |  2 ++
 4 files changed, 110 insertions(+)

Comments

Srivatsa Vaddagiri March 11, 2024, 5:39 a.m. UTC | #1
* Elliot Berman <quic_eberman@quicinc.com> [2024-02-22 15:16:34]:

> When booting a Gunyah virtual machine, the host VM may gain capabilities
> to interact with resources for the guest virtual machine. Examples of
> such resources are vCPUs or message queues. To use those resources, we
> need to translate the RM response into a gunyah_resource structure which
> are useful to Linux drivers. Presently, Linux drivers need only to know
> the type of resource, the capability ID, and an interrupt.
> 
> On ARM64 systems, the interrupt reported by Gunyah is the GIC interrupt
> ID number and always a SPI or extended SPI.
> 
> Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>

Minor nit below. LGTM otherwise

Reviewed-by: Srivatsa Vaddagiri <quic_svaddagi@quicinc.com>

> +struct gunyah_resource *
> +gunyah_rm_alloc_resource(struct gunyah_rm *rm,
> +			 struct gunyah_rm_hyp_resource *hyp_resource)
> +{
> +	struct gunyah_resource *ghrsc;
> +	int ret;
> +
> +	ghrsc = kzalloc(sizeof(*ghrsc), GFP_KERNEL);
> +	if (!ghrsc)
> +		return NULL;
> +
> +	ghrsc->type = hyp_resource->type;
> +	ghrsc->capid = le64_to_cpu(hyp_resource->cap_id);
> +	ghrsc->irq = IRQ_NOTCONNECTED;
> +	ghrsc->rm_label = le32_to_cpu(hyp_resource->resource_label);
> +	if (hyp_resource->virq) {
> +		struct irq_fwspec fwspec;
> +
> +
> +		fwspec.fwnode = rm->parent_fwnode;
> +		ret = arch_gunyah_fill_irq_fwspec_params(le32_to_cpu(hyp_resource->virq), &fwspec);
> +		if (ret) {
> +			dev_err(rm->dev,
> +				"Failed to translate interrupt for resource %d label: %d: %d\n",
> +				ghrsc->type, ghrsc->rm_label, ret);

Not bailing on error here appears wrong. Can you check?

> +		}
> +
> +		ret = irq_create_fwspec_mapping(&fwspec);
> +		if (ret < 0) {
> +			dev_err(rm->dev,
> +				"Failed to allocate interrupt for resource %d label: %d: %d\n",
> +				ghrsc->type, ghrsc->rm_label, ret);
> +			kfree(ghrsc);
> +			return NULL;
> +		}
> +		ghrsc->irq = ret;
> +	}
> +
> +	return ghrsc;
> +}
Elliot Berman March 11, 2024, 5:19 p.m. UTC | #2
On Mon, Mar 11, 2024 at 11:09:05AM +0530, Srivatsa Vaddagiri wrote:
> * Elliot Berman <quic_eberman@quicinc.com> [2024-02-22 15:16:34]:
> 
> > When booting a Gunyah virtual machine, the host VM may gain capabilities
> > to interact with resources for the guest virtual machine. Examples of
> > such resources are vCPUs or message queues. To use those resources, we
> > need to translate the RM response into a gunyah_resource structure which
> > are useful to Linux drivers. Presently, Linux drivers need only to know
> > the type of resource, the capability ID, and an interrupt.
> > 
> > On ARM64 systems, the interrupt reported by Gunyah is the GIC interrupt
> > ID number and always a SPI or extended SPI.
> > 
> > Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
> 
> Minor nit below. LGTM otherwise
> 
> Reviewed-by: Srivatsa Vaddagiri <quic_svaddagi@quicinc.com>
> 
> > +struct gunyah_resource *
> > +gunyah_rm_alloc_resource(struct gunyah_rm *rm,
> > +			 struct gunyah_rm_hyp_resource *hyp_resource)
> > +{
> > +	struct gunyah_resource *ghrsc;
> > +	int ret;
> > +
> > +	ghrsc = kzalloc(sizeof(*ghrsc), GFP_KERNEL);
> > +	if (!ghrsc)
> > +		return NULL;
> > +
> > +	ghrsc->type = hyp_resource->type;
> > +	ghrsc->capid = le64_to_cpu(hyp_resource->cap_id);
> > +	ghrsc->irq = IRQ_NOTCONNECTED;
> > +	ghrsc->rm_label = le32_to_cpu(hyp_resource->resource_label);
> > +	if (hyp_resource->virq) {
> > +		struct irq_fwspec fwspec;
> > +
> > +
> > +		fwspec.fwnode = rm->parent_fwnode;
> > +		ret = arch_gunyah_fill_irq_fwspec_params(le32_to_cpu(hyp_resource->virq), &fwspec);
> > +		if (ret) {
> > +			dev_err(rm->dev,
> > +				"Failed to translate interrupt for resource %d label: %d: %d\n",
> > +				ghrsc->type, ghrsc->rm_label, ret);
> 
> Not bailing on error here appears wrong. Can you check?
> 

Ah, yes. I'll return ghrsc here. I think it's better than returning
NULL because user of resource might be able to cope without the
interrupt and can let us get a more helpful kernel log messages because
the higher level VM function will complain.

> > +		}
> > +
> > +		ret = irq_create_fwspec_mapping(&fwspec);
> > +		if (ret < 0) {
> > +			dev_err(rm->dev,
> > +				"Failed to allocate interrupt for resource %d label: %d: %d\n",
> > +				ghrsc->type, ghrsc->rm_label, ret);
> > +			kfree(ghrsc);
> > +			return NULL;
> > +		}
> > +		ghrsc->irq = ret;
> > +	}
> > +
> > +	return ghrsc;
> > +}
Pavan Kondeti April 5, 2024, 3:10 a.m. UTC | #3
On Thu, Feb 22, 2024 at 03:16:34PM -0800, Elliot Berman wrote:
> When booting a Gunyah virtual machine, the host VM may gain capabilities
> to interact with resources for the guest virtual machine. Examples of
> such resources are vCPUs or message queues. To use those resources, we
> need to translate the RM response into a gunyah_resource structure which
> are useful to Linux drivers. Presently, Linux drivers need only to know
> the type of resource, the capability ID, and an interrupt.
> 
> On ARM64 systems, the interrupt reported by Gunyah is the GIC interrupt
> ID number and always a SPI or extended SPI.
> 
> Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
> ---
>  arch/arm64/include/asm/gunyah.h | 36 ++++++++++++++++++++++
>  drivers/virt/gunyah/rsc_mgr.c   | 67 +++++++++++++++++++++++++++++++++++++++++
>  drivers/virt/gunyah/rsc_mgr.h   |  5 +++
>  include/linux/gunyah.h          |  2 ++
>  4 files changed, 110 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/gunyah.h b/arch/arm64/include/asm/gunyah.h
> new file mode 100644
> index 0000000000000..0cd3debe22b64
> --- /dev/null
> +++ b/arch/arm64/include/asm/gunyah.h
> @@ -0,0 +1,36 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +#ifndef _ASM_GUNYAH_H
> +#define _ASM_GUNYAH_H
> +
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +
> +static inline int arch_gunyah_fill_irq_fwspec_params(u32 virq,
> +						 struct irq_fwspec *fwspec)
> +{
> +	/* Assume that Gunyah gave us an SPI or ESPI; defensively check it */
> +	if (WARN(virq < 32, "Unexpected virq: %d\n", virq)) {
> +		return -EINVAL;
> +	} else if (virq <= 1019) {
> +		fwspec->param_count = 3;
> +		fwspec->param[0] = 0; /* GIC_SPI */
> +		fwspec->param[1] = virq - 32; /* virq 32 -> SPI 0 */
> +		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
> +	} else if (WARN(virq < 4096, "Unexpected virq: %d\n", virq)) {
> +		return -EINVAL;
> +	} else if (virq < 5120) {
> +		fwspec->param_count = 3;
> +		fwspec->param[0] = 2; /* GIC_ESPI */
> +		fwspec->param[1] = virq - 4096; /* virq 4096 -> ESPI 0 */
> +		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
> +	} else {
> +		WARN(1, "Unexpected virq: %d\n", virq);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +

__get_intid_range() in gic-v3 driver looks more pleasing. Other than
that the logic for the translation looks good to me.

Thanks,
Pavan
Elliot Berman April 5, 2024, 3:18 p.m. UTC | #4
On Fri, Apr 05, 2024 at 08:40:40AM +0530, Pavan Kondeti wrote:
> On Thu, Feb 22, 2024 at 03:16:34PM -0800, Elliot Berman wrote:
> > When booting a Gunyah virtual machine, the host VM may gain capabilities
> > to interact with resources for the guest virtual machine. Examples of
> > such resources are vCPUs or message queues. To use those resources, we
> > need to translate the RM response into a gunyah_resource structure which
> > are useful to Linux drivers. Presently, Linux drivers need only to know
> > the type of resource, the capability ID, and an interrupt.
> > 
> > On ARM64 systems, the interrupt reported by Gunyah is the GIC interrupt
> > ID number and always a SPI or extended SPI.
> > 
> > Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
> > ---
> >  arch/arm64/include/asm/gunyah.h | 36 ++++++++++++++++++++++
> >  drivers/virt/gunyah/rsc_mgr.c   | 67 +++++++++++++++++++++++++++++++++++++++++
> >  drivers/virt/gunyah/rsc_mgr.h   |  5 +++
> >  include/linux/gunyah.h          |  2 ++
> >  4 files changed, 110 insertions(+)
> > 
> > diff --git a/arch/arm64/include/asm/gunyah.h b/arch/arm64/include/asm/gunyah.h
> > new file mode 100644
> > index 0000000000000..0cd3debe22b64
> > --- /dev/null
> > +++ b/arch/arm64/include/asm/gunyah.h
> > @@ -0,0 +1,36 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
> > + */
> > +#ifndef _ASM_GUNYAH_H
> > +#define _ASM_GUNYAH_H
> > +
> > +#include <linux/irq.h>
> > +#include <linux/irqdomain.h>
> > +
> > +static inline int arch_gunyah_fill_irq_fwspec_params(u32 virq,
> > +						 struct irq_fwspec *fwspec)
> > +{
> > +	/* Assume that Gunyah gave us an SPI or ESPI; defensively check it */
> > +	if (WARN(virq < 32, "Unexpected virq: %d\n", virq)) {
> > +		return -EINVAL;
> > +	} else if (virq <= 1019) {
> > +		fwspec->param_count = 3;
> > +		fwspec->param[0] = 0; /* GIC_SPI */
> > +		fwspec->param[1] = virq - 32; /* virq 32 -> SPI 0 */
> > +		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
> > +	} else if (WARN(virq < 4096, "Unexpected virq: %d\n", virq)) {
> > +		return -EINVAL;
> > +	} else if (virq < 5120) {
> > +		fwspec->param_count = 3;
> > +		fwspec->param[0] = 2; /* GIC_ESPI */
> > +		fwspec->param[1] = virq - 4096; /* virq 4096 -> ESPI 0 */
> > +		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
> > +	} else {
> > +		WARN(1, "Unexpected virq: %d\n", virq);
> > +		return -EINVAL;
> > +	}
> > +	return 0;
> > +}
> > +
> 
> __get_intid_range() in gic-v3 driver looks more pleasing. Other than
> that the logic for the translation looks good to me.

Agreed, updated for v18.

static inline int arch_gunyah_fill_irq_fwspec_params(u32 virq,
						 struct irq_fwspec *fwspec)
{
	/* Assume that Gunyah gave us an SPI or ESPI; defensively check it */
	switch (virq) {
	case 32 ... 1019:
		fwspec->param_count = 3;
		fwspec->param[0] = 0; /* GIC_SPI */
		fwspec->param[1] = virq - 32; /* virq 32 -> SPI 0 */
		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
		break;
	case 4096 ... 5119:
		fwspec->param_count = 3;
		fwspec->param[0] = 2; /* GIC_ESPI */
		fwspec->param[1] = virq - 4096; /* virq 4096 -> ESPI 0 */
		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
		break;
	default:
		WARN(1, "Unexpected virq: %d\n", virq)
		return -EINVAL;
	}
	return 0;
}
diff mbox series

Patch

diff --git a/arch/arm64/include/asm/gunyah.h b/arch/arm64/include/asm/gunyah.h
new file mode 100644
index 0000000000000..0cd3debe22b64
--- /dev/null
+++ b/arch/arm64/include/asm/gunyah.h
@@ -0,0 +1,36 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef _ASM_GUNYAH_H
+#define _ASM_GUNYAH_H
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+
+static inline int arch_gunyah_fill_irq_fwspec_params(u32 virq,
+						 struct irq_fwspec *fwspec)
+{
+	/* Assume that Gunyah gave us an SPI or ESPI; defensively check it */
+	if (WARN(virq < 32, "Unexpected virq: %d\n", virq)) {
+		return -EINVAL;
+	} else if (virq <= 1019) {
+		fwspec->param_count = 3;
+		fwspec->param[0] = 0; /* GIC_SPI */
+		fwspec->param[1] = virq - 32; /* virq 32 -> SPI 0 */
+		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
+	} else if (WARN(virq < 4096, "Unexpected virq: %d\n", virq)) {
+		return -EINVAL;
+	} else if (virq < 5120) {
+		fwspec->param_count = 3;
+		fwspec->param[0] = 2; /* GIC_ESPI */
+		fwspec->param[1] = virq - 4096; /* virq 4096 -> ESPI 0 */
+		fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
+	} else {
+		WARN(1, "Unexpected virq: %d\n", virq);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif
diff --git a/drivers/virt/gunyah/rsc_mgr.c b/drivers/virt/gunyah/rsc_mgr.c
index fb0efe68f0e22..c320c9ceaf8ac 100644
--- a/drivers/virt/gunyah/rsc_mgr.c
+++ b/drivers/virt/gunyah/rsc_mgr.c
@@ -9,9 +9,12 @@ 
 #include <linux/mutex.h>
 #include <linux/notifier.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/miscdevice.h>
 
+#include <asm/gunyah.h>
+
 #include "rsc_mgr.h"
 #include "vm_mgr.h"
 
@@ -121,6 +124,7 @@  struct gunyah_rm_message {
  * @send_ready: completed when we know Tx message queue can take more messages
  * @nh: notifier chain for clients interested in RM notification messages
  * @miscdev: /dev/gunyah
+ * @parent_fwnode: Parent IRQ fwnode to translate Gunyah hwirqs to Linux irqs
  */
 struct gunyah_rm {
 	struct device *dev;
@@ -138,6 +142,7 @@  struct gunyah_rm {
 	struct blocking_notifier_head nh;
 
 	struct miscdevice miscdev;
+	struct fwnode_handle *parent_fwnode;
 };
 
 /**
@@ -178,6 +183,53 @@  static inline int gunyah_rm_error_remap(enum gunyah_rm_error rm_error)
 	}
 }
 
+struct gunyah_resource *
+gunyah_rm_alloc_resource(struct gunyah_rm *rm,
+			 struct gunyah_rm_hyp_resource *hyp_resource)
+{
+	struct gunyah_resource *ghrsc;
+	int ret;
+
+	ghrsc = kzalloc(sizeof(*ghrsc), GFP_KERNEL);
+	if (!ghrsc)
+		return NULL;
+
+	ghrsc->type = hyp_resource->type;
+	ghrsc->capid = le64_to_cpu(hyp_resource->cap_id);
+	ghrsc->irq = IRQ_NOTCONNECTED;
+	ghrsc->rm_label = le32_to_cpu(hyp_resource->resource_label);
+	if (hyp_resource->virq) {
+		struct irq_fwspec fwspec;
+
+
+		fwspec.fwnode = rm->parent_fwnode;
+		ret = arch_gunyah_fill_irq_fwspec_params(le32_to_cpu(hyp_resource->virq), &fwspec);
+		if (ret) {
+			dev_err(rm->dev,
+				"Failed to translate interrupt for resource %d label: %d: %d\n",
+				ghrsc->type, ghrsc->rm_label, ret);
+		}
+
+		ret = irq_create_fwspec_mapping(&fwspec);
+		if (ret < 0) {
+			dev_err(rm->dev,
+				"Failed to allocate interrupt for resource %d label: %d: %d\n",
+				ghrsc->type, ghrsc->rm_label, ret);
+			kfree(ghrsc);
+			return NULL;
+		}
+		ghrsc->irq = ret;
+	}
+
+	return ghrsc;
+}
+
+void gunyah_rm_free_resource(struct gunyah_resource *ghrsc)
+{
+	irq_dispose_mapping(ghrsc->irq);
+	kfree(ghrsc);
+}
+
 static int gunyah_rm_init_message_payload(struct gunyah_rm_message *message,
 					  const void *msg, size_t hdr_size,
 					  size_t msg_size)
@@ -707,6 +759,7 @@  static int gunyah_rm_probe_rx_msgq(struct gunyah_rm *rm,
 
 static int gunyah_rm_probe(struct platform_device *pdev)
 {
+	struct device_node *parent_irq_node;
 	struct gunyah_rm *rm;
 	int ret;
 
@@ -734,6 +787,20 @@  static int gunyah_rm_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	parent_irq_node = of_irq_find_parent(pdev->dev.of_node);
+	if (!parent_irq_node) {
+		dev_err(&pdev->dev,
+			"Failed to find interrupt parent of resource manager\n");
+		return -ENODEV;
+	}
+
+	rm->parent_fwnode = of_node_to_fwnode(parent_irq_node);
+	if (!rm->parent_fwnode) {
+		dev_err(&pdev->dev,
+			"Failed to find interrupt parent domain of resource manager\n");
+		return -ENODEV;
+	}
+
 	rm->miscdev.parent = &pdev->dev;
 	rm->miscdev.name = "gunyah";
 	rm->miscdev.minor = MISC_DYNAMIC_MINOR;
diff --git a/drivers/virt/gunyah/rsc_mgr.h b/drivers/virt/gunyah/rsc_mgr.h
index 205b9ea735e53..52711de77bb79 100644
--- a/drivers/virt/gunyah/rsc_mgr.h
+++ b/drivers/virt/gunyah/rsc_mgr.h
@@ -99,6 +99,11 @@  struct gunyah_rm_hyp_resources {
 int gunyah_rm_get_hyp_resources(struct gunyah_rm *rm, u16 vmid,
 				struct gunyah_rm_hyp_resources **resources);
 
+struct gunyah_resource *
+gunyah_rm_alloc_resource(struct gunyah_rm *rm,
+			 struct gunyah_rm_hyp_resource *hyp_resource);
+void gunyah_rm_free_resource(struct gunyah_resource *ghrsc);
+
 int gunyah_rm_call(struct gunyah_rm *rsc_mgr, u32 message_id,
 		   const void *req_buf, size_t req_buf_size, void **resp_buf,
 		   size_t *resp_buf_size);
diff --git a/include/linux/gunyah.h b/include/linux/gunyah.h
index acd70f9824253..45033bdb5e151 100644
--- a/include/linux/gunyah.h
+++ b/include/linux/gunyah.h
@@ -29,6 +29,8 @@  struct gunyah_resource {
 	enum gunyah_resource_type type;
 	u64 capid;
 	unsigned int irq;
+
+	u32 rm_label;
 };
 
 /******************************************************************************/