Gunyah is a Type-1 hypervisor independent of any
high-level OS kernel, and runs in a higher CPU privilege level. It does
not depend on any lower-privileged OS kernel/code for its core
functionality. This increases its security and can support a much smaller
trusted computing base than a Type-2 hypervisor.
Gunyah is an open source hypervisor. The source repo is available at
https://github.com/quic/gunyah-hypervisor.
The diagram below shows the architecture.
::
VM A VM B
+-----+ +-----+ | +-----+ +-----+ +-----+
| | | | | | | | | | |
EL0 | APP | | APP | | | APP | | APP | | APP |
| | | | | | | | | | |
+-----+ +-----+ | +-----+ +-----+ +-----+
---------------------|-------------------------
+--------------+ | +----------------------+
| | | | |
EL1 | Linux Kernel | | |Linux kernel/Other OS | ...
| | | | |
+--------------+ | +----------------------+
--------hvc/smc------|------hvc/smc------------
+----------------------------------------+
| |
EL2 | Gunyah Hypervisor |
| |
+----------------------------------------+
Gunyah provides these following features.
- Threads and Scheduling: The scheduler schedules virtual CPUs (VCPUs) on
physical CPUs and enables time-sharing of the CPUs.
- Memory Management: Gunyah tracks memory ownership and use of all memory
under its control. Memory partitioning between VMs is a fundamental
security feature.
- Interrupt Virtualization: All interrupts are handled in the hypervisor
and routed to the assigned VM.
- Inter-VM Communication: There are several different mechanisms provided
for communicating between VMs.
- Device Virtualization: Para-virtualization of devices is supported using
inter-VM communication. Low level system features and devices such as
interrupt controllers are supported with emulation where required.
This series adds the basic framework for detecting that Linux is running
under Gunyah as a virtual machine, communication with the Gunyah Resource
Manager, and a basic virtual machine manager capable of launching virtual
machines. In a future series, I'll add more functionality to the VM Manager,
but functionality is kept limited here to reduce the number of patches to
review.
Changes in v6:
- *Replace gunyah-console with gunyah VM Manager*
- Move include/asm-generic/gunyah.h into include/linux/gunyah.h
- s/gunyah_msgq/gh_msgq/
- Minor tweaks and documentation tidying based on comments from Jiri, Greg, Arnd, Dmitry, and Bagas.
Changes in v5: https://lore.kernel.org/all/[email protected]/
- Dropped sysfs nodes
- Switch from aux bus to Gunyah RM bus for the subdevices
- Cleaning up RM console
Changes in v4: https://lore.kernel.org/all/[email protected]/
- Tidied up documentation throughout based on questions/feedback received
- Switched message queue implementation to use mailboxes
- Renamed "gunyah_device" as "gunyah_resource"
Changes in v3: https://lore.kernel.org/all/[email protected]/
- /Maintained/Supported/ in MAINTAINERS
- Tidied up documentation throughout based on questions/feedback received
- Moved hypercalls into arch/arm64/gunyah/; following hyper-v's implementation
- Drop opaque typedefs
- Move sysfs nodes under /sys/hypervisor/gunyah/
- Moved Gunyah console driver to drivers/tty/
- Reworked gunyah_device design to drop the Gunyah bus.
Changes in v2: https://lore.kernel.org/all/[email protected]/
- DT bindings clean up
- Switch hypercalls to follow SMCCC
v1: https://lore.kernel.org/all/[email protected]/
Elliot Berman (21):
docs: gunyah: Introduce Gunyah Hypervisor
dt-bindings: Add binding for gunyah hypervisor
gunyah: Common types and error codes for Gunyah hypercalls
arm64: smccc: Include alternative-macros.h
virt: gunyah: Add hypercalls to identify Gunyah
virt: gunyah: Identify hypervisor version
mailbox: Allow direct registration to a channel
virt: gunyah: msgq: Add hypercalls to send and receive messages
mailbox: Add Gunyah message queue mailbox
gunyah: rsc_mgr: Add resource manager RPC core
gunyah: rsc_mgr: Add subdevices bus
gunyah: rsc_mgr: Add VM lifecycle RPC
gunyah: vm_mgr: Introduce basic VM Manager
gunyah: rsc_mgr: Add RPC for sharing memory
gunyah: vm_mgr: Add/remove user memory regions
gunyah: vm_mgr: Add ioctls to support basic non-proxy VM boot
samples: Add sample userspace Gunyah VM Manager
gunyah: rsc_mgr: Add platform ops on mem_lend/mem_reclaim
firmware: qcom_scm: Use fixed width src vm bitmap
firmware: qcom_scm: Register Gunyah platform ops
docs: gunyah: Document Gunyah VM Manager
.../bindings/firmware/gunyah-hypervisor.yaml | 86 +++
.../userspace-api/ioctl/ioctl-number.rst | 1 +
Documentation/virt/gunyah/index.rst | 115 +++
Documentation/virt/gunyah/message-queue.rst | 63 ++
Documentation/virt/gunyah/vm-manager.rst | 94 +++
Documentation/virt/index.rst | 1 +
MAINTAINERS | 13 +
arch/arm64/Kbuild | 1 +
arch/arm64/gunyah/Makefile | 1 +
arch/arm64/gunyah/gunyah_hypercall.c | 102 +++
arch/arm64/include/uapi/asm/gunyah.h | 17 +
drivers/firmware/qcom_scm.c | 126 +++-
drivers/mailbox/Kconfig | 10 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/gunyah-msgq.c | 225 ++++++
drivers/mailbox/mailbox.c | 96 ++-
drivers/misc/fastrpc.c | 6 +-
drivers/net/wireless/ath/ath10k/qmi.c | 4 +-
drivers/remoteproc/qcom_q6v5_mss.c | 8 +-
drivers/soc/qcom/rmtfs_mem.c | 2 +-
drivers/virt/Kconfig | 1 +
drivers/virt/Makefile | 1 +
drivers/virt/gunyah/Kconfig | 35 +
drivers/virt/gunyah/Makefile | 7 +
drivers/virt/gunyah/gunyah.c | 46 ++
drivers/virt/gunyah/rsc_mgr.c | 690 ++++++++++++++++++
drivers/virt/gunyah/rsc_mgr.h | 146 ++++
drivers/virt/gunyah/rsc_mgr_bus.c | 83 +++
drivers/virt/gunyah/rsc_mgr_rpc.c | 475 ++++++++++++
drivers/virt/gunyah/vm_mgr.c | 288 ++++++++
drivers/virt/gunyah/vm_mgr.h | 52 ++
drivers/virt/gunyah/vm_mgr_mm.c | 245 +++++++
include/linux/arm-smccc.h | 1 +
include/linux/gunyah.h | 167 +++++
include/linux/gunyah_rsc_mgr.h | 161 ++++
include/linux/mailbox_client.h | 1 +
include/linux/mod_devicetable.h | 8 +
include/linux/qcom_scm.h | 2 +-
include/uapi/linux/gunyah.h | 53 ++
samples/Kconfig | 10 +
samples/Makefile | 1 +
samples/gunyah/.gitignore | 2 +
samples/gunyah/Makefile | 6 +
samples/gunyah/gunyah_vmm.c | 270 +++++++
samples/gunyah/sample_vm.dts | 69 ++
scripts/mod/devicetable-offsets.c | 3 +
scripts/mod/file2alias.c | 10 +
47 files changed, 3763 insertions(+), 43 deletions(-)
create mode 100644 Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
create mode 100644 Documentation/virt/gunyah/index.rst
create mode 100644 Documentation/virt/gunyah/message-queue.rst
create mode 100644 Documentation/virt/gunyah/vm-manager.rst
create mode 100644 arch/arm64/gunyah/Makefile
create mode 100644 arch/arm64/gunyah/gunyah_hypercall.c
create mode 100644 arch/arm64/include/uapi/asm/gunyah.h
create mode 100644 drivers/mailbox/gunyah-msgq.c
create mode 100644 drivers/virt/gunyah/Kconfig
create mode 100644 drivers/virt/gunyah/Makefile
create mode 100644 drivers/virt/gunyah/gunyah.c
create mode 100644 drivers/virt/gunyah/rsc_mgr.c
create mode 100644 drivers/virt/gunyah/rsc_mgr.h
create mode 100644 drivers/virt/gunyah/rsc_mgr_bus.c
create mode 100644 drivers/virt/gunyah/rsc_mgr_rpc.c
create mode 100644 drivers/virt/gunyah/vm_mgr.c
create mode 100644 drivers/virt/gunyah/vm_mgr.h
create mode 100644 drivers/virt/gunyah/vm_mgr_mm.c
create mode 100644 include/linux/gunyah.h
create mode 100644 include/linux/gunyah_rsc_mgr.h
create mode 100644 include/uapi/linux/gunyah.h
create mode 100644 samples/gunyah/.gitignore
create mode 100644 samples/gunyah/Makefile
create mode 100644 samples/gunyah/gunyah_vmm.c
create mode 100644 samples/gunyah/sample_vm.dts
base-commit: 247f34f7b80357943234f93f247a1ae6b6c3a740
--
2.25.1
Gunyah VM manager is a kernel moduel which exposes an interface to
Gunyah userspace to load, run, and interact with other Gunyah virtual
machines. The interface is a character device at /dev/gunyah.
Add a basic VM manager driver. Upcoming patches will add more ioctls
into this driver.
Co-developed-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Elliot Berman <[email protected]>
---
.../userspace-api/ioctl/ioctl-number.rst | 1 +
drivers/virt/gunyah/Kconfig | 8 +
drivers/virt/gunyah/Makefile | 3 +
drivers/virt/gunyah/vm_mgr.c | 152 ++++++++++++++++++
drivers/virt/gunyah/vm_mgr.h | 17 ++
include/uapi/linux/gunyah.h | 23 +++
6 files changed, 204 insertions(+)
create mode 100644 drivers/virt/gunyah/vm_mgr.c
create mode 100644 drivers/virt/gunyah/vm_mgr.h
create mode 100644 include/uapi/linux/gunyah.h
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 5f81e2a24a5c..1fa1a5877bd7 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -136,6 +136,7 @@ Code Seq# Include File Comments
'F' DD video/sstfb.h conflict!
'G' 00-3F drivers/misc/sgi-gru/grulib.h conflict!
'G' 00-0F xen/gntalloc.h, xen/gntdev.h conflict!
+'G' 40-4f linux/gunyah.h
'H' 00-7F linux/hiddev.h conflict!
'H' 00-0F linux/hidraw.h conflict!
'H' 01 linux/mei.h conflict!
diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
index 4de88d80aa7b..c5d239159118 100644
--- a/drivers/virt/gunyah/Kconfig
+++ b/drivers/virt/gunyah/Kconfig
@@ -25,3 +25,11 @@ config GUNYAH_RESORUCE_MANAGER
Say Y/M here if unsure.
+config GUNYAH_VM_MANAGER
+ tristate "Gunyah VM Manager"
+ depends on GUNYAH_RESORUCE_MANAGER
+ help
+ Gunyah VM manager is a kernel module which exposes an interface to
+ Gunyah userspace to load, run, and interact with other Gunyah
+ virtual machines. This module is required to launch other virtual
+ machines.
diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile
index 09c1bbd28b48..a69b1e2273af 100644
--- a/drivers/virt/gunyah/Makefile
+++ b/drivers/virt/gunyah/Makefile
@@ -2,3 +2,6 @@ obj-$(CONFIG_GUNYAH) += gunyah.o
gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_rpc.o rsc_mgr_bus.o
obj-$(CONFIG_GUNYAH_RESORUCE_MANAGER) += gunyah_rsc_mgr.o
+
+gunyah_vm_mgr-y += vm_mgr.o
+obj-$(CONFIG_GUNYAH_VM_MANAGER) += gunyah_vm_mgr.o
diff --git a/drivers/virt/gunyah/vm_mgr.c b/drivers/virt/gunyah/vm_mgr.c
new file mode 100644
index 000000000000..c48853dba11d
--- /dev/null
+++ b/drivers/virt/gunyah/vm_mgr.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "gh_vm_mgr: " fmt
+
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/gunyah_rsc_mgr.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+
+#include <uapi/linux/gunyah.h>
+
+#include "vm_mgr.h"
+
+static __must_check struct gunyah_vm *gunyah_vm_alloc(void)
+{
+ struct gunyah_vm *ghvm;
+ int ret;
+
+ ret = gh_rm_alloc_vmid(0);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ ghvm = kzalloc(sizeof(*ghvm), GFP_KERNEL);
+ if (!ghvm)
+ return ghvm;
+
+ ghvm->vmid = ret;
+
+ return ghvm;
+}
+
+static long gh_vm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long r;
+
+ switch (cmd) {
+ default:
+ r = -ENOTTY;
+ break;
+ }
+
+ return r;
+}
+
+static int gh_vm_release(struct inode *inode, struct file *filp)
+{
+ struct gunyah_vm *ghvm = filp->private_data;
+
+ kfree(ghvm);
+ return 0;
+}
+
+static const struct file_operations gh_vm_fops = {
+ .unlocked_ioctl = gh_vm_ioctl,
+ .release = gh_vm_release,
+ .llseek = noop_llseek,
+};
+
+static long gh_dev_ioctl_create_vm(unsigned long arg)
+{
+ struct gunyah_vm *ghvm;
+ struct file *file;
+ int fd, err;
+
+ /* arg reserved for future use. */
+ if (arg)
+ return -EINVAL;
+
+ ghvm = gunyah_vm_alloc();
+ if (IS_ERR_OR_NULL(ghvm))
+ return PTR_ERR(ghvm) ? : -ENOMEM;
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ err = fd;
+ goto err_destroy_vm;
+ }
+
+ file = anon_inode_getfile("gunyah-vm", &gh_vm_fops, ghvm, O_RDWR);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_put_fd;
+ }
+
+ fd_install(fd, file);
+
+ return fd;
+
+err_put_fd:
+ put_unused_fd(fd);
+err_destroy_vm:
+ kfree(ghvm);
+ return err;
+}
+
+static long gh_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case GH_CREATE_VM:
+ return gh_dev_ioctl_create_vm(arg);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct file_operations gh_dev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = gh_dev_ioctl,
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice gh_dev = {
+ .name = "gunyah",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &gh_dev_fops,
+};
+
+static int vm_mgr_probe(struct device *dev)
+{
+ return misc_register(&gh_dev);
+}
+
+static int vm_mgr_remove(struct device *dev)
+{
+ misc_deregister(&gh_dev);
+
+ return 0;
+}
+
+static struct gunyah_rsc_mgr_device_id vm_mgr_ids[] = {
+ { .name = GH_RM_DEVICE_VM_MGR },
+ {}
+};
+MODULE_DEVICE_TABLE(gunyah_rsc_mgr, vm_mgr_ids);
+
+static struct gh_rm_driver vm_mgr_drv = {
+ .drv = {
+ .name = KBUILD_MODNAME,
+ .probe = vm_mgr_probe,
+ .remove = vm_mgr_remove,
+ },
+ .id_table = vm_mgr_ids,
+};
+module_gh_rm_driver(vm_mgr_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Gunyah VM Manager");
+
diff --git a/drivers/virt/gunyah/vm_mgr.h b/drivers/virt/gunyah/vm_mgr.h
new file mode 100644
index 000000000000..d306ff5eac82
--- /dev/null
+++ b/drivers/virt/gunyah/vm_mgr.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _GH_PRIV_VM_MGR_H
+#define _GH_PRIV_VM_MGR_H
+
+#include <linux/gunyah_rsc_mgr.h>
+
+#include <uapi/linux/gunyah.h>
+
+struct gunyah_vm {
+ u16 vmid;
+};
+
+#endif
diff --git a/include/uapi/linux/gunyah.h b/include/uapi/linux/gunyah.h
new file mode 100644
index 000000000000..37ea6bd4c2fd
--- /dev/null
+++ b/include/uapi/linux/gunyah.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _UAPI_LINUX_GUNYAH
+#define _UAPI_LINUX_GUNYAH
+
+/*
+ * Userspace interface for /dev/gunyah - gunyah based virtual machine
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define GH_IOCTL_TYPE 'G'
+
+/*
+ * ioctls for /dev/gunyah fds:
+ */
+#define GH_CREATE_VM _IO(GH_IOCTL_TYPE, 0x40) /* Returns a Gunyah VM fd */
+
+#endif
--
2.25.1
Support virtual mailbox controllers and clients which are not platform
devices or come from the devicetree by allowing them to match client to
channel via some other mechanism.
Signed-off-by: Elliot Berman <[email protected]>
---
drivers/mailbox/mailbox.c | 96 ++++++++++++++++++++++++----------
include/linux/mailbox_client.h | 1 +
2 files changed, 69 insertions(+), 28 deletions(-)
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 4229b9b5da98..adf36c05fa43 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -317,6 +317,71 @@ int mbox_flush(struct mbox_chan *chan, unsigned long timeout)
}
EXPORT_SYMBOL_GPL(mbox_flush);
+static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
+{
+ struct device *dev = cl->dev;
+ unsigned long flags;
+ int ret;
+
+ if (chan->cl || !try_module_get(chan->mbox->dev->driver->owner)) {
+ dev_dbg(dev, "%s: mailbox not free\n", __func__);
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->msg_free = 0;
+ chan->msg_count = 0;
+ chan->active_req = NULL;
+ chan->cl = cl;
+ init_completion(&chan->tx_complete);
+
+ if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
+ chan->txdone_method = TXDONE_BY_ACK;
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (chan->mbox->ops->startup) {
+ ret = chan->mbox->ops->startup(chan);
+
+ if (ret) {
+ dev_err(dev, "Unable to startup the chan (%d)\n", ret);
+ mbox_free_channel(chan);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * mbox_bind_client - Request a mailbox channel.
+ * @chan: The mailbox channel to bind the client to.
+ * @cl: Identity of the client requesting the channel.
+ *
+ * The Client specifies its requirements and capabilities while asking for
+ * a mailbox channel. It can't be called from atomic context.
+ * The channel is exclusively allocated and can't be used by another
+ * client before the owner calls mbox_free_channel.
+ * After assignment, any packet received on this channel will be
+ * handed over to the client via the 'rx_callback'.
+ * The framework holds reference to the client, so the mbox_client
+ * structure shouldn't be modified until the mbox_free_channel returns.
+ *
+ * Return: 0 if the channel was assigned to the client successfully.
+ * <0 for request failure.
+ */
+int mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
+{
+ int ret;
+
+ mutex_lock(&con_mutex);
+ ret = __mbox_bind_client(chan, cl);
+ mutex_unlock(&con_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mbox_bind_client);
+
/**
* mbox_request_channel - Request a mailbox channel.
* @cl: Identity of the client requesting the channel.
@@ -340,7 +405,6 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
struct mbox_controller *mbox;
struct of_phandle_args spec;
struct mbox_chan *chan;
- unsigned long flags;
int ret;
if (!dev || !dev->of_node) {
@@ -372,33 +436,9 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
return chan;
}
- if (chan->cl || !try_module_get(mbox->dev->driver->owner)) {
- dev_dbg(dev, "%s: mailbox not free\n", __func__);
- mutex_unlock(&con_mutex);
- return ERR_PTR(-EBUSY);
- }
-
- spin_lock_irqsave(&chan->lock, flags);
- chan->msg_free = 0;
- chan->msg_count = 0;
- chan->active_req = NULL;
- chan->cl = cl;
- init_completion(&chan->tx_complete);
-
- if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
- chan->txdone_method = TXDONE_BY_ACK;
-
- spin_unlock_irqrestore(&chan->lock, flags);
-
- if (chan->mbox->ops->startup) {
- ret = chan->mbox->ops->startup(chan);
-
- if (ret) {
- dev_err(dev, "Unable to startup the chan (%d)\n", ret);
- mbox_free_channel(chan);
- chan = ERR_PTR(ret);
- }
- }
+ ret = __mbox_bind_client(chan, cl);
+ if (ret)
+ chan = ERR_PTR(ret);
mutex_unlock(&con_mutex);
return chan;
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
index 65229a45590f..734694912ef7 100644
--- a/include/linux/mailbox_client.h
+++ b/include/linux/mailbox_client.h
@@ -37,6 +37,7 @@ struct mbox_client {
void (*tx_done)(struct mbox_client *cl, void *mssg, int r);
};
+int mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl);
struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
const char *name);
struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
--
2.25.1
Add Gunyah Resource Manager RPC to launch an unauthenticated VM.
Signed-off-by: Elliot Berman <[email protected]>
---
drivers/virt/gunyah/Makefile | 2 +-
drivers/virt/gunyah/rsc_mgr.h | 55 +++++++
drivers/virt/gunyah/rsc_mgr_rpc.c | 264 ++++++++++++++++++++++++++++++
include/linux/gunyah_rsc_mgr.h | 54 ++++++
4 files changed, 374 insertions(+), 1 deletion(-)
create mode 100644 drivers/virt/gunyah/rsc_mgr_rpc.c
diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile
index a9169af6c61f..09c1bbd28b48 100644
--- a/drivers/virt/gunyah/Makefile
+++ b/drivers/virt/gunyah/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_GUNYAH) += gunyah.o
-gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_bus.o
+gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_rpc.o rsc_mgr_bus.o
obj-$(CONFIG_GUNYAH_RESORUCE_MANAGER) += gunyah_rsc_mgr.o
diff --git a/drivers/virt/gunyah/rsc_mgr.h b/drivers/virt/gunyah/rsc_mgr.h
index 129e9d514f2a..247664b8b008 100644
--- a/drivers/virt/gunyah/rsc_mgr.h
+++ b/drivers/virt/gunyah/rsc_mgr.h
@@ -28,6 +28,61 @@
#define GH_RM_ERROR_IRQ_INUSE 0x10
#define GH_RM_ERROR_IRQ_RELEASED 0x11
+/* Message IDs: VM Management */
+#define GH_RM_RPC_VM_ALLOC_VMID 0x56000001
+#define GH_RM_RPC_VM_DEALLOC_VMID 0x56000002
+#define GH_RM_RPC_VM_START 0x56000004
+#define GH_RM_RPC_VM_STOP 0x56000005
+#define GH_RM_RPC_VM_CONFIG_IMAGE 0x56000009
+#define GH_RM_RPC_VM_INIT 0x5600000B
+#define GH_RM_RPC_VM_GET_HYP_RESOURCES 0x56000020
+#define GH_RM_RPC_VM_GET_VMID 0x56000024
+#define GH_RM_RPC_VM_SET_BOOT_CONTEXT 0x56000031
+
+/* Call: CONSOLE_OPEN, CONSOLE_CLOSE, CONSOLE_FLUSH */
+struct gh_vm_common_vmid_req {
+ u16 vmid;
+ u16 reserved0;
+} __packed;
+
+/* Call: VM_STOP */
+struct gh_vm_stop_req {
+ u16 vmid;
+ u8 flags;
+ u8 reserved;
+ u32 stop_reason;
+} __packed;
+
+/* Call: VM_CONFIG_IMAGE */
+struct gh_vm_config_image_req {
+ u16 vmid;
+ u16 auth_mech;
+ u32 mem_handle;
+ u32 image_offset_low;
+ u32 image_offset_high;
+ u32 image_size_low;
+ u32 image_size_high;
+ u32 dtb_offset_low;
+ u32 dtb_offset_high;
+ u32 dtb_size_low;
+ u32 dtb_size_high;
+} __packed;
+
+/* Call: GET_HYP_RESOURCES */
+struct gh_vm_get_hyp_resources_resp {
+ u32 n_entries;
+ struct gh_rm_hyp_resource entries[];
+} __packed;
+
+/* Call: SET_BOOT_CONTEXT */
+struct gh_vm_set_boot_context_req {
+ u16 vmid;
+ u8 reg_set;
+ u8 reg_idx;
+ u32 val_low;
+ u32 val_high;
+} __packed;
+
int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
void **resp_buf, size_t *resp_buff_size);
diff --git a/drivers/virt/gunyah/rsc_mgr_rpc.c b/drivers/virt/gunyah/rsc_mgr_rpc.c
new file mode 100644
index 000000000000..33d27690c16e
--- /dev/null
+++ b/drivers/virt/gunyah/rsc_mgr_rpc.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "gh_rsc_mgr: " fmt
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/gunyah_rsc_mgr.h>
+
+#include "rsc_mgr.h"
+
+/*
+ * Several RM calls take only a VMID as a parameter and give only standard
+ * response back. Deduplicate boilerplate code by using this common call.
+ */
+static int gh_rm_common_vmid_call(u32 message_id, u16 vmid)
+{
+ void *resp = NULL;
+ struct gh_vm_common_vmid_req req_payload = {
+ .vmid = vmid,
+ };
+ size_t resp_size;
+ int ret;
+
+ ret = gh_rm_call(message_id, &req_payload, sizeof(req_payload), &resp, &resp_size);
+ if (!ret)
+ kfree(resp);
+
+ WARN_ON(!ret && resp_size);
+
+ return ret;
+}
+
+/**
+ * gh_rm_alloc_vmid() - Allocate a new VM in Gunyah. Returns the VM identifier.
+ * @vmid: Use GH_VMID_INVAL to dynamically allocate a VM. A reserved VMID can also be requested
+ * for a special-purpose platform-defined VM.
+ *
+ * Returns - the allocated VMID or negative value on error
+ */
+int gh_rm_alloc_vmid(u16 vmid)
+{
+ void *resp;
+ struct gh_vm_common_vmid_req req_payload = {
+ .vmid = vmid,
+ };
+ struct gh_vm_common_vmid_req *resp_payload;
+ size_t resp_size;
+ int ret;
+
+ if (vmid == GH_VMID_INVAL)
+ vmid = 0;
+
+ ret = gh_rm_call(GH_RM_RPC_VM_ALLOC_VMID, &req_payload, sizeof(req_payload), &resp,
+ &resp_size);
+ if (ret)
+ return ret;
+
+ if (!vmid) {
+ if (resp_size != sizeof(*resp_payload)) {
+ pr_warn("Incorrect response size for ALLOC_VMID: %lu\n", resp_size);
+ ret = -EINVAL;
+ } else {
+ resp_payload = resp;
+ ret = resp_payload->vmid;
+ }
+ } else if (resp_size) {
+ pr_warn("Received unexpected payload for ALLOC_VMID: %lu\n", resp_size);
+ }
+ kfree(resp);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_alloc_vmid);
+
+/**
+ * gh_rm_dealloc_vmid() - Dispose the VMID
+ * @vmid: VM identifier
+ */
+int gh_rm_dealloc_vmid(u16 vmid)
+{
+ return gh_rm_common_vmid_call(GH_RM_RPC_VM_DEALLOC_VMID, vmid);
+}
+EXPORT_SYMBOL_GPL(gh_rm_dealloc_vmid);
+
+/**
+ * gh_rm_vm_start() - Move the VM into "ready to run" state
+ * @vmid: VM identifier
+ *
+ * On VMs which use proxy scheduling, vcpu_run is needed to actually run the VM.
+ * On VMs which use Gunyah's scheduling, the vCPUs start executing in accordance with Gunyah
+ * scheduling policies.
+ */
+int gh_rm_vm_start(u16 vmid)
+{
+ return gh_rm_common_vmid_call(GH_RM_RPC_VM_START, vmid);
+}
+EXPORT_SYMBOL_GPL(gh_rm_vm_start);
+
+/**
+ * gh_rm_vm_stop() - Send a request to Resource Manager VM to stop a VM.
+ * @vmid: VM identifier
+ */
+int gh_rm_vm_stop(u16 vmid)
+{
+ struct gh_vm_stop_req req_payload = { 0 };
+ void *resp;
+ size_t resp_size;
+ int ret;
+
+ req_payload.vmid = vmid;
+
+ ret = gh_rm_call(GH_RM_RPC_VM_STOP, &req_payload, sizeof(req_payload), &resp, &resp_size);
+ if (ret)
+ return ret;
+ kfree(resp);
+
+ if (resp_size)
+ pr_warn("Received unexpected payload for VM_STOP: %lu\n", resp_size);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_vm_stop);
+
+int gh_rm_vm_configure(u16 vmid, enum gh_rm_vm_auth_mechanism auth_mechanism, u32 mem_handle,
+ u64 image_offset, u64 image_size, u64 dtb_offset, u64 dtb_size)
+{
+ struct gh_vm_config_image_req req_payload = { 0 };
+ void *resp;
+ size_t resp_size;
+ int ret;
+
+ req_payload.vmid = vmid;
+ req_payload.auth_mech = auth_mechanism;
+ req_payload.mem_handle = mem_handle;
+ req_payload.image_offset_low = image_offset;
+ req_payload.image_offset_high = image_offset >> 32;
+ req_payload.image_size_low = image_size;
+ req_payload.image_size_high = image_size >> 32;
+ req_payload.dtb_offset_low = dtb_offset;
+ req_payload.dtb_offset_high = dtb_offset >> 32;
+ req_payload.dtb_size_low = dtb_size;
+ req_payload.dtb_size_high = dtb_size >> 32;
+
+ ret = gh_rm_call(GH_RM_RPC_VM_CONFIG_IMAGE, &req_payload, sizeof(req_payload),
+ &resp, &resp_size);
+ if (ret)
+ return ret;
+ kfree(resp);
+
+ if (resp_size)
+ pr_warn("Received unexpected payload for VM_CONFIG_IMAGE: %lu\n", resp_size);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_vm_configure);
+
+/**
+ * gh_rm_vm_init() - Move the VM to initialized state.
+ * @vmid: VM identifier
+ *
+ * RM will allocate needed resources for the VM. After gh_rm_vm_init, gh_rm_get_hyp_resources()
+ * can be called to learn of the capabilities we can use with the new VM.
+ */
+int gh_rm_vm_init(u16 vmid)
+{
+ return gh_rm_common_vmid_call(GH_RM_RPC_VM_INIT, vmid);
+}
+EXPORT_SYMBOL_GPL(gh_rm_vm_init);
+
+/**
+ * gh_rm_get_hyp_resources() - Retrieve hypervisor resources (capabilities) associated with a VM
+ * @vmid: VMID of the other VM to get the resources of
+ * @resources: Set by gh_rm_get_hyp_resources and contains the returned hypervisor resources.
+ *
+ * Return: >=0 value indicates the number of gh_rm_hyp_resource entries filled into *resources
+ */
+ssize_t gh_rm_get_hyp_resources(u16 vmid, struct gh_rm_hyp_resource **resources)
+{
+ struct gh_vm_get_hyp_resources_resp *resp;
+ size_t resp_size;
+ int ret;
+ struct gh_vm_common_vmid_req req_payload = {0};
+
+ req_payload.vmid = vmid;
+
+ ret = gh_rm_call(GH_RM_RPC_VM_GET_HYP_RESOURCES,
+ &req_payload, sizeof(req_payload),
+ (void **)&resp, &resp_size);
+ if (ret)
+ return ret;
+
+ if (resp_size < sizeof(*resp) ||
+ (sizeof(*resp->entries) && (resp->n_entries > U32_MAX / sizeof(*resp->entries))) ||
+ (resp_size != sizeof(*resp) + (resp->n_entries * sizeof(*resp->entries)))) {
+ ret = -EIO;
+ goto out;
+ }
+
+ *resources = kmemdup(resp->entries, (resp->n_entries * sizeof(*resp->entries)), GFP_KERNEL);
+ ret = resp->n_entries;
+
+out:
+ kfree(resp);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_get_hyp_resources);
+
+/**
+ * gh_rm_get_vmid() - Retrieve VMID of this virtual machine
+ * @vmid: Filled with the VMID of this VM
+ */
+int gh_rm_get_vmid(u16 *vmid)
+{
+ void *resp;
+ size_t resp_size;
+ int ret;
+ int payload = 0;
+
+ ret = gh_rm_call(GH_RM_RPC_VM_GET_VMID, &payload, sizeof(payload), &resp, &resp_size);
+ if (ret)
+ return ret;
+
+ if (resp_size != sizeof(*vmid))
+ return -EIO;
+ *vmid = *(u16 *)resp;
+ kfree(resp);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_get_vmid);
+
+/**
+ * gh_rm_set_boot_context() - Set boot context of a VM
+ * @vmid: VM identifier
+ * @reg_set: Register Set
+ * @reg_idx: Index into the register set
+ * @value: Updated register value
+ */
+int gh_rm_set_boot_context(u16 vmid, u8 reg_set, u8 reg_idx, u64 value)
+{
+ struct gh_vm_set_boot_context_req req = { 0 };
+ int ret;
+ size_t resp_size;
+ void *resp;
+
+ req.vmid = vmid;
+ req.reg_set = reg_set;
+ req.reg_idx = reg_idx;
+ req.val_low = (value & 0xFFFFFFFF);
+ req.val_high = ((value >> 32) & 0xFFFFFFFF);
+
+ ret = gh_rm_call(GH_RM_RPC_VM_SET_BOOT_CONTEXT, &req, sizeof(req), &resp, &resp_size);
+ kfree(resp);
+
+ if (!ret && resp_size)
+ pr_warn("Received unexpected payload for SET_BOOT_CONTEXT: %lu\n", resp_size);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_set_boot_context);
diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h
index 0eeb7202fa33..169497f894c8 100644
--- a/include/linux/gunyah_rsc_mgr.h
+++ b/include/linux/gunyah_rsc_mgr.h
@@ -25,6 +25,60 @@ struct gh_rm_notification {
int gh_rm_register_notifier(struct notifier_block *nb);
int gh_rm_unregister_notifier(struct notifier_block *nb);
+enum gh_rm_vm_status {
+ GH_RM_VM_STATUS_NO_STATE = 0,
+ GH_RM_VM_STATUS_INIT = 1,
+ GH_RM_VM_STATUS_READY = 2,
+ GH_RM_VM_STATUS_RUNNING = 3,
+ GH_RM_VM_STATUS_PAUSED = 4,
+ GH_RM_VM_STATUS_LOAD = 5,
+ GH_RM_VM_STATUS_AUTH = 6,
+ GH_RM_VM_STATUS_INIT_FAILED = 8,
+ GH_RM_VM_STATUS_EXITED = 9,
+ GH_RM_VM_STATUS_RESETTING = 10,
+ GH_RM_VM_STATUS_RESET = 11,
+};
+
+/* RPC Calls */
+int gh_rm_alloc_vmid(u16 vmid);
+int gh_rm_dealloc_vmid(u16 vmid);
+int gh_rm_vm_start(u16 vmid);
+int gh_rm_vm_stop(u16 vmid);
+
+enum gh_rm_vm_auth_mechanism {
+ GH_RM_VM_AUTH_NONE = 0,
+ GH_RM_VM_AUTH_QCOM_PIL_ELF = 1,
+ GH_RM_VM_AUTH_QCOM_ANDROID_PVM = 2,
+};
+
+int gh_rm_vm_configure(u16 vmid, enum gh_rm_vm_auth_mechanism auth_mechanism, u32 mem_handle,
+ u64 image_offset, u64 image_size, u64 dtb_offset, u64 dtb_size);
+int gh_rm_vm_init(u16 vmid);
+
+struct gh_rm_hyp_resource {
+ u8 type;
+ u8 reserved;
+ u16 partner_vmid;
+ u32 resource_handle;
+ u32 resource_label;
+ u32 cap_id_low;
+ u32 cap_id_high;
+ u32 virq_handle;
+ u32 virq;
+ u32 base_low;
+ u32 base_high;
+ u32 size_low;
+ u32 size_high;
+} __packed;
+
+ssize_t gh_rm_get_hyp_resources(u16 vmid, struct gh_rm_hyp_resource **resources);
+int gh_rm_get_vmid(u16 *vmid);
+
+#define GH_RM_BOOT_CONTEXT_REG_SET_REGISTERS 0
+#define GH_RM_BOOT_CONTEXT_REG_SET_PC 1
+#define GH_RM_BOOT_CONTEXT_REG_SET_SP_ELx 2
+int gh_rm_set_boot_context(u16 vmid, u8 reg_set, u8 reg_idx, u64 value);
+
#define GH_RM_DEVICE_VM_MGR "vm_mgr"
struct gh_rm_driver {
--
2.25.1
The maximum VMID for assign_mem is 63. Use a u64 to represent this
bitmap instead of architecture-dependent "unsigned int" which varies in
size on 32-bit and 64-bit platforms.
Signed-off-by: Elliot Berman <[email protected]>
---
drivers/firmware/qcom_scm.c | 12 +++++++-----
drivers/misc/fastrpc.c | 6 ++++--
drivers/net/wireless/ath/ath10k/qmi.c | 4 ++--
drivers/remoteproc/qcom_q6v5_mss.c | 8 ++++----
drivers/soc/qcom/rmtfs_mem.c | 2 +-
include/linux/qcom_scm.h | 2 +-
6 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index cdbfe54c8146..92763dce6477 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -898,7 +898,7 @@ static int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region,
* Return negative errno on failure or 0 on success with @srcvm updated.
*/
int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
- unsigned int *srcvm,
+ u64 *srcvm,
const struct qcom_scm_vmperm *newvm,
unsigned int dest_cnt)
{
@@ -915,9 +915,9 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
__le32 *src;
void *ptr;
int ret, i, b;
- unsigned long srcvm_bits = *srcvm;
+ u64 srcvm_bits = *srcvm;
- src_sz = hweight_long(srcvm_bits) * sizeof(*src);
+ src_sz = hweight64(srcvm_bits) * sizeof(*src);
mem_to_map_sz = sizeof(*mem_to_map);
dest_sz = dest_cnt * sizeof(*destvm);
ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) +
@@ -930,8 +930,10 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
/* Fill source vmid detail */
src = ptr;
i = 0;
- for_each_set_bit(b, &srcvm_bits, BITS_PER_LONG)
- src[i++] = cpu_to_le32(b);
+ for (b = 0; b < BITS_PER_TYPE(u64); b++) {
+ if (srcvm_bits & BIT(b))
+ src[i++] = cpu_to_le32(b);
+ }
/* Fill details of mem buff to map */
mem_to_map = ptr + ALIGN(src_sz, SZ_64);
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 7ff0b63c25e3..2ad388f99fe1 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -299,11 +299,13 @@ static void fastrpc_free_map(struct kref *ref)
if (map->attr & FASTRPC_ATTR_SECUREMAP) {
struct qcom_scm_vmperm perm;
int err = 0;
+ u64 src;
+ src = BIT(map->fl->cctx->vmperms[0].vmid);
perm.vmid = QCOM_SCM_VMID_HLOS;
perm.perm = QCOM_SCM_PERM_RWX;
err = qcom_scm_assign_mem(map->phys, map->size,
- &(map->fl->cctx->vmperms[0].vmid), &perm, 1);
+ &src, &perm, 1);
if (err) {
dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
map->phys, map->size, err);
@@ -744,7 +746,7 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
* If subsystem VMIDs are defined in DTSI, then do
* hyp_assign from HLOS to those VM(s)
*/
- unsigned int perms = BIT(QCOM_SCM_VMID_HLOS);
+ u64 perms = BIT(QCOM_SCM_VMID_HLOS);
map->attr = attr;
err = qcom_scm_assign_mem(map->phys, (u64)map->size, &perms,
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
index 66cb7a1e628a..6d1d87e1cdde 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.c
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -28,7 +28,7 @@ static int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi,
{
struct qcom_scm_vmperm dst_perms[3];
struct ath10k *ar = qmi->ar;
- unsigned int src_perms;
+ u64 src_perms;
u32 perm_count;
int ret;
@@ -60,7 +60,7 @@ static int ath10k_qmi_unmap_msa_permission(struct ath10k_qmi *qmi,
{
struct qcom_scm_vmperm dst_perms;
struct ath10k *ar = qmi->ar;
- unsigned int src_perms;
+ u64 src_perms;
int ret;
src_perms = BIT(QCOM_SCM_VMID_MSS_MSA) | BIT(QCOM_SCM_VMID_WLAN);
diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index fddb63cffee0..9e8bde7a7ec4 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -227,8 +227,8 @@ struct q6v5 {
bool has_qaccept_regs;
bool has_ext_cntl_regs;
bool has_vq6;
- int mpss_perm;
- int mba_perm;
+ u64 mpss_perm;
+ u64 mba_perm;
const char *hexagon_mdt_image;
int version;
};
@@ -404,7 +404,7 @@ static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds,
}
}
-static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm,
+static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, u64 *current_perm,
bool local, bool remote, phys_addr_t addr,
size_t size)
{
@@ -939,7 +939,7 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw,
struct page *page;
dma_addr_t phys;
void *metadata;
- int mdata_perm;
+ u64 mdata_perm;
int xferop_ret;
size_t size;
void *vaddr;
diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c
index 0feaae357821..69991e47aa23 100644
--- a/drivers/soc/qcom/rmtfs_mem.c
+++ b/drivers/soc/qcom/rmtfs_mem.c
@@ -30,7 +30,7 @@ struct qcom_rmtfs_mem {
unsigned int client_id;
- unsigned int perms;
+ u64 perms;
};
static ssize_t qcom_rmtfs_mem_show(struct device *dev,
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index f8335644a01a..77f7b5837216 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -96,7 +96,7 @@ extern int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size,
u32 cp_nonpixel_start,
u32 cp_nonpixel_size);
extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
- unsigned int *src,
+ u64 *src,
const struct qcom_scm_vmperm *newvm,
unsigned int dest_cnt);
--
2.25.1
Document the ioctls and usage of Gunyah VM Manager driver.
Signed-off-by: Elliot Berman <[email protected]>
---
Documentation/virt/gunyah/index.rst | 1 +
Documentation/virt/gunyah/vm-manager.rst | 94 ++++++++++++++++++++++++
2 files changed, 95 insertions(+)
create mode 100644 Documentation/virt/gunyah/vm-manager.rst
diff --git a/Documentation/virt/gunyah/index.rst b/Documentation/virt/gunyah/index.rst
index fbadbdd24da7..9019a03b6f3e 100644
--- a/Documentation/virt/gunyah/index.rst
+++ b/Documentation/virt/gunyah/index.rst
@@ -7,6 +7,7 @@ Gunyah Hypervisor
.. toctree::
:maxdepth: 1
+ vm-manager
message-queue
Gunyah is a Type-1 hypervisor which is independent of any OS kernel, and runs in
diff --git a/Documentation/virt/gunyah/vm-manager.rst b/Documentation/virt/gunyah/vm-manager.rst
new file mode 100644
index 000000000000..c232ba05de7e
--- /dev/null
+++ b/Documentation/virt/gunyah/vm-manager.rst
@@ -0,0 +1,94 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================
+Virtual Machine Manager
+=======================
+
+The Gunyah Virtual Machine Manager is a Linux driver to support launching virtual machines.
+
+Summary
+=======
+
+Gunyah VMM presently supports launching non-proxy scheduled Linux-like virtual machines.
+
+Sample Userspace VMM
+====================
+
+A sample userspace VMM is included in samples/gunyah/ along with a sample minimal devicetree
+that can be used to launch a Linux-like virtual machine under Gunyah. To build this sample, enable
+CONFIG_SAMPLE_GUNYAH.
+
+IOCTLs and userspace VMM flows
+==============================
+
+The kernel exposes a char device interface at /dev/gunyah.
+
+To create a VM, use the GH_CREATE_VM ioctl. A successful call will return a "Gunyah VM" file descriptor.
+
+/dev/gunyah API Descriptions
+----------------------------
+
+GH_CREATE_VM
+~~~~~~~~~~~~
+
+Creates a Gunyah VM. The argument is reserved for future use and must be 0.
+
+Gunyah VM API Descriptions
+--------------------------
+
+GH_VM_SET_USER_MEM_REGION
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ struct gh_userspace_memory_region {
+ __u32 label;
+ __u32 flags;
+ __u64 guest_phys_addr;
+ __u64 memory_size;
+ __u64 userspace_addr;
+ };
+
+This ioctl allows the user to create or delete a memory parcel for a guest
+virtual machine. Each memory region is uniquely identified by a label;
+attempting to create two memory regions with the same label is not allowed.
+
+While VMM is guest-agnostic and allows runtime addition of memory regions,
+Linux guest virtual machines do not support accepting memory regions at runtime.
+Thus, memory regions should be provided before starting the VM and the VM
+configured to accept those memory regions at boot-up.
+
+The guest physical address is used by Linux to check the requested user regions
+do not overlap and to help find a corresponding memory region for calls like
+GH_VM_SET_DTB_CONFIG.
+
+To delete a memory region, call GH_VM_SET_USER_MEM_REGION with label set to the
+memory region of interest and memory_size set to 0.
+
+The flags field of gh_userspace_memory_region can set the following bits. All
+other bits must be 0 and are reserved for future use. The ioctl will return
+-EINVAL if an unsupported bit is detected.
+
+ - GH_MEM_ALLOW_READ/GH_MEM_ALLOW_WRITE/GH_MEM_ALLOW_EXEC sets read/write/exec permissions
+ for the guest, respectively.
+
+ - GH_MEM_LENT means that the memory will be unmapped from the host and be unaccessible by
+ the host while the guest has the region.
+
+GH_VM_SET_DTB_CONFIG
+~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ struct gh_vm_dtb_config {
+ __u64 gpa;
+ __u64 size;
+ };
+
+This ioctl sets the location of the VM's devicetree blob and is used by Gunyah
+Resource Manager to allocate resources.
+
+GH_VM_START
+~~~~~~~~~~~
+
+This ioctl starts the virtual machine.
--
2.25.1
Add a sample Gunyah VMM capable of launching a non-proxy scheduled VM.
Signed-off-by: Elliot Berman <[email protected]>
---
MAINTAINERS | 1 +
samples/Kconfig | 10 ++
samples/Makefile | 1 +
samples/gunyah/.gitignore | 2 +
samples/gunyah/Makefile | 6 +
samples/gunyah/gunyah_vmm.c | 270 +++++++++++++++++++++++++++++++++++
samples/gunyah/sample_vm.dts | 69 +++++++++
7 files changed, 359 insertions(+)
create mode 100644 samples/gunyah/.gitignore
create mode 100644 samples/gunyah/Makefile
create mode 100644 samples/gunyah/gunyah_vmm.c
create mode 100644 samples/gunyah/sample_vm.dts
diff --git a/MAINTAINERS b/MAINTAINERS
index e072a0d2e553..907a2267f0ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8946,6 +8946,7 @@ F: arch/arm64/gunyah/
F: drivers/mailbox/gunyah-msgq.c
F: drivers/virt/gunyah/
F: include/linux/gunyah*.h
+F: samples/gunyah/
HABANALABS PCI DRIVER
M: Oded Gabbay <[email protected]>
diff --git a/samples/Kconfig b/samples/Kconfig
index 0d81c00289ee..197c8a3610e7 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -263,6 +263,16 @@ config SAMPLE_CORESIGHT_SYSCFG
This demonstrates how a user may create their own CoreSight
configurations and easily load them into the system at runtime.
+config SAMPLE_GUNYAH
+ bool "Build example Gunyah Virtual Machine Manager"
+ depends on CC_CAN_LINK && HEADERS_INSTALL
+ depends on GUNYAH_VM_MANAGER
+ help
+ Build an example Gunyah VMM userspace program capable of launching
+ a basic virtual machine under the Gunyah hypervisor.
+ This demonstrates how to create a virtual machine under the Gunyah
+ hypervisor.
+
source "samples/rust/Kconfig"
endif # SAMPLES
diff --git a/samples/Makefile b/samples/Makefile
index 9832ef3f8fcb..2600bd4b82f8 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/
obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/
obj-$(CONFIG_SAMPLE_FPROBE) += fprobe/
obj-$(CONFIG_SAMPLES_RUST) += rust/
+obj-$(CONFIG_SAMPLE_GUNYAH) += gunyah/
diff --git a/samples/gunyah/.gitignore b/samples/gunyah/.gitignore
new file mode 100644
index 000000000000..adc7d1589fde
--- /dev/null
+++ b/samples/gunyah/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+/gunyah_vmm
diff --git a/samples/gunyah/Makefile b/samples/gunyah/Makefile
new file mode 100644
index 000000000000..faf14f9bb337
--- /dev/null
+++ b/samples/gunyah/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+userprogs-always-y += gunyah_vmm
+dtb-y += sample_vm.dtb
+
+userccflags += -I usr/include
diff --git a/samples/gunyah/gunyah_vmm.c b/samples/gunyah/gunyah_vmm.c
new file mode 100644
index 000000000000..ccc1786051f2
--- /dev/null
+++ b/samples/gunyah/gunyah_vmm.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/sysmacros.h>
+#define __USE_GNU
+#include <sys/mman.h>
+
+#include <linux/gunyah.h>
+
+struct vm_config {
+ int image_fd;
+ int dtb_fd;
+ int ramdisk_fd;
+
+ uint64_t guest_base;
+ uint64_t guest_size;
+
+ uint64_t image_offset;
+ off_t image_size;
+ uint64_t dtb_offset;
+ off_t dtb_size;
+ uint64_t ramdisk_offset;
+ off_t ramdisk_size;
+};
+
+static struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "image", required_argument, NULL, 'i' },
+ { "dtb", required_argument, NULL, 'd' },
+ { "ramdisk", optional_argument, NULL, 'r' },
+ { "base", optional_argument, NULL, 'B' },
+ { "size", optional_argument, NULL, 'S' },
+ { "image_offset", optional_argument, NULL, 'I' },
+ { "dtb_offset", optional_argument, NULL, 'D' },
+ { "ramdisk_offset", optional_argument, NULL, 'R' },
+ { }
+};
+
+static void print_help(char *cmd)
+{
+ printf("gunyah_vmm, a sample tool to launch Gunyah VMs\n"
+ "Usage: %s <options>\n"
+ " --help, -h this menu\n"
+ " --image, -i <image> VM image file to load (e.g. a kernel Image) [Required]\n"
+ " --dtb, -d <dtb> Devicetree to load [Required]\n"
+ " --ramdisk, -r <ramdisk> Ramdisk to load\n"
+ " --base, -B <address> Set the base address of guest's memory [Default: 0x80000000]\n"
+ " --size, -S <number> The number of bytes large to make the guest's memory [Default: 0x6400000 (100 MB)]\n"
+ " --image_offset, -I <number> Offset into guest memory to load the VM image file [Default: 0x10000]\n"
+ " --dtb_offset, -D <number> Offset into guest memory to load the DTB [Default: 0]\n"
+ " --ramdisk_offset, -R <number> Offset into guest memory to load a ramdisk [Default: 0x4600000]\n"
+ , cmd);
+}
+
+int main(int argc, char **argv)
+{
+ int gunyah_fd, vm_fd, guest_fd;
+ struct gh_userspace_memory_region guest_mem_desc = { 0 };
+ struct gh_vm_dtb_config dtb_config = { 0 };
+ char *guest_mem;
+ struct vm_config config = {
+ /* Defaults good enough to boot static kernel and a basic ramdisk */
+ .ramdisk_fd = -1,
+ .guest_base = 0x80000000,
+ .guest_size = 104857600, /* 100 MB */
+ .image_offset = 0,
+ .dtb_offset = 0x45f0000,
+ .ramdisk_offset = 0x4600000, /* put at +70MB (30MB for ramdisk) */
+ };
+ struct stat st;
+ int opt, optidx, ret = 0;
+ long int l;
+
+ while ((opt = getopt_long(argc, argv, "hi:d:r:B:S:I:D:R:c:", options, &optidx)) != -1) {
+ switch (opt) {
+ case 'i':
+ config.image_fd = open(optarg, O_RDONLY | O_CLOEXEC);
+ if (config.image_fd < 0) {
+ perror("Failed to open image");
+ return -1;
+ }
+ if (stat(optarg, &st) < 0) {
+ perror("Failed to stat image");
+ return -1;
+ }
+ config.image_size = st.st_size;
+ break;
+ case 'd':
+ config.dtb_fd = open(optarg, O_RDONLY | O_CLOEXEC);
+ if (config.dtb_fd < 0) {
+ perror("Failed to open dtb");
+ return -1;
+ }
+ if (stat(optarg, &st) < 0) {
+ perror("Failed to stat dtb");
+ return -1;
+ }
+ config.dtb_size = st.st_size;
+ break;
+ case 'r':
+ config.ramdisk_fd = open(optarg, O_RDONLY | O_CLOEXEC);
+ if (config.ramdisk_fd < 0) {
+ perror("Failed to open ramdisk");
+ return -1;
+ }
+ if (stat(optarg, &st) < 0) {
+ perror("Failed to stat ramdisk");
+ return -1;
+ }
+ config.ramdisk_size = st.st_size;
+ break;
+ case 'B':
+ l = strtol(optarg, NULL, 0);
+ if (l == LONG_MIN) {
+ perror("Failed to parse base address");
+ return -1;
+ }
+ config.guest_base = l;
+ break;
+ case 'S':
+ l = strtol(optarg, NULL, 0);
+ if (l == LONG_MIN) {
+ perror("Failed to parse memory size");
+ return -1;
+ }
+ config.guest_size = l;
+ break;
+ case 'I':
+ l = strtol(optarg, NULL, 0);
+ if (l == LONG_MIN) {
+ perror("Failed to parse image offset");
+ return -1;
+ }
+ config.image_offset = l;
+ break;
+ case 'D':
+ l = strtol(optarg, NULL, 0);
+ if (l == LONG_MIN) {
+ perror("Failed to parse dtb offset");
+ return -1;
+ }
+ config.dtb_offset = l;
+ break;
+ case 'R':
+ l = strtol(optarg, NULL, 0);
+ if (l == LONG_MIN) {
+ perror("Failed to parse ramdisk offset");
+ return -1;
+ }
+ config.ramdisk_offset = l;
+ break;
+ case 'h':
+ print_help(argv[0]);
+ return 0;
+ default:
+ print_help(argv[0]);
+ return -1;
+ }
+ }
+
+ if (!config.image_fd || !config.dtb_fd) {
+ print_help(argv[0]);
+ return -1;
+ }
+
+ if (config.image_offset + config.image_size > config.guest_size) {
+ fprintf(stderr, "Image offset and size puts it outside guest memory. Make image smaller or increase guest memory size.\n");
+ return -1;
+ }
+
+ if (config.dtb_offset + config.dtb_size > config.guest_size) {
+ fprintf(stderr, "DTB offset and size puts it outside guest memory. Make dtb smaller or increase guest memory size.\n");
+ return -1;
+ }
+
+ if (config.ramdisk_fd == -1 &&
+ config.ramdisk_offset + config.ramdisk_size > config.guest_size) {
+ fprintf(stderr, "Ramdisk offset and size puts it outside guest memory. Make ramdisk smaller or increase guest memory size.\n");
+ return -1;
+ }
+
+ gunyah_fd = open("/dev/gunyah", O_RDWR | O_CLOEXEC);
+ if (gunyah_fd < 0) {
+ perror("Failed to open /dev/gunyah");
+ return -1;
+ }
+
+ vm_fd = ioctl(gunyah_fd, GH_CREATE_VM, 0);
+ if (vm_fd < 0) {
+ perror("Failed to create vm");
+ return -1;
+ }
+
+ guest_fd = memfd_create("guest_memory", MFD_CLOEXEC);
+ if (guest_fd < 0) {
+ perror("Failed to create guest memfd");
+ return -1;
+ }
+
+ if (ftruncate(guest_fd, config.guest_size) < 0) {
+ perror("Failed to grow guest memory");
+ return -1;
+ }
+
+ guest_mem = mmap(NULL, config.guest_size, PROT_READ | PROT_WRITE, MAP_SHARED, guest_fd, 0);
+ if (guest_mem == MAP_FAILED) {
+ perror("Not enough memory");
+ return -1;
+ }
+
+ if (read(config.image_fd, guest_mem + config.image_offset, config.image_size) < 0) {
+ perror("Failed to read image into guest memory");
+ return -1;
+ }
+
+ if (read(config.dtb_fd, guest_mem + config.dtb_offset, config.dtb_size) < 0) {
+ perror("Failed to read dtb into guest memory");
+ return -1;
+ }
+
+ if (config.ramdisk_fd > 0 &&
+ read(config.ramdisk_fd, guest_mem + config.ramdisk_offset,
+ config.ramdisk_size) < 0) {
+ perror("Failed to read ramdisk into guest memory");
+ return -1;
+ }
+
+ guest_mem_desc.label = 0;
+ guest_mem_desc.flags = GH_MEM_ALLOW_READ | GH_MEM_ALLOW_WRITE | GH_MEM_ALLOW_EXEC;
+ guest_mem_desc.guest_phys_addr = config.guest_base;
+ guest_mem_desc.memory_size = config.guest_size;
+ guest_mem_desc.userspace_addr = (__u64)guest_mem;
+
+ if (ioctl(vm_fd, GH_VM_SET_USER_MEM_REGION, &guest_mem_desc) < 0) {
+ perror("Failed to register guest memory with VM");
+ return -1;
+ }
+
+ dtb_config.gpa = config.dtb_offset;
+ dtb_config.size = config.dtb_size;
+ if (ioctl(vm_fd, GH_VM_SET_DTB_CONFIG, &dtb_config) < 0) {
+ perror("Failed to set DTB configuration for VM");
+ return -1;
+ }
+
+ ret = ioctl(vm_fd, GH_VM_START);
+ if (ret) {
+ perror("GH_VM_START failed");
+ return -1;
+ }
+
+ while (1)
+ sleep(10);
+
+ return 0;
+}
diff --git a/samples/gunyah/sample_vm.dts b/samples/gunyah/sample_vm.dts
new file mode 100644
index 000000000000..1c410d58c298
--- /dev/null
+++ b/samples/gunyah/sample_vm.dts
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+/dts-v1/;
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ interrupt-parent = <&intc>;
+
+ chosen {
+ bootargs = "nokaslr";
+ };
+
+ cpus {
+ #address-cells = <0x2>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0 0>;
+ };
+ };
+
+ intc: interrupt-controller@3FFF0000 {
+ compatible = "arm,gic-v3";
+ #interrupt-cells = <3>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ interrupt-controller;
+ reg = <0 0x3FFF0000 0 0x10000>,
+ <0 0x3FFD0000 0 0x20000>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ always-on;
+ interrupts = <1 13 0x108>,
+ <1 14 0x108>,
+ <1 11 0x108>,
+ <1 10 0x108>;
+ clock-frequency = <19200000>;
+ };
+
+ gunyah-vm-config {
+ image-name = "linux_vm_0";
+ os-type = "linux";
+
+ memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ base-address = <0 0x80000000>;
+ };
+
+ interrupts {
+ config = <&intc>;
+ };
+
+ vcpus {
+ affinity-map = < 0 >;
+ sched-priority = < (-1) >;
+ sched-timeslice = < 2000 >;
+ };
+ };
+};
--
2.25.1
Qualcomm platforms have a firmware entity which performs access control
to physical pages. Dynamically started Gunyah virtual machines use the
QCOM_SCM_RM_MANAGED_VMID for access. Linux thus needs to assign access
to the memory used by guest VMs. Gunyah doesn't do this operation for us
since it is the current VM (typically VMID_HLOS) delegating the access
and not Gunyah itself. Use the Gunyah platform ops to achieve this so
that only Qualcomm platforms attempt to make the needed SCM calls.
Co-developed-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Elliot Berman <[email protected]>
---
drivers/firmware/qcom_scm.c | 114 +++++++++++++++++++++++++++++++++
include/linux/gunyah_rsc_mgr.h | 5 ++
2 files changed, 119 insertions(+)
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 92763dce6477..6be7c71f8609 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <linux/reset-controller.h>
#include <linux/arm-smccc.h>
+#include <linux/gunyah_rsc_mgr.h>
#include "qcom_scm.h"
@@ -27,6 +28,9 @@ module_param(download_mode, bool, 0);
#define SCM_HAS_IFACE_CLK BIT(1)
#define SCM_HAS_BUS_CLK BIT(2)
+#define QCOM_SCM_RM_MANAGED_VMID 0x3A
+#define QCOM_SCM_MAX_MANAGED_VMID 0x3F
+
struct qcom_scm {
struct device *dev;
struct clk *core_clk;
@@ -1292,6 +1296,113 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
}
EXPORT_SYMBOL(qcom_scm_lmh_dcvsh);
+static int qcom_scm_gh_rm_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel)
+{
+ struct qcom_scm_vmperm *new_perms;
+ u16 this_vmid;
+ u64 src, src_cpy;
+ int ret, i, n;
+
+ ret = gh_rm_get_vmid(&this_vmid);
+ if (ret)
+ return ret;
+
+ new_perms = kcalloc(mem_parcel->n_acl_entries, sizeof(*new_perms), GFP_KERNEL);
+ if (!new_perms)
+ return -ENOMEM;
+
+ for (n = 0; n < mem_parcel->n_acl_entries; n++) {
+ if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+ new_perms[n].vmid = mem_parcel->acl_entries[n].vmid;
+ else
+ new_perms[n].vmid = QCOM_SCM_RM_MANAGED_VMID;
+ if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_X)
+ new_perms[n].perm |= QCOM_SCM_PERM_EXEC;
+ if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_W)
+ new_perms[n].perm |= QCOM_SCM_PERM_WRITE;
+ if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_R)
+ new_perms[n].perm |= QCOM_SCM_PERM_READ;
+ }
+
+ if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+ src = (1ul << this_vmid);
+ else
+ src = (1ul << QCOM_SCM_RM_MANAGED_VMID);
+
+ for (i = 0; i < mem_parcel->n_mem_entries; i++) {
+ src_cpy = src;
+ ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
+ mem_parcel->mem_entries[i].size,
+ &src_cpy, new_perms, mem_parcel->n_acl_entries);
+ if (ret) {
+ src = 0;
+ for (n = 0; n < mem_parcel->n_acl_entries; n++) {
+ if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+ src |= (1ul << mem_parcel->acl_entries[n].vmid);
+ else
+ src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
+ }
+
+ if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+ new_perms[0].vmid = this_vmid;
+ else
+ new_perms[0].vmid = QCOM_SCM_RM_MANAGED_VMID;
+
+ for (i--; i >= 0; i--) {
+ src_cpy = src;
+ ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
+ mem_parcel->mem_entries[i].size,
+ &src_cpy, new_perms, 1);
+ WARN_ON_ONCE(ret);
+ }
+ break;
+ }
+ }
+
+ kfree(new_perms);
+ return ret;
+}
+
+static int qcom_scm_gh_rm_post_mem_reclaim(struct gh_rm_mem_parcel *mem_parcel)
+{
+ struct qcom_scm_vmperm new_perms;
+ u16 this_vmid;
+ u64 src = 0;
+ int ret, i, n;
+
+
+ ret = gh_rm_get_vmid(&this_vmid);
+ if (ret)
+ return ret;
+
+ if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+ new_perms.vmid = this_vmid;
+ else
+ new_perms.vmid = QCOM_SCM_RM_MANAGED_VMID;
+ new_perms.perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE | QCOM_SCM_PERM_READ;
+
+ for (n = 0; n < mem_parcel->n_acl_entries; n++) {
+ if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+ src |= (1ul << mem_parcel->acl_entries[n].vmid);
+ else
+ src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
+ }
+
+ for (i = 0; i < mem_parcel->n_mem_entries; i++) {
+ ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
+ mem_parcel->mem_entries[i].size,
+ &src, &new_perms, 1);
+ WARN_ON_ONCE(ret);
+ }
+
+ return ret;
+}
+
+static struct gunyah_rm_platform_ops qcom_scm_gh_rm_platform_ops = {
+ .pre_mem_share = qcom_scm_gh_rm_pre_mem_share,
+ .post_mem_reclaim = qcom_scm_gh_rm_post_mem_reclaim,
+};
+
static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
{
struct device_node *tcsr;
@@ -1414,6 +1525,9 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (download_mode)
qcom_scm_set_download_mode(true);
+ if (gh_rm_register_platform_ops(&qcom_scm_gh_rm_platform_ops))
+ dev_warn(__scm->dev, "Gunyah RM platform ops were already registered\n");
+
return 0;
}
diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h
index 6e5e67e96688..710e9a045f02 100644
--- a/include/linux/gunyah_rsc_mgr.h
+++ b/include/linux/gunyah_rsc_mgr.h
@@ -142,6 +142,11 @@ void gh_rm_driver_unregister(struct gh_rm_driver *ghrm_drv);
#define module_gh_rm_driver(ghrm_drv) \
module_driver(ghrm_drv, gh_rm_driver_register, gh_rm_driver_unregister)
+struct gunyah_rm_platform_ops {
+ int (*pre_mem_share)(struct gh_rm_mem_parcel *mem_parcel);
+ int (*post_mem_reclaim)(struct gh_rm_mem_parcel *mem_parcel);
+};
+
#if IS_ENABLED(CONFIG_GUNYAH)
int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops);
void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops);
--
2.25.1
Gunyah resource manager provides API to manipulate stage 2 page tables.
Manipulations are represented as a memory parcel. Memory parcels
describe a list of memory regions (intermediate physical address and
size), a list of new permissions for VMs, and the memory type (DDR or
MMIO). Memory parcels are uniquely identified by a handle allocated by
Gunyah. There are a few types of memory parcel sharing which Gunyah
supports:
- Sharing: the guest and host VM both have access
- Lending: only the guest has access; host VM loses access
- Donating: Permanently lent (not reclaimed even if guest shuts down)
Memory parcels that have been shared or lent can be reclaimed by the
host via an additional call. The reclaim operation restores the original
access the host VM had to the memory parcel and removes the access to
other VM.
One point to note that memory parcels don't describe where in the guest
VM the memory parcel should reside. The guest VM must accept the memory
parcel either explicitly via a "gh_rm_mem_accept" call (not introduced
here) or be configured to accept it automatically at boot. As the guest
VM accepts the memory parcel, it also mentions the IPA it wants to place
memory parcel.
Co-developed-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Elliot Berman <[email protected]>
---
drivers/virt/gunyah/rsc_mgr.h | 44 +++++++
drivers/virt/gunyah/rsc_mgr_rpc.c | 204 ++++++++++++++++++++++++++++++
include/linux/gunyah_rsc_mgr.h | 47 +++++++
3 files changed, 295 insertions(+)
diff --git a/drivers/virt/gunyah/rsc_mgr.h b/drivers/virt/gunyah/rsc_mgr.h
index 247664b8b008..f6ed58405ef4 100644
--- a/drivers/virt/gunyah/rsc_mgr.h
+++ b/drivers/virt/gunyah/rsc_mgr.h
@@ -28,6 +28,12 @@
#define GH_RM_ERROR_IRQ_INUSE 0x10
#define GH_RM_ERROR_IRQ_RELEASED 0x11
+/* Message IDs: Memory Management */
+#define GH_RM_RPC_MEM_LEND 0x51000012
+#define GH_RM_RPC_MEM_SHARE 0x51000013
+#define GH_RM_RPC_MEM_RECLAIM 0x51000015
+#define GH_RM_RPC_MEM_APPEND 0x51000018
+
/* Message IDs: VM Management */
#define GH_RM_RPC_VM_ALLOC_VMID 0x56000001
#define GH_RM_RPC_VM_DEALLOC_VMID 0x56000002
@@ -45,6 +51,44 @@ struct gh_vm_common_vmid_req {
u16 reserved0;
} __packed;
+/* Call: MEM_LEND, MEM_SHARE */
+struct gh_mem_share_req_header {
+ u8 mem_type;
+ u8 reserved0;
+#define GH_MEM_SHARE_REQ_FLAGS_APPEND BIT(1)
+ u8 flags;
+ u8 reserved1;
+ u32 label;
+} __packed;
+
+struct gh_mem_share_req_acl_section {
+ u32 n_entries;
+ struct gh_rm_mem_acl_entry entries[];
+};
+
+struct gh_mem_share_req_mem_section {
+ u16 n_entries;
+ u16 reserved0;
+ struct gh_rm_mem_entry entries[];
+};
+
+/* Call: MEM_RELEASE */
+struct gh_mem_release_req {
+ u32 mem_handle;
+ u8 flags;
+ u16 reserved0;
+ u8 reserved1;
+} __packed;
+
+/* Call: MEM_APPEND */
+struct gh_mem_append_req_header {
+ u32 mem_handle;
+#define GH_MEM_APPEND_REQ_FLAGS_END BIT(0)
+ u8 flags;
+ u16 reserved0;
+ u8 reserved1;
+} __packed;
+
/* Call: VM_STOP */
struct gh_vm_stop_req {
u16 vmid;
diff --git a/drivers/virt/gunyah/rsc_mgr_rpc.c b/drivers/virt/gunyah/rsc_mgr_rpc.c
index 33d27690c16e..17f88c3d5726 100644
--- a/drivers/virt/gunyah/rsc_mgr_rpc.c
+++ b/drivers/virt/gunyah/rsc_mgr_rpc.c
@@ -12,6 +12,8 @@
#include "rsc_mgr.h"
+#define GH_RM_MAX_MEM_ENTRIES 512
+
/*
* Several RM calls take only a VMID as a parameter and give only standard
* response back. Deduplicate boilerplate code by using this common call.
@@ -34,6 +36,208 @@ static int gh_rm_common_vmid_call(u32 message_id, u16 vmid)
return ret;
}
+static int _gh_rm_mem_append(u32 mem_handle, bool end_append,
+ struct gh_rm_mem_entry *mem_entries, size_t n_mem_entries)
+{
+ size_t msg_size = 0;
+ void *msg;
+ struct gh_mem_append_req_header *req_header;
+ struct gh_mem_share_req_mem_section *mem_section;
+ void *resp;
+ size_t resp_size;
+ int ret;
+
+ msg_size += sizeof(struct gh_mem_append_req_header);
+ msg_size += offsetof(struct gh_mem_share_req_mem_section, entries[n_mem_entries]);
+
+ msg = kzalloc(msg_size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ req_header = msg;
+ mem_section = (void *)req_header + sizeof(struct gh_mem_append_req_header);
+
+ req_header->mem_handle = mem_handle;
+ if (end_append)
+ req_header->flags |= GH_MEM_APPEND_REQ_FLAGS_END;
+
+ mem_section->n_entries = n_mem_entries;
+ memcpy(mem_section->entries, mem_entries, sizeof(*mem_entries) * n_mem_entries);
+
+ ret = gh_rm_call(GH_RM_RPC_MEM_APPEND, msg, msg_size, &resp, &resp_size);
+ if (ret)
+ return ret;
+ kfree(resp);
+
+ if (resp_size)
+ pr_warn("Received unexpected payload for MEM_APPEND: %lu\n", resp_size);
+
+ return ret;
+}
+
+static int gh_rm_mem_append(u32 mem_handle, bool allow_append,
+ struct gh_rm_mem_entry *mem_entries, size_t n_mem_entries)
+{
+ bool end_append;
+ size_t n;
+ int ret = 0;
+
+ while (n_mem_entries) {
+ if (n_mem_entries > GH_RM_MAX_MEM_ENTRIES) {
+ end_append = false;
+ n = GH_RM_MAX_MEM_ENTRIES;
+ } else {
+ end_append = !allow_append;
+ n = n_mem_entries;
+ }
+
+ ret = _gh_rm_mem_append(mem_handle, end_append, mem_entries, n);
+ if (ret)
+ break;
+
+ mem_entries += n;
+ n_mem_entries -= n;
+ }
+
+ return ret;
+}
+
+static int gh_rm_mem_lend_common(u32 message_id, struct gh_rm_mem_parcel *p)
+{
+ size_t msg_size = 0, initial_n_mem_entries = p->n_mem_entries;
+ void *msg, *resp;
+ struct gh_mem_share_req_header *req_header;
+ struct gh_mem_share_req_acl_section *acl_section;
+ struct gh_mem_share_req_mem_section *mem_section;
+ u32 *mem_attr_section;
+ size_t resp_size;
+ int ret;
+
+ if (!p->acl_entries || !p->n_acl_entries || !p->mem_entries || !p->n_mem_entries ||
+ p->n_acl_entries > U8_MAX)
+ return -EINVAL;
+
+ if (initial_n_mem_entries > GH_RM_MAX_MEM_ENTRIES)
+ initial_n_mem_entries = GH_RM_MAX_MEM_ENTRIES;
+
+ /* The format of the message goes:
+ * request header
+ * ACL entries (which VMs get what kind of access to this memory parcel)
+ * Memory entries (list of memory regions to share)
+ * Memory attributes (currently unused, we'll hard-code the size to 0)
+ */
+ msg_size += sizeof(struct gh_mem_share_req_header);
+ msg_size += offsetof(struct gh_mem_share_req_acl_section, entries[p->n_acl_entries]);
+ msg_size += offsetof(struct gh_mem_share_req_mem_section, entries[initial_n_mem_entries]);
+ msg_size += sizeof(u32); /* for memory attributes, currently unused */
+
+ msg = kzalloc(msg_size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ req_header = msg;
+ acl_section = (void *)req_header + sizeof(*req_header);
+ mem_section = (void *)acl_section + offsetof(struct gh_mem_share_req_acl_section,
+ entries[p->n_acl_entries]);
+ mem_attr_section = (void *)mem_section + offsetof(struct gh_mem_share_req_mem_section,
+ entries[initial_n_mem_entries]);
+
+ req_header->mem_type = p->mem_type;
+ if (initial_n_mem_entries != p->n_mem_entries)
+ req_header->flags |= GH_MEM_SHARE_REQ_FLAGS_APPEND;
+ req_header->label = p->label;
+
+ acl_section->n_entries = p->n_acl_entries;
+ memcpy(acl_section->entries, p->acl_entries, sizeof(*(p->acl_entries)) * p->n_acl_entries);
+
+ mem_section->n_entries = initial_n_mem_entries;
+ memcpy(mem_section->entries, p->mem_entries,
+ sizeof(*(p->mem_entries)) * initial_n_mem_entries);
+
+ /* Set n_entries for memory attribute section to 0 */
+ *mem_attr_section = 0;
+
+ ret = gh_rm_call(message_id, msg, msg_size, &resp, &resp_size);
+ if (ret)
+ return ret;
+
+ if (resp_size != sizeof(u32)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ p->mem_handle = *(u32 *)resp;
+
+ if (initial_n_mem_entries != p->n_mem_entries) {
+ ret = gh_rm_mem_append(p->mem_handle, false, &p->mem_entries[initial_n_mem_entries],
+ p->n_mem_entries - initial_n_mem_entries);
+ if (ret)
+ gh_rm_mem_reclaim(p);
+ }
+
+out:
+ kfree(resp);
+ return ret;
+}
+
+/**
+ * gh_rm_mem_lend() - Lend memory to other virtual machines.
+ * @parcel: Package the memory information of the memory to be lent.
+ *
+ * Returns - 0 on success; negative value on failure
+ *
+ * Lending removes Linux's access to the memory while the memory parcel is lent.
+ */
+int gh_rm_mem_lend(struct gh_rm_mem_parcel *parcel)
+{
+ return gh_rm_mem_lend_common(GH_RM_RPC_MEM_LEND, parcel);
+}
+EXPORT_SYMBOL_GPL(gh_rm_mem_lend);
+
+
+/**
+ * gh_rm_mem_share() - Share memory with other virtual machines.
+ * @parcel: Package the memory information of the memory to be shared.
+ *
+ * Returns - 0 on success; negative value on failure
+ *
+ * Sharing keeps Linux's access to the memory while the memory parcel is shared.
+ */
+int gh_rm_mem_share(struct gh_rm_mem_parcel *parcel)
+{
+ return gh_rm_mem_lend_common(GH_RM_RPC_MEM_SHARE, parcel);
+}
+EXPORT_SYMBOL_GPL(gh_rm_mem_share);
+
+/**
+ * gh_rm_mem_reclaim() - Reclaim a memory parcel
+ * @parcel: Package the memory information of the memory to be reclaimed.
+ *
+ * Returns - 0 on success; negative value on failure
+ *
+ * RM maps the associated memory back into the stage-2 page tables of the owner VM.
+ */
+int gh_rm_mem_reclaim(struct gh_rm_mem_parcel *parcel)
+{
+ struct gh_mem_release_req req = {
+ .mem_handle = parcel->mem_handle,
+ };
+ size_t resp_size;
+ void *resp;
+ int ret;
+
+ ret = gh_rm_call(GH_RM_RPC_MEM_RECLAIM, &req, sizeof(req), &resp, &resp_size);
+ if (ret)
+ return ret;
+ kfree(resp);
+
+ if (resp_size)
+ pr_warn("Received unexpected payload for MEM_RECLAIM: %lu\n", resp_size);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_mem_reclaim);
+
/**
* gh_rm_alloc_vmid() - Allocate a new VM in Gunyah. Returns the VM identifier.
* @vmid: Use GH_VMID_INVAL to dynamically allocate a VM. A reserved VMID can also be requested
diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h
index 169497f894c8..78d516032f6a 100644
--- a/include/linux/gunyah_rsc_mgr.h
+++ b/include/linux/gunyah_rsc_mgr.h
@@ -13,6 +13,7 @@
#include <linux/mod_devicetable.h>
#define GH_VMID_INVAL U16_MAX
+#define GH_MEM_HANDLE_INVAL U32_MAX
/* Gunyah recognizes VMID0 as an alias to the current VM's ID */
#define GH_VMID_SELF 0
@@ -39,7 +40,53 @@ enum gh_rm_vm_status {
GH_RM_VM_STATUS_RESET = 11,
};
+struct gh_rm_mem_acl_entry {
+ u16 vmid;
+#define GH_RM_ACL_X BIT(0)
+#define GH_RM_ACL_W BIT(1)
+#define GH_RM_ACL_R BIT(2)
+ u8 perms;
+ u8 reserved;
+} __packed;
+
+struct gh_rm_mem_entry {
+ u64 ipa_base;
+ u64 size;
+} __packed;
+
+enum gh_rm_mem_type {
+ GH_RM_MEM_TYPE_NORMAL = 0,
+ GH_RM_MEM_TYPE_IO = 1,
+};
+
+/*
+ * struct gh_rm_mem_parcel - Package info about memory to be lent/shared/donated/reclaimed
+ * @mem_type: The type of memory: normal (DDR) or IO
+ * @label: An client-specified identifier which can be used by the other VMs to identify the purpose
+ * of the memory parcel.
+ * @acl_entries: An array of access control entries. Each entry specifies a VM and what access
+ * is allowed for the memory parcel.
+ * @n_acl_entries: Count of the number of entries in the `acl_entries` array.
+ * @mem_entries: An list of regions to be associated with the memory parcel. Addresses should be
+ * (intermediate) physical addresses from Linux's perspective.
+ * @n_mem_entries: Count of the number of entries in the `mem_entries` array.
+ * @mem_handle: On success, filled with memory handle that RM allocates for this memory parcel
+ */
+struct gh_rm_mem_parcel {
+ enum gh_rm_mem_type mem_type;
+ u32 label;
+ size_t n_acl_entries;
+ struct gh_rm_mem_acl_entry *acl_entries;
+ size_t n_mem_entries;
+ struct gh_rm_mem_entry *mem_entries;
+ u32 mem_handle;
+};
+
/* RPC Calls */
+int gh_rm_mem_lend(struct gh_rm_mem_parcel *parcel);
+int gh_rm_mem_share(struct gh_rm_mem_parcel *parcel);
+int gh_rm_mem_reclaim(struct gh_rm_mem_parcel *parcel);
+
int gh_rm_alloc_vmid(u16 vmid);
int gh_rm_dealloc_vmid(u16 vmid);
int gh_rm_vm_start(u16 vmid);
--
2.25.1
On Qualcomm platforms, there is a firmware entity which controls access
to physical pages. In order to share memory with another VM, this entity
needs to be informed that the guest VM should have access to the memory.
Co-developed-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Elliot Berman <[email protected]>
---
drivers/virt/gunyah/rsc_mgr.c | 52 +++++++++++++++++++++++++++++++
drivers/virt/gunyah/rsc_mgr.h | 3 ++
drivers/virt/gunyah/rsc_mgr_rpc.c | 7 +++++
include/linux/gunyah_rsc_mgr.h | 12 ++++++-
4 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/drivers/virt/gunyah/rsc_mgr.c b/drivers/virt/gunyah/rsc_mgr.c
index f2f776edecb8..6e590f2a8cfa 100644
--- a/drivers/virt/gunyah/rsc_mgr.c
+++ b/drivers/virt/gunyah/rsc_mgr.c
@@ -105,6 +105,13 @@ struct gh_rsc_mgr {
static struct gh_rsc_mgr *__rsc_mgr;
SRCU_NOTIFIER_HEAD_STATIC(gh_rm_notifier);
+/* Needs to be out of the gh_rsc_mgr struct as platform_ops might probe before
+ * rsc mgr probes. We can't defer the platform_ops because it might be that
+ * Linux is not a Gunyah guest.
+ */
+static struct gunyah_rm_platform_ops *rm_platform_ops;
+static DECLARE_RWSEM(rm_platform_ops_lock);
+
static struct gh_rm_connection *gh_rm_alloc_connection(u32 msg_id, u8 type)
{
struct gh_rm_connection *connection;
@@ -498,6 +505,51 @@ int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
return ret;
}
+int gh_rm_platform_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel)
+{
+ int ret = 0;
+
+ down_read(&rm_platform_ops_lock);
+ if (rm_platform_ops && rm_platform_ops->pre_mem_share)
+ ret = rm_platform_ops->pre_mem_share(mem_parcel);
+ up_read(&rm_platform_ops_lock);
+ return ret;
+}
+
+int gh_rm_platform_post_mem_reclaim(struct gh_rm_mem_parcel *mem_parcel)
+{
+ int ret = 0;
+
+ down_read(&rm_platform_ops_lock);
+ if (rm_platform_ops && rm_platform_ops->post_mem_reclaim)
+ ret = rm_platform_ops->post_mem_reclaim(mem_parcel);
+ up_read(&rm_platform_ops_lock);
+ return ret;
+}
+
+int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops)
+{
+ int ret = 0;
+
+ down_write(&rm_platform_ops_lock);
+ if (!rm_platform_ops)
+ rm_platform_ops = platform_ops;
+ else
+ ret = -EEXIST;
+ up_write(&rm_platform_ops_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_register_platform_ops);
+
+void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops)
+{
+ down_write(&rm_platform_ops_lock);
+ if (rm_platform_ops == platform_ops)
+ rm_platform_ops = NULL;
+ up_write(&rm_platform_ops_lock);
+}
+EXPORT_SYMBOL_GPL(gh_rm_unregister_platform_ops);
+
int gh_rm_register_notifier(struct notifier_block *nb)
{
return srcu_notifier_chain_register(&gh_rm_notifier, nb);
diff --git a/drivers/virt/gunyah/rsc_mgr.h b/drivers/virt/gunyah/rsc_mgr.h
index f6ed58405ef4..49becf82edf7 100644
--- a/drivers/virt/gunyah/rsc_mgr.h
+++ b/drivers/virt/gunyah/rsc_mgr.h
@@ -130,6 +130,9 @@ struct gh_vm_set_boot_context_req {
int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
void **resp_buf, size_t *resp_buff_size);
+int gh_rm_platform_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel);
+int gh_rm_platform_post_mem_reclaim(struct gh_rm_mem_parcel *mem_parcel);
+
struct gh_rm_device {
struct device dev;
const char *name;
diff --git a/drivers/virt/gunyah/rsc_mgr_rpc.c b/drivers/virt/gunyah/rsc_mgr_rpc.c
index 17f88c3d5726..c7e3bb975640 100644
--- a/drivers/virt/gunyah/rsc_mgr_rpc.c
+++ b/drivers/virt/gunyah/rsc_mgr_rpc.c
@@ -120,6 +120,10 @@ static int gh_rm_mem_lend_common(u32 message_id, struct gh_rm_mem_parcel *p)
if (initial_n_mem_entries > GH_RM_MAX_MEM_ENTRIES)
initial_n_mem_entries = GH_RM_MAX_MEM_ENTRIES;
+ ret = gh_rm_platform_pre_mem_share(p);
+ if (ret)
+ return ret;
+
/* The format of the message goes:
* request header
* ACL entries (which VMs get what kind of access to this memory parcel)
@@ -163,6 +167,7 @@ static int gh_rm_mem_lend_common(u32 message_id, struct gh_rm_mem_parcel *p)
if (resp_size != sizeof(u32)) {
ret = -EIO;
+ gh_rm_platform_post_mem_reclaim(p);
goto out;
}
@@ -234,6 +239,8 @@ int gh_rm_mem_reclaim(struct gh_rm_mem_parcel *parcel)
if (resp_size)
pr_warn("Received unexpected payload for MEM_RECLAIM: %lu\n", resp_size);
+ ret = gh_rm_platform_post_mem_reclaim(parcel);
+
return ret;
}
EXPORT_SYMBOL_GPL(gh_rm_mem_reclaim);
diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h
index 78d516032f6a..6e5e67e96688 100644
--- a/include/linux/gunyah_rsc_mgr.h
+++ b/include/linux/gunyah_rsc_mgr.h
@@ -119,7 +119,6 @@ struct gh_rm_hyp_resource {
} __packed;
ssize_t gh_rm_get_hyp_resources(u16 vmid, struct gh_rm_hyp_resource **resources);
-int gh_rm_get_vmid(u16 *vmid);
#define GH_RM_BOOT_CONTEXT_REG_SET_REGISTERS 0
#define GH_RM_BOOT_CONTEXT_REG_SET_PC 1
@@ -143,4 +142,15 @@ void gh_rm_driver_unregister(struct gh_rm_driver *ghrm_drv);
#define module_gh_rm_driver(ghrm_drv) \
module_driver(ghrm_drv, gh_rm_driver_register, gh_rm_driver_unregister)
+#if IS_ENABLED(CONFIG_GUNYAH)
+int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops);
+void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops);
+int gh_rm_get_vmid(u16 *vmid);
+#else
+static inline int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops)
+ { return 0; }
+static inline void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops) { }
+static inline int gh_rm_get_vmid(u16 *vmid) { return -ENODEV; }
+#endif
+
#endif
--
2.25.1
Gunyah Resource Manager exposes an interface which should occupy a few
devices on Linux:
- a remote procedure call framework to talk to RM
- a virtual machine manager to launch VMs, share memory with them,
schedule runtime for those VMs, and handle certain faults.
Create a virtual device bus for the console and VM Manager functions.
Signed-off-by: Elliot Berman <[email protected]>
---
drivers/virt/gunyah/Makefile | 2 +-
drivers/virt/gunyah/rsc_mgr.c | 42 ++++++++++++++--
drivers/virt/gunyah/rsc_mgr.h | 10 ++++
drivers/virt/gunyah/rsc_mgr_bus.c | 83 +++++++++++++++++++++++++++++++
include/linux/gunyah_rsc_mgr.h | 19 +++++++
include/linux/mod_devicetable.h | 8 +++
scripts/mod/devicetable-offsets.c | 3 ++
scripts/mod/file2alias.c | 10 ++++
8 files changed, 173 insertions(+), 4 deletions(-)
create mode 100644 drivers/virt/gunyah/rsc_mgr_bus.c
diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile
index 2c18b0a56413..a9169af6c61f 100644
--- a/drivers/virt/gunyah/Makefile
+++ b/drivers/virt/gunyah/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_GUNYAH) += gunyah.o
-gunyah_rsc_mgr-y += rsc_mgr.o
+gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_bus.o
obj-$(CONFIG_GUNYAH_RESORUCE_MANAGER) += gunyah_rsc_mgr.o
diff --git a/drivers/virt/gunyah/rsc_mgr.c b/drivers/virt/gunyah/rsc_mgr.c
index a9fde703cbbe..f2f776edecb8 100644
--- a/drivers/virt/gunyah/rsc_mgr.c
+++ b/drivers/virt/gunyah/rsc_mgr.c
@@ -98,6 +98,8 @@ struct gh_rsc_mgr {
struct mutex send_lock;
struct work_struct recv_work;
+
+ struct gh_rm_device vm_mgr_dev;
};
static struct gh_rsc_mgr *__rsc_mgr;
@@ -567,17 +569,27 @@ static int gh_rm_drv_probe(struct platform_device *pdev)
__rsc_mgr = rsc_mgr;
+ ret = gh_rm_device_add(&rsc_mgr->vm_mgr_dev, &pdev->dev, GH_RM_DEVICE_VM_MGR);
+ if (ret)
+ goto err_msgq;
+
return 0;
+
+err_msgq:
+ gh_msgq_remove(&rsc_mgr->msgq);
+ return ret;
}
static int gh_rm_drv_remove(struct platform_device *pdev)
{
struct gh_rsc_mgr *rsc_mgr = platform_get_drvdata(pdev);
+ gh_rm_device_delete(&rsc_mgr->vm_mgr_dev);
+
__rsc_mgr = NULL;
- mbox_free_channel(gunyah_msgq_chan(&rsc_mgr->msgq));
- gunyah_msgq_remove(&rsc_mgr->msgq);
+ mbox_free_channel(gh_msgq_chan(&rsc_mgr->msgq));
+ gh_msgq_remove(&rsc_mgr->msgq);
return 0;
}
@@ -596,7 +608,31 @@ static struct platform_driver gh_rm_driver = {
.of_match_table = gh_rm_of_match,
},
};
-module_platform_driver(gh_rsc_mgr_driver);
+
+static int __init gh_rm_init(void)
+{
+ int ret;
+
+ ret = gh_rm_bus_register();
+ if (ret) {
+ pr_err("Failed to register gh_rm_bus: %d\n", ret);
+ return ret;
+ }
+
+ ret = platform_driver_register(&gh_rm_driver);
+ if (ret)
+ gh_rm_bus_unregister();
+
+ return ret;
+}
+subsys_initcall(gh_rm_init);
+
+static void __exit gh_rm_exit(void)
+{
+ platform_driver_unregister(&gh_rm_driver);
+ gh_rm_bus_unregister();
+}
+module_exit(gh_rm_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Gunyah Resource Manager Driver");
diff --git a/drivers/virt/gunyah/rsc_mgr.h b/drivers/virt/gunyah/rsc_mgr.h
index e4f2499267bf..129e9d514f2a 100644
--- a/drivers/virt/gunyah/rsc_mgr.h
+++ b/drivers/virt/gunyah/rsc_mgr.h
@@ -31,4 +31,14 @@
int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
void **resp_buf, size_t *resp_buff_size);
+struct gh_rm_device {
+ struct device dev;
+ const char *name;
+};
+
+int gh_rm_bus_register(void);
+void gh_rm_bus_unregister(void);
+int gh_rm_device_add(struct gh_rm_device *ghrm_dev, struct device *parent, const char *name);
+void gh_rm_device_delete(struct gh_rm_device *ghrm_dev);
+
#endif
diff --git a/drivers/virt/gunyah/rsc_mgr_bus.c b/drivers/virt/gunyah/rsc_mgr_bus.c
new file mode 100644
index 000000000000..bbb3bdc747cc
--- /dev/null
+++ b/drivers/virt/gunyah/rsc_mgr_bus.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "gh_rsc_mgr: " fmt
+
+#include <linux/device.h>
+#include <linux/gunyah_rsc_mgr.h>
+#include <linux/mod_devicetable.h>
+
+#include "rsc_mgr.h"
+
+#define to_gh_rm_device(dev) container_of(dev, struct gh_rm_device, dev)
+#define to_gh_rm_driver(drv) container_of(drv, struct gh_rm_driver, drv)
+
+static int gh_rm_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct gh_rm_device *ghrm_dev = to_gh_rm_device(dev);
+ struct gh_rm_driver *ghrm_drv = to_gh_rm_driver(drv);
+
+ return !strncmp(ghrm_dev->name, ghrm_drv->id_table->name, GUNYAH_RSC_MGR_NAME_SIZE);
+}
+
+static int gh_rm_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct gh_rm_device *ghrm_dev = to_gh_rm_device(dev);
+
+ return add_uevent_var(env, "MODALIAS=%s%s", GUNYAH_RSC_MGR_PREFIX, ghrm_dev->name);
+}
+
+static struct bus_type gh_rm_bus = {
+ .name = "gh_rsc_mgr",
+ .match = gh_rm_bus_match,
+ .uevent = gh_rm_bus_uevent,
+};
+
+int gh_rm_bus_register(void)
+{
+ return bus_register(&gh_rm_bus);
+}
+
+void gh_rm_bus_unregister(void)
+{
+ bus_unregister(&gh_rm_bus);
+}
+
+int gh_rm_device_add(struct gh_rm_device *ghrm_dev, struct device *parent, const char *name)
+{
+ ghrm_dev->dev.bus = &gh_rm_bus;
+ ghrm_dev->dev.parent = parent;
+ ghrm_dev->name = name;
+
+ device_initialize(&ghrm_dev->dev);
+
+ dev_set_name(&ghrm_dev->dev, "%s.%s", dev_name(parent), name);
+ return device_add(&ghrm_dev->dev);
+}
+
+void gh_rm_device_delete(struct gh_rm_device *ghrm_dev)
+{
+ device_del(&ghrm_dev->dev);
+}
+
+int __gh_rm_driver_register(struct gh_rm_driver *ghrm_drv, struct module *owner,
+ const char *modname)
+{
+ if (WARN_ON(!ghrm_drv->drv.probe) || WARN_ON(!ghrm_drv->id_table))
+ return -EINVAL;
+
+ ghrm_drv->drv.bus = &gh_rm_bus;
+ ghrm_drv->drv.owner = owner;
+ ghrm_drv->drv.mod_name = modname;
+
+ return driver_register(&ghrm_drv->drv);
+}
+EXPORT_SYMBOL_GPL(__gh_rm_driver_register);
+
+void gh_rm_driver_unregister(struct gh_rm_driver *ghrm_drv)
+{
+ driver_unregister(&ghrm_drv->drv);
+}
+EXPORT_SYMBOL_GPL(gh_rm_driver_unregister);
diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h
index b3b37225b7fb..0eeb7202fa33 100644
--- a/include/linux/gunyah_rsc_mgr.h
+++ b/include/linux/gunyah_rsc_mgr.h
@@ -9,6 +9,8 @@
#include <linux/list.h>
#include <linux/notifier.h>
#include <linux/gunyah.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
#define GH_VMID_INVAL U16_MAX
@@ -23,4 +25,21 @@ struct gh_rm_notification {
int gh_rm_register_notifier(struct notifier_block *nb);
int gh_rm_unregister_notifier(struct notifier_block *nb);
+#define GH_RM_DEVICE_VM_MGR "vm_mgr"
+
+struct gh_rm_driver {
+ const struct gunyah_rsc_mgr_device_id *id_table;
+ struct device_driver drv;
+};
+
+int __gh_rm_driver_register(struct gh_rm_driver *ghrm_drv, struct module *owner,
+ const char *modname);
+#define gh_rm_driver_register(ghrm_drv) \
+ __gh_rm_driver_register(ghrm_drv, THIS_MODULE, KBUILD_MODNAME)
+
+void gh_rm_driver_unregister(struct gh_rm_driver *ghrm_drv);
+
+#define module_gh_rm_driver(ghrm_drv) \
+ module_driver(ghrm_drv, gh_rm_driver_register, gh_rm_driver_unregister)
+
#endif
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 549590e9c644..c4dc0ee6ae00 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -911,4 +911,12 @@ struct ishtp_device_id {
kernel_ulong_t driver_data;
};
+#define GUNYAH_RSC_MGR_NAME_SIZE 32
+#define GUNYAH_RSC_MGR_PREFIX "gh_rsc_mgr:"
+
+struct gunyah_rsc_mgr_device_id {
+ const char name[GUNYAH_RSC_MGR_NAME_SIZE];
+ kernel_ulong_t driver_data;
+};
+
#endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
index c0d3bcb99138..7b6944ed9336 100644
--- a/scripts/mod/devicetable-offsets.c
+++ b/scripts/mod/devicetable-offsets.c
@@ -262,5 +262,8 @@ int main(void)
DEVID(ishtp_device_id);
DEVID_FIELD(ishtp_device_id, guid);
+ DEVID(gunyah_rsc_mgr_device_id);
+ DEVID_FIELD(gunyah_rsc_mgr_device_id, name);
+
return 0;
}
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 80d973144fde..a02b9e8e02a8 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -1452,6 +1452,15 @@ static int do_dfl_entry(const char *filename, void *symval, char *alias)
return 1;
}
+/* Looks like: gh_rsc_mgr:S */
+static int do_gunyah_rsc_mgr_entry(const char *filename, void *symval, char *alias)
+{
+ DEF_FIELD_ADDR(symval, gunyah_rsc_mgr_device_id, name);
+ sprintf(alias, GUNYAH_RSC_MGR_PREFIX "%s", *name);
+
+ return 1;
+}
+
/* Does namelen bytes of name exactly match the symbol? */
static bool sym_is(const char *name, unsigned namelen, const char *symbol)
{
@@ -1531,6 +1540,7 @@ static const struct devtable devtable[] = {
{"ssam", SIZE_ssam_device_id, do_ssam_entry},
{"dfl", SIZE_dfl_device_id, do_dfl_entry},
{"ishtp", SIZE_ishtp_device_id, do_ishtp_entry},
+ {"gh_rsc_mgr", SIZE_gunyah_rsc_mgr_device_id, do_gunyah_rsc_mgr_entry},
};
/* Create MODULE_ALIAS() statements.
--
2.25.1
Add hypercalls to send and receive messages on a Gunyah message queue.
Signed-off-by: Elliot Berman <[email protected]>
---
arch/arm64/gunyah/gunyah_hypercall.c | 33 ++++++++++++++++++++++++++++
include/linux/gunyah.h | 5 +++++
2 files changed, 38 insertions(+)
diff --git a/arch/arm64/gunyah/gunyah_hypercall.c b/arch/arm64/gunyah/gunyah_hypercall.c
index 0beb3123d650..d28600909be4 100644
--- a/arch/arm64/gunyah/gunyah_hypercall.c
+++ b/arch/arm64/gunyah/gunyah_hypercall.c
@@ -26,6 +26,8 @@
GH_FN_ID(GH_CALL_TYPE_HYPERCALL, fn))
#define GH_HYPERCALL_HYP_IDENTIFY GH_HYPERCALL(0x0000)
+#define GH_HYPERCALL_MSGQ_SEND GH_HYPERCALL(0x001B)
+#define GH_HYPERCALL_MSGQ_RECV GH_HYPERCALL(0x001C)
/**
* gh_hypercall_get_uid() - Returns a UID when running under a Gunyah hypervisor
@@ -65,5 +67,36 @@ void gh_hypercall_hyp_identify(struct gh_hypercall_hyp_identify_resp *hyp_identi
}
EXPORT_SYMBOL_GPL(gh_hypercall_hyp_identify);
+int gh_hypercall_msgq_send(u64 capid, size_t size, uintptr_t buff, int tx_flags, bool *ready)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_hvc(GH_HYPERCALL_MSGQ_SEND, capid, size, buff, tx_flags, 0, &res);
+
+ if (res.a0)
+ return res.a0;
+
+ *ready = res.a1;
+
+ return res.a0;
+}
+EXPORT_SYMBOL_GPL(gh_hypercall_msgq_send);
+
+int gh_hypercall_msgq_recv(u64 capid, uintptr_t buff, size_t size, size_t *recv_size, bool *ready)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_hvc(GH_HYPERCALL_MSGQ_RECV, capid, buff, size, 0, &res);
+
+ if (res.a0)
+ return res.a0;
+
+ *recv_size = res.a1;
+ *ready = res.a2;
+
+ return res.a0;
+}
+EXPORT_SYMBOL_GPL(gh_hypercall_msgq_recv);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Gunyah Hypervisor Hypercalls");
diff --git a/include/linux/gunyah.h b/include/linux/gunyah.h
index 166156f69df9..c863cac4a3cf 100644
--- a/include/linux/gunyah.h
+++ b/include/linux/gunyah.h
@@ -102,4 +102,9 @@ static inline u16 gh_api_version(void)
void gh_hypercall_get_uid(u32 uid[4]);
void gh_hypercall_hyp_identify(struct gh_hypercall_hyp_identify_resp *hyp_identity);
+#define GH_HYPERCALL_MSGQ_TX_FLAGS_PUSH BIT(0)
+
+int gh_hypercall_msgq_send(u64 capid, size_t size, uintptr_t buff, int tx_flags, bool *ready);
+int gh_hypercall_msgq_recv(u64 capid, uintptr_t buff, size_t size, size_t *recv_size, bool *ready);
+
#endif
--
2.25.1
Gunyah message queues are a unidirectional inter-VM pipe for messages up
to 1024 bytes. This driver supports pairing a receiver message queue and
a transmitter message queue to expose a single mailbox channel.
Signed-off-by: Elliot Berman <[email protected]>
---
Documentation/virt/gunyah/message-queue.rst | 8 +
MAINTAINERS | 1 +
drivers/mailbox/Kconfig | 10 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/gunyah-msgq.c | 225 ++++++++++++++++++++
include/linux/gunyah.h | 61 +++++-
6 files changed, 305 insertions(+), 2 deletions(-)
create mode 100644 drivers/mailbox/gunyah-msgq.c
diff --git a/Documentation/virt/gunyah/message-queue.rst b/Documentation/virt/gunyah/message-queue.rst
index c9f4d75e2291..7324fc616684 100644
--- a/Documentation/virt/gunyah/message-queue.rst
+++ b/Documentation/virt/gunyah/message-queue.rst
@@ -53,3 +53,11 @@ reference message queue 2's capability ID.
| | | | | |
| | | | | |
+---------------+ +-----------------+ +---------------+
+
+Gunyah message queues are exposed as mailboxes. To create the mailbox, create
+a mbox_client and call `gh_msgq_init`. On receipt of the RX_READY interrupt,
+all messages in the RX message queue are read and pushed via the `rx_callback`
+of the registered mbox_client.
+
+.. kernel-doc:: drivers/mailbox/gunyah-msgq.c
+ :identifiers: gh_msgq_init
diff --git a/MAINTAINERS b/MAINTAINERS
index 211977dc344b..586539eadd3b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8943,6 +8943,7 @@ S: Supported
F: Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
F: Documentation/virt/gunyah/
F: arch/arm64/gunyah/
+F: drivers/mailbox/gunyah-msgq.c
F: drivers/virt/gunyah/
F: include/linux/gunyah.h
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 05d6fae800e3..baf9451c5f04 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -41,6 +41,16 @@ config IMX_MBOX
help
Mailbox implementation for i.MX Messaging Unit (MU).
+config GUNYAH_MESSAGE_QUEUES
+ tristate "Gunyah Message Queue Mailbox"
+ depends on GUNYAH
+ help
+ Mailbox implementation for Gunyah Message Queues. Gunyah message queues
+ are an IPC mechanism to pass short messages between virtual machines
+ running under the Gunyah hypervisor.
+
+ Say Y here if you run Linux as a Gunyah virtual machine.
+
config PLATFORM_MHU
tristate "Platform MHU Mailbox"
depends on OF
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index fc9376117111..5f929bb55e9a 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -55,6 +55,8 @@ obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
+obj-$(CONFIG_GUNYAH) += gunyah-msgq.o
+
obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o
obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
diff --git a/drivers/mailbox/gunyah-msgq.c b/drivers/mailbox/gunyah-msgq.c
new file mode 100644
index 000000000000..c4d2ede9334f
--- /dev/null
+++ b/drivers/mailbox/gunyah-msgq.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gunyah.h>
+#include <linux/printk.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#define mbox_chan_to_msgq(chan) (container_of(chan->mbox, struct gh_msgq, mbox))
+
+static inline bool gh_msgq_has_tx(struct gh_msgq *msgq)
+{
+ return msgq->tx_ghrsc->type == GUNYAH_RESOURCE_TYPE_MSGQ_TX;
+}
+
+static inline bool gh_msgq_has_rx(struct gh_msgq *msgq)
+{
+ return msgq->rx_ghrsc->type == GUNYAH_RESOURCE_TYPE_MSGQ_RX;
+}
+
+static irqreturn_t gh_msgq_rx_irq_handler(int irq, void *data)
+{
+ struct gh_msgq *msgq = data;
+ struct gh_msgq_rx_data rx_data;
+ unsigned long gh_err;
+ ssize_t ret;
+ bool ready = false;
+
+ do {
+ gh_err = gh_hypercall_msgq_recv(msgq->rx_ghrsc->capid,
+ (uintptr_t)&rx_data.data, sizeof(rx_data.data),
+ &rx_data.length, &ready);
+ if (gh_err == GH_ERROR_OK) {
+ mbox_chan_received_data(gh_msgq_chan(msgq), &rx_data);
+ } else if (GH_ERROR_MSGQUEUE_EMPTY) {
+ break;
+ } else {
+ pr_warn("Failed to receive data from msgq for %s: %ld\n",
+ msgq->mbox.dev ? dev_name(msgq->mbox.dev) : "", ret);
+ break;
+ }
+ } while (ready);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t gh_msgq_tx_irq_handler(int irq, void *data)
+{
+ struct gh_msgq *msgq = data;
+
+ mbox_chan_txdone(gh_msgq_chan(msgq), 0);
+
+ return IRQ_HANDLED;
+}
+
+static void gh_msgq_txdone_tasklet(unsigned long data)
+{
+ struct gh_msgq *msgq = (struct gh_msgq *)data;
+
+ mbox_chan_txdone(gh_msgq_chan(msgq), msgq->last_status);
+}
+
+static int gh_msgq_send_data(struct mbox_chan *chan, void *data)
+{
+ struct gh_msgq *msgq = mbox_chan_to_msgq(chan);
+ struct gh_msgq_tx_data *msgq_data = data;
+ u64 tx_flags = 0;
+ unsigned long ret;
+ bool ready;
+
+ if (msgq_data->push)
+ tx_flags |= GH_HYPERCALL_MSGQ_TX_FLAGS_PUSH;
+
+ ret = gh_hypercall_msgq_send(msgq->tx_ghrsc->capid, msgq_data->length,
+ (uintptr_t)msgq_data->data, tx_flags, &ready);
+
+ /**
+ * unlikely because Linux tracks state of msgq and should not try to
+ * send message when msgq is full.
+ */
+ if (unlikely(ret == GH_ERROR_MSGQUEUE_FULL))
+ return -EAGAIN;
+
+ /**
+ * Propagate all other errors to client. If we return error to mailbox
+ * framework, then no other messages can be sent and nobody will know
+ * to retry this message.
+ */
+ msgq->last_status = gh_remap_error(ret);
+
+ /**
+ * This message was successfully sent, but message queue isn't ready to
+ * receive more messages because it's now full. Mailbox framework
+ * requires that we only report that message was transmitted only when
+ * we're ready to transmit another message. We'll get that in the form
+ * of tx IRQ once the other side starts to drain the msgq.
+ */
+ if (ret == GH_ERROR_OK && !ready)
+ return 0;
+
+ /**
+ * We can send more messages. Mailbox framework requires that tx done
+ * happens asynchronously to sending the message. Gunyah message queues
+ * tell us right away on the hypercall return whether we can send more
+ * messages. To work around this, defer the txdone to a tasklet.
+ */
+ tasklet_schedule(&msgq->txdone_tasklet);
+
+ return 0;
+}
+
+struct mbox_chan_ops gh_msgq_ops = {
+ .send_data = gh_msgq_send_data,
+};
+
+/**
+ * gh_msgq_init() - Initialize a Gunyah message queue with an mbox_client
+ * @parent: optional, device parent used for the mailbox controller
+ * @msgq: Pointer to the gh_msgq to initialize
+ * @cl: A mailbox client to bind to the mailbox channel that the message queue creates
+ * @tx_ghrsc: optional, the transmission side of the message queue
+ * @rx_ghrsc: optional, the receiving side of the message queue
+ *
+ * At least one of tx_ghrsc and rx_ghrsc should be not NULL. Most message queue use cases come with
+ * a pair of message queues to facilitiate bidirectional communication. When tx_ghrsc is set,
+ * the client can send messages with mbox_send_message(gh_msgq_chan(msgq), msg). When rx_ghrsc
+ * is set, the mbox_client should register an .rx_callback() and the message queue driver will
+ * push all available messages upon receiving the RX ready interrupt. The messages should be
+ * consumed or copied by the client right away as the gh_msgq_rx_data will be replaced/destroyed
+ * after the callback.
+ *
+ * Returns - 0 on success, negative otherwise
+ */
+int gh_msgq_init(struct device *parent, struct gh_msgq *msgq, struct mbox_client *cl,
+ struct gunyah_resource *tx_ghrsc, struct gunyah_resource *rx_ghrsc)
+{
+ int ret;
+
+ /* Must have at least a tx_ghrsc or rx_ghrsc and that they are the right device types */
+ if ((!tx_ghrsc && !rx_ghrsc) ||
+ (tx_ghrsc && tx_ghrsc->type != GUNYAH_RESOURCE_TYPE_MSGQ_TX) ||
+ (rx_ghrsc && rx_ghrsc->type != GUNYAH_RESOURCE_TYPE_MSGQ_RX))
+ return -EINVAL;
+
+ msgq->tx_ghrsc = tx_ghrsc;
+ msgq->rx_ghrsc = rx_ghrsc;
+
+ msgq->mbox.dev = parent;
+ msgq->mbox.ops = &gh_msgq_ops;
+ msgq->mbox.chans = kcalloc(1, sizeof(*msgq->mbox.chans), GFP_KERNEL);
+ msgq->mbox.num_chans = 1;
+ msgq->mbox.txdone_irq = true;
+
+ if (gh_msgq_has_tx(msgq)) {
+ ret = request_irq(msgq->tx_ghrsc->irq, gh_msgq_tx_irq_handler, 0, "gh_msgq_tx",
+ msgq);
+ if (ret)
+ goto err_chans;
+ }
+
+ if (gh_msgq_has_rx(msgq)) {
+ ret = request_threaded_irq(msgq->rx_ghrsc->irq, NULL, gh_msgq_rx_irq_handler,
+ IRQF_ONESHOT, "gh_msgq_rx", msgq);
+ if (ret)
+ goto err_tx_irq;
+ }
+
+ tasklet_init(&msgq->txdone_tasklet, gh_msgq_txdone_tasklet, (unsigned long)msgq);
+
+ ret = mbox_controller_register(&msgq->mbox);
+ if (ret)
+ goto err_rx_irq;
+
+ ret = mbox_bind_client(gh_msgq_chan(msgq), cl);
+ if (ret)
+ goto err_mbox;
+
+ return 0;
+err_mbox:
+ mbox_controller_unregister(&msgq->mbox);
+err_rx_irq:
+ if (gh_msgq_has_rx(msgq))
+ free_irq(msgq->rx_ghrsc->irq, msgq);
+err_tx_irq:
+ if (gh_msgq_has_tx(msgq))
+ free_irq(msgq->tx_ghrsc->irq, msgq);
+err_chans:
+ kfree(msgq->mbox.chans);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_msgq_init);
+
+void gh_msgq_remove(struct gh_msgq *msgq)
+{
+ if (gh_msgq_has_rx(msgq))
+ free_irq(msgq->rx_ghrsc->irq, msgq);
+
+ if (gh_msgq_has_tx(msgq))
+ free_irq(msgq->tx_ghrsc->irq, msgq);
+
+ kfree(msgq->mbox.chans);
+}
+EXPORT_SYMBOL_GPL(gh_msgq_remove);
+
+
+static int __init gh_msgq_init_module(void)
+{
+ if (gh_api_version() != GUNYAH_API_V1) {
+ pr_warn("Unrecognized gunyah version: %u. Currently supported: %d\n",
+ gh_api_version(), GUNYAH_API_V1);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+module_init(gh_msgq_init_module);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Gunyah Message Queue Driver");
diff --git a/include/linux/gunyah.h b/include/linux/gunyah.h
index c863cac4a3cf..e317d7ac938f 100644
--- a/include/linux/gunyah.h
+++ b/include/linux/gunyah.h
@@ -7,10 +7,67 @@
#define _GUNYAH_H
#include <linux/bitfield.h>
-#include <linux/types.h>
#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+#include <linux/types.h>
+
+/* Follows resource manager's resource types for VM_GET_HYP_RESOURCES */
+enum gunyah_resource_type {
+ GUNYAH_RESOURCE_TYPE_BELL_TX = 0,
+ GUNYAH_RESOURCE_TYPE_BELL_RX = 1,
+ GUNYAH_RESOURCE_TYPE_MSGQ_TX = 2,
+ GUNYAH_RESOURCE_TYPE_MSGQ_RX = 3,
+ GUNYAH_RESOURCE_TYPE_VCPU = 4,
+};
+
+struct gunyah_resource {
+ enum gunyah_resource_type type;
+ u64 capid;
+ int irq;
+};
+
+/**
+ * Gunyah Message Queues
+ */
+
+#define GH_MSGQ_MAX_MSG_SIZE 240
+
+struct gh_msgq_tx_data {
+ size_t length;
+ bool push;
+ char data[];
+};
+
+struct gh_msgq_rx_data {
+ size_t length;
+ char data[GH_MSGQ_MAX_MSG_SIZE];
+};
+
+struct gh_msgq {
+ struct gunyah_resource *tx_ghrsc;
+ struct gunyah_resource *rx_ghrsc;
+
+ /* msgq private */
+ int last_status;
+ struct mbox_controller mbox;
+ struct tasklet_struct txdone_tasklet;
+};
+
+
+int gh_msgq_init(struct device *parent, struct gh_msgq *msgq, struct mbox_client *cl,
+ struct gunyah_resource *tx_ghrsc, struct gunyah_resource *rx_ghrsc);
+void gh_msgq_remove(struct gh_msgq *msgq);
+
+static inline struct mbox_chan *gh_msgq_chan(struct gh_msgq *msgq)
+{
+ return &msgq->mbox.chans[0];
+}
+
+/******************************************************************************/
+/* Common arch-independent macros and definitions for Gunyah hypercalls */
-/* Common Gunyah macros */
#define GH_CAPID_INVAL U64_MAX
#define GH_VMID_ROOT_VM 0xff
--
2.25.1
Fix build error when CONFIG_ARM64_SVE is selected and
asm/alternative-macros.h wasn't implicitly included by another header.
Signed-off-by: Elliot Berman <[email protected]>
---
include/linux/arm-smccc.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 220c8c60e021..6a627cdbbdec 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -383,6 +383,7 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
/* nVHE hypervisor doesn't have a current thread so needs separate checks */
#if defined(CONFIG_ARM64_SVE) && !defined(__KVM_NVHE_HYPERVISOR__)
+#include <asm/alternative-macros.h>
#define SMCCC_SVE_CHECK ALTERNATIVE("nop \n", "bl __arm_smccc_sve_check \n", \
ARM64_SVE)
--
2.25.1
Add remaining ioctls to support non-proxy VM boot:
- Gunyah Resource Manager uses the VM's devicetree to configure the
virtual machine. The location of the devicetree in the guest's
virtual memory can be declared via the SET_DTB_CONFIG ioctl.
- Trigger start of the virtual machine with VM_START ioctl.
Co-developed-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Prakruthi Deepak Heragu <[email protected]>
Signed-off-by: Elliot Berman <[email protected]>
---
arch/arm64/include/uapi/asm/gunyah.h | 17 ++++++
drivers/virt/gunyah/vm_mgr.c | 91 ++++++++++++++++++++++++++++
drivers/virt/gunyah/vm_mgr.h | 9 +++
drivers/virt/gunyah/vm_mgr_mm.c | 29 +++++++--
include/uapi/linux/gunyah.h | 8 +++
5 files changed, 149 insertions(+), 5 deletions(-)
create mode 100644 arch/arm64/include/uapi/asm/gunyah.h
diff --git a/arch/arm64/include/uapi/asm/gunyah.h b/arch/arm64/include/uapi/asm/gunyah.h
new file mode 100644
index 000000000000..54986f075ef5
--- /dev/null
+++ b/arch/arm64/include/uapi/asm/gunyah.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _UAPI_ASM_GUNYAH
+#define _UAPI_ASM_GUNYAH
+
+#define GH_REG_GENERAL 0x10000
+#define GH_REG_X_MASK 0x1f
+#define GH_REG_SPECIAL 0x20000
+
+#define GH_REG_X(n) (GH_REG_GENERAL | (n & GH_REG_X_MASK))
+#define GH_REG_PC (GH_REG_SPECIAL)
+#define GH_REG_SP_EL1 (GH_REG_SPECIAL + 1)
+
+#endif
diff --git a/drivers/virt/gunyah/vm_mgr.c b/drivers/virt/gunyah/vm_mgr.c
index a993e81a20e2..3dae2fd9e412 100644
--- a/drivers/virt/gunyah/vm_mgr.c
+++ b/drivers/virt/gunyah/vm_mgr.c
@@ -9,6 +9,7 @@
#include <linux/file.h>
#include <linux/gunyah_rsc_mgr.h>
#include <linux/miscdevice.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <uapi/linux/gunyah.h>
@@ -32,10 +33,79 @@ static __must_check struct gunyah_vm *gunyah_vm_alloc(void)
mutex_init(&ghvm->mm_lock);
INIT_LIST_HEAD(&ghvm->memory_mappings);
+ init_rwsem(&ghvm->status_lock);
return ghvm;
}
+static int gh_vm_start(struct gunyah_vm *ghvm)
+{
+ struct gunyah_vm_memory_mapping *mapping;
+ u64 dtb_offset;
+ u32 mem_handle;
+ int ret;
+
+ down_write(&ghvm->status_lock);
+ if (ghvm->vm_status != GH_RM_VM_STATUS_NO_STATE) {
+ up_write(&ghvm->status_lock);
+ return 0;
+ }
+
+ mapping = gh_vm_mem_mapping_find_mapping(ghvm,
+ ghvm->dtb_config.gpa, ghvm->dtb_config.size);
+ if (!mapping) {
+ pr_warn("Failed to find the memory_handle for DTB\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mem_handle = mapping->parcel.mem_handle;
+ dtb_offset = ghvm->dtb_config.gpa - mapping->guest_phys_addr;
+
+ ret = gh_rm_vm_configure(ghvm->vmid, ghvm->auth, mem_handle,
+ 0, 0, dtb_offset, ghvm->dtb_config.size);
+ if (ret) {
+ pr_warn("Failed to configure VM: %d\n", ret);
+ goto err;
+ }
+
+ ret = gh_rm_vm_init(ghvm->vmid);
+ if (ret) {
+ pr_warn("Failed to initialize VM: %d\n", ret);
+ goto err;
+ }
+
+ ret = gh_rm_vm_start(ghvm->vmid);
+ if (ret) {
+ pr_warn("Failed to start VM: %d\n", ret);
+ goto err;
+ }
+
+ ghvm->vm_status = GH_RM_VM_STATUS_READY;
+
+ up_write(&ghvm->status_lock);
+ return ret;
+err:
+ ghvm->vm_status = GH_RM_VM_STATUS_INIT_FAILED;
+ up_write(&ghvm->status_lock);
+ return ret;
+}
+
+static void gh_vm_stop(struct gunyah_vm *ghvm)
+{
+ int ret;
+
+ down_write(&ghvm->status_lock);
+ if (ghvm->vm_status == GH_RM_VM_STATUS_READY) {
+ ret = gh_rm_vm_stop(ghvm->vmid);
+ if (ret)
+ pr_warn("Failed to stop VM: %d\n", ret);
+ }
+
+ ghvm->vm_status = GH_RM_VM_STATUS_EXITED;
+ up_write(&ghvm->status_lock);
+}
+
static long gh_vm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct gunyah_vm *ghvm = filp->private_data;
@@ -79,6 +149,25 @@ static long gh_vm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
break;
}
+ case GH_VM_SET_DTB_CONFIG: {
+ struct gh_vm_dtb_config dtb_config;
+
+ r = -EFAULT;
+ if (copy_from_user(&dtb_config, argp, sizeof(dtb_config)))
+ break;
+
+ dtb_config.size = PAGE_ALIGN(dtb_config.size);
+ ghvm->dtb_config = dtb_config;
+
+ r = 0;
+ break;
+ }
+ case GH_VM_START: {
+ r = gh_vm_start(ghvm);
+ if (r)
+ r = -EINVAL;
+ break;
+ }
default:
r = -ENOTTY;
break;
@@ -92,6 +181,8 @@ static int gh_vm_release(struct inode *inode, struct file *filp)
struct gunyah_vm *ghvm = filp->private_data;
struct gunyah_vm_memory_mapping *mapping, *tmp;
+ gh_vm_stop(ghvm);
+
list_for_each_entry_safe(mapping, tmp, &ghvm->memory_mappings, list) {
gh_vm_mem_mapping_reclaim(ghvm, mapping);
}
diff --git a/drivers/virt/gunyah/vm_mgr.h b/drivers/virt/gunyah/vm_mgr.h
index ab1f0cb0758a..38ce3a2d9329 100644
--- a/drivers/virt/gunyah/vm_mgr.h
+++ b/drivers/virt/gunyah/vm_mgr.h
@@ -9,6 +9,7 @@
#include <linux/gunyah_rsc_mgr.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/rwsem.h>
#include <uapi/linux/gunyah.h>
@@ -31,6 +32,12 @@ struct gunyah_vm_memory_mapping {
struct gunyah_vm {
u16 vmid;
+ enum gh_rm_vm_auth_mechanism auth;
+ struct gh_vm_dtb_config dtb_config;
+
+ enum gh_rm_vm_status vm_status;
+ struct rw_semaphore status_lock;
+
struct mutex mm_lock;
struct list_head memory_mappings;
};
@@ -39,5 +46,7 @@ struct gunyah_vm_memory_mapping *gh_vm_mem_mapping_alloc(struct gunyah_vm *ghvm,
struct gh_userspace_memory_region *region);
void gh_vm_mem_mapping_reclaim(struct gunyah_vm *ghvm, struct gunyah_vm_memory_mapping *mapping);
struct gunyah_vm_memory_mapping *gh_vm_mem_mapping_find(struct gunyah_vm *ghvm, u32 label);
+struct gunyah_vm_memory_mapping *gh_vm_mem_mapping_find_mapping(struct gunyah_vm *ghvm,
+ u64 gpa, u32 size);
#endif
diff --git a/drivers/virt/gunyah/vm_mgr_mm.c b/drivers/virt/gunyah/vm_mgr_mm.c
index a5a57a38ee09..a7ca9a2e1627 100644
--- a/drivers/virt/gunyah/vm_mgr_mm.c
+++ b/drivers/virt/gunyah/vm_mgr_mm.c
@@ -34,11 +34,6 @@ void gh_vm_mem_mapping_reclaim(struct gunyah_vm *ghvm, struct gunyah_vm_memory_m
int i, ret = 0;
if (mapping->parcel.mem_handle != GH_MEM_HANDLE_INVAL) {
- down_read(&ghvm->status_lock);
- if (mapping->parcel.mem_handle == ghvm->primary_mem_handle &&
- ghvm->vm_status == GH_RM_VM_STATUS_NO_STATE)
- ghvm->primary_mem_handle = 0;
- up_read(&ghvm->status_lock);
ret = gh_rm_mem_reclaim(&mapping->parcel);
if (ret)
pr_warn("Failed to reclaim memory parcel for label %d: %d\n",
@@ -58,6 +53,30 @@ void gh_vm_mem_mapping_reclaim(struct gunyah_vm *ghvm, struct gunyah_vm_memory_m
mutex_unlock(&ghvm->mm_lock);
}
+struct gunyah_vm_memory_mapping *gh_vm_mem_mapping_find_mapping(struct gunyah_vm *ghvm,
+ u64 gpa, u32 size)
+{
+ struct gunyah_vm_memory_mapping *mapping = NULL;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ghvm->mm_lock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ list_for_each_entry(mapping, &ghvm->memory_mappings, list) {
+ if (gpa >= mapping->guest_phys_addr &&
+ (gpa + size <= mapping->guest_phys_addr +
+ (mapping->npages << PAGE_SHIFT))) {
+ goto unlock;
+ }
+ }
+
+ mapping = NULL;
+unlock:
+ mutex_unlock(&ghvm->mm_lock);
+ return mapping;
+}
+
struct gunyah_vm_memory_mapping *gh_vm_mem_mapping_find(struct gunyah_vm *ghvm, u32 label)
{
struct gunyah_vm_memory_mapping *mapping;
diff --git a/include/uapi/linux/gunyah.h b/include/uapi/linux/gunyah.h
index 93f4b99fcaf6..575cb1cbd215 100644
--- a/include/uapi/linux/gunyah.h
+++ b/include/uapi/linux/gunyah.h
@@ -42,4 +42,12 @@ struct gh_userspace_memory_region {
#define GH_VM_SET_USER_MEM_REGION _IOW(GH_IOCTL_TYPE, 0x41, \
struct gh_userspace_memory_region)
+struct gh_vm_dtb_config {
+ __u64 gpa;
+ __u64 size;
+};
+#define GH_VM_SET_DTB_CONFIG _IOW(GH_IOCTL_TYPE, 0x42, struct gh_vm_dtb_config)
+
+#define GH_VM_START _IO(GH_IOCTL_TYPE, 0x45)
+
#endif
--
2.25.1
The resource manager is a special virtual machine which is always
running on a Gunyah system. It provides APIs for creating and destroying
VMs, secure memory management, sharing/lending of memory between VMs,
and setup of inter-VM communication. Calls to the resource manager are
made via message queues.
This patch implements the basic probing and RPC mechanism to make those
API calls. Request/response calls can be made with gh_rm_call.
Drivers can also register to notifications pushed by RM via
gh_rm_register_notifier
Specific API calls that resource manager supports will be implemented in
subsequent patches.
Signed-off-by: Elliot Berman <[email protected]>
---
MAINTAINERS | 2 +-
drivers/virt/gunyah/Kconfig | 15 +
drivers/virt/gunyah/Makefile | 3 +
drivers/virt/gunyah/rsc_mgr.c | 602 +++++++++++++++++++++++++++++++++
drivers/virt/gunyah/rsc_mgr.h | 34 ++
include/linux/gunyah_rsc_mgr.h | 26 ++
6 files changed, 681 insertions(+), 1 deletion(-)
create mode 100644 drivers/virt/gunyah/rsc_mgr.c
create mode 100644 drivers/virt/gunyah/rsc_mgr.h
create mode 100644 include/linux/gunyah_rsc_mgr.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 586539eadd3b..e072a0d2e553 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8945,7 +8945,7 @@ F: Documentation/virt/gunyah/
F: arch/arm64/gunyah/
F: drivers/mailbox/gunyah-msgq.c
F: drivers/virt/gunyah/
-F: include/linux/gunyah.h
+F: include/linux/gunyah*.h
HABANALABS PCI DRIVER
M: Oded Gabbay <[email protected]>
diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
index 127156a678a6..4de88d80aa7b 100644
--- a/drivers/virt/gunyah/Kconfig
+++ b/drivers/virt/gunyah/Kconfig
@@ -10,3 +10,18 @@ config GUNYAH
Say Y/M here to enable the drivers needed to interact in a Gunyah
virtual environment.
+
+config GUNYAH_RESORUCE_MANAGER
+ tristate "Gunyah Resource Manager"
+ select MAILBOX
+ select GUNYAH_MESSAGE_QUEUES
+ depends on GUNYAH
+ default y
+ help
+ The resource manager (RM) is a privileged application VM supporting
+ the Gunyah Hypervisor. Enable this driver to support communicating
+ with Gunyah RM. This is typically required for a VM running under
+ Gunyah wanting to have Gunyah-awareness.
+
+ Say Y/M here if unsure.
+
diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile
index 2ac4ee64b89d..2c18b0a56413 100644
--- a/drivers/virt/gunyah/Makefile
+++ b/drivers/virt/gunyah/Makefile
@@ -1 +1,4 @@
obj-$(CONFIG_GUNYAH) += gunyah.o
+
+gunyah_rsc_mgr-y += rsc_mgr.o
+obj-$(CONFIG_GUNYAH_RESORUCE_MANAGER) += gunyah_rsc_mgr.o
diff --git a/drivers/virt/gunyah/rsc_mgr.c b/drivers/virt/gunyah/rsc_mgr.c
new file mode 100644
index 000000000000..a9fde703cbbe
--- /dev/null
+++ b/drivers/virt/gunyah/rsc_mgr.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "gh_rsc_mgr: " fmt
+
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/gunyah.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/kthread.h>
+#include <linux/notifier.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/gunyah_rsc_mgr.h>
+#include <linux/platform_device.h>
+
+#include "rsc_mgr.h"
+
+/* Resource Manager Header */
+struct gh_rm_rpc_hdr {
+ u8 version : 4, hdr_words : 4;
+ u8 type : 2, fragments : 6;
+ u16 seq;
+ u32 msg_id;
+} __packed;
+
+/* Standard reply header */
+struct gh_rm_rpc_reply_hdr {
+ struct gh_rm_rpc_hdr rpc_hdr;
+ u32 err_code;
+} __packed;
+
+/* RPC Header versions */
+#define GH_RM_RPC_HDR_VERSION_ONE 0x1
+
+/* RPC Header words */
+#define GH_RM_RPC_HDR_WORDS 0x2
+
+/* RPC Message types */
+#define GH_RM_RPC_TYPE_CONT 0x0
+#define GH_RM_RPC_TYPE_REQ 0x1
+#define GH_RM_RPC_TYPE_RPLY 0x2
+#define GH_RM_RPC_TYPE_NOTIF 0x3
+
+#define GH_RM_MAX_NUM_FRAGMENTS 62
+
+#define GH_RM_MAX_MSG_SIZE (GH_MSGQ_MAX_MSG_SIZE - sizeof(struct gh_rm_rpc_hdr))
+
+/**
+ * struct gh_rm_connection - Represents a complete message from resource manager
+ * @payload: Combined payload of all the fragments (msg headers stripped off).
+ * @size: Size of the payload.
+ * @ret: Linux return code, set in case there was an error processing connection
+ * @msg_id: Message ID from the header.
+ * @type: GH_RM_RPC_TYPE_RPLY or GH_RM_RPC_TYPE_NOTIF.
+ * @num_fragments: total number of fragments expected to be received.
+ * @fragments_received: fragments received so far.
+ * @rm_error: For request/reply sequences with standard replies.
+ * @seq: Sequence ID for the main message.
+ * @seq_done: Signals caller that the RM reply has been received
+ */
+struct gh_rm_connection {
+ void *payload;
+ size_t size;
+ int ret;
+ u32 msg_id;
+ u8 type;
+
+ u8 num_fragments;
+ u8 fragments_received;
+
+ /* only for req/reply sequence */
+ u32 rm_error;
+ u16 seq;
+ struct completion seq_done;
+};
+
+struct gh_rm_notif_complete {
+ struct gh_rm_connection *conn;
+ struct work_struct work;
+};
+
+struct gh_rsc_mgr {
+ struct gunyah_resource tx_ghrsc, rx_ghrsc;
+ struct gh_msgq msgq;
+ struct mbox_client msgq_client;
+ struct gh_rm_connection *active_rx_connection;
+ int last_tx_ret;
+
+ struct idr call_idr;
+ struct mutex call_idr_lock;
+
+ struct mutex send_lock;
+
+ struct work_struct recv_work;
+};
+
+static struct gh_rsc_mgr *__rsc_mgr;
+SRCU_NOTIFIER_HEAD_STATIC(gh_rm_notifier);
+
+static struct gh_rm_connection *gh_rm_alloc_connection(u32 msg_id, u8 type)
+{
+ struct gh_rm_connection *connection;
+
+ connection = kzalloc(sizeof(*connection), GFP_KERNEL);
+ if (!connection)
+ return NULL;
+
+ connection->type = type;
+ connection->msg_id = msg_id;
+
+ return connection;
+}
+
+static int gh_rm_init_connection_payload(struct gh_rm_connection *connection, void *msg,
+ size_t hdr_size, size_t msg_size)
+{
+ struct gh_rm_rpc_hdr *hdr = msg;
+ size_t max_buf_size, payload_size;
+
+ if (hdr_size > msg_size)
+ return -EINVAL;
+
+ payload_size = msg_size - hdr_size;
+
+ connection->num_fragments = hdr->fragments;
+ connection->fragments_received = 0;
+ connection->type = hdr->type;
+
+ /* There's not going to be any payload, no need to allocate buffer. */
+ if (!payload_size && !connection->num_fragments)
+ return 0;
+
+ /*
+ * maximum payload size is GH_MSGQ_MAX_MSG_SIZE - hdr_size
+ * and can received (hdr->fragments + 1) of those
+ */
+ max_buf_size = (GH_MSGQ_MAX_MSG_SIZE - hdr_size) * (hdr->fragments + 1);
+
+ connection->payload = kzalloc(max_buf_size, GFP_KERNEL);
+ if (!connection->payload)
+ return -ENOMEM;
+
+ memcpy(connection->payload, msg + hdr_size, payload_size);
+ connection->size = payload_size;
+ return 0;
+}
+
+static void gh_rm_notif_work(struct work_struct *work)
+{
+ struct gh_rm_notif_complete *notif = container_of(work, struct gh_rm_notif_complete, work);
+ struct gh_rm_connection *connection = notif->conn;
+ u32 notif_id = connection->msg_id;
+ struct gh_rm_notification notification = {
+ .buff = connection->payload,
+ .size = connection->size,
+ };
+
+ srcu_notifier_call_chain(&gh_rm_notifier, notif_id, ¬ification);
+
+ kfree(connection->payload);
+ kfree(connection);
+ kfree(notif);
+}
+
+static struct gh_rm_connection *gh_rm_process_notif(struct gh_rsc_mgr *rsc_mgr,
+ void *msg, size_t msg_size)
+{
+ struct gh_rm_rpc_hdr *hdr = msg;
+ struct gh_rm_connection *connection;
+
+ connection = gh_rm_alloc_connection(hdr->msg_id, hdr->type);
+ if (!connection) {
+ pr_err("Failed to alloc connection for notification, dropping.\n");
+ return NULL;
+ }
+
+ if (gh_rm_init_connection_payload(connection, msg, sizeof(*hdr), msg_size)) {
+ pr_err("Failed to alloc connection buffer for notification, dropping.\n");
+ kfree(connection);
+ return NULL;
+ }
+
+ return connection;
+}
+
+static struct gh_rm_connection *gh_rm_process_rply(struct gh_rsc_mgr *rsc_mgr,
+ void *msg, size_t msg_size)
+{
+ struct gh_rm_rpc_reply_hdr *reply_hdr = msg;
+ struct gh_rm_rpc_hdr *hdr = msg;
+ struct gh_rm_connection *connection;
+
+ if (mutex_lock_interruptible(&rsc_mgr->call_idr_lock))
+ return ERR_PTR(-ERESTARTSYS);
+
+ connection = idr_find(&rsc_mgr->call_idr, hdr->seq);
+ mutex_unlock(&rsc_mgr->call_idr_lock);
+
+ if (!connection) {
+ pr_err("Failed to find connection for sequence %u\n", hdr->seq);
+ return NULL;
+ }
+ if (connection->msg_id != hdr->msg_id) {
+ pr_err("Reply for sequence %u expected msg_id: %x but got %x\n", hdr->seq,
+ connection->msg_id, hdr->msg_id);
+ /*
+ * Don't complete connection and error the client, maybe
+ * resource manager will send us the expected reply sequence soon.
+ */
+ return NULL;
+ }
+
+ if (gh_rm_init_connection_payload(connection, msg, sizeof(*reply_hdr), msg_size)) {
+ pr_err("Failed to alloc connection buffer for sequence %d\n", hdr->seq);
+ /* Send connection complete and error the client. */
+ connection->ret = -ENOMEM;
+ complete(&connection->seq_done);
+ return NULL;
+ }
+
+ connection->rm_error = reply_hdr->err_code;
+ return connection;
+}
+
+static void gh_rm_process_cont(struct gh_rm_connection *connection, void *msg, size_t msg_size)
+{
+ struct gh_rm_rpc_hdr *hdr = msg;
+ size_t payload_size = msg_size - sizeof(*hdr);
+
+ /*
+ * hdr->fragments and hdr->msg_id preserves the value from first reply
+ * or notif message. To detect mishandling, check it's still intact.
+ */
+ if (connection->msg_id != hdr->msg_id)
+ pr_warn("Appending mismatched continuation with id %d to connection with id %d\n",
+ hdr->msg_id, connection->msg_id);
+ if (connection->num_fragments != hdr->fragments)
+ pr_warn("Number of fragments mismatch for seq: %d\n", hdr->seq);
+
+ memcpy(connection->payload + connection->size, msg + sizeof(*hdr), payload_size);
+ connection->size += payload_size;
+ connection->fragments_received++;
+}
+
+static bool gh_rm_complete_connection(struct gh_rm_connection *connection)
+{
+ struct gh_rm_notif_complete *notif_work;
+
+ if (!connection)
+ return false;
+
+ if (connection->fragments_received != connection->num_fragments)
+ return false;
+
+ switch (connection->type) {
+ case GH_RM_RPC_TYPE_RPLY:
+ complete(&connection->seq_done);
+ break;
+ case GH_RM_RPC_TYPE_NOTIF:
+ notif_work = kzalloc(sizeof(*notif_work), GFP_KERNEL);
+ if (notif_work == NULL)
+ break;
+
+ notif_work->conn = connection;
+ INIT_WORK(¬if_work->work, gh_rm_notif_work);
+
+ schedule_work(¬if_work->work);
+ break;
+ default:
+ pr_err("Invalid message type (%d) received\n", connection->type);
+ break;
+ }
+
+ return true;
+}
+
+static void gh_rm_abort_connection(struct gh_rm_connection *connection)
+{
+ switch (connection->type) {
+ case GH_RM_RPC_TYPE_RPLY:
+ connection->ret = -EIO;
+ complete(&connection->seq_done);
+ break;
+ case GH_RM_RPC_TYPE_NOTIF:
+ fallthrough;
+ default:
+ kfree(connection->payload);
+ kfree(connection);
+ }
+}
+
+static void gh_rm_msgq_rx_data(struct mbox_client *cl, void *mssg)
+{
+ struct gh_rsc_mgr *rsc_mgr = container_of(cl, struct gh_rsc_mgr, msgq_client);
+ struct gh_msgq_rx_data *rx_data = mssg;
+ void *msg = rx_data->data;
+ size_t msg_size = rx_data->length;
+ struct gh_rm_rpc_hdr *hdr;
+
+ if (msg_size <= sizeof(struct gh_rm_rpc_hdr)) {
+ pr_err("Invalid message size received: %ld is too small\n", msg_size);
+ return;
+ }
+
+ hdr = msg;
+ switch (hdr->type) {
+ case GH_RM_RPC_TYPE_NOTIF:
+ if (rsc_mgr->active_rx_connection) {
+ /* Not possible per protocol. Do something better than BUG_ON */
+ pr_warn("Received start of new notification without finishing existing message series.\n");
+ gh_rm_abort_connection(rsc_mgr->active_rx_connection);
+ }
+ rsc_mgr->active_rx_connection = gh_rm_process_notif(rsc_mgr, msg, msg_size);
+ break;
+ case GH_RM_RPC_TYPE_RPLY:
+ if (rsc_mgr->active_rx_connection) {
+ /* Not possible per protocol. Do something better than BUG_ON */
+ pr_warn("Received start of new reply without finishing existing message series.\n");
+ gh_rm_abort_connection(rsc_mgr->active_rx_connection);
+ }
+ rsc_mgr->active_rx_connection = gh_rm_process_rply(rsc_mgr, msg, msg_size);
+ break;
+ case GH_RM_RPC_TYPE_CONT:
+ if (!rsc_mgr->active_rx_connection) {
+ pr_warn("Received a continuation message without receiving initial message\n");
+ break;
+ }
+ gh_rm_process_cont(rsc_mgr->active_rx_connection, msg, msg_size);
+ break;
+ default:
+ pr_err("Invalid message type (%d) received\n", hdr->type);
+ return;
+ }
+
+ if (gh_rm_complete_connection(rsc_mgr->active_rx_connection))
+ rsc_mgr->active_rx_connection = NULL;
+}
+
+static void gh_rm_msgq_tx_done(struct mbox_client *cl, void *mssg, int r)
+{
+ struct gh_rsc_mgr *rsc_mgr = container_of(cl, struct gh_rsc_mgr, msgq_client);
+
+ kfree(mssg);
+ rsc_mgr->last_tx_ret = r;
+}
+
+static int gh_rm_send_request(struct gh_rsc_mgr *rsc_mgr, u32 message_id,
+ const void *req_buff, size_t req_buff_size,
+ struct gh_rm_connection *connection)
+{
+ size_t buff_size_remaining = req_buff_size;
+ const void *req_buff_curr = req_buff;
+ struct gh_rm_rpc_hdr *hdr;
+ u32 cont_fragments = req_buff_size / GH_RM_MAX_MSG_SIZE;
+ size_t payload_size;
+ struct gh_msgq_tx_data *msg;
+ int i, ret;
+
+ if (WARN(cont_fragments > GH_RM_MAX_NUM_FRAGMENTS,
+ "Limit exceeded for the number of fragments: %u\n", cont_fragments))
+ return -E2BIG;
+
+ ret = mutex_lock_interruptible(&rsc_mgr->send_lock);
+ if (ret)
+ return ret;
+
+ /* Consider also the 'request' packet for the loop count */
+ for (i = 0; i <= cont_fragments; i++) {
+ if (buff_size_remaining > GH_RM_MAX_MSG_SIZE) {
+ payload_size = GH_RM_MAX_MSG_SIZE;
+ buff_size_remaining -= payload_size;
+ } else {
+ payload_size = buff_size_remaining;
+ }
+
+ msg = kzalloc(sizeof(*msg) + GH_MSGQ_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Fill header */
+ hdr = (struct gh_rm_rpc_hdr *)msg->data;
+ hdr->version = GH_RM_RPC_HDR_VERSION_ONE;
+ hdr->hdr_words = GH_RM_RPC_HDR_WORDS;
+ hdr->type = i == 0 ? GH_RM_RPC_TYPE_REQ : GH_RM_RPC_TYPE_CONT;
+ hdr->fragments = cont_fragments;
+ hdr->seq = connection->seq;
+ hdr->msg_id = message_id;
+
+ /* Copy payload */
+ memcpy(msg->data + sizeof(*hdr), req_buff_curr, payload_size);
+ req_buff_curr += payload_size;
+
+ /* Force the last fragment to immediately alert the receiver */
+ msg->push = i == cont_fragments;
+ msg->length = sizeof(*hdr) + payload_size;
+
+ ret = mbox_send_message(gh_msgq_chan(&rsc_mgr->msgq), msg);
+ if (ret < 0) {
+ kfree(msg);
+ break;
+ }
+
+ if (rsc_mgr->last_tx_ret) {
+ ret = rsc_mgr->last_tx_ret;
+ break;
+ }
+ }
+
+out:
+ mutex_unlock(&rsc_mgr->send_lock);
+ return ret < 0 ? ret : 0;
+}
+
+/**
+ * gh_rm_call: Achieve request-response type communication with RPC
+ * @message_id: The RM RPC message-id
+ * @req_buff: Request buffer that contains the payload
+ * @req_buff_size: Total size of the payload
+ * @resp_buf: Pointer to a response buffer
+ * @resp_buff_size: Size of the response buffer
+ *
+ * Make a request to the RM-VM and wait for reply back. For a successful
+ * response, the function returns the payload. The size of the payload is set in
+ * resp_buff_size. The resp_buf should be freed by the caller.
+ *
+ * Context: Process context. Will sleep waiting for reply.
+ * Return: >0 is standard reply error from RM. <0 on internal error.
+ */
+int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
+ void **resp_buf, size_t *resp_buff_size)
+{
+ struct gh_rm_connection *connection;
+ int ret;
+ struct gh_rsc_mgr *rsc_mgr = __rsc_mgr;
+
+ /* messaged_id 0 is reserved */
+ if (!message_id)
+ return -EINVAL;
+
+ if (!rsc_mgr)
+ return -EPROBE_DEFER;
+
+ connection = gh_rm_alloc_connection(message_id, GH_RM_RPC_TYPE_RPLY);
+ if (!connection)
+ return -ENOMEM;
+
+ init_completion(&connection->seq_done);
+
+ /* Allocate a new seq number for this connection */
+ if (mutex_lock_interruptible(&rsc_mgr->call_idr_lock)) {
+ kfree(connection);
+ return -ERESTARTSYS;
+ }
+ connection->seq = idr_alloc_cyclic(&rsc_mgr->call_idr, connection, 0, U16_MAX, GFP_KERNEL);
+ mutex_unlock(&rsc_mgr->call_idr_lock);
+
+ /* Send the request to the Resource Manager */
+ ret = gh_rm_send_request(rsc_mgr, message_id, req_buff, req_buff_size, connection);
+ if (ret < 0)
+ goto out;
+
+ /* Wait for response */
+ ret = wait_for_completion_interruptible(&connection->seq_done);
+ if (ret)
+ goto out;
+
+ if (connection->ret) {
+ ret = connection->ret;
+ kfree(connection->payload);
+ goto out;
+ }
+
+ if (connection->rm_error) {
+ ret = connection->rm_error;
+ kfree(connection->payload);
+ goto out;
+ }
+
+ *resp_buf = connection->payload;
+ *resp_buff_size = connection->size;
+
+out:
+ mutex_lock(&rsc_mgr->call_idr_lock);
+ idr_remove(&rsc_mgr->call_idr, connection->seq);
+ mutex_unlock(&rsc_mgr->call_idr_lock);
+
+ kfree(connection);
+ return ret;
+}
+
+int gh_rm_register_notifier(struct notifier_block *nb)
+{
+ return srcu_notifier_chain_register(&gh_rm_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(gh_rm_register_notifier);
+
+int gh_rm_unregister_notifier(struct notifier_block *nb)
+{
+ return srcu_notifier_chain_unregister(&gh_rm_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(gh_rm_unregister_notifier);
+
+static int gh_msgq_platform_probe_direction(struct platform_device *pdev,
+ u8 gh_type, int idx, struct gunyah_resource *ghrsc)
+{
+ int ret;
+ struct device_node *node = pdev->dev.of_node;
+
+ ghrsc->type = gh_type;
+
+ ghrsc->irq = platform_get_irq(pdev, idx);
+ if (ghrsc->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq%d: %d\n", idx, ghrsc->irq);
+ return ghrsc->irq;
+ }
+
+ ret = of_property_read_u64_index(node, "reg", idx, &ghrsc->capid);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to get capid%d: %d\n", idx, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gh_rm_drv_probe(struct platform_device *pdev)
+{
+ struct gh_rsc_mgr *rsc_mgr;
+ int ret;
+
+ rsc_mgr = devm_kzalloc(&pdev->dev, sizeof(*rsc_mgr), GFP_KERNEL);
+ if (!rsc_mgr)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, rsc_mgr);
+
+ mutex_init(&rsc_mgr->call_idr_lock);
+ idr_init(&rsc_mgr->call_idr);
+ mutex_init(&rsc_mgr->send_lock);
+
+ ret = gh_msgq_platform_probe_direction(pdev, GUNYAH_RESOURCE_TYPE_MSGQ_TX, 0,
+ &rsc_mgr->tx_ghrsc);
+ if (ret)
+ return ret;
+
+ ret = gh_msgq_platform_probe_direction(pdev, GUNYAH_RESOURCE_TYPE_MSGQ_RX, 1,
+ &rsc_mgr->rx_ghrsc);
+ if (ret)
+ return ret;
+
+ rsc_mgr->msgq_client.dev = &pdev->dev;
+ rsc_mgr->msgq_client.tx_block = true;
+ rsc_mgr->msgq_client.rx_callback = gh_rm_msgq_rx_data;
+ rsc_mgr->msgq_client.tx_done = gh_rm_msgq_tx_done;
+
+ ret = gh_msgq_init(&pdev->dev, &rsc_mgr->msgq, &rsc_mgr->msgq_client,
+ &rsc_mgr->tx_ghrsc, &rsc_mgr->rx_ghrsc);
+ if (ret)
+ return ret;
+
+ __rsc_mgr = rsc_mgr;
+
+ return 0;
+}
+
+static int gh_rm_drv_remove(struct platform_device *pdev)
+{
+ struct gh_rsc_mgr *rsc_mgr = platform_get_drvdata(pdev);
+
+ __rsc_mgr = NULL;
+
+ mbox_free_channel(gunyah_msgq_chan(&rsc_mgr->msgq));
+ gunyah_msgq_remove(&rsc_mgr->msgq);
+
+ return 0;
+}
+
+static const struct of_device_id gh_rm_of_match[] = {
+ { .compatible = "gunyah-resource-manager" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gh_rm_of_match);
+
+static struct platform_driver gh_rm_driver = {
+ .probe = gh_rm_drv_probe,
+ .remove = gh_rm_drv_remove,
+ .driver = {
+ .name = "gh_rsc_mgr",
+ .of_match_table = gh_rm_of_match,
+ },
+};
+module_platform_driver(gh_rsc_mgr_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Gunyah Resource Manager Driver");
diff --git a/drivers/virt/gunyah/rsc_mgr.h b/drivers/virt/gunyah/rsc_mgr.h
new file mode 100644
index 000000000000..e4f2499267bf
--- /dev/null
+++ b/drivers/virt/gunyah/rsc_mgr.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef __GH_RSC_MGR_PRIV_H
+#define __GH_RSC_MGR_PRIV_H
+
+#include <linux/gunyah.h>
+
+/* RM Error codes */
+#define GH_RM_ERROR_OK 0x0
+#define GH_RM_ERROR_UNIMPLEMENTED 0xFFFFFFFF
+#define GH_RM_ERROR_NOMEM 0x1
+#define GH_RM_ERROR_NORESOURCE 0x2
+#define GH_RM_ERROR_DENIED 0x3
+#define GH_RM_ERROR_INVALID 0x4
+#define GH_RM_ERROR_BUSY 0x5
+#define GH_RM_ERROR_ARGUMENT_INVALID 0x6
+#define GH_RM_ERROR_HANDLE_INVALID 0x7
+#define GH_RM_ERROR_VALIDATE_FAILED 0x8
+#define GH_RM_ERROR_MAP_FAILED 0x9
+#define GH_RM_ERROR_MEM_INVALID 0xA
+#define GH_RM_ERROR_MEM_INUSE 0xB
+#define GH_RM_ERROR_MEM_RELEASED 0xC
+#define GH_RM_ERROR_VMID_INVALID 0xD
+#define GH_RM_ERROR_LOOKUP_FAILED 0xE
+#define GH_RM_ERROR_IRQ_INVALID 0xF
+#define GH_RM_ERROR_IRQ_INUSE 0x10
+#define GH_RM_ERROR_IRQ_RELEASED 0x11
+
+int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
+ void **resp_buf, size_t *resp_buff_size);
+
+#endif
diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h
new file mode 100644
index 000000000000..b3b37225b7fb
--- /dev/null
+++ b/include/linux/gunyah_rsc_mgr.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _GUNYAH_RSC_MGR_H
+#define _GUNYAH_RSC_MGR_H
+
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/gunyah.h>
+
+#define GH_VMID_INVAL U16_MAX
+
+/* Gunyah recognizes VMID0 as an alias to the current VM's ID */
+#define GH_VMID_SELF 0
+
+struct gh_rm_notification {
+ const void *buff;
+ const size_t size;
+};
+
+int gh_rm_register_notifier(struct notifier_block *nb);
+int gh_rm_unregister_notifier(struct notifier_block *nb);
+
+#endif
--
2.25.1
Add hypercalls to identify when Linux is running a virtual machine under
Gunyah.
There are two calls to help identify Gunyah:
1. gh_hypercall_get_uid() returns a UID when running under a Gunyah
hypervisor.
2. gh_hypercall_hyp_identify() returns build information and a set of
feature flags that are supported by Gunyah.
Signed-off-by: Elliot Berman <[email protected]>
---
MAINTAINERS | 2 +
arch/arm64/Kbuild | 1 +
arch/arm64/gunyah/Makefile | 1 +
arch/arm64/gunyah/gunyah_hypercall.c | 69 ++++++++++++++++++++++++++++
drivers/virt/Kconfig | 1 +
drivers/virt/gunyah/Kconfig | 12 +++++
include/linux/gunyah.h | 25 ++++++++++
7 files changed, 111 insertions(+)
create mode 100644 arch/arm64/gunyah/Makefile
create mode 100644 arch/arm64/gunyah/gunyah_hypercall.c
create mode 100644 drivers/virt/gunyah/Kconfig
diff --git a/MAINTAINERS b/MAINTAINERS
index 5eec8618a087..211977dc344b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8942,6 +8942,8 @@ L: [email protected]
S: Supported
F: Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
F: Documentation/virt/gunyah/
+F: arch/arm64/gunyah/
+F: drivers/virt/gunyah/
F: include/linux/gunyah.h
HABANALABS PCI DRIVER
diff --git a/arch/arm64/Kbuild b/arch/arm64/Kbuild
index 5bfbf7d79c99..e4847ba0e3c9 100644
--- a/arch/arm64/Kbuild
+++ b/arch/arm64/Kbuild
@@ -3,6 +3,7 @@ obj-y += kernel/ mm/ net/
obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_XEN) += xen/
obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/
+obj-$(CONFIG_GUNYAH) += gunyah/
obj-$(CONFIG_CRYPTO) += crypto/
# for cleaning
diff --git a/arch/arm64/gunyah/Makefile b/arch/arm64/gunyah/Makefile
new file mode 100644
index 000000000000..9fbc720b6fb6
--- /dev/null
+++ b/arch/arm64/gunyah/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_GUNYAH) += gunyah_hypercall.o
diff --git a/arch/arm64/gunyah/gunyah_hypercall.c b/arch/arm64/gunyah/gunyah_hypercall.c
new file mode 100644
index 000000000000..0beb3123d650
--- /dev/null
+++ b/arch/arm64/gunyah/gunyah_hypercall.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/module.h>
+#include <linux/gunyah.h>
+
+#define GH_CALL_TYPE_PLATFORM_CALL 0
+#define GH_CALL_TYPE_HYPERCALL 2
+#define GH_CALL_TYPE_SERVICE 3
+#define GH_CALL_TYPE_SHIFT 14
+#define GH_CALL_FUNCTION_NUM_MASK 0x3fff
+
+#define GH_FN_ID(type, num) ((type) << GH_CALL_TYPE_SHIFT | ((num) & GH_CALL_FUNCTION_NUM_MASK))
+
+#define GH_SERVICE(fn) ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_VENDOR_HYP, \
+ GH_FN_ID(GH_CALL_TYPE_SERVICE, fn))
+
+#define GH_HYPERCALL_CALL_UID GH_SERVICE(0x3f01)
+
+#define GH_HYPERCALL(fn) ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_VENDOR_HYP, \
+ GH_FN_ID(GH_CALL_TYPE_HYPERCALL, fn))
+
+#define GH_HYPERCALL_HYP_IDENTIFY GH_HYPERCALL(0x0000)
+
+/**
+ * gh_hypercall_get_uid() - Returns a UID when running under a Gunyah hypervisor
+ * @uid: An array of 4 u32's (u32 uid[4];)
+ *
+ * Caller should compare the resulting UID to a list of known Gunyah UIDs to
+ * confirm that Linux is running as a guest of Gunyah.
+ */
+void gh_hypercall_get_uid(u32 uid[4])
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_hvc(GH_HYPERCALL_CALL_UID, &res);
+
+ uid[0] = res.a0;
+ uid[1] = res.a1;
+ uid[2] = res.a2;
+ uid[3] = res.a3;
+}
+EXPORT_SYMBOL_GPL(gh_hypercall_get_uid);
+
+/**
+ * gh_hypercall_hyp_identify() - Returns build information and feature flags
+ * supported by Gunyah.
+ * @hyp_identity: filled by the hypercall with the API info and feature flags.
+ */
+void gh_hypercall_hyp_identify(struct gh_hypercall_hyp_identify_resp *hyp_identity)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_hvc(GH_HYPERCALL_HYP_IDENTIFY, &res);
+
+ hyp_identity->api_info = res.a0;
+ hyp_identity->flags[0] = res.a1;
+ hyp_identity->flags[1] = res.a2;
+ hyp_identity->flags[2] = res.a3;
+}
+EXPORT_SYMBOL_GPL(gh_hypercall_hyp_identify);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Gunyah Hypervisor Hypercalls");
diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
index 87ef258cec64..259dc2be6cad 100644
--- a/drivers/virt/Kconfig
+++ b/drivers/virt/Kconfig
@@ -52,4 +52,5 @@ source "drivers/virt/coco/efi_secret/Kconfig"
source "drivers/virt/coco/sev-guest/Kconfig"
+source "drivers/virt/gunyah/Kconfig"
endif
diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
new file mode 100644
index 000000000000..127156a678a6
--- /dev/null
+++ b/drivers/virt/gunyah/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config GUNYAH
+ tristate "Gunyah Virtualization drivers"
+ depends on ARM64
+ help
+ The Gunyah drivers are the helper interfaces that runs in a guest VM
+ such as basic inter-VM IPC and signaling mechanisms, and higher level
+ services such as memory/device sharing, IRQ sharing, and so on.
+
+ Say Y/M here to enable the drivers needed to interact in a Gunyah
+ virtual environment.
diff --git a/include/linux/gunyah.h b/include/linux/gunyah.h
index 824e20a11d27..2765d2b40198 100644
--- a/include/linux/gunyah.h
+++ b/include/linux/gunyah.h
@@ -6,6 +6,7 @@
#ifndef _GUNYAH_H
#define _GUNYAH_H
+#include <linux/bitfield.h>
#include <linux/types.h>
#include <linux/errno.h>
@@ -71,4 +72,28 @@ static inline int gh_remap_error(int gh_error)
}
}
+#define GUNYAH_API_V1 1
+
+#define GH_API_INFO_API_VERSION_MASK GENMASK_ULL(13, 0)
+#define GH_API_INFO_BIG_ENDIAN BIT_ULL(14)
+#define GH_API_INFO_IS_64BIT BIT_ULL(15)
+#define GH_API_INFO_VARIANT_MASK GENMASK_ULL(63, 56)
+
+#define GH_IDENTIFY_PARTITION_CSPACE BIT_ULL(0)
+#define GH_IDENTIFY_DOORBELL BIT_ULL(1)
+#define GH_IDENTIFY_MSGQUEUE BIT_ULL(2)
+#define GH_IDENTIFY_VIC BIT_ULL(3)
+#define GH_IDENTIFY_VPM BIT_ULL(4)
+#define GH_IDENTIFY_VCPU BIT_ULL(5)
+#define GH_IDENTIFY_MEMEXTENT BIT_ULL(6)
+#define GH_IDENTIFY_TRACE_CTRL BIT_ULL(7)
+
+struct gh_hypercall_hyp_identify_resp {
+ u64 api_info;
+ u64 flags[3];
+};
+
+void gh_hypercall_get_uid(u32 uid[4]);
+void gh_hypercall_hyp_identify(struct gh_hypercall_hyp_identify_resp *hyp_identity);
+
#endif
--
2.25.1
When Linux is booted as a guest under the Gunyah hypervisor, the Gunyah
Resource Manager applies a devicetree overlay describing the virtual
platform configuration of the guest VM, such as the message queue
capability IDs for communicating with the Resource Manager. This
information is not otherwise discoverable by a VM: the Gunyah hypervisor
core does not provide a direct interface to discover capability IDs nor
a way to communicate with RM without having already known the
corresponding message queue capability ID. Add the DT bindings that
Gunyah adheres for the hypervisor node and message queues.
Signed-off-by: Elliot Berman <[email protected]>
---
.../bindings/firmware/gunyah-hypervisor.yaml | 86 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 87 insertions(+)
create mode 100644 Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
diff --git a/Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml b/Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
new file mode 100644
index 000000000000..3a8c1c2157a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/firmware/gunyah-hypervisor.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Gunyah Hypervisor
+
+maintainers:
+ - Murali Nalajala <[email protected]>
+ - Elliot Berman <[email protected]>
+
+description: |+
+ Gunyah virtual machines use this information to determine the capability IDs
+ of the message queues used to communicate with the Gunyah Resource Manager.
+ See also: https://github.com/quic/gunyah-resource-manager/blob/develop/src/vm_creation/dto_construct.c
+
+properties:
+ compatible:
+ items:
+ - const: gunyah-hypervisor-1.0
+ - const: gunyah-hypervisor
+
+ "#address-cells":
+ description: Number of cells needed to represent 64-bit capability IDs.
+ const: 2
+
+ "#size-cells":
+ description: must be 0, because capability IDs are not memory address
+ ranges and do not have a size.
+ const: 0
+
+patternProperties:
+ "^gunyah-resource-mgr(@.*)?":
+ type: object
+ description:
+ Resource Manager node which is required to communicate to Resource
+ Manager VM using Gunyah Message Queues.
+
+ properties:
+ compatible:
+ items:
+ - const: gunyah-resource-manager-1-0
+ - const: gunyah-resource-manager
+
+ reg:
+ items:
+ - description: Gunyah capability ID of the TX message queue
+ - description: Gunyah capability ID of the RX message queue
+
+ interrupts:
+ items:
+ - description: Interrupt for the TX message queue
+ - description: Interrupt for the RX message queue
+
+ additionalProperties: false
+
+ required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+required:
+ - compatible
+ - "#address-cells"
+ - "#size-cells"
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ hypervisor {
+ #address-cells = <2>;
+ #size-cells = <0>;
+ compatible = "gunyah-hypervisor-1.0", "gunyah-hypervisor";
+
+ gunyah-resource-mgr@0 {
+ compatible = "gunyah-resource-manager-1-0", "gunyah-resource-manager";
+ interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX full IRQ */
+ <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX empty IRQ */
+ reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
+ /* TX, RX cap ids */
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 9479cb3054cb..1de8d00dacb2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8940,6 +8940,7 @@ M: Elliot Berman <[email protected]>
M: Murali Nalajala <[email protected]>
L: [email protected]
S: Supported
+F: Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
F: Documentation/virt/gunyah/
HABANALABS PCI DRIVER
--
2.25.1
On 26/10/2022 21:58, Elliot Berman wrote:
> Fix build error when CONFIG_ARM64_SVE is selected and
> asm/alternative-macros.h wasn't implicitly included by another header.
Please include the build error into the commit message to help anybody
looking for the solution for the same issue.
>
> Signed-off-by: Elliot Berman <[email protected]>
> ---
> include/linux/arm-smccc.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
> index 220c8c60e021..6a627cdbbdec 100644
> --- a/include/linux/arm-smccc.h
> +++ b/include/linux/arm-smccc.h
> @@ -383,6 +383,7 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
>
> /* nVHE hypervisor doesn't have a current thread so needs separate checks */
> #if defined(CONFIG_ARM64_SVE) && !defined(__KVM_NVHE_HYPERVISOR__)
> +#include <asm/alternative-macros.h>
>
> #define SMCCC_SVE_CHECK ALTERNATIVE("nop \n", "bl __arm_smccc_sve_check \n", \
> ARM64_SVE)
--
With best wishes
Dmitry
On 10/26/2022 12:46 PM, Dmitry Baryshkov wrote:
> On 26/10/2022 21:58, Elliot Berman wrote:
>> Fix build error when CONFIG_ARM64_SVE is selected and
>> asm/alternative-macros.h wasn't implicitly included by another header.
>
> Please include the build error into the commit message to help anybody
> looking for the solution for the same issue.
>
Now that the gunyah_hypercall implementation has been moved to its own
module, this change isn't needed because asm/alternative-macros.h got
implicitly included now. I can drop this, although not sure if we think
it's still correct to have it?
After I got rid of the other header files, for reference:
In file included from arch/arm64/gunyah/gunyah_hypercall.c:6:
arch/arm64/gunyah/gunyah_hypercall.c: In function ‘gh_hypercall_msgq_send’:
./include/linux/arm-smccc.h:387:25: error: expected string literal
before ‘ALTERNATIVE’
387 | #define SMCCC_SVE_CHECK ALTERNATIVE("nop \n", "bl
__arm_smccc_sve_check \n", \
>>
>> Signed-off-by: Elliot Berman <[email protected]>
>> ---
>> Â include/linux/arm-smccc.h | 1 +
>> Â 1 file changed, 1 insertion(+)
>>
>> diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
>> index 220c8c60e021..6a627cdbbdec 100644
>> --- a/include/linux/arm-smccc.h
>> +++ b/include/linux/arm-smccc.h
>> @@ -383,6 +383,7 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0,
>> unsigned long a1,
>> Â /* nVHE hypervisor doesn't have a current thread so needs separate
>> checks */
>> Â #if defined(CONFIG_ARM64_SVE) && !defined(__KVM_NVHE_HYPERVISOR__)
>> +#include <asm/alternative-macros.h>
>>  #define SMCCC_SVE_CHECK ALTERNATIVE("nop \n", "bl
>> __arm_smccc_sve_check \n", \
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ARM64_SVE)
>
On Wed, 26 Oct 2022 at 23:24, Elliot Berman <[email protected]> wrote:
>
>
> On 10/26/2022 12:46 PM, Dmitry Baryshkov wrote:
> > On 26/10/2022 21:58, Elliot Berman wrote:
> >> Fix build error when CONFIG_ARM64_SVE is selected and
> >> asm/alternative-macros.h wasn't implicitly included by another header.
> >
> > Please include the build error into the commit message to help anybody
> > looking for the solution for the same issue.
> >
>
> Now that the gunyah_hypercall implementation has been moved to its own
> module, this change isn't needed because asm/alternative-macros.h got
> implicitly included now. I can drop this, although not sure if we think
> it's still correct to have it?
>
> After I got rid of the other header files, for reference:
>
> In file included from arch/arm64/gunyah/gunyah_hypercall.c:6:
> arch/arm64/gunyah/gunyah_hypercall.c: In function ‘gh_hypercall_msgq_send’:
> ./include/linux/arm-smccc.h:387:25: error: expected string literal
> before ‘ALTERNATIVE’
> 387 | #define SMCCC_SVE_CHECK ALTERNATIVE("nop \n", "bl
> __arm_smccc_sve_check \n", \
Please add this message to the commit log.
>
> >>
> >> Signed-off-by: Elliot Berman <[email protected]>
> >> ---
> >> include/linux/arm-smccc.h | 1 +
> >> 1 file changed, 1 insertion(+)
> >>
> >> diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
> >> index 220c8c60e021..6a627cdbbdec 100644
> >> --- a/include/linux/arm-smccc.h
> >> +++ b/include/linux/arm-smccc.h
> >> @@ -383,6 +383,7 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0,
> >> unsigned long a1,
> >> /* nVHE hypervisor doesn't have a current thread so needs separate
> >> checks */
> >> #if defined(CONFIG_ARM64_SVE) && !defined(__KVM_NVHE_HYPERVISOR__)
> >> +#include <asm/alternative-macros.h>
> >> #define SMCCC_SVE_CHECK ALTERNATIVE("nop \n", "bl
> >> __arm_smccc_sve_check \n", \
> >> ARM64_SVE)
> >
--
With best wishes
Dmitry
Hi Elliot,
I love your patch! Perhaps something to improve:
[auto build test WARNING on 247f34f7b80357943234f93f247a1ae6b6c3a740]
url: https://github.com/intel-lab-lkp/linux/commits/Elliot-Berman/Drivers-for-gunyah-hypervisor/20221027-030321
base: 247f34f7b80357943234f93f247a1ae6b6c3a740
patch link: https://lore.kernel.org/r/20221026185846.3983888-21-quic_eberman%40quicinc.com
patch subject: [PATCH v6 20/21] firmware: qcom_scm: Register Gunyah platform ops
config: powerpc-allyesconfig
compiler: powerpc-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/1e2d3fe903f1c461c594bd312add58369a7475cd
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Elliot-Berman/Drivers-for-gunyah-hypervisor/20221027-030321
git checkout 1e2d3fe903f1c461c594bd312add58369a7475cd
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/firmware/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All warnings (new ones prefixed by >>):
drivers/firmware/qcom_scm.c: In function 'qcom_scm_gh_rm_pre_mem_share':
>> drivers/firmware/qcom_scm.c:1330:28: warning: left shift count >= width of type [-Wshift-count-overflow]
1330 | src = (1ul << QCOM_SCM_RM_MANAGED_VMID);
| ^~
drivers/firmware/qcom_scm.c:1343:53: warning: left shift count >= width of type [-Wshift-count-overflow]
1343 | src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
| ^~
drivers/firmware/qcom_scm.c: In function 'qcom_scm_gh_rm_post_mem_reclaim':
drivers/firmware/qcom_scm.c:1388:37: warning: left shift count >= width of type [-Wshift-count-overflow]
1388 | src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
| ^~
vim +1330 drivers/firmware/qcom_scm.c
1298
1299 static int qcom_scm_gh_rm_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel)
1300 {
1301 struct qcom_scm_vmperm *new_perms;
1302 u16 this_vmid;
1303 u64 src, src_cpy;
1304 int ret, i, n;
1305
1306 ret = gh_rm_get_vmid(&this_vmid);
1307 if (ret)
1308 return ret;
1309
1310 new_perms = kcalloc(mem_parcel->n_acl_entries, sizeof(*new_perms), GFP_KERNEL);
1311 if (!new_perms)
1312 return -ENOMEM;
1313
1314 for (n = 0; n < mem_parcel->n_acl_entries; n++) {
1315 if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
1316 new_perms[n].vmid = mem_parcel->acl_entries[n].vmid;
1317 else
1318 new_perms[n].vmid = QCOM_SCM_RM_MANAGED_VMID;
1319 if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_X)
1320 new_perms[n].perm |= QCOM_SCM_PERM_EXEC;
1321 if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_W)
1322 new_perms[n].perm |= QCOM_SCM_PERM_WRITE;
1323 if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_R)
1324 new_perms[n].perm |= QCOM_SCM_PERM_READ;
1325 }
1326
1327 if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
1328 src = (1ul << this_vmid);
1329 else
> 1330 src = (1ul << QCOM_SCM_RM_MANAGED_VMID);
1331
1332 for (i = 0; i < mem_parcel->n_mem_entries; i++) {
1333 src_cpy = src;
1334 ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
1335 mem_parcel->mem_entries[i].size,
1336 &src_cpy, new_perms, mem_parcel->n_acl_entries);
1337 if (ret) {
1338 src = 0;
1339 for (n = 0; n < mem_parcel->n_acl_entries; n++) {
1340 if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
1341 src |= (1ul << mem_parcel->acl_entries[n].vmid);
1342 else
1343 src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
1344 }
1345
1346 if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
1347 new_perms[0].vmid = this_vmid;
1348 else
1349 new_perms[0].vmid = QCOM_SCM_RM_MANAGED_VMID;
1350
1351 for (i--; i >= 0; i--) {
1352 src_cpy = src;
1353 ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
1354 mem_parcel->mem_entries[i].size,
1355 &src_cpy, new_perms, 1);
1356 WARN_ON_ONCE(ret);
1357 }
1358 break;
1359 }
1360 }
1361
1362 kfree(new_perms);
1363 return ret;
1364 }
1365
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Hi Elliot,
On Wed, Oct 26, 2022 at 11:58:34AM -0700, Elliot Berman wrote:
> Gunyah message queues are a unidirectional inter-VM pipe for messages up
> to 1024 bytes. This driver supports pairing a receiver message queue and
> a transmitter message queue to expose a single mailbox channel.
>
> Signed-off-by: Elliot Berman <[email protected]>
<snip>
> +static irqreturn_t gh_msgq_tx_irq_handler(int irq, void *data)
> +{
> + struct gh_msgq *msgq = data;
> +
> + mbox_chan_txdone(gh_msgq_chan(msgq), 0);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void gh_msgq_txdone_tasklet(unsigned long data)
> +{
> + struct gh_msgq *msgq = (struct gh_msgq *)data;
> +
> + mbox_chan_txdone(gh_msgq_chan(msgq), msgq->last_status);
> +}
> +
> +static int gh_msgq_send_data(struct mbox_chan *chan, void *data)
> +{
> + struct gh_msgq *msgq = mbox_chan_to_msgq(chan);
> + struct gh_msgq_tx_data *msgq_data = data;
> + u64 tx_flags = 0;
> + unsigned long ret;
> + bool ready;
> +
> + if (msgq_data->push)
> + tx_flags |= GH_HYPERCALL_MSGQ_TX_FLAGS_PUSH;
> +
> + ret = gh_hypercall_msgq_send(msgq->tx_ghrsc->capid, msgq_data->length,
> + (uintptr_t)msgq_data->data, tx_flags, &ready);
> +
> + /**
> + * unlikely because Linux tracks state of msgq and should not try to
> + * send message when msgq is full.
> + */
> + if (unlikely(ret == GH_ERROR_MSGQUEUE_FULL))
> + return -EAGAIN;
> +
> + /**
> + * Propagate all other errors to client. If we return error to mailbox
> + * framework, then no other messages can be sent and nobody will know
> + * to retry this message.
> + */
> + msgq->last_status = gh_remap_error(ret);
> +
> + /**
> + * This message was successfully sent, but message queue isn't ready to
> + * receive more messages because it's now full. Mailbox framework
> + * requires that we only report that message was transmitted only when
> + * we're ready to transmit another message. We'll get that in the form
> + * of tx IRQ once the other side starts to drain the msgq.
> + */
> + if (ret == GH_ERROR_OK && !ready)
> + return 0;
> +
> + /**
> + * We can send more messages. Mailbox framework requires that tx done
> + * happens asynchronously to sending the message. Gunyah message queues
> + * tell us right away on the hypercall return whether we can send more
> + * messages. To work around this, defer the txdone to a tasklet.
> + */
> + tasklet_schedule(&msgq->txdone_tasklet);
> +
Nice comments.
irq_work would be a better choice.
> + return 0;
> +}
> +
> +struct mbox_chan_ops gh_msgq_ops = {
> + .send_data = gh_msgq_send_data,
> +};
> +
> +/**
> + * gh_msgq_init() - Initialize a Gunyah message queue with an mbox_client
> + * @parent: optional, device parent used for the mailbox controller
> + * @msgq: Pointer to the gh_msgq to initialize
> + * @cl: A mailbox client to bind to the mailbox channel that the message queue creates
> + * @tx_ghrsc: optional, the transmission side of the message queue
> + * @rx_ghrsc: optional, the receiving side of the message queue
> + *
> + * At least one of tx_ghrsc and rx_ghrsc should be not NULL. Most message queue use cases come with
> + * a pair of message queues to facilitiate bidirectional communication. When tx_ghrsc is set,
> + * the client can send messages with mbox_send_message(gh_msgq_chan(msgq), msg). When rx_ghrsc
> + * is set, the mbox_client should register an .rx_callback() and the message queue driver will
> + * push all available messages upon receiving the RX ready interrupt. The messages should be
> + * consumed or copied by the client right away as the gh_msgq_rx_data will be replaced/destroyed
> + * after the callback.
> + *
> + * Returns - 0 on success, negative otherwise
> + */
> +int gh_msgq_init(struct device *parent, struct gh_msgq *msgq, struct mbox_client *cl,
> + struct gunyah_resource *tx_ghrsc, struct gunyah_resource *rx_ghrsc)
> +{
> + int ret;
> +
> + /* Must have at least a tx_ghrsc or rx_ghrsc and that they are the right device types */
> + if ((!tx_ghrsc && !rx_ghrsc) ||
> + (tx_ghrsc && tx_ghrsc->type != GUNYAH_RESOURCE_TYPE_MSGQ_TX) ||
> + (rx_ghrsc && rx_ghrsc->type != GUNYAH_RESOURCE_TYPE_MSGQ_RX))
> + return -EINVAL;
> +
> + msgq->tx_ghrsc = tx_ghrsc;
> + msgq->rx_ghrsc = rx_ghrsc;
> +
> + msgq->mbox.dev = parent;
> + msgq->mbox.ops = &gh_msgq_ops;
> + msgq->mbox.chans = kcalloc(1, sizeof(*msgq->mbox.chans), GFP_KERNEL);
Error handling missing.
minor nit pick:
If you initialize num_chans to 1 before, then you can use that as the first
argument to kcalloc() which makes it more readable since you opted for kcalloc()
instead of kzalloc() there.
> + msgq->mbox.num_chans = 1;
> + msgq->mbox.txdone_irq = true;
> +
> + if (gh_msgq_has_tx(msgq)) {
> + ret = request_irq(msgq->tx_ghrsc->irq, gh_msgq_tx_irq_handler, 0, "gh_msgq_tx",
> + msgq);
> + if (ret)
> + goto err_chans;
> + }
> +
> + if (gh_msgq_has_rx(msgq)) {
> + ret = request_threaded_irq(msgq->rx_ghrsc->irq, NULL, gh_msgq_rx_irq_handler,
> + IRQF_ONESHOT, "gh_msgq_rx", msgq);
> + if (ret)
> + goto err_tx_irq;
> + }
> +
> + tasklet_init(&msgq->txdone_tasklet, gh_msgq_txdone_tasklet, (unsigned long)msgq);
If you wish to use tasklets, use tasklet_setup().
> +
> + ret = mbox_controller_register(&msgq->mbox);
> + if (ret)
> + goto err_rx_irq;
> +
> + ret = mbox_bind_client(gh_msgq_chan(msgq), cl);
> + if (ret)
> + goto err_mbox;
> +
> + return 0;
> +err_mbox:
> + mbox_controller_unregister(&msgq->mbox);
> +err_rx_irq:
> + if (gh_msgq_has_rx(msgq))
> + free_irq(msgq->rx_ghrsc->irq, msgq);
> +err_tx_irq:
> + if (gh_msgq_has_tx(msgq))
> + free_irq(msgq->tx_ghrsc->irq, msgq);
> +err_chans:
> + kfree(msgq->mbox.chans);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(gh_msgq_init);
> +
> +void gh_msgq_remove(struct gh_msgq *msgq)
> +{
> + if (gh_msgq_has_rx(msgq))
> + free_irq(msgq->rx_ghrsc->irq, msgq);
> +
> + if (gh_msgq_has_tx(msgq))
> + free_irq(msgq->tx_ghrsc->irq, msgq);
> +
> + kfree(msgq->mbox.chans);
> +}
> +EXPORT_SYMBOL_GPL(gh_msgq_remove);
> +
Is gh_msgq_remove() supposed to undo every thing done in gh_msgq_init()?
ex: mbox controller and channel are not unregistered.
Thanks,
Pavan
On 26/10/2022 14:58, Elliot Berman wrote:
> When Linux is booted as a guest under the Gunyah hypervisor, the Gunyah
> Resource Manager applies a devicetree overlay describing the virtual
> platform configuration of the guest VM, such as the message queue
> capability IDs for communicating with the Resource Manager. This
> information is not otherwise discoverable by a VM: the Gunyah hypervisor
> core does not provide a direct interface to discover capability IDs nor
> a way to communicate with RM without having already known the
> corresponding message queue capability ID. Add the DT bindings that
> Gunyah adheres for the hypervisor node and message queues.
>
> Signed-off-by: Elliot Berman <[email protected]>
> ---
> .../bindings/firmware/gunyah-hypervisor.yaml | 86 +++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 87 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
>
> diff --git a/Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml b/Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
> new file mode 100644
> index 000000000000..3a8c1c2157a4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/firmware/gunyah-hypervisor.yaml
> @@ -0,0 +1,86 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/firmware/gunyah-hypervisor.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Gunyah Hypervisor
> +
> +maintainers:
> + - Murali Nalajala <[email protected]>
> + - Elliot Berman <[email protected]>
> +
> +description: |+
> + Gunyah virtual machines use this information to determine the capability IDs
> + of the message queues used to communicate with the Gunyah Resource Manager.
> + See also: https://github.com/quic/gunyah-resource-manager/blob/develop/src/vm_creation/dto_construct.c
> +
> +properties:
> + compatible:
> + items:
> + - const: gunyah-hypervisor-1.0
> + - const: gunyah-hypervisor
You are sending next version while we still keep discussing old one...
and without necessary changes. Instead keep discussing the previous one
till we reach consensus.
These compatibles look wrong based on our discussion.
Best regards,
Krzysztof
On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman <[email protected]> wrote:
.....
> +
> + gunyah-resource-mgr@0 {
> + compatible = "gunyah-resource-manager-1-0", "gunyah-resource-manager";
> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX full IRQ */
> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX empty IRQ */
> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
> + /* TX, RX cap ids */
> + };
>
All these resources are used only by the mailbox controller driver.
So, this should be the mailbox controller node, rather than the
mailbox user.
One option is to load gunyah-resource-manager as a module that relies
on the gunyah-mailbox provider. That would also avoid the "Allow
direct registration to a channel" hack patch.
thanks.
Hi Jassi,
On 10/27/2022 7:33 PM, Jassi Brar wrote:
> On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
<[email protected]> wrote:
> .....
>> +
>> + gunyah-resource-mgr@0 {
>> + compatible = "gunyah-resource-manager-1-0",
"gunyah-resource-manager";
>> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
full IRQ */
>> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
empty IRQ */
>> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
>> + /* TX, RX cap ids */
>> + };
>>
> All these resources are used only by the mailbox controller driver.
> So, this should be the mailbox controller node, rather than the
> mailbox user.> One option is to load gunyah-resource-manager as a
module that relies
> on the gunyah-mailbox provider. That would also avoid the "Allow
> direct registration to a channel" hack patch.
A message queue to another guest VM wouldn't be known at boot time and
thus couldn't be described on the devicetree. We will need "Allow direct
registration to a channel" patch anyway to support those message queues.
I would like to have one consistent mechanism to set up message queues.
- Elliot
On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
>
> Hi Jassi,
>
> On 10/27/2022 7:33 PM, Jassi Brar wrote:
> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
> <[email protected]> wrote:
> > .....
> >> +
> >> + gunyah-resource-mgr@0 {
> >> + compatible = "gunyah-resource-manager-1-0",
> "gunyah-resource-manager";
> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
> full IRQ */
> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
> empty IRQ */
> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
> >> + /* TX, RX cap ids */
> >> + };
> >>
> > All these resources are used only by the mailbox controller driver.
> > So, this should be the mailbox controller node, rather than the
> > mailbox user.> One option is to load gunyah-resource-manager as a
> module that relies
> > on the gunyah-mailbox provider. That would also avoid the "Allow
> > direct registration to a channel" hack patch.
>
> A message queue to another guest VM wouldn't be known at boot time and
> thus couldn't be described on the devicetree.
>
I think you need to implement of_xlate() ... or please tell me what
exactly you need to specify in the dt.
thnx.
On 10/27/2022 6:55 AM, Pavan Kondeti wrote:
> Hi Elliot,
>
> On Wed, Oct 26, 2022 at 11:58:34AM -0700, Elliot Berman wrote:
>> Gunyah message queues are a unidirectional inter-VM pipe for messages up
>> to 1024 bytes. This driver supports pairing a receiver message queue and
>> a transmitter message queue to expose a single mailbox channel.
>>
>> Signed-off-by: Elliot Berman <[email protected]>
>
> <snip>
>
>> +static irqreturn_t gh_msgq_tx_irq_handler(int irq, void *data)
>> +{
>> + struct gh_msgq *msgq = data;
>> +
>> + mbox_chan_txdone(gh_msgq_chan(msgq), 0);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void gh_msgq_txdone_tasklet(unsigned long data)
>> +{
>> + struct gh_msgq *msgq = (struct gh_msgq *)data;
>> +
>> + mbox_chan_txdone(gh_msgq_chan(msgq), msgq->last_status);
>> +}
>> +
>> +static int gh_msgq_send_data(struct mbox_chan *chan, void *data)
>> +{
>> + struct gh_msgq *msgq = mbox_chan_to_msgq(chan);
>> + struct gh_msgq_tx_data *msgq_data = data;
>> + u64 tx_flags = 0;
>> + unsigned long ret;
>> + bool ready;
>> +
>> + if (msgq_data->push)
>> + tx_flags |= GH_HYPERCALL_MSGQ_TX_FLAGS_PUSH;
>> +
>> + ret = gh_hypercall_msgq_send(msgq->tx_ghrsc->capid, msgq_data->length,
>> + (uintptr_t)msgq_data->data, tx_flags, &ready);
>> +
>> + /**
>> + * unlikely because Linux tracks state of msgq and should not try to
>> + * send message when msgq is full.
>> + */
>> + if (unlikely(ret == GH_ERROR_MSGQUEUE_FULL))
>> + return -EAGAIN;
>> +
>> + /**
>> + * Propagate all other errors to client. If we return error to mailbox
>> + * framework, then no other messages can be sent and nobody will know
>> + * to retry this message.
>> + */
>> + msgq->last_status = gh_remap_error(ret);
>> +
>> + /**
>> + * This message was successfully sent, but message queue isn't ready to
>> + * receive more messages because it's now full. Mailbox framework
>> + * requires that we only report that message was transmitted only when
>> + * we're ready to transmit another message. We'll get that in the form
>> + * of tx IRQ once the other side starts to drain the msgq.
>> + */
>> + if (ret == GH_ERROR_OK && !ready)
>> + return 0;
>> +
>> + /**
>> + * We can send more messages. Mailbox framework requires that tx done
>> + * happens asynchronously to sending the message. Gunyah message queues
>> + * tell us right away on the hypercall return whether we can send more
>> + * messages. To work around this, defer the txdone to a tasklet.
>> + */
>> + tasklet_schedule(&msgq->txdone_tasklet);
>> +
>
> Nice comments.
>
> irq_work would be a better choice.
>
>> + return 0;
>> +}
>> +
>> +struct mbox_chan_ops gh_msgq_ops = {
>> + .send_data = gh_msgq_send_data,
>> +};
>> +
>> +/**
>> + * gh_msgq_init() - Initialize a Gunyah message queue with an mbox_client
>> + * @parent: optional, device parent used for the mailbox controller
>> + * @msgq: Pointer to the gh_msgq to initialize
>> + * @cl: A mailbox client to bind to the mailbox channel that the message queue creates
>> + * @tx_ghrsc: optional, the transmission side of the message queue
>> + * @rx_ghrsc: optional, the receiving side of the message queue
>> + *
>> + * At least one of tx_ghrsc and rx_ghrsc should be not NULL. Most message queue use cases come with
>> + * a pair of message queues to facilitiate bidirectional communication. When tx_ghrsc is set,
>> + * the client can send messages with mbox_send_message(gh_msgq_chan(msgq), msg). When rx_ghrsc
>> + * is set, the mbox_client should register an .rx_callback() and the message queue driver will
>> + * push all available messages upon receiving the RX ready interrupt. The messages should be
>> + * consumed or copied by the client right away as the gh_msgq_rx_data will be replaced/destroyed
>> + * after the callback.
>> + *
>> + * Returns - 0 on success, negative otherwise
>> + */
>> +int gh_msgq_init(struct device *parent, struct gh_msgq *msgq, struct mbox_client *cl,
>> + struct gunyah_resource *tx_ghrsc, struct gunyah_resource *rx_ghrsc)
>> +{
>> + int ret;
>> +
>> + /* Must have at least a tx_ghrsc or rx_ghrsc and that they are the right device types */
>> + if ((!tx_ghrsc && !rx_ghrsc) ||
>> + (tx_ghrsc && tx_ghrsc->type != GUNYAH_RESOURCE_TYPE_MSGQ_TX) ||
>> + (rx_ghrsc && rx_ghrsc->type != GUNYAH_RESOURCE_TYPE_MSGQ_RX))
>> + return -EINVAL;
>> +
>> + msgq->tx_ghrsc = tx_ghrsc;
>> + msgq->rx_ghrsc = rx_ghrsc;
>> +
>> + msgq->mbox.dev = parent;
>> + msgq->mbox.ops = &gh_msgq_ops;
>> + msgq->mbox.chans = kcalloc(1, sizeof(*msgq->mbox.chans), GFP_KERNEL);
>
> Error handling missing.
>
> minor nit pick:
>
> If you initialize num_chans to 1 before, then you can use that as the first
> argument to kcalloc() which makes it more readable since you opted for kcalloc()
> instead of kzalloc() there.
>
>> + msgq->mbox.num_chans = 1;
>> + msgq->mbox.txdone_irq = true;
>> +
>> + if (gh_msgq_has_tx(msgq)) {
>> + ret = request_irq(msgq->tx_ghrsc->irq, gh_msgq_tx_irq_handler, 0, "gh_msgq_tx",
>> + msgq);
>> + if (ret)
>> + goto err_chans;
>> + }
>> +
>> + if (gh_msgq_has_rx(msgq)) {
>> + ret = request_threaded_irq(msgq->rx_ghrsc->irq, NULL, gh_msgq_rx_irq_handler,
>> + IRQF_ONESHOT, "gh_msgq_rx", msgq);
>> + if (ret)
>> + goto err_tx_irq;
>> + }
>> +
>> + tasklet_init(&msgq->txdone_tasklet, gh_msgq_txdone_tasklet, (unsigned long)msgq);
>
> If you wish to use tasklets, use tasklet_setup().
>
>> +
>> + ret = mbox_controller_register(&msgq->mbox);
>> + if (ret)
>> + goto err_rx_irq;
>> +
>> + ret = mbox_bind_client(gh_msgq_chan(msgq), cl);
>> + if (ret)
>> + goto err_mbox;
>> +
>> + return 0;
>> +err_mbox:
>> + mbox_controller_unregister(&msgq->mbox);
>> +err_rx_irq:
>> + if (gh_msgq_has_rx(msgq))
>> + free_irq(msgq->rx_ghrsc->irq, msgq);
>> +err_tx_irq:
>> + if (gh_msgq_has_tx(msgq))
>> + free_irq(msgq->tx_ghrsc->irq, msgq);
>> +err_chans:
>> + kfree(msgq->mbox.chans);
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(gh_msgq_init);
>> +
>> +void gh_msgq_remove(struct gh_msgq *msgq)
>> +{
>> + if (gh_msgq_has_rx(msgq))
>> + free_irq(msgq->rx_ghrsc->irq, msgq);
>> +
>> + if (gh_msgq_has_tx(msgq))
>> + free_irq(msgq->tx_ghrsc->irq, msgq);
>> +
>> + kfree(msgq->mbox.chans);
>> +}
>> +EXPORT_SYMBOL_GPL(gh_msgq_remove);
>> +
>
> Is gh_msgq_remove() supposed to undo every thing done in gh_msgq_init()?
> ex: mbox controller and channel are not unregistered.
>
Ah thanks for catching, updated this as well as rest of your comments.
On Wed, Oct 26, 2022 at 11:58:35AM -0700, Elliot Berman wrote:
> The resource manager is a special virtual machine which is always
> running on a Gunyah system. It provides APIs for creating and destroying
> VMs, secure memory management, sharing/lending of memory between VMs,
> and setup of inter-VM communication. Calls to the resource manager are
> made via message queues.
>
> This patch implements the basic probing and RPC mechanism to make those
> API calls. Request/response calls can be made with gh_rm_call.
> Drivers can also register to notifications pushed by RM via
> gh_rm_register_notifier
>
> Specific API calls that resource manager supports will be implemented in
> subsequent patches.
>
> Signed-off-by: Elliot Berman <[email protected]>
> ---
> MAINTAINERS | 2 +-
> drivers/virt/gunyah/Kconfig | 15 +
> drivers/virt/gunyah/Makefile | 3 +
> drivers/virt/gunyah/rsc_mgr.c | 602 +++++++++++++++++++++++++++++++++
> drivers/virt/gunyah/rsc_mgr.h | 34 ++
> include/linux/gunyah_rsc_mgr.h | 26 ++
> 6 files changed, 681 insertions(+), 1 deletion(-)
> create mode 100644 drivers/virt/gunyah/rsc_mgr.c
> create mode 100644 drivers/virt/gunyah/rsc_mgr.h
> create mode 100644 include/linux/gunyah_rsc_mgr.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 586539eadd3b..e072a0d2e553 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8945,7 +8945,7 @@ F: Documentation/virt/gunyah/
> F: arch/arm64/gunyah/
> F: drivers/mailbox/gunyah-msgq.c
> F: drivers/virt/gunyah/
> -F: include/linux/gunyah.h
> +F: include/linux/gunyah*.h
>
> HABANALABS PCI DRIVER
> M: Oded Gabbay <[email protected]>
> diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
> index 127156a678a6..4de88d80aa7b 100644
> --- a/drivers/virt/gunyah/Kconfig
> +++ b/drivers/virt/gunyah/Kconfig
> @@ -10,3 +10,18 @@ config GUNYAH
>
> Say Y/M here to enable the drivers needed to interact in a Gunyah
> virtual environment.
> +
> +config GUNYAH_RESORUCE_MANAGER
> + tristate "Gunyah Resource Manager"
> + select MAILBOX
> + select GUNYAH_MESSAGE_QUEUES
> + depends on GUNYAH
> + default y
You only have "default y" if your machine can not boot without it.
Please do not add that here.
> + help
> + The resource manager (RM) is a privileged application VM supporting
> + the Gunyah Hypervisor. Enable this driver to support communicating
> + with Gunyah RM. This is typically required for a VM running under
> + Gunyah wanting to have Gunyah-awareness.
> +
> + Say Y/M here if unsure.
> +
> diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile
> index 2ac4ee64b89d..2c18b0a56413 100644
> --- a/drivers/virt/gunyah/Makefile
> +++ b/drivers/virt/gunyah/Makefile
> @@ -1 +1,4 @@
> obj-$(CONFIG_GUNYAH) += gunyah.o
> +
> +gunyah_rsc_mgr-y += rsc_mgr.o
> +obj-$(CONFIG_GUNYAH_RESORUCE_MANAGER) += gunyah_rsc_mgr.o
> diff --git a/drivers/virt/gunyah/rsc_mgr.c b/drivers/virt/gunyah/rsc_mgr.c
> new file mode 100644
> index 000000000000..a9fde703cbbe
> --- /dev/null
> +++ b/drivers/virt/gunyah/rsc_mgr.c
> @@ -0,0 +1,602 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#define pr_fmt(fmt) "gh_rsc_mgr: " fmt
This is a driver, you should never need this as you should be using the
dev_*() calls, not pr_*() calls as you always have access to a struct
device, right?
So you can drop this.
> +
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/sched.h>
> +#include <linux/gunyah.h>
> +#include <linux/module.h>
> +#include <linux/of_irq.h>
> +#include <linux/kthread.h>
> +#include <linux/notifier.h>
> +#include <linux/workqueue.h>
> +#include <linux/completion.h>
> +#include <linux/gunyah_rsc_mgr.h>
> +#include <linux/platform_device.h>
> +
> +#include "rsc_mgr.h"
> +
> +/* Resource Manager Header */
> +struct gh_rm_rpc_hdr {
> + u8 version : 4, hdr_words : 4;
> + u8 type : 2, fragments : 6;
Ick, that's hard to read. One variable per line please?
And why the bit packed stuff? Are you sure this is the way to do this?
Why not use a bitmask instead?
> + u16 seq;
> + u32 msg_id;
> +} __packed;
> +
> +/* Standard reply header */
> +struct gh_rm_rpc_reply_hdr {
> + struct gh_rm_rpc_hdr rpc_hdr;
> + u32 err_code;
> +} __packed;
> +
> +/* RPC Header versions */
> +#define GH_RM_RPC_HDR_VERSION_ONE 0x1
> +
> +/* RPC Header words */
> +#define GH_RM_RPC_HDR_WORDS 0x2
> +
> +/* RPC Message types */
> +#define GH_RM_RPC_TYPE_CONT 0x0
> +#define GH_RM_RPC_TYPE_REQ 0x1
> +#define GH_RM_RPC_TYPE_RPLY 0x2
> +#define GH_RM_RPC_TYPE_NOTIF 0x3
> +
> +#define GH_RM_MAX_NUM_FRAGMENTS 62
> +
> +#define GH_RM_MAX_MSG_SIZE (GH_MSGQ_MAX_MSG_SIZE - sizeof(struct gh_rm_rpc_hdr))
> +
> +/**
> + * struct gh_rm_connection - Represents a complete message from resource manager
> + * @payload: Combined payload of all the fragments (msg headers stripped off).
> + * @size: Size of the payload.
> + * @ret: Linux return code, set in case there was an error processing connection
> + * @msg_id: Message ID from the header.
> + * @type: GH_RM_RPC_TYPE_RPLY or GH_RM_RPC_TYPE_NOTIF.
> + * @num_fragments: total number of fragments expected to be received.
> + * @fragments_received: fragments received so far.
> + * @rm_error: For request/reply sequences with standard replies.
> + * @seq: Sequence ID for the main message.
> + * @seq_done: Signals caller that the RM reply has been received
> + */
> +struct gh_rm_connection {
> + void *payload;
> + size_t size;
> + int ret;
> + u32 msg_id;
> + u8 type;
> +
> + u8 num_fragments;
> + u8 fragments_received;
> +
> + /* only for req/reply sequence */
> + u32 rm_error;
> + u16 seq;
> + struct completion seq_done;
> +};
> +
> +struct gh_rm_notif_complete {
> + struct gh_rm_connection *conn;
> + struct work_struct work;
> +};
> +
> +struct gh_rsc_mgr {
> + struct gunyah_resource tx_ghrsc, rx_ghrsc;
> + struct gh_msgq msgq;
> + struct mbox_client msgq_client;
> + struct gh_rm_connection *active_rx_connection;
> + int last_tx_ret;
> +
> + struct idr call_idr;
> + struct mutex call_idr_lock;
> +
> + struct mutex send_lock;
> +
> + struct work_struct recv_work;
> +};
> +
> +static struct gh_rsc_mgr *__rsc_mgr;
Sorry, no, you don't get to just limit yourself to one of these. Please
make this properly handle any number of "resource managers", static
variables like this is not ok.
> +SRCU_NOTIFIER_HEAD_STATIC(gh_rm_notifier);
Why do you need a notifier list?
Who will register for this? For what? Why?
> +static int gh_rm_drv_probe(struct platform_device *pdev)
> +{
> + struct gh_rsc_mgr *rsc_mgr;
> + int ret;
> +
> + rsc_mgr = devm_kzalloc(&pdev->dev, sizeof(*rsc_mgr), GFP_KERNEL);
> + if (!rsc_mgr)
> + return -ENOMEM;
> + platform_set_drvdata(pdev, rsc_mgr);
> +
> + mutex_init(&rsc_mgr->call_idr_lock);
> + idr_init(&rsc_mgr->call_idr);
> + mutex_init(&rsc_mgr->send_lock);
> +
> + ret = gh_msgq_platform_probe_direction(pdev, GUNYAH_RESOURCE_TYPE_MSGQ_TX, 0,
> + &rsc_mgr->tx_ghrsc);
> + if (ret)
> + return ret;
> +
> + ret = gh_msgq_platform_probe_direction(pdev, GUNYAH_RESOURCE_TYPE_MSGQ_RX, 1,
> + &rsc_mgr->rx_ghrsc);
> + if (ret)
> + return ret;
> +
> + rsc_mgr->msgq_client.dev = &pdev->dev;
So your client device is the platform device, and not a new bridge
device that you create instead? Why?
> + rsc_mgr->msgq_client.tx_block = true;
> + rsc_mgr->msgq_client.rx_callback = gh_rm_msgq_rx_data;
> + rsc_mgr->msgq_client.tx_done = gh_rm_msgq_tx_done;
> +
> + ret = gh_msgq_init(&pdev->dev, &rsc_mgr->msgq, &rsc_mgr->msgq_client,
> + &rsc_mgr->tx_ghrsc, &rsc_mgr->rx_ghrsc);
> + if (ret)
> + return ret;
> +
> + __rsc_mgr = rsc_mgr;
> +
> + return 0;
> +}
> +static struct platform_driver gh_rm_driver = {
> + .probe = gh_rm_drv_probe,
> + .remove = gh_rm_drv_remove,
> + .driver = {
> + .name = "gh_rsc_mgr",
> + .of_match_table = gh_rm_of_match,
> + },
Wait, why is this a platform driver? This is binding to a real device
on a real bus, not a random platform description in DT, right?
Or is it controlled by your DT? I can't figure that out here, sorry.
thanks,
greg k-h
On 11/1/2022 9:23 AM, Jassi Brar wrote:
> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
>>
>> Hi Jassi,
>>
>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
>> <[email protected]> wrote:
>> > .....
>> >> +
>> >> + gunyah-resource-mgr@0 {
>> >> + compatible = "gunyah-resource-manager-1-0",
>> "gunyah-resource-manager";
>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
>> full IRQ */
>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
>> empty IRQ */
>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
>> >> + /* TX, RX cap ids */
>> >> + };
>> >>
>> > All these resources are used only by the mailbox controller driver.
>> > So, this should be the mailbox controller node, rather than the
>> > mailbox user.> One option is to load gunyah-resource-manager as a
>> module that relies
>> > on the gunyah-mailbox provider. That would also avoid the "Allow
>> > direct registration to a channel" hack patch.
>>
>> A message queue to another guest VM wouldn't be known at boot time and
>> thus couldn't be described on the devicetree.
>>
> I think you need to implement of_xlate() ... or please tell me what
> exactly you need to specify in the dt.
Dynamically created virtual machines can't be known on the dt, so there
is nothing to specify in the DT. There couldn't be a devicetree node for
the message queue client because that client is only exists once the VM
is created by userspace.
As a more concrete example, there is QRTR (net/qrtr) virtualization
support which is implemented with Gunyah message queues. Whether a QRTR
client needs to be for VM is only determined when launching the VM as
well as which message queue resource the QRTR client should be using.
Since many VMs could be running on a system, it's not possible to know
the number of mailbox controllers (i.e. message queues) nor the number
of mailbox clients (e.g. QRTR) as a static configuration in the DT.
Thanks,
Elliot
On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
>
>
>
> On 11/1/2022 9:23 AM, Jassi Brar wrote:
> > On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
> >>
> >> Hi Jassi,
> >>
> >> On 10/27/2022 7:33 PM, Jassi Brar wrote:
> >> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
> >> <[email protected]> wrote:
> >> > .....
> >> >> +
> >> >> + gunyah-resource-mgr@0 {
> >> >> + compatible = "gunyah-resource-manager-1-0",
> >> "gunyah-resource-manager";
> >> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
> >> full IRQ */
> >> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
> >> empty IRQ */
> >> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
> >> >> + /* TX, RX cap ids */
> >> >> + };
> >> >>
> >> > All these resources are used only by the mailbox controller driver.
> >> > So, this should be the mailbox controller node, rather than the
> >> > mailbox user.> One option is to load gunyah-resource-manager as a
> >> module that relies
> >> > on the gunyah-mailbox provider. That would also avoid the "Allow
> >> > direct registration to a channel" hack patch.
> >>
> >> A message queue to another guest VM wouldn't be known at boot time and
> >> thus couldn't be described on the devicetree.
> >>
> > I think you need to implement of_xlate() ... or please tell me what
> > exactly you need to specify in the dt.
>
> Dynamically created virtual machines can't be known on the dt, so there
> is nothing to specify in the DT. There couldn't be a devicetree node for
> the message queue client because that client is only exists once the VM
> is created by userspace.
>
The underlying "physical channel" is the synchronous SMC instruction,
which remains 1 irrespective of the number of mailbox instances
created.
So basically you are sharing one resource among users. Why doesn't the
RM request the "smc instruction" channel once and share it among
users?
-j
On 11/1/2022 2:58 PM, Jassi Brar wrote:
> On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
>>
>>
>>
>> On 11/1/2022 9:23 AM, Jassi Brar wrote:
>>> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
>>>>
>>>> Hi Jassi,
>>>>
>>>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
>>>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
>>>> <[email protected]> wrote:
>>>> > .....
>>>> >> +
>>>> >> + gunyah-resource-mgr@0 {
>>>> >> + compatible = "gunyah-resource-manager-1-0",
>>>> "gunyah-resource-manager";
>>>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
>>>> full IRQ */
>>>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
>>>> empty IRQ */
>>>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
>>>> >> + /* TX, RX cap ids */
>>>> >> + };
>>>> >>
>>>> > All these resources are used only by the mailbox controller driver.
>>>> > So, this should be the mailbox controller node, rather than the
>>>> > mailbox user.> One option is to load gunyah-resource-manager as a
>>>> module that relies
>>>> > on the gunyah-mailbox provider. That would also avoid the "Allow
>>>> > direct registration to a channel" hack patch.
>>>>
>>>> A message queue to another guest VM wouldn't be known at boot time and
>>>> thus couldn't be described on the devicetree.
>>>>
>>> I think you need to implement of_xlate() ... or please tell me what
>>> exactly you need to specify in the dt.
>>
>> Dynamically created virtual machines can't be known on the dt, so there
>> is nothing to specify in the DT. There couldn't be a devicetree node for
>> the message queue client because that client is only exists once the VM
>> is created by userspace.
>>
> The underlying "physical channel" is the synchronous SMC instruction,
> which remains 1 irrespective of the number of mailbox instances
> created.
I disagree that the physical channel is the SMC instruction. Regardless
though, there are num_online_cpus() "physical channels" with this
perspective.
> So basically you are sharing one resource among users. Why doesn't the
> RM request the "smc instruction" channel once and share it among
> users?
I suppose in this scenario, a single mailbox channel would represent all
message queues? This would cause Linux to serialize *all* message queue
hypercalls. Sorry, I can only think negative implications.
Error handling needs to move into clients: if a TX message queue becomes
full or an RX message queue becomes empty, then we'll need to return
error back to the client right away. The clients would need to register
for the RTS/RTR interrupts to know when to send/receive messages and
have retry error handling. If the mailbox controller retried for the
clients as currently proposed, then we could get into a scenario where a
message queue could never be ready to send/receive and thus stuck
forever trying to process that message. The effect here would be that
the mailbox controller becomes a wrapper to some SMC instructions that
aren't related at the SMC instruction level.
A single channel would limit performance of SMP systems because only one
core could send/receive a message. There is no such limitation for
message queues to behave like this.
Thanks,
Elliot
On 11/1/2022 11:02 AM, Greg Kroah-Hartman wrote:
> On Wed, Oct 26, 2022 at 11:58:35AM -0700, Elliot Berman wrote:
>> The resource manager is a special virtual machine which is always
>> running on a Gunyah system. It provides APIs for creating and destroying
>> VMs, secure memory management, sharing/lending of memory between VMs,
>> and setup of inter-VM communication. Calls to the resource manager are
>> made via message queues.
>>
>> This patch implements the basic probing and RPC mechanism to make those
>> API calls. Request/response calls can be made with gh_rm_call.
>> Drivers can also register to notifications pushed by RM via
>> gh_rm_register_notifier
>>
>> Specific API calls that resource manager supports will be implemented in
>> subsequent patches.
>>
>> Signed-off-by: Elliot Berman <[email protected]>
>> ---
>> MAINTAINERS | 2 +-
>> drivers/virt/gunyah/Kconfig | 15 +
>> drivers/virt/gunyah/Makefile | 3 +
>> drivers/virt/gunyah/rsc_mgr.c | 602 +++++++++++++++++++++++++++++++++
>> drivers/virt/gunyah/rsc_mgr.h | 34 ++
>> include/linux/gunyah_rsc_mgr.h | 26 ++
>> 6 files changed, 681 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/virt/gunyah/rsc_mgr.c
>> create mode 100644 drivers/virt/gunyah/rsc_mgr.h
>> create mode 100644 include/linux/gunyah_rsc_mgr.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 586539eadd3b..e072a0d2e553 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -8945,7 +8945,7 @@ F: Documentation/virt/gunyah/
>> F: arch/arm64/gunyah/
>> F: drivers/mailbox/gunyah-msgq.c
>> F: drivers/virt/gunyah/
>> -F: include/linux/gunyah.h
>> +F: include/linux/gunyah*.h
>>
>> HABANALABS PCI DRIVER
>> M: Oded Gabbay <[email protected]>
>> diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
>> index 127156a678a6..4de88d80aa7b 100644
>> --- a/drivers/virt/gunyah/Kconfig
>> +++ b/drivers/virt/gunyah/Kconfig
>> @@ -10,3 +10,18 @@ config GUNYAH
>>
>> Say Y/M here to enable the drivers needed to interact in a Gunyah
>> virtual environment.
>> +
>> +config GUNYAH_RESORUCE_MANAGER
>> + tristate "Gunyah Resource Manager"
>> + select MAILBOX
>> + select GUNYAH_MESSAGE_QUEUES
>> + depends on GUNYAH
>> + default y
>
> You only have "default y" if your machine can not boot without it.
> Please do not add that here.
>
There's a guideline in Documentation/kbuild/kconfig-language.rst to
provide some sane defaults for subdriver behavior. Here, CONFIG_GUNYAH
is default n. It's unlikely for someone to want to have Linux with base
Gunyah support (hypercalls and hypervisor detection) without also having
the Resource Manager driver. If it's better, I could change to default m?
>> + help
>> + The resource manager (RM) is a privileged application VM supporting
>> + the Gunyah Hypervisor. Enable this driver to support communicating
>> + with Gunyah RM. This is typically required for a VM running under
>> + Gunyah wanting to have Gunyah-awareness.
>> +
>> + Say Y/M here if unsure.
>> +
>> diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile
>> index 2ac4ee64b89d..2c18b0a56413 100644
>> --- a/drivers/virt/gunyah/Makefile
>> +++ b/drivers/virt/gunyah/Makefile
>> @@ -1 +1,4 @@
>> obj-$(CONFIG_GUNYAH) += gunyah.o
>> +
>> +gunyah_rsc_mgr-y += rsc_mgr.o
>> +obj-$(CONFIG_GUNYAH_RESORUCE_MANAGER) += gunyah_rsc_mgr.o
>> diff --git a/drivers/virt/gunyah/rsc_mgr.c b/drivers/virt/gunyah/rsc_mgr.c
>> new file mode 100644
>> index 000000000000..a9fde703cbbe
>> --- /dev/null
>> +++ b/drivers/virt/gunyah/rsc_mgr.c
>> @@ -0,0 +1,602 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
>> + */
>> +
>> +#define pr_fmt(fmt) "gh_rsc_mgr: " fmt
>
> This is a driver, you should never need this as you should be using the
> dev_*() calls, not pr_*() calls as you always have access to a struct
> device, right?
>
> So you can drop this.
>
>
Ack
>> +
>> +#include <linux/of.h>
>> +#include <linux/slab.h>
>> +#include <linux/mutex.h>
>> +#include <linux/sched.h>
>> +#include <linux/gunyah.h>
>> +#include <linux/module.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/kthread.h>
>> +#include <linux/notifier.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/completion.h>
>> +#include <linux/gunyah_rsc_mgr.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include "rsc_mgr.h"
>> +
>> +/* Resource Manager Header */
>> +struct gh_rm_rpc_hdr {
>> + u8 version : 4, hdr_words : 4;
>> + u8 type : 2, fragments : 6;
>
> Ick, that's hard to read. One variable per line please?
Ack.
> And why the bit packed stuff? Are you sure this is the way to do this?
> Why not use a bitmask instead?
>
I felt bit packed implementation is cleaner and easier to map to
understanding what the fields are used for.
>> + u16 seq;
>> + u32 msg_id;
>> +} __packed;
>> +
>> +/* Standard reply header */
>> +struct gh_rm_rpc_reply_hdr {
>> + struct gh_rm_rpc_hdr rpc_hdr;
>> + u32 err_code;
>> +} __packed;
>> +
>> +/* RPC Header versions */
>> +#define GH_RM_RPC_HDR_VERSION_ONE 0x1
>> +
>> +/* RPC Header words */
>> +#define GH_RM_RPC_HDR_WORDS 0x2
>> +
>> +/* RPC Message types */
>> +#define GH_RM_RPC_TYPE_CONT 0x0
>> +#define GH_RM_RPC_TYPE_REQ 0x1
>> +#define GH_RM_RPC_TYPE_RPLY 0x2
>> +#define GH_RM_RPC_TYPE_NOTIF 0x3
>> +
>> +#define GH_RM_MAX_NUM_FRAGMENTS 62
>> +
>> +#define GH_RM_MAX_MSG_SIZE (GH_MSGQ_MAX_MSG_SIZE - sizeof(struct gh_rm_rpc_hdr))
>> +
>> +/**
>> + * struct gh_rm_connection - Represents a complete message from resource manager
>> + * @payload: Combined payload of all the fragments (msg headers stripped off).
>> + * @size: Size of the payload.
>> + * @ret: Linux return code, set in case there was an error processing connection
>> + * @msg_id: Message ID from the header.
>> + * @type: GH_RM_RPC_TYPE_RPLY or GH_RM_RPC_TYPE_NOTIF.
>> + * @num_fragments: total number of fragments expected to be received.
>> + * @fragments_received: fragments received so far.
>> + * @rm_error: For request/reply sequences with standard replies.
>> + * @seq: Sequence ID for the main message.
>> + * @seq_done: Signals caller that the RM reply has been received
>> + */
>> +struct gh_rm_connection {
>> + void *payload;
>> + size_t size;
>> + int ret;
>> + u32 msg_id;
>> + u8 type;
>> +
>> + u8 num_fragments;
>> + u8 fragments_received;
>> +
>> + /* only for req/reply sequence */
>> + u32 rm_error;
>> + u16 seq;
>> + struct completion seq_done;
>> +};
>> +
>> +struct gh_rm_notif_complete {
>> + struct gh_rm_connection *conn;
>> + struct work_struct work;
>> +};
>> +
>> +struct gh_rsc_mgr {
>> + struct gunyah_resource tx_ghrsc, rx_ghrsc;
>> + struct gh_msgq msgq;
>> + struct mbox_client msgq_client;
>> + struct gh_rm_connection *active_rx_connection;
>> + int last_tx_ret;
>> +
>> + struct idr call_idr;
>> + struct mutex call_idr_lock;
>> +
>> + struct mutex send_lock;
>> +
>> + struct work_struct recv_work;
>> +};
>> +
>> +static struct gh_rsc_mgr *__rsc_mgr;
>
> Sorry, no, you don't get to just limit yourself to one of these. Please
> make this properly handle any number of "resource managers", static
> variables like this is not ok.
>
There will only ever be one resource manager. optee, psci, and qcom_scm
use a similar approach.
>> +SRCU_NOTIFIER_HEAD_STATIC(gh_rm_notifier);
>
> Why do you need a notifier list?
>
> Who will register for this? For what? Why?
>
The majority of notifications that RM sends to Linux will be related to
VM state, e.g. "VM crashed." I've not added the handling in VM manager
yet to reduce the number of patches in this series. It was used in the
previous series for the console driver. I can remove for now and
re-introduce it once VM manager makes use?
>> +static int gh_rm_drv_probe(struct platform_device *pdev)
>> +{
>> + struct gh_rsc_mgr *rsc_mgr;
>> + int ret;
>> +
>> + rsc_mgr = devm_kzalloc(&pdev->dev, sizeof(*rsc_mgr), GFP_KERNEL);
>> + if (!rsc_mgr)
>> + return -ENOMEM;
>> + platform_set_drvdata(pdev, rsc_mgr);
>> +
>> + mutex_init(&rsc_mgr->call_idr_lock);
>> + idr_init(&rsc_mgr->call_idr);
>> + mutex_init(&rsc_mgr->send_lock);
>> +
>> + ret = gh_msgq_platform_probe_direction(pdev, GUNYAH_RESOURCE_TYPE_MSGQ_TX, 0,
>> + &rsc_mgr->tx_ghrsc);
>> + if (ret)
>> + return ret;
>> +
>> + ret = gh_msgq_platform_probe_direction(pdev, GUNYAH_RESOURCE_TYPE_MSGQ_RX, 1,
>> + &rsc_mgr->rx_ghrsc);
>> + if (ret)
>> + return ret;
>> +
>> + rsc_mgr->msgq_client.dev = &pdev->dev;
>
> So your client device is the platform device, and not a new bridge
> device that you create instead? Why?
>
Answered below
>> + rsc_mgr->msgq_client.tx_block = true;
>> + rsc_mgr->msgq_client.rx_callback = gh_rm_msgq_rx_data;
>> + rsc_mgr->msgq_client.tx_done = gh_rm_msgq_tx_done;
>> +
>> + ret = gh_msgq_init(&pdev->dev, &rsc_mgr->msgq, &rsc_mgr->msgq_client,
>> + &rsc_mgr->tx_ghrsc, &rsc_mgr->rx_ghrsc);
>> + if (ret)
>> + return ret;
>> +
>> + __rsc_mgr = rsc_mgr;
>> +
>> + return 0;
>> +}
>> +static struct platform_driver gh_rm_driver = {
>> + .probe = gh_rm_drv_probe,
>> + .remove = gh_rm_drv_remove,
>> + .driver = {
>> + .name = "gh_rsc_mgr",
>> + .of_match_table = gh_rm_of_match,
>> + },
>
> Wait, why is this a platform driver? This is binding to a real device
> on a real bus, not a random platform description in DT, right?
This a binding for a real device and not a "random platform description"
in DT to get the driver probed.
> Or is it controlled by your DT? I can't figure that out here, sorry.
There is some info in Patch 2 about why the DT node exists and how it
looks. Essentially, The DT node is provided by Gunyah during boot and
describes how Linux can communicate with resource manager.
Thanks,
Elliot
On Tue, Nov 1, 2022 at 7:12 PM Elliot Berman <[email protected]> wrote:
>
>
>
> On 11/1/2022 2:58 PM, Jassi Brar wrote:
> > On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
> >>
> >>
> >>
> >> On 11/1/2022 9:23 AM, Jassi Brar wrote:
> >>> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
> >>>>
> >>>> Hi Jassi,
> >>>>
> >>>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
> >>>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
> >>>> <[email protected]> wrote:
> >>>> > .....
> >>>> >> +
> >>>> >> + gunyah-resource-mgr@0 {
> >>>> >> + compatible = "gunyah-resource-manager-1-0",
> >>>> "gunyah-resource-manager";
> >>>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
> >>>> full IRQ */
> >>>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
> >>>> empty IRQ */
> >>>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
> >>>> >> + /* TX, RX cap ids */
> >>>> >> + };
> >>>> >>
> >>>> > All these resources are used only by the mailbox controller driver.
> >>>> > So, this should be the mailbox controller node, rather than the
> >>>> > mailbox user.> One option is to load gunyah-resource-manager as a
> >>>> module that relies
> >>>> > on the gunyah-mailbox provider. That would also avoid the "Allow
> >>>> > direct registration to a channel" hack patch.
> >>>>
> >>>> A message queue to another guest VM wouldn't be known at boot time and
> >>>> thus couldn't be described on the devicetree.
> >>>>
> >>> I think you need to implement of_xlate() ... or please tell me what
> >>> exactly you need to specify in the dt.
> >>
> >> Dynamically created virtual machines can't be known on the dt, so there
> >> is nothing to specify in the DT. There couldn't be a devicetree node for
> >> the message queue client because that client is only exists once the VM
> >> is created by userspace.
> >>
> > The underlying "physical channel" is the synchronous SMC instruction,
> > which remains 1 irrespective of the number of mailbox instances
> > created.
>
> I disagree that the physical channel is the SMC instruction. Regardless
> though, there are num_online_cpus() "physical channels" with this
> perspective.
>
> > So basically you are sharing one resource among users. Why doesn't the
> > RM request the "smc instruction" channel once and share it among
> > users?
>
> I suppose in this scenario, a single mailbox channel would represent all
> message queues? This would cause Linux to serialize *all* message queue
> hypercalls. Sorry, I can only think negative implications.
>
> Error handling needs to move into clients: if a TX message queue becomes
> full or an RX message queue becomes empty, then we'll need to return
> error back to the client right away. The clients would need to register
> for the RTS/RTR interrupts to know when to send/receive messages and
> have retry error handling. If the mailbox controller retried for the
> clients as currently proposed, then we could get into a scenario where a
> message queue could never be ready to send/receive and thus stuck
> forever trying to process that message. The effect here would be that
> the mailbox controller becomes a wrapper to some SMC instructions that
> aren't related at the SMC instruction level.
>
> A single channel would limit performance of SMP systems because only one
> core could send/receive a message. There is no such limitation for
> message queues to behave like this.
>
This is just an illusion. If Gunyah can handle multiple calls from a
VM parallely, even with the "bind-client-to-channel" hack you can't
make sure different channels run on different cpu cores. If you are
ok with that, you could simply populate a mailbox controller with N
channels and allocate them in any order the clients ask.
-j
On Tue, Nov 01, 2022 at 05:12:58PM -0700, Elliot Berman wrote:
>
>
> On 11/1/2022 11:02 AM, Greg Kroah-Hartman wrote:
> > On Wed, Oct 26, 2022 at 11:58:35AM -0700, Elliot Berman wrote:
> > > The resource manager is a special virtual machine which is always
> > > running on a Gunyah system. It provides APIs for creating and destroying
> > > VMs, secure memory management, sharing/lending of memory between VMs,
> > > and setup of inter-VM communication. Calls to the resource manager are
> > > made via message queues.
> > >
> > > This patch implements the basic probing and RPC mechanism to make those
> > > API calls. Request/response calls can be made with gh_rm_call.
> > > Drivers can also register to notifications pushed by RM via
> > > gh_rm_register_notifier
> > >
> > > Specific API calls that resource manager supports will be implemented in
> > > subsequent patches.
> > >
> > > Signed-off-by: Elliot Berman <[email protected]>
> > > ---
> > > MAINTAINERS | 2 +-
> > > drivers/virt/gunyah/Kconfig | 15 +
> > > drivers/virt/gunyah/Makefile | 3 +
> > > drivers/virt/gunyah/rsc_mgr.c | 602 +++++++++++++++++++++++++++++++++
> > > drivers/virt/gunyah/rsc_mgr.h | 34 ++
> > > include/linux/gunyah_rsc_mgr.h | 26 ++
> > > 6 files changed, 681 insertions(+), 1 deletion(-)
> > > create mode 100644 drivers/virt/gunyah/rsc_mgr.c
> > > create mode 100644 drivers/virt/gunyah/rsc_mgr.h
> > > create mode 100644 include/linux/gunyah_rsc_mgr.h
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 586539eadd3b..e072a0d2e553 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -8945,7 +8945,7 @@ F: Documentation/virt/gunyah/
> > > F: arch/arm64/gunyah/
> > > F: drivers/mailbox/gunyah-msgq.c
> > > F: drivers/virt/gunyah/
> > > -F: include/linux/gunyah.h
> > > +F: include/linux/gunyah*.h
> > > HABANALABS PCI DRIVER
> > > M: Oded Gabbay <[email protected]>
> > > diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
> > > index 127156a678a6..4de88d80aa7b 100644
> > > --- a/drivers/virt/gunyah/Kconfig
> > > +++ b/drivers/virt/gunyah/Kconfig
> > > @@ -10,3 +10,18 @@ config GUNYAH
> > > Say Y/M here to enable the drivers needed to interact in a Gunyah
> > > virtual environment.
> > > +
> > > +config GUNYAH_RESORUCE_MANAGER
> > > + tristate "Gunyah Resource Manager"
> > > + select MAILBOX
> > > + select GUNYAH_MESSAGE_QUEUES
> > > + depends on GUNYAH
> > > + default y
> >
> > You only have "default y" if your machine can not boot without it.
> > Please do not add that here.
> >
>
> There's a guideline in Documentation/kbuild/kconfig-language.rst to provide
> some sane defaults for subdriver behavior. Here, CONFIG_GUNYAH is default n.
> It's unlikely for someone to want to have Linux with base Gunyah support
> (hypercalls and hypervisor detection) without also having the Resource
> Manager driver. If it's better, I could change to default m?
Why is this a separate build option at all anyway? If you want
CONFIG_GUNYAH why would you ever turn this off? So why even allow it to
be an option? Just always built it depending on the main option.
> > > +/* Resource Manager Header */
> > > +struct gh_rm_rpc_hdr {
> > > + u8 version : 4, hdr_words : 4;
> > > + u8 type : 2, fragments : 6;
> >
> > Ick, that's hard to read. One variable per line please?
>
> Ack.
>
> > And why the bit packed stuff? Are you sure this is the way to do this?
> > Why not use a bitmask instead?
> >
>
> I felt bit packed implementation is cleaner and easier to map to
> understanding what the fields are used for.
Ah, so this isn't what is on the "wire", then don't use a bitfield like
this, use a real variable and that will be faster and simpler to
understand.
> > > +static struct gh_rsc_mgr *__rsc_mgr;
> >
> > Sorry, no, you don't get to just limit yourself to one of these. Please
> > make this properly handle any number of "resource managers", static
> > variables like this is not ok.
> >
>
> There will only ever be one resource manager. optee, psci, and qcom_scm use
> a similar approach.
And all of those are also wrong.
There is no need for this variable at all, you are doing extra work to
make this a "single" device. Just always work off of the device that
the driver core gave you and all is good and you will have no limits on
how many different ones you eventually get. It will be less code
overall, so it's the right thing to do.
> > > +SRCU_NOTIFIER_HEAD_STATIC(gh_rm_notifier);
> >
> > Why do you need a notifier list?
> >
> > Who will register for this? For what? Why?
> >
>
> The majority of notifications that RM sends to Linux will be related to VM
> state, e.g. "VM crashed." I've not added the handling in VM manager yet to
> reduce the number of patches in this series. It was used in the previous
> series for the console driver. I can remove for now and re-introduce it once
> VM manager makes use?
Please remove if you are not using it. Notifier lists are almost always
wrong when it comes to the driver model, so please don't add them now,
we can discuss it later if you feel it really needs to be introduced
then.
> > > +static struct platform_driver gh_rm_driver = {
> > > + .probe = gh_rm_drv_probe,
> > > + .remove = gh_rm_drv_remove,
> > > + .driver = {
> > > + .name = "gh_rsc_mgr",
> > > + .of_match_table = gh_rm_of_match,
> > > + },
> >
> > Wait, why is this a platform driver? This is binding to a real device
> > on a real bus, not a random platform description in DT, right?
>
> This a binding for a real device and not a "random platform description" in
> DT to get the driver probed.
>
> > Or is it controlled by your DT? I can't figure that out here, sorry.
>
> There is some info in Patch 2 about why the DT node exists and how it looks.
> Essentially, The DT node is provided by Gunyah during boot and describes how
> Linux can communicate with resource manager.
Ick, ok, for now let's leave this alone but for dynamic devices, you
should never use a platform device. All devices that hang off of this
controller better not be platform devices, but belong to the bus type of
your new bus, right?
thanks,
greg k-h
On Wed, Oct 26, 2022 at 11:58:38AM -0700, Elliot Berman wrote:
> +#define GH_CREATE_VM _IO(GH_IOCTL_TYPE, 0x40) /* Returns a Gunyah VM fd */
Why 0x40? Why not just use the same KVM ioctl numbers and names as you
are doing the same thing as them, right?
Normally your first ioctl is "0x01", not "0x40", so this feels really
odd.
thanks,
greg k-h
On Wed, Oct 26, 2022, at 20:58, Elliot Berman wrote:
> +static const struct file_operations gh_vm_fops = {
> + .unlocked_ioctl = gh_vm_ioctl,
> + .release = gh_vm_release,
> + .llseek = noop_llseek,
> +};
There should be a .compat_ioctl entry here, otherwise it is
impossible to use from 32-bit tasks. If all commands have
arguments passed through a pointer to a properly defined
structure, you can just set it to compat_ptr_ioctl.
> +static long gh_dev_ioctl_create_vm(unsigned long arg)
> +{
> + struct gunyah_vm *ghvm;
> + struct file *file;
> + int fd, err;
> +
> + /* arg reserved for future use. */
> + if (arg)
> + return -EINVAL;
Do you have something specific in mind here? If 'create'
is the only command you support, and it has no arguments,
it would be easier to do it implicitly during open() and
have each fd opened from /dev/gunyah represent a new VM.
> + ghvm = gunyah_vm_alloc();
> + if (IS_ERR_OR_NULL(ghvm))
> + return PTR_ERR(ghvm) ? : -ENOMEM;
If you find yourself using IS_ERR_OR_NULL(), you have
usually made a mistake. In this case, the gunyah_vm_alloc()
function is badly defined and should just return -ENOMEM
for an allocation failure.
> +static struct gunyah_rsc_mgr_device_id vm_mgr_ids[] = {
> + { .name = GH_RM_DEVICE_VM_MGR },
> + {}
> +};
> +MODULE_DEVICE_TABLE(gunyah_rsc_mgr, vm_mgr_ids);
> +
> +static struct gh_rm_driver vm_mgr_drv = {
> + .drv = {
> + .name = KBUILD_MODNAME,
> + .probe = vm_mgr_probe,
> + .remove = vm_mgr_remove,
> + },
> + .id_table = vm_mgr_ids,
> +};
> +module_gh_rm_driver(vm_mgr_drv);
It looks like the gunyah_rsc_mgr_device_id in this case is
purely internal to the kernel, so you are adding abstraction
layers to something that does not need to be abstracted
because the host side has no corresponding concept of
devices.
I'm correct, you can just turn the entire bus/device/driver
structure within your code into simple function calls, where
the main code calls vm_mgr_probe() as an exported function
instead of creating a device.
Arnd
On Wed, Oct 26, 2022 at 11:58:46AM -0700, Elliot Berman wrote:
> diff --git a/Documentation/virt/gunyah/vm-manager.rst b/Documentation/virt/gunyah/vm-manager.rst
> new file mode 100644
> index 000000000000..c232ba05de7e
> --- /dev/null
> +++ b/Documentation/virt/gunyah/vm-manager.rst
> @@ -0,0 +1,94 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +=======================
> +Virtual Machine Manager
> +=======================
> +
> +The Gunyah Virtual Machine Manager is a Linux driver to support launching virtual machines.
> +
> +Summary
> +=======
> +
> +Gunyah VMM presently supports launching non-proxy scheduled Linux-like virtual machines.
> +
> +Sample Userspace VMM
> +====================
> +
> +A sample userspace VMM is included in samples/gunyah/ along with a sample minimal devicetree
> +that can be used to launch a Linux-like virtual machine under Gunyah. To build this sample, enable
> +CONFIG_SAMPLE_GUNYAH.
> +
> +IOCTLs and userspace VMM flows
> +==============================
> +
> +The kernel exposes a char device interface at /dev/gunyah.
> +
> +To create a VM, use the GH_CREATE_VM ioctl. A successful call will return a "Gunyah VM" file descriptor.
> +
> +/dev/gunyah API Descriptions
> +----------------------------
> +
> +GH_CREATE_VM
> +~~~~~~~~~~~~
> +
> +Creates a Gunyah VM. The argument is reserved for future use and must be 0.
> +
> +Gunyah VM API Descriptions
> +--------------------------
> +
> +GH_VM_SET_USER_MEM_REGION
> +~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +::
> +
> + struct gh_userspace_memory_region {
> + __u32 label;
> + __u32 flags;
> + __u64 guest_phys_addr;
> + __u64 memory_size;
> + __u64 userspace_addr;
> + };
> +
> +This ioctl allows the user to create or delete a memory parcel for a guest
> +virtual machine. Each memory region is uniquely identified by a label;
> +attempting to create two memory regions with the same label is not allowed.
> +
> +While VMM is guest-agnostic and allows runtime addition of memory regions,
> +Linux guest virtual machines do not support accepting memory regions at runtime.
> +Thus, memory regions should be provided before starting the VM and the VM
> +configured to accept those memory regions at boot-up.
> +
> +The guest physical address is used by Linux to check the requested user regions
> +do not overlap and to help find a corresponding memory region for calls like
> +GH_VM_SET_DTB_CONFIG.
> +
> +To delete a memory region, call GH_VM_SET_USER_MEM_REGION with label set to the
> +memory region of interest and memory_size set to 0.
> +
> +The flags field of gh_userspace_memory_region can set the following bits. All
> +other bits must be 0 and are reserved for future use. The ioctl will return
> +-EINVAL if an unsupported bit is detected.
> +
> + - GH_MEM_ALLOW_READ/GH_MEM_ALLOW_WRITE/GH_MEM_ALLOW_EXEC sets read/write/exec permissions
> + for the guest, respectively.
> +
> + - GH_MEM_LENT means that the memory will be unmapped from the host and be unaccessible by
> + the host while the guest has the region.
> +
> +GH_VM_SET_DTB_CONFIG
> +~~~~~~~~~~~~~~~~~~~~
> +
> +::
> +
> + struct gh_vm_dtb_config {
> + __u64 gpa;
> + __u64 size;
> + };
> +
> +This ioctl sets the location of the VM's devicetree blob and is used by Gunyah
> +Resource Manager to allocate resources.
> +
> +GH_VM_START
> +~~~~~~~~~~~
> +
> +This ioctl starts the virtual machine.
I think the wording can be better:
---- >8 ----
diff --git a/Documentation/virt/gunyah/vm-manager.rst b/Documentation/virt/gunyah/vm-manager.rst
index c232ba05de7e96..772fd970b91d7e 100644
--- a/Documentation/virt/gunyah/vm-manager.rst
+++ b/Documentation/virt/gunyah/vm-manager.rst
@@ -4,18 +4,15 @@
Virtual Machine Manager
=======================
-The Gunyah Virtual Machine Manager is a Linux driver to support launching virtual machines.
-
-Summary
-=======
-
-Gunyah VMM presently supports launching non-proxy scheduled Linux-like virtual machines.
+The Gunyah Virtual Machine Manager is a Linux driver for launching virtual
+machines using Gunyah. It presently supports launching non-proxy scheduled
+Linux-like virtual machines.
Sample Userspace VMM
====================
-A sample userspace VMM is included in samples/gunyah/ along with a sample minimal devicetree
-that can be used to launch a Linux-like virtual machine under Gunyah. To build this sample, enable
+A sample userspace VMM is included in samples/gunyah/ along with a minimal
+devicetree that can be used to launch a VM. To build this sample, enable
CONFIG_SAMPLE_GUNYAH.
IOCTLs and userspace VMM flows
@@ -23,7 +20,8 @@ IOCTLs and userspace VMM flows
The kernel exposes a char device interface at /dev/gunyah.
-To create a VM, use the GH_CREATE_VM ioctl. A successful call will return a "Gunyah VM" file descriptor.
+To create a VM, use the GH_CREATE_VM ioctl. A successful call will return a
+"Gunyah VM" file descriptor.
/dev/gunyah API Descriptions
----------------------------
@@ -51,29 +49,28 @@ GH_VM_SET_USER_MEM_REGION
This ioctl allows the user to create or delete a memory parcel for a guest
virtual machine. Each memory region is uniquely identified by a label;
-attempting to create two memory regions with the same label is not allowed.
+attempting to create two regions with the same label is not allowed.
While VMM is guest-agnostic and allows runtime addition of memory regions,
Linux guest virtual machines do not support accepting memory regions at runtime.
-Thus, memory regions should be provided before starting the VM and the VM
-configured to accept those memory regions at boot-up.
+Thus, memory regions should be provided before starting the VM and the VM must
+be configured to accept these at boot-up.
-The guest physical address is used by Linux to check the requested user regions
-do not overlap and to help find a corresponding memory region for calls like
-GH_VM_SET_DTB_CONFIG.
+The guest physical address is used by Linux kernel to check that the requested
+user regions do not overlap and to help find the corresponding memory region
+for calls like GH_VM_SET_DTB_CONFIG.
To delete a memory region, call GH_VM_SET_USER_MEM_REGION with label set to the
-memory region of interest and memory_size set to 0.
+desired region and memory_size set to 0.
-The flags field of gh_userspace_memory_region can set the following bits. All
+The flags field of gh_userspace_memory_region accepts the following bits. All
other bits must be 0 and are reserved for future use. The ioctl will return
-EINVAL if an unsupported bit is detected.
- - GH_MEM_ALLOW_READ/GH_MEM_ALLOW_WRITE/GH_MEM_ALLOW_EXEC sets read/write/exec permissions
- for the guest, respectively.
-
- - GH_MEM_LENT means that the memory will be unmapped from the host and be unaccessible by
- the host while the guest has the region.
+ - GH_MEM_ALLOW_READ/GH_MEM_ALLOW_WRITE/GH_MEM_ALLOW_EXEC sets read/write/exec
+ permissions for the guest, respectively.
+ - GH_MEM_LENT means that the memory will be unmapped from the host and be
+ unaccessible by the host while the guest has the region.
GH_VM_SET_DTB_CONFIG
~~~~~~~~~~~~~~~~~~~~
@@ -91,4 +88,4 @@ Resource Manager to allocate resources.
GH_VM_START
~~~~~~~~~~~
-This ioctl starts the virtual machine.
+This ioctl starts the VM.
Thanks.
--
An old man doll... just what I always wanted! - Clara
On 11/2/2022 6:05 AM, Bagas Sanjaya wrote:
> On Wed, Oct 26, 2022 at 11:58:46AM -0700, Elliot Berman wrote:
>> diff --git a/Documentation/virt/gunyah/vm-manager.rst b/Documentation/virt/gunyah/vm-manager.rst
>> new file mode 100644
>> index 000000000000..c232ba05de7e
>> --- /dev/null
>> +++ b/Documentation/virt/gunyah/vm-manager.rst
>> @@ -0,0 +1,94 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +=======================
>> +Virtual Machine Manager
>> +=======================
>> +
>> +The Gunyah Virtual Machine Manager is a Linux driver to support launching virtual machines.
>> +
>> +Summary
>> +=======
>> +
>> +Gunyah VMM presently supports launching non-proxy scheduled Linux-like virtual machines.
>> +
>> +Sample Userspace VMM
>> +====================
>> +
>> +A sample userspace VMM is included in samples/gunyah/ along with a sample minimal devicetree
>> +that can be used to launch a Linux-like virtual machine under Gunyah. To build this sample, enable
>> +CONFIG_SAMPLE_GUNYAH.
>> +
>> +IOCTLs and userspace VMM flows
>> +==============================
>> +
>> +The kernel exposes a char device interface at /dev/gunyah.
>> +
>> +To create a VM, use the GH_CREATE_VM ioctl. A successful call will return a "Gunyah VM" file descriptor.
>> +
>> +/dev/gunyah API Descriptions
>> +----------------------------
>> +
>> +GH_CREATE_VM
>> +~~~~~~~~~~~~
>> +
>> +Creates a Gunyah VM. The argument is reserved for future use and must be 0.
>> +
>> +Gunyah VM API Descriptions
>> +--------------------------
>> +
>> +GH_VM_SET_USER_MEM_REGION
>> +~~~~~~~~~~~~~~~~~~~~~~~~~
>> +
>> +::
>> +
>> + struct gh_userspace_memory_region {
>> + __u32 label;
>> + __u32 flags;
>> + __u64 guest_phys_addr;
>> + __u64 memory_size;
>> + __u64 userspace_addr;
>> + };
>> +
>> +This ioctl allows the user to create or delete a memory parcel for a guest
>> +virtual machine. Each memory region is uniquely identified by a label;
>> +attempting to create two memory regions with the same label is not allowed.
>> +
>> +While VMM is guest-agnostic and allows runtime addition of memory regions,
>> +Linux guest virtual machines do not support accepting memory regions at runtime.
>> +Thus, memory regions should be provided before starting the VM and the VM
>> +configured to accept those memory regions at boot-up.
>> +
>> +The guest physical address is used by Linux to check the requested user regions
>> +do not overlap and to help find a corresponding memory region for calls like
>> +GH_VM_SET_DTB_CONFIG.
>> +
>> +To delete a memory region, call GH_VM_SET_USER_MEM_REGION with label set to the
>> +memory region of interest and memory_size set to 0.
>> +
>> +The flags field of gh_userspace_memory_region can set the following bits. All
>> +other bits must be 0 and are reserved for future use. The ioctl will return
>> +-EINVAL if an unsupported bit is detected.
>> +
>> + - GH_MEM_ALLOW_READ/GH_MEM_ALLOW_WRITE/GH_MEM_ALLOW_EXEC sets read/write/exec permissions
>> + for the guest, respectively.
>> +
>> + - GH_MEM_LENT means that the memory will be unmapped from the host and be unaccessible by
>> + the host while the guest has the region.
>> +
>> +GH_VM_SET_DTB_CONFIG
>> +~~~~~~~~~~~~~~~~~~~~
>> +
>> +::
>> +
>> + struct gh_vm_dtb_config {
>> + __u64 gpa;
>> + __u64 size;
>> + };
>> +
>> +This ioctl sets the location of the VM's devicetree blob and is used by Gunyah
>> +Resource Manager to allocate resources.
>> +
>> +GH_VM_START
>> +~~~~~~~~~~~
>> +
>> +This ioctl starts the virtual machine.
>
> I think the wording can be better:
>
> ---- >8 ----
>
> diff --git a/Documentation/virt/gunyah/vm-manager.rst b/Documentation/virt/gunyah/vm-manager.rst
> index c232ba05de7e96..772fd970b91d7e 100644
> --- a/Documentation/virt/gunyah/vm-manager.rst
> +++ b/Documentation/virt/gunyah/vm-manager.rst
> @@ -4,18 +4,15 @@
> Virtual Machine Manager
> =======================
>
> -The Gunyah Virtual Machine Manager is a Linux driver to support launching virtual machines.
> -
> -Summary
> -=======
> -
> -Gunyah VMM presently supports launching non-proxy scheduled Linux-like virtual machines.
> +The Gunyah Virtual Machine Manager is a Linux driver for launching virtual
> +machines using Gunyah. It presently supports launching non-proxy scheduled
> +Linux-like virtual machines.
>
> Sample Userspace VMM
> ====================
>
> -A sample userspace VMM is included in samples/gunyah/ along with a sample minimal devicetree
> -that can be used to launch a Linux-like virtual machine under Gunyah. To build this sample, enable
> +A sample userspace VMM is included in samples/gunyah/ along with a minimal
> +devicetree that can be used to launch a VM. To build this sample, enable
> CONFIG_SAMPLE_GUNYAH.
>
> IOCTLs and userspace VMM flows
> @@ -23,7 +20,8 @@ IOCTLs and userspace VMM flows
>
> The kernel exposes a char device interface at /dev/gunyah.
>
> -To create a VM, use the GH_CREATE_VM ioctl. A successful call will return a "Gunyah VM" file descriptor.
> +To create a VM, use the GH_CREATE_VM ioctl. A successful call will return a
> +"Gunyah VM" file descriptor.
>
> /dev/gunyah API Descriptions
> ----------------------------
> @@ -51,29 +49,28 @@ GH_VM_SET_USER_MEM_REGION
>
> This ioctl allows the user to create or delete a memory parcel for a guest
> virtual machine. Each memory region is uniquely identified by a label;
> -attempting to create two memory regions with the same label is not allowed.
> +attempting to create two regions with the same label is not allowed.
>
> While VMM is guest-agnostic and allows runtime addition of memory regions,
> Linux guest virtual machines do not support accepting memory regions at runtime.
> -Thus, memory regions should be provided before starting the VM and the VM
> -configured to accept those memory regions at boot-up.
> +Thus, memory regions should be provided before starting the VM and the VM must
> +be configured to accept these at boot-up.
>
> -The guest physical address is used by Linux to check the requested user regions
> -do not overlap and to help find a corresponding memory region for calls like
> -GH_VM_SET_DTB_CONFIG.
> +The guest physical address is used by Linux kernel to check that the requested
> +user regions do not overlap and to help find the corresponding memory region
> +for calls like GH_VM_SET_DTB_CONFIG.
>
> To delete a memory region, call GH_VM_SET_USER_MEM_REGION with label set to the
> -memory region of interest and memory_size set to 0.
> +desired region and memory_size set to 0.
>
> -The flags field of gh_userspace_memory_region can set the following bits. All
> +The flags field of gh_userspace_memory_region accepts the following bits. All
> other bits must be 0 and are reserved for future use. The ioctl will return
> -EINVAL if an unsupported bit is detected.
>
> - - GH_MEM_ALLOW_READ/GH_MEM_ALLOW_WRITE/GH_MEM_ALLOW_EXEC sets read/write/exec permissions
> - for the guest, respectively.
> -
> - - GH_MEM_LENT means that the memory will be unmapped from the host and be unaccessible by
> - the host while the guest has the region.
> + - GH_MEM_ALLOW_READ/GH_MEM_ALLOW_WRITE/GH_MEM_ALLOW_EXEC sets read/write/exec
> + permissions for the guest, respectively.
> + - GH_MEM_LENT means that the memory will be unmapped from the host and be
> + unaccessible by the host while the guest has the region.
One side question -- before, you asked that I add newline between the
list entries. Here, you've removed them. When do I need the extra
newline vs not?
https://lore.kernel.org/all/[email protected]/
>
> GH_VM_SET_DTB_CONFIG
> ~~~~~~~~~~~~~~~~~~~~
> @@ -91,4 +88,4 @@ Resource Manager to allocate resources.
> GH_VM_START
> ~~~~~~~~~~~
>
> -This ioctl starts the virtual machine.
> +This ioctl starts the VM.
>
> Thanks.
>
Thanks for reviewing and providing all the suggestions. I've applied all
of them.
Hi Jassi,
On 11/1/2022 7:01 PM, Jassi Brar wrote:
> On Tue, Nov 1, 2022 at 7:12 PM Elliot Berman <[email protected]> wrote:
>>
>>
>>
>> On 11/1/2022 2:58 PM, Jassi Brar wrote:
>>> On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
>>>>
>>>>
>>>>
>>>> On 11/1/2022 9:23 AM, Jassi Brar wrote:
>>>>> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
>>>>>>
>>>>>> Hi Jassi,
>>>>>>
>>>>>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
>>>>>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
>>>>>> <[email protected]> wrote:
>>>>>> > .....
>>>>>> >> +
>>>>>> >> + gunyah-resource-mgr@0 {
>>>>>> >> + compatible = "gunyah-resource-manager-1-0",
>>>>>> "gunyah-resource-manager";
>>>>>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
>>>>>> full IRQ */
>>>>>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
>>>>>> empty IRQ */
>>>>>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
>>>>>> >> + /* TX, RX cap ids */
>>>>>> >> + };
>>>>>> >>
>>>>>> > All these resources are used only by the mailbox controller driver.
>>>>>> > So, this should be the mailbox controller node, rather than the
>>>>>> > mailbox user.> One option is to load gunyah-resource-manager as a
>>>>>> module that relies
>>>>>> > on the gunyah-mailbox provider. That would also avoid the "Allow
>>>>>> > direct registration to a channel" hack patch.
>>>>>>
>>>>>> A message queue to another guest VM wouldn't be known at boot time and
>>>>>> thus couldn't be described on the devicetree.
>>>>>>
>>>>> I think you need to implement of_xlate() ... or please tell me what
>>>>> exactly you need to specify in the dt.
>>>>
>>>> Dynamically created virtual machines can't be known on the dt, so there
>>>> is nothing to specify in the DT. There couldn't be a devicetree node for
>>>> the message queue client because that client is only exists once the VM
>>>> is created by userspace.
>>>>
>>> The underlying "physical channel" is the synchronous SMC instruction,
>>> which remains 1 irrespective of the number of mailbox instances
>>> created.
>>
>> I disagree that the physical channel is the SMC instruction. Regardless
>> though, there are num_online_cpus() "physical channels" with this
>> perspective.
>>
>>> So basically you are sharing one resource among users. Why doesn't the
>>> RM request the "smc instruction" channel once and share it among
>>> users?
>>
>> I suppose in this scenario, a single mailbox channel would represent all
>> message queues? This would cause Linux to serialize *all* message queue
>> hypercalls. Sorry, I can only think negative implications.
>>
>> Error handling needs to move into clients: if a TX message queue becomes
>> full or an RX message queue becomes empty, then we'll need to return
>> error back to the client right away. The clients would need to register
>> for the RTS/RTR interrupts to know when to send/receive messages and
>> have retry error handling. If the mailbox controller retried for the
>> clients as currently proposed, then we could get into a scenario where a
>> message queue could never be ready to send/receive and thus stuck
>> forever trying to process that message. The effect here would be that
>> the mailbox controller becomes a wrapper to some SMC instructions that
>> aren't related at the SMC instruction level.
>>
>> A single channel would limit performance of SMP systems because only one
>> core could send/receive a message. There is no such limitation for
>> message queues to behave like this.
>>
> This is just an illusion. If Gunyah can handle multiple calls from a
> VM parallely, even with the "bind-client-to-channel" hack you can't
> make sure different channels run on different cpu cores. If you are
> ok with that, you could simply populate a mailbox controller with N
> channels and allocate them in any order the clients ask.
I wanted to make sure I understood the ask here completely. On what
basis is N chosen? Who would be the mailbox clients?
Thanks,
Elliot
On 11/1/2022 7:53 PM, Greg Kroah-Hartman wrote:
> On Tue, Nov 01, 2022 at 05:12:58PM -0700, Elliot Berman wrote:
>>
>>
>> On 11/1/2022 11:02 AM, Greg Kroah-Hartman wrote:
>>> On Wed, Oct 26, 2022 at 11:58:35AM -0700, Elliot Berman wrote:
>>>> The resource manager is a special virtual machine which is always
>>>> running on a Gunyah system. It provides APIs for creating and destroying
>>>> VMs, secure memory management, sharing/lending of memory between VMs,
>>>> and setup of inter-VM communication. Calls to the resource manager are
>>>> made via message queues.
>>>>
>>>> This patch implements the basic probing and RPC mechanism to make those
>>>> API calls. Request/response calls can be made with gh_rm_call.
>>>> Drivers can also register to notifications pushed by RM via
>>>> gh_rm_register_notifier
>>>>
>>>> Specific API calls that resource manager supports will be implemented in
>>>> subsequent patches.
>>>>
>>>> Signed-off-by: Elliot Berman <[email protected]>
>>>> ---
>>>> MAINTAINERS | 2 +-
>>>> drivers/virt/gunyah/Kconfig | 15 +
>>>> drivers/virt/gunyah/Makefile | 3 +
>>>> drivers/virt/gunyah/rsc_mgr.c | 602 +++++++++++++++++++++++++++++++++
>>>> drivers/virt/gunyah/rsc_mgr.h | 34 ++
>>>> include/linux/gunyah_rsc_mgr.h | 26 ++
>>>> 6 files changed, 681 insertions(+), 1 deletion(-)
>>>> create mode 100644 drivers/virt/gunyah/rsc_mgr.c
>>>> create mode 100644 drivers/virt/gunyah/rsc_mgr.h
>>>> create mode 100644 include/linux/gunyah_rsc_mgr.h
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index 586539eadd3b..e072a0d2e553 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -8945,7 +8945,7 @@ F: Documentation/virt/gunyah/
>>>> F: arch/arm64/gunyah/
>>>> F: drivers/mailbox/gunyah-msgq.c
>>>> F: drivers/virt/gunyah/
>>>> -F: include/linux/gunyah.h
>>>> +F: include/linux/gunyah*.h
>>>> HABANALABS PCI DRIVER
>>>> M: Oded Gabbay <[email protected]>
>>>> diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
>>>> index 127156a678a6..4de88d80aa7b 100644
>>>> --- a/drivers/virt/gunyah/Kconfig
>>>> +++ b/drivers/virt/gunyah/Kconfig
>>>> @@ -10,3 +10,18 @@ config GUNYAH
>>>> Say Y/M here to enable the drivers needed to interact in a Gunyah
>>>> virtual environment.
>>>> +
>>>> +config GUNYAH_RESORUCE_MANAGER
>>>> + tristate "Gunyah Resource Manager"
>>>> + select MAILBOX
>>>> + select GUNYAH_MESSAGE_QUEUES
>>>> + depends on GUNYAH
>>>> + default y
>>>
>>> You only have "default y" if your machine can not boot without it.
>>> Please do not add that here.
>>>
>>
>> There's a guideline in Documentation/kbuild/kconfig-language.rst to provide
>> some sane defaults for subdriver behavior. Here, CONFIG_GUNYAH is default n.
>> It's unlikely for someone to want to have Linux with base Gunyah support
>> (hypercalls and hypervisor detection) without also having the Resource
>> Manager driver. If it's better, I could change to default m?
>
> Why is this a separate build option at all anyway? If you want
> CONFIG_GUNYAH why would you ever turn this off? So why even allow it to
> be an option? Just always built it depending on the main option.
>
Ack.
I'll also end up removing CONFIG_GUNYAH_MESSAGE_QUEUES from patch 9 as well.
>>>> +/* Resource Manager Header */
>>>> +struct gh_rm_rpc_hdr {
>>>> + u8 version : 4, hdr_words : 4;
>>>> + u8 type : 2, fragments : 6;
>>>
>>> Ick, that's hard to read. One variable per line please?
>>
>> Ack.
>>
>>> And why the bit packed stuff? Are you sure this is the way to do this?
>>> Why not use a bitmask instead?
>>>
>>
>> I felt bit packed implementation is cleaner and easier to map to
>> understanding what the fields are used for.
>
> Ah, so this isn't what is on the "wire", then don't use a bitfield like
> this, use a real variable and that will be faster and simpler to
> understand.
>
This is what's on the "wire". Whether I use bitfield or bit packed would
be functionally the same and is just a cosmetic change IMO.
>>>> +static struct gh_rsc_mgr *__rsc_mgr;
>>>
>>> Sorry, no, you don't get to just limit yourself to one of these. Please
>>> make this properly handle any number of "resource managers", static
>>> variables like this is not ok.
>>>
>>
>> There will only ever be one resource manager. optee, psci, and qcom_scm use
>> a similar approach.
>
> And all of those are also wrong.
>
> There is no need for this variable at all, you are doing extra work to
> make this a "single" device. Just always work off of the device that
> the driver core gave you and all is good and you will have no limits on
> how many different ones you eventually get. It will be less code
> overall, so it's the right thing to do.
>
Ack
>>>> +SRCU_NOTIFIER_HEAD_STATIC(gh_rm_notifier);
>>>
>>> Why do you need a notifier list?
>>>
>>> Who will register for this? For what? Why?
>>>
>>
>> The majority of notifications that RM sends to Linux will be related to VM
>> state, e.g. "VM crashed." I've not added the handling in VM manager yet to
>> reduce the number of patches in this series. It was used in the previous
>> series for the console driver. I can remove for now and re-introduce it once
>> VM manager makes use?
>
> Please remove if you are not using it. Notifier lists are almost always
> wrong when it comes to the driver model, so please don't add them now,
> we can discuss it later if you feel it really needs to be introduced
> then.
>
Ack
>>>> +static struct platform_driver gh_rm_driver = {
>>>> + .probe = gh_rm_drv_probe,
>>>> + .remove = gh_rm_drv_remove,
>>>> + .driver = {
>>>> + .name = "gh_rsc_mgr",
>>>> + .of_match_table = gh_rm_of_match,
>>>> + },
>>>
>>> Wait, why is this a platform driver? This is binding to a real device
>>> on a real bus, not a random platform description in DT, right?
>>
>> This a binding for a real device and not a "random platform description" in
>> DT to get the driver probed.
>>
>>> Or is it controlled by your DT? I can't figure that out here, sorry.
>>
>> There is some info in Patch 2 about why the DT node exists and how it looks.
>> Essentially, The DT node is provided by Gunyah during boot and describes how
>> Linux can communicate with resource manager.
>
> Ick, ok, for now let's leave this alone but for dynamic devices, you
> should never use a platform device. All devices that hang off of this
> controller better not be platform devices, but belong to the bus type of
> your new bus, right?
>
That's correct.
+Michael
On 11/1/2022 10:14 PM, Greg Kroah-Hartman wrote:
> On Wed, Oct 26, 2022 at 11:58:38AM -0700, Elliot Berman wrote:
>> +#define GH_CREATE_VM _IO(GH_IOCTL_TYPE, 0x40) /* Returns a Gunyah VM fd */
>
> Why 0x40? Why not just use the same KVM ioctl numbers and names as you
> are doing the same thing as them, right?
We've designed so that there are a few ioctls that will feel similar to
KVM ioctls since we know this design has been successful, but we don't
intend to support KVM ioctls 1:1. Gunyah has different semantics for
many of the name-identical ioctls. It seems odd to mix some re-used KVM
ioctls with novel Gunyah ioctls?
>
> Normally your first ioctl is "0x01", not "0x40", so this feels really
> odd.
>
Documentation/userspace-api/ioctl/iocl-number.rst advises to pick an
unused block. We picked ioctl code 'G' and unused sequence numbers under
that code. I'm ok to move the block around.
Thanks,
Elliot
On 11/2/2022 12:31 AM, Arnd Bergmann wrote:
> On Wed, Oct 26, 2022, at 20:58, Elliot Berman wrote:
>
>> +static const struct file_operations gh_vm_fops = {
>> + .unlocked_ioctl = gh_vm_ioctl,
>> + .release = gh_vm_release,
>> + .llseek = noop_llseek,
>> +};
>
> There should be a .compat_ioctl entry here, otherwise it is
> impossible to use from 32-bit tasks. If all commands have
> arguments passed through a pointer to a properly defined
> structure, you can just set it to compat_ptr_ioctl.
>
Ack.
>> +static long gh_dev_ioctl_create_vm(unsigned long arg)
>> +{
>> + struct gunyah_vm *ghvm;
>> + struct file *file;
>> + int fd, err;
>> +
>> + /* arg reserved for future use. */
>> + if (arg)
>> + return -EINVAL;
>
> Do you have something specific in mind here? If 'create'
> is the only command you support, and it has no arguments,
> it would be easier to do it implicitly during open() and
> have each fd opened from /dev/gunyah represent a new VM.
>
I'd like the argument here to support different types of virtual
machines. I want to leave open what "different types" can be in case
something new comes up in the future, but immediately "different type"
would correspond to a few different authentication mechanisms for
virtual machines that Gunyah supports.
In this series, I'm only supporting unauthenticated virtual machines
because they are the simplest to get up and running from a Linux
userspace. When I introduce the other authentication mechanisms, I'll
expand much more on how they work, but I'll give quick overview here.
Other authentication mechanisms that are currently supported by Gunyah
are "protected VM" and, on Qualcomm platforms, "PIL/carveout VMs".
Protected VMs are *loosely* similar to the protected firmware design for
KVM and intended to support Android virtualization use cases.
PIL/carevout VMs are special virtual machines that can run on Qualcomm
firmware which authenticate in a way similar to remoteproc firmware (MDT
loader).
>> + ghvm = gunyah_vm_alloc();
>> + if (IS_ERR_OR_NULL(ghvm))
>> + return PTR_ERR(ghvm) ? : -ENOMEM;
>
> If you find yourself using IS_ERR_OR_NULL(), you have
> usually made a mistake. In this case, the gunyah_vm_alloc()
> function is badly defined and should just return -ENOMEM
> for an allocation failure.
>
Ack
>> +static struct gunyah_rsc_mgr_device_id vm_mgr_ids[] = {
>> + { .name = GH_RM_DEVICE_VM_MGR },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(gunyah_rsc_mgr, vm_mgr_ids);
>> +
>> +static struct gh_rm_driver vm_mgr_drv = {
>> + .drv = {
>> + .name = KBUILD_MODNAME,
>> + .probe = vm_mgr_probe,
>> + .remove = vm_mgr_remove,
>> + },
>> + .id_table = vm_mgr_ids,
>> +};
>> +module_gh_rm_driver(vm_mgr_drv);
>
> It looks like the gunyah_rsc_mgr_device_id in this case is
> purely internal to the kernel, so you are adding abstraction
> layers to something that does not need to be abstracted
> because the host side has no corresponding concept of
> devices.
>
> I'm correct, you can just turn the entire bus/device/driver
> structure within your code into simple function calls, where
> the main code calls vm_mgr_probe() as an exported function
> instead of creating a device.
Ack. I can do this, although I am nervous about this snowballing into a
situation where I have a mega-module.
> Please stop beating everything in a single module.
https://lore.kernel.org/all/[email protected]/
Thanks,
Elliot
On Wed, Nov 2, 2022 at 1:06 PM Elliot Berman <[email protected]> wrote:
>
> Hi Jassi,
>
> On 11/1/2022 7:01 PM, Jassi Brar wrote:
> > On Tue, Nov 1, 2022 at 7:12 PM Elliot Berman <[email protected]> wrote:
> >>
> >>
> >>
> >> On 11/1/2022 2:58 PM, Jassi Brar wrote:
> >>> On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
> >>>>
> >>>>
> >>>>
> >>>> On 11/1/2022 9:23 AM, Jassi Brar wrote:
> >>>>> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
> >>>>>>
> >>>>>> Hi Jassi,
> >>>>>>
> >>>>>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
> >>>>>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
> >>>>>> <[email protected]> wrote:
> >>>>>> > .....
> >>>>>> >> +
> >>>>>> >> + gunyah-resource-mgr@0 {
> >>>>>> >> + compatible = "gunyah-resource-manager-1-0",
> >>>>>> "gunyah-resource-manager";
> >>>>>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
> >>>>>> full IRQ */
> >>>>>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
> >>>>>> empty IRQ */
> >>>>>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
> >>>>>> >> + /* TX, RX cap ids */
> >>>>>> >> + };
> >>>>>> >>
> >>>>>> > All these resources are used only by the mailbox controller driver.
> >>>>>> > So, this should be the mailbox controller node, rather than the
> >>>>>> > mailbox user.> One option is to load gunyah-resource-manager as a
> >>>>>> module that relies
> >>>>>> > on the gunyah-mailbox provider. That would also avoid the "Allow
> >>>>>> > direct registration to a channel" hack patch.
> >>>>>>
> >>>>>> A message queue to another guest VM wouldn't be known at boot time and
> >>>>>> thus couldn't be described on the devicetree.
> >>>>>>
> >>>>> I think you need to implement of_xlate() ... or please tell me what
> >>>>> exactly you need to specify in the dt.
> >>>>
> >>>> Dynamically created virtual machines can't be known on the dt, so there
> >>>> is nothing to specify in the DT. There couldn't be a devicetree node for
> >>>> the message queue client because that client is only exists once the VM
> >>>> is created by userspace.
> >>>>
> >>> The underlying "physical channel" is the synchronous SMC instruction,
> >>> which remains 1 irrespective of the number of mailbox instances
> >>> created.
> >>
> >> I disagree that the physical channel is the SMC instruction. Regardless
> >> though, there are num_online_cpus() "physical channels" with this
> >> perspective.
> >>
> >>> So basically you are sharing one resource among users. Why doesn't the
> >>> RM request the "smc instruction" channel once and share it among
> >>> users?
> >>
> >> I suppose in this scenario, a single mailbox channel would represent all
> >> message queues? This would cause Linux to serialize *all* message queue
> >> hypercalls. Sorry, I can only think negative implications.
> >>
> >> Error handling needs to move into clients: if a TX message queue becomes
> >> full or an RX message queue becomes empty, then we'll need to return
> >> error back to the client right away. The clients would need to register
> >> for the RTS/RTR interrupts to know when to send/receive messages and
> >> have retry error handling. If the mailbox controller retried for the
> >> clients as currently proposed, then we could get into a scenario where a
> >> message queue could never be ready to send/receive and thus stuck
> >> forever trying to process that message. The effect here would be that
> >> the mailbox controller becomes a wrapper to some SMC instructions that
> >> aren't related at the SMC instruction level.
> >>
> >> A single channel would limit performance of SMP systems because only one
> >> core could send/receive a message. There is no such limitation for
> >> message queues to behave like this.
> >>
> > This is just an illusion. If Gunyah can handle multiple calls from a
> > VM parallely, even with the "bind-client-to-channel" hack you can't
> > make sure different channels run on different cpu cores. If you are
> > ok with that, you could simply populate a mailbox controller with N
> > channels and allocate them in any order the clients ask.
>
> I wanted to make sure I understood the ask here completely. On what
> basis is N chosen? Who would be the mailbox clients?
>
A channel structure is cheap, so any number that is not likely to run
out. Say you have 10 possible users in a VM, set N=16. I know ideally
it should be precise and flexible but the gain in simplicity makes the
trade-off very acceptable.
thanks.
On 11/2/2022 11:24 AM, Jassi Brar wrote:
> On Wed, Nov 2, 2022 at 1:06 PM Elliot Berman <[email protected]> wrote:
>>
>> Hi Jassi,
>>
>> On 11/1/2022 7:01 PM, Jassi Brar wrote:
>>> On Tue, Nov 1, 2022 at 7:12 PM Elliot Berman <[email protected]> wrote:
>>>>
>>>>
>>>>
>>>> On 11/1/2022 2:58 PM, Jassi Brar wrote:
>>>>> On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 11/1/2022 9:23 AM, Jassi Brar wrote:
>>>>>>> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
>>>>>>>>
>>>>>>>> Hi Jassi,
>>>>>>>>
>>>>>>>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
>>>>>>>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
>>>>>>>> <[email protected]> wrote:
>>>>>>>> > .....
>>>>>>>> >> +
>>>>>>>> >> + gunyah-resource-mgr@0 {
>>>>>>>> >> + compatible = "gunyah-resource-manager-1-0",
>>>>>>>> "gunyah-resource-manager";
>>>>>>>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
>>>>>>>> full IRQ */
>>>>>>>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
>>>>>>>> empty IRQ */
>>>>>>>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
>>>>>>>> >> + /* TX, RX cap ids */
>>>>>>>> >> + };
>>>>>>>> >>
>>>>>>>> > All these resources are used only by the mailbox controller driver.
>>>>>>>> > So, this should be the mailbox controller node, rather than the
>>>>>>>> > mailbox user.> One option is to load gunyah-resource-manager as a
>>>>>>>> module that relies
>>>>>>>> > on the gunyah-mailbox provider. That would also avoid the "Allow
>>>>>>>> > direct registration to a channel" hack patch.
>>>>>>>>
>>>>>>>> A message queue to another guest VM wouldn't be known at boot time and
>>>>>>>> thus couldn't be described on the devicetree.
>>>>>>>>
>>>>>>> I think you need to implement of_xlate() ... or please tell me what
>>>>>>> exactly you need to specify in the dt.
>>>>>>
>>>>>> Dynamically created virtual machines can't be known on the dt, so there
>>>>>> is nothing to specify in the DT. There couldn't be a devicetree node for
>>>>>> the message queue client because that client is only exists once the VM
>>>>>> is created by userspace.
>>>>>>
>>>>> The underlying "physical channel" is the synchronous SMC instruction,
>>>>> which remains 1 irrespective of the number of mailbox instances
>>>>> created.
>>>>
>>>> I disagree that the physical channel is the SMC instruction. Regardless
>>>> though, there are num_online_cpus() "physical channels" with this
>>>> perspective.
>>>>
>>>>> So basically you are sharing one resource among users. Why doesn't the
>>>>> RM request the "smc instruction" channel once and share it among
>>>>> users?
>>>>
>>>> I suppose in this scenario, a single mailbox channel would represent all
>>>> message queues? This would cause Linux to serialize *all* message queue
>>>> hypercalls. Sorry, I can only think negative implications.
>>>>
>>>> Error handling needs to move into clients: if a TX message queue becomes
>>>> full or an RX message queue becomes empty, then we'll need to return
>>>> error back to the client right away. The clients would need to register
>>>> for the RTS/RTR interrupts to know when to send/receive messages and
>>>> have retry error handling. If the mailbox controller retried for the
>>>> clients as currently proposed, then we could get into a scenario where a
>>>> message queue could never be ready to send/receive and thus stuck
>>>> forever trying to process that message. The effect here would be that
>>>> the mailbox controller becomes a wrapper to some SMC instructions that
>>>> aren't related at the SMC instruction level.
>>>>
>>>> A single channel would limit performance of SMP systems because only one
>>>> core could send/receive a message. There is no such limitation for
>>>> message queues to behave like this.
>>>>
>>> This is just an illusion. If Gunyah can handle multiple calls from a
>>> VM parallely, even with the "bind-client-to-channel" hack you can't
>>> make sure different channels run on different cpu cores. If you are
>>> ok with that, you could simply populate a mailbox controller with N
>>> channels and allocate them in any order the clients ask.
>>
>> I wanted to make sure I understood the ask here completely. On what
>> basis is N chosen? Who would be the mailbox clients?
>>
> A channel structure is cheap, so any number that is not likely to run
> out. Say you have 10 possible users in a VM, set N=16. I know ideally
> it should be precise and flexible but the gain in simplicity makes the
> trade-off very acceptable.
I think I get the direction you are thinking now. N is chosen based off
of how many clients there might be. One mailbox controller will
represent all message queues and each channel will be one message queue.
There are some limitations that might make it more complex to implement
than having 1 message queue per controller like I have now.
My interpretation is that mailbox controller knows the configuration of
its channels before being bound to a client. For dynamically created
message queues, the client would need tell the controller about the
message queue configuration. I didn't find example where client is
providing information about a channel to the controller.
1. need a mechanism to allow the client to provide the
gunyah_resources for the channel (i.e. the irqs and cap ids).
2. Still need to have bind-client-to-channel patch since clients
aren't real devices and so shouldn't be on DT.
Thanks,
Elliot
On Wed, Nov 02, 2022 at 11:04:45AM -0700, Elliot Berman wrote:
> > > > > +/* Resource Manager Header */
> > > > > +struct gh_rm_rpc_hdr {
> > > > > + u8 version : 4, hdr_words : 4;
> > > > > + u8 type : 2, fragments : 6;
> > > >
> > > > Ick, that's hard to read. One variable per line please?
> > >
> > > Ack.
> > >
> > > > And why the bit packed stuff? Are you sure this is the way to do this?
> > > > Why not use a bitmask instead?
> > > >
> > >
> > > I felt bit packed implementation is cleaner and easier to map to
> > > understanding what the fields are used for.
> >
> > Ah, so this isn't what is on the "wire", then don't use a bitfield like
> > this, use a real variable and that will be faster and simpler to
> > understand.
> >
>
> This is what's on the "wire". Whether I use bitfield or bit packed would be
> functionally the same and is just a cosmetic change IMO.
Ah, that wasn't obvious at all.
Usually using bitfields like this for "wire" protocols is not a good
idea (endian issues and all of that.) Please use a bitmask instead, as
that way you know exactly what is happening, and the compiler can
usually generate much better code overall.
And as this is on the wire, please specify the endian values, _AND_ use
the proper kernel types for stuff that goes between user/kernel or
kernel/hardware, as you are not doing that here.
thanks,
greg k-h
On Wed, Nov 02, 2022 at 11:44:51AM -0700, Elliot Berman wrote:
>
>
> On 11/2/2022 12:31 AM, Arnd Bergmann wrote:
> > On Wed, Oct 26, 2022, at 20:58, Elliot Berman wrote:
> >
> > > +static const struct file_operations gh_vm_fops = {
> > > + .unlocked_ioctl = gh_vm_ioctl,
> > > + .release = gh_vm_release,
> > > + .llseek = noop_llseek,
> > > +};
> >
> > There should be a .compat_ioctl entry here, otherwise it is
> > impossible to use from 32-bit tasks. If all commands have
> > arguments passed through a pointer to a properly defined
> > structure, you can just set it to compat_ptr_ioctl.
> >
>
> Ack.
>
> > > +static long gh_dev_ioctl_create_vm(unsigned long arg)
> > > +{
> > > + struct gunyah_vm *ghvm;
> > > + struct file *file;
> > > + int fd, err;
> > > +
> > > + /* arg reserved for future use. */
> > > + if (arg)
> > > + return -EINVAL;
> >
> > Do you have something specific in mind here? If 'create'
> > is the only command you support, and it has no arguments,
> > it would be easier to do it implicitly during open() and
> > have each fd opened from /dev/gunyah represent a new VM.
> >
>
> I'd like the argument here to support different types of virtual machines. I
> want to leave open what "different types" can be in case something new comes
> up in the future, but immediately "different type" would correspond to a few
> different authentication mechanisms for virtual machines that Gunyah
> supports.
Please don't add code that does not actually do something now, as that
makes it impossible to review properly, _AND_ no one knows what is going
to happen in the future. In the future, you can just add a new ioctl
and all is good, no need to break working userspace by suddenly looking
at the arg value and doing something with it.
thanks,
greg k-h
On Wed, Nov 02, 2022 at 11:45:12AM -0700, Elliot Berman wrote:
> +Michael
>
> On 11/1/2022 10:14 PM, Greg Kroah-Hartman wrote:
> > On Wed, Oct 26, 2022 at 11:58:38AM -0700, Elliot Berman wrote:
> > > +#define GH_CREATE_VM _IO(GH_IOCTL_TYPE, 0x40) /* Returns a Gunyah VM fd */
> >
> > Why 0x40? Why not just use the same KVM ioctl numbers and names as you
> > are doing the same thing as them, right?
>
> We've designed so that there are a few ioctls that will feel similar to KVM
> ioctls since we know this design has been successful, but we don't intend to
> support KVM ioctls 1:1. Gunyah has different semantics for many of the
> name-identical ioctls. It seems odd to mix some re-used KVM ioctls with
> novel Gunyah ioctls?
Even if you don't support it 1:1, at least for the ones that are the
same thing, pick the same numbers as that's a nicer thing to do, right?
> > Normally your first ioctl is "0x01", not "0x40", so this feels really
> > odd.
> >
>
> Documentation/userspace-api/ioctl/iocl-number.rst advises to pick an unused
> block. We picked ioctl code 'G' and unused sequence numbers under that code.
> I'm ok to move the block around.
How do you know you picked an unused block? It wasn't obvious where you
got these values from at all, and unfortunatly, no one really ever
updates that documentation file. Luckily it never really matters.
thanks,
greg k-h
On Wed, Nov 2, 2022 at 6:23 PM Elliot Berman <[email protected]> wrote:
>
>
>
> On 11/2/2022 11:24 AM, Jassi Brar wrote:
> > On Wed, Nov 2, 2022 at 1:06 PM Elliot Berman <[email protected]> wrote:
> >>
> >> Hi Jassi,
> >>
> >> On 11/1/2022 7:01 PM, Jassi Brar wrote:
> >>> On Tue, Nov 1, 2022 at 7:12 PM Elliot Berman <[email protected]> wrote:
> >>>>
> >>>>
> >>>>
> >>>> On 11/1/2022 2:58 PM, Jassi Brar wrote:
> >>>>> On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>> On 11/1/2022 9:23 AM, Jassi Brar wrote:
> >>>>>>> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
> >>>>>>>>
> >>>>>>>> Hi Jassi,
> >>>>>>>>
> >>>>>>>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
> >>>>>>>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
> >>>>>>>> <[email protected]> wrote:
> >>>>>>>> > .....
> >>>>>>>> >> +
> >>>>>>>> >> + gunyah-resource-mgr@0 {
> >>>>>>>> >> + compatible = "gunyah-resource-manager-1-0",
> >>>>>>>> "gunyah-resource-manager";
> >>>>>>>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
> >>>>>>>> full IRQ */
> >>>>>>>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
> >>>>>>>> empty IRQ */
> >>>>>>>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
> >>>>>>>> >> + /* TX, RX cap ids */
> >>>>>>>> >> + };
> >>>>>>>> >>
> >>>>>>>> > All these resources are used only by the mailbox controller driver.
> >>>>>>>> > So, this should be the mailbox controller node, rather than the
> >>>>>>>> > mailbox user.> One option is to load gunyah-resource-manager as a
> >>>>>>>> module that relies
> >>>>>>>> > on the gunyah-mailbox provider. That would also avoid the "Allow
> >>>>>>>> > direct registration to a channel" hack patch.
> >>>>>>>>
> >>>>>>>> A message queue to another guest VM wouldn't be known at boot time and
> >>>>>>>> thus couldn't be described on the devicetree.
> >>>>>>>>
> >>>>>>> I think you need to implement of_xlate() ... or please tell me what
> >>>>>>> exactly you need to specify in the dt.
> >>>>>>
> >>>>>> Dynamically created virtual machines can't be known on the dt, so there
> >>>>>> is nothing to specify in the DT. There couldn't be a devicetree node for
> >>>>>> the message queue client because that client is only exists once the VM
> >>>>>> is created by userspace.
> >>>>>>
> >>>>> The underlying "physical channel" is the synchronous SMC instruction,
> >>>>> which remains 1 irrespective of the number of mailbox instances
> >>>>> created.
> >>>>
> >>>> I disagree that the physical channel is the SMC instruction. Regardless
> >>>> though, there are num_online_cpus() "physical channels" with this
> >>>> perspective.
> >>>>
> >>>>> So basically you are sharing one resource among users. Why doesn't the
> >>>>> RM request the "smc instruction" channel once and share it among
> >>>>> users?
> >>>>
> >>>> I suppose in this scenario, a single mailbox channel would represent all
> >>>> message queues? This would cause Linux to serialize *all* message queue
> >>>> hypercalls. Sorry, I can only think negative implications.
> >>>>
> >>>> Error handling needs to move into clients: if a TX message queue becomes
> >>>> full or an RX message queue becomes empty, then we'll need to return
> >>>> error back to the client right away. The clients would need to register
> >>>> for the RTS/RTR interrupts to know when to send/receive messages and
> >>>> have retry error handling. If the mailbox controller retried for the
> >>>> clients as currently proposed, then we could get into a scenario where a
> >>>> message queue could never be ready to send/receive and thus stuck
> >>>> forever trying to process that message. The effect here would be that
> >>>> the mailbox controller becomes a wrapper to some SMC instructions that
> >>>> aren't related at the SMC instruction level.
> >>>>
> >>>> A single channel would limit performance of SMP systems because only one
> >>>> core could send/receive a message. There is no such limitation for
> >>>> message queues to behave like this.
> >>>>
> >>> This is just an illusion. If Gunyah can handle multiple calls from a
> >>> VM parallely, even with the "bind-client-to-channel" hack you can't
> >>> make sure different channels run on different cpu cores. If you are
> >>> ok with that, you could simply populate a mailbox controller with N
> >>> channels and allocate them in any order the clients ask.
> >>
> >> I wanted to make sure I understood the ask here completely. On what
> >> basis is N chosen? Who would be the mailbox clients?
> >>
> > A channel structure is cheap, so any number that is not likely to run
> > out. Say you have 10 possible users in a VM, set N=16. I know ideally
> > it should be precise and flexible but the gain in simplicity makes the
> > trade-off very acceptable.
>
> I think I get the direction you are thinking now. N is chosen based off
> of how many clients there might be. One mailbox controller will
> represent all message queues and each channel will be one message queue.
> There are some limitations that might make it more complex to implement
> than having 1 message queue per controller like I have now.
>
> My interpretation is that mailbox controller knows the configuration of
> its channels before being bound to a client. For dynamically created
> message queues, the client would need tell the controller about the
> message queue configuration. I didn't find example where client is
> providing information about a channel to the controller.
>
> 1. need a mechanism to allow the client to provide the
> gunyah_resources for the channel (i.e. the irqs and cap ids).
>
IIUC there is exactly one resource-manager in a VM. Right?
Looking at your code, TX and RX irq are used only by the mailbox
driver and are the same for all clients/users. So that should be a
property under the mailbox controller node. Not sure what cap ids are.
> 2. Still need to have bind-client-to-channel patch since clients
> aren't real devices and so shouldn't be on DT.
>
the clients may be virtual (serial, gpio etc) but the resource-manager
requires some mailbox hardware to communicate, so the resource-manager
should be the mailbox client (that further spawns virtual devices)
thnx.
On Wed, Nov 2, 2022, at 19:44, Elliot Berman wrote:
> On 11/2/2022 12:31 AM, Arnd Bergmann wrote:
>>> +static long gh_dev_ioctl_create_vm(unsigned long arg)
>>> +{
>>> + struct gunyah_vm *ghvm;
>>> + struct file *file;
>>> + int fd, err;
>>> +
>>> + /* arg reserved for future use. */
>>> + if (arg)
>>> + return -EINVAL;
>>
>> Do you have something specific in mind here? If 'create'
>> is the only command you support, and it has no arguments,
>> it would be easier to do it implicitly during open() and
>> have each fd opened from /dev/gunyah represent a new VM.
>>
>
> I'd like the argument here to support different types of virtual
> machines. I want to leave open what "different types" can be in case
> something new comes up in the future, but immediately "different type"
> would correspond to a few different authentication mechanisms for
> virtual machines that Gunyah supports.
>
> In this series, I'm only supporting unauthenticated virtual machines
> because they are the simplest to get up and running from a Linux
> userspace. When I introduce the other authentication mechanisms, I'll
> expand much more on how they work, but I'll give quick overview here.
> Other authentication mechanisms that are currently supported by Gunyah
> are "protected VM" and, on Qualcomm platforms, "PIL/carveout VMs".
> Protected VMs are *loosely* similar to the protected firmware design for
> KVM and intended to support Android virtualization use cases.
> PIL/carevout VMs are special virtual machines that can run on Qualcomm
> firmware which authenticate in a way similar to remoteproc firmware (MDT
> loader).
Ok, thanks for the background. Having different types of virtual
machines does mean that you may need some complexity, but I would
still lean towards using the simpler context management of opening
the /dev/gunyah device node to get a new context, and then using
ioctls on each fd to manage that context, instead of going through
the extra indirection of having a secondary 'open context' command
that always requires opening two file descriptors.
>> I'm correct, you can just turn the entire bus/device/driver
>> structure within your code into simple function calls, where
>> the main code calls vm_mgr_probe() as an exported function
>> instead of creating a device.
>
> Ack. I can do this, although I am nervous about this snowballing into a
> situation where I have a mega-module.
>
> > Please stop beating everything in a single module.
>
> https://lore.kernel.org/all/[email protected]/
I see you concern, but I wasn't suggesting having everything
in one module either. There are three common ways of splitting
things into separate modules:
- I suggested having the vm_mgr module as a library block that
exports a few symbols which get used by the core module. The
module doesn't do anything on its own, but loading the core
module forces loading the vm_mgr.
- Alternatively one can do the opposite, and have symbols
exported by the core module, with the vm_mgr module using
it. This would make sense if you commonly have the core
module loaded on virtual machines that do not need to manage
other VMs.
- The method you have is to have a lower "bus" level that
abstracts device providers from consumers, with both sides
hooking into the bus. This makes sense for physical buses
like PCI or USB where both the host driver and the function
driver are unaware of implementation details of the other,
but in your case it does not seem like a good fit.
Arnd
On 11/2/2022 8:21 PM, Jassi Brar wrote:
> On Wed, Nov 2, 2022 at 6:23 PM Elliot Berman <[email protected]> wrote:
>>
>>
>>
>> On 11/2/2022 11:24 AM, Jassi Brar wrote:
>>> On Wed, Nov 2, 2022 at 1:06 PM Elliot Berman <[email protected]> wrote:
>>>>
>>>> Hi Jassi,
>>>>
>>>> On 11/1/2022 7:01 PM, Jassi Brar wrote:
>>>>> On Tue, Nov 1, 2022 at 7:12 PM Elliot Berman <[email protected]> wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 11/1/2022 2:58 PM, Jassi Brar wrote:
>>>>>>> On Tue, Nov 1, 2022 at 3:35 PM Elliot Berman <[email protected]> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 11/1/2022 9:23 AM, Jassi Brar wrote:
>>>>>>>>> On Mon, Oct 31, 2022 at 10:20 PM Elliot Berman <[email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>> Hi Jassi,
>>>>>>>>>>
>>>>>>>>>> On 10/27/2022 7:33 PM, Jassi Brar wrote:
>>>>>>>>>> > On Wed, Oct 26, 2022 at 1:59 PM Elliot Berman
>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>> > .....
>>>>>>>>>> >> +
>>>>>>>>>> >> + gunyah-resource-mgr@0 {
>>>>>>>>>> >> + compatible = "gunyah-resource-manager-1-0",
>>>>>>>>>> "gunyah-resource-manager";
>>>>>>>>>> >> + interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>, /* TX
>>>>>>>>>> full IRQ */
>>>>>>>>>> >> + <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; /* RX
>>>>>>>>>> empty IRQ */
>>>>>>>>>> >> + reg = <0x00000000 0x00000000>, <0x00000000 0x00000001>;
>>>>>>>>>> >> + /* TX, RX cap ids */
>>>>>>>>>> >> + };
>>>>>>>>>> >>
>>>>>>>>>> > All these resources are used only by the mailbox controller driver.
>>>>>>>>>> > So, this should be the mailbox controller node, rather than the
>>>>>>>>>> > mailbox user.> One option is to load gunyah-resource-manager as a
>>>>>>>>>> module that relies
>>>>>>>>>> > on the gunyah-mailbox provider. That would also avoid the "Allow
>>>>>>>>>> > direct registration to a channel" hack patch.
>>>>>>>>>>
>>>>>>>>>> A message queue to another guest VM wouldn't be known at boot time and
>>>>>>>>>> thus couldn't be described on the devicetree.
>>>>>>>>>>
>>>>>>>>> I think you need to implement of_xlate() ... or please tell me what
>>>>>>>>> exactly you need to specify in the dt.
>>>>>>>>
>>>>>>>> Dynamically created virtual machines can't be known on the dt, so there
>>>>>>>> is nothing to specify in the DT. There couldn't be a devicetree node for
>>>>>>>> the message queue client because that client is only exists once the VM
>>>>>>>> is created by userspace.
>>>>>>>>
>>>>>>> The underlying "physical channel" is the synchronous SMC instruction,
>>>>>>> which remains 1 irrespective of the number of mailbox instances
>>>>>>> created.
>>>>>>
>>>>>> I disagree that the physical channel is the SMC instruction. Regardless
>>>>>> though, there are num_online_cpus() "physical channels" with this
>>>>>> perspective.
>>>>>>
>>>>>>> So basically you are sharing one resource among users. Why doesn't the
>>>>>>> RM request the "smc instruction" channel once and share it among
>>>>>>> users?
>>>>>>
>>>>>> I suppose in this scenario, a single mailbox channel would represent all
>>>>>> message queues? This would cause Linux to serialize *all* message queue
>>>>>> hypercalls. Sorry, I can only think negative implications.
>>>>>>
>>>>>> Error handling needs to move into clients: if a TX message queue becomes
>>>>>> full or an RX message queue becomes empty, then we'll need to return
>>>>>> error back to the client right away. The clients would need to register
>>>>>> for the RTS/RTR interrupts to know when to send/receive messages and
>>>>>> have retry error handling. If the mailbox controller retried for the
>>>>>> clients as currently proposed, then we could get into a scenario where a
>>>>>> message queue could never be ready to send/receive and thus stuck
>>>>>> forever trying to process that message. The effect here would be that
>>>>>> the mailbox controller becomes a wrapper to some SMC instructions that
>>>>>> aren't related at the SMC instruction level.
>>>>>>
>>>>>> A single channel would limit performance of SMP systems because only one
>>>>>> core could send/receive a message. There is no such limitation for
>>>>>> message queues to behave like this.
>>>>>>
>>>>> This is just an illusion. If Gunyah can handle multiple calls from a
>>>>> VM parallely, even with the "bind-client-to-channel" hack you can't
>>>>> make sure different channels run on different cpu cores. If you are
>>>>> ok with that, you could simply populate a mailbox controller with N
>>>>> channels and allocate them in any order the clients ask.
>>>>
>>>> I wanted to make sure I understood the ask here completely. On what
>>>> basis is N chosen? Who would be the mailbox clients?
>>>>
>>> A channel structure is cheap, so any number that is not likely to run
>>> out. Say you have 10 possible users in a VM, set N=16. I know ideally
>>> it should be precise and flexible but the gain in simplicity makes the
>>> trade-off very acceptable.
>>
>> I think I get the direction you are thinking now. N is chosen based off
>> of how many clients there might be. One mailbox controller will
>> represent all message queues and each channel will be one message queue.
>> There are some limitations that might make it more complex to implement
>> than having 1 message queue per controller like I have now.
>>
>> My interpretation is that mailbox controller knows the configuration of
>> its channels before being bound to a client. For dynamically created
>> message queues, the client would need tell the controller about the
>> message queue configuration. I didn't find example where client is
>> providing information about a channel to the controller.
>>
>> 1. need a mechanism to allow the client to provide the
>> gunyah_resources for the channel (i.e. the irqs and cap ids).
>>
> IIUC there is exactly one resource-manager in a VM. Right?
> Looking at your code, TX and RX irq are used only by the mailbox
> driver and are the same for all clients/users. So that should be a
> property under the mailbox controller node. Not sure what cap ids are.
>
Ah -- "message queues" are a generic inter-VM communication mechanism
offered by Gunyah. One use case for message queues is to communicate
with the resource-manager, but other message queues can exist between
other virtual machines. Those other message queues use different TX and
RX irq and have different client protocols.
In mailbox terminology, we have one known channel at boot-up time (the
resource manager). That known channel can inform Linux about other
channels at runtime. The client (not the controller) decodes received
data from the channel to discover the new channels.
One approach we found was coming from pcc.c, which has their own
request_channel function (pcc_mbox_request_channel). We could follow
this approach as well...
>> 2. Still need to have bind-client-to-channel patch since clients
>> aren't real devices and so shouldn't be on DT.
>>
> the clients may be virtual (serial, gpio etc) but the resource-manager
> requires some mailbox hardware to communicate, so the resource-manager
> should be the mailbox client (that further spawns virtual devices)
Yes, this the design I'm aiming for. Also want to highlight that the
resource-manager spawns Gunyah virtual devices such as message queue
channels.
Thanks,
Elliot
On 11/3/2022 2:39 AM, Arnd Bergmann wrote:
> On Wed, Nov 2, 2022, at 19:44, Elliot Berman wrote:
>> On 11/2/2022 12:31 AM, Arnd Bergmann wrote:
>>>> +static long gh_dev_ioctl_create_vm(unsigned long arg)
>>>> +{
>>>> + struct gunyah_vm *ghvm;
>>>> + struct file *file;
>>>> + int fd, err;
>>>> +
>>>> + /* arg reserved for future use. */
>>>> + if (arg)
>>>> + return -EINVAL;
>>>
>>> Do you have something specific in mind here? If 'create'
>>> is the only command you support, and it has no arguments,
>>> it would be easier to do it implicitly during open() and
>>> have each fd opened from /dev/gunyah represent a new VM.
>>>
>>
>> I'd like the argument here to support different types of virtual
>> machines. I want to leave open what "different types" can be in case
>> something new comes up in the future, but immediately "different type"
>> would correspond to a few different authentication mechanisms for
>> virtual machines that Gunyah supports.
>>
>> In this series, I'm only supporting unauthenticated virtual machines
>> because they are the simplest to get up and running from a Linux
>> userspace. When I introduce the other authentication mechanisms, I'll
>> expand much more on how they work, but I'll give quick overview here.
>> Other authentication mechanisms that are currently supported by Gunyah
>> are "protected VM" and, on Qualcomm platforms, "PIL/carveout VMs".
>> Protected VMs are *loosely* similar to the protected firmware design for
>> KVM and intended to support Android virtualization use cases.
>> PIL/carevout VMs are special virtual machines that can run on Qualcomm
>> firmware which authenticate in a way similar to remoteproc firmware (MDT
>> loader).
>
> Ok, thanks for the background. Having different types of virtual
> machines does mean that you may need some complexity, but I would
> still lean towards using the simpler context management of opening
> the /dev/gunyah device node to get a new context, and then using
> ioctls on each fd to manage that context, instead of going through
> the extra indirection of having a secondary 'open context' command
> that always requires opening two file descriptors.
>
>>> I'm correct, you can just turn the entire bus/device/driver
>>> structure within your code into simple function calls, where
>>> the main code calls vm_mgr_probe() as an exported function
>>> instead of creating a device.
>>
>> Ack. I can do this, although I am nervous about this snowballing into a
>> situation where I have a mega-module.
>>
>> > Please stop beating everything in a single module.
>>
>> https://lore.kernel.org/all/[email protected]/
>
> I see you concern, but I wasn't suggesting having everything
> in one module either. There are three common ways of splitting
> things into separate modules:
>
> - I suggested having the vm_mgr module as a library block that
> exports a few symbols which get used by the core module. The
> module doesn't do anything on its own, but loading the core
> module forces loading the vm_mgr.
>
Got the idea, I'll do this.
- Elliot
> - Alternatively one can do the opposite, and have symbols
> exported by the core module, with the vm_mgr module using
> it. This would make sense if you commonly have the core
> module loaded on virtual machines that do not need to manage
> other VMs.
>
> - The method you have is to have a lower "bus" level that
> abstracts device providers from consumers, with both sides
> hooking into the bus. This makes sense for physical buses
> like PCI or USB where both the host driver and the function
> driver are unaware of implementation details of the other,
> but in your case it does not seem like a good fit.
>
> Arnd
On 11/2/2022 5:22 PM, Greg Kroah-Hartman wrote:
> On Wed, Nov 02, 2022 at 11:04:45AM -0700, Elliot Berman wrote:
>>>>>> +/* Resource Manager Header */
>>>>>> +struct gh_rm_rpc_hdr {
>>>>>> + u8 version : 4, hdr_words : 4;
>>>>>> + u8 type : 2, fragments : 6;
>>>>>
>>>>> Ick, that's hard to read. One variable per line please?
>>>>
>>>> Ack.
>>>>
>>>>> And why the bit packed stuff? Are you sure this is the way to do this?
>>>>> Why not use a bitmask instead?
>>>>>
>>>>
>>>> I felt bit packed implementation is cleaner and easier to map to
>>>> understanding what the fields are used for.
>>>
>>> Ah, so this isn't what is on the "wire", then don't use a bitfield like
>>> this, use a real variable and that will be faster and simpler to
>>> understand.
>>>
>>
>> This is what's on the "wire". Whether I use bitfield or bit packed would be
>> functionally the same and is just a cosmetic change IMO.
>
> Ah, that wasn't obvious at all.
>
> Usually using bitfields like this for "wire" protocols is not a good
> idea (endian issues and all of that.) Please use a bitmask instead, as
> that way you know exactly what is happening, and the compiler can
> usually generate much better code overall.
>
> And as this is on the wire, please specify the endian values, _AND_ use
> the proper kernel types for stuff that goes between user/kernel or
> kernel/hardware, as you are not doing that here.
Ack
On 11/2/2022 5:20 PM, Greg Kroah-Hartman wrote:
> On Wed, Nov 02, 2022 at 11:44:51AM -0700, Elliot Berman wrote:
>>
>>
>> On 11/2/2022 12:31 AM, Arnd Bergmann wrote:
>>> On Wed, Oct 26, 2022, at 20:58, Elliot Berman wrote:
>>>
>>>> +static const struct file_operations gh_vm_fops = {
>>>> + .unlocked_ioctl = gh_vm_ioctl,
>>>> + .release = gh_vm_release,
>>>> + .llseek = noop_llseek,
>>>> +};
>>>
>>> There should be a .compat_ioctl entry here, otherwise it is
>>> impossible to use from 32-bit tasks. If all commands have
>>> arguments passed through a pointer to a properly defined
>>> structure, you can just set it to compat_ptr_ioctl.
>>>
>>
>> Ack.
>>
>>>> +static long gh_dev_ioctl_create_vm(unsigned long arg)
>>>> +{
>>>> + struct gunyah_vm *ghvm;
>>>> + struct file *file;
>>>> + int fd, err;
>>>> +
>>>> + /* arg reserved for future use. */
>>>> + if (arg)
>>>> + return -EINVAL;
>>>
>>> Do you have something specific in mind here? If 'create'
>>> is the only command you support, and it has no arguments,
>>> it would be easier to do it implicitly during open() and
>>> have each fd opened from /dev/gunyah represent a new VM.
>>>
>>
>> I'd like the argument here to support different types of virtual machines. I
>> want to leave open what "different types" can be in case something new comes
>> up in the future, but immediately "different type" would correspond to a few
>> different authentication mechanisms for virtual machines that Gunyah
>> supports.
>
> Please don't add code that does not actually do something now, as that
> makes it impossible to review properly, _AND_ no one knows what is going
> to happen in the future. In the future, you can just add a new ioctl
> and all is good, no need to break working userspace by suddenly looking
> at the arg value and doing something with it.
>
I think the argument does something today and it's documented to need to
be 0. If a userspace from the future provides non-zero value, Linux will
correctly reject it because it doesn't know how to interpret the
different VM types.
I can document it more clearly as the VM type field and support only the
one VM type today.
Creating new ioctl for each VM type feels to me like I didn't design
CREATE_VM ioctl right in first place.
Thanks,
Elliot
On 11/2/2022 5:22 PM, Greg Kroah-Hartman wrote:
> On Wed, Nov 02, 2022 at 11:04:45AM -0700, Elliot Berman wrote:
>>>>>> +/* Resource Manager Header */
>>>>>> +struct gh_rm_rpc_hdr {
>>>>>> + u8 version : 4, hdr_words : 4;
>>>>>> + u8 type : 2, fragments : 6;
>>>>>
>>>>> Ick, that's hard to read. One variable per line please?
>>>>
>>>> Ack.
>>>>
>>>>> And why the bit packed stuff? Are you sure this is the way to do this?
>>>>> Why not use a bitmask instead?
>>>>>
>>>>
>>>> I felt bit packed implementation is cleaner and easier to map to
>>>> understanding what the fields are used for.
>>>
>>> Ah, so this isn't what is on the "wire", then don't use a bitfield like
>>> this, use a real variable and that will be faster and simpler to
>>> understand.
>>>
>>
>> This is what's on the "wire". Whether I use bitfield or bit packed would be
>> functionally the same and is just a cosmetic change IMO.
>
> Ah, that wasn't obvious at all.
>
> Usually using bitfields like this for "wire" protocols is not a good
> idea (endian issues and all of that.) Please use a bitmask instead, as
> that way you know exactly what is happening, and the compiler can
> usually generate much better code overall.
>
> And as this is on the wire, please specify the endian values, _AND_ use
> the proper kernel types for stuff that goes between user/kernel or
> kernel/hardware, as you are not doing that here.
Ack
On 11/2/2022 5:24 PM, Greg Kroah-Hartman wrote:
> On Wed, Nov 02, 2022 at 11:45:12AM -0700, Elliot Berman wrote:
>> +Michael
>>
>> On 11/1/2022 10:14 PM, Greg Kroah-Hartman wrote:
>>> On Wed, Oct 26, 2022 at 11:58:38AM -0700, Elliot Berman wrote:
>>>> +#define GH_CREATE_VM _IO(GH_IOCTL_TYPE, 0x40) /* Returns a Gunyah VM fd */
>>>
>>> Why 0x40? Why not just use the same KVM ioctl numbers and names as you
>>> are doing the same thing as them, right?
>>
>> We've designed so that there are a few ioctls that will feel similar to KVM
>> ioctls since we know this design has been successful, but we don't intend to
>> support KVM ioctls 1:1. Gunyah has different semantics for many of the
>> name-identical ioctls. It seems odd to mix some re-used KVM ioctls with
>> novel Gunyah ioctls?
>
> Even if you don't support it 1:1, at least for the ones that are the
> same thing, pick the same numbers as that's a nicer thing to do, right?
>
Does same thing == interpretation of arguments is the same? For
instance, GH_CREATE_VM and KVM_CREATE_VM interpret the arguments
differently. Same for KVM_SET_USERSPACE_MEMORY. The high level
functionality should be similar for most all hypervisors since they will
all support creating a VM and probably sharing memory with that VM. The
arguments for that will necessarily look similar, but they will probably
be subtly different because the hypervisors support different features.
I don't think userspace that supports both KVM and Gunyah will benefit
much from re-using the same numbers since those re-used ioctl calls
still need to sit within the context of a Gunyah VM.
Thanks,
Elliot
On Fri, Nov 4, 2022, at 01:11, Elliot Berman wrote:
> On 11/2/2022 5:24 PM, Greg Kroah-Hartman wrote:
>> On Wed, Nov 02, 2022 at 11:45:12AM -0700, Elliot Berman wrote:
>>
>> Even if you don't support it 1:1, at least for the ones that are the
>> same thing, pick the same numbers as that's a nicer thing to do, right?
>>
>
> Does same thing == interpretation of arguments is the same? For
> instance, GH_CREATE_VM and KVM_CREATE_VM interpret the arguments
> differently. Same for KVM_SET_USERSPACE_MEMORY. The high level
> functionality should be similar for most all hypervisors since they will
> all support creating a VM and probably sharing memory with that VM. The
> arguments for that will necessarily look similar, but they will probably
> be subtly different because the hypervisors support different features.
I think in the ideal case, you should make the arguments and the
command codes the same for any command where that is possible. If
you come across a command that is shared with KVM but just needs
another flag, that would involve coordinating with the KVM maintainers
about sharing the definition so the same flag does not get reused
in an incompatible way.
For commands that cannot fit into the existing definition, there
should be a different command code, using your own namespace and
not the 0xAE block that KVM has. It still makes sense to follow
the argument structure roughly here, unless there is a technical
reason for making it different.
> I don't think userspace that supports both KVM and Gunyah will benefit
> much from re-using the same numbers since those re-used ioctl calls
> still need to sit within the context of a Gunyah VM.
One immediate benefit is for tools that work on running processes,
such as strace, gdb or qemu-user. If they encounter a known command,
they can correctly display the arguments etc.
Another benefit is for sharing portions of the VMM that live in
outside processes like vhost-user based device emulation that
interacts with irqfd, memfd etc. The more similar the command
interface is, the easier it gets to keep these tools portable.
Arnd
On 11/4/2022 1:10 AM, Arnd Bergmann wrote:
> On Fri, Nov 4, 2022, at 01:11, Elliot Berman wrote:
>> On 11/2/2022 5:24 PM, Greg Kroah-Hartman wrote:
>>> On Wed, Nov 02, 2022 at 11:45:12AM -0700, Elliot Berman wrote:
>>>
>>> Even if you don't support it 1:1, at least for the ones that are the
>>> same thing, pick the same numbers as that's a nicer thing to do, right?
>>>
>>
>> Does same thing == interpretation of arguments is the same? For
>> instance, GH_CREATE_VM and KVM_CREATE_VM interpret the arguments
>> differently. Same for KVM_SET_USERSPACE_MEMORY. The high level
>> functionality should be similar for most all hypervisors since they will
>> all support creating a VM and probably sharing memory with that VM. The
>> arguments for that will necessarily look similar, but they will probably
>> be subtly different because the hypervisors support different features.
>
> I think in the ideal case, you should make the arguments and the
> command codes the same for any command where that is possible. If
> you come across a command that is shared with KVM but just needs
> another flag, that would involve coordinating with the KVM maintainers
> about sharing the definition so the same flag does not get reused
> in an incompatible way.
>
I think the converse also needs to be true; KVM would need to check that
new flags don't get used in some incompatible way with Gunyah, even if
one of us is just -EINVAL'ing. I don't think Gunyah and KVM should be
reliant on the other reviewing shared ioctls.
The problem is a bit worse because "machine type" is architecture-
dependent whereas the planned Gunyah flags are architecture-independent.
KVM within itself re-uses flags between architectures so Gunyah would
need to reserve some flags from all architectures that KVM supports.
> For commands that cannot fit into the existing definition, there
> should be a different command code, using your own namespace and
> not the 0xAE block that KVM has. It still makes sense to follow
> the argument structure roughly here, unless there is a technical
> reason for making it different.
>
>> I don't think userspace that supports both KVM and Gunyah will benefit
>> much from re-using the same numbers since those re-used ioctl calls
>> still need to sit within the context of a Gunyah VM.
>
> One immediate benefit is for tools that work on running processes,
> such as strace, gdb or qemu-user. If they encounter a known command,
> they can correctly display the arguments etc.
>
We can update these tools and anyway there will be different ioctls to
get started. There are important ioctls that wouldn't be correctly
displayed off the bat anyway; work would need to be done to support the
Gunyah ioctls either way. Whereas tooling update is temporary, the
coupling of KVM and Gunyah ioctls would be permanent.
> Another benefit is for sharing portions of the VMM that live in
> outside processes like vhost-user based device emulation that
> interacts with irqfd, memfd etc. The more similar the command
> interface is, the easier it gets to keep these tools portable.
>
Hypervisor interfaces already have different ioctls for equivalent
functionality [1], so VMMs that want to scale to multiple hypervisors
already abstract out ioctl-level interfaces so there wouldn't be any
code-reuse even if Gunyah and KVM shared the same ioctl number. Between
hypervisors, the best case is there is design similarity for userspace,
which makes it easier to add new hypervisor support for VMMs and that's
what we are aiming for.
[1]: e.g. compare KVM, acrn, xen for implementing virtual interrupts.
KVM and acrn use independently implemented irqfd interfaces, xen has
totally different implementation called event channels.
Thanks,
Elliot
On 11/4/2022 3:38 PM, Elliot Berman wrote:
>
>
> On 11/4/2022 1:10 AM, Arnd Bergmann wrote:
>> On Fri, Nov 4, 2022, at 01:11, Elliot Berman wrote:
>>> On 11/2/2022 5:24 PM, Greg Kroah-Hartman wrote:
>>>> On Wed, Nov 02, 2022 at 11:45:12AM -0700, Elliot Berman wrote:
>>>>
>>>> Even if you don't support it 1:1, at least for the ones that are the
>>>> same thing, pick the same numbers as that's a nicer thing to do, right?
>>>>
>>>
>>> Does same thing == interpretation of arguments is the same? For
>>> instance, GH_CREATE_VM and KVM_CREATE_VM interpret the arguments
>>> differently. Same for KVM_SET_USERSPACE_MEMORY. The high level
>>> functionality should be similar for most all hypervisors since they will
>>> all support creating a VM and probably sharing memory with that VM. The
>>> arguments for that will necessarily look similar, but they will probably
>>> be subtly different because the hypervisors support different features.
>>
>> I think in the ideal case, you should make the arguments and the
>> command codes the same for any command where that is possible. If
>> you come across a command that is shared with KVM but just needs
>> another flag, that would involve coordinating with the KVM maintainers
>> about sharing the definition so the same flag does not get reused
>> in an incompatible way.
>>
>
> I think the converse also needs to be true; KVM would need to check that
> new flags don't get used in some incompatible way with Gunyah, even if
> one of us is just -EINVAL'ing. I don't think Gunyah and KVM should be
> reliant on the other reviewing shared ioctls.
>
> The problem is a bit worse because "machine type" is architecture-
> dependent whereas the planned Gunyah flags are architecture-independent.
> KVM within itself re-uses flags between architectures so Gunyah would
> need to reserve some flags from all architectures that KVM supports.
I agree w/ Elliot. We would like to keep Gunyah independent and not rely
on the existing KVM ioctls space. We should allow new hypervisor drivers
interfaces addition in Linux kernel without them relying on KVM.
>
>> For commands that cannot fit into the existing definition, there
>> should be a different command code, using your own namespace and
>> not the 0xAE block that KVM has. It still makes sense to follow
>> the argument structure roughly here, unless there is a technical
>> reason for making it different.
>>
>>> I don't think userspace that supports both KVM and Gunyah will benefit
>>> much from re-using the same numbers since those re-used ioctl calls
>>> still need to sit within the context of a Gunyah VM.
>>
>> One immediate benefit is for tools that work on running processes,
>> such as strace, gdb or qemu-user. If they encounter a known command,
>> they can correctly display the arguments etc.
>>
>
> We can update these tools and anyway there will be different ioctls to
> get started. There are important ioctls that wouldn't be correctly
> displayed off the bat anyway; work would need to be done to support the
> Gunyah ioctls either way. Whereas tooling update is temporary, the
> coupling of KVM and Gunyah ioctls would be permanent.
Agree, tools can be updated and that is the easy part as we grow the s/w
stack around Gunyah in userspace, like we already do w/ CrosVM (Virtual
Machine Manager) and QEMU will be next followed by rust-vmm. All of them
can be done without Gunyah ioctls relying anything on the KVM ioctls.
Elliot has also explained very well that we don't to go to KVM
maintainers for any of our additions and we also don't want them to come
to us, since there is no interoperability testing. It is best that both
Hypervisors and their Linux interfaces evolve independently.
---Trilok Soni
Hi Arnd, Greg,
On 11/4/2022 9:19 PM, Trilok Soni wrote:
> On 11/4/2022 3:38 PM, Elliot Berman wrote:
>>
>>
>> On 11/4/2022 1:10 AM, Arnd Bergmann wrote:
>>> On Fri, Nov 4, 2022, at 01:11, Elliot Berman wrote:
>>>> On 11/2/2022 5:24 PM, Greg Kroah-Hartman wrote:
>>>>> On Wed, Nov 02, 2022 at 11:45:12AM -0700, Elliot Berman wrote:
>>>>>
>>>>> Even if you don't support it 1:1, at least for the ones that are the
>>>>> same thing, pick the same numbers as that's a nicer thing to do,
>>>>> right?
>>>>>
>>>>
>>>> Does same thing == interpretation of arguments is the same? For
>>>> instance, GH_CREATE_VM and KVM_CREATE_VM interpret the arguments
>>>> differently. Same for KVM_SET_USERSPACE_MEMORY. The high level
>>>> functionality should be similar for most all hypervisors since they
>>>> will
>>>> all support creating a VM and probably sharing memory with that VM. The
>>>> arguments for that will necessarily look similar, but they will
>>>> probably
>>>> be subtly different because the hypervisors support different features.
>>>
>>> I think in the ideal case, you should make the arguments and the
>>> command codes the same for any command where that is possible. If
>>> you come across a command that is shared with KVM but just needs
>>> another flag, that would involve coordinating with the KVM maintainers
>>> about sharing the definition so the same flag does not get reused
>>> in an incompatible way.
>>>
>>
>> I think the converse also needs to be true; KVM would need to check that
>> new flags don't get used in some incompatible way with Gunyah, even if
>> one of us is just -EINVAL'ing. I don't think Gunyah and KVM should be
>> reliant on the other reviewing shared ioctls.
>>
>> The problem is a bit worse because "machine type" is architecture-
>> dependent whereas the planned Gunyah flags are architecture-independent.
>> KVM within itself re-uses flags between architectures so Gunyah would
>> need to reserve some flags from all architectures that KVM supports.
>
> I agree w/ Elliot. We would like to keep Gunyah independent and not rely
> on the existing KVM ioctls space. We should allow new hypervisor drivers
> interfaces addition in Linux kernel without them relying on KVM.
>
>>
>>> For commands that cannot fit into the existing definition, there
>>> should be a different command code, using your own namespace and
>>> not the 0xAE block that KVM has. It still makes sense to follow
>>> the argument structure roughly here, unless there is a technical
>>> reason for making it different.
>>>
>>>> I don't think userspace that supports both KVM and Gunyah will benefit
>>>> much from re-using the same numbers since those re-used ioctl calls
>>>> still need to sit within the context of a Gunyah VM.
>>>
>>> One immediate benefit is for tools that work on running processes,
>>> such as strace, gdb or qemu-user. If they encounter a known command,
>>> they can correctly display the arguments etc.
>>>
>>
>> We can update these tools and anyway there will be different ioctls to
>> get started. There are important ioctls that wouldn't be correctly
>> displayed off the bat anyway; work would need to be done to support the
>> Gunyah ioctls either way. Whereas tooling update is temporary, the
>> coupling of KVM and Gunyah ioctls would be permanent.
>
> Agree, tools can be updated and that is the easy part as we grow the s/w
> stack around Gunyah in userspace, like we already do w/ CrosVM (Virtual
> Machine Manager) and QEMU will be next followed by rust-vmm. All of them
> can be done without Gunyah ioctls relying anything on the KVM ioctls.
> Elliot has also explained very well that we don't to go to KVM
> maintainers for any of our additions and we also don't want them to come
> to us, since there is no interoperability testing. It is best that both
> Hypervisors and their Linux interfaces evolve independently.
Are above explanations reasonable to not re-use KVM ioctl numbers?
Thanks,
Elliot
On Thu, Nov 10, 2022 at 04:03:10PM -0800, Elliot Berman wrote:
> > Agree, tools can be updated and that is the easy part as we grow the s/w
> > stack around Gunyah in userspace, like we already do w/ CrosVM (Virtual
> > Machine Manager) and QEMU will be next followed by rust-vmm. All of them
> > can be done without Gunyah ioctls relying anything on the KVM ioctls.
> > Elliot has also explained very well that we don't to go to KVM
> > maintainers for any of our additions and we also don't want them to come
> > to us, since there is no interoperability testing. It is best that both
> > Hypervisors and their Linux interfaces evolve independently.
>
> Are above explanations reasonable to not re-use KVM ioctl numbers?
Try getting close at least, where possible please. As your ioctl
numbers didn't even start at 0, it's a bit odd...
thanks,
greg k-h
On 11/10/2022 10:24 PM, Greg Kroah-Hartman wrote:
> On Thu, Nov 10, 2022 at 04:03:10PM -0800, Elliot Berman wrote:
>>> Agree, tools can be updated and that is the easy part as we grow the s/w
>>> stack around Gunyah in userspace, like we already do w/ CrosVM (Virtual
>>> Machine Manager) and QEMU will be next followed by rust-vmm. All of them
>>> can be done without Gunyah ioctls relying anything on the KVM ioctls.
>>> Elliot has also explained very well that we don't to go to KVM
>>> maintainers for any of our additions and we also don't want them to come
>>> to us, since there is no interoperability testing. It is best that both
>>> Hypervisors and their Linux interfaces evolve independently.
>>
>> Are above explanations reasonable to not re-use KVM ioctl numbers?
>
> Try getting close at least, where possible please. As your ioctl
> numbers didn't even start at 0, it's a bit odd...
Ack, will do.
Thanks,
Elliot