// SPDX-License-Identifier: GPL-2.0-or-later /* * Based on virtio_mmio.c * Copyright 2011-2014, ARM Ltd. * * Copyright 2022-2023 Virtual Open Systems SAS. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __LOOPBACK_H__ #define __LOOPBACK_H__ #define DRIVER "LOOPBACK" /* max Minor devices */ #define MAX_DEV 1 /* Define mmap elements limit */ #define MMAP_LIMIT 200 #ifdef DEBUG #define DBG(...) pr_crit(__VA_ARGS__) #else #define DBG(...) #endif /* DEBUG */ /* * The alignment to use between consumer and producer parts of vring. * Currently hardcoded to the page size. */ #define VIRTIO_MMIO_VRING_ALIGN PAGE_SIZE #define to_virtio_mmio_device(_plat_dev) \ container_of(_plat_dev, struct virtio_mmio_device, vdev) typedef struct virtio_device_info_struct { unsigned long magic; unsigned long version; unsigned long device_id; unsigned long vendor; } virtio_device_info_struct_t; typedef struct virtio_neg { uint64_t notification; uint64_t data; uint64_t size; bool read; atomic_t done; } virtio_neg_t; struct virtio_mmio_device { struct virtio_device vdev; struct platform_device *pdev; void __iomem *base; unsigned long version; /* A list of queues so we can dispatch IRQs */ spinlock_t lock; struct list_head virtqueues; }; struct virtio_mmio_vq_info { /* the actual virtqueue */ struct virtqueue *vq; /* the list node for the virtqueues list */ struct list_head node; }; /* * Print the pdev: * *static void print_virtio_pdev(struct platform_device *pdev) *{ * int i; * * pr_info("Print the pdev:\n"); * pr_info("\t.name = %s\n", pdev->name); * pr_info("\t.id = %d\n", pdev->id); * pr_info("\t.num_resources = %d\n", pdev->num_resources); * * for (i=0; i < pdev->num_resources; i++) { * pr_info("\t.num_resource = %d\n", i); * pr_info("\t\t.start = 0x%llx\n", pdev->resource[i].start); * pr_info("\t\t.end = 0x%llx\n", pdev->resource[i].end); * pr_info("\t\t.flags = 0x%lx\n", pdev->resource[i].flags); * } *} * *Result: * * .name = a003e00.virtio_mmio * .id = -1 * .num_resources = 2 * .num_resource = 0 * .start = 0xa003e00 * .end = 0xa003fff * .flags = 0x200 * .num_resource = 1 * .start = 0x2c * .end = 0x2c * .flags = 0x401 */ /* mmap finctionality */ #ifndef VM_RESERVED #define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) #endif struct mmap_info { void *data; int reference; }; void print_neg_flag(uint64_t neg_flag, bool read) { if (read) DBG("Read:\n"); else DBG("Write:\n"); switch (neg_flag) { case VIRTIO_MMIO_MAGIC_VALUE: //0x000 DBG("\tVIRTIO_MMIO_MAGIC_VALUE\n"); break; case VIRTIO_MMIO_VERSION: //0x004 DBG("\tVIRTIO_MMIO_VERSION\n"); break; case VIRTIO_MMIO_DEVICE_ID: //0x008 DBG("\tVIRTIO_MMIO_DEVICE_ID\n"); break; case VIRTIO_MMIO_VENDOR_ID: //0x00c DBG("\tVIRTIO_MMIO_VENDOR_ID\n"); break; case VIRTIO_MMIO_DEVICE_FEATURES: //0x010 DBG("\tVIRTIO_MMIO_DEVICE_FEATURES\n"); break; case VIRTIO_MMIO_DEVICE_FEATURES_SEL: //0x014 DBG("\tVIRTIO_MMIO_DEVICE_FEATURES_SEL\n"); break; case VIRTIO_MMIO_DRIVER_FEATURES: //0x020 DBG("\tVIRTIO_MMIO_DRIVER_FEATURES\n"); break; case VIRTIO_MMIO_DRIVER_FEATURES_SEL: //0x024 DBG("\tVIRTIO_MMIO_DRIVER_FEATURES_SEL\n"); break; case VIRTIO_MMIO_GUEST_PAGE_SIZE: //0x028 DBG("\tVIRTIO_MMIO_GUEST_PAGE_SIZE\n"); break; case VIRTIO_MMIO_QUEUE_SEL: //0x030 DBG("\tVIRTIO_MMIO_QUEUE_SEL\n"); break; case VIRTIO_MMIO_QUEUE_NUM_MAX: //0x034 DBG("\tVIRTIO_MMIO_QUEUE_NUM_MAX\n"); break; case VIRTIO_MMIO_QUEUE_NUM: //0x038 DBG("\tVIRTIO_MMIO_QUEUE_NUM\n"); break; case VIRTIO_MMIO_QUEUE_ALIGN: //0x03c DBG("\tVIRTIO_MMIO_QUEUE_ALIGN\n"); break; case VIRTIO_MMIO_QUEUE_PFN: //0x040 DBG("\tVIRTIO_MMIO_QUEUE_PFN\n"); break; case VIRTIO_MMIO_QUEUE_READY: //0x044 DBG("\tVIRTIO_MMIO_QUEUE_READY\n"); break; case VIRTIO_MMIO_QUEUE_NOTIFY: //0x050 DBG("\tVIRTIO_MMIO_QUEUE_NOTIFY\n"); break; case VIRTIO_MMIO_INTERRUPT_STATUS: //0x060 DBG("\tVIRTIO_MMIO_INTERRUPT_STATUS\n"); break; case VIRTIO_MMIO_INTERRUPT_ACK: //0x064 DBG("\tVIRTIO_MMIO_INTERRUPT_ACK\n"); break; case VIRTIO_MMIO_STATUS: //0x070 DBG("\tVIRTIO_MMIO_STATUS\n"); break; case VIRTIO_MMIO_QUEUE_DESC_LOW: //0x080 DBG("\tVIRTIO_MMIO_QUEUE_DESC_LOW\n"); break; case VIRTIO_MMIO_QUEUE_DESC_HIGH: //0x084 DBG("\tVIRTIO_MMIO_QUEUE_DESC_HIGH\n"); break; case VIRTIO_MMIO_QUEUE_AVAIL_LOW: //0x090 DBG("\tVIRTIO_MMIO_QUEUE_AVAIL_LOW\n"); break; case VIRTIO_MMIO_QUEUE_AVAIL_HIGH: //0x094 DBG("\tVIRTIO_MMIO_QUEUE_AVAIL_HIGH\n"); break; case VIRTIO_MMIO_QUEUE_USED_LOW: //0x0a0 DBG("\tVIRTIO_MMIO_QUEUE_USED_LOW\n"); break; case VIRTIO_MMIO_QUEUE_USED_HIGH: //0x0a4 DBG("\tVIRTIO_MMIO_QUEUE_USED_HIGH\n"); break; case VIRTIO_MMIO_SHM_SEL: //0x0ac DBG("\tVIRTIO_MMIO_SHM_SEL\n"); break; case VIRTIO_MMIO_SHM_LEN_LOW: //0x0b0 DBG("\tVIRTIO_MMIO_SHM_LEN_LOW\n"); break; case VIRTIO_MMIO_SHM_LEN_HIGH: //0x0b4 DBG("\tVIRTIO_MMIO_SHM_LEN_HIGH\n"); break; case VIRTIO_MMIO_SHM_BASE_LOW: //0x0b8 DBG("\tVIRTIO_MMIO_SHM_BASE_LOW\n"); break; case VIRTIO_MMIO_SHM_BASE_HIGH: //0x0bc DBG("\tVIRTIO_MMIO_SHM_BASE_HIGH\n"); break; case VIRTIO_MMIO_CONFIG_GENERATION: //0x0fc DBG("\tVIRTIO_MMIO_CONFIG_GENERATION\n"); break; default: if (neg_flag >= VIRTIO_MMIO_CONFIG) DBG("\tVIRTIO_MMIO_CONFIG\n"); else DBG("\tNegotiation flag Unknown: %lld\n", neg_flag); return; } } void print_data(const void *buf, size_t size) { int offset = 10; int i, j; DBG("Print data from linux virtio-rng side:\n"); printk(KERN_CRIT ""); for (i = 0; i < size; i += offset) { printk(KERN_CONT "\t\t"); for (j = i; (j < i + offset) && (j < size); j++) printk(KERN_CONT "%d, ", *((uint8_t *)(buf + j))); printk(KERN_CRIT ""); } } typedef struct efd_data { int efd[2]; int pid; } efd_data_t; /* * ref: https://elixir.bootlin.com/linux/latest/source/arch/x86/mm/init_64.c#L1419 */ /* IOCTL defines */ #define EFD_INIT _IOC(_IOC_WRITE, 'k', 1, sizeof(efd_data)) #define WAKEUP _IOC(_IOC_WRITE, 'k', 2, 0) #define START_LOOPBACK _IOC(_IOC_WRITE, 'k', 3, sizeof(virtio_device_info_struct_t)) #define IRQ _IOC(_IOC_WRITE, 'k', 4, sizeof(int)) #define SHARE_VQS _IOC(_IOC_WRITE, 'k', 5, sizeof(uint32_t)) #define SHARE_BUF _IOC(_IOC_WRITE, 'k', 6, sizeof(uint64_t)) #define SHARE_COM_STRUCT _IOC(_IOC_WRITE, 'k', 7, 0) /* device data holder, this structure may be extended to hold additional data */ struct loopback_device_data { struct cdev cdev; }; #endif /* __VIRTUALNET_H__ */