/*D:300 * The Guest console driver * * This is a trivial console driver: we use lguest's DMA mechanism to send * bytes out, and register a DMA buffer to receive bytes in. It is assumed to * be present and available from the very beginning of boot. * * Writing console drivers is one of the few remaining Dark Arts in Linux. * Fortunately for us, the path of virtual consoles has been well-trodden by * the PowerPC folks, who wrote "hvc_console.c" to generically support any * virtual console. We use that infrastructure which only requires us to write * the basic put_chars and get_chars functions and call the right register * functions. :*/ /* Copyright (C) 2006 Rusty Russell, IBM Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "hvc_console.h" /*D:340 This is our single console input buffer, with associated "struct * lguest_dma" referring to it. Note the 0-terminated length array, and the * use of physical address for the buffer itself. */ static char inbuf[256]; static struct lguest_dma cons_input = { .used_len = 0, .addr[0] = __pa(inbuf), .len[0] = sizeof(inbuf), .len[1] = 0 }; /*D:310 The put_chars() callback is pretty straightforward. * * First we put the pointer and length in a "struct lguest_dma": we only have * one pointer, so we set the second length to 0. Then we use SEND_DMA to send * the data to (Host) buffers attached to the console key. Usually a device's * key is a physical address within the device's memory, but because the * console device doesn't have any associated physical memory, we use the * LGUEST_CONSOLE_DMA_KEY constant (aka 0). */ static int put_chars(u32 vtermno, const char *buf, int count) { struct lguest_dma dma; /* FIXME: DMA buffers in a "struct lguest_dma" are not allowed * to go over page boundaries. This never seems to happen, * but if it did we'd need to fix this code. */ dma.len[0] = count; dma.len[1] = 0; dma.addr[0] = __pa(buf); lguest_send_dma(LGUEST_CONSOLE_DMA_KEY, &dma); /* We're expected to return the amount of data we wrote: all of it. */ return count; } /*D:350 get_chars() is the callback from the hvc_console infrastructure when * an interrupt is received. * * Firstly we see if our buffer has been filled: if not, we return. The rest * of the code deals with the fact that the hvc_console() infrastructure only * asks us for 16 bytes at a time. We keep a "cons_offset" variable for * partially-read buffers. */ static int get_chars(u32 vtermno, char *buf, int count) { static int cons_offset; /* Nothing left to see here... */ if (!cons_input.used_len) return 0; /* You want more than we have to give? Well, try wanting less! */ if (cons_input.used_len - cons_offset < count) count = cons_input.used_len - cons_offset; /* Copy across to their buffer and increment offset. */ memcpy(buf, inbuf + cons_offset, count); cons_offset += count; /* Finished? Zero offset, and reset cons_input so Host will use it * again. */ if (cons_offset == cons_input.used_len) { cons_offset = 0; cons_input.used_len = 0; } return count; } /*:*/ static struct hv_ops lguest_cons = { .get_chars = get_chars, .put_chars = put_chars, }; /*D:320 Console drivers are initialized very early so boot messages can go * out. At this stage, the console is output-only. Our driver checks we're a * Guest, and if so hands hvc_instantiate() the console number (0), priority * (0), and the struct hv_ops containing the put_chars() function. */ static int __init cons_init(void) { if (strcmp(paravirt_ops.name, "lguest") != 0) return 0; return hvc_instantiate(0, 0, &lguest_cons); } console_initcall(cons_init); /*D:370 To set up and manage our virtual console, we call hvc_alloc() and * stash the result in the private pointer of the "struct lguest_device". * Since we never remove the console device we never need this pointer again, * but using ->private is considered good form, and you never know who's going * to copy your driver. * * Once the console is set up, we bind our input buffer ready for input. */ static int lguestcons_probe(struct lguest_device *lgdev) { int err; /* The first argument of hvc_alloc() is the virtual console number, so * we use zero. The second argument is the interrupt number. * * The third argument is a "struct hv_ops" containing the put_chars() * and get_chars() pointers. The final argument is the output buffer * size: we use 256 and expect the Host to have room for us to send * that much. */ lgdev->private = hvc_alloc(0, lgdev_irq(lgdev), &lguest_cons, 256); if (IS_ERR(lgdev->private)) return PTR_ERR(lgdev->private); /* We bind a single DMA buffer at key LGUEST_CONSOLE_DMA_KEY. * "cons_input" is that statically-initialized global DMA buffer we saw * above, and we also give the interrupt we want. */ err = lguest_bind_dma(LGUEST_CONSOLE_DMA_KEY, &cons_input, 1, lgdev_irq(lgdev)); if (err) printk("lguest console: failed to bind buffer.\n"); return err; } /* Note the use of lgdev_irq() for the interrupt number. We tell hvc_alloc() * to expect input when this interrupt is triggered, and then tell * lguest_bind_dma() that is the interrupt to send us when input comes in. */ /*D:360 From now on the console driver follows standard Guest driver form: * register_lguest_driver() registers the device type and probe function, and * the probe function sets up the device. * * The standard "struct lguest_driver": */ static struct lguest_driver lguestcons_drv = { .name = "lguestcons", .owner = THIS_MODULE, .device_type = LGUEST_DEVICE_T_CONSOLE, .probe = lguestcons_probe, }; /* The standard init function */ static int __init hvc_lguest_init(void) { return register_lguest_driver(&lguestcons_drv); } module_init(hvc_lguest_init);