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>
2011-07-03 21:35:48 +04:00
# include <linux/module.h>
2008-05-31 00:09:44 +04:00
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. */
2013-03-20 09:14:29 +04:00
if ( virtqueue_add_inbuf ( vq , & sg , 1 , buf , GFP_KERNEL ) < 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
{
2012-05-28 10:48:40 +04:00
int ret ;
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 ;
2012-05-28 10:48:40 +04:00
ret = wait_for_completion_killable ( & have_data ) ;
if ( ret < 0 )
return ret ;
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
} ;
2012-05-28 10:48:42 +04:00
static int probe_common ( struct virtio_device * vdev )
2008-05-31 00:09:44 +04:00
{
int err ;
2013-03-08 04:30:18 +04:00
if ( vq ) {
/* We only support one device for now */
return - EBUSY ;
}
2008-05-31 00:09:44 +04:00
/* We expect a single virtqueue. */
2009-06-13 08:16:36 +04:00
vq = virtio_find_single_vq ( vdev , random_recv_done , " input " ) ;
2013-03-08 04:30:18 +04:00
if ( IS_ERR ( vq ) ) {
err = PTR_ERR ( vq ) ;
vq = NULL ;
return err ;
}
2008-05-31 00:09:44 +04:00
err = hwrng_register ( & virtio_hwrng ) ;
if ( err ) {
2009-06-13 08:16:36 +04:00
vdev - > config - > del_vqs ( vdev ) ;
2013-03-08 04:30:18 +04:00
vq = NULL ;
2008-05-31 00:09:44 +04:00
return err ;
}
return 0 ;
}
2012-05-28 10:48:42 +04:00
static void remove_common ( struct virtio_device * vdev )
2008-05-31 00:09:44 +04:00
{
vdev - > config - > reset ( vdev ) ;
2012-05-28 10:48:41 +04:00
busy = false ;
2008-05-31 00:09:44 +04:00
hwrng_unregister ( & virtio_hwrng ) ;
2009-06-13 08:16:36 +04:00
vdev - > config - > del_vqs ( vdev ) ;
2013-03-08 04:30:18 +04:00
vq = NULL ;
2008-05-31 00:09:44 +04:00
}
2012-05-28 10:48:42 +04:00
static int virtrng_probe ( struct virtio_device * vdev )
{
return probe_common ( vdev ) ;
}
2012-11-19 22:26:26 +04:00
static void virtrng_remove ( struct virtio_device * vdev )
2012-05-28 10:48:42 +04:00
{
remove_common ( vdev ) ;
}
2012-05-28 10:48:43 +04:00
# ifdef CONFIG_PM
static int virtrng_freeze ( struct virtio_device * vdev )
{
remove_common ( vdev ) ;
return 0 ;
}
static int virtrng_restore ( struct virtio_device * vdev )
{
return probe_common ( vdev ) ;
}
# endif
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 ,
2012-12-22 03:12:08 +04:00
. remove = virtrng_remove ,
2012-05-28 10:48:43 +04:00
# ifdef CONFIG_PM
. freeze = virtrng_freeze ,
. restore = virtrng_restore ,
# endif
2008-05-31 00:09:44 +04:00
} ;
2013-02-13 10:29:28 +04:00
module_virtio_driver ( virtio_rng_driver ) ;
2008-05-31 00:09:44 +04:00
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_DESCRIPTION ( " Virtio random number driver " ) ;
MODULE_LICENSE ( " GPL " ) ;