18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/completion.h> 78c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 88c2ecf20Sopenharmony_ci#include <linux/list.h> 98c2ecf20Sopenharmony_ci#include <linux/mutex.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 128c2ecf20Sopenharmony_ci#include <asm/atomic.h> 138c2ecf20Sopenharmony_ci#include <init.h> 148c2ecf20Sopenharmony_ci#include <irq_kern.h> 158c2ecf20Sopenharmony_ci#include <os.h> 168c2ecf20Sopenharmony_ci#include "port.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct port_list { 198c2ecf20Sopenharmony_ci struct list_head list; 208c2ecf20Sopenharmony_ci atomic_t wait_count; 218c2ecf20Sopenharmony_ci int has_connection; 228c2ecf20Sopenharmony_ci struct completion done; 238c2ecf20Sopenharmony_ci int port; 248c2ecf20Sopenharmony_ci int fd; 258c2ecf20Sopenharmony_ci spinlock_t lock; 268c2ecf20Sopenharmony_ci struct list_head pending; 278c2ecf20Sopenharmony_ci struct list_head connections; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct port_dev { 318c2ecf20Sopenharmony_ci struct port_list *port; 328c2ecf20Sopenharmony_ci int helper_pid; 338c2ecf20Sopenharmony_ci int telnetd_pid; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct connection { 378c2ecf20Sopenharmony_ci struct list_head list; 388c2ecf20Sopenharmony_ci int fd; 398c2ecf20Sopenharmony_ci int helper_pid; 408c2ecf20Sopenharmony_ci int socket[2]; 418c2ecf20Sopenharmony_ci int telnetd_pid; 428c2ecf20Sopenharmony_ci struct port_list *port; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic irqreturn_t pipe_interrupt(int irq, void *data) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct connection *conn = data; 488c2ecf20Sopenharmony_ci int fd; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci fd = os_rcv_fd(conn->socket[0], &conn->helper_pid); 518c2ecf20Sopenharmony_ci if (fd < 0) { 528c2ecf20Sopenharmony_ci if (fd == -EAGAIN) 538c2ecf20Sopenharmony_ci return IRQ_NONE; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n", 568c2ecf20Sopenharmony_ci -fd); 578c2ecf20Sopenharmony_ci os_close_file(conn->fd); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci list_del(&conn->list); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci conn->fd = fd; 638c2ecf20Sopenharmony_ci list_add(&conn->list, &conn->port->connections); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci complete(&conn->port->done); 668c2ecf20Sopenharmony_ci return IRQ_HANDLED; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define NO_WAITER_MSG \ 708c2ecf20Sopenharmony_ci "****\n" \ 718c2ecf20Sopenharmony_ci "There are currently no UML consoles waiting for port connections.\n" \ 728c2ecf20Sopenharmony_ci "Either disconnect from one to make it available or activate some more\n" \ 738c2ecf20Sopenharmony_ci "by enabling more consoles in the UML /etc/inittab.\n" \ 748c2ecf20Sopenharmony_ci "****\n" 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int port_accept(struct port_list *port) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct connection *conn; 798c2ecf20Sopenharmony_ci int fd, socket[2], pid; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci fd = port_connection(port->fd, socket, &pid); 828c2ecf20Sopenharmony_ci if (fd < 0) { 838c2ecf20Sopenharmony_ci if (fd != -EAGAIN) 848c2ecf20Sopenharmony_ci printk(KERN_ERR "port_accept : port_connection " 858c2ecf20Sopenharmony_ci "returned %d\n", -fd); 868c2ecf20Sopenharmony_ci goto out; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci conn = kmalloc(sizeof(*conn), GFP_ATOMIC); 908c2ecf20Sopenharmony_ci if (conn == NULL) { 918c2ecf20Sopenharmony_ci printk(KERN_ERR "port_accept : failed to allocate " 928c2ecf20Sopenharmony_ci "connection\n"); 938c2ecf20Sopenharmony_ci goto out_close; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci *conn = ((struct connection) 968c2ecf20Sopenharmony_ci { .list = LIST_HEAD_INIT(conn->list), 978c2ecf20Sopenharmony_ci .fd = fd, 988c2ecf20Sopenharmony_ci .socket = { socket[0], socket[1] }, 998c2ecf20Sopenharmony_ci .telnetd_pid = pid, 1008c2ecf20Sopenharmony_ci .port = port }); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, 1038c2ecf20Sopenharmony_ci IRQF_SHARED, "telnetd", conn)) { 1048c2ecf20Sopenharmony_ci printk(KERN_ERR "port_accept : failed to get IRQ for " 1058c2ecf20Sopenharmony_ci "telnetd\n"); 1068c2ecf20Sopenharmony_ci goto out_free; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (atomic_read(&port->wait_count) == 0) { 1108c2ecf20Sopenharmony_ci os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG)); 1118c2ecf20Sopenharmony_ci printk(KERN_ERR "No one waiting for port\n"); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci list_add(&conn->list, &port->pending); 1148c2ecf20Sopenharmony_ci return 1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci out_free: 1178c2ecf20Sopenharmony_ci kfree(conn); 1188c2ecf20Sopenharmony_ci out_close: 1198c2ecf20Sopenharmony_ci os_close_file(fd); 1208c2ecf20Sopenharmony_ci os_kill_process(pid, 1); 1218c2ecf20Sopenharmony_ci out: 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ports_mutex); 1268c2ecf20Sopenharmony_cistatic LIST_HEAD(ports); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void port_work_proc(struct work_struct *unused) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct port_list *port; 1318c2ecf20Sopenharmony_ci struct list_head *ele; 1328c2ecf20Sopenharmony_ci unsigned long flags; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci local_irq_save(flags); 1358c2ecf20Sopenharmony_ci list_for_each(ele, &ports) { 1368c2ecf20Sopenharmony_ci port = list_entry(ele, struct port_list, list); 1378c2ecf20Sopenharmony_ci if (!port->has_connection) 1388c2ecf20Sopenharmony_ci continue; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci while (port_accept(port)) 1418c2ecf20Sopenharmony_ci ; 1428c2ecf20Sopenharmony_ci port->has_connection = 0; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci local_irq_restore(flags); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ciDECLARE_WORK(port_work, port_work_proc); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic irqreturn_t port_interrupt(int irq, void *data) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct port_list *port = data; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci port->has_connection = 1; 1548c2ecf20Sopenharmony_ci schedule_work(&port_work); 1558c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_civoid *port_data(int port_num) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct list_head *ele; 1618c2ecf20Sopenharmony_ci struct port_list *port; 1628c2ecf20Sopenharmony_ci struct port_dev *dev = NULL; 1638c2ecf20Sopenharmony_ci int fd; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_lock(&ports_mutex); 1668c2ecf20Sopenharmony_ci list_for_each(ele, &ports) { 1678c2ecf20Sopenharmony_ci port = list_entry(ele, struct port_list, list); 1688c2ecf20Sopenharmony_ci if (port->port == port_num) 1698c2ecf20Sopenharmony_ci goto found; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci port = kmalloc(sizeof(struct port_list), GFP_KERNEL); 1728c2ecf20Sopenharmony_ci if (port == NULL) { 1738c2ecf20Sopenharmony_ci printk(KERN_ERR "Allocation of port list failed\n"); 1748c2ecf20Sopenharmony_ci goto out; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci fd = port_listen_fd(port_num); 1788c2ecf20Sopenharmony_ci if (fd < 0) { 1798c2ecf20Sopenharmony_ci printk(KERN_ERR "binding to port %d failed, errno = %d\n", 1808c2ecf20Sopenharmony_ci port_num, -fd); 1818c2ecf20Sopenharmony_ci goto out_free; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, 1858c2ecf20Sopenharmony_ci IRQF_SHARED, "port", port)) { 1868c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num); 1878c2ecf20Sopenharmony_ci goto out_close; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci *port = ((struct port_list) 1918c2ecf20Sopenharmony_ci { .list = LIST_HEAD_INIT(port->list), 1928c2ecf20Sopenharmony_ci .wait_count = ATOMIC_INIT(0), 1938c2ecf20Sopenharmony_ci .has_connection = 0, 1948c2ecf20Sopenharmony_ci .port = port_num, 1958c2ecf20Sopenharmony_ci .fd = fd, 1968c2ecf20Sopenharmony_ci .pending = LIST_HEAD_INIT(port->pending), 1978c2ecf20Sopenharmony_ci .connections = LIST_HEAD_INIT(port->connections) }); 1988c2ecf20Sopenharmony_ci spin_lock_init(&port->lock); 1998c2ecf20Sopenharmony_ci init_completion(&port->done); 2008c2ecf20Sopenharmony_ci list_add(&port->list, &ports); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci found: 2038c2ecf20Sopenharmony_ci dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL); 2048c2ecf20Sopenharmony_ci if (dev == NULL) { 2058c2ecf20Sopenharmony_ci printk(KERN_ERR "Allocation of port device entry failed\n"); 2068c2ecf20Sopenharmony_ci goto out; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci *dev = ((struct port_dev) { .port = port, 2108c2ecf20Sopenharmony_ci .helper_pid = -1, 2118c2ecf20Sopenharmony_ci .telnetd_pid = -1 }); 2128c2ecf20Sopenharmony_ci goto out; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci out_close: 2158c2ecf20Sopenharmony_ci os_close_file(fd); 2168c2ecf20Sopenharmony_ci out_free: 2178c2ecf20Sopenharmony_ci kfree(port); 2188c2ecf20Sopenharmony_ci out: 2198c2ecf20Sopenharmony_ci mutex_unlock(&ports_mutex); 2208c2ecf20Sopenharmony_ci return dev; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ciint port_wait(void *data) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct port_dev *dev = data; 2268c2ecf20Sopenharmony_ci struct connection *conn; 2278c2ecf20Sopenharmony_ci struct port_list *port = dev->port; 2288c2ecf20Sopenharmony_ci int fd; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci atomic_inc(&port->wait_count); 2318c2ecf20Sopenharmony_ci while (1) { 2328c2ecf20Sopenharmony_ci fd = -ERESTARTSYS; 2338c2ecf20Sopenharmony_ci if (wait_for_completion_interruptible(&port->done)) 2348c2ecf20Sopenharmony_ci goto out; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci spin_lock(&port->lock); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci conn = list_entry(port->connections.next, struct connection, 2398c2ecf20Sopenharmony_ci list); 2408c2ecf20Sopenharmony_ci list_del(&conn->list); 2418c2ecf20Sopenharmony_ci spin_unlock(&port->lock); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci os_shutdown_socket(conn->socket[0], 1, 1); 2448c2ecf20Sopenharmony_ci os_close_file(conn->socket[0]); 2458c2ecf20Sopenharmony_ci os_shutdown_socket(conn->socket[1], 1, 1); 2468c2ecf20Sopenharmony_ci os_close_file(conn->socket[1]); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* This is done here because freeing an IRQ can't be done 2498c2ecf20Sopenharmony_ci * within the IRQ handler. So, pipe_interrupt always ups 2508c2ecf20Sopenharmony_ci * the semaphore regardless of whether it got a successful 2518c2ecf20Sopenharmony_ci * connection. Then we loop here throwing out failed 2528c2ecf20Sopenharmony_ci * connections until a good one is found. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci um_free_irq(TELNETD_IRQ, conn); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (conn->fd >= 0) 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci os_close_file(conn->fd); 2598c2ecf20Sopenharmony_ci kfree(conn); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci fd = conn->fd; 2638c2ecf20Sopenharmony_ci dev->helper_pid = conn->helper_pid; 2648c2ecf20Sopenharmony_ci dev->telnetd_pid = conn->telnetd_pid; 2658c2ecf20Sopenharmony_ci kfree(conn); 2668c2ecf20Sopenharmony_ci out: 2678c2ecf20Sopenharmony_ci atomic_dec(&port->wait_count); 2688c2ecf20Sopenharmony_ci return fd; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_civoid port_remove_dev(void *d) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct port_dev *dev = d; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (dev->helper_pid != -1) 2768c2ecf20Sopenharmony_ci os_kill_process(dev->helper_pid, 0); 2778c2ecf20Sopenharmony_ci if (dev->telnetd_pid != -1) 2788c2ecf20Sopenharmony_ci os_kill_process(dev->telnetd_pid, 1); 2798c2ecf20Sopenharmony_ci dev->helper_pid = -1; 2808c2ecf20Sopenharmony_ci dev->telnetd_pid = -1; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_civoid port_kern_free(void *d) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct port_dev *dev = d; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci port_remove_dev(dev); 2888c2ecf20Sopenharmony_ci kfree(dev); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic void free_port(void) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct list_head *ele; 2948c2ecf20Sopenharmony_ci struct port_list *port; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci list_for_each(ele, &ports) { 2978c2ecf20Sopenharmony_ci port = list_entry(ele, struct port_list, list); 2988c2ecf20Sopenharmony_ci free_irq_by_fd(port->fd); 2998c2ecf20Sopenharmony_ci os_close_file(port->fd); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci__uml_exitcall(free_port); 304