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
*/
# 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>
/* The host will fill any buffer we give it with sweet, sweet randomness. We
* give it 64 bytes at a time , and the hwrng framework takes it 4 bytes at a
* time . */
# define RANDOM_DATA_SIZE 64
static struct virtqueue * vq ;
static u32 * random_data ;
static unsigned int data_left ;
static DECLARE_COMPLETION ( have_data ) ;
static void random_recv_done ( struct virtqueue * vq )
{
2009-06-13 08:16:39 +04:00
unsigned int len ;
2008-05-31 00:09:44 +04:00
2009-04-23 11:12:59 +04:00
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
2008-05-31 00:09:44 +04:00
if ( ! vq - > vq_ops - > get_buf ( vq , & len ) )
2009-04-23 11:12:59 +04:00
return ;
2008-05-31 00:09:44 +04:00
2009-06-13 08:16:39 +04:00
data_left + = len ;
2008-05-31 00:09:44 +04:00
complete ( & have_data ) ;
}
static void register_buffer ( void )
{
struct scatterlist sg ;
2009-06-13 08:16:39 +04:00
sg_init_one ( & sg , random_data + data_left , RANDOM_DATA_SIZE - data_left ) ;
2008-05-31 00:09:44 +04:00
/* There should always be room for one buffer. */
if ( vq - > vq_ops - > add_buf ( vq , & sg , 0 , 1 , random_data ) ! = 0 )
BUG ( ) ;
vq - > vq_ops - > kick ( vq ) ;
}
/* At least we don't udelay() in a loop like some other drivers. */
static int virtio_data_present ( struct hwrng * rng , int wait )
{
2009-06-13 08:16:39 +04:00
if ( data_left > = sizeof ( u32 ) )
2008-05-31 00:09:44 +04:00
return 1 ;
2009-06-13 08:16:39 +04:00
again :
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
/* Not enough? Re-register. */
if ( unlikely ( data_left < sizeof ( u32 ) ) ) {
register_buffer ( ) ;
goto again ;
}
2008-05-31 00:09:44 +04:00
return 1 ;
}
/* virtio_data_present() must have succeeded before this is called. */
static int virtio_data_read ( struct hwrng * rng , u32 * data )
{
2009-06-13 08:16:39 +04:00
BUG_ON ( data_left < sizeof ( u32 ) ) ;
data_left - = sizeof ( u32 ) ;
* data = random_data [ data_left / 4 ] ;
2008-05-31 00:09:44 +04:00
2009-06-13 08:16:39 +04:00
if ( data_left < sizeof ( u32 ) ) {
2008-05-31 00:09:44 +04:00
init_completion ( & have_data ) ;
register_buffer ( ) ;
}
return sizeof ( * data ) ;
}
static struct hwrng virtio_hwrng = {
. name = " virtio " ,
. data_present = virtio_data_present ,
. data_read = virtio_data_read ,
} ;
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 ;
}
register_buffer ( ) ;
return 0 ;
}
static void virtrng_remove ( struct virtio_device * vdev )
{
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 } ,
} ;
static struct virtio_driver virtio_rng = {
. 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 )
{
int err ;
random_data = kmalloc ( RANDOM_DATA_SIZE , GFP_KERNEL ) ;
if ( ! random_data )
return - ENOMEM ;
err = register_virtio_driver ( & virtio_rng ) ;
if ( err )
kfree ( random_data ) ;
return err ;
}
static void __exit fini ( void )
{
kfree ( random_data ) ;
unregister_virtio_driver ( & virtio_rng ) ;
}
module_init ( init ) ;
module_exit ( fini ) ;
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_DESCRIPTION ( " Virtio random number driver " ) ;
MODULE_LICENSE ( " GPL " ) ;