Skip to content

Commit

Permalink
[S390] zcrypt: add support for large random numbers
Browse files Browse the repository at this point in the history
This patch allows user space applications to access large amounts of
truly random data. The random data source is the build-in hardware
random number generator on the CEX2C cards.

Signed-off-by: Ralph Wuerthner <rwuerthn@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
  • Loading branch information
Ralph Wuerthner authored and Heiko Carstens committed Apr 17, 2008
1 parent 893f112 commit 2f7c8bd
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/crypto/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ config ZCRYPT
tristate "Support for PCI-attached cryptographic adapters"
depends on S390
select ZCRYPT_MONOLITHIC if ZCRYPT="y"
select HW_RANDOM
help
Select this option if you want to use a PCI-attached cryptographic
adapter like:
Expand Down
120 changes: 120 additions & 0 deletions drivers/s390/crypto/zcrypt_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <linux/compat.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/hw_random.h>

#include "zcrypt_api.h"

Expand All @@ -52,6 +53,9 @@ static LIST_HEAD(zcrypt_device_list);
static int zcrypt_device_count = 0;
static atomic_t zcrypt_open_count = ATOMIC_INIT(0);

static int zcrypt_rng_device_add(void);
static void zcrypt_rng_device_remove(void);

/**
* Device attributes common for all crypto devices.
*/
Expand Down Expand Up @@ -216,6 +220,22 @@ int zcrypt_device_register(struct zcrypt_device *zdev)
__zcrypt_increase_preference(zdev);
zcrypt_device_count++;
spin_unlock_bh(&zcrypt_device_lock);
if (zdev->ops->rng) {
rc = zcrypt_rng_device_add();
if (rc)
goto out_unregister;
}
return 0;

out_unregister:
spin_lock_bh(&zcrypt_device_lock);
zcrypt_device_count--;
list_del_init(&zdev->list);
spin_unlock_bh(&zcrypt_device_lock);
sysfs_remove_group(&zdev->ap_dev->device.kobj,
&zcrypt_device_attr_group);
put_device(&zdev->ap_dev->device);
zcrypt_device_put(zdev);
out:
return rc;
}
Expand All @@ -226,6 +246,8 @@ EXPORT_SYMBOL(zcrypt_device_register);
*/
void zcrypt_device_unregister(struct zcrypt_device *zdev)
{
if (zdev->ops->rng)
zcrypt_rng_device_remove();
spin_lock_bh(&zcrypt_device_lock);
zcrypt_device_count--;
list_del_init(&zdev->list);
Expand Down Expand Up @@ -427,6 +449,37 @@ static long zcrypt_send_cprb(struct ica_xcRB *xcRB)
return -ENODEV;
}

static long zcrypt_rng(char *buffer)
{
struct zcrypt_device *zdev;
int rc;

spin_lock_bh(&zcrypt_device_lock);
list_for_each_entry(zdev, &zcrypt_device_list, list) {
if (!zdev->online || !zdev->ops->rng)
continue;
zcrypt_device_get(zdev);
get_device(&zdev->ap_dev->device);
zdev->request_count++;
__zcrypt_decrease_preference(zdev);
if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
spin_unlock_bh(&zcrypt_device_lock);
rc = zdev->ops->rng(zdev, buffer);
spin_lock_bh(&zcrypt_device_lock);
module_put(zdev->ap_dev->drv->driver.owner);
} else
rc = -EAGAIN;
zdev->request_count--;
__zcrypt_increase_preference(zdev);
put_device(&zdev->ap_dev->device);
zcrypt_device_put(zdev);
spin_unlock_bh(&zcrypt_device_lock);
return rc;
}
spin_unlock_bh(&zcrypt_device_lock);
return -ENODEV;
}

static void zcrypt_status_mask(char status[AP_DEVICES])
{
struct zcrypt_device *zdev;
Expand Down Expand Up @@ -1041,6 +1094,73 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer,
return count;
}

static int zcrypt_rng_device_count;
static u32 *zcrypt_rng_buffer;
static int zcrypt_rng_buffer_index;
static DEFINE_MUTEX(zcrypt_rng_mutex);

static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data)
{
int rc;

/**
* We don't need locking here because the RNG API guarantees serialized
* read method calls.
*/
if (zcrypt_rng_buffer_index == 0) {
rc = zcrypt_rng((char *) zcrypt_rng_buffer);
if (rc < 0)
return -EIO;
zcrypt_rng_buffer_index = rc / sizeof *data;
}
*data = zcrypt_rng_buffer[--zcrypt_rng_buffer_index];
return sizeof *data;
}

static struct hwrng zcrypt_rng_dev = {
.name = "zcrypt",
.data_read = zcrypt_rng_data_read,
};

static int zcrypt_rng_device_add(void)
{
int rc = 0;

mutex_lock(&zcrypt_rng_mutex);
if (zcrypt_rng_device_count == 0) {
zcrypt_rng_buffer = (u32 *) get_zeroed_page(GFP_KERNEL);
if (!zcrypt_rng_buffer) {
rc = -ENOMEM;
goto out;
}
zcrypt_rng_buffer_index = 0;
rc = hwrng_register(&zcrypt_rng_dev);
if (rc)
goto out_free;
zcrypt_rng_device_count = 1;
} else
zcrypt_rng_device_count++;
mutex_unlock(&zcrypt_rng_mutex);
return 0;

out_free:
free_page((unsigned long) zcrypt_rng_buffer);
out:
mutex_unlock(&zcrypt_rng_mutex);
return rc;
}

static void zcrypt_rng_device_remove(void)
{
mutex_lock(&zcrypt_rng_mutex);
zcrypt_rng_device_count--;
if (zcrypt_rng_device_count == 0) {
hwrng_unregister(&zcrypt_rng_dev);
free_page((unsigned long) zcrypt_rng_buffer);
}
mutex_unlock(&zcrypt_rng_mutex);
}

/**
* The module initialization code.
*/
Expand Down
8 changes: 8 additions & 0 deletions drivers/s390/crypto/zcrypt_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,21 @@ struct ica_z90_status {
#define ZCRYPT_CEX2C 5
#define ZCRYPT_CEX2A 6

/**
* Large random numbers are pulled in 4096 byte chunks from the crypto cards
* and stored in a page. Be carefull when increasing this buffer due to size
* limitations for AP requests.
*/
#define ZCRYPT_RNG_BUFFER_SIZE 4096

struct zcrypt_device;

struct zcrypt_ops {
long (*rsa_modexpo)(struct zcrypt_device *, struct ica_rsa_modexpo *);
long (*rsa_modexpo_crt)(struct zcrypt_device *,
struct ica_rsa_modexpo_crt *);
long (*send_cprb)(struct zcrypt_device *, struct ica_xcRB *);
long (*rng)(struct zcrypt_device *, char *);
};

struct zcrypt_device {
Expand Down
Loading

0 comments on commit 2f7c8bd

Please sign in to comment.