2007-10-22 05:03:39 +04:00
/*D:300
* The Guest console driver
*
* 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 .
: */
/*M:002 The console can be flooded: while the Guest is processing input the
* Host can send more . Buffering in the Host could alleviate this , but it is a
* difficult problem in general . : */
/* Copyright (C) 2006, 2007 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 <linux/err.h>
# include <linux/init.h>
# include <linux/virtio.h>
# include <linux/virtio_console.h>
# include "hvc_console.h"
/*D:340 These represent our input and output console queues, and the virtio
* operations for them . */
static struct virtqueue * in_vq , * out_vq ;
static struct virtio_device * vdev ;
/* This is our input buffer, and how much data is left in it. */
static unsigned int in_len ;
static char * in , * inbuf ;
/* The operations for our console. */
static struct hv_ops virtio_cons ;
2008-06-20 17:24:15 +04:00
/* The hvc device */
static struct hvc_struct * hvc ;
2007-10-22 05:03:39 +04:00
/*D:310 The put_chars() callback is pretty straightforward.
*
* We turn the characters into a scatter - gather list , add it to the output
* queue and then kick the Host . Then we sit here waiting for it to finish :
* inefficient in theory , but in practice implementations will do it
* immediately ( lguest ' s Launcher does ) . */
static int put_chars ( u32 vtermno , const char * buf , int count )
{
struct scatterlist sg [ 1 ] ;
unsigned int len ;
/* This is a convenient routine to initialize a single-elem sg list */
sg_init_one ( sg , buf , count ) ;
/* add_buf wants a token to identify this buffer: we hand it any
* non - NULL pointer , since there ' s only ever one buffer . */
if ( out_vq - > vq_ops - > add_buf ( out_vq , sg , 1 , 0 , ( void * ) 1 ) = = 0 ) {
/* Tell Host to go! */
out_vq - > vq_ops - > kick ( out_vq ) ;
/* Chill out until it's done with the buffer. */
while ( ! out_vq - > vq_ops - > get_buf ( out_vq , & len ) )
cpu_relax ( ) ;
}
/* We're expected to return the amount of data we wrote: all of it. */
return count ;
}
/* Create a scatter-gather list representing our input buffer and put it in the
* queue . */
static void add_inbuf ( void )
{
struct scatterlist sg [ 1 ] ;
sg_init_one ( sg , inbuf , PAGE_SIZE ) ;
/* We should always be able to add one buffer to an empty queue. */
if ( in_vq - > vq_ops - > add_buf ( in_vq , sg , 0 , 1 , inbuf ) ! = 0 )
BUG ( ) ;
in_vq - > vq_ops - > kick ( in_vq ) ;
}
/*D:350 get_chars() is the callback from the hvc_console infrastructure when
* an interrupt is received .
*
* Most of the code deals with the fact that the hvc_console ( ) infrastructure
* only asks us for 16 bytes at a time . We keep in_offset and in_used fields
* for partially - filled buffers . */
static int get_chars ( u32 vtermno , char * buf , int count )
{
/* If we don't have an input queue yet, we can't get input. */
BUG_ON ( ! in_vq ) ;
/* No buffer? Try to get one. */
if ( ! in_len ) {
in = in_vq - > vq_ops - > get_buf ( in_vq , & in_len ) ;
if ( ! in )
return 0 ;
}
/* You want more than we have to give? Well, try wanting less! */
if ( in_len < count )
count = in_len ;
/* Copy across to their buffer and increment offset. */
memcpy ( buf , in , count ) ;
in + = count ;
in_len - = count ;
/* Finished? Re-register buffer so Host will use it again. */
if ( in_len = = 0 )
add_inbuf ( ) ;
return count ;
}
/*:*/
/*D:320 Console drivers are initialized very early so boot messages can go out,
* so we do things slightly differently from the generic virtio initialization
* of the net and block drivers .
*
* At this stage , the console is output - only . It ' s too early to set up a
* virtqueue , so we let the drivers do some boutique early - output thing . */
int __init virtio_cons_early_init ( int ( * put_chars ) ( u32 , const char * , int ) )
{
virtio_cons . put_chars = put_chars ;
return hvc_instantiate ( 0 , 0 , & virtio_cons ) ;
}
2008-06-20 17:24:15 +04:00
/*
* we support only one console , the hvc struct is a global var
* There is no need to do anything
*/
static int notifier_add_vio ( struct hvc_struct * hp , int data )
{
hp - > irq_requested = 1 ;
return 0 ;
}
static void notifier_del_vio ( struct hvc_struct * hp , int data )
{
hp - > irq_requested = 0 ;
}
static void hvc_handle_input ( struct virtqueue * vq )
{
if ( hvc_poll ( hvc ) )
hvc_kick ( ) ;
}
2007-10-22 05:03:39 +04:00
/*D:370 Once we're further in boot, we get probed like any other virtio device.
* At this stage we set up the output virtqueue .
*
* To set up and manage our virtual console , we call hvc_alloc ( ) . Since we
* never remove the console device we never need this pointer again .
*
* Finally we put our input buffer in the input queue , ready to receive . */
2007-11-06 01:51:01 +03:00
static int __devinit virtcons_probe ( struct virtio_device * dev )
2007-10-22 05:03:39 +04:00
{
int err ;
vdev = dev ;
/* This is the scratch page we use to receive console input */
inbuf = kmalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! inbuf ) {
err = - ENOMEM ;
goto fail ;
}
/* Find the input queue. */
/* FIXME: This is why we want to wean off hvc: we do nothing
* when input comes in . */
2008-06-20 17:24:15 +04:00
in_vq = vdev - > config - > find_vq ( vdev , 0 , hvc_handle_input ) ;
2007-10-22 05:03:39 +04:00
if ( IS_ERR ( in_vq ) ) {
err = PTR_ERR ( in_vq ) ;
goto free ;
}
2008-02-05 07:49:56 +03:00
out_vq = vdev - > config - > find_vq ( vdev , 1 , NULL ) ;
2007-10-22 05:03:39 +04:00
if ( IS_ERR ( out_vq ) ) {
err = PTR_ERR ( out_vq ) ;
goto free_in_vq ;
}
/* Start using the new console output. */
virtio_cons . get_chars = get_chars ;
virtio_cons . put_chars = put_chars ;
2008-06-20 17:24:15 +04:00
virtio_cons . notifier_add = notifier_add_vio ;
virtio_cons . notifier_del = notifier_del_vio ;
2007-10-22 05:03:39 +04:00
/* The first argument of hvc_alloc() is the virtual console number, so
2008-06-20 17:24:15 +04:00
* we use zero . The second argument is the parameter for the
* notification mechanism ( like irq number ) . We currently leave this
* as zero , virtqueues have implicit notifications .
2007-10-22 05:03:39 +04:00
*
* The third argument is a " struct hv_ops " containing the put_chars ( )
2008-06-20 17:24:15 +04:00
* get_chars ( ) , notifier_add ( ) and notifier_del ( ) pointers .
* The final argument is the output buffer size : we can do any size ,
* so we put PAGE_SIZE here . */
2007-10-22 05:03:39 +04:00
hvc = hvc_alloc ( 0 , 0 , & virtio_cons , PAGE_SIZE ) ;
if ( IS_ERR ( hvc ) ) {
err = PTR_ERR ( hvc ) ;
goto free_out_vq ;
}
/* Register the input buffer the first time. */
add_inbuf ( ) ;
return 0 ;
free_out_vq :
vdev - > config - > del_vq ( out_vq ) ;
free_in_vq :
vdev - > config - > del_vq ( in_vq ) ;
free :
kfree ( inbuf ) ;
fail :
return err ;
}
static struct virtio_device_id id_table [ ] = {
{ VIRTIO_ID_CONSOLE , VIRTIO_DEV_ANY_ID } ,
{ 0 } ,
} ;
static struct virtio_driver virtio_console = {
. driver . name = KBUILD_MODNAME ,
. driver . owner = THIS_MODULE ,
. id_table = id_table ,
. probe = virtcons_probe ,
} ;
static int __init init ( void )
{
return register_virtio_driver ( & virtio_console ) ;
}
module_init ( init ) ;
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_DESCRIPTION ( " Virtio console driver " ) ;
MODULE_LICENSE ( " GPL " ) ;