1/* 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 27#include <asm/xen/hypervisor.h> 28 29#include <xen/xen.h> 30#include <xen/page.h> 31#include <xen/events.h> 32#include <xen/interface/io/console.h> 33#include <xen/hvc-console.h> 34 35#include "hvc_console.h" 36 37#define HVC_COOKIE 0x58656e /* "Xen" in hex */ 38 39static struct hvc_struct *hvc; 40static int xencons_irq; 41 42/* ------------------------------------------------------------------ */ 43 44static unsigned long console_pfn = ~0ul; 45 46static inline struct xencons_interface *xencons_interface(void) 47{ 48 if (console_pfn == ~0ul) 49 return mfn_to_virt(xen_start_info->console.domU.mfn); 50 else 51 return __va(console_pfn << PAGE_SHIFT); 52} 53 54static inline void notify_daemon(void) 55{ 56 /* Use evtchn: this is called early, before irq is set up. */ 57 notify_remote_via_evtchn(xen_start_info->console.domU.evtchn); 58} 59 60static int __write_console(const char *data, int len) 61{ 62 struct xencons_interface *intf = xencons_interface(); 63 XENCONS_RING_IDX cons, prod; 64 int sent = 0; 65 66 cons = intf->out_cons; 67 prod = intf->out_prod; 68 mb(); /* update queue values before going on */ 69 BUG_ON((prod - cons) > sizeof(intf->out)); 70 71 while ((sent < len) && ((prod - cons) < sizeof(intf->out))) 72 intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; 73 74 wmb(); /* write ring before updating pointer */ 75 intf->out_prod = prod; 76 77 if (sent) 78 notify_daemon(); 79 return sent; 80} 81 82static int domU_write_console(uint32_t vtermno, const char *data, int len) 83{ 84 int ret = len; 85 86 /* 87 * Make sure the whole buffer is emitted, polling if 88 * necessary. We don't ever want to rely on the hvc daemon 89 * because the most interesting console output is when the 90 * kernel is crippled. 91 */ 92 while (len) { 93 int sent = __write_console(data, len); 94 95 data += sent; 96 len -= sent; 97 98 if (unlikely(len)) 99 HYPERVISOR_sched_op(SCHEDOP_yield, NULL); 100 } 101 102 return ret; 103} 104 105static int domU_read_console(uint32_t vtermno, char *buf, int len) 106{ 107 struct xencons_interface *intf = xencons_interface(); 108 XENCONS_RING_IDX cons, prod; 109 int recv = 0; 110 111 cons = intf->in_cons; 112 prod = intf->in_prod; 113 mb(); /* get pointers before reading ring */ 114 BUG_ON((prod - cons) > sizeof(intf->in)); 115 116 while (cons != prod && recv < len) 117 buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; 118 119 mb(); /* read ring before consuming */ 120 intf->in_cons = cons; 121 122 notify_daemon(); 123 return recv; 124} 125 126static struct hv_ops domU_hvc_ops = { 127 .get_chars = domU_read_console, 128 .put_chars = domU_write_console, 129 .notifier_add = notifier_add_irq, 130 .notifier_del = notifier_del_irq, 131 .notifier_hangup = notifier_hangup_irq, 132}; 133 134static int dom0_read_console(uint32_t vtermno, char *buf, int len) 135{ 136 return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); 137} 138 139/* 140 * Either for a dom0 to write to the system console, or a domU with a 141 * debug version of Xen 142 */ 143static int dom0_write_console(uint32_t vtermno, const char *str, int len) 144{ 145 int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); 146 if (rc < 0) 147 return 0; 148 149 return len; 150} 151 152static struct hv_ops dom0_hvc_ops = { 153 .get_chars = dom0_read_console, 154 .put_chars = dom0_write_console, 155 .notifier_add = notifier_add_irq, 156 .notifier_del = notifier_del_irq, 157 .notifier_hangup = notifier_hangup_irq, 158}; 159 160static int __init xen_hvc_init(void) 161{ 162 struct hvc_struct *hp; 163 struct hv_ops *ops; 164 165 if (!xen_pv_domain()) 166 return -ENODEV; 167 168 if (xen_initial_domain()) { 169 ops = &dom0_hvc_ops; 170 xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); 171 } else { 172 if (!xen_start_info->console.domU.evtchn) 173 return -ENODEV; 174 175 ops = &domU_hvc_ops; 176 xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); 177 } 178 if (xencons_irq < 0) 179 xencons_irq = 0; /* NO_IRQ */ 180 else 181 irq_set_noprobe(xencons_irq); 182 183 hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256); 184 if (IS_ERR(hp)) 185 return PTR_ERR(hp); 186 187 hvc = hp; 188 189 console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn); 190 191 return 0; 192} 193 194void xen_console_resume(void) 195{ 196 if (xencons_irq) 197 rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq); 198} 199 200static void __exit xen_hvc_fini(void) 201{ 202 if (hvc) 203 hvc_remove(hvc); 204} 205 206static int xen_cons_init(void) 207{ 208 struct hv_ops *ops; 209 210 if (!xen_pv_domain()) 211 return 0; 212 213 if (xen_initial_domain()) 214 ops = &dom0_hvc_ops; 215 else 216 ops = &domU_hvc_ops; 217 218 hvc_instantiate(HVC_COOKIE, 0, ops); 219 return 0; 220} 221 222module_init(xen_hvc_init); 223module_exit(xen_hvc_fini); 224console_initcall(xen_cons_init); 225 226#ifdef CONFIG_EARLY_PRINTK 227static void xenboot_write_console(struct console *console, const char *string, 228 unsigned len) 229{ 230 unsigned int linelen, off = 0; 231 const char *pos; 232 233 dom0_write_console(0, string, len); 234 235 if (xen_initial_domain()) 236 return; 237 238 domU_write_console(0, "(early) ", 8); 239 while (off < len && NULL != (pos = strchr(string+off, '\n'))) { 240 linelen = pos-string+off; 241 if (off + linelen > len) 242 break; 243 domU_write_console(0, string+off, linelen); 244 domU_write_console(0, "\r\n", 2); 245 off += linelen + 1; 246 } 247 if (off < len) 248 domU_write_console(0, string+off, len-off); 249} 250 251struct console xenboot_console = { 252 .name = "xenboot", 253 .write = xenboot_write_console, 254 .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, 255}; 256#endif /* CONFIG_EARLY_PRINTK */ 257 258void xen_raw_console_write(const char *str) 259{ 260 dom0_write_console(0, str, strlen(str)); 261} 262 263void xen_raw_printk(const char *fmt, ...) 264{ 265 static char buf[512]; 266 va_list ap; 267 268 va_start(ap, fmt); 269 vsnprintf(buf, sizeof(buf), fmt, ap); 270 va_end(ap); 271 272 xen_raw_console_write(buf); 273} 274