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);
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)103 size_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