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 39 static struct hvc_struct *hvc; 40 static int xencons_irq; 41 42 /* ------------------------------------------------------------------ */ 43 44 static unsigned long console_pfn = ~0ul; 45 46 static 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 54 static 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 60 static 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 82 static 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 105 static 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 126 static 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 134 static 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 */ 143 static 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 152 static 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 160 static 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 181 hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256); 182 if (IS_ERR(hp)) 183 return PTR_ERR(hp); 184 185 hvc = hp; 186 187 console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn); 188 189 return 0; 190 } 191 192 void xen_console_resume(void) 193 { 194 if (xencons_irq) 195 rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq); 196 } 197 198 static void __exit xen_hvc_fini(void) 199 { 200 if (hvc) 201 hvc_remove(hvc); 202 } 203 204 static int xen_cons_init(void) 205 { 206 struct hv_ops *ops; 207 208 if (!xen_pv_domain()) 209 return 0; 210 211 if (xen_initial_domain()) 212 ops = &dom0_hvc_ops; 213 else 214 ops = &domU_hvc_ops; 215 216 hvc_instantiate(HVC_COOKIE, 0, ops); 217 return 0; 218 } 219 220 module_init(xen_hvc_init); 221 module_exit(xen_hvc_fini); 222 console_initcall(xen_cons_init); 223 224 #ifdef CONFIG_EARLY_PRINTK 225 static void xenboot_write_console(struct console *console, const char *string, 226 unsigned len) 227 { 228 unsigned int linelen, off = 0; 229 const char *pos; 230 231 dom0_write_console(0, string, len); 232 233 if (xen_initial_domain()) 234 return; 235 236 domU_write_console(0, "(early) ", 8); 237 while (off < len && NULL != (pos = strchr(string+off, '\n'))) { 238 linelen = pos-string+off; 239 if (off + linelen > len) 240 break; 241 domU_write_console(0, string+off, linelen); 242 domU_write_console(0, "\r\n", 2); 243 off += linelen + 1; 244 } 245 if (off < len) 246 domU_write_console(0, string+off, len-off); 247 } 248 249 struct console xenboot_console = { 250 .name = "xenboot", 251 .write = xenboot_write_console, 252 .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, 253 }; 254 #endif /* CONFIG_EARLY_PRINTK */ 255 256 void xen_raw_console_write(const char *str) 257 { 258 dom0_write_console(0, str, strlen(str)); 259 } 260 261 void xen_raw_printk(const char *fmt, ...) 262 { 263 static char buf[512]; 264 va_list ap; 265 266 va_start(ap, fmt); 267 vsnprintf(buf, sizeof(buf), fmt, ap); 268 va_end(ap); 269 270 xen_raw_console_write(buf); 271 } 272