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