1 /****************************************************************************** 2 * drivers/char/consoled.c 3 * 4 * A backend driver for Xen's PV console. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; If not, see <http://www.gnu.org/licenses/>. 18 * 19 * Copyright (c) 2017 Citrix Systems Ltd. 20 */ 21 22 #include <xen/lib.h> 23 #include <xen/event.h> 24 #include <xen/pv_console.h> 25 #include <xen/consoled.h> 26 27 #include <asm/guest.h> 28 29 static struct xencons_interface *cons_ring; 30 static DEFINE_SPINLOCK(rx_lock); 31 consoled_set_ring_addr(struct xencons_interface * ring)32void consoled_set_ring_addr(struct xencons_interface *ring) 33 { 34 cons_ring = ring; 35 } 36 consoled_get_ring_addr(void)37struct xencons_interface *consoled_get_ring_addr(void) 38 { 39 return cons_ring; 40 } 41 42 #define BUF_SZ 255 43 static char buf[BUF_SZ + 1]; 44 45 /* Receives characters from a domain's PV console */ consoled_guest_rx(void)46size_t consoled_guest_rx(void) 47 { 48 size_t recv = 0, idx = 0; 49 XENCONS_RING_IDX cons, prod; 50 51 if ( !cons_ring ) 52 return 0; 53 54 spin_lock(&rx_lock); 55 56 cons = cons_ring->out_cons; 57 prod = ACCESS_ONCE(cons_ring->out_prod); 58 59 /* 60 * Latch pointers before accessing the ring. Included compiler barrier also 61 * ensures that pointers are really read only once into local variables. 62 */ 63 smp_rmb(); 64 65 ASSERT((prod - cons) <= sizeof(cons_ring->out)); 66 67 /* Is the ring empty? */ 68 if ( cons == prod ) 69 goto out; 70 71 while ( cons != prod ) 72 { 73 char c = cons_ring->out[MASK_XENCONS_IDX(cons++, cons_ring->out)]; 74 75 buf[idx++] = c; 76 recv++; 77 78 if ( idx >= BUF_SZ ) 79 { 80 pv_console_puts(buf); 81 idx = 0; 82 } 83 } 84 85 if ( idx ) 86 { 87 buf[idx] = '\0'; 88 pv_console_puts(buf); 89 } 90 91 /* No need for a mem barrier because every character was already consumed */ 92 barrier(); 93 ACCESS_ONCE(cons_ring->out_cons) = cons; 94 pv_shim_inject_evtchn(pv_console_evtchn()); 95 96 out: 97 spin_unlock(&rx_lock); 98 99 return recv; 100 } 101 102 /* Sends a character into a domain's PV console */ consoled_guest_tx(char c)103size_t consoled_guest_tx(char c) 104 { 105 size_t sent = 0; 106 XENCONS_RING_IDX cons, prod; 107 108 if ( !cons_ring ) 109 return 0; 110 111 cons = ACCESS_ONCE(cons_ring->in_cons); 112 prod = cons_ring->in_prod; 113 114 /* 115 * Latch pointers before accessing the ring. Included compiler barrier also 116 * ensures that pointers are really read only once into local variables. 117 */ 118 smp_rmb(); 119 120 ASSERT((prod - cons) <= sizeof(cons_ring->in)); 121 122 /* Is the ring out of space? */ 123 if ( sizeof(cons_ring->in) - (prod - cons) == 0 ) 124 goto notify; 125 126 cons_ring->in[MASK_XENCONS_IDX(prod++, cons_ring->in)] = c; 127 sent++; 128 129 /* Write to the ring before updating the pointer */ 130 smp_wmb(); 131 ACCESS_ONCE(cons_ring->in_prod) = prod; 132 133 notify: 134 /* Always notify the guest: prevents receive path from getting stuck. */ 135 pv_shim_inject_evtchn(pv_console_evtchn()); 136 137 return sent; 138 } 139 140 /* 141 * Local variables: 142 * mode: C 143 * c-file-style: "BSD" 144 * c-basic-offset: 4 145 * tab-width: 4 146 * indent-tabs-mode: nil 147 * End: 148 */ 149