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