2035 lines · 46.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Mediated virtual PCI serial host device driver * * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. *     Author: Neo Jia <cjia@nvidia.com> *             Kirti Wankhede <kwankhede@nvidia.com> * * Sample driver that creates mdev device that simulates serial port over PCI * card. */ #include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/cdev.h>#include <linux/sched.h>#include <linux/wait.h>#include <linux/vfio.h>#include <linux/iommu.h>#include <linux/sysfs.h>#include <linux/ctype.h>#include <linux/file.h>#include <linux/mdev.h>#include <linux/pci.h>#include <linux/serial.h>#include <uapi/linux/serial_reg.h>#include <linux/eventfd.h>#include <linux/anon_inodes.h> /* * #defines */ #define VERSION_STRING  "0.1"#define DRIVER_AUTHOR   "NVIDIA Corporation" #define MTTY_CLASS_NAME "mtty" #define MTTY_NAME       "mtty" #define MTTY_STRING_LEN		16 #define MTTY_CONFIG_SPACE_SIZE  0xff#define MTTY_IO_BAR_SIZE        0x8#define MTTY_MMIO_BAR_SIZE      0x100000 #define STORE_LE16(addr, val)   (*(u16 *)addr = val)#define STORE_LE32(addr, val)   (*(u32 *)addr = val) #define MAX_FIFO_SIZE   16 #define CIRCULAR_BUF_INC_IDX(idx)    (idx = (idx + 1) & (MAX_FIFO_SIZE - 1)) #define MTTY_VFIO_PCI_OFFSET_SHIFT   40 #define MTTY_VFIO_PCI_OFFSET_TO_INDEX(off)   (off >> MTTY_VFIO_PCI_OFFSET_SHIFT)#define MTTY_VFIO_PCI_INDEX_TO_OFFSET(index) \				((u64)(index) << MTTY_VFIO_PCI_OFFSET_SHIFT)#define MTTY_VFIO_PCI_OFFSET_MASK    \				(((u64)(1) << MTTY_VFIO_PCI_OFFSET_SHIFT) - 1)#define MAX_MTTYS	24 /* * Global Structures */ static const struct class mtty_class = {	.name	= MTTY_CLASS_NAME}; static struct mtty_dev {	dev_t		vd_devt;	struct cdev	vd_cdev;	struct idr	vd_idr;	struct device	dev;	struct mdev_parent parent;} mtty_dev; struct mdev_region_info {	u64 start;	u64 phys_start;	u32 size;	u64 vfio_offset;}; #if defined(DEBUG_REGS)static const char *wr_reg[] = {	"TX",	"IER",	"FCR",	"LCR",	"MCR",	"LSR",	"MSR",	"SCR"}; static const char *rd_reg[] = {	"RX",	"IER",	"IIR",	"LCR",	"MCR",	"LSR",	"MSR",	"SCR"};#endif /* loop back buffer */struct rxtx {	u8 fifo[MAX_FIFO_SIZE];	u8 head, tail;	u8 count;}; struct serial_port {	u8 uart_reg[8];         /* 8 registers */	struct rxtx rxtx;       /* loop back buffer */	bool dlab;	bool overrun;	u16 divisor;	u8 fcr;                 /* FIFO control register */	u8 max_fifo_size;	u8 intr_trigger_level;  /* interrupt trigger level */}; struct mtty_data {	u64 magic;#define MTTY_MAGIC 0x7e9d09898c3e2c4e /* Nothing clever, just random */	u32 major_ver;#define MTTY_MAJOR_VER 1	u32 minor_ver;#define MTTY_MINOR_VER 0	u32 nr_ports;	u32 flags;	struct serial_port ports[2];}; struct mdev_state; struct mtty_migration_file {	struct file *filp;	struct mutex lock;	struct mdev_state *mdev_state;	struct mtty_data data;	ssize_t filled_size;	u8 disabled:1;}; /* State of each mdev device */struct mdev_state {	struct vfio_device vdev;	struct eventfd_ctx *intx_evtfd;	struct eventfd_ctx *msi_evtfd;	int irq_index;	u8 *vconfig;	struct mutex ops_lock;	struct mdev_device *mdev;	struct mdev_region_info region_info[VFIO_PCI_NUM_REGIONS];	u32 bar_mask[VFIO_PCI_NUM_REGIONS];	struct list_head next;	struct serial_port s[2];	struct mutex rxtx_lock;	struct vfio_device_info dev_info;	int nr_ports;	enum vfio_device_mig_state state;	struct mutex state_mutex;	struct mutex reset_mutex;	struct mtty_migration_file *saving_migf;	struct mtty_migration_file *resuming_migf;	u8 deferred_reset:1;	u8 intx_mask:1;}; static struct mtty_type {	struct mdev_type type;	int nr_ports;} mtty_types[2] = {	{ .nr_ports = 1, .type.sysfs_name = "1",	  .type.pretty_name = "Single port serial" },	{ .nr_ports = 2, .type.sysfs_name = "2",	  .type.pretty_name = "Dual port serial" },}; static struct mdev_type *mtty_mdev_types[] = {	&mtty_types[0].type,	&mtty_types[1].type,}; static atomic_t mdev_avail_ports = ATOMIC_INIT(MAX_MTTYS); static const struct file_operations vd_fops = {	.owner          = THIS_MODULE,}; static const struct vfio_device_ops mtty_dev_ops; /* Helper functions */ static void dump_buffer(u8 *buf, uint32_t count){#if defined(DEBUG)	int i; 	pr_info("Buffer:\n");	for (i = 0; i < count; i++) {		pr_info("%2x ", *(buf + i));		if ((i + 1) % 16 == 0)			pr_info("\n");	}#endif} static bool is_intx(struct mdev_state *mdev_state){	return mdev_state->irq_index == VFIO_PCI_INTX_IRQ_INDEX;} static bool is_msi(struct mdev_state *mdev_state){	return mdev_state->irq_index == VFIO_PCI_MSI_IRQ_INDEX;} static bool is_noirq(struct mdev_state *mdev_state){	return !is_intx(mdev_state) && !is_msi(mdev_state);} static void mtty_trigger_interrupt(struct mdev_state *mdev_state){	lockdep_assert_held(&mdev_state->ops_lock); 	if (is_msi(mdev_state)) {		if (mdev_state->msi_evtfd)			eventfd_signal(mdev_state->msi_evtfd);	} else if (is_intx(mdev_state)) {		if (mdev_state->intx_evtfd && !mdev_state->intx_mask) {			eventfd_signal(mdev_state->intx_evtfd);			mdev_state->intx_mask = true;		}	}} static void mtty_create_config_space(struct mdev_state *mdev_state){	/* PCI dev ID */	STORE_LE32((u32 *) &mdev_state->vconfig[0x0], 0x32534348); 	/* Control: I/O+, Mem-, BusMaster- */	STORE_LE16((u16 *) &mdev_state->vconfig[0x4], 0x0001); 	/* Status: capabilities list absent */	STORE_LE16((u16 *) &mdev_state->vconfig[0x6], 0x0200); 	/* Rev ID */	mdev_state->vconfig[0x8] =  0x10; 	/* programming interface class : 16550-compatible serial controller */	mdev_state->vconfig[0x9] =  0x02; 	/* Sub class : 00 */	mdev_state->vconfig[0xa] =  0x00; 	/* Base class : Simple Communication controllers */	mdev_state->vconfig[0xb] =  0x07; 	/* base address registers */	/* BAR0: IO space */	STORE_LE32((u32 *) &mdev_state->vconfig[0x10], 0x000001);	mdev_state->bar_mask[0] = ~(MTTY_IO_BAR_SIZE) + 1; 	if (mdev_state->nr_ports == 2) {		/* BAR1: IO space */		STORE_LE32((u32 *) &mdev_state->vconfig[0x14], 0x000001);		mdev_state->bar_mask[1] = ~(MTTY_IO_BAR_SIZE) + 1;	} 	/* Subsystem ID */	STORE_LE32((u32 *) &mdev_state->vconfig[0x2c], 0x32534348); 	mdev_state->vconfig[0x34] =  0x00;   /* Cap Ptr */	mdev_state->vconfig[0x3d] =  0x01;   /* interrupt pin (INTA#) */ 	/* Vendor specific data */	mdev_state->vconfig[0x40] =  0x23;	mdev_state->vconfig[0x43] =  0x80;	mdev_state->vconfig[0x44] =  0x23;	mdev_state->vconfig[0x48] =  0x23;	mdev_state->vconfig[0x4c] =  0x23; 	mdev_state->vconfig[0x60] =  0x50;	mdev_state->vconfig[0x61] =  0x43;	mdev_state->vconfig[0x62] =  0x49;	mdev_state->vconfig[0x63] =  0x20;	mdev_state->vconfig[0x64] =  0x53;	mdev_state->vconfig[0x65] =  0x65;	mdev_state->vconfig[0x66] =  0x72;	mdev_state->vconfig[0x67] =  0x69;	mdev_state->vconfig[0x68] =  0x61;	mdev_state->vconfig[0x69] =  0x6c;	mdev_state->vconfig[0x6a] =  0x2f;	mdev_state->vconfig[0x6b] =  0x55;	mdev_state->vconfig[0x6c] =  0x41;	mdev_state->vconfig[0x6d] =  0x52;	mdev_state->vconfig[0x6e] =  0x54;} static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,				 u8 *buf, u32 count){	u32 cfg_addr, bar_mask, bar_index = 0; 	switch (offset) {	case 0x04: /* device control */	case 0x06: /* device status */		/* do nothing */		break;	case 0x3c:  /* interrupt line */		mdev_state->vconfig[0x3c] = buf[0];		break;	case 0x3d:		/*		 * Interrupt Pin is hardwired to INTA.		 * This field is write protected by hardware		 */		break;	case 0x10:  /* BAR0 */	case 0x14:  /* BAR1 */		if (offset == 0x10)			bar_index = 0;		else if (offset == 0x14)			bar_index = 1; 		if ((mdev_state->nr_ports == 1) && (bar_index == 1)) {			STORE_LE32(&mdev_state->vconfig[offset], 0);			break;		} 		cfg_addr = *(u32 *)buf;		pr_info("BAR%d addr 0x%x\n", bar_index, cfg_addr); 		if (cfg_addr == 0xffffffff) {			bar_mask = mdev_state->bar_mask[bar_index];			cfg_addr = (cfg_addr & bar_mask);		} 		cfg_addr |= (mdev_state->vconfig[offset] & 0x3ul);		STORE_LE32(&mdev_state->vconfig[offset], cfg_addr);		break;	case 0x18:  /* BAR2 */	case 0x1c:  /* BAR3 */	case 0x20:  /* BAR4 */		STORE_LE32(&mdev_state->vconfig[offset], 0);		break;	default:		pr_info("PCI config write @0x%x of %d bytes not handled\n",			offset, count);		break;	}} static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,				u16 offset, u8 *buf, u32 count){	u8 data = *buf; 	/* Handle data written by guest */	switch (offset) {	case UART_TX:		/* if DLAB set, data is LSB of divisor */		if (mdev_state->s[index].dlab) {			mdev_state->s[index].divisor |= data;			break;		} 		mutex_lock(&mdev_state->rxtx_lock); 		/* save in TX buffer */		if (mdev_state->s[index].rxtx.count <				mdev_state->s[index].max_fifo_size) {			mdev_state->s[index].rxtx.fifo[					mdev_state->s[index].rxtx.head] = data;			mdev_state->s[index].rxtx.count++;			CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.head);			mdev_state->s[index].overrun = false; 			/*			 * Trigger interrupt if receive data interrupt is			 * enabled and fifo reached trigger level			 */			if ((mdev_state->s[index].uart_reg[UART_IER] &						UART_IER_RDI) &&			   (mdev_state->s[index].rxtx.count ==				    mdev_state->s[index].intr_trigger_level)) {				/* trigger interrupt */#if defined(DEBUG_INTR)				pr_err("Serial port %d: Fifo level trigger\n",					index);#endif				mtty_trigger_interrupt(mdev_state);			}		} else {#if defined(DEBUG_INTR)			pr_err("Serial port %d: Buffer Overflow\n", index);#endif			mdev_state->s[index].overrun = true; 			/*			 * Trigger interrupt if receiver line status interrupt			 * is enabled			 */			if (mdev_state->s[index].uart_reg[UART_IER] &								UART_IER_RLSI)				mtty_trigger_interrupt(mdev_state);		}		mutex_unlock(&mdev_state->rxtx_lock);		break; 	case UART_IER:		/* if DLAB set, data is MSB of divisor */		if (mdev_state->s[index].dlab)			mdev_state->s[index].divisor |= (u16)data << 8;		else {			mdev_state->s[index].uart_reg[offset] = data;			mutex_lock(&mdev_state->rxtx_lock);			if ((data & UART_IER_THRI) &&			    (mdev_state->s[index].rxtx.head ==					mdev_state->s[index].rxtx.tail)) {#if defined(DEBUG_INTR)				pr_err("Serial port %d: IER_THRI write\n",					index);#endif				mtty_trigger_interrupt(mdev_state);			} 			mutex_unlock(&mdev_state->rxtx_lock);		} 		break; 	case UART_FCR:		mdev_state->s[index].fcr = data; 		mutex_lock(&mdev_state->rxtx_lock);		if (data & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)) {			/* clear loop back FIFO */			mdev_state->s[index].rxtx.count = 0;			mdev_state->s[index].rxtx.head = 0;			mdev_state->s[index].rxtx.tail = 0;		}		mutex_unlock(&mdev_state->rxtx_lock); 		switch (data & UART_FCR_TRIGGER_MASK) {		case UART_FCR_TRIGGER_1:			mdev_state->s[index].intr_trigger_level = 1;			break; 		case UART_FCR_TRIGGER_4:			mdev_state->s[index].intr_trigger_level = 4;			break; 		case UART_FCR_TRIGGER_8:			mdev_state->s[index].intr_trigger_level = 8;			break; 		case UART_FCR_TRIGGER_14:			mdev_state->s[index].intr_trigger_level = 14;			break;		} 		/*		 * Set trigger level to 1 otherwise or  implement timer with		 * timeout of 4 characters and on expiring that timer set		 * Recevice data timeout in IIR register		 */		mdev_state->s[index].intr_trigger_level = 1;		if (data & UART_FCR_ENABLE_FIFO)			mdev_state->s[index].max_fifo_size = MAX_FIFO_SIZE;		else {			mdev_state->s[index].max_fifo_size = 1;			mdev_state->s[index].intr_trigger_level = 1;		} 		break; 	case UART_LCR:		if (data & UART_LCR_DLAB) {			mdev_state->s[index].dlab = true;			mdev_state->s[index].divisor = 0;		} else			mdev_state->s[index].dlab = false; 		mdev_state->s[index].uart_reg[offset] = data;		break; 

Lines 1–500 of 2035.