1 /******************************************************************************
2 * drivers/char/xen_pv_console.c
3 *
4 * A frontend driver for Xen's PV console.
5 * Can be used when Xen is running on top of Xen in pv-in-pvh mode.
6 * (Linux's name for this is hvc console)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; If not, see <http://www.gnu.org/licenses/>.
20 *
21 * Copyright (c) 2017 Citrix Systems Ltd.
22 */
23
24 #include <xen/lib.h>
25 #include <xen/hypercall.h>
26 #include <xen/pv_console.h>
27
28 #include <asm/fixmap.h>
29 #include <asm/guest.h>
30
31 #include <public/io/console.h>
32
33 static struct xencons_interface *cons_ring;
34 static evtchn_port_t cons_evtchn;
35 static serial_rx_fn cons_rx_handler;
36 static DEFINE_SPINLOCK(tx_lock);
37
38 bool pv_console;
39
pv_console_init(void)40 void pv_console_init(void)
41 {
42 long r;
43 uint64_t raw_pfn = 0, raw_evtchn = 0;
44
45 if ( !xen_guest )
46 {
47 printk("PV console init failed: xen_guest mode is not active!\n");
48 return;
49 }
50
51 r = xen_hypercall_hvm_get_param(HVM_PARAM_CONSOLE_PFN, &raw_pfn);
52 if ( r < 0 )
53 goto error;
54
55 r = xen_hypercall_hvm_get_param(HVM_PARAM_CONSOLE_EVTCHN, &raw_evtchn);
56 if ( r < 0 )
57 goto error;
58
59 set_fixmap(FIX_PV_CONSOLE, raw_pfn << PAGE_SHIFT);
60 cons_ring = (struct xencons_interface *)fix_to_virt(FIX_PV_CONSOLE);
61 cons_evtchn = raw_evtchn;
62
63 printk("Initialised PV console at 0x%p with pfn %#lx and evtchn %#x\n",
64 cons_ring, raw_pfn, cons_evtchn);
65 pv_console = true;
66
67 return;
68
69 error:
70 printk("Couldn't initialise PV console\n");
71 }
72
pv_console_set_rx_handler(serial_rx_fn fn)73 void __init pv_console_set_rx_handler(serial_rx_fn fn)
74 {
75 cons_rx_handler = fn;
76 }
77
pv_console_init_postirq(void)78 void __init pv_console_init_postirq(void)
79 {
80 if ( !cons_ring )
81 return;
82
83 xen_hypercall_evtchn_unmask(cons_evtchn);
84 }
85
notify_daemon(void)86 static void notify_daemon(void)
87 {
88 xen_hypercall_evtchn_send(cons_evtchn);
89 }
90
pv_console_evtchn(void)91 evtchn_port_t pv_console_evtchn(void)
92 {
93 return cons_evtchn;
94 }
95
pv_console_rx(struct cpu_user_regs * regs)96 size_t pv_console_rx(struct cpu_user_regs *regs)
97 {
98 char c;
99 XENCONS_RING_IDX cons, prod;
100 size_t recv = 0;
101
102 if ( !cons_ring )
103 return 0;
104
105 prod = ACCESS_ONCE(cons_ring->in_prod);
106 cons = cons_ring->in_cons;
107
108 /*
109 * Latch pointers before accessing the ring. Included compiler barrier also
110 * ensures that pointers are really read only once into local variables.
111 */
112 smp_rmb();
113
114 ASSERT((prod - cons) <= sizeof(cons_ring->in));
115
116 while ( cons != prod )
117 {
118 c = cons_ring->in[MASK_XENCONS_IDX(cons++, cons_ring->in)];
119 if ( cons_rx_handler )
120 cons_rx_handler(c, regs);
121 recv++;
122 }
123
124 /* No need for a mem barrier because every character was already consumed */
125 barrier();
126 ACCESS_ONCE(cons_ring->in_cons) = cons;
127 notify_daemon();
128
129 return recv;
130 }
131
pv_ring_puts(const char * buf)132 static size_t pv_ring_puts(const char *buf)
133 {
134 XENCONS_RING_IDX cons, prod;
135 size_t sent = 0, avail;
136 bool put_r = false;
137
138 while ( buf[sent] != '\0' || put_r )
139 {
140 cons = ACCESS_ONCE(cons_ring->out_cons);
141 prod = cons_ring->out_prod;
142
143 /*
144 * Latch pointers before accessing the ring. Included compiler barrier
145 * ensures that pointers are really read only once into local variables.
146 */
147 smp_rmb();
148
149 ASSERT((prod - cons) <= sizeof(cons_ring->out));
150 avail = sizeof(cons_ring->out) - (prod - cons);
151
152 if ( avail == 0 )
153 {
154 /* Wait for xenconsoled to consume our output */
155 xen_hypercall_sched_op(SCHEDOP_yield, NULL);
156 continue;
157 }
158
159 while ( avail && (buf[sent] != '\0' || put_r) )
160 {
161 if ( put_r )
162 {
163 cons_ring->out[MASK_XENCONS_IDX(prod++, cons_ring->out)] = '\r';
164 put_r = false;
165 }
166 else
167 {
168 cons_ring->out[MASK_XENCONS_IDX(prod++, cons_ring->out)] =
169 buf[sent];
170
171 /* Send '\r' for every '\n' */
172 if ( buf[sent] == '\n' )
173 put_r = true;
174 sent++;
175 }
176 avail--;
177 }
178
179 /* Write to the ring before updating the pointer */
180 smp_wmb();
181 ACCESS_ONCE(cons_ring->out_prod) = prod;
182 notify_daemon();
183 }
184
185 return sent;
186 }
187
pv_console_puts(const char * buf)188 void pv_console_puts(const char *buf)
189 {
190 unsigned long flags;
191
192 if ( !cons_ring )
193 return;
194
195 spin_lock_irqsave(&tx_lock, flags);
196 pv_ring_puts(buf);
197 spin_unlock_irqrestore(&tx_lock, flags);
198 }
199
200 /*
201 * Local variables:
202 * mode: C
203 * c-file-style: "BSD"
204 * c-basic-offset: 4
205 * tab-width: 4
206 * indent-tabs-mode: nil
207 * End:
208 */
209