* [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers
@ 2025-12-24 13:52 joaopeixoto
2025-12-24 13:52 ` [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto
` (6 more replies)
0 siblings, 7 replies; 31+ messages in thread
From: joaopeixoto @ 2025-12-24 13:52 UTC (permalink / raw)
To: linux-kernel
Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet,
dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren,
heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel,
linux, linux-doc, linux-riscv, maddy, mani, nathan,
neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will
From: João Peixoto <joaopeixoto@osyx.tech>
This series introduces support for the Bao hypervisor guest-side drivers
under drivers/virt/bao and the associated Device Tree bindings, UAPI,
and MAINTAINERS entries.
Bao is a lightweight static-partitioning hypervisor for embedded and
safety-critical systems. This series adds:
- The Bao IPC shared memory driver, which enables Linux guests to
communicate with each other through shared memory regions.
- The Bao I/O Dispatcher driver, which allows Bao's VMs to share I/O
devices using device paravirtualization (VirtIO).
Patch overview:
1. dt-bindings: Add Bao IPC shared memory driver binding
- Provides a standardized DT description for Bao IPC shared memory
devices used for inter-VM communication.
2. virt: add Bao IPC shared memory driver
- Character device driver that maps shared-memory regions and
communicates with the hypervisor via architecture-specific hypercalls
(SMC/HVC on ARM, SBI ecall on RISC-V).
3. dt-bindings: Add Bao I/O dispatcher driver binding
- DT binding for the Bao I/O Dispatcher, describing memory regions,
interrupts, and compatible strings for backend VMs.
4. virt: add Bao I/O dispatcher driver
- Implements the I/O Dispatcher kernel module bridging Bao Remote I/O
with VirtIO backend devices.
- Includes architecture-specific headers for ARM, ARM64, and RISC-V,
driver framework files, UAPI headers, Kconfig/Makefile integration,
and ioctl documentation.
5. MAINTAINERS: Add entries for Bao hypervisor drivers
- Registers maintainers for all Bao hypervisor components to ensure
proper kernel review and notifications.
This series has been validated on Linux guests running under Bao hypervisor,
ensuring correct initialization, read/write operations for IPC shared
memory, and proper I/O Dispatcher functionality for backend VMs.
Feedback and review from maintainers of virtualization, architecture-specific
code (ARM, ARM64, RISC-V), Device Tree bindings, and UAPI are welcome.
João Peixoto (5):
dt-bindings: Add Bao IPC shared memory driver binding
virt: add Bao IPC shared memory driver
dt-bindings: Add Bao I/O dispatcher driver binding
virt: add Bao I/O dispatcher driver
MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT
bindings
.../bindings/bao/io-dispatcher.yaml | 67 +++
.../devicetree/bindings/bao/ipcshmem.yaml | 99 ++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
.../userspace-api/ioctl/ioctl-number.rst | 2 +
MAINTAINERS | 12 +
arch/arm/include/asm/bao.h | 62 ++
arch/arm64/include/asm/bao.h | 62 ++
arch/riscv/include/asm/bao.h | 61 ++
drivers/virt/Kconfig | 2 +
drivers/virt/Makefile | 2 +
drivers/virt/bao/Kconfig | 5 +
drivers/virt/bao/Makefile | 4 +
drivers/virt/bao/io-dispatcher/Kconfig | 16 +
drivers/virt/bao/io-dispatcher/Makefile | 4 +
drivers/virt/bao/io-dispatcher/bao_drv.h | 386 +++++++++++++
drivers/virt/bao/io-dispatcher/dm.c | 330 +++++++++++
drivers/virt/bao/io-dispatcher/driver.c | 348 +++++++++++
drivers/virt/bao/io-dispatcher/hypercall.h | 30 +
drivers/virt/bao/io-dispatcher/intc.c | 68 +++
drivers/virt/bao/io-dispatcher/io_client.c | 435 ++++++++++++++
.../virt/bao/io-dispatcher/io_dispatcher.c | 207 +++++++
drivers/virt/bao/io-dispatcher/ioctls.c | 145 +++++
drivers/virt/bao/io-dispatcher/ioeventfd.c | 336 +++++++++++
drivers/virt/bao/io-dispatcher/irqfd.c | 341 +++++++++++
drivers/virt/bao/ipcshmem/Kconfig | 9 +
drivers/virt/bao/ipcshmem/Makefile | 3 +
drivers/virt/bao/ipcshmem/ipcshmem.c | 539 ++++++++++++++++++
include/uapi/linux/bao.h | 124 ++++
28 files changed, 3701 insertions(+)
create mode 100644 Documentation/devicetree/bindings/bao/io-dispatcher.yaml
create mode 100644 Documentation/devicetree/bindings/bao/ipcshmem.yaml
create mode 100644 arch/arm/include/asm/bao.h
create mode 100644 arch/arm64/include/asm/bao.h
create mode 100644 arch/riscv/include/asm/bao.h
create mode 100644 drivers/virt/bao/Kconfig
create mode 100644 drivers/virt/bao/Makefile
create mode 100644 drivers/virt/bao/io-dispatcher/Kconfig
create mode 100644 drivers/virt/bao/io-dispatcher/Makefile
create mode 100644 drivers/virt/bao/io-dispatcher/bao_drv.h
create mode 100644 drivers/virt/bao/io-dispatcher/dm.c
create mode 100644 drivers/virt/bao/io-dispatcher/driver.c
create mode 100644 drivers/virt/bao/io-dispatcher/hypercall.h
create mode 100644 drivers/virt/bao/io-dispatcher/intc.c
create mode 100644 drivers/virt/bao/io-dispatcher/io_client.c
create mode 100644 drivers/virt/bao/io-dispatcher/io_dispatcher.c
create mode 100644 drivers/virt/bao/io-dispatcher/ioctls.c
create mode 100644 drivers/virt/bao/io-dispatcher/ioeventfd.c
create mode 100644 drivers/virt/bao/io-dispatcher/irqfd.c
create mode 100644 drivers/virt/bao/ipcshmem/Kconfig
create mode 100644 drivers/virt/bao/ipcshmem/Makefile
create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c
create mode 100644 include/uapi/linux/bao.h
--
2.43.0
^ permalink raw reply [flat|nested] 31+ messages in thread* [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto @ 2025-12-24 13:52 ` joaopeixoto 2025-12-24 16:18 ` Rob Herring (Arm) 2025-12-25 8:57 ` Krzysztof Kozlowski 2025-12-24 13:52 ` [PATCH 2/5] virt: add Bao IPC shared memory driver joaopeixoto ` (5 subsequent siblings) 6 siblings, 2 replies; 31+ messages in thread From: joaopeixoto @ 2025-12-24 13:52 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> This patch adds a Device Tree binding for the Bao IPC shared memory device, which provides a standardized description of the hardware interface used for inter-VM communication in Bao-based systems. The binding documents the following properties: - compatible: "bao,ipcshmem" - reg: Memory region for the shared memory device - id: Unique device identifier - read-channel: [offset, size] for reading from the shared memory - write-channel: [offset, size] for writing to the shared memory - interrupts: Interrupts used by the device This enables kernel drivers and userspace tools to correctly instantiate and configure Bao IPC shared memory devices based on the DT description, facilitating efficient communication between VMs. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- .../devicetree/bindings/bao/ipcshmem.yaml | 99 +++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 101 insertions(+) create mode 100644 Documentation/devicetree/bindings/bao/ipcshmem.yaml diff --git a/Documentation/devicetree/bindings/bao/ipcshmem.yaml b/Documentation/devicetree/bindings/bao/ipcshmem.yaml new file mode 100644 index 000000000000..398ac610c29f --- /dev/null +++ b/Documentation/devicetree/bindings/bao/ipcshmem.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/bao/ipcshmem.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Bao IPC Shared Memory Device + +maintainers: + - José Martins <jose@osyx.tech> + - David Cerdeira <davidmcerdeira@osyx.tech> + - João Peixoto <joaopeixoto@osyx.tech> + +description: | + Shared memory based communication device for Bao hypervisor guests. + It allows the kernel to interface with guests running under + the Bao hypervisor, providing a character device interface + for exchanging data through dedicated shared-memory regions. + +properties: + compatible: + const: "bao,ipcshmem" + + reg: + description: | + Memory resource for the shared memory device. + maxItems: 4 + type: array + items: + type: integer + + id: + description: Driver instance ID + type: integer + minimum: 0 + + read-channel: + description: | + Defines the shared-memory region used by the guest → host data path. + + The value is a 2-cell array describing a sub-region inside the main + `reg` area: + - The first cell is the byte offset from the beginning of the + shared-memory region specified in `reg`. + - The second cell is the size of the readable region in bytes. + + The driver will only read data from this sub-region. + type: array + items: + type: integer + minItems: 2 + maxItems: 2 + + write-channel: + description: | + Defines the shared-memory region used by the host → guest data path. + + The value is a 2-cell array describing a sub-region inside the main + `reg` area: + - The first cell is the byte offset from the beginning of the + shared-memory region specified in `reg`. + - The second cell is the size of the writable region in bytes. + + The driver will only write data into this sub-region. + type: array + items: + type: integer + minItems: 2 + maxItems: 2 + + interrupts: + description: | + Interrupt specification for the device. + type: array + items: + type: integer + minItems: 3 + maxItems: 3 + +required: + - compatible + - reg + - id + - read-channel + - write-channel + - interrupts + +additionalProperties: false + +examples: + - | + bao-ipc@f0000000 { + compatible = "bao,ipcshmem"; + reg = <0x0 0xf0000000 0x0 0x00010000>; + read-channel = <0x0 0x2000>; + write-channel = <0x2000 0x2000>; + interrupts = <0 52 1>; + id = <0>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index c7591b2aec2a..c047fbd6b91a 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -223,6 +223,8 @@ patternProperties: description: Shenzhen AZW Technology Co., Ltd. "^baikal,.*": description: BAIKAL ELECTRONICS, JSC + "^bao,.*": + description: Bao Hypervisor "^bananapi,.*": description: BIPAI KEJI LIMITED "^beacon,.*": -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding 2025-12-24 13:52 ` [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto @ 2025-12-24 16:18 ` Rob Herring (Arm) 2025-12-25 8:57 ` Krzysztof Kozlowski 1 sibling, 0 replies; 31+ messages in thread From: Rob Herring (Arm) @ 2025-12-24 16:18 UTC (permalink / raw) To: joaopeixoto Cc: will, linux-kernel, bagasdotme, maddy, krzk+dt, alex, conor+dt, haren, dan.j.williams, jose, prabhakar.mahadev-lad.rj, linux-arm-kernel, nathan, pjw, linux-riscv, catalin.marinas, heiko, devicetree, gregkh, dev, linux, neil.armstrong, kever.yang, corbet, ajd, aou, davidmcerdeira, palmer, mani, linux-doc On Wed, 24 Dec 2025 13:52:13 +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This patch adds a Device Tree binding for the Bao IPC shared memory > device, which provides a standardized description of the hardware > interface used for inter-VM communication in Bao-based systems. > > The binding documents the following properties: > > - compatible: "bao,ipcshmem" > - reg: Memory region for the shared memory device > - id: Unique device identifier > - read-channel: [offset, size] for reading from the shared memory > - write-channel: [offset, size] for writing to the shared memory > - interrupts: Interrupts used by the device > > This enables kernel drivers and userspace tools to correctly > instantiate and configure Bao IPC shared memory devices based > on the DT description, facilitating efficient communication > between VMs. > > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > .../devicetree/bindings/bao/ipcshmem.yaml | 99 +++++++++++++++++++ > .../devicetree/bindings/vendor-prefixes.yaml | 2 + > 2 files changed, 101 insertions(+) > create mode 100644 Documentation/devicetree/bindings/bao/ipcshmem.yaml > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: dtschema/dtc warnings/errors: doc reference errors (make refcheckdocs): See https://patchwork.kernel.org/project/devicetree/patch/20251224135217.25350-2-joaopeixoto@osyx.tech The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding 2025-12-24 13:52 ` [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto 2025-12-24 16:18 ` Rob Herring (Arm) @ 2025-12-25 8:57 ` Krzysztof Kozlowski 1 sibling, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2025-12-25 8:57 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Dec 24, 2025 at 01:52:13PM +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This patch adds a Device Tree binding for the Bao IPC shared memory > device, which provides a standardized description of the hardware > interface used for inter-VM communication in Bao-based systems. > All you emails bounced as spam, so you need tofix your email setup. I found this only via lore. > The binding documents the following properties: > > - compatible: "bao,ipcshmem" > - reg: Memory region for the shared memory device > - id: Unique device identifier > - read-channel: [offset, size] for reading from the shared memory > - write-channel: [offset, size] for writing to the shared memory > - interrupts: Interrupts used by the device Irrelevant. Drop. > > This enables kernel drivers and userspace tools to correctly > instantiate and configure Bao IPC shared memory devices based > on the DT description, facilitating efficient communication > between VMs. > Irrelevant. Describe hardware. > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > .../devicetree/bindings/bao/ipcshmem.yaml | 99 +++++++++++++++++++ > .../devicetree/bindings/vendor-prefixes.yaml | 2 + > 2 files changed, 101 insertions(+) > create mode 100644 Documentation/devicetree/bindings/bao/ipcshmem.yaml Filename matching compatible. > > diff --git a/Documentation/devicetree/bindings/bao/ipcshmem.yaml b/Documentation/devicetree/bindings/bao/ipcshmem.yaml > new file mode 100644 > index 000000000000..398ac610c29f > --- /dev/null > +++ b/Documentation/devicetree/bindings/bao/ipcshmem.yaml > @@ -0,0 +1,99 @@ > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause > +%YAML 1.2 > +--- > +$id: "http://devicetree.org/schemas/bao/ipcshmem.yaml#" > +$schema: "http://devicetree.org/meta-schemas/core.yaml#" > + > +title: Bao IPC Shared Memory Device > + > +maintainers: > + - José Martins <jose@osyx.tech> > + - David Cerdeira <davidmcerdeira@osyx.tech> > + - João Peixoto <joaopeixoto@osyx.tech> > + > +description: | > + Shared memory based communication device for Bao hypervisor guests. > + It allows the kernel to interface with guests running under > + the Bao hypervisor, providing a character device interface > + for exchanging data through dedicated shared-memory regions. > + > +properties: > + compatible: > + const: "bao,ipcshmem" > + > + reg: > + description: | > + Memory resource for the shared memory device. > + maxItems: 4 > + type: array > + items: > + type: integer Don't send us LLM junk. Nothing here is even close to proper bindings, which means: 1. You did not write it yourself, because it is impossible to come with syntax like that (just does not exist), 2. Did not even bother to test it. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 2/5] virt: add Bao IPC shared memory driver 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto 2025-12-24 13:52 ` [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto @ 2025-12-24 13:52 ` joaopeixoto 2025-12-24 15:53 ` Greg KH ` (2 more replies) 2025-12-24 13:52 ` [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto ` (4 subsequent siblings) 6 siblings, 3 replies; 31+ messages in thread From: joaopeixoto @ 2025-12-24 13:52 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> Add a new driver providing an interface for communication with guests hosted by the Bao hypervisor using shared-memory channels. The driver exposes read/write regions defined in device tree and notifies the hypervisor via an architecture-specific hypercall (SMC/HVC on ARM and SBI ecall on RISC-V). The patch introduces: - drivers/bao/ with the initial Bao IPC shared-memory implementation - Kconfig entry enabling BAO_SHMEM - Makefile integration for building the driver - A character device interface supporting mmap(), read(), and write() - Platform driver support using DT properties for channel layout Each device instance maps its assigned shared-memory region, validates read/write channel configuration, and exposes a /dev/baoipc<N> node used by user space to exchange data with Bao guests. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 1 + drivers/virt/bao/Kconfig | 3 + drivers/virt/bao/Makefile | 3 + drivers/virt/bao/ipcshmem/Kconfig | 9 + drivers/virt/bao/ipcshmem/Makefile | 3 + drivers/virt/bao/ipcshmem/ipcshmem.c | 539 +++++++++++++++++++++++++++ 7 files changed, 560 insertions(+) create mode 100644 drivers/virt/bao/Kconfig create mode 100644 drivers/virt/bao/Makefile create mode 100644 drivers/virt/bao/ipcshmem/Kconfig create mode 100644 drivers/virt/bao/ipcshmem/Makefile create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 52eb7e4ba71f..cb98c4c52fd1 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -47,6 +47,8 @@ source "drivers/virt/nitro_enclaves/Kconfig" source "drivers/virt/acrn/Kconfig" +source "drivers/virt/bao/Kconfig" + endif source "drivers/virt/coco/Kconfig" diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index f29901bd7820..623a671f8711 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -10,3 +10,4 @@ obj-y += vboxguest/ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-y += coco/ +obj-$(CONFIG_BAO_SHMEM) += bao/ diff --git a/drivers/virt/bao/Kconfig b/drivers/virt/bao/Kconfig new file mode 100644 index 000000000000..4f7929d57475 --- /dev/null +++ b/drivers/virt/bao/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +source "drivers/virt/bao/ipcshmem/Kconfig" diff --git a/drivers/virt/bao/Makefile b/drivers/virt/bao/Makefile new file mode 100644 index 000000000000..68f5d3f282c4 --- /dev/null +++ b/drivers/virt/bao/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_BAO_SHMEM) += ipcshmem/ diff --git a/drivers/virt/bao/ipcshmem/Kconfig b/drivers/virt/bao/ipcshmem/Kconfig new file mode 100644 index 000000000000..42690073e819 --- /dev/null +++ b/drivers/virt/bao/ipcshmem/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +config BAO_SHMEM + tristate "Bao hypervisor shared memory support" + + help + This enables support for Bao shared memory communication. + It allows the kernel to interface with guests running under + the Bao hypervisor, providing a character device interface + for exchanging data through dedicated shared-memory regions. diff --git a/drivers/virt/bao/ipcshmem/Makefile b/drivers/virt/bao/ipcshmem/Makefile new file mode 100644 index 000000000000..e027dcdb06aa --- /dev/null +++ b/drivers/virt/bao/ipcshmem/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_BAO_SHMEM) += bao.o +bao-objs += ipcshmem.o diff --git a/drivers/virt/bao/ipcshmem/ipcshmem.c b/drivers/virt/bao/ipcshmem/ipcshmem.c new file mode 100644 index 000000000000..cadb79bfca6e --- /dev/null +++ b/drivers/virt/bao/ipcshmem/ipcshmem.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor IPC Through Shared-memory Driver + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * David Cerdeira <davidmcerdeira@osyx.tech> + * José Martins <jose@osyx.tech> + * João Peixoto <joaopeixoto@osyx.tech> + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/platform_device.h> +#include <linux/ioctl.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/mm.h> + +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM) +#include <linux/arm-smccc.h> +#include <asm/memory.h> +#elif CONFIG_RISCV +#include <asm/sbi.h> +#endif + +#define DEV_NAME "baoipc" +#define MAX_DEVICES 16 +#define NAME_LEN 32 + +static dev_t bao_ipcshmem_devt; +struct class *cl; + +/** + * struct bao_ipcshmem - Bao IPC shared memory device + * @cdev: Character device interface + * @dev: Device structure + * @id: Device instance ID + * @label: Name/label of the device + * @read_base: Base address of the read channel + * @read_size: Size of the read channel + * @write_base: Base address of the write channel + * @write_size: Size of the write channel + * @physical_base: Physical memory base address + * @shmem_size: Total size of the shared memory region + */ +struct bao_ipcshmem { + struct cdev cdev; + struct device *dev; + int id; + char label[NAME_LEN]; + void *read_base; + size_t read_size; + void *write_base; + size_t write_size; + phys_addr_t *physical_base; + size_t shmem_size; +}; + +#ifdef CONFIG_ARM64 +/** + * bao_ipcshmem_notify - Notify the Bao hypervisor of an IPC shared memory event (ARM64) + * @dev: IPC shared memory device + * + * Executes a fast SMC hypercall to notify the hypervisor of an event + * associated with the given IPC shared memory device. + * + * Return: Hypercall return value. + */ +static uint64_t bao_ipcshmem_notify(struct bao_ipcshmem *dev) +{ + register uint64_t x0 asm("x0") = + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, + ARM_SMCCC_OWNER_VENDOR_HYP, 1); + register uint64_t x1 asm("x1") = dev->id; + register uint64_t x2 asm("x2") = 0; + + asm volatile("hvc 0\t\n" : "=r"(x0) : "r"(x0), "r"(x1), "r"(x2)); + + return x0; +} +#elif CONFIG_ARM +/** + * bao_ipcshmem_notify - Notify the Bao hypervisor of an IPC shared memory event (ARM) + * @dev: IPC shared memory device + * + * Executes a fast SMC hypercall to notify the hypervisor of an event + * associated with the given IPC shared memory device. + * + * Return: Hypercall return value. + */ +static uint32_t bao_ipcshmem_notify(struct bao_ipcshmem *dev) +{ + register uint32_t r0 asm("r0") = + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, + ARM_SMCCC_OWNER_VENDOR_HYP, 1); + register uint32_t r1 asm("r1") = dev->id; + register uint32_t r2 asm("r2") = 0; + + asm volatile("hvc #0\t\n" : "=r"(r0) : "r"(r0), "r"(r1), "r"(r2)); + + return r0; +} +#elif CONFIG_RISCV +/** + * bao_ipcshmem_notify - Notify the Bao hypervisor of an IPC shared memory event (RISC-V) + * @dev: IPC shared memory device + * + * Executes an SBI call to notify the Bao hypervisor of an IPC shared memory event. + * + * Return: SBI call error code. + */ +static uint64_t bao_ipcshmem_notify(struct bao_ipcshmem *dev) +{ + struct sbiret ret = sbi_ecall(0x08000ba0, 1, dev->id, 0, 0, 0, 0, 0); + + return ret.error; +} +#endif + +/** + * bao_ipcshmem_mmap_fops - mmap handler for IPC shared memory + * @filp: File pointer + * @vma: Virtual memory area + * + * Maps the physical shared memory of the Bao IPC device into + * userspace using remap_pfn_range. + * + * Return: 0 on success, -EFAULT on failure. + */ +static int bao_ipcshmem_mmap_fops(struct file *filp, struct vm_area_struct *vma) +{ + struct bao_ipcshmem *bao = filp->private_data; + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + phys_addr_t paddr; + + if (WARN_ON_ONCE(!bao)) + return -ENODEV; + + if (!vsize) + return -EINVAL; + + if (offset >= bao->shmem_size) + return -EINVAL; + + if (vsize > bao->shmem_size - offset) + return -EINVAL; + + paddr = (phys_addr_t)bao->physical_base + offset; + + if (!PAGE_ALIGNED(paddr)) + return -EINVAL; + + if (remap_pfn_range(vma, vma->vm_start, + paddr >> PAGE_SHIFT, + vsize, vma->vm_page_prot)) + return -EFAULT; + + return 0; +} + +/** + * bao_ipcshmem_read_fops - read handler for IPC shared memory + * @filp: File pointer + * @buf: Userspace buffer + * @count: Number of bytes to read + * @ppos: File offset + * + * Copies data from the Bao IPC read buffer to userspace. + * + * Return: Number of bytes read, or 0 if EOF. + */ +static ssize_t bao_ipcshmem_read_fops(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct bao_ipcshmem *bao = filp->private_data; + size_t available; + + if (WARN_ON_ONCE(!bao || !buf || !ppos)) + return -EINVAL; + + if (*ppos >= bao->read_size) + return 0; + + available = bao->read_size - *ppos; + if (count > available) + count = available; + + if (copy_to_user(buf, bao->read_base + *ppos, count)) + return -EFAULT; + + *ppos += count; + return count; +} + +/** + * bao_ipcshmem_write_fops - write handler for IPC shared memory + * @filp: File pointer + * @buf: Userspace buffer + * @count: Number of bytes to write + * @ppos: File offset + * + * Copies data from userspace to the Bao IPC write buffer and + * notifies the hypervisor of the update. + * + * Return: Number of bytes written. + */ +static ssize_t bao_ipcshmem_write_fops(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct bao_ipcshmem *bao = filp->private_data; + size_t avail; + + if (WARN_ON_ONCE(!bao || !buf || !ppos)) + return -EINVAL; + + if (*ppos >= bao->write_size) + return 0; + + avail = bao->write_size - *ppos; + if (count > avail) + count = avail; + + if (copy_from_user(bao->write_base + *ppos, buf, count)) + return -EFAULT; + + *ppos += count; + + /* Notify any listeners that new data is available */ + bao_ipcshmem_notify(bao); + + return count; +} + +/** + * bao_ipcshmem_open_fops - open handler for IPC shared memory + * @inode: Inode pointer + * @filp: File pointer + * + * Associates the file with the Bao IPC device and increments + * the kobject reference. + * + * Return: 0 on success. + */ +static int bao_ipcshmem_open_fops(struct inode *inode, struct file *filp) +{ + struct bao_ipcshmem *bao; + + if (WARN_ON_ONCE(!inode || !filp)) + return -EINVAL; + + bao = container_of(inode->i_cdev, struct bao_ipcshmem, cdev); + filp->private_data = bao; + + kobject_get(&bao->dev->kobj); + + return 0; +} + +/** + * bao_ipcshmem_release_fops - release handler for IPC shared memory + * @inode: Inode pointer + * @filp: File pointer + * + * Disassociates the file from the Bao IPC device and decrements + * the kobject reference. + * + * Return: 0 on success. + */ +static int bao_ipcshmem_release_fops(struct inode *inode, struct file *filp) +{ + struct bao_ipcshmem *bao; + + if (WARN_ON_ONCE(!inode || !filp)) + return -EINVAL; + + bao = container_of(inode->i_cdev, struct bao_ipcshmem, cdev); + filp->private_data = NULL; + + kobject_put(&bao->dev->kobj); + + return 0; +} + +static const struct file_operations bao_ipcshmem_fops = { + .owner = THIS_MODULE, + .read = bao_ipcshmem_read_fops, + .write = bao_ipcshmem_write_fops, + .mmap = bao_ipcshmem_mmap_fops, + .open = bao_ipcshmem_open_fops, + .release = bao_ipcshmem_release_fops +}; + +/** + * bao_ipcshmem_register - Register a Bao IPC shared memory device + * @pdev: Platform device + * + * Maps the shared memory region, validates channel layout, initializes + * the read/write buffers, registers the character device, and creates + * the sysfs device entry. + * + * Return: 0 on success, negative error code on failure. + */ +static int bao_ipcshmem_register(struct platform_device *pdev) +{ + int ret = 0, id = -1; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct module *owner = THIS_MODULE; + struct resource *r; + dev_t devt; + resource_size_t shmem_size; + u32 write_offset, read_offset, write_size, read_size; + bool rd_in_range, wr_in_range, disjoint; + void *shmem_base_addr = NULL; + struct bao_ipcshmem *bao; + + /* Get memory resource */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(dev, "failed to get memory resource\n"); + return -EINVAL; + } + + /* Read channel offsets and sizes */ + of_property_read_u32_index(np, "read-channel", 0, &read_offset); + of_property_read_u32_index(np, "read-channel", 1, &read_size); + of_property_read_u32_index(np, "write-channel", 0, &write_offset); + of_property_read_u32_index(np, "write-channel", 1, &write_size); + + /* Validate memory layout */ + rd_in_range = (r->start + read_offset + read_size) <= r->end; + wr_in_range = (r->start + write_offset + write_size) <= r->end; + disjoint = ((read_offset + read_size) <= write_offset) || + ((write_offset + write_size) <= read_offset); + + if (!rd_in_range || !wr_in_range || !disjoint) { + dev_err(dev, "invalid channel layout\n"); + dev_err(dev, "rd_in_range=%d, wr_in_range=%d, disjoint=%d\n", + rd_in_range, wr_in_range, disjoint); + return -EINVAL; + } + + /* Map shared memory */ + shmem_size = r->end - r->start + 1; + shmem_base_addr = memremap(r->start, shmem_size, MEMREMAP_WB); + if (!shmem_base_addr) { + dev_err(dev, "failed to map shared memory\n"); + return -ENOMEM; + } + + /* Read device ID from device tree */ + of_property_read_u32(np, "id", &id); + if (id >= MAX_DEVICES) { + dev_err(dev, "invalid device id %d\n", id); + ret = -EINVAL; + goto err_unmap; + } + + /* Allocate and initialize device structure */ + bao = devm_kzalloc(dev, sizeof(*bao), GFP_KERNEL); + if (!bao) { + ret = -ENOMEM; + goto err_unmap; + } + + snprintf(bao->label, NAME_LEN, "%s%d", DEV_NAME, id); + bao->id = id; + bao->read_size = read_size; + bao->write_size = write_size; + bao->read_base = shmem_base_addr + read_offset; + bao->write_base = shmem_base_addr + write_offset; + bao->physical_base = (void *)r->start; + bao->shmem_size = shmem_size; + + cdev_init(&bao->cdev, &bao_ipcshmem_fops); + bao->cdev.owner = owner; + + /* Add character device */ + devt = MKDEV(MAJOR(bao_ipcshmem_devt), id); + ret = cdev_add(&bao->cdev, devt, 1); + if (ret) + goto err_unmap; + + /* Create device node */ + bao->dev = device_create(cl, dev, devt, bao, bao->label); + if (IS_ERR(bao->dev)) { + ret = PTR_ERR(bao->dev); + goto err_cdev; + } + dev_set_drvdata(bao->dev, bao); + + return 0; + +err_cdev: + cdev_del(&bao->cdev); +err_unmap: + memunmap(shmem_base_addr); + dev_err(dev, "failed initialization\n"); + return ret; +} + +/** + * bao_ipcshmem_unregister - Unregister a Bao IPC shared memory device + * @pdev: Platform device + * + * Unmaps the shared memory region, destroys the sysfs device, and + * deletes the character device. + */ +static void bao_ipcshmem_unregister(struct platform_device *pdev) +{ + struct bao_ipcshmem *bao; + void *shmem_base; + + /* Retrieve the driver data */ + bao = dev_get_drvdata(&pdev->dev); + if (!bao) + return; + + /* Unmap shared memory */ + if (bao->read_base) + shmem_base = (void *)((uintptr_t)bao->read_base - + ((uintptr_t)bao->read_base - (uintptr_t)bao->physical_base)); + else + shmem_base = NULL; + + if (shmem_base) + memunmap(shmem_base); + + /* Destroy device node */ + if (bao->dev) + device_destroy(cl, bao->cdev.dev); + + /* Delete character device */ + cdev_del(&bao->cdev); +} + +static const struct of_device_id of_bao_ipcshmem_match[] = { + { + .compatible = "bao,ipcshmem", + }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, of_bao_ipcshmem_match); + +static struct platform_driver bao_ipcshmem_driver = { + .probe = bao_ipcshmem_register, + .remove = bao_ipcshmem_unregister, + .driver = { + .name = DEV_NAME, + .of_match_table = of_bao_ipcshmem_match, + }, +}; + +/** + * bao_ipcshmem_init - Module initialization for Bao IPC shared memory + * + * Creates a device class, allocates a range of character device numbers, + * and registers the platform driver. + * + * Return: 0 on success, negative error code on failure. + */ +static int __init bao_ipcshmem_init(void) +{ + int ret; + + /* Create device class */ + cl = class_create(DEV_NAME); + if (IS_ERR(cl)) { + ret = PTR_ERR(cl); + pr_err("unable to create class %s\n", DEV_NAME); + return ret; + } + + /* Allocate major/minor numbers for character devices */ + ret = alloc_chrdev_region(&bao_ipcshmem_devt, 0, MAX_DEVICES, DEV_NAME); + if (ret) { + pr_err("unable to allocate chrdev region for %s\n", DEV_NAME); + goto err_class_destroy; + } + + /* Register platform driver */ + ret = platform_driver_register(&bao_ipcshmem_driver); + if (ret) { + pr_err("unable to register platform driver for %s\n", DEV_NAME); + goto err_unregister_chrdev; + } + + return 0; + +err_unregister_chrdev: + unregister_chrdev_region(bao_ipcshmem_devt, MAX_DEVICES); +err_class_destroy: + class_destroy(cl); + return ret; +} + +/** + * bao_ipcshmem_exit - Module exit for Bao IPC shared memory + * + * Unregisters the platform driver, releases the character device region, + * and destroys the device class. + */ +static void __exit bao_ipcshmem_exit(void) +{ + /* Unregister the platform driver */ + platform_driver_unregister(&bao_ipcshmem_driver); + + /* Release the allocated character device numbers */ + unregister_chrdev_region(bao_ipcshmem_devt, MAX_DEVICES); + + /* Destroy the device class */ + class_destroy(cl); +} + +module_init(bao_ipcshmem_init); +module_exit(bao_ipcshmem_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Cerdeira <davidmcerdeira@osyx.tech>"); +MODULE_AUTHOR("José Martins <jose@osyx.tech>"); +MODULE_AUTHOR("João Peixoto <joaopeixoto@osyx.tech>"); +MODULE_DESCRIPTION("Bao Hypervisor IPC Through Shared-memory Driver"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 2/5] virt: add Bao IPC shared memory driver 2025-12-24 13:52 ` [PATCH 2/5] virt: add Bao IPC shared memory driver joaopeixoto @ 2025-12-24 15:53 ` Greg KH 2025-12-24 15:54 ` Greg KH 2025-12-25 9:02 ` Krzysztof Kozlowski 2 siblings, 0 replies; 31+ messages in thread From: Greg KH @ 2025-12-24 15:53 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Dec 24, 2025 at 01:52:14PM +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > Add a new driver providing an interface for communication with guests > hosted by the Bao hypervisor using shared-memory channels. The driver > exposes read/write regions defined in device tree and notifies the > hypervisor via an architecture-specific hypercall (SMC/HVC on ARM and > SBI ecall on RISC-V). > > The patch introduces: > - drivers/bao/ with the initial Bao IPC shared-memory implementation > - Kconfig entry enabling BAO_SHMEM > - Makefile integration for building the driver > - A character device interface supporting mmap(), read(), and write() > - Platform driver support using DT properties for channel layout > > Each device instance maps its assigned shared-memory region, validates > read/write channel configuration, and exposes a /dev/baoipc<N> node > used by user space to exchange data with Bao guests. > > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > drivers/virt/Kconfig | 2 + > drivers/virt/Makefile | 1 + > drivers/virt/bao/Kconfig | 3 + > drivers/virt/bao/Makefile | 3 + > drivers/virt/bao/ipcshmem/Kconfig | 9 + > drivers/virt/bao/ipcshmem/Makefile | 3 + > drivers/virt/bao/ipcshmem/ipcshmem.c | 539 +++++++++++++++++++++++++++ Why two subdirs deep for a single .c file? Why not just put this in drivers/virt/ instead? thanks, greg k-h ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 2/5] virt: add Bao IPC shared memory driver 2025-12-24 13:52 ` [PATCH 2/5] virt: add Bao IPC shared memory driver joaopeixoto 2025-12-24 15:53 ` Greg KH @ 2025-12-24 15:54 ` Greg KH 2025-12-25 9:02 ` Krzysztof Kozlowski 2 siblings, 0 replies; 31+ messages in thread From: Greg KH @ 2025-12-24 15:54 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Dec 24, 2025 at 01:52:14PM +0000, joaopeixoto@osyx.tech wrote: > +struct class *cl; Why not just use the misc device api instead? No need for a full class for just one char device, right? Or even if you have multiple ones, again, no need for a whole class. That should make the code much simpler overall. thanks, greg k-h ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 2/5] virt: add Bao IPC shared memory driver 2025-12-24 13:52 ` [PATCH 2/5] virt: add Bao IPC shared memory driver joaopeixoto 2025-12-24 15:53 ` Greg KH 2025-12-24 15:54 ` Greg KH @ 2025-12-25 9:02 ` Krzysztof Kozlowski 2 siblings, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2025-12-25 9:02 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Dec 24, 2025 at 01:52:14PM +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > Add a new driver providing an interface for communication with guests > hosted by the Bao hypervisor using shared-memory channels. The driver > exposes read/write regions defined in device tree and notifies the > hypervisor via an architecture-specific hypercall (SMC/HVC on ARM and > SBI ecall on RISC-V). > > The patch introduces: > - drivers/bao/ with the initial Bao IPC shared-memory implementation > - Kconfig entry enabling BAO_SHMEM > - Makefile integration for building the driver > - A character device interface supporting mmap(), read(), and write() > - Platform driver support using DT properties for channel layout > > Each device instance maps its assigned shared-memory region, validates > read/write channel configuration, and exposes a /dev/baoipc<N> node > used by user space to exchange data with Bao guests. > > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > drivers/virt/Kconfig | 2 + > drivers/virt/Makefile | 1 + > drivers/virt/bao/Kconfig | 3 + > drivers/virt/bao/Makefile | 3 + > drivers/virt/bao/ipcshmem/Kconfig | 9 + > drivers/virt/bao/ipcshmem/Makefile | 3 + > drivers/virt/bao/ipcshmem/ipcshmem.c | 539 +++++++++++++++++++++++++++ > 7 files changed, 560 insertions(+) > create mode 100644 drivers/virt/bao/Kconfig > create mode 100644 drivers/virt/bao/Makefile > create mode 100644 drivers/virt/bao/ipcshmem/Kconfig > create mode 100644 drivers/virt/bao/ipcshmem/Makefile > create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c > > diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig > index 52eb7e4ba71f..cb98c4c52fd1 100644 > --- a/drivers/virt/Kconfig > +++ b/drivers/virt/Kconfig > @@ -47,6 +47,8 @@ source "drivers/virt/nitro_enclaves/Kconfig" > > source "drivers/virt/acrn/Kconfig" > > +source "drivers/virt/bao/Kconfig" > + > endif > > source "drivers/virt/coco/Kconfig" > diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile > index f29901bd7820..623a671f8711 100644 > --- a/drivers/virt/Makefile > +++ b/drivers/virt/Makefile > @@ -10,3 +10,4 @@ obj-y += vboxguest/ > obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ > obj-$(CONFIG_ACRN_HSM) += acrn/ > obj-y += coco/ > +obj-$(CONFIG_BAO_SHMEM) += bao/ > diff --git a/drivers/virt/bao/Kconfig b/drivers/virt/bao/Kconfig > new file mode 100644 > index 000000000000..4f7929d57475 > --- /dev/null > +++ b/drivers/virt/bao/Kconfig > @@ -0,0 +1,3 @@ > +# SPDX-License-Identifier: GPL-2.0 > + > +source "drivers/virt/bao/ipcshmem/Kconfig" > diff --git a/drivers/virt/bao/Makefile b/drivers/virt/bao/Makefile > new file mode 100644 > index 000000000000..68f5d3f282c4 > --- /dev/null > +++ b/drivers/virt/bao/Makefile > @@ -0,0 +1,3 @@ > +# SPDX-License-Identifier: GPL-2.0 > + > +obj-$(CONFIG_BAO_SHMEM) += ipcshmem/ > diff --git a/drivers/virt/bao/ipcshmem/Kconfig b/drivers/virt/bao/ipcshmem/Kconfig > new file mode 100644 > index 000000000000..42690073e819 > --- /dev/null > +++ b/drivers/virt/bao/ipcshmem/Kconfig > @@ -0,0 +1,9 @@ > +# SPDX-License-Identifier: GPL-2.0 > +config BAO_SHMEM > + tristate "Bao hypervisor shared memory support" > + > + help > + This enables support for Bao shared memory communication. > + It allows the kernel to interface with guests running under > + the Bao hypervisor, providing a character device interface > + for exchanging data through dedicated shared-memory regions. > diff --git a/drivers/virt/bao/ipcshmem/Makefile b/drivers/virt/bao/ipcshmem/Makefile > new file mode 100644 > index 000000000000..e027dcdb06aa > --- /dev/null > +++ b/drivers/virt/bao/ipcshmem/Makefile > @@ -0,0 +1,3 @@ > +# SPDX-License-Identifier: GPL-2.0 > +obj-$(CONFIG_BAO_SHMEM) += bao.o > +bao-objs += ipcshmem.o > diff --git a/drivers/virt/bao/ipcshmem/ipcshmem.c b/drivers/virt/bao/ipcshmem/ipcshmem.c > new file mode 100644 > index 000000000000..cadb79bfca6e > --- /dev/null > +++ b/drivers/virt/bao/ipcshmem/ipcshmem.c > @@ -0,0 +1,539 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Bao Hypervisor IPC Through Shared-memory Driver > + * > + * Copyright (c) Bao Project and Contributors. All rights reserved. > + * > + * Authors: > + * David Cerdeira <davidmcerdeira@osyx.tech> > + * José Martins <jose@osyx.tech> > + * João Peixoto <joaopeixoto@osyx.tech> > + */ > + > +#include <linux/types.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/uaccess.h> > +#include <linux/fs.h> > +#include <linux/io.h> > +#include <linux/ioport.h> > +#include <linux/interrupt.h> > +#include <linux/of.h> > +#include <linux/io.h> > +#include <linux/mutex.h> > +#include <linux/poll.h> > +#include <linux/platform_device.h> > +#include <linux/ioctl.h> > +#include <linux/cdev.h> > +#include <linux/device.h> > +#include <linux/spinlock.h> > +#include <linux/mutex.h> > +#include <linux/wait.h> > +#include <linux/mm.h> > + > +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM) > +#include <linux/arm-smccc.h> > +#include <asm/memory.h> > +#elif CONFIG_RISCV > +#include <asm/sbi.h> > +#endif > + > +#define DEV_NAME "baoipc" > +#define MAX_DEVICES 16 > +#define NAME_LEN 32 > + > +static dev_t bao_ipcshmem_devt; > +struct class *cl; > + > +/** > + * struct bao_ipcshmem - Bao IPC shared memory device > + * @cdev: Character device interface > + * @dev: Device structure > + * @id: Device instance ID > + * @label: Name/label of the device > + * @read_base: Base address of the read channel > + * @read_size: Size of the read channel > + * @write_base: Base address of the write channel > + * @write_size: Size of the write channel > + * @physical_base: Physical memory base address > + * @shmem_size: Total size of the shared memory region > + */ > +struct bao_ipcshmem { > + struct cdev cdev; > + struct device *dev; > + int id; > + char label[NAME_LEN]; > + void *read_base; > + size_t read_size; > + void *write_base; > + size_t write_size; > + phys_addr_t *physical_base; > + size_t shmem_size; > +}; > + > +#ifdef CONFIG_ARM64 No ifdefs. Read Linux coding style first. > +/** > + * bao_ipcshmem_notify - Notify the Bao hypervisor of an IPC shared memory event (ARM64) > + * @dev: IPC shared memory device > + * > + * Executes a fast SMC hypercall to notify the hypervisor of an event > + * associated with the given IPC shared memory device. > + * > + * Return: Hypercall return value. > + */ > +static uint64_t bao_ipcshmem_notify(struct bao_ipcshmem *dev) > +{ > + register uint64_t x0 asm("x0") = > + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, > + ARM_SMCCC_OWNER_VENDOR_HYP, 1); > + register uint64_t x1 asm("x1") = dev->id; > + register uint64_t x2 asm("x2") = 0; > + > + asm volatile("hvc 0\t\n" : "=r"(x0) : "r"(x0), "r"(x1), "r"(x2)); > + > + return x0; > +} > +#elif CONFIG_ARM > +/** > + * bao_ipcshmem_notify - Notify the Bao hypervisor of an IPC shared memory event (ARM) > + * @dev: IPC shared memory device > + * > + * Executes a fast SMC hypercall to notify the hypervisor of an event > + * associated with the given IPC shared memory device. > + * > + * Return: Hypercall return value. > + */ > +static uint32_t bao_ipcshmem_notify(struct bao_ipcshmem *dev) > +{ > + register uint32_t r0 asm("r0") = > + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, > + ARM_SMCCC_OWNER_VENDOR_HYP, 1); > + register uint32_t r1 asm("r1") = dev->id; > + register uint32_t r2 asm("r2") = 0; > + > + asm volatile("hvc #0\t\n" : "=r"(r0) : "r"(r0), "r"(r1), "r"(r2)); > + > + return r0; > +} > +#elif CONFIG_RISCV > +/** > + * bao_ipcshmem_notify - Notify the Bao hypervisor of an IPC shared memory event (RISC-V) > + * @dev: IPC shared memory device > + * > + * Executes an SBI call to notify the Bao hypervisor of an IPC shared memory event. > + * > + * Return: SBI call error code. > + */ > +static uint64_t bao_ipcshmem_notify(struct bao_ipcshmem *dev) > +{ > + struct sbiret ret = sbi_ecall(0x08000ba0, 1, dev->id, 0, 0, 0, 0, 0); > + > + return ret.error; > +} > +#endif > + > +/** > + * bao_ipcshmem_mmap_fops - mmap handler for IPC shared memory > + * @filp: File pointer > + * @vma: Virtual memory area > + * > + * Maps the physical shared memory of the Bao IPC device into > + * userspace using remap_pfn_range. > + * > + * Return: 0 on success, -EFAULT on failure. > + */ > +static int bao_ipcshmem_mmap_fops(struct file *filp, struct vm_area_struct *vma) > +{ > + struct bao_ipcshmem *bao = filp->private_data; > + unsigned long vsize = vma->vm_end - vma->vm_start; > + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; > + phys_addr_t paddr; > + > + if (WARN_ON_ONCE(!bao)) > + return -ENODEV; > + > + if (!vsize) > + return -EINVAL; > + > + if (offset >= bao->shmem_size) > + return -EINVAL; > + > + if (vsize > bao->shmem_size - offset) > + return -EINVAL; > + > + paddr = (phys_addr_t)bao->physical_base + offset; > + > + if (!PAGE_ALIGNED(paddr)) > + return -EINVAL; > + > + if (remap_pfn_range(vma, vma->vm_start, > + paddr >> PAGE_SHIFT, > + vsize, vma->vm_page_prot)) > + return -EFAULT; > + > + return 0; > +} > + > +/** > + * bao_ipcshmem_read_fops - read handler for IPC shared memory > + * @filp: File pointer > + * @buf: Userspace buffer > + * @count: Number of bytes to read > + * @ppos: File offset > + * > + * Copies data from the Bao IPC read buffer to userspace. > + * > + * Return: Number of bytes read, or 0 if EOF. > + */ > +static ssize_t bao_ipcshmem_read_fops(struct file *filp, char __user *buf, > + size_t count, loff_t *ppos) > +{ > + struct bao_ipcshmem *bao = filp->private_data; > + size_t available; > + > + if (WARN_ON_ONCE(!bao || !buf || !ppos)) > + return -EINVAL; > + > + if (*ppos >= bao->read_size) > + return 0; > + > + available = bao->read_size - *ppos; > + if (count > available) > + count = available; > + > + if (copy_to_user(buf, bao->read_base + *ppos, count)) > + return -EFAULT; > + > + *ppos += count; > + return count; > +} > + > +/** > + * bao_ipcshmem_write_fops - write handler for IPC shared memory > + * @filp: File pointer > + * @buf: Userspace buffer > + * @count: Number of bytes to write > + * @ppos: File offset > + * > + * Copies data from userspace to the Bao IPC write buffer and > + * notifies the hypervisor of the update. > + * > + * Return: Number of bytes written. > + */ > +static ssize_t bao_ipcshmem_write_fops(struct file *filp, const char __user *buf, > + size_t count, loff_t *ppos) > +{ > + struct bao_ipcshmem *bao = filp->private_data; > + size_t avail; > + > + if (WARN_ON_ONCE(!bao || !buf || !ppos)) > + return -EINVAL; > + > + if (*ppos >= bao->write_size) > + return 0; > + > + avail = bao->write_size - *ppos; > + if (count > avail) > + count = avail; > + > + if (copy_from_user(bao->write_base + *ppos, buf, count)) > + return -EFAULT; > + > + *ppos += count; > + > + /* Notify any listeners that new data is available */ > + bao_ipcshmem_notify(bao); > + > + return count; > +} > + > +/** > + * bao_ipcshmem_open_fops - open handler for IPC shared memory > + * @inode: Inode pointer > + * @filp: File pointer > + * > + * Associates the file with the Bao IPC device and increments > + * the kobject reference. > + * > + * Return: 0 on success. > + */ > +static int bao_ipcshmem_open_fops(struct inode *inode, struct file *filp) > +{ > + struct bao_ipcshmem *bao; > + > + if (WARN_ON_ONCE(!inode || !filp)) > + return -EINVAL; > + > + bao = container_of(inode->i_cdev, struct bao_ipcshmem, cdev); > + filp->private_data = bao; > + > + kobject_get(&bao->dev->kobj); > + > + return 0; > +} > + > +/** > + * bao_ipcshmem_release_fops - release handler for IPC shared memory > + * @inode: Inode pointer > + * @filp: File pointer > + * > + * Disassociates the file from the Bao IPC device and decrements > + * the kobject reference. > + * > + * Return: 0 on success. > + */ > +static int bao_ipcshmem_release_fops(struct inode *inode, struct file *filp) > +{ > + struct bao_ipcshmem *bao; > + > + if (WARN_ON_ONCE(!inode || !filp)) > + return -EINVAL; > + > + bao = container_of(inode->i_cdev, struct bao_ipcshmem, cdev); > + filp->private_data = NULL; > + > + kobject_put(&bao->dev->kobj); > + > + return 0; > +} > + > +static const struct file_operations bao_ipcshmem_fops = { > + .owner = THIS_MODULE, > + .read = bao_ipcshmem_read_fops, > + .write = bao_ipcshmem_write_fops, > + .mmap = bao_ipcshmem_mmap_fops, > + .open = bao_ipcshmem_open_fops, > + .release = bao_ipcshmem_release_fops > +}; > + > +/** > + * bao_ipcshmem_register - Register a Bao IPC shared memory device > + * @pdev: Platform device > + * > + * Maps the shared memory region, validates channel layout, initializes > + * the read/write buffers, registers the character device, and creates > + * the sysfs device entry. > + * > + * Return: 0 on success, negative error code on failure. > + */ > +static int bao_ipcshmem_register(struct platform_device *pdev) NAK, where did you get it from? Probe functions are ABSOLUTELY NEVER called register. > +{ > + int ret = 0, id = -1; > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + struct module *owner = THIS_MODULE; > + struct resource *r; > + dev_t devt; No, read Linux coding style. > + resource_size_t shmem_size; > + u32 write_offset, read_offset, write_size, read_size; > + bool rd_in_range, wr_in_range, disjoint; > + void *shmem_base_addr = NULL; > + struct bao_ipcshmem *bao; This is really poor coding style, barely readable and maintainable. You need to rewtite this driver completely to match what we expect in Linux kernel, for example base your work on last, reviewed code. I am not even looking at rest of this - please prove that you value our time by sending something following Linux kernel style. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto 2025-12-24 13:52 ` [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto 2025-12-24 13:52 ` [PATCH 2/5] virt: add Bao IPC shared memory driver joaopeixoto @ 2025-12-24 13:52 ` joaopeixoto 2025-12-24 16:18 ` Rob Herring (Arm) 2025-12-25 8:58 ` Krzysztof Kozlowski 2025-12-24 13:52 ` [PATCH 4/5] virt: add Bao I/O dispatcher driver joaopeixoto ` (3 subsequent siblings) 6 siblings, 2 replies; 31+ messages in thread From: joaopeixoto @ 2025-12-24 13:52 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> This patch adds a Device Tree binding for the Bao I/O Dispatcher kernel module, which can be loaded into backend VMs. The I/O Dispatcher provides the bridge between the Bao hypervisor Remote I/O system and the frontend device model in userspace, offering a unified API to support various VirtIO backends. The dispatcher handles hypercalls to the Bao hypervisor, IRQ/eventfd forwarding, and provides a character device interface for frontend devices, enabling efficient communication between the hypervisor and userspace device models. The binding documents the following properties: - compatible: "bao,io-dispatcher" - reg: Memory regions for the dispatcher (multiple VirtIO devices) - interrupts: Interrupts used by the devices - interrupt-parent: Parent interrupt controller This enables kernel drivers to correctly instantiate and configure Bao I/O Dispatcher modules based on the DT description. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- .../bindings/bao/io-dispatcher.yaml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Documentation/devicetree/bindings/bao/io-dispatcher.yaml diff --git a/Documentation/devicetree/bindings/bao/io-dispatcher.yaml b/Documentation/devicetree/bindings/bao/io-dispatcher.yaml new file mode 100644 index 000000000000..7795f55d3ff9 --- /dev/null +++ b/Documentation/devicetree/bindings/bao/io-dispatcher.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/bao/io-dispatcher.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Bao I/O Dispatcher Device + +maintainers: + - João Peixoto <joaopeixoto@osyx.tech> + - José Martins <jose@osyx.tech> + - David Cerdeira <davidmcerdeira@osyx.tech> + +description: | + I/O Dispatcher device for Bao hypervisor guests. Handles multiple VirtIO + backend devices and their interrupts. + +properties: + compatible: + const: "bao,io-dispatcher" + description: Device compatible string. + + reg: + description: | + Memory regions for each VirtIO backend device. + maxItems: 20 + type: array + items: + type: integer + + interrupts: + description: | + Interrupt numbers for each VirtIO backend device. + type: array + items: + type: integer + minItems: 3 + maxItems: 3 + + interrupt-parent: + description: Parent interrupt controller node + type: string + +required: + - compatible + - reg + - interrupts + - interrupt-parent + +additionalProperties: false + +examples: + - | + bao_io_dispatcher: bao-io-dispatcher { + compatible = "bao,io-dispatcher"; + reg = <0x0 0x50000000 0x0 0x01000000 + 0x0 0x51000000 0x0 0x01000000 + 0x0 0x52000000 0x0 0x01000000 + 0x0 0x53000000 0x0 0x01000000 + 0x0 0x54000000 0x0 0x01000000>; + interrupts = <0x0 0x08 0x1 + 0x0 0x09 0x1 + 0x0 0x0a 0x1 + 0x0 0x0b 0x1 + 0x0 0x0c 0x1>; + interrupt-parent = <&gic>; + }; -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding 2025-12-24 13:52 ` [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto @ 2025-12-24 16:18 ` Rob Herring (Arm) 2025-12-25 8:58 ` Krzysztof Kozlowski 1 sibling, 0 replies; 31+ messages in thread From: Rob Herring (Arm) @ 2025-12-24 16:18 UTC (permalink / raw) To: joaopeixoto Cc: mani, linux, linux-riscv, nathan, devicetree, catalin.marinas, kever.yang, dan.j.williams, linux-doc, haren, palmer, conor+dt, linux-kernel, linux-arm-kernel, alex, jose, neil.armstrong, gregkh, heiko, prabhakar.mahadev-lad.rj, will, corbet, dev, aou, krzk+dt, maddy, bagasdotme, ajd, pjw, davidmcerdeira On Wed, 24 Dec 2025 13:52:15 +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This patch adds a Device Tree binding for the Bao I/O Dispatcher kernel > module, which can be loaded into backend VMs. The I/O Dispatcher > provides the bridge between the Bao hypervisor Remote I/O system and the > frontend device model in userspace, offering a unified API to support > various VirtIO backends. > > The dispatcher handles hypercalls to the Bao hypervisor, IRQ/eventfd > forwarding, and provides a character device interface for frontend > devices, enabling efficient communication between the hypervisor and > userspace device models. > > The binding documents the following properties: > - compatible: "bao,io-dispatcher" > - reg: Memory regions for the dispatcher (multiple VirtIO devices) > - interrupts: Interrupts used by the devices > - interrupt-parent: Parent interrupt controller > > This enables kernel drivers to correctly instantiate and configure Bao > I/O Dispatcher modules based on the DT description. > > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > .../bindings/bao/io-dispatcher.yaml | 67 +++++++++++++++++++ > 1 file changed, 67 insertions(+) > create mode 100644 Documentation/devicetree/bindings/bao/io-dispatcher.yaml > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: ./Documentation/devicetree/bindings/bao/ipcshmem.yaml:4:6: [error] string value is redundantly quoted with any quotes (quoted-strings) ./Documentation/devicetree/bindings/bao/ipcshmem.yaml:5:10: [error] string value is redundantly quoted with any quotes (quoted-strings) ./Documentation/devicetree/bindings/bao/ipcshmem.yaml:17:1: [error] syntax error: found character '\t' that cannot start any token (syntax) ./Documentation/devicetree/bindings/bao/io-dispatcher.yaml:4:6: [error] string value is redundantly quoted with any quotes (quoted-strings) ./Documentation/devicetree/bindings/bao/io-dispatcher.yaml:5:10: [error] string value is redundantly quoted with any quotes (quoted-strings) ./Documentation/devicetree/bindings/bao/io-dispatcher.yaml:20:12: [error] string value is redundantly quoted with any quotes (quoted-strings) dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/ipcshmem.yaml: ignoring, error parsing file ./Documentation/devicetree/bindings/bao/ipcshmem.yaml:17:1: found a tab character where an indentation space is expected /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties:reg: 'anyOf' conditional failed, one must be fixed: 'type' is not one of ['maxItems', 'description', 'deprecated'] hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. 'items' is not one of ['maxItems', 'description', 'deprecated'] hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. Additional properties are not allowed ('type' was unexpected) hint: Arrays must be described with a combination of minItems/maxItems/items 'maxItems' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] 'type' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] 'items' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties:reg:items: 'anyOf' conditional failed, one must be fixed: 'maxItems' is a required property hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. 'type' is not one of ['maxItems', 'description', 'deprecated'] hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. Additional properties are not allowed ('type' was unexpected) hint: Arrays must be described with a combination of minItems/maxItems/items 'type' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] hint: "items" can be a list defining each entry or a schema applying to all items. A list has an implicit size. A schema requires minItems/maxItems to define the size. hint: cell array properties must define how many entries and what the entries are when there is more than one entry. from schema $id: http://devicetree.org/meta-schemas/cell.yaml 1 was expected hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. hint: cell array properties must define how many entries and what the entries are when there is more than one entry. from schema $id: http://devicetree.org/meta-schemas/cell.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties:reg:type: 'array' is not one of ['boolean', 'object'] from schema $id: http://devicetree.org/meta-schemas/core.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties:interrupts:type: 'array' is not one of ['boolean', 'object'] from schema $id: http://devicetree.org/meta-schemas/core.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties:interrupt-parent:type: 'string' is not one of ['boolean', 'object'] from schema $id: http://devicetree.org/meta-schemas/core.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties:interrupts: 'anyOf' conditional failed, one must be fixed: 'type' is not one of ['maxItems', 'description', 'deprecated'] hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. 'items' is not one of ['maxItems', 'description', 'deprecated'] hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. 'minItems' is not one of ['maxItems', 'description', 'deprecated'] hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. Additional properties are not allowed ('type' was unexpected) hint: Arrays must be described with a combination of minItems/maxItems/items 'type' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] 'items' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] 'minItems' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] 'maxItems' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties:interrupts:items: 'anyOf' conditional failed, one must be fixed: 'maxItems' is a required property hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. 'type' is not one of ['maxItems', 'description', 'deprecated'] hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. Additional properties are not allowed ('type' was unexpected) hint: Arrays must be described with a combination of minItems/maxItems/items 'type' is not one of ['description', 'deprecated', 'const', 'enum', 'minimum', 'maximum', 'multipleOf', 'default', '$ref', 'oneOf'] hint: "items" can be a list defining each entry or a schema applying to all items. A list has an implicit size. A schema requires minItems/maxItems to define the size. hint: cell array properties must define how many entries and what the entries are when there is more than one entry. from schema $id: http://devicetree.org/meta-schemas/cell.yaml 1 was expected hint: Only "maxItems" is required for a single entry if there are no constraints defined for the values. hint: cell array properties must define how many entries and what the entries are when there is more than one entry. from schema $id: http://devicetree.org/meta-schemas/cell.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/bao/io-dispatcher.yaml: properties: False schema does not allow {'description': 'Parent interrupt controller node', 'type': 'string'} from schema $id: http://devicetree.org/meta-schemas/interrupts.yaml make[2]: *** Deleting file 'Documentation/devicetree/bindings/bao/ipcshmem.example.dts' Documentation/devicetree/bindings/bao/ipcshmem.yaml:17:1: found a tab character where an indentation space is expected make[2]: *** [Documentation/devicetree/bindings/Makefile:26: Documentation/devicetree/bindings/bao/ipcshmem.example.dts] Error 1 make[2]: *** Waiting for unfinished jobs.... make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1565: dt_binding_check] Error 2 make: *** [Makefile:248: __sub-make] Error 2 doc reference errors (make refcheckdocs): See https://patchwork.kernel.org/project/devicetree/patch/20251224135217.25350-4-joaopeixoto@osyx.tech The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding 2025-12-24 13:52 ` [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto 2025-12-24 16:18 ` Rob Herring (Arm) @ 2025-12-25 8:58 ` Krzysztof Kozlowski 1 sibling, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2025-12-25 8:58 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Dec 24, 2025 at 01:52:15PM +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This patch adds a Device Tree binding for the Bao I/O Dispatcher kernel > module, which can be loaded into backend VMs. The I/O Dispatcher > provides the bridge between the Bao hypervisor Remote I/O system and the > frontend device model in userspace, offering a unified API to support > various VirtIO backends. > > The dispatcher handles hypercalls to the Bao hypervisor, IRQ/eventfd > forwarding, and provides a character device interface for frontend > devices, enabling efficient communication between the hypervisor and > userspace device models. > > The binding documents the following properties: > - compatible: "bao,io-dispatcher" > - reg: Memory regions for the dispatcher (multiple VirtIO devices) > - interrupts: Interrupts used by the devices > - interrupt-parent: Parent interrupt controller > > This enables kernel drivers to correctly instantiate and configure Bao > I/O Dispatcher modules based on the DT description. > > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > .../bindings/bao/io-dispatcher.yaml | 67 +++++++++++++++++++ > 1 file changed, 67 insertions(+) > create mode 100644 Documentation/devicetree/bindings/bao/io-dispatcher.yaml > > diff --git a/Documentation/devicetree/bindings/bao/io-dispatcher.yaml b/Documentation/devicetree/bindings/bao/io-dispatcher.yaml > new file mode 100644 > index 000000000000..7795f55d3ff9 > --- /dev/null > +++ b/Documentation/devicetree/bindings/bao/io-dispatcher.yaml > @@ -0,0 +1,67 @@ > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause > +%YAML 1.2 > +--- > +$id: "http://devicetree.org/schemas/bao/io-dispatcher.yaml#" You did not even bother to test it. > +$schema: "http://devicetree.org/meta-schemas/core.yaml#" > + > +title: Bao I/O Dispatcher Device > + > +maintainers: > + - João Peixoto <joaopeixoto@osyx.tech> > + - José Martins <jose@osyx.tech> > + - David Cerdeira <davidmcerdeira@osyx.tech> > + > +description: | > + I/O Dispatcher device for Bao hypervisor guests. Handles multiple VirtIO > + backend devices and their interrupts. > + > +properties: > + compatible: > + const: "bao,io-dispatcher" > + description: Device compatible string. > + > + reg: > + description: | > + Memory regions for each VirtIO backend device. > + maxItems: 20 > + type: array > + items: > + type: integer Don't send us LLM junk. You cannot come with such syntax, it does not exist. Sending such LLM output is disregard to our time. NAK Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 4/5] virt: add Bao I/O dispatcher driver 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto ` (2 preceding siblings ...) 2025-12-24 13:52 ` [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto @ 2025-12-24 13:52 ` joaopeixoto 2025-12-25 9:12 ` Krzysztof Kozlowski 2025-12-24 13:52 ` [PATCH 5/5] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings joaopeixoto ` (2 subsequent siblings) 6 siblings, 1 reply; 31+ messages in thread From: joaopeixoto @ 2025-12-24 13:52 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> Introduce the Bao I/O Dispatcher, a kernel module that can be loaded into backend VMs. It provides the bridge between the Bao hypervisor Remote I/O system (Bao's mechanism for forwarding I/O requests from frontend VMs to backend VMs) and the VirtIO backend device, offering a unified API to support various VirtIO backends. This patch includes: - Architecture-specific headers for ARM, ARM64, and RISC-V - Driver framework in drivers/virt/bao/ including dm, driver, intc, io_client, io_dispatcher, ioctls, ioeventfd, and irqfd - Kconfig and Makefile entries to enable building the module - UAPI header for userspace API exposure - Documentation update for ioctl number references Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- .../userspace-api/ioctl/ioctl-number.rst | 2 + arch/arm/include/asm/bao.h | 62 +++ arch/arm64/include/asm/bao.h | 62 +++ arch/riscv/include/asm/bao.h | 61 +++ drivers/virt/Makefile | 1 + drivers/virt/bao/Kconfig | 2 + drivers/virt/bao/Makefile | 1 + drivers/virt/bao/io-dispatcher/Kconfig | 16 + drivers/virt/bao/io-dispatcher/Makefile | 4 + drivers/virt/bao/io-dispatcher/bao_drv.h | 386 ++++++++++++++++ drivers/virt/bao/io-dispatcher/dm.c | 330 +++++++++++++ drivers/virt/bao/io-dispatcher/driver.c | 348 ++++++++++++++ drivers/virt/bao/io-dispatcher/hypercall.h | 30 ++ drivers/virt/bao/io-dispatcher/intc.c | 68 +++ drivers/virt/bao/io-dispatcher/io_client.c | 435 ++++++++++++++++++ .../virt/bao/io-dispatcher/io_dispatcher.c | 207 +++++++++ drivers/virt/bao/io-dispatcher/ioctls.c | 145 ++++++ drivers/virt/bao/io-dispatcher/ioeventfd.c | 336 ++++++++++++++ drivers/virt/bao/io-dispatcher/irqfd.c | 341 ++++++++++++++ include/uapi/linux/bao.h | 124 +++++ 20 files changed, 2961 insertions(+) create mode 100644 arch/arm/include/asm/bao.h create mode 100644 arch/arm64/include/asm/bao.h create mode 100644 arch/riscv/include/asm/bao.h create mode 100644 drivers/virt/bao/io-dispatcher/Kconfig create mode 100644 drivers/virt/bao/io-dispatcher/Makefile create mode 100644 drivers/virt/bao/io-dispatcher/bao_drv.h create mode 100644 drivers/virt/bao/io-dispatcher/dm.c create mode 100644 drivers/virt/bao/io-dispatcher/driver.c create mode 100644 drivers/virt/bao/io-dispatcher/hypercall.h create mode 100644 drivers/virt/bao/io-dispatcher/intc.c create mode 100644 drivers/virt/bao/io-dispatcher/io_client.c create mode 100644 drivers/virt/bao/io-dispatcher/io_dispatcher.c create mode 100644 drivers/virt/bao/io-dispatcher/ioctls.c create mode 100644 drivers/virt/bao/io-dispatcher/ioeventfd.c create mode 100644 drivers/virt/bao/io-dispatcher/irqfd.c create mode 100644 include/uapi/linux/bao.h diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 7232b3544cec..b0dbc307a9cb 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -349,6 +349,8 @@ Code Seq# Include File Comments <mailto:luzmaximilian@gmail.com> 0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver <mailto:luzmaximilian@gmail.com> +0xA6 all uapi/linux/bao.h Bao hypervisor + <mailto:info@bao-project.org> 0xAA 00-3F linux/uapi/linux/userfaultfd.h 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h diff --git a/arch/arm/include/asm/bao.h b/arch/arm/include/asm/bao.h new file mode 100644 index 000000000000..9474ce459987 --- /dev/null +++ b/arch/arm/include/asm/bao.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hypercall for Bao Hypervisor on ARM + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __ASM_ARM_BAO_H +#define __ASM_ARM_BAO_H + +#include <asm/bao.h> +#include <linux/bao.h> +#include <linux/arm-smccc.h> + +/** + * asm_bao_hypercall_remio - Perform a Bao Remote I/O hypercall + * @request: Bao VirtIO request + * + * Executes a Bao Remote I/O hypercall using ARM HVC instruction. + * + * Return: Filled remio_hypercall_ret structure containing the results. + */ +static inline struct remio_hypercall_ret +asm_bao_hypercall_remio(struct bao_virtio_request *request) +{ + register int x0 asm("r0") = + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, + ARM_SMCCC_OWNER_VENDOR_HYP, REMIO_HC_ID); + register u32 x1 asm("r1") = request->dm_id; + register u32 x2 asm("r2") = request->addr; + register u32 x3 asm("r3") = request->op; + register u32 x4 asm("r4") = request->value; + register u32 x5 asm("r5") = request->request_id; + register u32 x6 asm("r6") = 0; + + struct remio_hypercall_ret ret; + + asm volatile("hvc 0\n\t" + : "=r"(x0), "=r"(x1), "=r"(x2), "=r"(x3), "=r"(x4), + "=r"(x5), "=r"(x6) + : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) + : "memory"); + + ret.hyp_ret = 0; + ret.remio_hyp_ret = x0; + ret.pending_requests = x6; + + request->addr = x1; + request->op = x2; + request->value = x3; + request->access_width = x4; + request->request_id = x5; + + return ret; +} + +#endif /* __ASM_ARM_BAO_H */ diff --git a/arch/arm64/include/asm/bao.h b/arch/arm64/include/asm/bao.h new file mode 100644 index 000000000000..047b7204edd4 --- /dev/null +++ b/arch/arm64/include/asm/bao.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hypercall for Bao Hypervisor on ARM64 + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __ASM_ARM64_BAO_H +#define __ASM_ARM64_BAO_H + +#include <asm/bao.h> +#include <linux/bao.h> +#include <linux/arm-smccc.h> + +/** + * asm_bao_hypercall_remio - Perform a Bao Remote I/O hypercall + * @request: Bao VirtIO request + * + * Executes a Bao Remote I/O hypercall using ARM64 HVC instruction. + * + * Return: Filled bao_virtio_request structure containing the results. + */ +static inline struct remio_hypercall_ret +asm_bao_hypercall_remio(struct bao_virtio_request *request) +{ + register int x0 asm("x0") = + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, + ARM_SMCCC_OWNER_VENDOR_HYP, REMIO_HC_ID); + register u64 x1 asm("x1") = request->dm_id; + register u64 x2 asm("x2") = request->addr; + register u64 x3 asm("x3") = request->op; + register u64 x4 asm("x4") = request->value; + register u64 x5 asm("x5") = request->request_id; + register u64 x6 asm("x6") = 0; + + struct remio_hypercall_ret ret; + + asm volatile("hvc 0\n\t" + : "=r"(x0), "=r"(x1), "=r"(x2), "=r"(x3), "=r"(x4), + "=r"(x5), "=r"(x6) + : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) + : "memory"); + + ret.hyp_ret = 0; + ret.remio_hyp_ret = x0; + ret.pending_requests = x6; + + request->addr = x1; + request->op = x2; + request->value = x3; + request->access_width = x4; + request->request_id = x5; + + return ret; +} + +#endif /* __ASM_ARM64_BAO_H */ diff --git a/arch/riscv/include/asm/bao.h b/arch/riscv/include/asm/bao.h new file mode 100644 index 000000000000..e2a1bd492a9a --- /dev/null +++ b/arch/riscv/include/asm/bao.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hypercall for Bao Hypervisor on RISC-V + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __ASM_RISCV_BAO_H +#define __ASM_RISCV_BAO_H + +#include <asm/sbi.h> +#include <linux/bao.h> + +/** + * asm_bao_hypercall_remio - Perform a Bao Remote I/O hypercall + * @request: Bao VirtIO request + * + * Executes a Bao hypercall using inline assembly. + * + * Return: Filled bao_virtio_request structure containing the results. + */ +static inline struct remio_hypercall_ret +asm_bao_hypercall_remio(struct bao_virtio_request *request) +{ + struct remio_hypercall_ret ret; + + register uintptr_t a0 asm("a0") = (uintptr_t)(request->dm_id); + register uintptr_t a1 asm("a1") = (uintptr_t)(request->addr); + register uintptr_t a2 asm("a2") = (uintptr_t)(request->op); + register uintptr_t a3 asm("a3") = (uintptr_t)(request->value); + register uintptr_t a4 asm("a4") = (uintptr_t)(request->request_id); + register uintptr_t a5 asm("a5") = (uintptr_t)(0); + register uintptr_t a6 asm("a6") = (uintptr_t)(REMIO_HC_ID); + register uintptr_t a7 asm("a7") = (uintptr_t)(0x08000ba0); + + asm volatile("ecall" + : "+r"(a0), "+r"(a1), "+r"(a2), "+r"(a3), "+r"(a4), + "+r"(a5), "+r"(a6), "+r"(a7) + : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), + "r"(a6), "r"(a7) + : "memory"); + + ret.hyp_ret = a0; + ret.remio_hyp_ret = a1; + ret.pending_requests = a7; + + request->addr = a2; + request->op = a3; + request->value = a4; + request->access_width = a5; + request->request_id = a6; + + return ret; +} + +#endif /* __ASM_RISCV_BAO_H */ diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index 623a671f8711..8bffc7ccd29e 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-y += coco/ obj-$(CONFIG_BAO_SHMEM) += bao/ +obj-$(CONFIG_BAO_IO_DISPATCHER) += bao/ diff --git a/drivers/virt/bao/Kconfig b/drivers/virt/bao/Kconfig index 4f7929d57475..ab08a20db8c4 100644 --- a/drivers/virt/bao/Kconfig +++ b/drivers/virt/bao/Kconfig @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 source "drivers/virt/bao/ipcshmem/Kconfig" + +source "drivers/virt/bao/io-dispatcher/Kconfig" diff --git a/drivers/virt/bao/Makefile b/drivers/virt/bao/Makefile index 68f5d3f282c4..c463f04cf206 100644 --- a/drivers/virt/bao/Makefile +++ b/drivers/virt/bao/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_BAO_SHMEM) += ipcshmem/ +obj-$(CONFIG_BAO_IO_DISPATCHER) += io-dispatcher/ diff --git a/drivers/virt/bao/io-dispatcher/Kconfig b/drivers/virt/bao/io-dispatcher/Kconfig new file mode 100644 index 000000000000..fc9226e20790 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +config BAO_IO_DISPATCHER + tristate "Bao Hypervisor I/O Dispatcher" + select EVENTFD + + help + The Bao I/O Dispatcher is a kernel module for backend Linux VMs + running under the Bao hypervisor. It establishes the connection + between the Remote I/O system (Bao's mechanism for forwarding + I/O requests from frontend VMs to the backend VMs) and the + VirtIO backend device. + + This provides a unified API to support various VirtIO backends, + allowing Bao guests to perform I/O through the hypervisor + transparently. + diff --git a/drivers/virt/bao/io-dispatcher/Makefile b/drivers/virt/bao/io-dispatcher/Makefile new file mode 100644 index 000000000000..19225f524217 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_BAO_IO_DISPATCHER) += bao.o +bao-objs += ioctls.o ioeventfd.o io_client.o io_dispatcher.o irqfd.o dm.o intc.o driver.o + diff --git a/drivers/virt/bao/io-dispatcher/bao_drv.h b/drivers/virt/bao/io-dispatcher/bao_drv.h new file mode 100644 index 000000000000..4abcdf2eae10 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/bao_drv.h @@ -0,0 +1,386 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Provides some definitions for the Bao Hypervisor modules + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __BAO_DRV_H +#define __BAO_DRV_H + +#include <linux/bao.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/interrupt.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/bitops.h> + +#define BAO_IOEVENTFD_FLAG_DATAMATCH BIT(1) +#define BAO_IOEVENTFD_FLAG_DEASSIGN BIT(2) +#define BAO_IRQFD_FLAG_DEASSIGN 1U +#define BAO_IO_CLIENT_DESTROYING 0U +#define BAO_DM_FLAG_DESTROYING 0U +#define BAO_DM_FLAG_CLEARING_IOREQ 1U + +struct bao_dm; +struct bao_io_client; + +typedef int (*bao_io_client_handler_t)(struct bao_io_client *client, + struct bao_virtio_request *req); + +/** + * struct bao_io_client - Bao I/O client + * @name: Client name + * @dm: The DM that the client belongs to + * @list: List node for this bao_io_client + * @is_control: If this client is the control client + * @flags: Flags (BAO_IO_CLIENT_*) + * @virtio_requests: List of free I/O requests + * @range_list: I/O ranges + * @handler: I/O request handler for this client + * @thread: Kernel thread executing the handler + * @wq: Wait queue used for thread parking + * @priv: Private data for the handler + */ +struct bao_io_client { + char name[BAO_NAME_MAX_LEN]; + struct bao_dm *dm; + struct list_head list; + bool is_control; + unsigned long flags; + struct list_head virtio_requests; + + /* protects virtio_requests list */ + struct mutex virtio_requests_lock; + + struct list_head range_list; + + /* protects range_list */ + struct rw_semaphore range_lock; + + bao_io_client_handler_t handler; + struct task_struct *thread; + wait_queue_head_t wq; + void *priv; +}; + +/** + * struct bao_dm - Bao backend device model (DM) + * @list: Entry within global list of all DMs + * @info: DM information (id, shmem_addr, shmem_size, irq, fd) + * @shmem_base_addr: The base address of the shared memory (only used for unmapping purposes) + * @flags: Flags (BAO_IO_DISPATCHER_DM_*) + * @ioeventfds: List of all ioeventfds + * @ioeventfd_client: Ioeventfd client + * @irqfds: List of all irqfds + * @irqfd_server: Workqueue responsible for irqfd handling + * @io_clients: List of all bao_io_client + * @control_client: Control client + */ +struct bao_dm { + struct list_head list; + struct bao_dm_info info; + void *shmem_base_addr; + unsigned long flags; + + struct list_head ioeventfds; + + /* protects ioeventfds list */ + struct mutex ioeventfds_lock; + + struct bao_io_client *ioeventfd_client; + + struct list_head irqfds; + + /* protects irqfds list */ + struct mutex irqfds_lock; + + struct workqueue_struct *irqfd_server; + + /* protects io_clients list */ + struct rw_semaphore io_clients_lock; + + struct list_head io_clients; + struct bao_io_client *control_client; +}; + +/** + * struct bao_io_range - Represents a range of I/O addresses + * @list: List node for linking multiple ranges + * @start: Start address of the range + * @end: End address of the range (inclusive) + */ +struct bao_io_range { + struct list_head list; + u64 start; + u64 end; +}; + +/* Global list of all Bao device models */ +extern struct list_head bao_dm_list; + +/* Lock protecting access to bao_dm_list */ +extern rwlock_t bao_dm_list_lock; + +/** + * bao_dm_create - Create a backend device model (DM) + * @info: DM information (id, shmem_addr, shmem_size, irq, fd) + * + * Return: Pointer to the created DM on success, NULL on error. + */ +struct bao_dm *bao_dm_create(struct bao_dm_info *info); + +/** + * bao_dm_destroy - Destroy a backend device model (DM) + * @dm: DM to be destroyed + */ +void bao_dm_destroy(struct bao_dm *dm); + +/** + * bao_dm_get_info - Retrieve information of a DM + * @info: Structure to be filled; id field must contain the DM ID + * + * Return: true on success, false on error. + */ +bool bao_dm_get_info(struct bao_dm_info *info); + +/** + * bao_dm_ioctl - Handle DM-related ioctls + * @filp: Open file pointer + * @cmd: IOCTL command + * @ioctl_param: Pointer to IOCTL parameter + * + * Return: ioctl result code. + */ +long bao_dm_ioctl(struct file *filp, unsigned int cmd, + unsigned long ioctl_param); + +/** + * bao_io_client_create - Create a backend I/O client + * @dm: DM this client belongs to + * @handler: I/O client handler for requests + * @data: Private data passed to the handler + * @is_control: True if this is the control client + * @name: Name of the I/O client + * + * Return: Pointer to the created I/O client, or NULL on failure. + */ +struct bao_io_client *bao_io_client_create(struct bao_dm *dm, + bao_io_client_handler_t handler, + void *data, bool is_control, + const char *name); + +/** + * bao_io_clients_destroy - Destroy all I/O clients of a DM + * @dm: DM whose I/O clients are to be destroyed + */ +void bao_io_clients_destroy(struct bao_dm *dm); + +/** + * bao_io_client_attach - Attach a thread to an I/O client + * @client: I/O client to attach + * + * The thread will wait for I/O requests on this client. + * + * Return: 0 on success, negative error code on failure. + */ +int bao_io_client_attach(struct bao_io_client *client); + +/** + * bao_io_client_range_add - Add an I/O range to monitor in a client + * @client: I/O client + * @start: Start address of the range + * @end: End address of the range (inclusive) + * + * Return: 0 on success, negative error code on failure. + */ +int bao_io_client_range_add(struct bao_io_client *client, u64 start, u64 end); + +/** + * bao_io_client_range_del - Remove an I/O range from a client + * @client: I/O client + * @start: Start address of the range + * @end: End address of the range (inclusive) + */ +void bao_io_client_range_del(struct bao_io_client *client, u64 start, u64 end); + +/** + * bao_io_client_request - Retrieve the oldest I/O request from a client + * @client: I/O client + * @req: Pointer to virtio request structure to fill + * + * Return: 0 on success, negative error code if no request is available. + */ +int bao_io_client_request(struct bao_io_client *client, + struct bao_virtio_request *req); + +/** + * bao_io_client_push_request - Push an I/O request into a client + * @client: I/O client + * @req: I/O request to push + * + * Return: true if a request was pushed, false otherwise. + */ +bool bao_io_client_push_request(struct bao_io_client *client, + struct bao_virtio_request *req); + +/** + * bao_io_client_pop_request - Pop the oldest I/O request from a client + * @client: I/O client + * @req: Buffer to store the popped request + * + * Return: true if a request was popped, false if the queue was empty. + */ +bool bao_io_client_pop_request(struct bao_io_client *client, struct bao_virtio_request *req); + +/** + * bao_io_client_find - Find the I/O client for a given request + * @dm: DM that the I/O request belongs to + * @req: I/O request to locate + * + * Return: Pointer to the I/O client handling the request, or NULL if none found. + */ +struct bao_io_client *bao_io_client_find(struct bao_dm *dm, + struct bao_virtio_request *req); + +/** + * bao_ioeventfd_client_init - Initialize the Ioeventfd client for a DM + * @dm: DM that the Ioeventfd client belongs to + * + * Return: 0 on success, negative error code on failure. + */ +int bao_ioeventfd_client_init(struct bao_dm *dm); + +/** + * bao_ioeventfd_client_destroy - Destroy the Ioeventfd client for a DM + * @dm: DM that the Ioeventfd client belongs to + */ +void bao_ioeventfd_client_destroy(struct bao_dm *dm); + +/** + * bao_ioeventfd_client_config - Configure an Ioeventfd client + * @dm: DM that the Ioeventfd client belongs to + * @config: Ioeventfd configuration to apply + * + * Return: 0 on success, negative error code on failure. + */ +int bao_ioeventfd_client_config(struct bao_dm *dm, + struct bao_ioeventfd *config); + +/** + * bao_irqfd_server_init - Initialize the Irqfd server for a DM + * @dm: DM that the Irqfd server belongs to + * + * Return: 0 on success, negative error code on failure. + */ +int bao_irqfd_server_init(struct bao_dm *dm); + +/** + * bao_irqfd_server_destroy - Destroy the Irqfd server for a DM + * @dm: DM that the Irqfd server belongs to + */ +void bao_irqfd_server_destroy(struct bao_dm *dm); + +/** + * bao_irqfd_server_config - Configure an Irqfd server + * @dm: DM that the Irqfd server belongs to + * @config: Irqfd configuration to apply + * + * Return: 0 on success, negative error code on failure. + */ +int bao_irqfd_server_config(struct bao_dm *dm, struct bao_irqfd *config); + +/** + * bao_io_dispatcher_init - Initialize the I/O Dispatcher for a DM + * @dm: DM to initialize on the I/O Dispatcher + * + * Return: 0 on success, negative error code on failure. + */ +int bao_io_dispatcher_init(struct bao_dm *dm); + +/** + * bao_io_dispatcher_destroy - Destroy the I/O Dispatcher for a DM + * @dm: DM to destroy on the I/O Dispatcher + */ +void bao_io_dispatcher_destroy(struct bao_dm *dm); + +/** + * bao_io_dispatcher_setup - Setup the I/O Dispatcher + * + * Return: 0 on success, negative error code on failure. + */ +int bao_io_dispatcher_setup(void); + +/** + * bao_io_dispatcher_remove - Remove the I/O Dispatcher + */ +void bao_io_dispatcher_remove(void); + +/** + * bao_dispatch_io - Acquire and dispatch I/O requests from the Bao Hypervisor + * @dm: DM whose I/O clients will handle the requests + * + * Return: 0 on success, negative error code on failure. + */ +int bao_dispatch_io(struct bao_dm *dm); + +/** + * bao_io_dispatcher_pause - Pause the I/O Dispatcher for a DM + * @dm: DM to pause + */ +void bao_io_dispatcher_pause(struct bao_dm *dm); + +/** + * bao_io_dispatcher_resume - Resume the I/O Dispatcher for a DM + * @dm: DM to resume + */ +void bao_io_dispatcher_resume(struct bao_dm *dm); + +/** + * bao_intc_register - Register the interrupt controller for a DM + * @dm: DM that the interrupt controller belongs to + * + * Return: 0 on success, negative error code on failure. + */ +int bao_intc_register(struct bao_dm *dm); + +/** + * bao_intc_unregister - Unregister the interrupt controller for a DM + * @dm: DM that the interrupt controller belongs to + */ +void bao_intc_unregister(struct bao_dm *dm); + +/** + * bao_intc_setup_handler - Setup the interrupt controller handler + * @handler: Function pointer to the interrupt handler + * @dm: DM that the interrupt controller belongs to + */ +void bao_intc_setup_handler(void (*handler)(struct bao_dm *dm)); + +/** + * bao_intc_remove_handler - Remove the interrupt controller handler + */ +void bao_intc_remove_handler(void); + +/** + * bao_io_dispatcher_driver_ioctl - Handle I/O Dispatcher ioctls + * @filp: Open file pointer + * @cmd: IOCTL command + * @ioctl_param: Pointer to IOCTL parameter + * + * Return: ioctl result code. + */ +long bao_io_dispatcher_driver_ioctl(struct file *filp, unsigned int cmd, + unsigned long ioctl_param); + +#endif /* __BAO_DRV_H */ diff --git a/drivers/virt/bao/io-dispatcher/dm.c b/drivers/virt/bao/io-dispatcher/dm.c new file mode 100644 index 000000000000..633d9dd206e1 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/dm.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor Backend Device Model (DM) + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include "bao_drv.h" +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/anon_inodes.h> +#include <linux/miscdevice.h> +#include "hypercall.h" + +/* + * List of all backend device models (DMs) + */ +LIST_HEAD(bao_dm_list); + +/* + * Lock to protect bao_dm_list: + * - Read: worker thread dispatching I/O requests + * - Write: DM creation via ioctl + */ +DEFINE_RWLOCK(bao_dm_list_lock); + +static int bao_dm_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int bao_dm_release(struct inode *inode, struct file *filp) +{ + struct bao_dm *dm = filp->private_data; + + if (WARN_ON_ONCE(!dm)) + return -ENODEV; + + bao_dm_destroy(dm); + + kfree(dm); + filp->private_data = NULL; + + return 0; +} + +/** + * bao_dm_mmap - mmap backend DM shared memory to userspace + * @filp: File pointer for the DM device + * @vma: Virtual memory area for mapping + * + * Maps the previously allocated kernel memory region of the backend + * DM into the userspace address space. + * + * Return: 0 on success, negative errno on failure + */ +static int bao_dm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct bao_dm *dm = filp->private_data; + unsigned long vsize, offset; + phys_addr_t phys; + + if (WARN_ON_ONCE(!dm)) + return -ENODEV; + + vsize = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + if (!vsize || offset) + return -EINVAL; + + if (vsize > dm->info.shmem_size) + return -EINVAL; + + phys = dm->info.shmem_addr; + if (!PAGE_ALIGNED(phys)) + return -EINVAL; + + if (remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT, + vsize, vma->vm_page_prot)) + return -EFAULT; + + return 0; +} + +/** + * bao_dm_llseek - Adjust file offset for backend DM device + * @file: File pointer for the DM device + * @offset: Offset to seek + * @whence: Reference point (SEEK_SET, SEEK_CUR, SEEK_END) + * + * Adjusts the file position for the backend DM device, allowing + * userspace to seek within the shared memory region. + * + * Return: New file position on success, negative errno on failure + */ +static loff_t bao_dm_llseek(struct file *file, loff_t offset, int whence) +{ + struct bao_dm *bao = file->private_data; + loff_t new_pos; + + if (WARN_ON_ONCE(!bao)) + return -ENODEV; + + switch (whence) { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = file->f_pos + offset; + break; + case SEEK_END: + new_pos = bao->info.shmem_size + offset; + break; + default: + return -EINVAL; + } + + if (new_pos < 0 || new_pos > bao->info.shmem_size) + return -EINVAL; + + file->f_pos = new_pos; + return new_pos; +} + +static const struct file_operations bao_dm_fops = { + .owner = THIS_MODULE, + .open = bao_dm_open, + .release = bao_dm_release, + .unlocked_ioctl = bao_dm_ioctl, + .llseek = bao_dm_llseek, + .mmap = bao_dm_mmap, +}; + +struct bao_dm *bao_dm_create(struct bao_dm_info *info) +{ + struct bao_dm *dm; + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON(!info)) + return NULL; + + /* Check if a DM with the same ID already exists */ + read_lock(&bao_dm_list_lock); + list_for_each_entry(dm, &bao_dm_list, list) { + if (dm->info.id == info->id) { + read_unlock(&bao_dm_list_lock); + return NULL; + } + } + read_unlock(&bao_dm_list_lock); + + /* Allocate the DM structure */ + dm = kzalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return NULL; + + INIT_LIST_HEAD(&dm->io_clients); + init_rwsem(&dm->io_clients_lock); + + dm->info = *info; + + /* Initialize IO dispatcher */ + bao_io_dispatcher_init(dm); + + /* Add DM to global list */ + write_lock_bh(&bao_dm_list_lock); + list_add(&dm->list, &bao_dm_list); + write_unlock_bh(&bao_dm_list_lock); + + /* Create control client */ + snprintf(name, sizeof(name), "bao-ioctlc%u", dm->info.id); + dm->control_client = bao_io_client_create(dm, NULL, NULL, true, name); + if (!dm->control_client) { + pr_err("%s: failed to create control client for DM %u\n", + __func__, dm->info.id); + goto err_remove_dm; + } + + /* Initialize IOEVENTFD client */ + if (bao_ioeventfd_client_init(dm)) { + pr_err("%s: failed to initialize ioeventfd for DM %u\n", + __func__, dm->info.id); + goto err_destroy_io_clients; + } + + /* Initialize IRQFD server */ + if (bao_irqfd_server_init(dm)) { + pr_err("%s: failed to initialize irqfd for DM %u\n", + __func__, dm->info.id); + goto err_destroy_io_clients; + } + + /* Map shared memory */ + dm->shmem_base_addr = + memremap(dm->info.shmem_addr, dm->info.shmem_size, MEMREMAP_WB); + if (!dm->shmem_base_addr) { + pr_err("%s: failed to map memory region for DM %u\n", + __func__, dm->info.id); + goto err_destroy_irqfd; + } + + return dm; + +err_destroy_irqfd: + bao_irqfd_server_destroy(dm); + +err_destroy_io_clients: + bao_io_clients_destroy(dm); + +err_remove_dm: + write_lock_bh(&bao_dm_list_lock); + list_del(&dm->list); + write_unlock_bh(&bao_dm_list_lock); + kfree(dm); + + return NULL; +} + +void bao_dm_destroy(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm)) + return; + + /* Mark DM as destroying to prevent concurrent access */ + set_bit(BAO_DM_FLAG_DESTROYING, &dm->flags); + + /* Remove from global DM list */ + write_lock_bh(&bao_dm_list_lock); + list_del_init(&dm->list); + write_unlock_bh(&bao_dm_list_lock); + + /* Clear DM info for safety */ + dm->info.id = 0; + dm->info.shmem_addr = 0; + dm->info.shmem_size = 0; + dm->info.irq = 0; + + /* Unmap shared memory */ + if (dm->shmem_base_addr) + memunmap(dm->shmem_base_addr); + + /* Close the associated file descriptor if any */ + if (dm->info.fd >= 0) + put_unused_fd(dm->info.fd); + + /* Destroy all server/client resources */ + bao_irqfd_server_destroy(dm); + bao_io_clients_destroy(dm); + bao_io_dispatcher_destroy(dm); + + /* Clear destroying flag */ + clear_bit(BAO_DM_FLAG_DESTROYING, &dm->flags); + + /* Free DM structure */ + kfree(dm); +} + +/** + * bao_dm_create_anonymous_inode - Create an anonymous inode for a backend DM + * @dm: The backend device model (DM) + * + * Creates an anonymous inode that exposes the backend DM to userspace. + * The frontend DM can use the returned file descriptor to request + * services from the backend DM directly. + * + * Return: File descriptor on success, negative errno on failure + */ +static int bao_dm_create_anonymous_inode(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + struct file *file; + int fd; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + /* Allocate an unused file descriptor */ + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return fd; + + /* Create anonymous inode for this DM */ + snprintf(name, sizeof(name), "bao-dm%u", dm->info.id); + file = anon_inode_getfile(name, &bao_dm_fops, dm, O_RDWR); + if (IS_ERR(file)) { + put_unused_fd(fd); + return PTR_ERR(file); + } + + /* Install the file descriptor and store in DM */ + fd_install(fd, file); + dm->info.fd = fd; + + return fd; +} + +bool bao_dm_get_info(struct bao_dm_info *info) +{ + struct bao_dm *dm; + bool found = false; + + if (WARN_ON_ONCE(!info)) + return false; + + /* Search the global DM list for matching ID */ + list_for_each_entry(dm, &bao_dm_list, list) { + if (dm->info.id == info->id) { + info->shmem_addr = dm->info.shmem_addr; + info->shmem_size = dm->info.shmem_size; + info->irq = dm->info.irq; + + /* Create anonymous inode and store FD */ + info->fd = bao_dm_create_anonymous_inode(dm); + + found = true; + break; + } + } + + return found; +} diff --git a/drivers/virt/bao/io-dispatcher/driver.c b/drivers/virt/bao/io-dispatcher/driver.c new file mode 100644 index 000000000000..11d73107e8ca --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/driver.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Dispatcher Kernel Driver + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/of_irq.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include "bao_drv.h" + +#define DEV_NAME "bao-io-dispatcher" + +static dev_t bao_iodispatcher_devt; +struct class *bao_iodispatcher_cl; + +/** + * struct bao_iodispatcher_drv - Bao I/O Dispatcher driver + * @cdev: Character device for the dispatcher + * @dev: Pointer to the device + * + * Represents the Bao I/O Dispatcher kernel module instance and its + * associated character device. + */ +struct bao_iodispatcher_drv { + struct cdev cdev; + struct device *dev; +}; + +/** + * bao_io_dispatcher_driver_open_fops - Open the Bao I/O Dispatcher device + * @inode: Inode representing the character device + * @filp: File pointer for the opened device + * + * Sets the private_data pointer in @filp to the Bao I/O Dispatcher + * driver instance and takes a reference to its kobject. + * + * Return: 0 on success + */ +static int bao_io_dispatcher_driver_open_fops(struct inode *inode, + struct file *filp) +{ + struct bao_iodispatcher_drv *drv; + + if (WARN_ON_ONCE(!inode || !inode->i_cdev)) + return -ENODEV; + + drv = container_of(inode->i_cdev, struct bao_iodispatcher_drv, cdev); + filp->private_data = drv; + + /* Increment reference count for the underlying device */ + kobject_get(&drv->dev->kobj); + + return 0; +} + +/** + * bao_io_dispatcher_driver_release_fops - Release the Bao I/O Dispatcher device + * @inode: Inode representing the character device + * @filp: File pointer for the device being released + * + * Clears the private_data pointer in @filp and drops the reference + * to the device's kobject. + * + * Return: 0 on success + */ +static int bao_io_dispatcher_driver_release_fops(struct inode *inode, + struct file *filp) +{ + struct bao_iodispatcher_drv *drv; + + if (WARN_ON_ONCE(!inode || !inode->i_cdev)) + return -ENODEV; + + drv = container_of(inode->i_cdev, struct bao_iodispatcher_drv, cdev); + + /* Clear private data to avoid dangling pointer */ + filp->private_data = NULL; + + /* Decrement reference count of the underlying device */ + kobject_put(&drv->dev->kobj); + + return 0; +} + +/** + * bao_io_dispatcher_driver_ioctl_fops - File operations ioctl handler + * @filp: File pointer for the device + * @cmd: IOCTL command + * @arg: Argument for the IOCTL command + * + * Wrapper function to call the main Bao I/O Dispatcher IOCTL handler. + * + * Return: Result from bao_io_dispatcher_driver_ioctl() + */ +static long bao_io_dispatcher_driver_ioctl_fops(struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + return bao_io_dispatcher_driver_ioctl(filp, cmd, arg); +} + +static const struct file_operations bao_io_dispatcher_driver_fops = { + .owner = THIS_MODULE, + .open = bao_io_dispatcher_driver_open_fops, + .release = bao_io_dispatcher_driver_release_fops, + .unlocked_ioctl = bao_io_dispatcher_driver_ioctl_fops, +}; + +/** + * bao_io_dispatcher_driver_register - Register the Bao I/O Dispatcher driver + * @pdev: Platform device pointer + * + * This function sets up the Bao I/O Dispatcher backend for the given platform + * device. It allocates the character device, creates device models (DMs) for + * each backend, registers interrupts, and creates the + * device nodes for userspace access. + * + * Return: 0 on success, negative error code on failure + */ +static int bao_io_dispatcher_driver_register(struct platform_device *pdev) +{ + int ret, irq; + struct module *owner = THIS_MODULE; + struct resource *r; + dev_t devt; + resource_size_t reg_size; + struct bao_iodispatcher_drv *drv; + struct bao_dm *dm; + struct bao_dm_info dm_info; + + /* Setup the I/O dispatcher hardware */ + ret = bao_io_dispatcher_setup(); + if (ret) { + dev_err(&pdev->dev, "setup I/O Dispatcher failed!\n"); + return ret; + } + + /* Allocate driver structure */ + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) { + ret = -ENOMEM; + goto err_io_dispatcher; + } + + /* Create DM instances */ + for (int i = 0; i < BAO_IO_MAX_DMS; i++) { + r = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!r) + break; + + irq = platform_get_irq(pdev, i); + if (irq < 0) { + dev_err(&pdev->dev, + "Failed to read interrupt number at index %d\n", + i); + ret = irq; + goto err_unregister_dms; + } + + reg_size = resource_size(r); + + dm_info.id = i; + dm_info.shmem_addr = (unsigned long)r->start; + dm_info.shmem_size = (unsigned long)reg_size; + dm_info.irq = irq; + dm_info.fd = 0; + + dm = bao_dm_create(&dm_info); + if (!dm) { + dev_err(&pdev->dev, + "failed to create Bao DM %d\n", i); + ret = -ENOMEM; + goto err_unregister_dms; + } + + ret = bao_intc_register(dm); + if (ret) { + dev_err(&pdev->dev, "failed to register interrupt %d\n", irq); + goto err_unregister_dms; + } + } + + /* Initialize character device */ + cdev_init(&drv->cdev, &bao_io_dispatcher_driver_fops); + drv->cdev.owner = owner; + + devt = MKDEV(MAJOR(bao_iodispatcher_devt), 0); + ret = cdev_add(&drv->cdev, devt, 1); + if (ret) + goto err_unregister_irqs; + + /* Create device node */ + drv->dev = device_create(bao_iodispatcher_cl, &pdev->dev, devt, drv, DEV_NAME); + if (IS_ERR(drv->dev)) { + ret = PTR_ERR(drv->dev); + goto err_cdev; + } + + dev_set_drvdata(drv->dev, drv); + return 0; + +err_cdev: + cdev_del(&drv->cdev); + +err_unregister_irqs: + list_for_each_entry(dm, &bao_dm_list, list) + bao_intc_unregister(dm); + +err_unregister_dms: + list_for_each_entry(dm, &bao_dm_list, list) + bao_dm_destroy(dm); + +err_io_dispatcher: + bao_io_dispatcher_remove(); + dev_err(&pdev->dev, "failed initialization\n"); + return ret; +} + +/** + * bao_io_dispatcher_driver_unregister - Unregister the Bao I/O Dispatcher driver + * @pdev: Platform device pointer + * + * This function tears down the Bao I/O Dispatcher backend for the given + * platform device. It removes the dispatcher, destroys all backend + * device models (DMs), and unregisters their associated interrupts. + */ +static void bao_io_dispatcher_driver_unregister(struct platform_device *pdev) +{ + struct bao_dm *dm, *tmp; + + /* Remove I/O dispatcher hardware */ + bao_io_dispatcher_remove(); + + /* Destroy all DM instances safely */ + list_for_each_entry_safe(dm, tmp, &bao_dm_list, list) { + /* Remove DM from list and free resources */ + bao_dm_destroy(dm); + + /* Unregister associated interrupts */ + bao_intc_unregister(dm); + } +} + +static const struct of_device_id bao_io_dispatcher_driver_dt_ids[] = { + { .compatible = "bao,io-dispatcher" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, bao_io_dispatcher_driver_dt_ids); + +static struct platform_driver bao_io_dispatcher_driver = { + .probe = bao_io_dispatcher_driver_register, + .remove = bao_io_dispatcher_driver_unregister, + .driver = { + .name = "bao-io-dispatcher", + .of_match_table = + of_match_ptr(bao_io_dispatcher_driver_dt_ids), + .owner = THIS_MODULE, + }, +}; + +/** + * bao_io_dispatcher_driver_init - Module initialization for Bao I/O Dispatcher + * + * Allocates the character device region, creates the device class, and + * registers the platform driver for the Bao I/O Dispatcher. + * + * Return: 0 on success, negative error code on failure. + */ +static int __init bao_io_dispatcher_driver_init(void) +{ + int ret; + + /* Create device class */ + bao_iodispatcher_cl = class_create(DEV_NAME); + if (IS_ERR(bao_iodispatcher_cl)) { + ret = PTR_ERR(bao_iodispatcher_cl); + pr_err("unable to create class %s\n", DEV_NAME); + return ret; + } + + /* Allocate character device numbers */ + ret = alloc_chrdev_region(&bao_iodispatcher_devt, 0, BAO_IO_MAX_DMS, DEV_NAME); + if (ret < 0) { + pr_err("unable to allocate chrdev region for %s\n", DEV_NAME); + class_destroy(bao_iodispatcher_cl); + return ret; + } + + /* Register the platform driver */ + ret = platform_driver_register(&bao_io_dispatcher_driver); + if (ret < 0) { + pr_err("unable to register platform driver for %s\n", DEV_NAME); + unregister_chrdev_region(bao_iodispatcher_devt, BAO_IO_MAX_DMS); + class_destroy(bao_iodispatcher_cl); + } + + return ret; +} + +/** + * bao_io_dispatcher_driver_exit - Module cleanup for Bao I/O Dispatcher + * + * Unregisters the platform driver, frees the character device region, and + * destroys the device class. + */ +static void __exit bao_io_dispatcher_driver_exit(void) +{ + /* Unregister the platform driver */ + platform_driver_unregister(&bao_io_dispatcher_driver); + + /* Release character device numbers */ + unregister_chrdev_region(bao_iodispatcher_devt, BAO_IO_MAX_DMS); + + /* Destroy the device class */ + class_destroy(bao_iodispatcher_cl); +} + +module_init(bao_io_dispatcher_driver_init); +module_exit(bao_io_dispatcher_driver_exit); + +MODULE_AUTHOR("João Peixoto <joaopeixoto@osyx.tech>"); +MODULE_AUTHOR("David Cerdeira <davidmcerdeira@osyx.tech>"); +MODULE_AUTHOR("José Martins <jose@osyx.tech>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Bao Hypervisor I/O Dispatcher Kernel Driver"); diff --git a/drivers/virt/bao/io-dispatcher/hypercall.h b/drivers/virt/bao/io-dispatcher/hypercall.h new file mode 100644 index 000000000000..49b58075558a --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/hypercall.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hypercall API for Bao Hypervisor + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __BAO_HYPERCALL_H +#define __BAO_HYPERCALL_H + +#include <asm/bao.h> +#include <linux/bao.h> + +/** + * bao_hypercall_remio() - Performs a Remote I/O Hypercall + * @request: Bao VirtIO request + * + * @return: The VirtIO request structure + */ +static inline struct remio_hypercall_ret bao_hypercall_remio(struct bao_virtio_request *request) +{ + return asm_bao_hypercall_remio(request); +} + +#endif /* __BAO_HYPERCALL_H */ diff --git a/drivers/virt/bao/io-dispatcher/intc.c b/drivers/virt/bao/io-dispatcher/intc.c new file mode 100644 index 000000000000..61c0248c0a8e --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/intc.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Dispatcher Interrupt Controller + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/of_irq.h> +#include "bao_drv.h" + +/* Top-level handler registered by the Bao interrupt controller */ +static void (*bao_intc_handler)(struct bao_dm *dm); + +/** + * bao_interrupt_handler - Top-level interrupt handler for Bao DM + * @irq: Interrupt number + * @dev: Pointer to the Bao device model (struct bao_dm) + * + * Invokes the registered Bao interrupt controller handler, if any. + */ +static irqreturn_t bao_interrupt_handler(int irq, void *dev) +{ + struct bao_dm *dm = (struct bao_dm *)dev; + + if (bao_intc_handler) + bao_intc_handler(dm); + + return IRQ_HANDLED; +} + +void bao_intc_setup_handler(void (*handler)(struct bao_dm *dm)) +{ + bao_intc_handler = handler; +} + +void bao_intc_remove_handler(void) +{ + bao_intc_handler = NULL; +} + +int bao_intc_register(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + scnprintf(name, sizeof(name), "bao-iodintc%d", dm->info.id); + + return request_irq(dm->info.irq, bao_interrupt_handler, 0, name, dm); +} + +void bao_intc_unregister(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm)) + return; + + free_irq(dm->info.irq, dm); +} diff --git a/drivers/virt/bao/io-dispatcher/io_client.c b/drivers/virt/bao/io-dispatcher/io_client.c new file mode 100644 index 000000000000..ab7fb46c5e6a --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/io_client.c @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Client + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include "bao_drv.h" +#include "hypercall.h" +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/io.h> + +/** + * struct bao_io_request - Bao I/O request structure + * @list: List node linking all requests + * @virtio_request: The VirtIO I/O request payload + * + * Represents a single I/O request for a Bao I/O client. + */ +struct bao_io_request { + struct list_head list; + struct bao_virtio_request virtio_request; +}; + +/** + * bao_io_client_has_pending_requests - Check if an I/O client has pending requests + * @client: The bao_io_client to check + * + * Return true if there are pending VirtIO requests. + */ +static inline bool +bao_io_client_has_pending_requests(struct bao_io_client *client) +{ + if (WARN_ON_ONCE(!client)) + return false; + + return !list_empty(&client->virtio_requests); +} + +/** + * bao_io_client_is_destroying - Check if an I/O client is being destroyed + * @client: The bao_io_client to check + * + * Return true if the client is in the process of being destroyed. + */ +static inline bool bao_io_client_is_destroying(struct bao_io_client *client) +{ + if (WARN_ON_ONCE(!client)) + return true; + + return test_bit(BAO_IO_CLIENT_DESTROYING, &client->flags); +} + +bool bao_io_client_push_request(struct bao_io_client *client, + struct bao_virtio_request *req) +{ + struct bao_io_request *io_req; + + if (WARN_ON_ONCE(!client || !req)) + return false; + + io_req = kzalloc(sizeof(*io_req), GFP_KERNEL); + if (!io_req) + return false; + + io_req->virtio_request = *req; + + mutex_lock(&client->virtio_requests_lock); + list_add_tail(&io_req->list, &client->virtio_requests); + mutex_unlock(&client->virtio_requests_lock); + + return true; +} + +bool bao_io_client_pop_request(struct bao_io_client *client, struct bao_virtio_request *ret) +{ + struct bao_io_request *req; + + if (WARN_ON_ONCE(!client || !ret)) + return false; + + mutex_lock(&client->virtio_requests_lock); + + /* Get the first request or NULL if list is empty */ + req = list_first_entry_or_null(&client->virtio_requests, + struct bao_io_request, list); + if (!req) { + mutex_unlock(&client->virtio_requests_lock); + return false; + } + + /* Remove from list and copy the virtio request */ + list_del(&req->list); + *ret = req->virtio_request; + + mutex_unlock(&client->virtio_requests_lock); + + /* Free the allocated request */ + kfree(req); + + return true; +} + +/** + * bao_io_client_destroy - Destroy an I/O client + * @client: The bao_io_client to destroy + * + * Stops the client's thread if needed, frees all I/O ranges, removes + * the client from its DM lists, and releases associated resources. + */ +static void bao_io_client_destroy(struct bao_io_client *client) +{ + struct bao_io_client *range, *next; + struct bao_dm *dm; + + if (WARN_ON_ONCE(!client)) + return; + + dm = client->dm; + + /* Pause the IO dispatcher to avoid races */ + bao_io_dispatcher_pause(dm); + + /* Mark client as destroying */ + set_bit(BAO_IO_CLIENT_DESTROYING, &client->flags); + + /* Stop client-specific resources */ + if (client->is_control) { + wake_up_interruptible(&client->wq); + } else { + /* Stop eventfd client and associated thread */ + bao_ioeventfd_client_destroy(dm); + if (client->thread) + kthread_stop(client->thread); + } + + /* Free range list safely */ + down_write(&client->range_lock); + list_for_each_entry_safe(range, next, &client->range_list, list) { + list_del(&range->list); + kfree(range); + } + up_write(&client->range_lock); + + /* Remove client from DM */ + down_write(&dm->io_clients_lock); + if (client->is_control) + dm->control_client = NULL; + else + dm->ioeventfd_client = NULL; + + list_del(&client->list); + up_write(&dm->io_clients_lock); + + /* Resume the IO dispatcher */ + bao_io_dispatcher_resume(dm); + + /* Free the client structure */ + kfree(client); +} + +void bao_io_clients_destroy(struct bao_dm *dm) +{ + struct bao_io_client *client, *next; + + if (WARN_ON_ONCE(!dm)) + return; + + list_for_each_entry_safe(client, next, &dm->io_clients, list) { + bao_io_client_destroy(client); + } +} + +int bao_io_client_attach(struct bao_io_client *client) +{ + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + if (client->is_control) { + /* + * In the Control client, a user space thread waits on the waitqueue. + * The thread should wait until: + * - There are pending I/O requests to be processed + * - The I/O client is going to be destroyed + */ + wait_event_interruptible(client->wq, + bao_io_client_has_pending_requests(client) || + bao_io_client_is_destroying(client)); + if (bao_io_client_is_destroying(client)) + return -EPERM; + } else { + /* + * In the non-control client (e.g., Ioeventfd Client), a kernel space + * thread waits on the waitqueue. + * The thread should wait until: + * - There are pending I/O requests to be processed + * - The I/O client is going to be destroyed + * - The kernel thread is going to be stopped + */ + wait_event_interruptible(client->wq, + bao_io_client_has_pending_requests(client) || + bao_io_client_is_destroying(client) || + kthread_should_stop()); + if (bao_io_client_is_destroying(client) || + kthread_should_stop()) { + if (kthread_should_stop()) + bao_io_client_destroy(client); + return -EPERM; + } + } + + return 0; +} + +/** + * bao_io_client_kernel_thread - Thread for processing a kernel I/O client + * @data: Pointer to the bao_io_client structure + * + * Return: 0 on completion + */ +static int bao_io_client_kernel_thread(void *data) +{ + struct bao_io_client *client = data; + struct bao_virtio_request req; + struct remio_hypercall_ret hret; + bool stop = false; + int ret; + + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + while (!stop && !kthread_should_stop()) { + /* Attach client and wait for pending requests or destruction */ + ret = bao_io_client_attach(client); + if (ret < 0) { + stop = true; + break; + } + + /* Process all pending requests */ + while (bao_io_client_has_pending_requests(client) && !stop) { + if (!bao_io_client_pop_request(client, &req)) { + pr_err("%s: failed to pop I/O request\n", __func__); + stop = true; + break; + } + + /* Invoke the client's handler */ + ret = client->handler(client, &req); + if (ret < 0) { + pr_warn("%s: client handler returned %d\n", __func__, ret); + break; + } + + /* Perform hypercall */ + hret = bao_hypercall_remio(&req); + if (hret.hyp_ret || hret.remio_hyp_ret) { + pr_err("%s: hypercall failed: hyp_ret=%lld, remio_hyp_ret=%lld\n", + __func__, hret.hyp_ret, hret.remio_hyp_ret); + stop = true; + break; + } + } + } + + return 0; +} + +struct bao_io_client *bao_io_client_create(struct bao_dm *dm, + bao_io_client_handler_t handler, + void *data, bool is_control, + const char *name) +{ + struct bao_io_client *client; + + if (WARN_ON_ONCE(!dm || !name)) + return NULL; + + if (!handler && !is_control) + return NULL; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + /* Initialize client fields */ + client->handler = handler; + client->dm = dm; + client->priv = data; + client->is_control = is_control; + if (name) + strscpy(client->name, name, sizeof(client->name)); + + INIT_LIST_HEAD(&client->virtio_requests); + init_rwsem(&client->range_lock); + INIT_LIST_HEAD(&client->range_list); + init_waitqueue_head(&client->wq); + + /* Start kernel thread if handler is provided */ + if (client->handler) { + client->thread = kthread_run(bao_io_client_kernel_thread, + client, "%s-kthread", + client->name); + if (IS_ERR(client->thread)) { + kfree(client); + return NULL; + } + } + + /* Add client to DM safely */ + down_write(&dm->io_clients_lock); + if (is_control) + dm->control_client = client; + else + dm->ioeventfd_client = client; + + list_add(&client->list, &dm->io_clients); + up_write(&dm->io_clients_lock); + + /* Process any pending requests for control client */ + if (is_control) { + while (bao_dispatch_io(dm) > 0) + ; + } + + return client; +} + +int bao_io_client_request(struct bao_io_client *client, + struct bao_virtio_request *req) +{ + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + if (!bao_io_client_pop_request(client, req)) + return -EFAULT; + + return 0; +} + +int bao_io_client_range_add(struct bao_io_client *client, u64 start, u64 end) +{ + struct bao_io_range *range; + + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + if (end < start) + return -EINVAL; + + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + + range->start = start; + range->end = end; + + /* Add the range to the client's list safely */ + down_write(&client->range_lock); + list_add(&range->list, &client->range_list); + up_write(&client->range_lock); + + return 0; +} + +void bao_io_client_range_del(struct bao_io_client *client, u64 start, u64 end) +{ + struct bao_io_range *range, *tmp; + + if (WARN_ON_ONCE(!client)) + return; + + down_write(&client->range_lock); + list_for_each_entry_safe(range, tmp, &client->range_list, list) { + if (range->start == start && range->end == end) { + list_del(&range->list); + kfree(range); + break; + } + } + up_write(&client->range_lock); +} + +/** + * bao_io_request_in_range - Check if the I/O request is in the range + * @range: The I/O request range + * @req: The I/O request to be checked + * + * @return True if the I/O request is in the range, False otherwise + */ +static bool bao_io_request_in_range(struct bao_io_range *range, + struct bao_virtio_request *req) +{ + if (WARN_ON_ONCE(!range || !req)) + return false; + + if (req->addr >= range->start && + (req->addr + req->access_width - 1) <= range->end) + return true; + + return false; +} + +struct bao_io_client *bao_io_client_find(struct bao_dm *dm, + struct bao_virtio_request *req) +{ + struct bao_io_client *client, *found = NULL; + struct bao_io_range *range; + + if (WARN_ON_ONCE(!dm || !req)) + return NULL; + + list_for_each_entry(client, &dm->io_clients, list) { + down_read(&client->range_lock); + list_for_each_entry(range, &client->range_list, list) { + if (bao_io_request_in_range(range, req)) { + found = client; + break; + } + } + up_read(&client->range_lock); + + if (found) + break; + } + + /* Fallback to control client if no matching client found */ + return found ? found : dm->control_client; +} diff --git a/drivers/virt/bao/io-dispatcher/io_dispatcher.c b/drivers/virt/bao/io-dispatcher/io_dispatcher.c new file mode 100644 index 000000000000..a7aa768f98ca --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/io_dispatcher.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Dispatcher + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kthread.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/eventfd.h> +#include <linux/workqueue.h> +#include <linux/delay.h> + +#include <linux/bao.h> +#include "bao_drv.h" +#include "hypercall.h" + +/** + * struct bao_io_dispatcher_work - Work item for I/O dispatching + * @work: Work struct for scheduling on workqueue + * @dm: Pointer to the associated Bao device model + * + * Represents a single work item that dispatches I/O requests + * for a specific Bao device model. + */ +struct bao_io_dispatcher_work { + struct work_struct work; + struct bao_dm *dm; +}; + +/* Array of I/O dispatcher work items, one per Bao DM */ +static struct bao_io_dispatcher_work io_dispatcher_work[BAO_IO_MAX_DMS]; + +/* Workqueues dedicated to dispatching I/O requests for each Bao DM */ +static struct workqueue_struct *bao_io_dispatcher_wq[BAO_IO_MAX_DMS]; + +void bao_io_dispatcher_destroy(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm)) + return; + + if (bao_io_dispatcher_wq[dm->info.id]) { + /* Pause dispatching to prevent races */ + bao_io_dispatcher_pause(dm); + + /* Destroy the workqueue */ + destroy_workqueue(bao_io_dispatcher_wq[dm->info.id]); + bao_io_dispatcher_wq[dm->info.id] = NULL; + + /* Remove the registered interrupt handler */ + bao_intc_remove_handler(); + } +} + +int bao_dispatch_io(struct bao_dm *dm) +{ + struct bao_io_client *client; + struct bao_virtio_request req; + struct remio_hypercall_ret ret; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + /* Prepare request for hypercall */ + req.dm_id = dm->info.id; + req.op = BAO_IO_ASK; + req.addr = 0; + req.value = 0; + req.request_id = 0; + + /* Perform hypercall to retrieve I/O request */ + ret = bao_hypercall_remio(&req); + if (ret.hyp_ret || ret.remio_hyp_ret) + return -EFAULT; + + /* Find the appropriate client for this request */ + down_read(&dm->io_clients_lock); + client = bao_io_client_find(dm, &req); + if (!client) { + up_read(&dm->io_clients_lock); + return -EEXIST; + } + + /* Push request to client's queue */ + if (!bao_io_client_push_request(client, &req)) { + up_read(&dm->io_clients_lock); + return -EINVAL; + } + + /* Wake up the client thread if waiting */ + wake_up_interruptible(&client->wq); + up_read(&dm->io_clients_lock); + + /* Return the number of pending requests left to process */ + return ret.pending_requests; +} + +/** + * io_dispatcher - Workqueue handler for dispatching I/O + * @work: Work struct representing this dispatch operation + * + * Handles all pending I/O requests for the associated Bao DM. + * Executed in process context by the workqueue. + */ +static void io_dispatcher(struct work_struct *work) +{ + struct bao_io_dispatcher_work *bao_dm_work; + struct bao_dm *dm; + + if (WARN_ON_ONCE(!work)) + return; + + bao_dm_work = container_of(work, struct bao_io_dispatcher_work, work); + dm = bao_dm_work->dm; + + if (WARN_ON_ONCE(!dm)) + return; + + /* + * Dispatch I/O requests for the device model + * until there are no more pending requests. + */ + while (bao_dispatch_io(dm) > 0) + cpu_relax(); +} + +/** + * io_dispatcher_intc_handler - Interrupt handler for I/O requests + * @dm: Bao device model that triggered the interrupt + * + * Invoked by the interrupt controller when a new I/O request is available. + * Queues the corresponding work item onto the I/O dispatcher workqueue + * for processing in process context. + */ +static void io_dispatcher_intc_handler(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm || !bao_io_dispatcher_wq[dm->info.id])) + return; + + queue_work(bao_io_dispatcher_wq[dm->info.id], + &io_dispatcher_work[dm->info.id].work); +} + +void bao_io_dispatcher_pause(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm || !bao_io_dispatcher_wq[dm->info.id])) + return; + + /* Remove the interrupt handler to prevent new work */ + bao_intc_remove_handler(); + + /* Drain any pending work to ensure no tasks are running */ + drain_workqueue(bao_io_dispatcher_wq[dm->info.id]); +} + +void bao_io_dispatcher_resume(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm || !bao_io_dispatcher_wq[dm->info.id])) + return; + + /* Reinstall the interrupt handler */ + bao_intc_setup_handler(io_dispatcher_intc_handler); + + /* Queue a work item to resume dispatching */ + queue_work(bao_io_dispatcher_wq[dm->info.id], + &io_dispatcher_work[dm->info.id].work); +} + +int bao_io_dispatcher_init(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + snprintf(name, sizeof(name), "bao-iodwq%u", dm->info.id); + + bao_io_dispatcher_wq[dm->info.id] = + alloc_workqueue(name, WQ_HIGHPRI | WQ_MEM_RECLAIM, 1); + if (!bao_io_dispatcher_wq[dm->info.id]) + return -ENOMEM; + + io_dispatcher_work[dm->info.id].dm = dm; + INIT_WORK(&io_dispatcher_work[dm->info.id].work, io_dispatcher); + + /* Setup interrupt handler for this dispatcher */ + bao_intc_setup_handler(io_dispatcher_intc_handler); + + return 0; +} + +int bao_io_dispatcher_setup(void) +{ + return 0; +} + +void bao_io_dispatcher_remove(void) +{ +} diff --git a/drivers/virt/bao/io-dispatcher/ioctls.c b/drivers/virt/bao/io-dispatcher/ioctls.c new file mode 100644 index 000000000000..77a177800882 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/ioctls.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor IOCTLs Handler for the I/O Dispatcher kernel module + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/file.h> +#include <linux/cpu.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/anon_inodes.h> +#include <linux/bao.h> +#include "bao_drv.h" +#include "hypercall.h" + +long bao_io_dispatcher_driver_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct bao_dm_info *info; + + switch (cmd) { + case BAO_IOCTL_DM_GET_INFO: + info = memdup_user((void __user *)arg, sizeof(*info)); + if (IS_ERR(info)) + return PTR_ERR(info); + + if (!bao_dm_get_info(info)) { + kfree(info); + return -ENOENT; + } + + if (copy_to_user((void __user *)arg, info, sizeof(*info))) { + kfree(info); + return -EFAULT; + } + + kfree(info); + return 0; + + default: + return -ENOTTY; + } +} + +long bao_dm_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct bao_dm *dm = filp->private_data; + int rc; + + if (WARN_ON_ONCE(!dm)) + return -ENODEV; + + switch (cmd) { + case BAO_IOCTL_IO_CLIENT_ATTACH: { + struct bao_virtio_request *req; + + req = memdup_user((void __user *)arg, sizeof(*req)); + if (IS_ERR(req)) { + rc = PTR_ERR(req); + break; + } + + if (!dm->control_client) { + rc = -ENOENT; + goto out_free; + } + + rc = bao_io_client_attach(dm->control_client); + if (rc) + goto out_free; + + rc = bao_io_client_request(dm->control_client, req); + if (rc) + goto out_free; + + if (copy_to_user((void __user *)arg, req, sizeof(*req))) { + rc = -EFAULT; + goto out_free; + } + + rc = 0; + +out_free: + kfree(req); + break; + } + case BAO_IOCTL_IO_REQUEST_COMPLETE: { + struct bao_virtio_request *req; + struct remio_hypercall_ret hret; + + req = memdup_user((void __user *)arg, sizeof(*req)); + if (IS_ERR(req)) { + rc = PTR_ERR(req); + break; + } + + hret = bao_hypercall_remio(req); + kfree(req); + + /* Translate hypercall result to proper errno */ + if (hret.hyp_ret || hret.remio_hyp_ret) + rc = -EIO; + else + rc = 0; + + break; + } + case BAO_IOCTL_IOEVENTFD: { + struct bao_ioeventfd ioeventfd; + + if (copy_from_user(&ioeventfd, (void __user *)arg, + sizeof(struct bao_ioeventfd))) + return -EFAULT; + + rc = bao_ioeventfd_client_config(dm, &ioeventfd); + break; + } + case BAO_IOCTL_IRQFD: { + struct bao_irqfd irqfd; + + if (copy_from_user(&irqfd, (void __user *)arg, + sizeof(struct bao_irqfd))) + return -EFAULT; + + rc = bao_irqfd_server_config(dm, &irqfd); + break; + } + default: + rc = -ENOTTY; + break; + } + + return rc; +} diff --git a/drivers/virt/bao/io-dispatcher/ioeventfd.c b/drivers/virt/bao/io-dispatcher/ioeventfd.c new file mode 100644 index 000000000000..8eb92193e31b --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/ioeventfd.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor Ioeventfd Client + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include "bao_drv.h" +#include <linux/eventfd.h> + +/** + * struct ioeventfd - Properties of an I/O eventfd + * @list: List node linking this ioeventfd + * @eventfd: Associated eventfd context + * @addr: Start address of the I/O range + * @data: Data used for matching (if not wildcard) + * @length: Length of the I/O range + * @wildcard: True if data matching is not required + * + * Represents an I/O eventfd registered for a Bao device model. + */ +struct ioeventfd { + struct list_head list; + struct eventfd_ctx *eventfd; + u64 addr; + u64 data; + int length; + bool wildcard; +}; + +/** + * bao_ioeventfd_shutdown - Release and remove an ioeventfd + * @dm: Bao device model owning the ioeventfd (lock must be held) + * @p: Ioeventfd to shut down + */ +static void bao_ioeventfd_shutdown(struct bao_dm *dm, struct ioeventfd *p) +{ + lockdep_assert_held(&dm->ioeventfds_lock); + + if (WARN_ON_ONCE(!p)) + return; + + eventfd_ctx_put(p->eventfd); + list_del(&p->list); + kfree(p); +} + +/** + * bao_ioeventfd_config_valid - Validate ioeventfd configuration + * @config: Ioeventfd configuration + * + * Returns true if config is non-NULL, address+length does not wrap, + * and length is 1, 2, 4, or 8 bytes. + */ +static bool bao_ioeventfd_config_valid(struct bao_ioeventfd *config) +{ + if (WARN_ON_ONCE(!config)) + return false; + + /* Check for address overflow */ + if (config->addr + config->len < config->addr) + return false; + + /* Only allow standard byte widths */ + if (!(config->len == 1 || config->len == 2 || config->len == 4 || + config->len == 8)) + return false; + + return true; +} + +/** + * bao_ioeventfd_is_conflict - Check if an ioeventfd conflicts with existing ones + * @dm: Bao device model (lock must be held) + * @ioeventfd: Ioeventfd to check + * + * Returns true if an existing ioeventfd matches address, eventfd, + * and optionally data. + */ +static bool bao_ioeventfd_is_conflict(struct bao_dm *dm, + struct ioeventfd *ioeventfd) +{ + struct ioeventfd *p; + + lockdep_assert_held(&dm->ioeventfds_lock); + + if (WARN_ON_ONCE(!dm || !ioeventfd)) + return true; + + list_for_each_entry(p, &dm->ioeventfds, list) { + if (p->eventfd == ioeventfd->eventfd && + p->addr == ioeventfd->addr && + (p->wildcard || ioeventfd->wildcard || p->data == ioeventfd->data)) { + return true; + } + } + + return false; +} + +/** + * bao_ioeventfd_match - Find ioeventfd matching an I/O request + * @dm: Bao device model (lock must be held) + * @addr: I/O request address + * @data: I/O request data + * @len: I/O request length + * + * Returns the matching ioeventfd, or NULL if none matches. + */ +static struct ioeventfd *bao_ioeventfd_match(struct bao_dm *dm, u64 addr, + u64 data, int len) +{ + struct ioeventfd *p; + + lockdep_assert_held(&dm->ioeventfds_lock); + + if (WARN_ON_ONCE(!dm)) + return NULL; + + list_for_each_entry(p, &dm->ioeventfds, list) { + if (p->addr == addr && + p->length >= len && + (p->wildcard || p->data == data)) { + return p; + } + } + + return NULL; +} + +/** + * bao_ioeventfd_assign - Assign and create an eventfd for a DM + * @dm: Bao device model to assign the eventfd to + * @config: Configuration of the eventfd to create + * + * Creates a new ioeventfd associated with the given eventfd and + * adds it to the Bao DM. Validates the configuration, checks for + * conflicts with existing ioeventfds, and registers the corresponding + * I/O client address range. Supports optional data matching for + * virtio 1.0 notifications; if not set, wildcard matching is used. + * + * Returns 0 on success or a negative error code on failure: + * -EINVAL: Invalid configuration + * -ENOMEM: Memory allocation failure + * -EEXIST: Conflicting ioeventfd already exists + * Other negative values may be returned by bao_io_client_range_add(). + */ +static int bao_ioeventfd_assign(struct bao_dm *dm, struct bao_ioeventfd *config) +{ + struct eventfd_ctx *eventfd; + struct ioeventfd *new; + int rc = 0; + + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + if (!bao_ioeventfd_config_valid(config)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(config->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + rc = -ENOMEM; + goto err_put_eventfd; + } + + INIT_LIST_HEAD(&new->list); + new->addr = config->addr; + new->length = config->len; + new->eventfd = eventfd; + new->wildcard = !(config->flags & BAO_IOEVENTFD_FLAG_DATAMATCH); + if (!new->wildcard) + new->data = config->data; + + mutex_lock(&dm->ioeventfds_lock); + + if (bao_ioeventfd_is_conflict(dm, new)) { + rc = -EEXIST; + goto err_unlock_free; + } + + rc = bao_io_client_range_add(dm->ioeventfd_client, + new->addr, new->addr + new->length - 1); + if (rc < 0) + goto err_unlock_free; + + list_add_tail(&new->list, &dm->ioeventfds); + mutex_unlock(&dm->ioeventfds_lock); + + return 0; + +err_unlock_free: + mutex_unlock(&dm->ioeventfds_lock); + kfree(new); +err_put_eventfd: + eventfd_ctx_put(eventfd); + return rc; +} + +/** + * bao_ioeventfd_deassign - Deassign and destroy an eventfd from a DM + * @dm: Bao device model to deassign the eventfd from + * @config: Configuration of the eventfd to remove + * + * Finds the ioeventfd associated with the given eventfd and removes + * it from the Bao DM. The corresponding I/O client range is deleted, + * the ioeventfd is shut down, and the eventfd context is released. + * + * Returns 0 on success or a negative error code if the eventfd lookup fails. + */ +static int bao_ioeventfd_deassign(struct bao_dm *dm, + struct bao_ioeventfd *config) +{ + struct ioeventfd *p; + struct eventfd_ctx *eventfd; + + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(config->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + mutex_lock(&dm->ioeventfds_lock); + + list_for_each_entry(p, &dm->ioeventfds, list) { + if (p->eventfd != eventfd) + continue; + + /* Remove the associated client I/O range */ + bao_io_client_range_del(dm->ioeventfd_client, + p->addr, p->addr + p->length - 1); + + /* Shutdown and free the ioeventfd */ + bao_ioeventfd_shutdown(dm, p); + break; + } + + mutex_unlock(&dm->ioeventfds_lock); + eventfd_ctx_put(eventfd); + + return 0; +} + +/** + * bao_ioeventfd_handler - Handle an Ioeventfd client I/O request + * @client: Ioeventfd client associated with the request + * @req: I/O request to process + * + * Processes I/O requests from the Bao I/O client kernel thread + * (bao_io_client_kernel_thread). For READ operations, the value is + * ignored and set to 0 since virtio MMIO drivers only write to the + * `QueueNotify` field. WRITE operations are checked against the + * registered ioeventfds, and the corresponding eventfd is signaled + * if a match is found. + * + * Returns 0 on success. + */ +static int bao_ioeventfd_handler(struct bao_io_client *client, + struct bao_virtio_request *req) +{ + struct ioeventfd *p; + + if (WARN_ON_ONCE(!client || !req)) + return -EINVAL; + + /* Handle read requests: just return 0 */ + if (req->op == BAO_IO_READ) { + req->value = 0; + return 0; + } + + /* Handle write requests */ + mutex_lock(&client->dm->ioeventfds_lock); + + p = bao_ioeventfd_match(client->dm, req->addr, req->value, + req->access_width); + if (p) + eventfd_signal(p->eventfd); + + mutex_unlock(&client->dm->ioeventfds_lock); + + return 0; +} + +int bao_ioeventfd_client_config(struct bao_dm *dm, struct bao_ioeventfd *config) +{ + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + if (config->flags & BAO_IOEVENTFD_FLAG_DEASSIGN) + bao_ioeventfd_deassign(dm, config); + + return bao_ioeventfd_assign(dm, config); +} + +int bao_ioeventfd_client_init(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + mutex_init(&dm->ioeventfds_lock); + INIT_LIST_HEAD(&dm->ioeventfds); + + snprintf(name, sizeof(name), "bao-ioevfdc%u", dm->info.id); + + dm->ioeventfd_client = bao_io_client_create(dm, bao_ioeventfd_handler, + NULL, false, name); + if (!dm->ioeventfd_client) + return -ENOMEM; + + return 0; +} + +void bao_ioeventfd_client_destroy(struct bao_dm *dm) +{ + struct ioeventfd *p, *next; + + if (WARN_ON_ONCE(!dm)) + return; + + mutex_lock(&dm->ioeventfds_lock); + list_for_each_entry_safe(p, next, &dm->ioeventfds, list) + bao_ioeventfd_shutdown(dm, p); + mutex_unlock(&dm->ioeventfds_lock); +} diff --git a/drivers/virt/bao/io-dispatcher/irqfd.c b/drivers/virt/bao/io-dispatcher/irqfd.c new file mode 100644 index 000000000000..145ed7428d9c --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/irqfd.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor Irqfd Server + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/eventfd.h> +#include <linux/file.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include "hypercall.h" + +#include "bao_drv.h" + +/** + * struct irqfd - Properties of an IRQ eventfd + * @dm: Associated Bao device model + * @wait: Wait queue entry for blocking/waking + * @shutdown: Work struct for async shutdown + * @eventfd: Eventfd used to signal interrupts + * @list: List node within &bao_dm.irqfds + * @pt: Poll table for select/poll on the eventfd + * + * Represents an IRQ eventfd registered to a Bao device model. + */ +struct irqfd { + struct bao_dm *dm; + wait_queue_entry_t wait; + struct work_struct shutdown; + struct eventfd_ctx *eventfd; + struct list_head list; + poll_table pt; +}; + +/** + * bao_irqfd_shutdown - Release and remove an irqfd + * @irqfd: IRQ eventfd to shut down (lock must be held) + * + * Removes the irqfd from the DM list, unregisters it from the + * eventfd wait queue, releases the eventfd, and frees the struct. + */ +static void bao_irqfd_shutdown(struct irqfd *irqfd) +{ + u64 cnt; + + if (WARN_ON_ONCE(!irqfd || !irqfd->dm)) + return; + + lockdep_assert_held(&irqfd->dm->irqfds_lock); + + /* Remove from DM list */ + list_del_init(&irqfd->list); + + /* Remove wait queue from eventfd context */ + eventfd_ctx_remove_wait_queue(irqfd->eventfd, &irqfd->wait, &cnt); + + /* Release the eventfd context */ + eventfd_ctx_put(irqfd->eventfd); + + /* Free irqfd structure */ + kfree(irqfd); +} + +/** + * bao_irqfd_inject - Inject a notify hypercall into the Bao hypervisor + * @id: Bao DM ID + * + * Sends a BAO_IO_NOTIFY hypercall to the hypervisor for the specified DM. + * + * Returns 0 on success or -EFAULT if the hypercall fails. + */ +static int bao_irqfd_inject(int id) +{ + struct bao_virtio_request request = { + .dm_id = id, + .addr = 0, + .op = BAO_IO_NOTIFY, + .value = 0, + .access_width = 0, + .request_id = 0, + }; + struct remio_hypercall_ret ret; + + ret = bao_hypercall_remio(&request); + if (ret.hyp_ret != 0 || ret.remio_hyp_ret != 0) + return -EFAULT; + + return 0; +} + +/** + * bao_irqfd_wakeup - Custom wake-up handler for eventfd signaling + * @wait: Wait queue entry + * @mode: Mode flags + * @sync: Sync indicator + * @key: Poll bits (cast from void *) + * + * Called by the Linux kernel poll table when the underlying eventfd is signaled. + * Injects a Bao notify hypercall on POLLIN or schedules shutdown on POLLHUP. + * + * Returns 0. + */ +static int bao_irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, + int sync, void *key) +{ + struct irqfd *irqfd; + struct bao_dm *dm; + unsigned long poll_bits; + + if (WARN_ON_ONCE(!wait || !key)) + return -EINVAL; + + irqfd = container_of(wait, struct irqfd, wait); + dm = irqfd->dm; + poll_bits = (unsigned long)key; + + /* Inject IRQ on POLLIN */ + if (poll_bits & POLLIN) + bao_irqfd_inject(dm->info.id); + + /* Schedule shutdown on POLLHUP */ + if (poll_bits & POLLHUP) + queue_work(dm->irqfd_server, &irqfd->shutdown); + + return 0; +} + +/** + * bao_irqfd_poll_func - Register an IRQFD with a poll table + * @file: File to poll + * @wqh: Wait queue head + * @pt: Poll table + * + * Adds the irqfd's wait queue entry to the kernel wait queue for event monitoring. + */ +static void bao_irqfd_poll_func(struct file *file, wait_queue_head_t *wqh, + poll_table *pt) +{ + struct irqfd *irqfd; + + if (WARN_ON_ONCE(!pt || !wqh)) + return; + + irqfd = container_of(pt, struct irqfd, pt); + add_wait_queue(wqh, &irqfd->wait); +} + +/** + * irqfd_shutdown_work - Workqueue handler to shutdown an irqfd + * @work: Work struct for the shutdown operation + * + * Removes and frees the irqfd from the DM under lock if it is still linked. + */ +static void irqfd_shutdown_work(struct work_struct *work) +{ + struct irqfd *irqfd; + struct bao_dm *dm; + + if (WARN_ON_ONCE(!work)) + return; + + irqfd = container_of(work, struct irqfd, shutdown); + dm = irqfd->dm; + + if (WARN_ON_ONCE(!dm)) + return; + + mutex_lock(&dm->irqfds_lock); + if (!list_empty(&irqfd->list)) + bao_irqfd_shutdown(irqfd); + mutex_unlock(&dm->irqfds_lock); +} + +/** + * bao_irqfd_assign - Assign an eventfd to a DM and create an irqfd + * @dm: Bao device model to assign the eventfd + * @args: Configuration of the irqfd to assign + * + * Allocates and initializes an irqfd for the given eventfd, registers + * it with the DM, sets up the wait queue and poll table, and triggers + * an initial notify if the eventfd is already signaled. + * + * Returns 0 on success or a negative error code on failure: + * -ENOMEM if allocation fails + * -EBADF if the file descriptor is invalid + * -EBUSY if an irqfd with the same eventfd already exists + * Other negative values may be returned by eventfd_ctx_fileget(). + */ +static int bao_irqfd_assign(struct bao_dm *dm, struct bao_irqfd *args) +{ + struct eventfd_ctx *eventfd = NULL; + struct irqfd *irqfd, *tmp; + __poll_t events; + struct fd f; + int ret = 0; + + if (WARN_ON_ONCE(!dm || !args)) + return -EINVAL; + + irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL); + if (!irqfd) + return -ENOMEM; + + irqfd->dm = dm; + INIT_LIST_HEAD(&irqfd->list); + INIT_WORK(&irqfd->shutdown, irqfd_shutdown_work); + + /* Get the file descriptor and associated eventfd context */ + f = fdget(args->fd); + if (!fd_file(f)) { + ret = -EBADF; + goto out_free_irqfd; + } + + eventfd = eventfd_ctx_fileget(fd_file(f)); + if (IS_ERR(eventfd)) { + ret = PTR_ERR(eventfd); + goto out_fdput; + } + irqfd->eventfd = eventfd; + + /* Initialize wait queue and poll table for this irqfd */ + init_waitqueue_func_entry(&irqfd->wait, bao_irqfd_wakeup); + init_poll_funcptr(&irqfd->pt, bao_irqfd_poll_func); + + /* Check for conflicts with existing irqfds */ + mutex_lock(&dm->irqfds_lock); + list_for_each_entry(tmp, &dm->irqfds, list) { + if (irqfd->eventfd == tmp->eventfd) { + ret = -EBUSY; + mutex_unlock(&dm->irqfds_lock); + goto out_put_eventfd; + } + } + list_add_tail(&irqfd->list, &dm->irqfds); + mutex_unlock(&dm->irqfds_lock); + + /* Trigger initial notification if the eventfd is already signaled */ + events = vfs_poll(fd_file(f), &irqfd->pt); + if (events & EPOLLIN) + bao_irqfd_inject(dm->info.id); + + fdput(f); + return 0; + +out_put_eventfd: + eventfd_ctx_put(eventfd); +out_fdput: + fdput(f); +out_free_irqfd: + kfree(irqfd); + return ret; +} + +/** + * bao_irqfd_deassign - Deassign an eventfd and destroy the associated irqfd + * @dm: Bao device model to remove the irqfd from + * @args: Configuration of the irqfd to deassign + * + * Finds the irqfd associated with the given eventfd in the DM, + * shuts it down, and releases the eventfd context. + * + * Returns 0 on success or a negative error code if the eventfd lookup fails. + */ +static int bao_irqfd_deassign(struct bao_dm *dm, struct bao_irqfd *args) +{ + struct irqfd *irqfd, *tmp; + struct eventfd_ctx *eventfd; + + if (WARN_ON_ONCE(!dm || !args)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + mutex_lock(&dm->irqfds_lock); + list_for_each_entry_safe(irqfd, tmp, &dm->irqfds, list) { + if (irqfd->eventfd == eventfd) { + bao_irqfd_shutdown(irqfd); + break; + } + } + mutex_unlock(&dm->irqfds_lock); + + eventfd_ctx_put(eventfd); + + return 0; +} + +int bao_irqfd_server_config(struct bao_dm *dm, struct bao_irqfd *config) +{ + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + if (config->flags & BAO_IRQFD_FLAG_DEASSIGN) + return bao_irqfd_deassign(dm, config); + + return bao_irqfd_assign(dm, config); +} + +int bao_irqfd_server_init(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + mutex_init(&dm->irqfds_lock); + INIT_LIST_HEAD(&dm->irqfds); + + snprintf(name, sizeof(name), "bao-ioirqfds%u", dm->info.id); + + dm->irqfd_server = alloc_workqueue(name, WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!dm->irqfd_server) + return -ENOMEM; + + return 0; +} + +void bao_irqfd_server_destroy(struct bao_dm *dm) +{ + struct irqfd *irqfd, *next; + + if (WARN_ON_ONCE(!dm)) + return; + + if (dm->irqfd_server) + destroy_workqueue(dm->irqfd_server); + + mutex_lock(&dm->irqfds_lock); + list_for_each_entry_safe(irqfd, next, &dm->irqfds, list) + bao_irqfd_shutdown(irqfd); + mutex_unlock(&dm->irqfds_lock); +} diff --git a/include/uapi/linux/bao.h b/include/uapi/linux/bao.h new file mode 100644 index 000000000000..581b094861ce --- /dev/null +++ b/include/uapi/linux/bao.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Provides the Bao Hypervisor IOCTLs and global structures + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef _UAPI_BAO_H +#define _UAPI_BAO_H + +#include <linux/types.h> +#include <linux/uuid.h> +#include <linux/list.h> + +#define BAO_IO_WRITE 0x0 +#define BAO_IO_READ 0x1 +#define BAO_IO_ASK 0x2 +#define BAO_IO_NOTIFY 0x3 + +#define BAO_NAME_MAX_LEN 16 +#define BAO_IO_REQUEST_MAX 64 +#define BAO_IO_MAX_DMS 16 + +/** + * struct bao_virtio_request - Parameters of a Bao VirtIO request + * @dm_id: Device model ID + * @addr: MMIO register address accessed + * @op: Operation type (WRITE, READ, ASK, NOTIFY) + * @value: Value to write or read + * @access_width: Access width (VirtIO MMIO supports 4-byte aligned accesses) + * @request_id: Request ID of the I/O request + */ +struct bao_virtio_request { + __u64 dm_id; + __u64 addr; + __u64 op; + __u64 value; + __u64 access_width; + __u64 request_id; +}; + +/** + * struct bao_ioeventfd - Parameters of an ioeventfd request + * @fd: Eventfd file descriptor associated with the I/O request + * @flags: Logical OR of BAO_IOEVENTFD_FLAG_* + * @addr: Start address of the I/O range + * @len: Length of the I/O range + * @reserved: Reserved, must be 0 + * @data: Data for matching (used if data matching is enabled) + */ +struct bao_ioeventfd { + __u32 fd; + __u32 flags; + __u64 addr; + __u32 len; + __u32 reserved; + __u64 data; +}; + +/** + * struct bao_irqfd - Parameters of an IRQFD request + * @fd: File descriptor of the eventfd + * @flags: Flags associated with the eventfd + */ +struct bao_irqfd { + __s32 fd; + __u32 flags; +}; + +/** + * struct bao_dm_info - Parameters of a Bao device model + * @id: Virtual ID of the DM + * @shmem_addr: Base address of the shared memory + * @shmem_size: Size of the shared memory + * @irq: IRQ number + * @fd: File descriptor of the DM + */ +struct bao_dm_info { + __u32 id; + __u64 shmem_addr; + __u64 shmem_size; + __u32 irq; + __s32 fd; +}; + +/* + * The ioctl type for Bao, documented in + * Documentation/userspace-api/ioctl/ioctl-number.rst + */ +#define BAO_IOCTL_TYPE 0xA6 + +/* + * Bao userspace IOCTL commands + * Follows Linux kernel convention, see Documentation/driver-api/ioctl.rst + */ +#define BAO_IOCTL_DM_GET_INFO _IOWR(BAO_IOCTL_TYPE, 0x01, struct bao_dm_info) +#define BAO_IOCTL_IO_CLIENT_ATTACH \ + _IOWR(BAO_IOCTL_TYPE, 0x02, struct bao_virtio_request) +#define BAO_IOCTL_IO_REQUEST_COMPLETE \ + _IOW(BAO_IOCTL_TYPE, 0x03, struct bao_virtio_request) +#define BAO_IOCTL_IOEVENTFD _IOW(BAO_IOCTL_TYPE, 0x04, struct bao_ioeventfd) +#define BAO_IOCTL_IRQFD _IOW(BAO_IOCTL_TYPE, 0x05, struct bao_irqfd) + +/* Remote I/O Hypercall ID */ +#define REMIO_HC_ID 0x2 + +/** + * struct remio_hypercall_ret - Remote I/O hypercall return values + * @hyp_ret: Generic return value from the Bao hypercall + * @remio_hyp_ret: Remote I/O–specific return value + * @pending_requests: Number of pending requests (only for Remote I/O Ask hypercall) + */ +struct remio_hypercall_ret { + u64 hyp_ret; + u64 remio_hyp_ret; + u64 pending_requests; +}; + +#endif /* _UAPI_BAO_H */ -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 4/5] virt: add Bao I/O dispatcher driver 2025-12-24 13:52 ` [PATCH 4/5] virt: add Bao I/O dispatcher driver joaopeixoto @ 2025-12-25 9:12 ` Krzysztof Kozlowski 0 siblings, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2025-12-25 9:12 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Dec 24, 2025 at 01:52:16PM +0000, joaopeixoto@osyx.tech wrote: > +static const struct of_device_id bao_io_dispatcher_driver_dt_ids[] = { > + { .compatible = "bao,io-dispatcher" }, > + { /* sentinel */ } > +}; > + > +MODULE_DEVICE_TABLE(of, bao_io_dispatcher_driver_dt_ids); > + > +static struct platform_driver bao_io_dispatcher_driver = { > + .probe = bao_io_dispatcher_driver_register, > + .remove = bao_io_dispatcher_driver_unregister, > + .driver = { > + .name = "bao-io-dispatcher", > + .of_match_table = > + of_match_ptr(bao_io_dispatcher_driver_dt_ids), You have warnings here. Do extensive building of your code before sending. > + .owner = THIS_MODULE, NAK Don't send us 12-year-old vendor code. Tools already report this, so you just did not run them... Nothing here is evn formatted correctly... Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 5/5] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto ` (3 preceding siblings ...) 2025-12-24 13:52 ` [PATCH 4/5] virt: add Bao I/O dispatcher driver joaopeixoto @ 2025-12-24 13:52 ` joaopeixoto 2025-12-25 8:52 ` Krzysztof Kozlowski 2025-12-25 8:51 ` [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers Krzysztof Kozlowski 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto 6 siblings, 1 reply; 31+ messages in thread From: joaopeixoto @ 2025-12-24 13:52 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> Add MAINTAINERS entries for the Bao hypervisor components including: - Bao IPC shared memory driver and its device tree bindings - Bao I/O dispatcher driver and its device tree bindings - Kernel headers for Bao (ARM, ARM64, and RISC-V) - UAPI header This ensures that the kernel review and notification system correctly identifies the maintainers for all Bao hypervisor components in the kernel tree. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index dc731d37c8fe..e50ad6a1bc4a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4321,6 +4321,18 @@ F: drivers/video/backlight/ F: include/linux/backlight.h F: include/linux/pwm_backlight.h +BAO HYPERVISOR +M: José Martins <jose@osyx.tech> +M: David Cerdeira <davidmcerdeira@osyx.tech> +M: João Peixoto <joaopeixoto@osyx.tech> +S: Maintained +F: Documentation/devicetree/bindings/bao/ +F: arch/arm/include/asm/bao.h +F: arch/arm64/include/asm/bao.h +F: arch/riscv/include/asm/bao.h +F: drivers/virt/bao +F: include/uapi/linux/bao.h + BARCO P50 GPIO DRIVER M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com> M: Peter Korsgaard <peter.korsgaard@barco.com> -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 5/5] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings 2025-12-24 13:52 ` [PATCH 5/5] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings joaopeixoto @ 2025-12-25 8:52 ` Krzysztof Kozlowski 0 siblings, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2025-12-25 8:52 UTC (permalink / raw) To: joaopeixoto, linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On 24/12/2025 14:52, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > Add MAINTAINERS entries for the Bao hypervisor components including: > - Bao IPC shared memory driver and its device tree bindings > - Bao I/O dispatcher driver and its device tree bindings > - Kernel headers for Bao (ARM, ARM64, and RISC-V) > - UAPI header This is irrelevant. > > This ensures that the kernel review and notification system correctly > identifies the maintainers for all Bao hypervisor components in the > kernel tree. This is irrelevant as well. Write concise yet useful commit msgs. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto ` (4 preceding siblings ...) 2025-12-24 13:52 ` [PATCH 5/5] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings joaopeixoto @ 2025-12-25 8:51 ` Krzysztof Kozlowski 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto 6 siblings, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2025-12-25 8:51 UTC (permalink / raw) To: joaopeixoto, linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On 24/12/2025 14:52, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This series introduces support for the Bao hypervisor guest-side drivers > under drivers/virt/bao and the associated Device Tree bindings, UAPI, > and MAINTAINERS entries. > > Bao is a lightweight static-partitioning hypervisor for embedded and > safety-critical systems. This series adds: > - The Bao IPC shared memory driver, which enables Linux guests to > communicate with each other through shared memory regions. > - The Bao I/O Dispatcher driver, which allows Bao's VMs to share I/O > devices using device paravirtualization (VirtIO). > None of these patches, except MAINTAINERS reached my mailbox. Fix your submission process, so it will not be marked by spam and bounced. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 0/6] virt: Add Bao hypervisor IPC and I/O dispatcher drivers 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto ` (5 preceding siblings ...) 2025-12-25 8:51 ` [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers Krzysztof Kozlowski @ 2026-01-07 16:28 ` joaopeixoto 2026-01-07 16:28 ` [PATCH 1/6] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto ` (6 more replies) 6 siblings, 7 replies; 31+ messages in thread From: joaopeixoto @ 2026-01-07 16:28 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> This series introduces support for Bao hypervisor guest-side drivers under drivers/virt/bao, along with the associated Device Tree bindings, UAPI headers, and MAINTAINERS entries. Bao is a lightweight static-partitioning hypervisor for embedded and safety-critical systems. This series adds: - Bao IPC shared memory driver: Enables Linux guests to communicate with each other via shared memory regions. - Bao I/O Dispatcher driver: Allows Bao VMs to share I/O devices using device paravirtualization (VirtIO). Key updates in this version to align with upstream requirements: 1. Switched to misc device API: Removed all cdev, class, and alloc_chrdev_region code in favor of misc_register() for simpler, standard device management. 2. Clean kernel style and formatting: All code passes checkpatch.pl, with consistent variable declaration, function naming, and comment style. 3. Architecture abstraction: Consolidated ARM, ARM64, and RISC-V hypercall logic into architecture-specific headers, removing in-driver #ifdefs. 4. Commit messages: Now concise and Linux-kernel-style, describing behavior and impact clearly. 5. Device Tree compliance: Fixed all make dt_binding_check errors to ensure DT binding correctness. This series has been validated on Linux guests running under Bao hypervisor, confirming correct initialization, IPC shared-memory read/write operations, and I/O Dispatcher functionality for backend VMs. Feedback and review from maintainers of virtualization, architecture-specific code (ARM, ARM64, RISC-V), Device Tree bindings, and UAPI are welcome. João Peixoto (6): dt-bindings: Add Bao IPC shared memory driver binding virt: bao: Add Bao IPC shared memory driver dt-bindings: Add Bao I/O dispatcher driver binding virt: bao: Add Bao I/O dispatcher driver virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings .../bindings/bao/bao,io-dispatcher.yaml | 75 ++++ .../devicetree/bindings/bao/bao,ipcshmem.yaml | 82 ++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + .../userspace-api/ioctl/ioctl-number.rst | 2 + MAINTAINERS | 13 + arch/arm/include/asm/bao.h | 60 +++ arch/arm64/include/asm/bao.h | 60 +++ arch/riscv/include/asm/bao.h | 60 +++ drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 2 + drivers/virt/bao/Kconfig | 5 + drivers/virt/bao/Makefile | 4 + drivers/virt/bao/io-dispatcher/Kconfig | 15 + drivers/virt/bao/io-dispatcher/Makefile | 4 + drivers/virt/bao/io-dispatcher/bao_drv.h | 361 ++++++++++++++++ drivers/virt/bao/io-dispatcher/dm.c | 405 ++++++++++++++++++ drivers/virt/bao/io-dispatcher/driver.c | 185 ++++++++ drivers/virt/bao/io-dispatcher/intc.c | 64 +++ drivers/virt/bao/io-dispatcher/io_client.c | 405 ++++++++++++++++++ .../virt/bao/io-dispatcher/io_dispatcher.c | 179 ++++++++ drivers/virt/bao/io-dispatcher/ioeventfd.c | 323 ++++++++++++++ drivers/virt/bao/io-dispatcher/irqfd.c | 314 ++++++++++++++ drivers/virt/bao/ipcshmem/Kconfig | 8 + drivers/virt/bao/ipcshmem/Makefile | 3 + drivers/virt/bao/ipcshmem/ipcshmem.c | 252 +++++++++++ include/linux/bao.h | 44 ++ include/uapi/linux/bao.h | 98 +++++ 27 files changed, 3027 insertions(+) create mode 100644 Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml create mode 100644 Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml create mode 100644 arch/arm/include/asm/bao.h create mode 100644 arch/arm64/include/asm/bao.h create mode 100644 arch/riscv/include/asm/bao.h create mode 100644 drivers/virt/bao/Kconfig create mode 100644 drivers/virt/bao/Makefile create mode 100644 drivers/virt/bao/io-dispatcher/Kconfig create mode 100644 drivers/virt/bao/io-dispatcher/Makefile create mode 100644 drivers/virt/bao/io-dispatcher/bao_drv.h create mode 100644 drivers/virt/bao/io-dispatcher/dm.c create mode 100644 drivers/virt/bao/io-dispatcher/driver.c create mode 100644 drivers/virt/bao/io-dispatcher/intc.c create mode 100644 drivers/virt/bao/io-dispatcher/io_client.c create mode 100644 drivers/virt/bao/io-dispatcher/io_dispatcher.c create mode 100644 drivers/virt/bao/io-dispatcher/ioeventfd.c create mode 100644 drivers/virt/bao/io-dispatcher/irqfd.c create mode 100644 drivers/virt/bao/ipcshmem/Kconfig create mode 100644 drivers/virt/bao/ipcshmem/Makefile create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c create mode 100644 include/linux/bao.h create mode 100644 include/uapi/linux/bao.h -- 2.43.0 ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 1/6] dt-bindings: Add Bao IPC shared memory driver binding 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto @ 2026-01-07 16:28 ` joaopeixoto 2026-01-07 16:46 ` Krzysztof Kozlowski 2026-01-07 16:28 ` [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver joaopeixoto ` (5 subsequent siblings) 6 siblings, 1 reply; 31+ messages in thread From: joaopeixoto @ 2026-01-07 16:28 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> This patch introduces a device tree binding for the Bao IPC Shared Memory device, which enables communication between Bao hypervisor guests through dedicated shared-memory regions. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- .../devicetree/bindings/bao/bao,ipcshmem.yaml | 82 +++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 84 insertions(+) create mode 100644 Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml diff --git a/Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml b/Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml new file mode 100644 index 000000000000..fa91800db99a --- /dev/null +++ b/Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bao/bao,ipcshmem.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bao IPC Shared Memory Device + +maintainers: + - José Martins <jose@osyx.tech> + - David Cerdeira <davidmcerdeira@osyx.tech> + - João Peixoto <joaopeixoto@osyx.tech> + +description: | + Shared memory based communication device for Bao hypervisor guests. + + The device describes a set of shared-memory regions used for + communication between Bao guests. Each guest instantiating this + device uses one region for reading data produced by a peer guest + and another region for writing data consumed by that peer. + +properties: + compatible: + const: bao,ipcshmem + + reg: + description: + Shared memory region used for IPC. + minItems: 2 + maxItems: 2 + + read-channel: + description: | + Shared-memory sub-region that this guest reads from. + + This region is written by the peer Bao guest and read by the + guest instantiating this device. + + Consists of two cells: + - offset into the shared-memory region defined by `reg` + - size in bytes + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 2 + + write-channel: + description: | + Shared-memory sub-region that this guest writes to. + + This region is written by the guest instantiating this device and + read by the peer Bao guest. + + Consists of two cells: + - offset into the shared-memory region defined by `reg` + - size in bytes + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 2 + + id: + description: + Driver instance ID. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - read-channel + - write-channel + - id + +additionalProperties: false + +examples: + - | + bao-ipc@f0000000 { + compatible = "bao,ipcshmem"; + reg = <0x0 0xf0000000 0x0 0x00010000>; + read-channel = <0x0 0x2000>; + write-channel = <0x2000 0x2000>; + id = <0>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index c7591b2aec2a..c047fbd6b91a 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -223,6 +223,8 @@ patternProperties: description: Shenzhen AZW Technology Co., Ltd. "^baikal,.*": description: BAIKAL ELECTRONICS, JSC + "^bao,.*": + description: Bao Hypervisor "^bananapi,.*": description: BIPAI KEJI LIMITED "^beacon,.*": -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 1/6] dt-bindings: Add Bao IPC shared memory driver binding 2026-01-07 16:28 ` [PATCH 1/6] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto @ 2026-01-07 16:46 ` Krzysztof Kozlowski 0 siblings, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2026-01-07 16:46 UTC (permalink / raw) To: joaopeixoto, linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On 07/01/2026 17:28, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This patch introduces a device tree binding for the Bao IPC Shared Memory > device, which enables communication between Bao hypervisor guests through > dedicated shared-memory regions. > > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> Respond to feedback instead of ignoring it. I don't see any changelog either. Last posting was LLM junk so I will not spend much time on this. A nit, subject: drop second/last, redundant "binding". The "dt-bindings" prefix is already stating that these are bindings. See also: https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18 Do not attach (thread) your patchsets to some other threads (unrelated or older versions). This buries them deep in the mailbox and might interfere with applying entire sets. See also: https://elixir.bootlin.com/linux/v6.16-rc2/source/Documentation/process/submitting-patches.rst#L830 > --- > .../devicetree/bindings/bao/bao,ipcshmem.yaml | 82 +++++++++++++++++++ > .../devicetree/bindings/vendor-prefixes.yaml | 2 + > 2 files changed, 84 insertions(+) > create mode 100644 Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml > > diff --git a/Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml b/Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml > new file mode 100644 > index 000000000000..fa91800db99a > --- /dev/null > +++ b/Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml > @@ -0,0 +1,82 @@ > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/bao/bao,ipcshmem.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: Bao IPC Shared Memory Device Nothing here is suitable for bindings, really. Simplified node for establishing channel of communication to hypervisor would be allowed. But multiple devices for that? No point. Develop proper interface with your hypervisor for all this. > + > +maintainers: > + - José Martins <jose@osyx.tech> > + - David Cerdeira <davidmcerdeira@osyx.tech> > + - João Peixoto <joaopeixoto@osyx.tech> > + > +description: | > + Shared memory based communication device for Bao hypervisor guests. > + > + The device describes a set of shared-memory regions used for > + communication between Bao guests. Each guest instantiating this > + device uses one region for reading data produced by a peer guest > + and another region for writing data consumed by that peer. > + > +properties: > + compatible: > + const: bao,ipcshmem > + > + reg: > + description: > + Shared memory region used for IPC. > + minItems: 2 > + maxItems: 2 Look at other bindings. > + > + read-channel: > + description: | > + Shared-memory sub-region that this guest reads from. > + > + This region is written by the peer Bao guest and read by the > + guest instantiating this device. > + > + Consists of two cells: > + - offset into the shared-memory region defined by `reg` > + - size in bytes > + $ref: /schemas/types.yaml#/definitions/uint32-array > + minItems: 2 > + maxItems: 2 Drop property, reg defines it. > + > + write-channel: Drop property, reg defines it. > + description: | > + Shared-memory sub-region that this guest writes to. > + > + This region is written by the guest instantiating this device and > + read by the peer Bao guest. > + > + Consists of two cells: > + - offset into the shared-memory region defined by `reg` > + - size in bytes > + $ref: /schemas/types.yaml#/definitions/uint32-array > + minItems: 2 > + maxItems: 2 > + > + id: > + description: > + Driver instance ID. > + $ref: /schemas/types.yaml#/definitions/uint32 NAK, not allowed. Read writing bindings. > + > +required: > + - compatible > + - reg > + - read-channel > + - write-channel > + - id > + > +additionalProperties: false > + > +examples: > + - | > + bao-ipc@f0000000 { Node names should be generic. See also an explanation and list of examples (not exhaustive) in DT specification: https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation If you cannot find a name matching your device, please check in kernel sources for similar cases or you can grow the spec (via pull request to DT spec repo). > + compatible = "bao,ipcshmem"; > + reg = <0x0 0xf0000000 0x0 0x00010000>; > + read-channel = <0x0 0x2000>; > + write-channel = <0x2000 0x2000>; > + id = <0>; > + }; > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml > index c7591b2aec2a..c047fbd6b91a 100644 > --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml > +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml > @@ -223,6 +223,8 @@ patternProperties: > description: Shenzhen AZW Technology Co., Ltd. > "^baikal,.*": > description: BAIKAL ELECTRONICS, JSC > + "^bao,.*": > + description: Bao Hypervisor Vendor prefixes are for companies. What is the company here? What is stock ticker or website? > "^bananapi,.*": > description: BIPAI KEJI LIMITED > "^beacon,.*": Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto 2026-01-07 16:28 ` [PATCH 1/6] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto @ 2026-01-07 16:28 ` joaopeixoto 2026-01-07 18:54 ` Randy Dunlap 2026-01-14 20:37 ` Andrew Jones 2026-01-07 16:28 ` [PATCH 3/6] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto ` (4 subsequent siblings) 6 siblings, 2 replies; 31+ messages in thread From: joaopeixoto @ 2026-01-07 16:28 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> This driver provides an interface for guests running on the Bao hypervisor to communicate with each other through shared-memory channels. Each guest is assigned a dedicated read and write region defined in the device tree. Userspace can access these regions via standard read/write/mmap operations. Writes to the write region trigger a hypervisor notification through an architecture-specific hypercall (HVC on ARM, SBI ecall on RISC-V). Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- arch/arm/include/asm/bao.h | 31 ++++ arch/arm64/include/asm/bao.h | 31 ++++ arch/riscv/include/asm/bao.h | 31 ++++ drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 1 + drivers/virt/bao/Kconfig | 3 + drivers/virt/bao/Makefile | 3 + drivers/virt/bao/ipcshmem/Kconfig | 8 + drivers/virt/bao/ipcshmem/Makefile | 3 + drivers/virt/bao/ipcshmem/ipcshmem.c | 255 +++++++++++++++++++++++++++ 10 files changed, 368 insertions(+) create mode 100644 arch/arm/include/asm/bao.h create mode 100644 arch/arm64/include/asm/bao.h create mode 100644 arch/riscv/include/asm/bao.h create mode 100644 drivers/virt/bao/Kconfig create mode 100644 drivers/virt/bao/Makefile create mode 100644 drivers/virt/bao/ipcshmem/Kconfig create mode 100644 drivers/virt/bao/ipcshmem/Makefile create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c diff --git a/arch/arm/include/asm/bao.h b/arch/arm/include/asm/bao.h new file mode 100644 index 000000000000..9644d06be705 --- /dev/null +++ b/arch/arm/include/asm/bao.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Bao Hypervisor Hypercall Interface + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __ASM_ARM_BAO_H +#define __ASM_ARM_BAO_H + +#include <linux/arm-smccc.h> + +static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, + unsigned long ipcshmem_id) +{ + struct arm_smccc_res res; + + arm_smccc_hvc(ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, + ARM_SMCCC_OWNER_VENDOR_HYP, + hypercall_id), + ipcshmem_id, 0, 0, 0, 0, 0, 0, &res); + + return res.a0; +} + +#endif /* __ASM_ARM_BAO_H */ diff --git a/arch/arm64/include/asm/bao.h b/arch/arm64/include/asm/bao.h new file mode 100644 index 000000000000..a1819966b59b --- /dev/null +++ b/arch/arm64/include/asm/bao.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Bao Hypervisor Hypercall Interface + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __ASM_ARM64_BAO_H +#define __ASM_ARM64_BAO_H + +#include <linux/arm-smccc.h> + +static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, + unsigned long ipcshmem_id) +{ + struct arm_smccc_res res; + + arm_smccc_hvc(ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, + ARM_SMCCC_OWNER_VENDOR_HYP, + hypercall_id), + ipcshmem_id, 0, 0, 0, 0, 0, 0, &res); + + return res.a0; +} + +#endif /* __ASM_ARM64_BAO_H */ diff --git a/arch/riscv/include/asm/bao.h b/arch/riscv/include/asm/bao.h new file mode 100644 index 000000000000..35658f37e1bd --- /dev/null +++ b/arch/riscv/include/asm/bao.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Bao Hypervisor Hypercall Interface + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __ASM_RISCV_BAO_H +#define __ASM_RISCV_BAO_H + +#include <asm/sbi.h> + +#define BAO_SBI_EXT_ID 0x08000ba0 + +static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, + unsigned long ipcshmem_id) +{ + struct sbiret ret; + + ret = sbi_ecall(BAO_SBI_EXT_ID, hypercall_id, ipcshmem_id, 0, 0, 0, 0, + 0); + + return ret.error; +} + +#endif /* __ASM_RISCV_BAO_H */ diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 52eb7e4ba71f..cb98c4c52fd1 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -47,6 +47,8 @@ source "drivers/virt/nitro_enclaves/Kconfig" source "drivers/virt/acrn/Kconfig" +source "drivers/virt/bao/Kconfig" + endif source "drivers/virt/coco/Kconfig" diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index f29901bd7820..623a671f8711 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -10,3 +10,4 @@ obj-y += vboxguest/ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-y += coco/ +obj-$(CONFIG_BAO_SHMEM) += bao/ diff --git a/drivers/virt/bao/Kconfig b/drivers/virt/bao/Kconfig new file mode 100644 index 000000000000..4f7929d57475 --- /dev/null +++ b/drivers/virt/bao/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +source "drivers/virt/bao/ipcshmem/Kconfig" diff --git a/drivers/virt/bao/Makefile b/drivers/virt/bao/Makefile new file mode 100644 index 000000000000..68f5d3f282c4 --- /dev/null +++ b/drivers/virt/bao/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_BAO_SHMEM) += ipcshmem/ diff --git a/drivers/virt/bao/ipcshmem/Kconfig b/drivers/virt/bao/ipcshmem/Kconfig new file mode 100644 index 000000000000..966bb75aa495 --- /dev/null +++ b/drivers/virt/bao/ipcshmem/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +config BAO_SHMEM + tristate "Bao hypervisor shared memory support" + help + This enables support for Bao shared memory communication. + It allows the kernel to interface with guests running under + the Bao hypervisor, providing a character device interface + for exchanging data through dedicated shared-memory regions. diff --git a/drivers/virt/bao/ipcshmem/Makefile b/drivers/virt/bao/ipcshmem/Makefile new file mode 100644 index 000000000000..e027dcdb06aa --- /dev/null +++ b/drivers/virt/bao/ipcshmem/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_BAO_SHMEM) += bao.o +bao-objs += ipcshmem.o diff --git a/drivers/virt/bao/ipcshmem/ipcshmem.c b/drivers/virt/bao/ipcshmem/ipcshmem.c new file mode 100644 index 000000000000..f3892d41248c --- /dev/null +++ b/drivers/virt/bao/ipcshmem/ipcshmem.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor IPC Through Shared-memory Driver + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#include <linux/platform_device.h> +#include <linux/miscdevice.h> +#include <linux/of.h> +#include <linux/mm.h> +#include <linux/io.h> +#include <asm/bao.h> + +#define BAO_IPCSHMEM_NAME_LEN 16 + +/* IPC through shared-memory hypercall ID */ +#define BAO_IPCSHMEM_HYPERCALL_ID 0x1 + +struct bao_ipcshmem { + struct miscdevice miscdev; + int id; + char label[BAO_IPCSHMEM_NAME_LEN]; + void *read_base; + size_t read_size; + void *write_base; + size_t write_size; + phys_addr_t physical_base; + size_t shmem_size; + void *shmem_base_addr; +}; + +static int bao_ipcshmem_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct bao_ipcshmem *bao = filp->private_data; + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + phys_addr_t paddr; + + if (!vsize) + return -EINVAL; + + if (offset >= bao->shmem_size || + vsize > bao->shmem_size - offset) + return -EINVAL; + + paddr = bao->physical_base + offset; + + if (!PAGE_ALIGNED(paddr)) + return -EINVAL; + + return remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT, vsize, + vma->vm_page_prot); +} + +static ssize_t bao_ipcshmem_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct bao_ipcshmem *bao = filp->private_data; + size_t available; + + if (*ppos >= bao->read_size) + return 0; + + available = bao->read_size - *ppos; + count = min(count, available); + + if (copy_to_user(buf, bao->read_base + *ppos, count)) + return -EFAULT; + + *ppos += count; + return count; +} + +static ssize_t bao_ipcshmem_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct bao_ipcshmem *bao = filp->private_data; + size_t available; + + if (*ppos >= bao->write_size) + return 0; + + available = bao->write_size - *ppos; + count = min(count, available); + + if (copy_from_user(bao->write_base + *ppos, buf, count)) + return -EFAULT; + + *ppos += count; + + /* Notify Bao hypervisor */ + bao_ipcshmem_hypercall(BAO_IPCSHMEM_HYPERCALL_ID, bao->id); + + return count; +} + +static int bao_ipcshmem_open(struct inode *inode, struct file *filp) +{ + struct bao_ipcshmem *bao; + + bao = container_of(filp->private_data, struct bao_ipcshmem, miscdev); + filp->private_data = bao; + + return 0; +} + +static int bao_ipcshmem_release(struct inode *inode, struct file *filp) +{ + filp->private_data = NULL; + return 0; +} + +static const struct file_operations bao_ipcshmem_fops = { + .owner = THIS_MODULE, + .read = bao_ipcshmem_read, + .write = bao_ipcshmem_write, + .mmap = bao_ipcshmem_mmap, + .open = bao_ipcshmem_open, + .release = bao_ipcshmem_release, +}; + +static int bao_ipcshmem_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct resource *r; + struct bao_ipcshmem *bao; + resource_size_t shmem_size; + u32 write_offset; + u32 read_offset; + u32 write_size; + u32 read_size; + u32 id; + bool rd_in_range; + bool wr_in_range; + bool disjoint; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(dev, "missing shared memory resource\n"); + return -ENODEV; + } + + ret = of_property_read_u32(np, "id", &id); + if (ret) { + dev_err(dev, "missing or invalid 'id' property\n"); + return ret; + } + + ret = of_property_read_u32_index(np, "read-channel", 0, &read_offset); + if (ret) { + dev_err(dev, "failed to read 'read-channel' offset: %d\n", ret); + return ret; + } + + ret = of_property_read_u32_index(np, "read-channel", 1, &read_size); + if (ret) { + dev_err(dev, "failed to read 'read-channel' size: %d\n", ret); + return ret; + } + + ret = of_property_read_u32_index(np, "write-channel", 0, &write_offset); + if (ret) { + dev_err(dev, "failed to read 'write-channel' offset: %d\n", ret); + return ret; + } + + ret = of_property_read_u32_index(np, "write-channel", 1, &write_size); + if (ret) { + dev_err(dev, "failed to read 'write-channel' size: %d\n", ret); + return ret; + } + + shmem_size = resource_size(r); + + rd_in_range = (read_offset + read_size) <= shmem_size; + wr_in_range = (write_offset + write_size) <= shmem_size; + disjoint = ((read_offset + read_size) <= write_offset) || + ((write_offset + write_size) <= read_offset); + + if (!rd_in_range || !wr_in_range || !disjoint) { + dev_err(dev, "invalid read/write channel ranges\n"); + return -EINVAL; + } + + bao = devm_kzalloc(dev, sizeof(*bao), GFP_KERNEL); + if (!bao) + return -ENOMEM; + + bao->shmem_base_addr = + devm_memremap(dev, r->start, shmem_size, MEMREMAP_WB); + if (!bao->shmem_base_addr) { + dev_err(dev, "failed to remap shared memory\n"); + return -ENOMEM; + } + + bao->id = id; + bao->read_size = read_size; + bao->write_size = write_size; + bao->read_base = (u8 *)bao->shmem_base_addr + read_offset; + bao->write_base = (u8 *)bao->shmem_base_addr + write_offset; + bao->physical_base = r->start; + bao->shmem_size = shmem_size; + + scnprintf(bao->label, BAO_IPCSHMEM_NAME_LEN, "baoipc%d", id); + + bao->miscdev.minor = MISC_DYNAMIC_MINOR; + bao->miscdev.name = bao->label; + bao->miscdev.fops = &bao_ipcshmem_fops; + bao->miscdev.parent = dev; + + ret = misc_register(&bao->miscdev); + if (ret) { + dev_err(dev, "failed to register misc device: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, bao); + + dev_info(dev, "Bao IPC shared memory device '%s' registered\n", bao->label); + return 0; +} + +static void bao_ipcshmem_remove(struct platform_device *pdev) +{ + struct bao_ipcshmem *bao = platform_get_drvdata(pdev); + + if (bao) + misc_deregister(&bao->miscdev); +} + +static const struct of_device_id of_bao_ipcshmem_match[] = { + { .compatible = "bao,ipcshmem" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_bao_ipcshmem_match); + +static struct platform_driver bao_ipcshmem_driver = { + .probe = bao_ipcshmem_probe, + .remove = bao_ipcshmem_remove, + .driver = { + .name = "baoipc", + .of_match_table = of_bao_ipcshmem_match, + }, +}; + +module_platform_driver(bao_ipcshmem_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Cerdeira <davidmcerdeira@osyx.tech>"); +MODULE_AUTHOR("José Martins <jose@osyx.tech>"); +MODULE_AUTHOR("João Peixoto <joaopeixoto@osyx.tech>"); +MODULE_DESCRIPTION("Bao Hypervisor IPC Through Shared-memory Driver"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver 2026-01-07 16:28 ` [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver joaopeixoto @ 2026-01-07 18:54 ` Randy Dunlap 2026-01-14 20:37 ` Andrew Jones 1 sibling, 0 replies; 31+ messages in thread From: Randy Dunlap @ 2026-01-07 18:54 UTC (permalink / raw) To: joaopeixoto, linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On 1/7/26 8:28 AM, joaopeixoto@osyx.tech wrote: > diff --git a/drivers/virt/bao/ipcshmem/Kconfig b/drivers/virt/bao/ipcshmem/Kconfig > new file mode 100644 > index 000000000000..966bb75aa495 > --- /dev/null > +++ b/drivers/virt/bao/ipcshmem/Kconfig > @@ -0,0 +1,8 @@ > +# SPDX-License-Identifier: GPL-2.0 > +config BAO_SHMEM > + tristate "Bao hypervisor shared memory support" > + help > + This enables support for Bao shared memory communication. > + It allows the kernel to interface with guests running under > + the Bao hypervisor, providing a character device interface > + for exchanging data through dedicated shared-memory regions. Please follow Documentation/process/coding-style.rst: 10) Kconfig configuration files ------------------------------- For all of the Kconfig* configuration files throughout the source tree, the indentation is somewhat different. Lines under a ``config`` definition are indented with one tab, while help text is indented an additional two spaces. Example:: config AUDIT bool "Auditing support" depends on NET help Enable auditing infrastructure that can be used with another kernel subsystem, such as SELinux (which requires this for logging of avc messages output). Does not do system-call auditing without CONFIG_AUDITSYSCALL. -- ~Randy ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver 2026-01-07 16:28 ` [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver joaopeixoto 2026-01-07 18:54 ` Randy Dunlap @ 2026-01-14 20:37 ` Andrew Jones 1 sibling, 0 replies; 31+ messages in thread From: Andrew Jones @ 2026-01-14 20:37 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Jan 07, 2026 at 04:28:25PM +0000, joaopeixoto@osyx.tech wrote: ... > diff --git a/arch/riscv/include/asm/bao.h b/arch/riscv/include/asm/bao.h > new file mode 100644 > index 000000000000..35658f37e1bd > --- /dev/null > +++ b/arch/riscv/include/asm/bao.h > @@ -0,0 +1,31 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Bao Hypervisor Hypercall Interface > + * > + * Copyright (c) Bao Project and Contributors. All rights reserved. > + * > + * Authors: > + * João Peixoto <joaopeixoto@osyx.tech> > + * José Martins <jose@osyx.tech> > + * David Cerdeira <davidmcerdeira@osyx.tech> > + */ > + > +#ifndef __ASM_RISCV_BAO_H > +#define __ASM_RISCV_BAO_H > + > +#include <asm/sbi.h> > + > +#define BAO_SBI_EXT_ID 0x08000ba0 > + > +static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, > + unsigned long ipcshmem_id) > +{ > + struct sbiret ret; > + > + ret = sbi_ecall(BAO_SBI_EXT_ID, hypercall_id, ipcshmem_id, 0, 0, 0, 0, > + 0); Just let lines like these stick out. We have up to 100 chars and these types of few-char wraps are the most annoying. Thanks, drew ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 3/6] dt-bindings: Add Bao I/O dispatcher driver binding 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto 2026-01-07 16:28 ` [PATCH 1/6] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto 2026-01-07 16:28 ` [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver joaopeixoto @ 2026-01-07 16:28 ` joaopeixoto 2026-01-07 16:47 ` Krzysztof Kozlowski 2026-01-07 16:28 ` [PATCH 4/6] virt: bao: Add Bao I/O dispatcher driver joaopeixoto ` (3 subsequent siblings) 6 siblings, 1 reply; 31+ messages in thread From: joaopeixoto @ 2026-01-07 16:28 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> This patch introduces a device tree binding for the Bao I/O Dispatcher, a device used in backend VMs running virtualized devices (e.g., VirtIO). Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- .../bindings/bao/bao,io-dispatcher.yaml | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml diff --git a/Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml b/Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml new file mode 100644 index 000000000000..8ca450e4b9d5 --- /dev/null +++ b/Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bao/bao,io-dispatcher.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bao I/O Dispatcher Device + +maintainers: + - João Peixoto <joaopeixoto@osyx.tech> + - José Martins <jose@osyx.tech> + - David Cerdeira <davidmcerdeira@osyx.tech> + +description: | + I/O Dispatcher device for Bao hypervisor guests that run virtualized + devices (e.g., VirtIO). + + This device is only required in backend VMs, which are responsible for + performing the actual I/O operations on physical hardware. Frontend + VMs, which only consume I/O services, do not require this device. + + The I/O Dispatcher provides access to one or more backend devices. + Each backend device is associated with a contiguous shared-memory + region used to exchange I/O buffers with the respective frontend + driver, and an interrupt used by the Bao hypervisor to notify the + backend VM of pending I/O requests. + +properties: + compatible: + const: bao,io-dispatcher + + reg: + description: | + Contiguous memory-mapped regions for each VirtIO backend device + managed by the I/O Dispatcher. + + Each region is used to exchange I/O buffers between the backend + and frontend devices. A single region corresponds to one + backend device. + minItems: 1 + maxItems: 64 + + interrupts: + description: | + Interrupts associated with the VirtIO backend devices. + + Each interrupt corresponds to a backend device and is used + by the Bao hypervisor to notify the backend VM of pending + I/O requests from the associated frontend driver. + minItems: 1 + maxItems: 64 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + bao-io-dispatcher@50000000 { + compatible = "bao,io-dispatcher"; + reg = <0x0 0x50000000 0x0 0x01000000 + 0x0 0x51000000 0x0 0x01000000 + 0x0 0x52000000 0x0 0x01000000 + 0x0 0x53000000 0x0 0x01000000 + 0x0 0x54000000 0x0 0x01000000>; + interrupts = <0x0 0x08 0x1 + 0x0 0x09 0x1 + 0x0 0x0a 0x1 + 0x0 0x0b 0x1 + 0x0 0x0c 0x1>; + interrupt-parent = <&gic>; + }; -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 3/6] dt-bindings: Add Bao I/O dispatcher driver binding 2026-01-07 16:28 ` [PATCH 3/6] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto @ 2026-01-07 16:47 ` Krzysztof Kozlowski 0 siblings, 0 replies; 31+ messages in thread From: Krzysztof Kozlowski @ 2026-01-07 16:47 UTC (permalink / raw) To: joaopeixoto, linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On 07/01/2026 17:28, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This patch introduces a device tree binding for the Bao I/O Dispatcher, > a device used in backend VMs running virtualized devices (e.g., VirtIO). > > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > .../bindings/bao/bao,io-dispatcher.yaml | 75 +++++++++++++++++++ > 1 file changed, 75 insertions(+) > create mode 100644 Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml > > diff --git a/Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml b/Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml > new file mode 100644 > index 000000000000..8ca450e4b9d5 > --- /dev/null > +++ b/Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml > @@ -0,0 +1,75 @@ > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/bao/bao,io-dispatcher.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: Bao I/O Dispatcher Device No, you don't get multiple devices per every driver. Look at gunyah or Mediatek Genio. Come back when you solve ENTIRE FEEDBACK given to them first. And then say how you solved that feedback because so far I see you only ignoring review of your LLM slop or other Microslop product. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 4/6] virt: bao: Add Bao I/O dispatcher driver 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto ` (2 preceding siblings ...) 2026-01-07 16:28 ` [PATCH 3/6] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto @ 2026-01-07 16:28 ` joaopeixoto 2026-01-14 20:32 ` Andrew Jones 2026-01-07 16:28 ` [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header joaopeixoto ` (2 subsequent siblings) 6 siblings, 1 reply; 31+ messages in thread From: joaopeixoto @ 2026-01-07 16:28 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> Introduce the Bao I/O Dispatcher, a kernel module for Bao hypervisor guests that run virtualized devices (e.g., VirtIO). This driver is only required in backend VMs, which are responsible for performing the actual I/O operations on physical hardware. Frontend VMs, which only consume I/O services, do not require this device. The I/O Dispatcher provides access to one or more backend devices. Each backend device is associated with a contiguous shared-memory region used to exchange I/O buffers with the respective frontend driver, and an interrupt used by the Bao hypervisor to notify the backend VM of pending I/O requests. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- .../userspace-api/ioctl/ioctl-number.rst | 2 + arch/arm/include/asm/bao.h | 30 ++ arch/arm64/include/asm/bao.h | 30 ++ arch/riscv/include/asm/bao.h | 30 ++ drivers/virt/Makefile | 1 + drivers/virt/bao/Kconfig | 2 + drivers/virt/bao/Makefile | 1 + drivers/virt/bao/io-dispatcher/Kconfig | 15 + drivers/virt/bao/io-dispatcher/Makefile | 4 + drivers/virt/bao/io-dispatcher/bao_drv.h | 361 ++++++++++++++++ drivers/virt/bao/io-dispatcher/dm.c | 405 ++++++++++++++++++ drivers/virt/bao/io-dispatcher/driver.c | 185 ++++++++ drivers/virt/bao/io-dispatcher/intc.c | 64 +++ drivers/virt/bao/io-dispatcher/io_client.c | 405 ++++++++++++++++++ .../virt/bao/io-dispatcher/io_dispatcher.c | 179 ++++++++ drivers/virt/bao/io-dispatcher/ioeventfd.c | 323 ++++++++++++++ drivers/virt/bao/io-dispatcher/irqfd.c | 314 ++++++++++++++ include/linux/bao.h | 41 ++ include/uapi/linux/bao.h | 98 +++++ 19 files changed, 2490 insertions(+) create mode 100644 drivers/virt/bao/io-dispatcher/Kconfig create mode 100644 drivers/virt/bao/io-dispatcher/Makefile create mode 100644 drivers/virt/bao/io-dispatcher/bao_drv.h create mode 100644 drivers/virt/bao/io-dispatcher/dm.c create mode 100644 drivers/virt/bao/io-dispatcher/driver.c create mode 100644 drivers/virt/bao/io-dispatcher/intc.c create mode 100644 drivers/virt/bao/io-dispatcher/io_client.c create mode 100644 drivers/virt/bao/io-dispatcher/io_dispatcher.c create mode 100644 drivers/virt/bao/io-dispatcher/ioeventfd.c create mode 100644 drivers/virt/bao/io-dispatcher/irqfd.c create mode 100644 include/linux/bao.h create mode 100644 include/uapi/linux/bao.h diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 7232b3544cec..b0dbc307a9cb 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -349,6 +349,8 @@ Code Seq# Include File Comments <mailto:luzmaximilian@gmail.com> 0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver <mailto:luzmaximilian@gmail.com> +0xA6 all uapi/linux/bao.h Bao hypervisor + <mailto:info@bao-project.org> 0xAA 00-3F linux/uapi/linux/userfaultfd.h 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h diff --git a/arch/arm/include/asm/bao.h b/arch/arm/include/asm/bao.h index 9644d06be705..5ece9ecb1455 100644 --- a/arch/arm/include/asm/bao.h +++ b/arch/arm/include/asm/bao.h @@ -14,6 +14,7 @@ #define __ASM_ARM_BAO_H #include <linux/arm-smccc.h> +#include <linux/bao.h> static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, unsigned long ipcshmem_id) @@ -28,4 +29,33 @@ static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, return res.a0; } +static inline unsigned long +bao_remio_hypercall(struct bao_remio_hypercall_ctx *ctx) +{ + register int r0 asm("r0") = + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, + ARM_SMCCC_OWNER_VENDOR_HYP, BAO_REMIO_HYPERCALL_ID); + register u32 r1 asm("r1") = ctx->dm_id; + register u32 r2 asm("r2") = ctx->addr; + register u32 r3 asm("r3") = ctx->op; + register u32 r4 asm("r4") = ctx->value; + register u32 r5 asm("r5") = ctx->request_id; + register u32 r6 asm("r6") = 0; + + asm volatile("hvc 0\n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r"(r4), + "=r"(r5), "=r"(r6) + : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5) + : "memory"); + + ctx->addr = r1; + ctx->op = r2; + ctx->value = r3; + ctx->access_width = r4; + ctx->request_id = r5; + ctx->npend_req = r6; + + return r0; +} + #endif /* __ASM_ARM_BAO_H */ diff --git a/arch/arm64/include/asm/bao.h b/arch/arm64/include/asm/bao.h index a1819966b59b..c7b7ec60c042 100644 --- a/arch/arm64/include/asm/bao.h +++ b/arch/arm64/include/asm/bao.h @@ -14,6 +14,7 @@ #define __ASM_ARM64_BAO_H #include <linux/arm-smccc.h> +#include <linux/bao.h> static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, unsigned long ipcshmem_id) @@ -28,4 +29,33 @@ static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, return res.a0; } +static inline unsigned long +bao_remio_hypercall(struct bao_remio_hypercall_ctx *ctx) +{ + register int x0 asm("x0") = + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, + ARM_SMCCC_OWNER_VENDOR_HYP, BAO_REMIO_HYPERCALL_ID); + register u64 x1 asm("x1") = ctx->dm_id; + register u64 x2 asm("x2") = ctx->addr; + register u64 x3 asm("x3") = ctx->op; + register u64 x4 asm("x4") = ctx->value; + register u64 x5 asm("x5") = ctx->request_id; + register u64 x6 asm("x6") = 0; + + asm volatile("hvc 0\n\t" + : "=r"(x0), "=r"(x1), "=r"(x2), "=r"(x3), "=r"(x4), + "=r"(x5), "=r"(x6) + : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) + : "memory"); + + ctx->addr = x1; + ctx->op = x2; + ctx->value = x3; + ctx->access_width = x4; + ctx->request_id = x5; + ctx->npend_req = x6; + + return x0; +} + #endif /* __ASM_ARM64_BAO_H */ diff --git a/arch/riscv/include/asm/bao.h b/arch/riscv/include/asm/bao.h index 35658f37e1bd..f04e6cd33fa9 100644 --- a/arch/riscv/include/asm/bao.h +++ b/arch/riscv/include/asm/bao.h @@ -14,6 +14,7 @@ #define __ASM_RISCV_BAO_H #include <asm/sbi.h> +#include <linux/bao.h> #define BAO_SBI_EXT_ID 0x08000ba0 @@ -28,4 +29,33 @@ static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, return ret.error; } +static inline unsigned long +bao_remio_hypercall(struct bao_remio_hypercall_ctx *ctx) +{ + register uintptr_t a0 asm("a0") = (uintptr_t)(ctx->dm_id); + register uintptr_t a1 asm("a1") = (uintptr_t)(ctx->addr); + register uintptr_t a2 asm("a2") = (uintptr_t)(ctx->op); + register uintptr_t a3 asm("a3") = (uintptr_t)(ctx->value); + register uintptr_t a4 asm("a4") = (uintptr_t)(ctx->request_id); + register uintptr_t a5 asm("a5") = (uintptr_t)(0); + register uintptr_t a6 asm("a6") = (uintptr_t)(BAO_REMIO_HYPERCALL_ID); + register uintptr_t a7 asm("a7") = (uintptr_t)(0x08000ba0); + + asm volatile("ecall" + : "+r"(a0), "+r"(a1), "+r"(a2), "+r"(a3), "+r"(a4), + "+r"(a5), "+r"(a6), "+r"(a7) + : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), + "r"(a6), "r"(a7) + : "memory"); + + ctx->addr = a2; + ctx->op = a3; + ctx->value = a4; + ctx->access_width = a5; + ctx->request_id = a6; + ctx->npend_req = a7; + + return a0; +} + #endif /* __ASM_RISCV_BAO_H */ diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index 623a671f8711..8bffc7ccd29e 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-y += coco/ obj-$(CONFIG_BAO_SHMEM) += bao/ +obj-$(CONFIG_BAO_IO_DISPATCHER) += bao/ diff --git a/drivers/virt/bao/Kconfig b/drivers/virt/bao/Kconfig index 4f7929d57475..ab08a20db8c4 100644 --- a/drivers/virt/bao/Kconfig +++ b/drivers/virt/bao/Kconfig @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 source "drivers/virt/bao/ipcshmem/Kconfig" + +source "drivers/virt/bao/io-dispatcher/Kconfig" diff --git a/drivers/virt/bao/Makefile b/drivers/virt/bao/Makefile index 68f5d3f282c4..c463f04cf206 100644 --- a/drivers/virt/bao/Makefile +++ b/drivers/virt/bao/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_BAO_SHMEM) += ipcshmem/ +obj-$(CONFIG_BAO_IO_DISPATCHER) += io-dispatcher/ diff --git a/drivers/virt/bao/io-dispatcher/Kconfig b/drivers/virt/bao/io-dispatcher/Kconfig new file mode 100644 index 000000000000..41c8c76c83b8 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +config BAO_IO_DISPATCHER + tristate "Bao Hypervisor I/O Dispatcher" + select EVENTFD + help + The Bao I/O Dispatcher is a kernel module for backend Linux VMs + running under the Bao hypervisor. It establishes the connection + between the Remote I/O system (Bao's mechanism for forwarding + I/O requests from frontend VMs to the backend VMs) and the + VirtIO backend device. + + This provides a unified API to support various VirtIO backends, + allowing Bao guests to perform I/O through the hypervisor + transparently. + diff --git a/drivers/virt/bao/io-dispatcher/Makefile b/drivers/virt/bao/io-dispatcher/Makefile new file mode 100644 index 000000000000..e18de1d1a026 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_BAO_IO_DISPATCHER) += bao.o +bao-objs += ioeventfd.o io_client.o io_dispatcher.o irqfd.o dm.o intc.o driver.o + diff --git a/drivers/virt/bao/io-dispatcher/bao_drv.h b/drivers/virt/bao/io-dispatcher/bao_drv.h new file mode 100644 index 000000000000..dd11be4484b1 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/bao_drv.h @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Provides some definitions for the Bao Hypervisor modules + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef __BAO_DRV_H +#define __BAO_DRV_H + +#include <linux/fs.h> +#include <linux/bao.h> +#include <uapi/linux/bao.h> + +#define BAO_NAME_MAX_LEN 16 +#define BAO_IO_MAX_DMS 16 + +#define BAO_IOEVENTFD_FLAG_DATAMATCH BIT(1) +#define BAO_IOEVENTFD_FLAG_DEASSIGN BIT(2) +#define BAO_IRQFD_FLAG_DEASSIGN 1U +#define BAO_IO_CLIENT_DESTROYING 0U + +struct bao_dm; +struct bao_io_client; + +typedef int (*bao_io_client_handler_t)(struct bao_io_client *client, + struct bao_virtio_request *req); + +/** + * enum bao_io_op - Bao hypervisor I/O operation types + * @BAO_IO_WRITE: Write operation + * @BAO_IO_READ: Read operation + * @BAO_IO_ASK: Request operation information (e.g., MMIO address) + * @BAO_IO_NOTIFY: Notify I/O completion + */ +enum bao_io_op { + BAO_IO_WRITE = 0, + BAO_IO_READ, + BAO_IO_ASK, + BAO_IO_NOTIFY, +}; + +/** + * struct bao_io_client - Bao I/O client + * @name: Client name + * @dm: The DM that the client belongs to + * @list: List node for this bao_io_client + * @is_control: If this client is the control client + * @flags: Flags (BAO_IO_CLIENT_*) + * @virtio_requests: List of free I/O requests + * @range_list: I/O ranges + * @handler: I/O request handler for this client + * @thread: Kernel thread executing the handler + * @wq: Wait queue used for thread parking + * @priv: Private data for the handler + */ +struct bao_io_client { + char name[BAO_NAME_MAX_LEN]; + struct bao_dm *dm; + struct list_head list; + bool is_control; + unsigned long flags; + struct list_head virtio_requests; + + /* protects virtio_requests list */ + struct mutex virtio_requests_lock; + + struct list_head range_list; + + /* protects range_list */ + struct rw_semaphore range_lock; + + bao_io_client_handler_t handler; + struct task_struct *thread; + wait_queue_head_t wq; + void *priv; +}; + +/** + * struct bao_dm - Bao backend device model (DM) + * @list: Entry within global list of all DMs + * @info: DM information (id, shmem_addr, shmem_size, irq, fd) + * @shmem_base_addr: The base address of the shared memory + * @ioeventfds: List of all ioeventfds + * @ioeventfd_client: Ioeventfd client + * @irqfds: List of all irqfds + * @irqfd_server: Workqueue responsible for irqfd handling + * @io_clients: List of all bao_io_client + * @control_client: Control client + * @refcount: Each open file holds a reference to the DM + */ +struct bao_dm { + struct list_head list; + struct bao_dm_info info; + void *shmem_base_addr; + + struct list_head ioeventfds; + + /* protects ioeventfds list */ + struct mutex ioeventfds_lock; + + struct bao_io_client *ioeventfd_client; + + struct list_head irqfds; + + /* protects irqfds list */ + struct mutex irqfds_lock; + + struct workqueue_struct *irqfd_server; + + /* protects io_clients list */ + struct rw_semaphore io_clients_lock; + + struct list_head io_clients; + struct bao_io_client *control_client; + + refcount_t refcount; +}; + +/** + * struct bao_io_range - Represents a range of I/O addresses + * @list: List node for linking multiple ranges + * @start: Start address of the range + * @end: End address of the range (inclusive) + */ +struct bao_io_range { + struct list_head list; + u64 start; + u64 end; +}; + +/* Global list of all Bao device models */ +extern struct list_head bao_dm_list; + +/* Lock protecting access to bao_dm_list */ +extern rwlock_t bao_dm_list_lock; + +/** + * bao_dm_create - Create a backend device model (DM) + * @info: DM information (id, shmem_addr, shmem_size, irq, fd) + * + * Return: Pointer to the created DM on success, NULL on error. + */ +struct bao_dm *bao_dm_create(struct bao_dm_info *info); + +/** + * bao_dm_destroy - Destroy a backend device model (DM) + * @dm: DM to be destroyed + */ +void bao_dm_destroy(struct bao_dm *dm); + +/** + * bao_dm_get_info - Retrieve information of a DM + * @info: Structure to be filled; id field must contain the DM ID + * + * Return: True on success, false on error. + */ +bool bao_dm_get_info(struct bao_dm_info *info); + +/** + * bao_io_client_create - Create a backend I/O client + * @dm: DM this client belongs to + * @handler: I/O client handler for requests + * @data: Private data passed to the handler + * @is_control: True if this is the control client + * @name: Name of the I/O client + * + * Return: Pointer to the created I/O client, NULL on failure. + */ +struct bao_io_client *bao_io_client_create(struct bao_dm *dm, + bao_io_client_handler_t handler, + void *data, bool is_control, + const char *name); + +/** + * bao_io_clients_destroy - Destroy all I/O clients of a DM + * @dm: DM whose I/O clients are to be destroyed + */ +void bao_io_clients_destroy(struct bao_dm *dm); + +/** + * bao_io_client_attach - Attach a thread to an I/O client + * @client: I/O client to attach + * + * The thread will wait for I/O requests on this client. + * + * Return: 0 on success, negative error code on failure. + */ +int bao_io_client_attach(struct bao_io_client *client); + +/** + * bao_io_client_range_add - Add an I/O range to monitor in a client + * @client: I/O client + * @start: Start address of the range + * @end: End address of the range (inclusive) + * + * Return: 0 on success, negative error code on failure. + */ +int bao_io_client_range_add(struct bao_io_client *client, u64 start, u64 end); + +/** + * bao_io_client_range_del - Remove an I/O range from a client + * @client: I/O client + * @start: Start address of the range + * @end: End address of the range (inclusive) + */ +void bao_io_client_range_del(struct bao_io_client *client, u64 start, u64 end); + +/** + * bao_io_client_request - Retrieve the oldest I/O request from a client + * @client: I/O client + * @req: Pointer to virtio request structure to fill + * + * Return: 0 on success, negative error code if no request is available. + */ +int bao_io_client_request(struct bao_io_client *client, + struct bao_virtio_request *req); + +/** + * bao_io_client_push_request - Push an I/O request into a client + * @client: I/O client + * @req: I/O request to push + * + * Return: True if a request was pushed, false otherwise. + */ +bool bao_io_client_push_request(struct bao_io_client *client, + struct bao_virtio_request *req); + +/** + * bao_io_client_pop_request - Pop the oldest I/O request from a client + * @client: I/O client + * @req: Buffer to store the popped request + * + * Return: True if a request was popped, false if the list was empty. + */ +bool bao_io_client_pop_request(struct bao_io_client *client, + struct bao_virtio_request *req); + +/** + * bao_io_client_find - Find the I/O client for a given request + * @dm: DM that the I/O request belongs to + * @req: I/O request to locate + * + * Return: Pointer to the I/O client handling the request, NULL if none found. + */ +struct bao_io_client *bao_io_client_find(struct bao_dm *dm, + struct bao_virtio_request *req); + +/** + * bao_ioeventfd_client_init - Initialize the Ioeventfd client for a DM + * @dm: DM that the Ioeventfd client belongs to + * + * Return: 0 on success, negative error code on failure. + */ +int bao_ioeventfd_client_init(struct bao_dm *dm); + +/** + * bao_ioeventfd_client_destroy - Destroy the Ioeventfd client for a DM + * @dm: DM that the Ioeventfd client belongs to + */ +void bao_ioeventfd_client_destroy(struct bao_dm *dm); + +/** + * bao_ioeventfd_client_config - Configure an Ioeventfd client + * @dm: DM that the Ioeventfd client belongs to + * @config: Ioeventfd configuration to apply + * + * Return: 0 on success, negative error code on failure. + */ +int bao_ioeventfd_client_config(struct bao_dm *dm, + struct bao_ioeventfd *config); + +/** + * bao_irqfd_server_init - Initialize the Irqfd server for a DM + * @dm: DM that the Irqfd server belongs to + * + * Return: 0 on success, negative error code on failure. + */ +int bao_irqfd_server_init(struct bao_dm *dm); + +/** + * bao_irqfd_server_destroy - Destroy the Irqfd server for a DM + * @dm: DM that the Irqfd server belongs to + */ +void bao_irqfd_server_destroy(struct bao_dm *dm); + +/** + * bao_irqfd_server_config - Configure an Irqfd server + * @dm: DM that the Irqfd server belongs to + * @config: Irqfd configuration to apply + * + * Return: 0 on success, negative error code on failure. + */ +int bao_irqfd_server_config(struct bao_dm *dm, struct bao_irqfd *config); + +/** + * bao_io_dispatcher_init - Initialize the I/O Dispatcher for a DM + * @dm: DM to initialize on the I/O Dispatcher + * + * Return: 0 on success, negative error code on failure. + */ +int bao_io_dispatcher_init(struct bao_dm *dm); + +/** + * bao_io_dispatcher_destroy - Destroy the I/O Dispatcher for a DM + * @dm: DM to destroy on the I/O Dispatcher + */ +void bao_io_dispatcher_destroy(struct bao_dm *dm); + +/** + * bao_dispatch_io - Acquire and dispatch I/O requests from the Bao Hypervisor + * @dm: DM whose I/O clients will handle the requests + * + * Return: 0 on success, negative error code on failure. + */ +int bao_dispatch_io(struct bao_dm *dm); + +/** + * bao_io_dispatcher_pause - Pause the I/O Dispatcher for a DM + * @dm: DM to pause + */ +void bao_io_dispatcher_pause(struct bao_dm *dm); + +/** + * bao_io_dispatcher_resume - Resume the I/O Dispatcher for a DM + * @dm: DM to resume + */ +void bao_io_dispatcher_resume(struct bao_dm *dm); + +/** + * bao_intc_init - Register the interrupt controller for a DM + * @dm: DM that the interrupt controller belongs to + * + * Return: 0 on success, negative error code on failure. + */ +int bao_intc_init(struct bao_dm *dm); + +/** + * bao_intc_destroy - Unregister the interrupt controller for a DM + * @dm: DM that the interrupt controller belongs to + */ +void bao_intc_destroy(struct bao_dm *dm); + +/** + * bao_intc_setup_handler - Setup the interrupt controller handler + * @handler: Function pointer to the interrupt handler + * @dm: DM that the interrupt controller belongs to + */ +void bao_intc_setup_handler(void (*handler)(struct bao_dm *dm)); + +/** + * bao_intc_remove_handler - Remove the interrupt controller handler + */ +void bao_intc_remove_handler(void); + +#endif /* __BAO_DRV_H */ diff --git a/drivers/virt/bao/io-dispatcher/dm.c b/drivers/virt/bao/io-dispatcher/dm.c new file mode 100644 index 000000000000..12c321fc8a1c --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/dm.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor Backend Device Model (DM) + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include "bao_drv.h" +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/anon_inodes.h> +#include <linux/file.h> +#include <asm/bao.h> + +/* + * List of all backend device models (DMs) + */ +LIST_HEAD(bao_dm_list); + +/* + * Lock to protect bao_dm_list + */ +DEFINE_RWLOCK(bao_dm_list_lock); + +static void bao_dm_get(struct bao_dm *dm) +{ + refcount_inc(&dm->refcount); +} + +static void bao_dm_put(struct bao_dm *dm) +{ + if (refcount_dec_and_test(&dm->refcount)) + kfree(dm); +} + +static int bao_dm_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int bao_dm_release(struct inode *inode, struct file *filp) +{ + struct bao_dm *dm = filp->private_data; + + if (WARN_ON_ONCE(!dm)) + return -ENODEV; + + filp->private_data = NULL; + bao_dm_put(dm); + + return 0; +} + +static long bao_dm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct bao_dm *dm = filp->private_data; + int rc; + + if (WARN_ON_ONCE(!dm)) + return -ENODEV; + + switch (cmd) { + case BAO_IOCTL_IO_CLIENT_ATTACH: { + struct bao_virtio_request *req; + + req = memdup_user((void __user *)arg, sizeof(*req)); + if (IS_ERR(req)) { + rc = PTR_ERR(req); + break; + } + + if (!dm->control_client) { + rc = -ENOENT; + goto out_free; + } + + rc = bao_io_client_attach(dm->control_client); + if (rc) + goto out_free; + + rc = bao_io_client_request(dm->control_client, req); + if (rc) + goto out_free; + + if (copy_to_user((void __user *)arg, req, sizeof(*req))) { + rc = -EFAULT; + goto out_free; + } + + rc = 0; + +out_free: + kfree(req); + break; + } + case BAO_IOCTL_IO_REQUEST_COMPLETE: { + struct bao_virtio_request *req; + struct bao_remio_hypercall_ctx ctx; + + req = memdup_user((void __user *)arg, sizeof(*req)); + if (IS_ERR(req)) { + rc = PTR_ERR(req); + break; + } + + ctx.dm_id = req->dm_id; + ctx.addr = req->addr; + ctx.op = req->op; + ctx.value = req->value; + ctx.access_width = req->access_width; + ctx.request_id = req->request_id; + + rc = bao_remio_hypercall(&ctx); + kfree(req); + + break; + } + case BAO_IOCTL_IOEVENTFD: { + struct bao_ioeventfd ioeventfd; + + if (copy_from_user(&ioeventfd, (void __user *)arg, + sizeof(struct bao_ioeventfd))) + return -EFAULT; + + rc = bao_ioeventfd_client_config(dm, &ioeventfd); + break; + } + case BAO_IOCTL_IRQFD: { + struct bao_irqfd irqfd; + + if (copy_from_user(&irqfd, (void __user *)arg, + sizeof(struct bao_irqfd))) + return -EFAULT; + + rc = bao_irqfd_server_config(dm, &irqfd); + break; + } + default: + rc = -ENOTTY; + break; + } + + return rc; +} + +/** + * bao_dm_mmap - mmap backend DM shared memory to userspace + * @filp: File pointer for the DM device + * @vma: Virtual memory area for mapping + * + * Return: 0 on success, negative errno on failure + */ +static int bao_dm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct bao_dm *dm = filp->private_data; + unsigned long vsize; + unsigned long offset; + phys_addr_t phys; + + if (WARN_ON_ONCE(!dm)) + return -ENODEV; + + vsize = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + if (!vsize || offset) + return -EINVAL; + + if (vsize > dm->info.shmem_size) + return -EINVAL; + + phys = dm->info.shmem_addr; + if (!PAGE_ALIGNED(phys)) + return -EINVAL; + + if (remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT, vsize, + vma->vm_page_prot)) + return -EFAULT; + + return 0; +} + +/** + * bao_dm_llseek - Adjust file offset for backend DM device + * @file: File pointer for the DM device + * @offset: Offset to seek + * @whence: Reference point (SEEK_SET, SEEK_CUR, SEEK_END) + * + * Return: New file position on success, negative errno on failure + */ +static loff_t bao_dm_llseek(struct file *file, loff_t offset, int whence) +{ + struct bao_dm *bao = file->private_data; + loff_t new_pos; + + if (WARN_ON_ONCE(!bao)) + return -ENODEV; + + switch (whence) { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = file->f_pos + offset; + break; + case SEEK_END: + new_pos = bao->info.shmem_size + offset; + break; + default: + return -EINVAL; + } + + if (new_pos < 0 || new_pos > bao->info.shmem_size) + return -EINVAL; + + file->f_pos = new_pos; + return new_pos; +} + +static const struct file_operations bao_dm_fops = { + .owner = THIS_MODULE, + .open = bao_dm_open, + .release = bao_dm_release, + .unlocked_ioctl = bao_dm_ioctl, + .llseek = bao_dm_llseek, + .mmap = bao_dm_mmap, +}; + +struct bao_dm *bao_dm_create(struct bao_dm_info *info) +{ + struct bao_dm *dm; + struct bao_dm *tmp; + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON(!info)) + return NULL; + + dm = kzalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return NULL; + + INIT_LIST_HEAD(&dm->list); + INIT_LIST_HEAD(&dm->io_clients); + init_rwsem(&dm->io_clients_lock); + + refcount_set(&dm->refcount, 1); + dm->info = *info; + + bao_io_dispatcher_init(dm); + + snprintf(name, sizeof(name), "bao-ioctlc%u", dm->info.id); + dm->control_client = bao_io_client_create(dm, NULL, NULL, true, name); + if (!dm->control_client) { + pr_err("%s: failed to create control client for DM %u\n", + __func__, dm->info.id); + goto err_remove_dm; + } + + if (bao_ioeventfd_client_init(dm)) { + pr_err("%s: failed to initialize ioeventfd for DM %u\n", + __func__, dm->info.id); + goto err_destroy_io_clients; + } + + if (bao_irqfd_server_init(dm)) { + pr_err("%s: failed to initialize irqfd for DM %u\n", __func__, + dm->info.id); + goto err_destroy_io_clients; + } + + dm->shmem_base_addr = + memremap(dm->info.shmem_addr, dm->info.shmem_size, MEMREMAP_WB); + if (!dm->shmem_base_addr) { + pr_err("%s: failed to map memory region for DM %u\n", __func__, + dm->info.id); + goto err_destroy_irqfd; + } + + write_lock(&bao_dm_list_lock); + list_for_each_entry(tmp, &bao_dm_list, list) { + if (tmp->info.id == info->id) { + write_unlock(&bao_dm_list_lock); + goto err_unmap; + } + } + list_add(&dm->list, &bao_dm_list); + write_unlock(&bao_dm_list_lock); + + return dm; + +err_unmap: + memunmap(dm->shmem_base_addr); + +err_destroy_irqfd: + bao_irqfd_server_destroy(dm); + +err_destroy_io_clients: + bao_io_clients_destroy(dm); + +err_remove_dm: + kfree(dm); + + return NULL; +} + +void bao_dm_destroy(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm)) + return; + + write_lock(&bao_dm_list_lock); + list_del_init(&dm->list); + write_unlock(&bao_dm_list_lock); + + dm->info.id = 0; + dm->info.shmem_addr = 0; + dm->info.shmem_size = 0; + dm->info.irq = 0; + + if (dm->shmem_base_addr) + memunmap(dm->shmem_base_addr); + + if (dm->info.fd >= 0) + put_unused_fd(dm->info.fd); + + bao_irqfd_server_destroy(dm); + bao_io_clients_destroy(dm); + bao_io_dispatcher_destroy(dm); + + bao_dm_put(dm); +} + +/** + * bao_dm_create_anonymous_inode - Create an anonymous inode for a backend DM + * @dm: The backend device model (DM) + * + * Creates an anonymous inode that exposes the backend DM to userspace. + * The frontend DM can use the returned file descriptor to request + * services from the backend DM directly. + * + * Return: File descriptor on success, negative errno on failure + */ +static int bao_dm_create_anonymous_inode(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + struct file *file; + int fd; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return fd; + + snprintf(name, sizeof(name), "bao-dm%u", dm->info.id); + bao_dm_get(dm); + file = anon_inode_getfile(name, &bao_dm_fops, dm, O_RDWR); + if (IS_ERR(file)) { + bao_dm_put(dm); + put_unused_fd(fd); + return PTR_ERR(file); + } + + fd_install(fd, file); + dm->info.fd = fd; + + return fd; +} + +bool bao_dm_get_info(struct bao_dm_info *info) +{ + struct bao_dm *dm; + bool found = false; + + if (WARN_ON_ONCE(!info)) + return false; + + read_lock(&bao_dm_list_lock); + list_for_each_entry(dm, &bao_dm_list, list) { + if (dm->info.id == info->id) { + bao_dm_get(dm); + found = true; + break; + } + } + read_unlock(&bao_dm_list_lock); + + if (!found) + return false; + + info->shmem_addr = dm->info.shmem_addr; + info->shmem_size = dm->info.shmem_size; + info->irq = dm->info.irq; + info->fd = bao_dm_create_anonymous_inode(dm); + + bao_dm_put(dm); + + return true; +} diff --git a/drivers/virt/bao/io-dispatcher/driver.c b/drivers/virt/bao/io-dispatcher/driver.c new file mode 100644 index 000000000000..488f9fb6e5f5 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/driver.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Dispatcher Kernel Driver + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#include <linux/platform_device.h> +#include <linux/of_irq.h> +#include <linux/miscdevice.h> +#include "bao_drv.h" + +struct bao_iodispatcher_drv { + struct miscdevice miscdev; +}; + +static int bao_io_dispatcher_driver_open(struct inode *inode, struct file *filp) +{ + struct miscdevice *misc = filp->private_data; + struct bao_iodispatcher_drv *drv; + + drv = container_of(misc, struct bao_iodispatcher_drv, + miscdev); + filp->private_data = drv; + + return 0; +} + +static int bao_io_dispatcher_driver_release(struct inode *inode, + struct file *filp) +{ + filp->private_data = NULL; + return 0; +} + +static long bao_io_dispatcher_driver_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct bao_dm_info *info; + + switch (cmd) { + case BAO_IOCTL_DM_GET_INFO: + info = memdup_user((void __user *)arg, sizeof(*info)); + if (IS_ERR(info)) + return PTR_ERR(info); + + if (!bao_dm_get_info(info)) { + kfree(info); + return -ENOENT; + } + + if (copy_to_user((void __user *)arg, info, sizeof(*info))) { + kfree(info); + return -EFAULT; + } + + kfree(info); + return 0; + + default: + return -ENOTTY; + } +} + +static const struct file_operations bao_io_dispatcher_driver_fops = { + .owner = THIS_MODULE, + .open = bao_io_dispatcher_driver_open, + .release = bao_io_dispatcher_driver_release, + .unlocked_ioctl = bao_io_dispatcher_driver_ioctl, +}; + +static int bao_io_dispatcher_driver_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bao_iodispatcher_drv *drv; + struct bao_dm *dm; + struct bao_dm_info dm_info; + struct resource *r; + int ret; + int irq; + int i; + resource_size_t reg_size; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + for (i = 0; i < BAO_IO_MAX_DMS; i++) { + r = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!r) + break; + + irq = platform_get_irq(pdev, i); + if (irq < 0) { + dev_err(dev, "failed to get IRQ at index %d\n", i); + ret = irq; + goto err_unregister_dms; + } + + reg_size = resource_size(r); + + dm_info.id = i; + dm_info.shmem_addr = (unsigned long)r->start; + dm_info.shmem_size = (unsigned long)reg_size; + dm_info.irq = irq; + dm_info.fd = 0; + + dm = bao_dm_create(&dm_info); + if (!dm) { + dev_err(dev, "failed to create Bao DM %d\n", i); + ret = -EINVAL; + goto err_unregister_dms; + } + + ret = bao_intc_init(dm); + if (ret) { + dev_err(dev, "failed to register interrupt %d\n", irq); + goto err_unregister_dms; + } + } + + drv->miscdev.minor = MISC_DYNAMIC_MINOR; + drv->miscdev.name = "bao-io-dispatcher"; + drv->miscdev.fops = &bao_io_dispatcher_driver_fops; + drv->miscdev.parent = dev; + + ret = misc_register(&drv->miscdev); + if (ret) { + dev_err(dev, "failed to register misc device: %d\n", ret); + goto err_unregister_irqs; + } + + platform_set_drvdata(pdev, drv); + + dev_info(dev, "Bao I/O dispatcher device registered\n"); + return 0; + +err_unregister_irqs: + list_for_each_entry(dm, &bao_dm_list, list) + bao_intc_destroy(dm); + +err_unregister_dms: + list_for_each_entry(dm, &bao_dm_list, list) + bao_dm_destroy(dm); + + return ret; +} + +static void bao_io_dispatcher_driver_remove(struct platform_device *pdev) +{ + struct bao_iodispatcher_drv *drv = platform_get_drvdata(pdev); + struct bao_dm *dm; + struct bao_dm *tmp; + + if (drv) + misc_deregister(&drv->miscdev); + + list_for_each_entry_safe(dm, tmp, &bao_dm_list, list) { + bao_intc_destroy(dm); + bao_dm_destroy(dm); + } +} + +static const struct of_device_id bao_io_dispatcher_driver_dt_ids[] = { + { .compatible = "bao,io-dispatcher" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bao_io_dispatcher_driver_dt_ids); + +static struct platform_driver bao_io_dispatcher_driver = { + .probe = bao_io_dispatcher_driver_probe, + .remove = bao_io_dispatcher_driver_remove, + .driver = { + .name = "bao-io-dispatcher", + .of_match_table = bao_io_dispatcher_driver_dt_ids, + }, +}; + +module_platform_driver(bao_io_dispatcher_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("João Peixoto <joaopeixoto@osyx.tech>"); +MODULE_AUTHOR("David Cerdeira <davidmcerdeira@osyx.tech>"); +MODULE_AUTHOR("José Martins <jose@osyx.tech>"); +MODULE_DESCRIPTION("Bao Hypervisor I/O Dispatcher Kernel Driver"); diff --git a/drivers/virt/bao/io-dispatcher/intc.c b/drivers/virt/bao/io-dispatcher/intc.c new file mode 100644 index 000000000000..40d8b6c1bd25 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/intc.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Dispatcher Interrupt Controller + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/interrupt.h> +#include "bao_drv.h" + +/* Top-level handler registered by the Bao interrupt controller */ +static void (*bao_intc_handler)(struct bao_dm *dm); + +/** + * bao_interrupt_handler - Top-level interrupt handler for Bao DM + * @irq: Interrupt number + * @dev: Pointer to the Bao device model (struct bao_dm) + * + * Invokes the registered Bao interrupt controller handler, if any. + */ +static irqreturn_t bao_interrupt_handler(int irq, void *dev) +{ + struct bao_dm *dm = (struct bao_dm *)dev; + + if (bao_intc_handler) + bao_intc_handler(dm); + + return IRQ_HANDLED; +} + +void bao_intc_setup_handler(void (*handler)(struct bao_dm *dm)) +{ + bao_intc_handler = handler; +} + +void bao_intc_remove_handler(void) +{ + bao_intc_handler = NULL; +} + +int bao_intc_init(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + scnprintf(name, sizeof(name), "bao-iodintc%d", dm->info.id); + + return request_irq(dm->info.irq, bao_interrupt_handler, 0, name, dm); +} + +void bao_intc_destroy(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm)) + return; + + free_irq(dm->info.irq, dm); +} diff --git a/drivers/virt/bao/io-dispatcher/io_client.c b/drivers/virt/bao/io-dispatcher/io_client.c new file mode 100644 index 000000000000..ae258d7bb9bd --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/io_client.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Client + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/kthread.h> +#include <asm/bao.h> +#include "bao_drv.h" + +/** + * struct bao_io_request - Bao I/O request structure + * @list: List node linking all requests + * @virtio_request: The VirtIO request payload + * + * Represents a single I/O request for a Bao I/O client. + */ +struct bao_io_request { + struct list_head list; + struct bao_virtio_request virtio_request; +}; + +/** + * bao_io_client_has_pending_requests - Check if an I/O client has pending requests + * @client: The bao_io_client to check + * + * Return: True if has pending I/O requests, false otherwise. + */ +static inline bool +bao_io_client_has_pending_requests(struct bao_io_client *client) +{ + if (WARN_ON_ONCE(!client)) + return false; + + return !list_empty(&client->virtio_requests); +} + +/** + * bao_io_client_is_destroying - Check if an I/O client is being destroyed + * @client: The bao_io_client to check + * + * Return: True if the client is being destroyed, false otherwise. + */ +static inline bool bao_io_client_is_destroying(struct bao_io_client *client) +{ + if (WARN_ON_ONCE(!client)) + return true; + + return test_bit(BAO_IO_CLIENT_DESTROYING, &client->flags); +} + +bool bao_io_client_push_request(struct bao_io_client *client, + struct bao_virtio_request *req) +{ + struct bao_io_request *io_req; + + if (WARN_ON_ONCE(!client || !req)) + return false; + + io_req = kzalloc(sizeof(*io_req), GFP_KERNEL); + if (!io_req) + return false; + + io_req->virtio_request = *req; + + mutex_lock(&client->virtio_requests_lock); + list_add_tail(&io_req->list, &client->virtio_requests); + mutex_unlock(&client->virtio_requests_lock); + + return true; +} + +bool bao_io_client_pop_request(struct bao_io_client *client, + struct bao_virtio_request *ret) +{ + struct bao_io_request *req; + + if (WARN_ON_ONCE(!client || !ret)) + return false; + + mutex_lock(&client->virtio_requests_lock); + + req = list_first_entry_or_null(&client->virtio_requests, + struct bao_io_request, list); + if (!req) { + mutex_unlock(&client->virtio_requests_lock); + return false; + } + + list_del(&req->list); + *ret = req->virtio_request; + + mutex_unlock(&client->virtio_requests_lock); + + kfree(req); + + return true; +} + +/** + * bao_io_client_destroy - Destroy an I/O client + * @client: The bao_io_client to destroy + */ +static void bao_io_client_destroy(struct bao_io_client *client) +{ + struct bao_io_client *range; + struct bao_io_client *next; + struct bao_dm *dm; + + if (WARN_ON_ONCE(!client)) + return; + + dm = client->dm; + + bao_io_dispatcher_pause(dm); + + set_bit(BAO_IO_CLIENT_DESTROYING, &client->flags); + + if (client->is_control) { + wake_up_interruptible(&client->wq); + } else { + bao_ioeventfd_client_destroy(dm); + if (client->thread) + kthread_stop(client->thread); + } + + down_write(&client->range_lock); + list_for_each_entry_safe(range, next, &client->range_list, list) { + list_del(&range->list); + kfree(range); + } + up_write(&client->range_lock); + + down_write(&dm->io_clients_lock); + if (client->is_control) + dm->control_client = NULL; + else + dm->ioeventfd_client = NULL; + + list_del(&client->list); + up_write(&dm->io_clients_lock); + + bao_io_dispatcher_resume(dm); + + kfree(client); +} + +void bao_io_clients_destroy(struct bao_dm *dm) +{ + struct bao_io_client *client, *next; + + if (WARN_ON_ONCE(!dm)) + return; + + list_for_each_entry_safe(client, next, &dm->io_clients, list) { + bao_io_client_destroy(client); + } +} + +int bao_io_client_attach(struct bao_io_client *client) +{ + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + if (client->is_control) { + wait_event_interruptible(client->wq, + bao_io_client_has_pending_requests(client) || + bao_io_client_is_destroying(client)); + if (bao_io_client_is_destroying(client)) + return -EPERM; + } else { + wait_event_interruptible(client->wq, + bao_io_client_has_pending_requests(client) || + bao_io_client_is_destroying(client) || + kthread_should_stop()); + if (bao_io_client_is_destroying(client) || + kthread_should_stop()) { + if (kthread_should_stop()) + bao_io_client_destroy(client); + return -EPERM; + } + } + + return 0; +} + +/** + * bao_io_client_kernel_thread - Thread for processing a kernel I/O client + * @data: Pointer to the bao_io_client structure + * + * Return: 0 on completion + */ +static int bao_io_client_kernel_thread(void *data) +{ + struct bao_io_client *client = data; + struct bao_virtio_request req; + struct bao_remio_hypercall_ctx ctx; + bool stop = false; + int ret; + + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + while (!stop && !kthread_should_stop()) { + ret = bao_io_client_attach(client); + if (ret < 0) { + stop = true; + break; + } + + while (bao_io_client_has_pending_requests(client) && !stop) { + if (!bao_io_client_pop_request(client, &req)) { + pr_err("%s: failed to pop I/O request\n", + __func__); + stop = true; + break; + } + + ret = client->handler(client, &req); + if (ret < 0) { + pr_warn("%s: client handler returned %d\n", + __func__, ret); + break; + } + + ctx.dm_id = req.dm_id; + ctx.op = req.op; + ctx.addr = req.addr; + ctx.value = req.value; + ctx.access_width = req.access_width; + ctx.request_id = req.request_id; + + if (bao_remio_hypercall(&ctx)) { + stop = true; + break; + } + } + } + + return 0; +} + +struct bao_io_client *bao_io_client_create(struct bao_dm *dm, + bao_io_client_handler_t handler, + void *data, bool is_control, + const char *name) +{ + struct bao_io_client *client; + + if (WARN_ON_ONCE(!dm || !name)) + return NULL; + + if (!handler && !is_control) + return NULL; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + client->handler = handler; + client->dm = dm; + client->priv = data; + client->is_control = is_control; + if (name) + strscpy(client->name, name, sizeof(client->name)); + + INIT_LIST_HEAD(&client->virtio_requests); + init_rwsem(&client->range_lock); + INIT_LIST_HEAD(&client->range_list); + init_waitqueue_head(&client->wq); + + if (client->handler) { + client->thread = kthread_run(bao_io_client_kernel_thread, + client, "%s-kthread", + client->name); + if (IS_ERR(client->thread)) { + kfree(client); + return NULL; + } + } + + down_write(&dm->io_clients_lock); + if (is_control) + dm->control_client = client; + else + dm->ioeventfd_client = client; + + list_add(&client->list, &dm->io_clients); + up_write(&dm->io_clients_lock); + + if (is_control) { + while (bao_dispatch_io(dm) > 0) + ; + } + + return client; +} + +int bao_io_client_request(struct bao_io_client *client, + struct bao_virtio_request *req) +{ + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + if (!bao_io_client_pop_request(client, req)) + return -EFAULT; + + return 0; +} + +int bao_io_client_range_add(struct bao_io_client *client, u64 start, u64 end) +{ + struct bao_io_range *range; + + if (WARN_ON_ONCE(!client)) + return -EINVAL; + + if (end < start) + return -EINVAL; + + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + + range->start = start; + range->end = end; + + down_write(&client->range_lock); + list_add(&range->list, &client->range_list); + up_write(&client->range_lock); + + return 0; +} + +void bao_io_client_range_del(struct bao_io_client *client, u64 start, u64 end) +{ + struct bao_io_range *range; + struct bao_io_range *tmp; + + if (WARN_ON_ONCE(!client)) + return; + + down_write(&client->range_lock); + list_for_each_entry_safe(range, tmp, &client->range_list, list) { + if (range->start == start && range->end == end) { + list_del(&range->list); + kfree(range); + break; + } + } + up_write(&client->range_lock); +} + +/** + * bao_io_request_in_range - Check if the I/O request is in the range + * @range: The I/O request range + * @req: The I/O request to be checked + * + * Return: True if the I/O request is in the range, false otherwise + */ +static bool bao_io_request_in_range(struct bao_io_range *range, + struct bao_virtio_request *req) +{ + if (WARN_ON_ONCE(!range || !req)) + return false; + + if (req->addr >= range->start && + (req->addr + req->access_width - 1) <= range->end) + return true; + + return false; +} + +struct bao_io_client *bao_io_client_find(struct bao_dm *dm, + struct bao_virtio_request *req) +{ + struct bao_io_client *client; + struct bao_io_client *found = NULL; + struct bao_io_range *range; + + if (WARN_ON_ONCE(!dm || !req)) + return NULL; + + list_for_each_entry(client, &dm->io_clients, list) { + down_read(&client->range_lock); + list_for_each_entry(range, &client->range_list, list) { + if (bao_io_request_in_range(range, req)) { + found = client; + break; + } + } + up_read(&client->range_lock); + + if (found) + break; + } + + return found ? found : dm->control_client; +} diff --git a/drivers/virt/bao/io-dispatcher/io_dispatcher.c b/drivers/virt/bao/io-dispatcher/io_dispatcher.c new file mode 100644 index 000000000000..15f6b8c6a439 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/io_dispatcher.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor I/O Dispatcher + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <asm/bao.h> +#include "bao_drv.h" + +/** + * struct bao_io_dispatcher_work - Work item for I/O dispatching + * @work: Work struct for scheduling on workqueue + * @dm: Pointer to the associated Bao device model + * + * Represents a single work item that dispatches I/O requests + * for a specific Bao device model. + */ +struct bao_io_dispatcher_work { + struct work_struct work; + struct bao_dm *dm; +}; + +/* Array of I/O dispatcher work items, one per Bao DM */ +static struct bao_io_dispatcher_work io_dispatcher_work[BAO_IO_MAX_DMS]; + +/* Workqueues dedicated to dispatching I/O requests for each Bao DM */ +static struct workqueue_struct *bao_io_dispatcher_wq[BAO_IO_MAX_DMS]; + +void bao_io_dispatcher_destroy(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm)) + return; + + if (bao_io_dispatcher_wq[dm->info.id]) { + bao_io_dispatcher_pause(dm); + + destroy_workqueue(bao_io_dispatcher_wq[dm->info.id]); + bao_io_dispatcher_wq[dm->info.id] = NULL; + + bao_intc_remove_handler(); + } +} + +int bao_dispatch_io(struct bao_dm *dm) +{ + struct bao_io_client *client; + struct bao_remio_hypercall_ctx ctx; + struct bao_virtio_request req; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + ctx.dm_id = dm->info.id; + ctx.op = BAO_IO_ASK; + ctx.addr = 0; + ctx.value = 0; + ctx.request_id = 0; + + if (bao_remio_hypercall(&ctx)) + return -EFAULT; + + req.dm_id = ctx.dm_id; + req.op = ctx.op; + req.addr = ctx.addr; + req.value = ctx.value; + req.access_width = ctx.access_width; + req.request_id = ctx.request_id; + + down_read(&dm->io_clients_lock); + client = bao_io_client_find(dm, &req); + if (!client) { + up_read(&dm->io_clients_lock); + return -ENODEV; + } + + if (!bao_io_client_push_request(client, &req)) { + up_read(&dm->io_clients_lock); + return -EINVAL; + } + + wake_up_interruptible(&client->wq); + up_read(&dm->io_clients_lock); + + return ctx.npend_req; +} + +/** + * io_dispatcher - Workqueue handler for dispatching I/O + * @work: Work struct representing this dispatch operation + * + * Handles all pending I/O requests for the associated Bao DM. + * Executed in process context by the workqueue. + */ +static void io_dispatcher(struct work_struct *work) +{ + struct bao_io_dispatcher_work *bao_dm_work; + struct bao_dm *dm; + + if (WARN_ON_ONCE(!work)) + return; + + bao_dm_work = container_of(work, struct bao_io_dispatcher_work, work); + dm = bao_dm_work->dm; + + if (WARN_ON_ONCE(!dm)) + return; + + while (bao_dispatch_io(dm) > 0) + cpu_relax(); +} + +/** + * io_dispatcher_intc_handler - Interrupt handler for I/O requests + * @dm: Bao device model that triggered the interrupt + * + * Invoked by the interrupt controller when a new I/O request is available. + * Queues the corresponding work item onto the I/O dispatcher workqueue + * for processing in process context. + */ +static void io_dispatcher_intc_handler(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm || !bao_io_dispatcher_wq[dm->info.id])) + return; + + queue_work(bao_io_dispatcher_wq[dm->info.id], + &io_dispatcher_work[dm->info.id].work); +} + +void bao_io_dispatcher_pause(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm || !bao_io_dispatcher_wq[dm->info.id])) + return; + + bao_intc_remove_handler(); + + drain_workqueue(bao_io_dispatcher_wq[dm->info.id]); +} + +void bao_io_dispatcher_resume(struct bao_dm *dm) +{ + if (WARN_ON_ONCE(!dm || !bao_io_dispatcher_wq[dm->info.id])) + return; + + bao_intc_setup_handler(io_dispatcher_intc_handler); + + queue_work(bao_io_dispatcher_wq[dm->info.id], + &io_dispatcher_work[dm->info.id].work); +} + +int bao_io_dispatcher_init(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + snprintf(name, sizeof(name), "bao-iodwq%u", dm->info.id); + + if (bao_io_dispatcher_wq[dm->info.id]) + return -EBUSY; + bao_io_dispatcher_wq[dm->info.id] = + alloc_workqueue(name, WQ_HIGHPRI | WQ_MEM_RECLAIM, 1); + if (!bao_io_dispatcher_wq[dm->info.id]) + return -ENOMEM; + + io_dispatcher_work[dm->info.id].dm = dm; + INIT_WORK(&io_dispatcher_work[dm->info.id].work, io_dispatcher); + + bao_intc_setup_handler(io_dispatcher_intc_handler); + + return 0; +} + diff --git a/drivers/virt/bao/io-dispatcher/ioeventfd.c b/drivers/virt/bao/io-dispatcher/ioeventfd.c new file mode 100644 index 000000000000..28172207ab57 --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/ioeventfd.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor Ioeventfd Client + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/eventfd.h> +#include "bao_drv.h" + +/** + * struct ioeventfd - Properties of an I/O eventfd + * @list: List node linking this ioeventfd + * @eventfd: Associated eventfd context + * @addr: Start address of the I/O range + * @data: Data used for matching (if not wildcard) + * @length: Length of the I/O range + * @wildcard: True if data matching is not required + * + * Represents an I/O eventfd registered for a Bao device model. + */ +struct ioeventfd { + struct list_head list; + struct eventfd_ctx *eventfd; + u64 addr; + u64 data; + int length; + bool wildcard; +}; + +/** + * bao_ioeventfd_shutdown - Release and remove an ioeventfd + * @dm: Bao device model owning the ioeventfd + * @p: Ioeventfd to shut down + */ +static void bao_ioeventfd_shutdown(struct bao_dm *dm, struct ioeventfd *p) +{ + lockdep_assert_held(&dm->ioeventfds_lock); + + if (WARN_ON_ONCE(!p)) + return; + + eventfd_ctx_put(p->eventfd); + list_del(&p->list); + kfree(p); +} + +/** + * bao_ioeventfd_config_valid - Validate ioeventfd configuration + * @config: Ioeventfd configuration + * + * Return: True if config is non-NULL, address+length does not wrap, + * and length is 1, 2, 4, or 8 bytes. + */ +static bool bao_ioeventfd_config_valid(struct bao_ioeventfd *config) +{ + if (WARN_ON_ONCE(!config)) + return false; + + if (config->addr + config->len < config->addr) + return false; + + if (!(config->len == 1 || config->len == 2 || config->len == 4 || + config->len == 8)) + return false; + + return true; +} + +/** + * bao_ioeventfd_is_conflict - Check if an ioeventfd conflicts with existing ones + * @dm: Bao device model + * @ioeventfd: Ioeventfd to check + * + * Return: True if an existing ioeventfd matches address, eventfd, + * and optionally data. + */ +static bool bao_ioeventfd_is_conflict(struct bao_dm *dm, + struct ioeventfd *ioeventfd) +{ + struct ioeventfd *p; + + lockdep_assert_held(&dm->ioeventfds_lock); + + if (WARN_ON_ONCE(!dm || !ioeventfd)) + return true; + + list_for_each_entry(p, &dm->ioeventfds, list) { + if (p->eventfd == ioeventfd->eventfd && + p->addr == ioeventfd->addr && + (p->wildcard || ioeventfd->wildcard || + p->data == ioeventfd->data)) { + return true; + } + } + + return false; +} + +/** + * bao_ioeventfd_match - Find ioeventfd matching an I/O request + * @dm: Bao device model + * @addr: I/O request address + * @data: I/O request data + * @len: I/O request length + * + * Return: The matching ioeventfd, NULL if none matches. + */ +static struct ioeventfd *bao_ioeventfd_match(struct bao_dm *dm, u64 addr, + u64 data, int len) +{ + struct ioeventfd *p; + + lockdep_assert_held(&dm->ioeventfds_lock); + + if (WARN_ON_ONCE(!dm)) + return NULL; + + list_for_each_entry(p, &dm->ioeventfds, list) { + if (p->addr == addr && p->length >= len && + (p->wildcard || p->data == data)) { + return p; + } + } + + return NULL; +} + +/** + * bao_ioeventfd_assign - Assign and create an eventfd for a DM + * @dm: Bao device model to assign the eventfd to + * @config: Configuration of the eventfd to create + * + * Creates a new ioeventfd associated with the given eventfd and + * adds it to the Bao DM. Validates the configuration, checks for + * conflicts with existing ioeventfds, and registers the corresponding + * I/O client address range. Supports optional data matching for + * virtio 1.0 notifications; if not set, wildcard matching is used. + * + * Return: 0 on success, a negative error code on failure + */ +static int bao_ioeventfd_assign(struct bao_dm *dm, struct bao_ioeventfd *config) +{ + struct eventfd_ctx *eventfd; + struct ioeventfd *new; + int rc = 0; + + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + if (!bao_ioeventfd_config_valid(config)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(config->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + rc = -ENOMEM; + goto err_put_eventfd; + } + + INIT_LIST_HEAD(&new->list); + new->addr = config->addr; + new->length = config->len; + new->eventfd = eventfd; + new->wildcard = !(config->flags & BAO_IOEVENTFD_FLAG_DATAMATCH); + if (!new->wildcard) + new->data = config->data; + + mutex_lock(&dm->ioeventfds_lock); + + if (bao_ioeventfd_is_conflict(dm, new)) { + rc = -EEXIST; + goto err_unlock_free; + } + + rc = bao_io_client_range_add(dm->ioeventfd_client, new->addr, + new->addr + new->length - 1); + if (rc < 0) + goto err_unlock_free; + + list_add_tail(&new->list, &dm->ioeventfds); + mutex_unlock(&dm->ioeventfds_lock); + + return 0; + +err_unlock_free: + mutex_unlock(&dm->ioeventfds_lock); + kfree(new); +err_put_eventfd: + eventfd_ctx_put(eventfd); + return rc; +} + +/** + * bao_ioeventfd_deassign - Deassign and destroy an eventfd from a DM + * @dm: Bao device model to deassign the eventfd from + * @config: Configuration of the eventfd to remove + * + * Return: 0 on success, a negative error code on failure + */ +static int bao_ioeventfd_deassign(struct bao_dm *dm, + struct bao_ioeventfd *config) +{ + struct ioeventfd *p; + struct eventfd_ctx *eventfd; + + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(config->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + mutex_lock(&dm->ioeventfds_lock); + + list_for_each_entry(p, &dm->ioeventfds, list) { + if (p->eventfd != eventfd) + continue; + + bao_io_client_range_del(dm->ioeventfd_client, p->addr, + p->addr + p->length - 1); + + bao_ioeventfd_shutdown(dm, p); + break; + } + + mutex_unlock(&dm->ioeventfds_lock); + eventfd_ctx_put(eventfd); + + return 0; +} + +/** + * bao_ioeventfd_handler - Handle an Ioeventfd client I/O request + * @client: Ioeventfd client associated with the request + * @req: I/O request to process + * + * Processes I/O requests from the Bao I/O client kernel thread + * (bao_io_client_kernel_thread). For READ operations, the value is + * ignored and set to 0 since virtio MMIO drivers only write to the + * `QueueNotify` field. WRITE operations are checked against the + * registered ioeventfds, and the corresponding eventfd is signaled + * if a match is found. + * + * Return: 0 on success, a negative error code on failure + */ +static int bao_ioeventfd_handler(struct bao_io_client *client, + struct bao_virtio_request *req) +{ + struct ioeventfd *p; + + if (WARN_ON_ONCE(!client || !req)) + return -EINVAL; + + if (req->op == BAO_IO_READ) { + req->value = 0; + return 0; + } + + mutex_lock(&client->dm->ioeventfds_lock); + + p = bao_ioeventfd_match(client->dm, req->addr, req->value, + req->access_width); + if (p) + eventfd_signal(p->eventfd); + + mutex_unlock(&client->dm->ioeventfds_lock); + + return 0; +} + +int bao_ioeventfd_client_config(struct bao_dm *dm, struct bao_ioeventfd *config) +{ + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + if (config->flags & BAO_IOEVENTFD_FLAG_DEASSIGN) + bao_ioeventfd_deassign(dm, config); + + return bao_ioeventfd_assign(dm, config); +} + +int bao_ioeventfd_client_init(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + mutex_init(&dm->ioeventfds_lock); + INIT_LIST_HEAD(&dm->ioeventfds); + + snprintf(name, sizeof(name), "bao-ioevfdc%u", dm->info.id); + + dm->ioeventfd_client = bao_io_client_create(dm, bao_ioeventfd_handler, + NULL, false, name); + if (!dm->ioeventfd_client) + return -ENOMEM; + + return 0; +} + +void bao_ioeventfd_client_destroy(struct bao_dm *dm) +{ + struct ioeventfd *p; + struct ioeventfd *next; + + if (WARN_ON_ONCE(!dm)) + return; + + mutex_lock(&dm->ioeventfds_lock); + list_for_each_entry_safe(p, next, &dm->ioeventfds, list) + bao_ioeventfd_shutdown(dm, p); + mutex_unlock(&dm->ioeventfds_lock); +} diff --git a/drivers/virt/bao/io-dispatcher/irqfd.c b/drivers/virt/bao/io-dispatcher/irqfd.c new file mode 100644 index 000000000000..ba31c3c5feaa --- /dev/null +++ b/drivers/virt/bao/io-dispatcher/irqfd.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bao Hypervisor Irqfd Server + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#include <linux/eventfd.h> +#include <linux/file.h> +#include <linux/poll.h> +#include <asm/bao.h> +#include "bao_drv.h" + +/** + * struct irqfd - Properties of an IRQ eventfd + * @dm: Associated Bao device model + * @wait: Wait queue entry for blocking/waking + * @shutdown: Work struct for async shutdown + * @eventfd: Eventfd used to signal interrupts + * @list: List node within &bao_dm.irqfds + * @pt: Poll table for select/poll on the eventfd + * + * Represents an IRQ eventfd registered to a Bao device model. + */ +struct irqfd { + struct bao_dm *dm; + wait_queue_entry_t wait; + struct work_struct shutdown; + struct eventfd_ctx *eventfd; + struct list_head list; + poll_table pt; +}; + +/** + * bao_irqfd_shutdown - Release and remove an irqfd + * @irqfd: IRQ eventfd to shut down (lock must be held) + */ +static void bao_irqfd_shutdown(struct irqfd *irqfd) +{ + u64 cnt; + + if (WARN_ON_ONCE(!irqfd || !irqfd->dm)) + return; + + lockdep_assert_held(&irqfd->dm->irqfds_lock); + + list_del_init(&irqfd->list); + + eventfd_ctx_remove_wait_queue(irqfd->eventfd, &irqfd->wait, &cnt); + + eventfd_ctx_put(irqfd->eventfd); + + kfree(irqfd); +} + +/** + * bao_irqfd_inject - Inject a notify hypercall into the Bao hypervisor + * @id: Bao DM ID + * + * Return: 0 on success, -EFAULT if the hypercall fails. + */ +static int bao_irqfd_inject(int id) +{ + struct bao_remio_hypercall_ctx ctx = { + .dm_id = id, + .addr = 0, + .op = BAO_IO_NOTIFY, + .value = 0, + .access_width = 0, + .request_id = 0, + }; + + if (bao_remio_hypercall(&ctx)) + return -EFAULT; + + return 0; +} + +/** + * bao_irqfd_wakeup - Custom wake-up handler for eventfd signaling + * @wait: Wait queue entry + * @mode: Mode flags + * @sync: Sync indicator + * @key: Poll bits (cast from void *) + * + * Called by the Linux kernel poll table when the underlying eventfd is signaled. + * Injects a Bao notify hypercall on POLLIN or schedules shutdown on POLLHUP. + * + * Return: 0 on success, a negative error code on failure + */ +static int bao_irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, + int sync, void *key) +{ + struct irqfd *irqfd; + struct bao_dm *dm; + unsigned long poll_bits; + + if (WARN_ON_ONCE(!wait || !key)) + return -EINVAL; + + irqfd = container_of(wait, struct irqfd, wait); + dm = irqfd->dm; + poll_bits = (unsigned long)key; + + if (poll_bits & POLLIN) + bao_irqfd_inject(dm->info.id); + + if (poll_bits & POLLHUP) + queue_work(dm->irqfd_server, &irqfd->shutdown); + + return 0; +} + +/** + * bao_irqfd_poll_func - Register an IRQFD with a poll table + * @file: File to poll + * @wqh: Wait queue head + * @pt: Poll table + * + * Adds the irqfd's wait queue entry to the kernel wait queue for event monitoring. + */ +static void bao_irqfd_poll_func(struct file *file, wait_queue_head_t *wqh, + poll_table *pt) +{ + struct irqfd *irqfd; + + if (WARN_ON_ONCE(!pt || !wqh)) + return; + + irqfd = container_of(pt, struct irqfd, pt); + add_wait_queue(wqh, &irqfd->wait); +} + +/** + * irqfd_shutdown_work - Workqueue handler to shutdown an irqfd + * @work: Work struct for the shutdown operation + * + * Removes and frees the irqfd from the DM under lock if it is still linked. + */ +static void irqfd_shutdown_work(struct work_struct *work) +{ + struct irqfd *irqfd; + struct bao_dm *dm; + + if (WARN_ON_ONCE(!work)) + return; + + irqfd = container_of(work, struct irqfd, shutdown); + dm = irqfd->dm; + + if (WARN_ON_ONCE(!dm)) + return; + + mutex_lock(&dm->irqfds_lock); + if (!list_empty(&irqfd->list)) + bao_irqfd_shutdown(irqfd); + mutex_unlock(&dm->irqfds_lock); +} + +/** + * bao_irqfd_assign - Assign an eventfd to a DM and create an irqfd + * @dm: Bao device model to assign the eventfd + * @args: Configuration of the irqfd to assign + * + * Return: 0 on success, a negative error code on failure + */ +static int bao_irqfd_assign(struct bao_dm *dm, struct bao_irqfd *args) +{ + struct eventfd_ctx *eventfd = NULL; + struct irqfd *irqfd; + struct irqfd *tmp; + __poll_t events; + struct fd f; + int ret = 0; + + if (WARN_ON_ONCE(!dm || !args)) + return -EINVAL; + + irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL); + if (!irqfd) + return -ENOMEM; + + irqfd->dm = dm; + INIT_LIST_HEAD(&irqfd->list); + INIT_WORK(&irqfd->shutdown, irqfd_shutdown_work); + + f = fdget(args->fd); + if (!fd_file(f)) { + ret = -EBADF; + goto out_free_irqfd; + } + + eventfd = eventfd_ctx_fileget(fd_file(f)); + if (IS_ERR(eventfd)) { + ret = PTR_ERR(eventfd); + goto out_fdput; + } + irqfd->eventfd = eventfd; + + init_waitqueue_func_entry(&irqfd->wait, bao_irqfd_wakeup); + init_poll_funcptr(&irqfd->pt, bao_irqfd_poll_func); + + mutex_lock(&dm->irqfds_lock); + list_for_each_entry(tmp, &dm->irqfds, list) { + if (irqfd->eventfd == tmp->eventfd) { + ret = -EBUSY; + mutex_unlock(&dm->irqfds_lock); + goto out_put_eventfd; + } + } + list_add_tail(&irqfd->list, &dm->irqfds); + mutex_unlock(&dm->irqfds_lock); + + events = vfs_poll(fd_file(f), &irqfd->pt); + if (events & EPOLLIN) + bao_irqfd_inject(dm->info.id); + + fdput(f); + return 0; + +out_put_eventfd: + eventfd_ctx_put(eventfd); +out_fdput: + fdput(f); +out_free_irqfd: + kfree(irqfd); + return ret; +} + +/** + * bao_irqfd_deassign - Deassign an eventfd and destroy the associated irqfd + * @dm: Bao device model to remove the irqfd from + * @args: Configuration of the irqfd to deassign + * + * Return: 0 on success, a negative error code on failure + */ +static int bao_irqfd_deassign(struct bao_dm *dm, struct bao_irqfd *args) +{ + struct irqfd *irqfd; + struct irqfd *tmp; + struct eventfd_ctx *eventfd; + + if (WARN_ON_ONCE(!dm || !args)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + mutex_lock(&dm->irqfds_lock); + list_for_each_entry_safe(irqfd, tmp, &dm->irqfds, list) { + if (irqfd->eventfd == eventfd) { + bao_irqfd_shutdown(irqfd); + break; + } + } + mutex_unlock(&dm->irqfds_lock); + + eventfd_ctx_put(eventfd); + + return 0; +} + +int bao_irqfd_server_config(struct bao_dm *dm, struct bao_irqfd *config) +{ + if (WARN_ON_ONCE(!dm || !config)) + return -EINVAL; + + if (config->flags & BAO_IRQFD_FLAG_DEASSIGN) + return bao_irqfd_deassign(dm, config); + + return bao_irqfd_assign(dm, config); +} + +int bao_irqfd_server_init(struct bao_dm *dm) +{ + char name[BAO_NAME_MAX_LEN]; + + if (WARN_ON_ONCE(!dm)) + return -EINVAL; + + mutex_init(&dm->irqfds_lock); + INIT_LIST_HEAD(&dm->irqfds); + + snprintf(name, sizeof(name), "bao-ioirqfds%u", dm->info.id); + + dm->irqfd_server = alloc_workqueue(name, WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!dm->irqfd_server) + return -ENOMEM; + + return 0; +} + +void bao_irqfd_server_destroy(struct bao_dm *dm) +{ + struct irqfd *irqfd; + struct irqfd *next; + + if (WARN_ON_ONCE(!dm)) + return; + + if (dm->irqfd_server) + destroy_workqueue(dm->irqfd_server); + + mutex_lock(&dm->irqfds_lock); + list_for_each_entry_safe(irqfd, next, &dm->irqfds, list) + bao_irqfd_shutdown(irqfd); + mutex_unlock(&dm->irqfds_lock); +} diff --git a/include/linux/bao.h b/include/linux/bao.h new file mode 100644 index 000000000000..5b06d2a17d21 --- /dev/null +++ b/include/linux/bao.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Bao Hypervisor Linux Kernel Header file + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef _LINUX_BAO_H +#define _LINUX_BAO_H + +#include <linux/types.h> + +/* Remote I/O Hypercall ID */ +#define BAO_REMIO_HYPERCALL_ID 0x2 + +/** + * struct bao_remio_hypercall_ctx - REMIO hypercall context + * @dm_id: Device model identifier + * @addr: Target address + * @op: Operation code + * @value: Value to read/write + * @access_width: Access width in bytes + * @request_id: Request identifier + * @npend_req: Number of pending requests + */ +struct bao_remio_hypercall_ctx { + u64 dm_id; + u64 addr; + u64 op; + u64 value; + u64 access_width; + u64 request_id; + u64 npend_req; +}; + +#endif /* _LINUX_BAO_H */ diff --git a/include/uapi/linux/bao.h b/include/uapi/linux/bao.h new file mode 100644 index 000000000000..7713a73e4e4e --- /dev/null +++ b/include/uapi/linux/bao.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Provides the Bao Hypervisor IOCTLs and global structures + * + * Copyright (c) Bao Project and Contributors. All rights reserved. + * + * Authors: + * João Peixoto <joaopeixoto@osyx.tech> + * José Martins <jose@osyx.tech> + * David Cerdeira <davidmcerdeira@osyx.tech> + */ + +#ifndef _UAPI_BAO_H +#define _UAPI_BAO_H + +#include <linux/types.h> + +/** + * struct bao_virtio_request - Parameters of a Bao VirtIO request + * @dm_id: Device model ID + * @addr: MMIO register address accessed + * @op: Operation type (WRITE = 0, READ, ASK, NOTIFY) + * @value: Value to write or read + * @access_width: Access width (VirtIO MMIO supports 4-byte aligned accesses) + * @request_id: Request ID of the I/O request + */ +struct bao_virtio_request { + __u64 dm_id; + __u64 addr; + __u64 op; + __u64 value; + __u64 access_width; + __u64 request_id; +}; + +/** + * struct bao_ioeventfd - Parameters of an ioeventfd request + * @fd: Eventfd file descriptor associated with the I/O request + * @flags: Logical OR of BAO_IOEVENTFD_FLAG_* + * @addr: Start address of the I/O range + * @len: Length of the I/O range + * @reserved: Reserved, must be 0 + * @data: Data for matching (used if data matching is enabled) + */ +struct bao_ioeventfd { + __u32 fd; + __u32 flags; + __u64 addr; + __u32 len; + __u32 reserved; + __u64 data; +}; + +/** + * struct bao_irqfd - Parameters of an IRQFD request + * @fd: File descriptor of the eventfd + * @flags: Flags associated with the eventfd + */ +struct bao_irqfd { + __s32 fd; + __u32 flags; +}; + +/** + * struct bao_dm_info - Parameters of a Bao device model + * @id: Virtual ID of the DM + * @shmem_addr: Base address of the shared memory + * @shmem_size: Size of the shared memory + * @irq: IRQ number + * @fd: File descriptor of the DM + */ +struct bao_dm_info { + __u32 id; + __u64 shmem_addr; + __u64 shmem_size; + __u32 irq; + __s32 fd; +}; + +/* + * The ioctl type for Bao, documented in + * Documentation/userspace-api/ioctl/ioctl-number.rst + */ +#define BAO_IOCTL_TYPE 0xA6 + +/* + * Bao userspace IOCTL commands + * Follows Linux kernel convention, see Documentation/driver-api/ioctl.rst + */ +#define BAO_IOCTL_DM_GET_INFO _IOWR(BAO_IOCTL_TYPE, 0x01, struct bao_dm_info) +#define BAO_IOCTL_IO_CLIENT_ATTACH \ + _IOWR(BAO_IOCTL_TYPE, 0x02, struct bao_virtio_request) +#define BAO_IOCTL_IO_REQUEST_COMPLETE \ + _IOW(BAO_IOCTL_TYPE, 0x03, struct bao_virtio_request) +#define BAO_IOCTL_IOEVENTFD _IOW(BAO_IOCTL_TYPE, 0x04, struct bao_ioeventfd) +#define BAO_IOCTL_IRQFD _IOW(BAO_IOCTL_TYPE, 0x05, struct bao_irqfd) + +#endif /* _UAPI_BAO_H */ -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 4/6] virt: bao: Add Bao I/O dispatcher driver 2026-01-07 16:28 ` [PATCH 4/6] virt: bao: Add Bao I/O dispatcher driver joaopeixoto @ 2026-01-14 20:32 ` Andrew Jones 0 siblings, 0 replies; 31+ messages in thread From: Andrew Jones @ 2026-01-14 20:32 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Jan 07, 2026 at 04:28:27PM +0000, joaopeixoto@osyx.tech wrote: ... > diff --git a/arch/riscv/include/asm/bao.h b/arch/riscv/include/asm/bao.h > index 35658f37e1bd..f04e6cd33fa9 100644 > --- a/arch/riscv/include/asm/bao.h > +++ b/arch/riscv/include/asm/bao.h > @@ -14,6 +14,7 @@ > #define __ASM_RISCV_BAO_H > > #include <asm/sbi.h> > +#include <linux/bao.h> > > #define BAO_SBI_EXT_ID 0x08000ba0 > > @@ -28,4 +29,33 @@ static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, > return ret.error; > } > > +static inline unsigned long > +bao_remio_hypercall(struct bao_remio_hypercall_ctx *ctx) > +{ > + register uintptr_t a0 asm("a0") = (uintptr_t)(ctx->dm_id); > + register uintptr_t a1 asm("a1") = (uintptr_t)(ctx->addr); > + register uintptr_t a2 asm("a2") = (uintptr_t)(ctx->op); > + register uintptr_t a3 asm("a3") = (uintptr_t)(ctx->value); > + register uintptr_t a4 asm("a4") = (uintptr_t)(ctx->request_id); > + register uintptr_t a5 asm("a5") = (uintptr_t)(0); > + register uintptr_t a6 asm("a6") = (uintptr_t)(BAO_REMIO_HYPERCALL_ID); > + register uintptr_t a7 asm("a7") = (uintptr_t)(0x08000ba0); ^ BAO_SBI_EXT_ID Using the experimental extension ID space would be fine for an RFC, but this can't be merged until an SBI implementation ID for Bao is added to the RISC-V SBI spec. Then the Bao EID would be '0xA000000 | <Bao-IMP-ID>' I think we'll also need to discuss whether or not firmware/hypervisor- specific extensions are exempt from all rules in chapter 3 of the SBI spec other than a7 being the EID. If not, then this function should just call __sbi_ecall() and the Bao hypercalls will not be allowed to modify any registers except a0 and a1. > + > + asm volatile("ecall" > + : "+r"(a0), "+r"(a1), "+r"(a2), "+r"(a3), "+r"(a4), > + "+r"(a5), "+r"(a6), "+r"(a7) > + : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), > + "r"(a6), "r"(a7) > + : "memory"); > + > + ctx->addr = a2; > + ctx->op = a3; > + ctx->value = a4; > + ctx->access_width = a5; > + ctx->request_id = a6; > + ctx->npend_req = a7; > + > + return a0; > +} Thanks, drew ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto ` (3 preceding siblings ...) 2026-01-07 16:28 ` [PATCH 4/6] virt: bao: Add Bao I/O dispatcher driver joaopeixoto @ 2026-01-07 16:28 ` joaopeixoto 2026-01-07 19:36 ` Greg KH 2026-01-14 20:34 ` Andrew Jones 2026-01-07 16:28 ` [PATCH 6/6] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings joaopeixoto 2026-01-07 19:37 ` [PATCH v2 0/6] virt: Add Bao hypervisor IPC and I/O dispatcher drivers Greg KH 6 siblings, 2 replies; 31+ messages in thread From: joaopeixoto @ 2026-01-07 16:28 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> Move the IPC shared-memory hypercall ID from architecture-specific headers into include/linux/bao.h. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- arch/arm/include/asm/bao.h | 5 ++--- arch/arm64/include/asm/bao.h | 5 ++--- arch/riscv/include/asm/bao.h | 7 +++---- drivers/virt/bao/ipcshmem/ipcshmem.c | 5 +---- include/linux/bao.h | 3 +++ 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/arch/arm/include/asm/bao.h b/arch/arm/include/asm/bao.h index 5ece9ecb1455..7d13591fe669 100644 --- a/arch/arm/include/asm/bao.h +++ b/arch/arm/include/asm/bao.h @@ -16,14 +16,13 @@ #include <linux/arm-smccc.h> #include <linux/bao.h> -static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, - unsigned long ipcshmem_id) +static inline unsigned long bao_ipcshmem_hypercall(unsigned long ipcshmem_id) { struct arm_smccc_res res; arm_smccc_hvc(ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, ARM_SMCCC_OWNER_VENDOR_HYP, - hypercall_id), + BAO_IPCSHMEM_HYPERCALL_ID), ipcshmem_id, 0, 0, 0, 0, 0, 0, &res); return res.a0; diff --git a/arch/arm64/include/asm/bao.h b/arch/arm64/include/asm/bao.h index c7b7ec60c042..409f4058d56e 100644 --- a/arch/arm64/include/asm/bao.h +++ b/arch/arm64/include/asm/bao.h @@ -16,14 +16,13 @@ #include <linux/arm-smccc.h> #include <linux/bao.h> -static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, - unsigned long ipcshmem_id) +static inline unsigned long bao_ipcshmem_hypercall(unsigned long ipcshmem_id) { struct arm_smccc_res res; arm_smccc_hvc(ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_VENDOR_HYP, - hypercall_id), + BAO_IPCSHMEM_HYPERCALL_ID), ipcshmem_id, 0, 0, 0, 0, 0, 0, &res); return res.a0; diff --git a/arch/riscv/include/asm/bao.h b/arch/riscv/include/asm/bao.h index f04e6cd33fa9..d168aba7d8ec 100644 --- a/arch/riscv/include/asm/bao.h +++ b/arch/riscv/include/asm/bao.h @@ -18,13 +18,12 @@ #define BAO_SBI_EXT_ID 0x08000ba0 -static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, - unsigned long ipcshmem_id) +static inline unsigned long bao_ipcshmem_hypercall(unsigned long ipcshmem_id) { struct sbiret ret; - ret = sbi_ecall(BAO_SBI_EXT_ID, hypercall_id, ipcshmem_id, 0, 0, 0, 0, - 0); + ret = sbi_ecall(BAO_SBI_EXT_ID, BAO_IPCSHMEM_HYPERCALL_ID, ipcshmem_id, + 0, 0, 0, 0, 0); return ret.error; } diff --git a/drivers/virt/bao/ipcshmem/ipcshmem.c b/drivers/virt/bao/ipcshmem/ipcshmem.c index f3892d41248c..593e89cb76bd 100644 --- a/drivers/virt/bao/ipcshmem/ipcshmem.c +++ b/drivers/virt/bao/ipcshmem/ipcshmem.c @@ -14,9 +14,6 @@ #define BAO_IPCSHMEM_NAME_LEN 16 -/* IPC through shared-memory hypercall ID */ -#define BAO_IPCSHMEM_HYPERCALL_ID 0x1 - struct bao_ipcshmem { struct miscdevice miscdev; int id; @@ -90,7 +87,7 @@ static ssize_t bao_ipcshmem_write(struct file *filp, const char __user *buf, *ppos += count; /* Notify Bao hypervisor */ - bao_ipcshmem_hypercall(BAO_IPCSHMEM_HYPERCALL_ID, bao->id); + bao_ipcshmem_hypercall(bao->id); return count; } diff --git a/include/linux/bao.h b/include/linux/bao.h index 5b06d2a17d21..b29830374788 100644 --- a/include/linux/bao.h +++ b/include/linux/bao.h @@ -15,6 +15,9 @@ #include <linux/types.h> +/* IPC through shared-memory hypercall ID */ +#define BAO_IPCSHMEM_HYPERCALL_ID 0x1 + /* Remote I/O Hypercall ID */ #define BAO_REMIO_HYPERCALL_ID 0x2 -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header 2026-01-07 16:28 ` [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header joaopeixoto @ 2026-01-07 19:36 ` Greg KH 2026-01-14 20:34 ` Andrew Jones 1 sibling, 0 replies; 31+ messages in thread From: Greg KH @ 2026-01-07 19:36 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Jan 07, 2026 at 04:28:28PM +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > Move the IPC shared-memory hypercall ID from architecture-specific > headers into include/linux/bao.h. That says _what_ you did, but not why you did it :( I have no idea why this is needed at all, sorry. > Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> > --- > arch/arm/include/asm/bao.h | 5 ++--- > arch/arm64/include/asm/bao.h | 5 ++--- > arch/riscv/include/asm/bao.h | 7 +++---- > drivers/virt/bao/ipcshmem/ipcshmem.c | 5 +---- > include/linux/bao.h | 3 +++ > 5 files changed, 11 insertions(+), 14 deletions(-) > > diff --git a/arch/arm/include/asm/bao.h b/arch/arm/include/asm/bao.h > index 5ece9ecb1455..7d13591fe669 100644 > --- a/arch/arm/include/asm/bao.h > +++ b/arch/arm/include/asm/bao.h > @@ -16,14 +16,13 @@ > #include <linux/arm-smccc.h> > #include <linux/bao.h> > > -static inline unsigned long bao_ipcshmem_hypercall(unsigned long hypercall_id, > - unsigned long ipcshmem_id) > +static inline unsigned long bao_ipcshmem_hypercall(unsigned long ipcshmem_id) This does not match what you said you were doing in the changelog :( ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header 2026-01-07 16:28 ` [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header joaopeixoto 2026-01-07 19:36 ` Greg KH @ 2026-01-14 20:34 ` Andrew Jones 1 sibling, 0 replies; 31+ messages in thread From: Andrew Jones @ 2026-01-14 20:34 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Jan 07, 2026 at 04:28:28PM +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > Move the IPC shared-memory hypercall ID from architecture-specific > headers into include/linux/bao.h. The series should be rebased to have this fixup integrated into the previous patches where the code is first introduced. Thanks, drew ^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 6/6] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto ` (4 preceding siblings ...) 2026-01-07 16:28 ` [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header joaopeixoto @ 2026-01-07 16:28 ` joaopeixoto 2026-01-07 19:37 ` [PATCH v2 0/6] virt: Add Bao hypervisor IPC and I/O dispatcher drivers Greg KH 6 siblings, 0 replies; 31+ messages in thread From: joaopeixoto @ 2026-01-07 16:28 UTC (permalink / raw) To: linux-kernel Cc: ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, gregkh, haren, heiko, joaopeixoto, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will From: João Peixoto <joaopeixoto@osyx.tech> Add MAINTAINERS entries for all the Bao hypervisor components. Signed-off-by: João Peixoto <joaopeixoto@osyx.tech> --- MAINTAINERS | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index dc731d37c8fe..4286efb307b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4321,6 +4321,19 @@ F: drivers/video/backlight/ F: include/linux/backlight.h F: include/linux/pwm_backlight.h +BAO HYPERVISOR +M: José Martins <jose@osyx.tech> +M: David Cerdeira <davidmcerdeira@osyx.tech> +M: João Peixoto <joaopeixoto@osyx.tech> +S: Maintained +F: Documentation/devicetree/bindings/bao/ +F: arch/arm/include/asm/bao.h +F: arch/arm64/include/asm/bao.h +F: arch/riscv/include/asm/bao.h +F: drivers/virt/bao +F: include/linux/bao.h +F: include/uapi/linux/bao.h + BARCO P50 GPIO DRIVER M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com> M: Peter Korsgaard <peter.korsgaard@barco.com> -- 2.43.0 ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 0/6] virt: Add Bao hypervisor IPC and I/O dispatcher drivers 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto ` (5 preceding siblings ...) 2026-01-07 16:28 ` [PATCH 6/6] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings joaopeixoto @ 2026-01-07 19:37 ` Greg KH 6 siblings, 0 replies; 31+ messages in thread From: Greg KH @ 2026-01-07 19:37 UTC (permalink / raw) To: joaopeixoto Cc: linux-kernel, ajd, alex, aou, bagasdotme, catalin.marinas, conor+dt, corbet, dan.j.williams, davidmcerdeira, devicetree, dev, haren, heiko, jose, kever.yang, krzk+dt, linux-arm-kernel, linux-doc, linux-riscv, maddy, mani, nathan, neil.armstrong, palmer, pjw, prabhakar.mahadev-lad.rj, robh, will On Wed, Jan 07, 2026 at 04:28:23PM +0000, joaopeixoto@osyx.tech wrote: > From: João Peixoto <joaopeixoto@osyx.tech> > > This series introduces support for Bao hypervisor guest-side drivers > under drivers/virt/bao, along with the associated Device Tree bindings, > UAPI headers, and MAINTAINERS entries. > > Bao is a lightweight static-partitioning hypervisor for embedded and > safety-critical systems. This series adds: > - Bao IPC shared memory driver: Enables Linux guests to communicate > with each other via shared memory regions. > - Bao I/O Dispatcher driver: Allows Bao VMs to share I/O devices using > device paravirtualization (VirtIO). > > Key updates in this version to align with upstream requirements: > 1. Switched to misc device API: Removed all cdev, class, and > alloc_chrdev_region code in favor of misc_register() for simpler, > standard device management. > 2. Clean kernel style and formatting: All code passes checkpatch.pl, > with consistent variable declaration, function naming, and comment style. > 3. Architecture abstraction: Consolidated ARM, ARM64, and RISC-V hypercall > logic into architecture-specific headers, removing in-driver #ifdefs. > 4. Commit messages: Now concise and Linux-kernel-style, describing > behavior and impact clearly. > 5. Device Tree compliance: Fixed all make dt_binding_check errors to > ensure DT binding correctness. > > This series has been validated on Linux guests running under Bao > hypervisor, confirming correct initialization, IPC shared-memory > read/write operations, and I/O Dispatcher functionality for > backend VMs. > > Feedback and review from maintainers of virtualization, > architecture-specific code (ARM, ARM64, RISC-V), Device Tree > bindings, and UAPI are welcome. > > João Peixoto (6): > dt-bindings: Add Bao IPC shared memory driver binding > virt: bao: Add Bao IPC shared memory driver > dt-bindings: Add Bao I/O dispatcher driver binding > virt: bao: Add Bao I/O dispatcher driver > virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header > MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT > bindings > > .../bindings/bao/bao,io-dispatcher.yaml | 75 ++++ > .../devicetree/bindings/bao/bao,ipcshmem.yaml | 82 ++++ > .../devicetree/bindings/vendor-prefixes.yaml | 2 + > .../userspace-api/ioctl/ioctl-number.rst | 2 + > MAINTAINERS | 13 + > arch/arm/include/asm/bao.h | 60 +++ > arch/arm64/include/asm/bao.h | 60 +++ > arch/riscv/include/asm/bao.h | 60 +++ > drivers/virt/Kconfig | 2 + > drivers/virt/Makefile | 2 + > drivers/virt/bao/Kconfig | 5 + > drivers/virt/bao/Makefile | 4 + > drivers/virt/bao/io-dispatcher/Kconfig | 15 + > drivers/virt/bao/io-dispatcher/Makefile | 4 + > drivers/virt/bao/io-dispatcher/bao_drv.h | 361 ++++++++++++++++ > drivers/virt/bao/io-dispatcher/dm.c | 405 ++++++++++++++++++ > drivers/virt/bao/io-dispatcher/driver.c | 185 ++++++++ > drivers/virt/bao/io-dispatcher/intc.c | 64 +++ > drivers/virt/bao/io-dispatcher/io_client.c | 405 ++++++++++++++++++ > .../virt/bao/io-dispatcher/io_dispatcher.c | 179 ++++++++ > drivers/virt/bao/io-dispatcher/ioeventfd.c | 323 ++++++++++++++ > drivers/virt/bao/io-dispatcher/irqfd.c | 314 ++++++++++++++ > drivers/virt/bao/ipcshmem/Kconfig | 8 + > drivers/virt/bao/ipcshmem/Makefile | 3 + > drivers/virt/bao/ipcshmem/ipcshmem.c | 252 +++++++++++ > include/linux/bao.h | 44 ++ > include/uapi/linux/bao.h | 98 +++++ > 27 files changed, 3027 insertions(+) > create mode 100644 Documentation/devicetree/bindings/bao/bao,io-dispatcher.yaml > create mode 100644 Documentation/devicetree/bindings/bao/bao,ipcshmem.yaml > create mode 100644 arch/arm/include/asm/bao.h > create mode 100644 arch/arm64/include/asm/bao.h > create mode 100644 arch/riscv/include/asm/bao.h > create mode 100644 drivers/virt/bao/Kconfig > create mode 100644 drivers/virt/bao/Makefile > create mode 100644 drivers/virt/bao/io-dispatcher/Kconfig > create mode 100644 drivers/virt/bao/io-dispatcher/Makefile > create mode 100644 drivers/virt/bao/io-dispatcher/bao_drv.h > create mode 100644 drivers/virt/bao/io-dispatcher/dm.c > create mode 100644 drivers/virt/bao/io-dispatcher/driver.c > create mode 100644 drivers/virt/bao/io-dispatcher/intc.c > create mode 100644 drivers/virt/bao/io-dispatcher/io_client.c > create mode 100644 drivers/virt/bao/io-dispatcher/io_dispatcher.c > create mode 100644 drivers/virt/bao/io-dispatcher/ioeventfd.c > create mode 100644 drivers/virt/bao/io-dispatcher/irqfd.c > create mode 100644 drivers/virt/bao/ipcshmem/Kconfig > create mode 100644 drivers/virt/bao/ipcshmem/Makefile > create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c > create mode 100644 include/linux/bao.h > create mode 100644 include/uapi/linux/bao.h > > -- > 2.43.0 > Hi, This is the friendly patch-bot of Greg Kroah-Hartman. You have sent him a patch that has triggered this response. He used to manually respond to these common problems, but in order to save his sanity (he kept writing the same thing over and over, yet to different people), I was created. Hopefully you will not take offence and will fix the problem in your patch and resubmit it so that it can be accepted into the Linux kernel tree. You are receiving this message because of the following common error(s) as indicated below: - This looks like a new version of a previously submitted patch, but you did not list below the --- line any changes from the previous version. Please read the section entitled "The canonical patch format" in the kernel file, Documentation/process/submitting-patches.rst for what needs to be done here to properly describe this. If you wish to discuss this problem further, or you have questions about how to resolve this issue, please feel free to respond to this email and Greg will reply once he has dug out from the pending patches received from other developers. thanks, greg k-h's patch email bot ^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2026-01-14 20:37 UTC | newest] Thread overview: 31+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-12-24 13:52 [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers joaopeixoto 2025-12-24 13:52 ` [PATCH 1/5] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto 2025-12-24 16:18 ` Rob Herring (Arm) 2025-12-25 8:57 ` Krzysztof Kozlowski 2025-12-24 13:52 ` [PATCH 2/5] virt: add Bao IPC shared memory driver joaopeixoto 2025-12-24 15:53 ` Greg KH 2025-12-24 15:54 ` Greg KH 2025-12-25 9:02 ` Krzysztof Kozlowski 2025-12-24 13:52 ` [PATCH 3/5] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto 2025-12-24 16:18 ` Rob Herring (Arm) 2025-12-25 8:58 ` Krzysztof Kozlowski 2025-12-24 13:52 ` [PATCH 4/5] virt: add Bao I/O dispatcher driver joaopeixoto 2025-12-25 9:12 ` Krzysztof Kozlowski 2025-12-24 13:52 ` [PATCH 5/5] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings joaopeixoto 2025-12-25 8:52 ` Krzysztof Kozlowski 2025-12-25 8:51 ` [PATCH 0/5] virt: Add Bao hypervisor IPC and I/O dispatcher drivers Krzysztof Kozlowski 2026-01-07 16:28 ` [PATCH v2 0/6] " joaopeixoto 2026-01-07 16:28 ` [PATCH 1/6] dt-bindings: Add Bao IPC shared memory driver binding joaopeixoto 2026-01-07 16:46 ` Krzysztof Kozlowski 2026-01-07 16:28 ` [PATCH 2/6] virt: bao: Add Bao IPC shared memory driver joaopeixoto 2026-01-07 18:54 ` Randy Dunlap 2026-01-14 20:37 ` Andrew Jones 2026-01-07 16:28 ` [PATCH 3/6] dt-bindings: Add Bao I/O dispatcher driver binding joaopeixoto 2026-01-07 16:47 ` Krzysztof Kozlowski 2026-01-07 16:28 ` [PATCH 4/6] virt: bao: Add Bao I/O dispatcher driver joaopeixoto 2026-01-14 20:32 ` Andrew Jones 2026-01-07 16:28 ` [PATCH 5/6] virt: bao: Move BAO_IPCSHMEM_HYPERCALL_ID to common header joaopeixoto 2026-01-07 19:36 ` Greg KH 2026-01-14 20:34 ` Andrew Jones 2026-01-07 16:28 ` [PATCH 6/6] MAINTAINERS: Add entries for Bao hypervisor drivers, headers, and DT bindings joaopeixoto 2026-01-07 19:37 ` [PATCH v2 0/6] virt: Add Bao hypervisor IPC and I/O dispatcher drivers Greg KH
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox