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)32 void consoled_set_ring_addr(struct xencons_interface *ring)
33 {
34     cons_ring = ring;
35 }
36 
consoled_get_ring_addr(void)37 struct 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)46 size_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, BUF_SZ);
81             idx = 0;
82         }
83     }
84 
85     if ( idx )
86         pv_console_puts(buf, idx);
87 
88     /* No need for a mem barrier because every character was already consumed */
89     barrier();
90     ACCESS_ONCE(cons_ring->out_cons) = cons;
91     pv_shim_inject_evtchn(pv_console_evtchn());
92 
93  out:
94     spin_unlock(&rx_lock);
95 
96     return recv;
97 }
98 
99 /* Sends a character into a domain's PV console */
consoled_guest_tx(char c)100 size_t consoled_guest_tx(char c)
101 {
102     size_t sent = 0;
103     XENCONS_RING_IDX cons, prod;
104 
105     if ( !cons_ring )
106         return 0;
107 
108     cons = ACCESS_ONCE(cons_ring->in_cons);
109     prod = cons_ring->in_prod;
110 
111     /*
112      * Latch pointers before accessing the ring. Included compiler barrier also
113      * ensures that pointers are really read only once into local variables.
114      */
115     smp_rmb();
116 
117     ASSERT((prod - cons) <= sizeof(cons_ring->in));
118 
119     /* Is the ring out of space? */
120     if ( sizeof(cons_ring->in) - (prod - cons) == 0 )
121         goto notify;
122 
123     cons_ring->in[MASK_XENCONS_IDX(prod++, cons_ring->in)] = c;
124     sent++;
125 
126     /* Write to the ring before updating the pointer */
127     smp_wmb();
128     ACCESS_ONCE(cons_ring->in_prod) = prod;
129 
130  notify:
131     /* Always notify the guest: prevents receive path from getting stuck. */
132     pv_shim_inject_evtchn(pv_console_evtchn());
133 
134     return sent;
135 }
136 
137 /*
138  * Local variables:
139  * mode: C
140  * c-file-style: "BSD"
141  * c-basic-offset: 4
142  * tab-width: 4
143  * indent-tabs-mode: nil
144  * End:
145  */
146