2008-05-31 00:09:44 +04:00
/*
* Randomness driver for virtio
* Copyright ( C ) 2007 , 2008 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 . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
2009-12-01 10:26:33 +03:00
2008-05-31 00:09:44 +04:00
# include <linux/err.h>
# include <linux/hw_random.h>
# include <linux/scatterlist.h>
# include <linux/spinlock.h>
# include <linux/virtio.h>
# include <linux/virtio_rng.h>
static struct virtqueue * vq ;
2009-12-01 10:26:33 +03:00
static unsigned int data_avail ;
2008-05-31 00:09:44 +04:00
static DECLARE_COMPLETION ( have_data ) ;
2009-12-01 10:26:33 +03:00
static bool busy ;
2008-05-31 00:09:44 +04:00
static void random_recv_done ( struct virtqueue * vq )
{
2009-04-23 11:12:59 +04:00
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
2010-04-13 17:11:42 +04:00
if ( ! virtqueue_get_buf ( vq , & data_avail ) )
2009-04-23 11:12:59 +04:00
return ;
2008-05-31 00:09:44 +04:00
complete ( & have_data ) ;
}
2009-12-01 10:26:33 +03:00
/* The host will fill any buffer we give it with sweet, sweet randomness. */
static void register_buffer ( u8 * buf , size_t size )
2008-05-31 00:09:44 +04:00
{
struct scatterlist sg ;
2009-12-01 10:26:33 +03:00
sg_init_one ( & sg , buf , size ) ;
2008-05-31 00:09:44 +04:00
/* There should always be room for one buffer. */
2010-04-13 17:11:42 +04:00
if ( virtqueue_add_buf ( vq , & sg , 0 , 1 , buf ) < 0 )
2008-05-31 00:09:44 +04:00
BUG ( ) ;
2009-12-01 10:26:33 +03:00
2010-04-13 17:11:42 +04:00
virtqueue_kick ( vq ) ;
2008-05-31 00:09:44 +04:00
}
2009-12-01 10:26:33 +03:00
static int virtio_read ( struct hwrng * rng , void * buf , size_t size , bool wait )
2008-05-31 00:09:44 +04:00
{
2009-12-01 10:26:33 +03:00
if ( ! busy ) {
busy = true ;
init_completion ( & have_data ) ;
register_buffer ( buf , size ) ;
}
2008-05-31 00:09:44 +04:00
if ( ! wait )
return 0 ;
wait_for_completion ( & have_data ) ;
2009-06-13 08:16:39 +04:00
2009-12-01 10:26:33 +03:00
busy = false ;
2009-06-13 08:16:39 +04:00
2009-12-01 10:26:33 +03:00
return data_avail ;
2008-05-31 00:09:44 +04:00
}
2009-12-01 10:26:33 +03:00
static void virtio_cleanup ( struct hwrng * rng )
2008-05-31 00:09:44 +04:00
{
2009-12-01 10:26:33 +03:00
if ( busy )
wait_for_completion ( & have_data ) ;
2008-05-31 00:09:44 +04:00
}
2009-12-01 10:26:33 +03:00
2008-05-31 00:09:44 +04:00
static struct hwrng virtio_hwrng = {
2009-12-01 10:26:33 +03:00
. name = " virtio " ,
. cleanup = virtio_cleanup ,
. read = virtio_read ,
2008-05-31 00:09:44 +04:00
} ;
static int virtrng_probe ( struct virtio_device * vdev )
{
int err ;
/* We expect a single virtqueue. */
2009-06-13 08:16:36 +04:00
vq = virtio_find_single_vq ( vdev , random_recv_done , " input " ) ;
2008-05-31 00:09:44 +04:00
if ( IS_ERR ( vq ) )
return PTR_ERR ( vq ) ;
err = hwrng_register ( & virtio_hwrng ) ;
if ( err ) {
2009-06-13 08:16:36 +04:00
vdev - > config - > del_vqs ( vdev ) ;
2008-05-31 00:09:44 +04:00
return err ;
}
return 0 ;
}
2009-10-01 12:28:35 +04:00
static void __devexit virtrng_remove ( struct virtio_device * vdev )
2008-05-31 00:09:44 +04:00
{
vdev - > config - > reset ( vdev ) ;
hwrng_unregister ( & virtio_hwrng ) ;
2009-06-13 08:16:36 +04:00
vdev - > config - > del_vqs ( vdev ) ;
2008-05-31 00:09:44 +04:00
}
static struct virtio_device_id id_table [ ] = {
{ VIRTIO_ID_RNG , VIRTIO_DEV_ANY_ID } ,
{ 0 } ,
} ;
2010-01-16 04:01:26 +03:00
static struct virtio_driver virtio_rng_driver = {
2008-05-31 00:09:44 +04:00
. driver . name = KBUILD_MODNAME ,
. driver . owner = THIS_MODULE ,
. id_table = id_table ,
. probe = virtrng_probe ,
. remove = __devexit_p ( virtrng_remove ) ,
} ;
static int __init init ( void )
{
2010-01-16 04:01:26 +03:00
return register_virtio_driver ( & virtio_rng_driver ) ;
2008-05-31 00:09:44 +04:00
}
static void __exit fini ( void )
{
2010-01-16 04:01:26 +03:00
unregister_virtio_driver ( & virtio_rng_driver ) ;
2008-05-31 00:09:44 +04:00
}
module_init ( init ) ;
module_exit ( fini ) ;
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_DESCRIPTION ( " Virtio random number driver " ) ;
MODULE_LICENSE ( " GPL " ) ;