blob: d5000aa0286448d92ebe262b57ded0e851d5e57a [file] [log] [blame]
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -07001/*
2 * xen console driver interface to hvc_console.c
3 *
4 * (c) 2007 Gerd Hoffmann <kraxel@suse.de>
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, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <linux/console.h>
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/init.h>
25#include <linux/types.h>
26
Stefano Stabellinieb5ef072012-01-27 18:31:36 +000027#include <asm/io.h>
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -070028#include <asm/xen/hypervisor.h>
Jeremy Fitzhardinge1ccbf532009-10-06 15:11:14 -070029
30#include <xen/xen.h>
Stefano Stabellinieb5ef072012-01-27 18:31:36 +000031#include <xen/interface/xen.h>
32#include <xen/hvm.h>
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -070033#include <xen/page.h>
34#include <xen/events.h>
35#include <xen/interface/io/console.h>
36#include <xen/hvc-console.h>
37
38#include "hvc_console.h"
39
40#define HVC_COOKIE 0x58656e /* "Xen" in hex */
41
42static struct hvc_struct *hvc;
43static int xencons_irq;
44
45/* ------------------------------------------------------------------ */
46
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +010047static unsigned long console_pfn = ~0ul;
Stefano Stabellinieb5ef072012-01-27 18:31:36 +000048static unsigned int console_evtchn = ~0ul;
49static struct xencons_interface *xencons_if = NULL;
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +010050
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -070051static inline struct xencons_interface *xencons_interface(void)
52{
Stefano Stabellinieb5ef072012-01-27 18:31:36 +000053 if (xencons_if != NULL)
54 return xencons_if;
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +010055 if (console_pfn == ~0ul)
56 return mfn_to_virt(xen_start_info->console.domU.mfn);
57 else
58 return __va(console_pfn << PAGE_SHIFT);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -070059}
60
61static inline void notify_daemon(void)
62{
63 /* Use evtchn: this is called early, before irq is set up. */
Stefano Stabellinieb5ef072012-01-27 18:31:36 +000064 if (console_evtchn == ~0ul)
65 notify_remote_via_evtchn(xen_start_info->console.domU.evtchn);
66 else
67 notify_remote_via_evtchn(console_evtchn);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -070068}
69
Jeremy Fitzhardinge7825cf12009-10-20 15:28:21 +090070static int __write_console(const char *data, int len)
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -070071{
72 struct xencons_interface *intf = xencons_interface();
73 XENCONS_RING_IDX cons, prod;
74 int sent = 0;
75
76 cons = intf->out_cons;
77 prod = intf->out_prod;
78 mb(); /* update queue values before going on */
79 BUG_ON((prod - cons) > sizeof(intf->out));
80
81 while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
82 intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
83
84 wmb(); /* write ring before updating pointer */
85 intf->out_prod = prod;
86
Jeremy Fitzhardinge403a85f2010-10-14 11:38:47 -070087 if (sent)
88 notify_daemon();
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -070089 return sent;
90}
91
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +010092static int domU_write_console(uint32_t vtermno, const char *data, int len)
Jeremy Fitzhardinge7825cf12009-10-20 15:28:21 +090093{
94 int ret = len;
95
96 /*
97 * Make sure the whole buffer is emitted, polling if
98 * necessary. We don't ever want to rely on the hvc daemon
99 * because the most interesting console output is when the
100 * kernel is crippled.
101 */
102 while (len) {
103 int sent = __write_console(data, len);
104
105 data += sent;
106 len -= sent;
107
108 if (unlikely(len))
109 HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
110 }
111
112 return ret;
113}
114
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100115static int domU_read_console(uint32_t vtermno, char *buf, int len)
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700116{
117 struct xencons_interface *intf = xencons_interface();
118 XENCONS_RING_IDX cons, prod;
119 int recv = 0;
120
121 cons = intf->in_cons;
122 prod = intf->in_prod;
123 mb(); /* get pointers before reading ring */
124 BUG_ON((prod - cons) > sizeof(intf->in));
125
126 while (cons != prod && recv < len)
127 buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
128
129 mb(); /* read ring before consuming */
130 intf->in_cons = cons;
131
132 notify_daemon();
133 return recv;
134}
135
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100136static struct hv_ops domU_hvc_ops = {
137 .get_chars = domU_read_console,
138 .put_chars = domU_write_console,
Christian Borntraeger611e0972008-06-20 15:24:08 +0200139 .notifier_add = notifier_add_irq,
140 .notifier_del = notifier_del_irq,
Hendrik Bruecknerfc362e22008-10-13 23:12:48 +0000141 .notifier_hangup = notifier_hangup_irq,
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700142};
143
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100144static int dom0_read_console(uint32_t vtermno, char *buf, int len)
145{
146 return HYPERVISOR_console_io(CONSOLEIO_read, len, buf);
147}
148
149/*
150 * Either for a dom0 to write to the system console, or a domU with a
151 * debug version of Xen
152 */
153static int dom0_write_console(uint32_t vtermno, const char *str, int len)
154{
155 int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str);
156 if (rc < 0)
157 return 0;
158
159 return len;
160}
161
162static struct hv_ops dom0_hvc_ops = {
163 .get_chars = dom0_read_console,
164 .put_chars = dom0_write_console,
165 .notifier_add = notifier_add_irq,
166 .notifier_del = notifier_del_irq,
167 .notifier_hangup = notifier_hangup_irq,
168};
169
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000170static int xen_hvm_console_init(void)
171{
172 int r;
173 uint64_t v = 0;
174 unsigned long mfn;
175
176 if (!xen_hvm_domain())
177 return -ENODEV;
178
179 if (xencons_if != NULL)
180 return -EBUSY;
181
182 r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
183 if (r < 0)
184 return -ENODEV;
185 console_evtchn = v;
186 hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v);
187 if (r < 0)
188 return -ENODEV;
189 mfn = v;
190 xencons_if = ioremap(mfn << PAGE_SHIFT, PAGE_SIZE);
191 if (xencons_if == NULL)
192 return -ENODEV;
193
194 return 0;
195}
196
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100197static int __init xen_hvc_init(void)
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700198{
199 struct hvc_struct *hp;
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100200 struct hv_ops *ops;
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000201 int r;
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700202
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000203 if (!xen_domain())
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +0100204 return -ENODEV;
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700205
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100206 if (xen_initial_domain()) {
207 ops = &dom0_hvc_ops;
208 xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0);
209 } else {
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100210 ops = &domU_hvc_ops;
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000211 if (xen_pv_domain()) {
212 if (!xen_start_info->console.domU.evtchn)
213 return -ENODEV;
214 console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn);
215 console_evtchn = xen_start_info->console.domU.evtchn;
216 } else {
217 r = xen_hvm_console_init();
218 if (r < 0)
219 return r;
220 }
221 xencons_irq = bind_evtchn_to_irq(console_evtchn);
222 if (xencons_irq < 0)
223 xencons_irq = 0; /* NO_IRQ */
224 else
225 irq_set_noprobe(xencons_irq);
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100226 }
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +0100227
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100228 hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700229 if (IS_ERR(hp))
230 return PTR_ERR(hp);
231
232 hvc = hp;
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +0100233
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700234 return 0;
235}
236
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +0100237void xen_console_resume(void)
238{
239 if (xencons_irq)
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000240 rebind_evtchn_irq(console_evtchn, xencons_irq);
Jeremy Fitzhardinge6b9b7322008-05-26 23:31:25 +0100241}
242
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100243static void __exit xen_hvc_fini(void)
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700244{
245 if (hvc)
246 hvc_remove(hvc);
247}
248
249static int xen_cons_init(void)
250{
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000251 const struct hv_ops *ops;
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100252
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000253 if (!xen_domain())
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700254 return 0;
255
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100256 if (xen_initial_domain())
257 ops = &dom0_hvc_ops;
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000258 else {
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100259 ops = &domU_hvc_ops;
260
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000261 if (xen_pv_domain())
262 console_evtchn = xen_start_info->console.domU.evtchn;
263 else
264 xen_hvm_console_init();
265 }
266
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100267 hvc_instantiate(HVC_COOKIE, 0, ops);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700268 return 0;
269}
270
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100271module_init(xen_hvc_init);
272module_exit(xen_hvc_fini);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700273console_initcall(xen_cons_init);
274
Jeremy Fitzhardinge0922abd2008-05-26 23:31:00 +0100275#ifdef CONFIG_EARLY_PRINTK
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700276static void xenboot_write_console(struct console *console, const char *string,
277 unsigned len)
278{
279 unsigned int linelen, off = 0;
280 const char *pos;
281
Stefano Stabellinieb5ef072012-01-27 18:31:36 +0000282 if (!xen_pv_domain())
283 return;
284
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100285 dom0_write_console(0, string, len);
Jeremy Fitzhardinge0922abd2008-05-26 23:31:00 +0100286
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100287 if (xen_initial_domain())
288 return;
289
290 domU_write_console(0, "(early) ", 8);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700291 while (off < len && NULL != (pos = strchr(string+off, '\n'))) {
292 linelen = pos-string+off;
293 if (off + linelen > len)
294 break;
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100295 domU_write_console(0, string+off, linelen);
296 domU_write_console(0, "\r\n", 2);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700297 off += linelen + 1;
298 }
299 if (off < len)
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100300 domU_write_console(0, string+off, len-off);
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700301}
302
303struct console xenboot_console = {
304 .name = "xenboot",
305 .write = xenboot_write_console,
Jeremy Fitzhardinge0922abd2008-05-26 23:31:00 +0100306 .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
Jeremy Fitzhardingeb536b4b2007-07-17 18:37:06 -0700307};
Jeremy Fitzhardinge0922abd2008-05-26 23:31:00 +0100308#endif /* CONFIG_EARLY_PRINTK */
Jeremy Fitzhardinge0acf10d2008-05-26 23:30:59 +0100309
310void xen_raw_console_write(const char *str)
311{
Jeremy Fitzhardinge4fe7d5a2010-09-02 16:17:06 +0100312 dom0_write_console(0, str, strlen(str));
Jeremy Fitzhardinge0acf10d2008-05-26 23:30:59 +0100313}
314
315void xen_raw_printk(const char *fmt, ...)
316{
317 static char buf[512];
318 va_list ap;
319
320 va_start(ap, fmt);
321 vsnprintf(buf, sizeof(buf), fmt, ap);
322 va_end(ap);
323
324 xen_raw_console_write(buf);
325}