162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * z/VM IUCV hypervisor console (HVC) device driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This HVC device driver provides terminal access using 662306a36Sopenharmony_ci * z/VM IUCV communication paths. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright IBM Corp. 2008, 2013 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#define KMSG_COMPONENT "hvc_iucv" 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <asm/ebcdic.h> 1862306a36Sopenharmony_ci#include <linux/ctype.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/device.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/mempool.h> 2362306a36Sopenharmony_ci#include <linux/moduleparam.h> 2462306a36Sopenharmony_ci#include <linux/tty.h> 2562306a36Sopenharmony_ci#include <linux/wait.h> 2662306a36Sopenharmony_ci#include <net/iucv/iucv.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "hvc_console.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* General device driver settings */ 3262306a36Sopenharmony_ci#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS 3362306a36Sopenharmony_ci#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* IUCV TTY message */ 3662306a36Sopenharmony_ci#define MSG_VERSION 0x02 /* Message version */ 3762306a36Sopenharmony_ci#define MSG_TYPE_ERROR 0x01 /* Error message */ 3862306a36Sopenharmony_ci#define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */ 3962306a36Sopenharmony_ci#define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */ 4062306a36Sopenharmony_ci#define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */ 4162306a36Sopenharmony_ci#define MSG_TYPE_DATA 0x10 /* Terminal data */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct iucv_tty_msg { 4462306a36Sopenharmony_ci u8 version; /* Message version */ 4562306a36Sopenharmony_ci u8 type; /* Message type */ 4662306a36Sopenharmony_ci#define MSG_MAX_DATALEN ((u16)(~0)) 4762306a36Sopenharmony_ci u16 datalen; /* Payload length */ 4862306a36Sopenharmony_ci u8 data[]; /* Payload buffer */ 4962306a36Sopenharmony_ci} __attribute__((packed)); 5062306a36Sopenharmony_ci#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data)) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cienum iucv_state_t { 5362306a36Sopenharmony_ci IUCV_DISCONN = 0, 5462306a36Sopenharmony_ci IUCV_CONNECTED = 1, 5562306a36Sopenharmony_ci IUCV_SEVERED = 2, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cienum tty_state_t { 5962306a36Sopenharmony_ci TTY_CLOSED = 0, 6062306a36Sopenharmony_ci TTY_OPENED = 1, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct hvc_iucv_private { 6462306a36Sopenharmony_ci struct hvc_struct *hvc; /* HVC struct reference */ 6562306a36Sopenharmony_ci u8 srv_name[8]; /* IUCV service name (ebcdic) */ 6662306a36Sopenharmony_ci unsigned char is_console; /* Linux console usage flag */ 6762306a36Sopenharmony_ci enum iucv_state_t iucv_state; /* IUCV connection status */ 6862306a36Sopenharmony_ci enum tty_state_t tty_state; /* TTY status */ 6962306a36Sopenharmony_ci struct iucv_path *path; /* IUCV path pointer */ 7062306a36Sopenharmony_ci spinlock_t lock; /* hvc_iucv_private lock */ 7162306a36Sopenharmony_ci#define SNDBUF_SIZE (PAGE_SIZE) /* must be < MSG_MAX_DATALEN */ 7262306a36Sopenharmony_ci void *sndbuf; /* send buffer */ 7362306a36Sopenharmony_ci size_t sndbuf_len; /* length of send buffer */ 7462306a36Sopenharmony_ci#define QUEUE_SNDBUF_DELAY (HZ / 25) 7562306a36Sopenharmony_ci struct delayed_work sndbuf_work; /* work: send iucv msg(s) */ 7662306a36Sopenharmony_ci wait_queue_head_t sndbuf_waitq; /* wait for send completion */ 7762306a36Sopenharmony_ci struct list_head tty_outqueue; /* outgoing IUCV messages */ 7862306a36Sopenharmony_ci struct list_head tty_inqueue; /* incoming IUCV messages */ 7962306a36Sopenharmony_ci struct device *dev; /* device structure */ 8062306a36Sopenharmony_ci u8 info_path[16]; /* IUCV path info (dev attr) */ 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct iucv_tty_buffer { 8462306a36Sopenharmony_ci struct list_head list; /* list pointer */ 8562306a36Sopenharmony_ci struct iucv_message msg; /* store an IUCV message */ 8662306a36Sopenharmony_ci size_t offset; /* data buffer offset */ 8762306a36Sopenharmony_ci struct iucv_tty_msg *mbuf; /* buffer to store input/output data */ 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* IUCV callback handler */ 9162306a36Sopenharmony_cistatic int hvc_iucv_path_pending(struct iucv_path *, u8 *, u8 *); 9262306a36Sopenharmony_cistatic void hvc_iucv_path_severed(struct iucv_path *, u8 *); 9362306a36Sopenharmony_cistatic void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); 9462306a36Sopenharmony_cistatic void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Kernel module parameter: use one terminal device as default */ 9862306a36Sopenharmony_cistatic unsigned long hvc_iucv_devices = 1; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* Array of allocated hvc iucv tty lines... */ 10162306a36Sopenharmony_cistatic struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; 10262306a36Sopenharmony_ci#define IUCV_HVC_CON_IDX (0) 10362306a36Sopenharmony_ci/* List of z/VM user ID filter entries (struct iucv_vmid_filter) */ 10462306a36Sopenharmony_ci#define MAX_VMID_FILTER (500) 10562306a36Sopenharmony_ci#define FILTER_WILDCARD_CHAR '*' 10662306a36Sopenharmony_cistatic size_t hvc_iucv_filter_size; 10762306a36Sopenharmony_cistatic void *hvc_iucv_filter; 10862306a36Sopenharmony_cistatic const char *hvc_iucv_filter_string; 10962306a36Sopenharmony_cistatic DEFINE_RWLOCK(hvc_iucv_filter_lock); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Kmem cache and mempool for iucv_tty_buffer elements */ 11262306a36Sopenharmony_cistatic struct kmem_cache *hvc_iucv_buffer_cache; 11362306a36Sopenharmony_cistatic mempool_t *hvc_iucv_mempool; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* IUCV handler callback functions */ 11662306a36Sopenharmony_cistatic struct iucv_handler hvc_iucv_handler = { 11762306a36Sopenharmony_ci .path_pending = hvc_iucv_path_pending, 11862306a36Sopenharmony_ci .path_severed = hvc_iucv_path_severed, 11962306a36Sopenharmony_ci .message_complete = hvc_iucv_msg_complete, 12062306a36Sopenharmony_ci .message_pending = hvc_iucv_msg_pending, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance. 12662306a36Sopenharmony_ci * @num: The HVC virtual terminal number (vtermno) 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * This function returns the struct hvc_iucv_private instance that corresponds 12962306a36Sopenharmony_ci * to the HVC virtual terminal number specified as parameter @num. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci if (num > hvc_iucv_devices) 13462306a36Sopenharmony_ci return NULL; 13562306a36Sopenharmony_ci return hvc_iucv_table[num]; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * alloc_tty_buffer() - Return a new struct iucv_tty_buffer element. 14062306a36Sopenharmony_ci * @size: Size of the internal buffer used to store data. 14162306a36Sopenharmony_ci * @flags: Memory allocation flags passed to mempool. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * This function allocates a new struct iucv_tty_buffer element and, optionally, 14462306a36Sopenharmony_ci * allocates an internal data buffer with the specified size @size. 14562306a36Sopenharmony_ci * The internal data buffer is always allocated with GFP_DMA which is 14662306a36Sopenharmony_ci * required for receiving and sending data with IUCV. 14762306a36Sopenharmony_ci * Note: The total message size arises from the internal buffer size and the 14862306a36Sopenharmony_ci * members of the iucv_tty_msg structure. 14962306a36Sopenharmony_ci * The function returns NULL if memory allocation has failed. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct iucv_tty_buffer *bufp; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci bufp = mempool_alloc(hvc_iucv_mempool, flags); 15662306a36Sopenharmony_ci if (!bufp) 15762306a36Sopenharmony_ci return NULL; 15862306a36Sopenharmony_ci memset(bufp, 0, sizeof(*bufp)); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (size > 0) { 16162306a36Sopenharmony_ci bufp->msg.length = MSG_SIZE(size); 16262306a36Sopenharmony_ci bufp->mbuf = kmalloc(bufp->msg.length, flags | GFP_DMA); 16362306a36Sopenharmony_ci if (!bufp->mbuf) { 16462306a36Sopenharmony_ci mempool_free(bufp, hvc_iucv_mempool); 16562306a36Sopenharmony_ci return NULL; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci bufp->mbuf->version = MSG_VERSION; 16862306a36Sopenharmony_ci bufp->mbuf->type = MSG_TYPE_DATA; 16962306a36Sopenharmony_ci bufp->mbuf->datalen = (u16) size; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci return bufp; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * destroy_tty_buffer() - destroy struct iucv_tty_buffer element. 17662306a36Sopenharmony_ci * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_cistatic void destroy_tty_buffer(struct iucv_tty_buffer *bufp) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci kfree(bufp->mbuf); 18162306a36Sopenharmony_ci mempool_free(bufp, hvc_iucv_mempool); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/** 18562306a36Sopenharmony_ci * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element. 18662306a36Sopenharmony_ci * @list: List containing struct iucv_tty_buffer elements. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cistatic void destroy_tty_buffer_list(struct list_head *list) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct iucv_tty_buffer *ent, *next; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci list_for_each_entry_safe(ent, next, list, list) { 19362306a36Sopenharmony_ci list_del(&ent->list); 19462306a36Sopenharmony_ci destroy_tty_buffer(ent); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/** 19962306a36Sopenharmony_ci * hvc_iucv_write() - Receive IUCV message & write data to HVC buffer. 20062306a36Sopenharmony_ci * @priv: Pointer to struct hvc_iucv_private 20162306a36Sopenharmony_ci * @buf: HVC buffer for writing received terminal data. 20262306a36Sopenharmony_ci * @count: HVC buffer size. 20362306a36Sopenharmony_ci * @has_more_data: Pointer to an int variable. 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * The function picks up pending messages from the input queue and receives 20662306a36Sopenharmony_ci * the message data that is then written to the specified buffer @buf. 20762306a36Sopenharmony_ci * If the buffer size @count is less than the data message size, the 20862306a36Sopenharmony_ci * message is kept on the input queue and @has_more_data is set to 1. 20962306a36Sopenharmony_ci * If all message data has been written, the message is removed from 21062306a36Sopenharmony_ci * the input queue. 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * The function returns the number of bytes written to the terminal, zero if 21362306a36Sopenharmony_ci * there are no pending data messages available or if there is no established 21462306a36Sopenharmony_ci * IUCV path. 21562306a36Sopenharmony_ci * If the IUCV path has been severed, then -EPIPE is returned to cause a 21662306a36Sopenharmony_ci * hang up (that is issued by the HVC layer). 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistatic int hvc_iucv_write(struct hvc_iucv_private *priv, 21962306a36Sopenharmony_ci char *buf, int count, int *has_more_data) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct iucv_tty_buffer *rb; 22262306a36Sopenharmony_ci int written; 22362306a36Sopenharmony_ci int rc; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* immediately return if there is no IUCV connection */ 22662306a36Sopenharmony_ci if (priv->iucv_state == IUCV_DISCONN) 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* if the IUCV path has been severed, return -EPIPE to inform the 23062306a36Sopenharmony_ci * HVC layer to hang up the tty device. */ 23162306a36Sopenharmony_ci if (priv->iucv_state == IUCV_SEVERED) 23262306a36Sopenharmony_ci return -EPIPE; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* check if there are pending messages */ 23562306a36Sopenharmony_ci if (list_empty(&priv->tty_inqueue)) 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* receive an iucv message and flip data to the tty (ldisc) */ 23962306a36Sopenharmony_ci rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci written = 0; 24262306a36Sopenharmony_ci if (!rb->mbuf) { /* message not yet received ... */ 24362306a36Sopenharmony_ci /* allocate mem to store msg data; if no memory is available 24462306a36Sopenharmony_ci * then leave the buffer on the list and re-try later */ 24562306a36Sopenharmony_ci rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC | GFP_DMA); 24662306a36Sopenharmony_ci if (!rb->mbuf) 24762306a36Sopenharmony_ci return -ENOMEM; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci rc = __iucv_message_receive(priv->path, &rb->msg, 0, 25062306a36Sopenharmony_ci rb->mbuf, rb->msg.length, NULL); 25162306a36Sopenharmony_ci switch (rc) { 25262306a36Sopenharmony_ci case 0: /* Successful */ 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case 2: /* No message found */ 25562306a36Sopenharmony_ci case 9: /* Message purged */ 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci default: 25862306a36Sopenharmony_ci written = -EIO; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci /* remove buffer if an error has occurred or received data 26162306a36Sopenharmony_ci * is not correct */ 26262306a36Sopenharmony_ci if (rc || (rb->mbuf->version != MSG_VERSION) || 26362306a36Sopenharmony_ci (rb->msg.length != MSG_SIZE(rb->mbuf->datalen))) 26462306a36Sopenharmony_ci goto out_remove_buffer; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci switch (rb->mbuf->type) { 26862306a36Sopenharmony_ci case MSG_TYPE_DATA: 26962306a36Sopenharmony_ci written = min_t(int, rb->mbuf->datalen - rb->offset, count); 27062306a36Sopenharmony_ci memcpy(buf, rb->mbuf->data + rb->offset, written); 27162306a36Sopenharmony_ci if (written < (rb->mbuf->datalen - rb->offset)) { 27262306a36Sopenharmony_ci rb->offset += written; 27362306a36Sopenharmony_ci *has_more_data = 1; 27462306a36Sopenharmony_ci goto out_written; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci case MSG_TYPE_WINSIZE: 27962306a36Sopenharmony_ci if (rb->mbuf->datalen != sizeof(struct winsize)) 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci /* The caller must ensure that the hvc is locked, which 28262306a36Sopenharmony_ci * is the case when called from hvc_iucv_get_chars() */ 28362306a36Sopenharmony_ci __hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci case MSG_TYPE_ERROR: /* ignored ... */ 28762306a36Sopenharmony_ci case MSG_TYPE_TERMENV: /* ignored ... */ 28862306a36Sopenharmony_ci case MSG_TYPE_TERMIOS: /* ignored ... */ 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciout_remove_buffer: 29362306a36Sopenharmony_ci list_del(&rb->list); 29462306a36Sopenharmony_ci destroy_tty_buffer(rb); 29562306a36Sopenharmony_ci *has_more_data = !list_empty(&priv->tty_inqueue); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciout_written: 29862306a36Sopenharmony_ci return written; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * hvc_iucv_get_chars() - HVC get_chars operation. 30362306a36Sopenharmony_ci * @vtermno: HVC virtual terminal number. 30462306a36Sopenharmony_ci * @buf: Pointer to a buffer to store data 30562306a36Sopenharmony_ci * @count: Size of buffer available for writing 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * The HVC thread calls this method to read characters from the back-end. 30862306a36Sopenharmony_ci * If an IUCV communication path has been established, pending IUCV messages 30962306a36Sopenharmony_ci * are received and data is copied into buffer @buf up to @count bytes. 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Locking: The routine gets called under an irqsave() spinlock; and 31262306a36Sopenharmony_ci * the routine locks the struct hvc_iucv_private->lock to call 31362306a36Sopenharmony_ci * helper functions. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_cistatic int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); 31862306a36Sopenharmony_ci int written; 31962306a36Sopenharmony_ci int has_more_data; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (count <= 0) 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!priv) 32562306a36Sopenharmony_ci return -ENODEV; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spin_lock(&priv->lock); 32862306a36Sopenharmony_ci has_more_data = 0; 32962306a36Sopenharmony_ci written = hvc_iucv_write(priv, buf, count, &has_more_data); 33062306a36Sopenharmony_ci spin_unlock(&priv->lock); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* if there are still messages on the queue... schedule another run */ 33362306a36Sopenharmony_ci if (has_more_data) 33462306a36Sopenharmony_ci hvc_kick(); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return written; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/** 34062306a36Sopenharmony_ci * hvc_iucv_queue() - Buffer terminal data for sending. 34162306a36Sopenharmony_ci * @priv: Pointer to struct hvc_iucv_private instance. 34262306a36Sopenharmony_ci * @buf: Buffer containing data to send. 34362306a36Sopenharmony_ci * @count: Size of buffer and amount of data to send. 34462306a36Sopenharmony_ci * 34562306a36Sopenharmony_ci * The function queues data for sending. To actually send the buffered data, 34662306a36Sopenharmony_ci * a work queue function is scheduled (with QUEUE_SNDBUF_DELAY). 34762306a36Sopenharmony_ci * The function returns the number of data bytes that has been buffered. 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * If the device is not connected, data is ignored and the function returns 35062306a36Sopenharmony_ci * @count. 35162306a36Sopenharmony_ci * If the buffer is full, the function returns 0. 35262306a36Sopenharmony_ci * If an existing IUCV communicaton path has been severed, -EPIPE is returned 35362306a36Sopenharmony_ci * (that can be passed to HVC layer to cause a tty hangup). 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic int hvc_iucv_queue(struct hvc_iucv_private *priv, const char *buf, 35662306a36Sopenharmony_ci int count) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci size_t len; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (priv->iucv_state == IUCV_DISCONN) 36162306a36Sopenharmony_ci return count; /* ignore data */ 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (priv->iucv_state == IUCV_SEVERED) 36462306a36Sopenharmony_ci return -EPIPE; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci len = min_t(size_t, count, SNDBUF_SIZE - priv->sndbuf_len); 36762306a36Sopenharmony_ci if (!len) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci memcpy(priv->sndbuf + priv->sndbuf_len, buf, len); 37162306a36Sopenharmony_ci priv->sndbuf_len += len; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (priv->iucv_state == IUCV_CONNECTED) 37462306a36Sopenharmony_ci schedule_delayed_work(&priv->sndbuf_work, QUEUE_SNDBUF_DELAY); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return len; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * hvc_iucv_send() - Send an IUCV message containing terminal data. 38162306a36Sopenharmony_ci * @priv: Pointer to struct hvc_iucv_private instance. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * If an IUCV communication path has been established, the buffered output data 38462306a36Sopenharmony_ci * is sent via an IUCV message and the number of bytes sent is returned. 38562306a36Sopenharmony_ci * Returns 0 if there is no established IUCV communication path or 38662306a36Sopenharmony_ci * -EPIPE if an existing IUCV communicaton path has been severed. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_cistatic int hvc_iucv_send(struct hvc_iucv_private *priv) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct iucv_tty_buffer *sb; 39162306a36Sopenharmony_ci int rc, len; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (priv->iucv_state == IUCV_SEVERED) 39462306a36Sopenharmony_ci return -EPIPE; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (priv->iucv_state == IUCV_DISCONN) 39762306a36Sopenharmony_ci return -EIO; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!priv->sndbuf_len) 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* allocate internal buffer to store msg data and also compute total 40362306a36Sopenharmony_ci * message length */ 40462306a36Sopenharmony_ci sb = alloc_tty_buffer(priv->sndbuf_len, GFP_ATOMIC); 40562306a36Sopenharmony_ci if (!sb) 40662306a36Sopenharmony_ci return -ENOMEM; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci memcpy(sb->mbuf->data, priv->sndbuf, priv->sndbuf_len); 40962306a36Sopenharmony_ci sb->mbuf->datalen = (u16) priv->sndbuf_len; 41062306a36Sopenharmony_ci sb->msg.length = MSG_SIZE(sb->mbuf->datalen); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci list_add_tail(&sb->list, &priv->tty_outqueue); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci rc = __iucv_message_send(priv->path, &sb->msg, 0, 0, 41562306a36Sopenharmony_ci (void *) sb->mbuf, sb->msg.length); 41662306a36Sopenharmony_ci if (rc) { 41762306a36Sopenharmony_ci /* drop the message here; however we might want to handle 41862306a36Sopenharmony_ci * 0x03 (msg limit reached) by trying again... */ 41962306a36Sopenharmony_ci list_del(&sb->list); 42062306a36Sopenharmony_ci destroy_tty_buffer(sb); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci len = priv->sndbuf_len; 42362306a36Sopenharmony_ci priv->sndbuf_len = 0; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return len; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci/** 42962306a36Sopenharmony_ci * hvc_iucv_sndbuf_work() - Send buffered data over IUCV 43062306a36Sopenharmony_ci * @work: Work structure. 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * This work queue function sends buffered output data over IUCV and, 43362306a36Sopenharmony_ci * if not all buffered data could be sent, reschedules itself. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_cistatic void hvc_iucv_sndbuf_work(struct work_struct *work) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct hvc_iucv_private *priv; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci priv = container_of(work, struct hvc_iucv_private, sndbuf_work.work); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 44262306a36Sopenharmony_ci hvc_iucv_send(priv); 44362306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/** 44762306a36Sopenharmony_ci * hvc_iucv_put_chars() - HVC put_chars operation. 44862306a36Sopenharmony_ci * @vtermno: HVC virtual terminal number. 44962306a36Sopenharmony_ci * @buf: Pointer to an buffer to read data from 45062306a36Sopenharmony_ci * @count: Size of buffer available for reading 45162306a36Sopenharmony_ci * 45262306a36Sopenharmony_ci * The HVC thread calls this method to write characters to the back-end. 45362306a36Sopenharmony_ci * The function calls hvc_iucv_queue() to queue terminal data for sending. 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * Locking: The method gets called under an irqsave() spinlock; and 45662306a36Sopenharmony_ci * locks struct hvc_iucv_private->lock. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); 46162306a36Sopenharmony_ci int queued; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (count <= 0) 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!priv) 46762306a36Sopenharmony_ci return -ENODEV; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci spin_lock(&priv->lock); 47062306a36Sopenharmony_ci queued = hvc_iucv_queue(priv, buf, count); 47162306a36Sopenharmony_ci spin_unlock(&priv->lock); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return queued; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/** 47762306a36Sopenharmony_ci * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time. 47862306a36Sopenharmony_ci * @hp: Pointer to the HVC device (struct hvc_struct) 47962306a36Sopenharmony_ci * @id: Additional data (originally passed to hvc_alloc): the index of an struct 48062306a36Sopenharmony_ci * hvc_iucv_private instance. 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private 48362306a36Sopenharmony_ci * instance that is derived from @id. Always returns 0. 48462306a36Sopenharmony_ci * 48562306a36Sopenharmony_ci * Locking: struct hvc_iucv_private->lock, spin_lock_bh 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_cistatic int hvc_iucv_notifier_add(struct hvc_struct *hp, int id) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct hvc_iucv_private *priv; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci priv = hvc_iucv_get_private(id); 49262306a36Sopenharmony_ci if (!priv) 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 49662306a36Sopenharmony_ci priv->tty_state = TTY_OPENED; 49762306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/** 50362306a36Sopenharmony_ci * hvc_iucv_cleanup() - Clean up and reset a z/VM IUCV HVC instance. 50462306a36Sopenharmony_ci * @priv: Pointer to the struct hvc_iucv_private instance. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_cistatic void hvc_iucv_cleanup(struct hvc_iucv_private *priv) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci destroy_tty_buffer_list(&priv->tty_outqueue); 50962306a36Sopenharmony_ci destroy_tty_buffer_list(&priv->tty_inqueue); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci priv->tty_state = TTY_CLOSED; 51262306a36Sopenharmony_ci priv->iucv_state = IUCV_DISCONN; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci priv->sndbuf_len = 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/** 51862306a36Sopenharmony_ci * tty_outqueue_empty() - Test if the tty outq is empty 51962306a36Sopenharmony_ci * @priv: Pointer to struct hvc_iucv_private instance. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistatic inline int tty_outqueue_empty(struct hvc_iucv_private *priv) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci int rc; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 52662306a36Sopenharmony_ci rc = list_empty(&priv->tty_outqueue); 52762306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return rc; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/** 53362306a36Sopenharmony_ci * flush_sndbuf_sync() - Flush send buffer and wait for completion 53462306a36Sopenharmony_ci * @priv: Pointer to struct hvc_iucv_private instance. 53562306a36Sopenharmony_ci * 53662306a36Sopenharmony_ci * The routine cancels a pending sndbuf work, calls hvc_iucv_send() 53762306a36Sopenharmony_ci * to flush any buffered terminal output data and waits for completion. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_cistatic void flush_sndbuf_sync(struct hvc_iucv_private *priv) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci int sync_wait; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci cancel_delayed_work_sync(&priv->sndbuf_work); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 54662306a36Sopenharmony_ci hvc_iucv_send(priv); /* force sending buffered data */ 54762306a36Sopenharmony_ci sync_wait = !list_empty(&priv->tty_outqueue); /* anything queued ? */ 54862306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (sync_wait) 55162306a36Sopenharmony_ci wait_event_timeout(priv->sndbuf_waitq, 55262306a36Sopenharmony_ci tty_outqueue_empty(priv), HZ/10); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/** 55662306a36Sopenharmony_ci * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up 55762306a36Sopenharmony_ci * @priv: Pointer to hvc_iucv_private structure 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * This routine severs an existing IUCV communication path and hangs 56062306a36Sopenharmony_ci * up the underlying HVC terminal device. 56162306a36Sopenharmony_ci * The hang-up occurs only if an IUCV communication path is established; 56262306a36Sopenharmony_ci * otherwise there is no need to hang up the terminal device. 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * The IUCV HVC hang-up is separated into two steps: 56562306a36Sopenharmony_ci * 1. After the IUCV path has been severed, the iucv_state is set to 56662306a36Sopenharmony_ci * IUCV_SEVERED. 56762306a36Sopenharmony_ci * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the 56862306a36Sopenharmony_ci * IUCV_SEVERED state causes the tty hang-up in the HVC layer. 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * If the tty has not yet been opened, clean up the hvc_iucv_private 57162306a36Sopenharmony_ci * structure to allow re-connects. 57262306a36Sopenharmony_ci * If the tty has been opened, let get_chars() return -EPIPE to signal 57362306a36Sopenharmony_ci * the HVC layer to hang up the tty and, if so, wake up the HVC thread 57462306a36Sopenharmony_ci * to call get_chars()... 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * Special notes on hanging up a HVC terminal instantiated as console: 57762306a36Sopenharmony_ci * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops) 57862306a36Sopenharmony_ci * 2. do_tty_hangup() calls tty->ops->close() for console_filp 57962306a36Sopenharmony_ci * => no hangup notifier is called by HVC (default) 58062306a36Sopenharmony_ci * 2. hvc_close() returns because of tty_hung_up_p(filp) 58162306a36Sopenharmony_ci * => no delete notifier is called! 58262306a36Sopenharmony_ci * Finally, the back-end is not being notified, thus, the tty session is 58362306a36Sopenharmony_ci * kept active (TTY_OPEN) to be ready for re-connects. 58462306a36Sopenharmony_ci * 58562306a36Sopenharmony_ci * Locking: spin_lock(&priv->lock) w/o disabling bh 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_cistatic void hvc_iucv_hangup(struct hvc_iucv_private *priv) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct iucv_path *path; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci path = NULL; 59262306a36Sopenharmony_ci spin_lock(&priv->lock); 59362306a36Sopenharmony_ci if (priv->iucv_state == IUCV_CONNECTED) { 59462306a36Sopenharmony_ci path = priv->path; 59562306a36Sopenharmony_ci priv->path = NULL; 59662306a36Sopenharmony_ci priv->iucv_state = IUCV_SEVERED; 59762306a36Sopenharmony_ci if (priv->tty_state == TTY_CLOSED) 59862306a36Sopenharmony_ci hvc_iucv_cleanup(priv); 59962306a36Sopenharmony_ci else 60062306a36Sopenharmony_ci /* console is special (see above) */ 60162306a36Sopenharmony_ci if (priv->is_console) { 60262306a36Sopenharmony_ci hvc_iucv_cleanup(priv); 60362306a36Sopenharmony_ci priv->tty_state = TTY_OPENED; 60462306a36Sopenharmony_ci } else 60562306a36Sopenharmony_ci hvc_kick(); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci spin_unlock(&priv->lock); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* finally sever path (outside of priv->lock due to lock ordering) */ 61062306a36Sopenharmony_ci if (path) { 61162306a36Sopenharmony_ci iucv_path_sever(path, NULL); 61262306a36Sopenharmony_ci iucv_path_free(path); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/** 61762306a36Sopenharmony_ci * hvc_iucv_notifier_hangup() - HVC notifier for TTY hangups. 61862306a36Sopenharmony_ci * @hp: Pointer to the HVC device (struct hvc_struct) 61962306a36Sopenharmony_ci * @id: Additional data (originally passed to hvc_alloc): 62062306a36Sopenharmony_ci * the index of an struct hvc_iucv_private instance. 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * This routine notifies the HVC back-end that a tty hangup (carrier loss, 62362306a36Sopenharmony_ci * virtual or otherwise) has occurred. 62462306a36Sopenharmony_ci * The z/VM IUCV HVC device driver ignores virtual hangups (vhangup()) 62562306a36Sopenharmony_ci * to keep an existing IUCV communication path established. 62662306a36Sopenharmony_ci * (Background: vhangup() is called from user space (by getty or login) to 62762306a36Sopenharmony_ci * disable writing to the tty by other applications). 62862306a36Sopenharmony_ci * If the tty has been opened and an established IUCV path has been severed 62962306a36Sopenharmony_ci * (we caused the tty hangup), the function calls hvc_iucv_cleanup(). 63062306a36Sopenharmony_ci * 63162306a36Sopenharmony_ci * Locking: struct hvc_iucv_private->lock 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_cistatic void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct hvc_iucv_private *priv; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci priv = hvc_iucv_get_private(id); 63862306a36Sopenharmony_ci if (!priv) 63962306a36Sopenharmony_ci return; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci flush_sndbuf_sync(priv); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 64462306a36Sopenharmony_ci /* NOTE: If the hangup was scheduled by ourself (from the iucv 64562306a36Sopenharmony_ci * path_servered callback [IUCV_SEVERED]), we have to clean up 64662306a36Sopenharmony_ci * our structure and to set state to TTY_CLOSED. 64762306a36Sopenharmony_ci * If the tty was hung up otherwise (e.g. vhangup()), then we 64862306a36Sopenharmony_ci * ignore this hangup and keep an established IUCV path open... 64962306a36Sopenharmony_ci * (...the reason is that we are not able to connect back to the 65062306a36Sopenharmony_ci * client if we disconnect on hang up) */ 65162306a36Sopenharmony_ci priv->tty_state = TTY_CLOSED; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (priv->iucv_state == IUCV_SEVERED) 65462306a36Sopenharmony_ci hvc_iucv_cleanup(priv); 65562306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci/** 65962306a36Sopenharmony_ci * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS 66062306a36Sopenharmony_ci * @hp: Pointer the HVC device (struct hvc_struct) 66162306a36Sopenharmony_ci * @active: True to raise or false to lower DTR/RTS lines 66262306a36Sopenharmony_ci * 66362306a36Sopenharmony_ci * This routine notifies the HVC back-end to raise or lower DTR/RTS 66462306a36Sopenharmony_ci * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to 66562306a36Sopenharmony_ci * drop the IUCV connection (similar to hang up the modem). 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_cistatic void hvc_iucv_dtr_rts(struct hvc_struct *hp, bool active) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct hvc_iucv_private *priv; 67062306a36Sopenharmony_ci struct iucv_path *path; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Raising the DTR/RTS is ignored as IUCV connections can be 67362306a36Sopenharmony_ci * established at any times. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci if (active) 67662306a36Sopenharmony_ci return; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci priv = hvc_iucv_get_private(hp->vtermno); 67962306a36Sopenharmony_ci if (!priv) 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Lowering the DTR/RTS lines disconnects an established IUCV 68362306a36Sopenharmony_ci * connection. 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ci flush_sndbuf_sync(priv); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 68862306a36Sopenharmony_ci path = priv->path; /* save reference to IUCV path */ 68962306a36Sopenharmony_ci priv->path = NULL; 69062306a36Sopenharmony_ci priv->iucv_state = IUCV_DISCONN; 69162306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Sever IUCV path outside of priv->lock due to lock ordering of: 69462306a36Sopenharmony_ci * priv->lock <--> iucv_table_lock */ 69562306a36Sopenharmony_ci if (path) { 69662306a36Sopenharmony_ci iucv_path_sever(path, NULL); 69762306a36Sopenharmony_ci iucv_path_free(path); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/** 70262306a36Sopenharmony_ci * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. 70362306a36Sopenharmony_ci * @hp: Pointer to the HVC device (struct hvc_struct) 70462306a36Sopenharmony_ci * @id: Additional data (originally passed to hvc_alloc): 70562306a36Sopenharmony_ci * the index of an struct hvc_iucv_private instance. 70662306a36Sopenharmony_ci * 70762306a36Sopenharmony_ci * This routine notifies the HVC back-end that the last tty device fd has been 70862306a36Sopenharmony_ci * closed. The function cleans up tty resources. The clean-up of the IUCV 70962306a36Sopenharmony_ci * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios 71062306a36Sopenharmony_ci * control setting. 71162306a36Sopenharmony_ci * 71262306a36Sopenharmony_ci * Locking: struct hvc_iucv_private->lock 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_cistatic void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct hvc_iucv_private *priv; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci priv = hvc_iucv_get_private(id); 71962306a36Sopenharmony_ci if (!priv) 72062306a36Sopenharmony_ci return; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci flush_sndbuf_sync(priv); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 72562306a36Sopenharmony_ci destroy_tty_buffer_list(&priv->tty_outqueue); 72662306a36Sopenharmony_ci destroy_tty_buffer_list(&priv->tty_inqueue); 72762306a36Sopenharmony_ci priv->tty_state = TTY_CLOSED; 72862306a36Sopenharmony_ci priv->sndbuf_len = 0; 72962306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci/** 73362306a36Sopenharmony_ci * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID 73462306a36Sopenharmony_ci * @ipvmid: Originating z/VM user ID (right padded with blanks) 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * Returns 0 if the z/VM user ID that is specified with @ipvmid is permitted to 73762306a36Sopenharmony_ci * connect, otherwise non-zero. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_cistatic int hvc_iucv_filter_connreq(u8 ipvmid[8]) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci const char *wildcard, *filter_entry; 74262306a36Sopenharmony_ci size_t i, len; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Note: default policy is ACCEPT if no filter is set */ 74562306a36Sopenharmony_ci if (!hvc_iucv_filter_size) 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci for (i = 0; i < hvc_iucv_filter_size; i++) { 74962306a36Sopenharmony_ci filter_entry = hvc_iucv_filter + (8 * i); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* If a filter entry contains the filter wildcard character, 75262306a36Sopenharmony_ci * reduce the length to match the leading portion of the user 75362306a36Sopenharmony_ci * ID only (wildcard match). Characters following the wildcard 75462306a36Sopenharmony_ci * are ignored. 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci wildcard = strnchr(filter_entry, 8, FILTER_WILDCARD_CHAR); 75762306a36Sopenharmony_ci len = (wildcard) ? wildcard - filter_entry : 8; 75862306a36Sopenharmony_ci if (0 == memcmp(ipvmid, filter_entry, len)) 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci return 1; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci/** 76562306a36Sopenharmony_ci * hvc_iucv_path_pending() - IUCV handler to process a connection request. 76662306a36Sopenharmony_ci * @path: Pending path (struct iucv_path) 76762306a36Sopenharmony_ci * @ipvmid: z/VM system identifier of originator 76862306a36Sopenharmony_ci * @ipuser: User specified data for this path 76962306a36Sopenharmony_ci * (AF_IUCV: port/service name and originator port) 77062306a36Sopenharmony_ci * 77162306a36Sopenharmony_ci * The function uses the @ipuser data to determine if the pending path belongs 77262306a36Sopenharmony_ci * to a terminal managed by this device driver. 77362306a36Sopenharmony_ci * If the path belongs to this driver, ensure that the terminal is not accessed 77462306a36Sopenharmony_ci * multiple times (only one connection to a terminal is allowed). 77562306a36Sopenharmony_ci * If the terminal is not yet connected, the pending path is accepted and is 77662306a36Sopenharmony_ci * associated to the appropriate struct hvc_iucv_private instance. 77762306a36Sopenharmony_ci * 77862306a36Sopenharmony_ci * Returns 0 if @path belongs to a terminal managed by the this device driver; 77962306a36Sopenharmony_ci * otherwise returns -ENODEV in order to dispatch this path to other handlers. 78062306a36Sopenharmony_ci * 78162306a36Sopenharmony_ci * Locking: struct hvc_iucv_private->lock 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_cistatic int hvc_iucv_path_pending(struct iucv_path *path, u8 *ipvmid, 78462306a36Sopenharmony_ci u8 *ipuser) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct hvc_iucv_private *priv, *tmp; 78762306a36Sopenharmony_ci u8 wildcard[9] = "lnxhvc "; 78862306a36Sopenharmony_ci int i, rc, find_unused; 78962306a36Sopenharmony_ci u8 nuser_data[16]; 79062306a36Sopenharmony_ci u8 vm_user_id[9]; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci ASCEBC(wildcard, sizeof(wildcard)); 79362306a36Sopenharmony_ci find_unused = !memcmp(wildcard, ipuser, 8); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* First, check if the pending path request is managed by this 79662306a36Sopenharmony_ci * IUCV handler: 79762306a36Sopenharmony_ci * - find a disconnected device if ipuser contains the wildcard 79862306a36Sopenharmony_ci * - find the device that matches the terminal ID in ipuser 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci priv = NULL; 80162306a36Sopenharmony_ci for (i = 0; i < hvc_iucv_devices; i++) { 80262306a36Sopenharmony_ci tmp = hvc_iucv_table[i]; 80362306a36Sopenharmony_ci if (!tmp) 80462306a36Sopenharmony_ci continue; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (find_unused) { 80762306a36Sopenharmony_ci spin_lock(&tmp->lock); 80862306a36Sopenharmony_ci if (tmp->iucv_state == IUCV_DISCONN) 80962306a36Sopenharmony_ci priv = tmp; 81062306a36Sopenharmony_ci spin_unlock(&tmp->lock); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci } else if (!memcmp(tmp->srv_name, ipuser, 8)) 81362306a36Sopenharmony_ci priv = tmp; 81462306a36Sopenharmony_ci if (priv) 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci if (!priv) 81862306a36Sopenharmony_ci return -ENODEV; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Enforce that ipvmid is allowed to connect to us */ 82162306a36Sopenharmony_ci read_lock(&hvc_iucv_filter_lock); 82262306a36Sopenharmony_ci rc = hvc_iucv_filter_connreq(ipvmid); 82362306a36Sopenharmony_ci read_unlock(&hvc_iucv_filter_lock); 82462306a36Sopenharmony_ci if (rc) { 82562306a36Sopenharmony_ci iucv_path_sever(path, ipuser); 82662306a36Sopenharmony_ci iucv_path_free(path); 82762306a36Sopenharmony_ci memcpy(vm_user_id, ipvmid, 8); 82862306a36Sopenharmony_ci vm_user_id[8] = 0; 82962306a36Sopenharmony_ci pr_info("A connection request from z/VM user ID %s " 83062306a36Sopenharmony_ci "was refused\n", vm_user_id); 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci spin_lock(&priv->lock); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* If the terminal is already connected or being severed, then sever 83762306a36Sopenharmony_ci * this path to enforce that there is only ONE established communication 83862306a36Sopenharmony_ci * path per terminal. */ 83962306a36Sopenharmony_ci if (priv->iucv_state != IUCV_DISCONN) { 84062306a36Sopenharmony_ci iucv_path_sever(path, ipuser); 84162306a36Sopenharmony_ci iucv_path_free(path); 84262306a36Sopenharmony_ci goto out_path_handled; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* accept path */ 84662306a36Sopenharmony_ci memcpy(nuser_data, ipuser + 8, 8); /* remote service (for af_iucv) */ 84762306a36Sopenharmony_ci memcpy(nuser_data + 8, ipuser, 8); /* local service (for af_iucv) */ 84862306a36Sopenharmony_ci path->msglim = 0xffff; /* IUCV MSGLIMIT */ 84962306a36Sopenharmony_ci path->flags &= ~IUCV_IPRMDATA; /* TODO: use IUCV_IPRMDATA */ 85062306a36Sopenharmony_ci rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv); 85162306a36Sopenharmony_ci if (rc) { 85262306a36Sopenharmony_ci iucv_path_sever(path, ipuser); 85362306a36Sopenharmony_ci iucv_path_free(path); 85462306a36Sopenharmony_ci goto out_path_handled; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci priv->path = path; 85762306a36Sopenharmony_ci priv->iucv_state = IUCV_CONNECTED; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* store path information */ 86062306a36Sopenharmony_ci memcpy(priv->info_path, ipvmid, 8); 86162306a36Sopenharmony_ci memcpy(priv->info_path + 8, ipuser + 8, 8); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* flush buffered output data... */ 86462306a36Sopenharmony_ci schedule_delayed_work(&priv->sndbuf_work, 5); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ciout_path_handled: 86762306a36Sopenharmony_ci spin_unlock(&priv->lock); 86862306a36Sopenharmony_ci return 0; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/** 87262306a36Sopenharmony_ci * hvc_iucv_path_severed() - IUCV handler to process a path sever. 87362306a36Sopenharmony_ci * @path: Pending path (struct iucv_path) 87462306a36Sopenharmony_ci * @ipuser: User specified data for this path 87562306a36Sopenharmony_ci * (AF_IUCV: port/service name and originator port) 87662306a36Sopenharmony_ci * 87762306a36Sopenharmony_ci * This function calls the hvc_iucv_hangup() function for the 87862306a36Sopenharmony_ci * respective IUCV HVC terminal. 87962306a36Sopenharmony_ci * 88062306a36Sopenharmony_ci * Locking: struct hvc_iucv_private->lock 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_cistatic void hvc_iucv_path_severed(struct iucv_path *path, u8 *ipuser) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci struct hvc_iucv_private *priv = path->private; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci hvc_iucv_hangup(priv); 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/** 89062306a36Sopenharmony_ci * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message. 89162306a36Sopenharmony_ci * @path: Pending path (struct iucv_path) 89262306a36Sopenharmony_ci * @msg: Pointer to the IUCV message 89362306a36Sopenharmony_ci * 89462306a36Sopenharmony_ci * The function puts an incoming message on the input queue for later 89562306a36Sopenharmony_ci * processing (by hvc_iucv_get_chars() / hvc_iucv_write()). 89662306a36Sopenharmony_ci * If the tty has not yet been opened, the message is rejected. 89762306a36Sopenharmony_ci * 89862306a36Sopenharmony_ci * Locking: struct hvc_iucv_private->lock 89962306a36Sopenharmony_ci */ 90062306a36Sopenharmony_cistatic void hvc_iucv_msg_pending(struct iucv_path *path, 90162306a36Sopenharmony_ci struct iucv_message *msg) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct hvc_iucv_private *priv = path->private; 90462306a36Sopenharmony_ci struct iucv_tty_buffer *rb; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* reject messages that exceed max size of iucv_tty_msg->datalen */ 90762306a36Sopenharmony_ci if (msg->length > MSG_SIZE(MSG_MAX_DATALEN)) { 90862306a36Sopenharmony_ci iucv_message_reject(path, msg); 90962306a36Sopenharmony_ci return; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci spin_lock(&priv->lock); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* reject messages if tty has not yet been opened */ 91562306a36Sopenharmony_ci if (priv->tty_state == TTY_CLOSED) { 91662306a36Sopenharmony_ci iucv_message_reject(path, msg); 91762306a36Sopenharmony_ci goto unlock_return; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* allocate tty buffer to save iucv msg only */ 92162306a36Sopenharmony_ci rb = alloc_tty_buffer(0, GFP_ATOMIC); 92262306a36Sopenharmony_ci if (!rb) { 92362306a36Sopenharmony_ci iucv_message_reject(path, msg); 92462306a36Sopenharmony_ci goto unlock_return; /* -ENOMEM */ 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci rb->msg = *msg; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci list_add_tail(&rb->list, &priv->tty_inqueue); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci hvc_kick(); /* wake up hvc thread */ 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ciunlock_return: 93362306a36Sopenharmony_ci spin_unlock(&priv->lock); 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci/** 93762306a36Sopenharmony_ci * hvc_iucv_msg_complete() - IUCV handler to process message completion 93862306a36Sopenharmony_ci * @path: Pending path (struct iucv_path) 93962306a36Sopenharmony_ci * @msg: Pointer to the IUCV message 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * The function is called upon completion of message delivery to remove the 94262306a36Sopenharmony_ci * message from the outqueue. Additional delivery information can be found 94362306a36Sopenharmony_ci * msg->audit: rejected messages (0x040000 (IPADRJCT)), and 94462306a36Sopenharmony_ci * purged messages (0x010000 (IPADPGNR)). 94562306a36Sopenharmony_ci * 94662306a36Sopenharmony_ci * Locking: struct hvc_iucv_private->lock 94762306a36Sopenharmony_ci */ 94862306a36Sopenharmony_cistatic void hvc_iucv_msg_complete(struct iucv_path *path, 94962306a36Sopenharmony_ci struct iucv_message *msg) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct hvc_iucv_private *priv = path->private; 95262306a36Sopenharmony_ci struct iucv_tty_buffer *ent, *next; 95362306a36Sopenharmony_ci LIST_HEAD(list_remove); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci spin_lock(&priv->lock); 95662306a36Sopenharmony_ci list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list) 95762306a36Sopenharmony_ci if (ent->msg.id == msg->id) { 95862306a36Sopenharmony_ci list_move(&ent->list, &list_remove); 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci wake_up(&priv->sndbuf_waitq); 96262306a36Sopenharmony_ci spin_unlock(&priv->lock); 96362306a36Sopenharmony_ci destroy_tty_buffer_list(&list_remove); 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic ssize_t hvc_iucv_dev_termid_show(struct device *dev, 96762306a36Sopenharmony_ci struct device_attribute *attr, 96862306a36Sopenharmony_ci char *buf) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct hvc_iucv_private *priv = dev_get_drvdata(dev); 97162306a36Sopenharmony_ci size_t len; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci len = sizeof(priv->srv_name); 97462306a36Sopenharmony_ci memcpy(buf, priv->srv_name, len); 97562306a36Sopenharmony_ci EBCASC(buf, len); 97662306a36Sopenharmony_ci buf[len++] = '\n'; 97762306a36Sopenharmony_ci return len; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic ssize_t hvc_iucv_dev_state_show(struct device *dev, 98162306a36Sopenharmony_ci struct device_attribute *attr, 98262306a36Sopenharmony_ci char *buf) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct hvc_iucv_private *priv = dev_get_drvdata(dev); 98562306a36Sopenharmony_ci return sprintf(buf, "%u:%u\n", priv->iucv_state, priv->tty_state); 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic ssize_t hvc_iucv_dev_peer_show(struct device *dev, 98962306a36Sopenharmony_ci struct device_attribute *attr, 99062306a36Sopenharmony_ci char *buf) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct hvc_iucv_private *priv = dev_get_drvdata(dev); 99362306a36Sopenharmony_ci char vmid[9], ipuser[9]; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci memset(vmid, 0, sizeof(vmid)); 99662306a36Sopenharmony_ci memset(ipuser, 0, sizeof(ipuser)); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci spin_lock_bh(&priv->lock); 99962306a36Sopenharmony_ci if (priv->iucv_state == IUCV_CONNECTED) { 100062306a36Sopenharmony_ci memcpy(vmid, priv->info_path, 8); 100162306a36Sopenharmony_ci memcpy(ipuser, priv->info_path + 8, 8); 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci spin_unlock_bh(&priv->lock); 100462306a36Sopenharmony_ci EBCASC(ipuser, 8); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci return sprintf(buf, "%s:%s\n", vmid, ipuser); 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci/* HVC operations */ 101162306a36Sopenharmony_cistatic const struct hv_ops hvc_iucv_ops = { 101262306a36Sopenharmony_ci .get_chars = hvc_iucv_get_chars, 101362306a36Sopenharmony_ci .put_chars = hvc_iucv_put_chars, 101462306a36Sopenharmony_ci .notifier_add = hvc_iucv_notifier_add, 101562306a36Sopenharmony_ci .notifier_del = hvc_iucv_notifier_del, 101662306a36Sopenharmony_ci .notifier_hangup = hvc_iucv_notifier_hangup, 101762306a36Sopenharmony_ci .dtr_rts = hvc_iucv_dtr_rts, 101862306a36Sopenharmony_ci}; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci/* IUCV HVC device attributes */ 102162306a36Sopenharmony_cistatic DEVICE_ATTR(termid, 0640, hvc_iucv_dev_termid_show, NULL); 102262306a36Sopenharmony_cistatic DEVICE_ATTR(state, 0640, hvc_iucv_dev_state_show, NULL); 102362306a36Sopenharmony_cistatic DEVICE_ATTR(peer, 0640, hvc_iucv_dev_peer_show, NULL); 102462306a36Sopenharmony_cistatic struct attribute *hvc_iucv_dev_attrs[] = { 102562306a36Sopenharmony_ci &dev_attr_termid.attr, 102662306a36Sopenharmony_ci &dev_attr_state.attr, 102762306a36Sopenharmony_ci &dev_attr_peer.attr, 102862306a36Sopenharmony_ci NULL, 102962306a36Sopenharmony_ci}; 103062306a36Sopenharmony_cistatic struct attribute_group hvc_iucv_dev_attr_group = { 103162306a36Sopenharmony_ci .attrs = hvc_iucv_dev_attrs, 103262306a36Sopenharmony_ci}; 103362306a36Sopenharmony_cistatic const struct attribute_group *hvc_iucv_dev_attr_groups[] = { 103462306a36Sopenharmony_ci &hvc_iucv_dev_attr_group, 103562306a36Sopenharmony_ci NULL, 103662306a36Sopenharmony_ci}; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci/** 104062306a36Sopenharmony_ci * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance 104162306a36Sopenharmony_ci * @id: hvc_iucv_table index 104262306a36Sopenharmony_ci * @is_console: Flag if the instance is used as Linux console 104362306a36Sopenharmony_ci * 104462306a36Sopenharmony_ci * This function allocates a new hvc_iucv_private structure and stores 104562306a36Sopenharmony_ci * the instance in hvc_iucv_table at index @id. 104662306a36Sopenharmony_ci * Returns 0 on success; otherwise non-zero. 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_cistatic int __init hvc_iucv_alloc(int id, unsigned int is_console) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci struct hvc_iucv_private *priv; 105162306a36Sopenharmony_ci char name[9]; 105262306a36Sopenharmony_ci int rc; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL); 105562306a36Sopenharmony_ci if (!priv) 105662306a36Sopenharmony_ci return -ENOMEM; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci spin_lock_init(&priv->lock); 105962306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->tty_outqueue); 106062306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->tty_inqueue); 106162306a36Sopenharmony_ci INIT_DELAYED_WORK(&priv->sndbuf_work, hvc_iucv_sndbuf_work); 106262306a36Sopenharmony_ci init_waitqueue_head(&priv->sndbuf_waitq); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci priv->sndbuf = (void *) get_zeroed_page(GFP_KERNEL); 106562306a36Sopenharmony_ci if (!priv->sndbuf) { 106662306a36Sopenharmony_ci kfree(priv); 106762306a36Sopenharmony_ci return -ENOMEM; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* set console flag */ 107162306a36Sopenharmony_ci priv->is_console = is_console; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* allocate hvc device */ 107462306a36Sopenharmony_ci priv->hvc = hvc_alloc(id, /* PAGE_SIZE */ 107562306a36Sopenharmony_ci id, &hvc_iucv_ops, 256); 107662306a36Sopenharmony_ci if (IS_ERR(priv->hvc)) { 107762306a36Sopenharmony_ci rc = PTR_ERR(priv->hvc); 107862306a36Sopenharmony_ci goto out_error_hvc; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* notify HVC thread instead of using polling */ 108262306a36Sopenharmony_ci priv->hvc->irq_requested = 1; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* setup iucv related information */ 108562306a36Sopenharmony_ci snprintf(name, 9, "lnxhvc%-2d", id); 108662306a36Sopenharmony_ci memcpy(priv->srv_name, name, 8); 108762306a36Sopenharmony_ci ASCEBC(priv->srv_name, 8); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* create and setup device */ 109062306a36Sopenharmony_ci priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); 109162306a36Sopenharmony_ci if (!priv->dev) { 109262306a36Sopenharmony_ci rc = -ENOMEM; 109362306a36Sopenharmony_ci goto out_error_dev; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci dev_set_name(priv->dev, "hvc_iucv%d", id); 109662306a36Sopenharmony_ci dev_set_drvdata(priv->dev, priv); 109762306a36Sopenharmony_ci priv->dev->bus = &iucv_bus; 109862306a36Sopenharmony_ci priv->dev->parent = iucv_root; 109962306a36Sopenharmony_ci priv->dev->groups = hvc_iucv_dev_attr_groups; 110062306a36Sopenharmony_ci priv->dev->release = (void (*)(struct device *)) kfree; 110162306a36Sopenharmony_ci rc = device_register(priv->dev); 110262306a36Sopenharmony_ci if (rc) { 110362306a36Sopenharmony_ci put_device(priv->dev); 110462306a36Sopenharmony_ci goto out_error_dev; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci hvc_iucv_table[id] = priv; 110862306a36Sopenharmony_ci return 0; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ciout_error_dev: 111162306a36Sopenharmony_ci hvc_remove(priv->hvc); 111262306a36Sopenharmony_ciout_error_hvc: 111362306a36Sopenharmony_ci free_page((unsigned long) priv->sndbuf); 111462306a36Sopenharmony_ci kfree(priv); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return rc; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/** 112062306a36Sopenharmony_ci * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_cistatic void __init hvc_iucv_destroy(struct hvc_iucv_private *priv) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci hvc_remove(priv->hvc); 112562306a36Sopenharmony_ci device_unregister(priv->dev); 112662306a36Sopenharmony_ci free_page((unsigned long) priv->sndbuf); 112762306a36Sopenharmony_ci kfree(priv); 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci/** 113162306a36Sopenharmony_ci * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID 113262306a36Sopenharmony_ci * @filter: String containing a comma-separated list of z/VM user IDs 113362306a36Sopenharmony_ci * @dest: Location where to store the parsed z/VM user ID 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_cistatic const char *hvc_iucv_parse_filter(const char *filter, char *dest) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci const char *nextdelim, *residual; 113862306a36Sopenharmony_ci size_t len; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci nextdelim = strchr(filter, ','); 114162306a36Sopenharmony_ci if (nextdelim) { 114262306a36Sopenharmony_ci len = nextdelim - filter; 114362306a36Sopenharmony_ci residual = nextdelim + 1; 114462306a36Sopenharmony_ci } else { 114562306a36Sopenharmony_ci len = strlen(filter); 114662306a36Sopenharmony_ci residual = filter + len; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (len == 0) 115062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* check for '\n' (if called from sysfs) */ 115362306a36Sopenharmony_ci if (filter[len - 1] == '\n') 115462306a36Sopenharmony_ci len--; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* prohibit filter entries containing the wildcard character only */ 115762306a36Sopenharmony_ci if (len == 1 && *filter == FILTER_WILDCARD_CHAR) 115862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (len > 8) 116162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* pad with blanks and save upper case version of user ID */ 116462306a36Sopenharmony_ci memset(dest, ' ', 8); 116562306a36Sopenharmony_ci while (len--) 116662306a36Sopenharmony_ci dest[len] = toupper(filter[len]); 116762306a36Sopenharmony_ci return residual; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci/** 117162306a36Sopenharmony_ci * hvc_iucv_setup_filter() - Set up z/VM user ID filter 117262306a36Sopenharmony_ci * @filter: String consisting of a comma-separated list of z/VM user IDs 117362306a36Sopenharmony_ci * 117462306a36Sopenharmony_ci * The function parses the @filter string and creates an array containing 117562306a36Sopenharmony_ci * the list of z/VM user ID filter entries. 117662306a36Sopenharmony_ci * Return code 0 means success, -EINVAL if the filter is syntactically 117762306a36Sopenharmony_ci * incorrect, -ENOMEM if there was not enough memory to allocate the 117862306a36Sopenharmony_ci * filter list array, or -ENOSPC if too many z/VM user IDs have been specified. 117962306a36Sopenharmony_ci */ 118062306a36Sopenharmony_cistatic int hvc_iucv_setup_filter(const char *val) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci const char *residual; 118362306a36Sopenharmony_ci int err; 118462306a36Sopenharmony_ci size_t size, count; 118562306a36Sopenharmony_ci void *array, *old_filter; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci count = strlen(val); 118862306a36Sopenharmony_ci if (count == 0 || (count == 1 && val[0] == '\n')) { 118962306a36Sopenharmony_ci size = 0; 119062306a36Sopenharmony_ci array = NULL; 119162306a36Sopenharmony_ci goto out_replace_filter; /* clear filter */ 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* count user IDs in order to allocate sufficient memory */ 119562306a36Sopenharmony_ci size = 1; 119662306a36Sopenharmony_ci residual = val; 119762306a36Sopenharmony_ci while ((residual = strchr(residual, ',')) != NULL) { 119862306a36Sopenharmony_ci residual++; 119962306a36Sopenharmony_ci size++; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* check if the specified list exceeds the filter limit */ 120362306a36Sopenharmony_ci if (size > MAX_VMID_FILTER) 120462306a36Sopenharmony_ci return -ENOSPC; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci array = kcalloc(size, 8, GFP_KERNEL); 120762306a36Sopenharmony_ci if (!array) 120862306a36Sopenharmony_ci return -ENOMEM; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci count = size; 121162306a36Sopenharmony_ci residual = val; 121262306a36Sopenharmony_ci while (*residual && count) { 121362306a36Sopenharmony_ci residual = hvc_iucv_parse_filter(residual, 121462306a36Sopenharmony_ci array + ((size - count) * 8)); 121562306a36Sopenharmony_ci if (IS_ERR(residual)) { 121662306a36Sopenharmony_ci err = PTR_ERR(residual); 121762306a36Sopenharmony_ci kfree(array); 121862306a36Sopenharmony_ci goto out_err; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci count--; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ciout_replace_filter: 122462306a36Sopenharmony_ci write_lock_bh(&hvc_iucv_filter_lock); 122562306a36Sopenharmony_ci old_filter = hvc_iucv_filter; 122662306a36Sopenharmony_ci hvc_iucv_filter_size = size; 122762306a36Sopenharmony_ci hvc_iucv_filter = array; 122862306a36Sopenharmony_ci write_unlock_bh(&hvc_iucv_filter_lock); 122962306a36Sopenharmony_ci kfree(old_filter); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci err = 0; 123262306a36Sopenharmony_ciout_err: 123362306a36Sopenharmony_ci return err; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/** 123762306a36Sopenharmony_ci * param_set_vmidfilter() - Set z/VM user ID filter parameter 123862306a36Sopenharmony_ci * @val: String consisting of a comma-separated list of z/VM user IDs 123962306a36Sopenharmony_ci * @kp: Kernel parameter pointing to hvc_iucv_filter array 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * The function sets up the z/VM user ID filter specified as comma-separated 124262306a36Sopenharmony_ci * list of user IDs in @val. 124362306a36Sopenharmony_ci * Note: If it is called early in the boot process, @val is stored and 124462306a36Sopenharmony_ci * parsed later in hvc_iucv_init(). 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_cistatic int param_set_vmidfilter(const char *val, const struct kernel_param *kp) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci int rc; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (!MACHINE_IS_VM || !hvc_iucv_devices) 125162306a36Sopenharmony_ci return -ENODEV; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci if (!val) 125462306a36Sopenharmony_ci return -EINVAL; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci rc = 0; 125762306a36Sopenharmony_ci if (slab_is_available()) 125862306a36Sopenharmony_ci rc = hvc_iucv_setup_filter(val); 125962306a36Sopenharmony_ci else 126062306a36Sopenharmony_ci hvc_iucv_filter_string = val; /* defer... */ 126162306a36Sopenharmony_ci return rc; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci/** 126562306a36Sopenharmony_ci * param_get_vmidfilter() - Get z/VM user ID filter 126662306a36Sopenharmony_ci * @buffer: Buffer to store z/VM user ID filter, 126762306a36Sopenharmony_ci * (buffer size assumption PAGE_SIZE) 126862306a36Sopenharmony_ci * @kp: Kernel parameter pointing to the hvc_iucv_filter array 126962306a36Sopenharmony_ci * 127062306a36Sopenharmony_ci * The function stores the filter as a comma-separated list of z/VM user IDs 127162306a36Sopenharmony_ci * in @buffer. Typically, sysfs routines call this function for attr show. 127262306a36Sopenharmony_ci */ 127362306a36Sopenharmony_cistatic int param_get_vmidfilter(char *buffer, const struct kernel_param *kp) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci int rc; 127662306a36Sopenharmony_ci size_t index, len; 127762306a36Sopenharmony_ci void *start, *end; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (!MACHINE_IS_VM || !hvc_iucv_devices) 128062306a36Sopenharmony_ci return -ENODEV; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci rc = 0; 128362306a36Sopenharmony_ci read_lock_bh(&hvc_iucv_filter_lock); 128462306a36Sopenharmony_ci for (index = 0; index < hvc_iucv_filter_size; index++) { 128562306a36Sopenharmony_ci start = hvc_iucv_filter + (8 * index); 128662306a36Sopenharmony_ci end = memchr(start, ' ', 8); 128762306a36Sopenharmony_ci len = (end) ? end - start : 8; 128862306a36Sopenharmony_ci memcpy(buffer + rc, start, len); 128962306a36Sopenharmony_ci rc += len; 129062306a36Sopenharmony_ci buffer[rc++] = ','; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci read_unlock_bh(&hvc_iucv_filter_lock); 129362306a36Sopenharmony_ci if (rc) 129462306a36Sopenharmony_ci buffer[--rc] = '\0'; /* replace last comma and update rc */ 129562306a36Sopenharmony_ci return rc; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci#define param_check_vmidfilter(name, p) __param_check(name, p, void) 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_cistatic const struct kernel_param_ops param_ops_vmidfilter = { 130162306a36Sopenharmony_ci .set = param_set_vmidfilter, 130262306a36Sopenharmony_ci .get = param_get_vmidfilter, 130362306a36Sopenharmony_ci}; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci/** 130662306a36Sopenharmony_ci * hvc_iucv_init() - z/VM IUCV HVC device driver initialization 130762306a36Sopenharmony_ci */ 130862306a36Sopenharmony_cistatic int __init hvc_iucv_init(void) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci int rc; 131162306a36Sopenharmony_ci unsigned int i; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (!hvc_iucv_devices) 131462306a36Sopenharmony_ci return -ENODEV; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (!MACHINE_IS_VM) { 131762306a36Sopenharmony_ci pr_notice("The z/VM IUCV HVC device driver cannot " 131862306a36Sopenharmony_ci "be used without z/VM\n"); 131962306a36Sopenharmony_ci rc = -ENODEV; 132062306a36Sopenharmony_ci goto out_error; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) { 132462306a36Sopenharmony_ci pr_err("%lu is not a valid value for the hvc_iucv= " 132562306a36Sopenharmony_ci "kernel parameter\n", hvc_iucv_devices); 132662306a36Sopenharmony_ci rc = -EINVAL; 132762306a36Sopenharmony_ci goto out_error; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* parse hvc_iucv_allow string and create z/VM user ID filter list */ 133162306a36Sopenharmony_ci if (hvc_iucv_filter_string) { 133262306a36Sopenharmony_ci rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); 133362306a36Sopenharmony_ci switch (rc) { 133462306a36Sopenharmony_ci case 0: 133562306a36Sopenharmony_ci break; 133662306a36Sopenharmony_ci case -ENOMEM: 133762306a36Sopenharmony_ci pr_err("Allocating memory failed with " 133862306a36Sopenharmony_ci "reason code=%d\n", 3); 133962306a36Sopenharmony_ci goto out_error; 134062306a36Sopenharmony_ci case -EINVAL: 134162306a36Sopenharmony_ci pr_err("hvc_iucv_allow= does not specify a valid " 134262306a36Sopenharmony_ci "z/VM user ID list\n"); 134362306a36Sopenharmony_ci goto out_error; 134462306a36Sopenharmony_ci case -ENOSPC: 134562306a36Sopenharmony_ci pr_err("hvc_iucv_allow= specifies too many " 134662306a36Sopenharmony_ci "z/VM user IDs\n"); 134762306a36Sopenharmony_ci goto out_error; 134862306a36Sopenharmony_ci default: 134962306a36Sopenharmony_ci goto out_error; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT, 135462306a36Sopenharmony_ci sizeof(struct iucv_tty_buffer), 135562306a36Sopenharmony_ci 0, 0, NULL); 135662306a36Sopenharmony_ci if (!hvc_iucv_buffer_cache) { 135762306a36Sopenharmony_ci pr_err("Allocating memory failed with reason code=%d\n", 1); 135862306a36Sopenharmony_ci rc = -ENOMEM; 135962306a36Sopenharmony_ci goto out_error; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, 136362306a36Sopenharmony_ci hvc_iucv_buffer_cache); 136462306a36Sopenharmony_ci if (!hvc_iucv_mempool) { 136562306a36Sopenharmony_ci pr_err("Allocating memory failed with reason code=%d\n", 2); 136662306a36Sopenharmony_ci kmem_cache_destroy(hvc_iucv_buffer_cache); 136762306a36Sopenharmony_ci rc = -ENOMEM; 136862306a36Sopenharmony_ci goto out_error; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci /* register the first terminal device as console 137262306a36Sopenharmony_ci * (must be done before allocating hvc terminal devices) */ 137362306a36Sopenharmony_ci rc = hvc_instantiate(0, IUCV_HVC_CON_IDX, &hvc_iucv_ops); 137462306a36Sopenharmony_ci if (rc) { 137562306a36Sopenharmony_ci pr_err("Registering HVC terminal device as " 137662306a36Sopenharmony_ci "Linux console failed\n"); 137762306a36Sopenharmony_ci goto out_error_memory; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* allocate hvc_iucv_private structs */ 138162306a36Sopenharmony_ci for (i = 0; i < hvc_iucv_devices; i++) { 138262306a36Sopenharmony_ci rc = hvc_iucv_alloc(i, (i == IUCV_HVC_CON_IDX) ? 1 : 0); 138362306a36Sopenharmony_ci if (rc) { 138462306a36Sopenharmony_ci pr_err("Creating a new HVC terminal device " 138562306a36Sopenharmony_ci "failed with error code=%d\n", rc); 138662306a36Sopenharmony_ci goto out_error_hvc; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci /* register IUCV callback handler */ 139162306a36Sopenharmony_ci rc = iucv_register(&hvc_iucv_handler, 0); 139262306a36Sopenharmony_ci if (rc) { 139362306a36Sopenharmony_ci pr_err("Registering IUCV handlers failed with error code=%d\n", 139462306a36Sopenharmony_ci rc); 139562306a36Sopenharmony_ci goto out_error_hvc; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci return 0; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ciout_error_hvc: 140162306a36Sopenharmony_ci for (i = 0; i < hvc_iucv_devices; i++) 140262306a36Sopenharmony_ci if (hvc_iucv_table[i]) 140362306a36Sopenharmony_ci hvc_iucv_destroy(hvc_iucv_table[i]); 140462306a36Sopenharmony_ciout_error_memory: 140562306a36Sopenharmony_ci mempool_destroy(hvc_iucv_mempool); 140662306a36Sopenharmony_ci kmem_cache_destroy(hvc_iucv_buffer_cache); 140762306a36Sopenharmony_ciout_error: 140862306a36Sopenharmony_ci kfree(hvc_iucv_filter); 140962306a36Sopenharmony_ci hvc_iucv_devices = 0; /* ensure that we do not provide any device */ 141062306a36Sopenharmony_ci return rc; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci/** 141462306a36Sopenharmony_ci * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter 141562306a36Sopenharmony_ci * @val: Parameter value (numeric) 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_cistatic int __init hvc_iucv_config(char *val) 141862306a36Sopenharmony_ci{ 141962306a36Sopenharmony_ci if (kstrtoul(val, 10, &hvc_iucv_devices)) 142062306a36Sopenharmony_ci pr_warn("hvc_iucv= invalid parameter value '%s'\n", val); 142162306a36Sopenharmony_ci return 1; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cidevice_initcall(hvc_iucv_init); 142662306a36Sopenharmony_ci__setup("hvc_iucv=", hvc_iucv_config); 142762306a36Sopenharmony_cicore_param(hvc_iucv_allow, hvc_iucv_filter, vmidfilter, 0640); 1428