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