/****************************************************************************** * drivers/char/consoled.c * * A backend driver for Xen's PV console. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . * * Copyright (c) 2017 Citrix Systems Ltd. */ #include #include #include #include #include static struct xencons_interface *cons_ring; static DEFINE_SPINLOCK(rx_lock); void consoled_set_ring_addr(struct xencons_interface *ring) { cons_ring = ring; } struct xencons_interface *consoled_get_ring_addr(void) { return cons_ring; } #define BUF_SZ 255 static char buf[BUF_SZ + 1]; /* Receives characters from a domain's PV console */ size_t consoled_guest_rx(void) { size_t recv = 0, idx = 0; XENCONS_RING_IDX cons, prod; if ( !cons_ring ) return 0; spin_lock(&rx_lock); cons = cons_ring->out_cons; prod = ACCESS_ONCE(cons_ring->out_prod); /* * Latch pointers before accessing the ring. Included compiler barrier also * ensures that pointers are really read only once into local variables. */ smp_rmb(); ASSERT((prod - cons) <= sizeof(cons_ring->out)); /* Is the ring empty? */ if ( cons == prod ) goto out; while ( cons != prod ) { char c = cons_ring->out[MASK_XENCONS_IDX(cons++, cons_ring->out)]; buf[idx++] = c; recv++; if ( idx >= BUF_SZ ) { pv_console_puts(buf); idx = 0; } } if ( idx ) { buf[idx] = '\0'; pv_console_puts(buf); } /* No need for a mem barrier because every character was already consumed */ barrier(); ACCESS_ONCE(cons_ring->out_cons) = cons; pv_shim_inject_evtchn(pv_console_evtchn()); out: spin_unlock(&rx_lock); return recv; } /* Sends a character into a domain's PV console */ size_t consoled_guest_tx(char c) { size_t sent = 0; XENCONS_RING_IDX cons, prod; if ( !cons_ring ) return 0; cons = ACCESS_ONCE(cons_ring->in_cons); prod = cons_ring->in_prod; /* * Latch pointers before accessing the ring. Included compiler barrier also * ensures that pointers are really read only once into local variables. */ smp_rmb(); ASSERT((prod - cons) <= sizeof(cons_ring->in)); /* Is the ring out of space? */ if ( sizeof(cons_ring->in) - (prod - cons) == 0 ) goto notify; cons_ring->in[MASK_XENCONS_IDX(prod++, cons_ring->in)] = c; sent++; /* Write to the ring before updating the pointer */ smp_wmb(); ACCESS_ONCE(cons_ring->in_prod) = prod; notify: /* Always notify the guest: prevents receive path from getting stuck. */ pv_shim_inject_evtchn(pv_console_evtchn()); return sent; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */