Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
# SPDX-License-Identifier: GPL-2.0
#
# S/390 crypto devices
#
ap-objs := ap_bus.o ap_card.o ap_queue.o
obj-$(CONFIG_AP) += ap.o
# zcrypt_api.o and zcrypt_msgtype*.o depend on ap.o
zcrypt-objs := zcrypt_api.o zcrypt_card.o zcrypt_queue.o
zcrypt-objs += zcrypt_msgtype6.o zcrypt_msgtype50.o
zcrypt-objs += zcrypt_ccamisc.o zcrypt_ep11misc.o
obj-$(CONFIG_ZCRYPT) += zcrypt.o
# adapter drivers depend on ap.o and zcrypt.o
obj-$(CONFIG_ZCRYPT) += zcrypt_cex4.o
# pkey base and api module
pkey-objs := pkey_base.o pkey_api.o pkey_sysfs.o
obj-$(CONFIG_PKEY) += pkey.o
# pkey cca handler module
pkey-cca-objs := pkey_cca.o
obj-$(CONFIG_PKEY_CCA) += pkey-cca.o
# pkey ep11 handler module
pkey-ep11-objs := pkey_ep11.o
obj-$(CONFIG_PKEY_EP11) += pkey-ep11.o
# pkey pckmo handler module
pkey-pckmo-objs := pkey_pckmo.o
obj-$(CONFIG_PKEY_PCKMO) += pkey-pckmo.o
# pkey uv handler module
pkey-uv-objs := pkey_uv.o
obj-$(CONFIG_PKEY_UV) += pkey-uv.o
# adjunct processor matrix
vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o
obj-$(CONFIG_VFIO_AP) += vfio_ap.o
File diff suppressed because it is too large Load Diff
+380
View File
@@ -0,0 +1,380 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2006, 2023
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
* Felix Beck <felix.beck@de.ibm.com>
* Holger Dengler <hd@linux.vnet.ibm.com>
*
* Adjunct processor bus header file.
*/
#ifndef _AP_BUS_H_
#define _AP_BUS_H_
#include <linux/device.h>
#include <linux/types.h>
#include <linux/hashtable.h>
#include <asm/isc.h>
#include <asm/ap.h>
#define AP_DEVICES 256 /* Number of AP devices. */
#define AP_DOMAINS 256 /* Number of AP domains. */
#define AP_IOCTLS 256 /* Number of ioctls. */
#define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */
#define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */
#define AP_POLL_TIME 1 /* Time in ticks between receive polls. */
#define AP_DEFAULT_MAX_MSG_SIZE (12 * 1024)
#define AP_TAPQ_ML_FIELD_CHUNK_SIZE (4096)
extern int ap_domain_index;
extern atomic_t ap_max_msg_size;
extern DECLARE_HASHTABLE(ap_queues, 8);
extern spinlock_t ap_queues_lock;
static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
{
return (*ptr & (0x80000000u >> nr)) != 0;
}
#define AP_RESPONSE_NORMAL 0x00
#define AP_RESPONSE_Q_NOT_AVAIL 0x01
#define AP_RESPONSE_RESET_IN_PROGRESS 0x02
#define AP_RESPONSE_DECONFIGURED 0x03
#define AP_RESPONSE_CHECKSTOPPED 0x04
#define AP_RESPONSE_BUSY 0x05
#define AP_RESPONSE_INVALID_ADDRESS 0x06
#define AP_RESPONSE_OTHERWISE_CHANGED 0x07
#define AP_RESPONSE_INVALID_GISA 0x08
#define AP_RESPONSE_Q_BOUND_TO_ANOTHER 0x09
#define AP_RESPONSE_STATE_CHANGE_IN_PROGRESS 0x0A
#define AP_RESPONSE_Q_NOT_BOUND 0x0B
#define AP_RESPONSE_Q_FULL 0x10
#define AP_RESPONSE_NO_PENDING_REPLY 0x10
#define AP_RESPONSE_INDEX_TOO_BIG 0x11
#define AP_RESPONSE_NO_FIRST_PART 0x13
#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15
#define AP_RESPONSE_REQ_FAC_NOT_INST 0x16
#define AP_RESPONSE_Q_BIND_ERROR 0x30
#define AP_RESPONSE_Q_NOT_AVAIL_FOR_ASSOC 0x31
#define AP_RESPONSE_Q_NOT_EMPTY 0x32
#define AP_RESPONSE_BIND_LIMIT_EXCEEDED 0x33
#define AP_RESPONSE_INVALID_ASSOC_SECRET 0x34
#define AP_RESPONSE_ASSOC_SECRET_NOT_UNIQUE 0x35
#define AP_RESPONSE_ASSOC_FAILED 0x36
#define AP_RESPONSE_INVALID_DOMAIN 0x42
/*
* Supported AP device types
*/
#define AP_DEVICE_TYPE_CEX4 10
#define AP_DEVICE_TYPE_CEX5 11
#define AP_DEVICE_TYPE_CEX6 12
#define AP_DEVICE_TYPE_CEX7 13
#define AP_DEVICE_TYPE_CEX8 14
/*
* AP queue state machine states
*/
enum ap_sm_state {
AP_SM_STATE_RESET_START = 0,
AP_SM_STATE_RESET_WAIT,
AP_SM_STATE_SETIRQ_WAIT,
AP_SM_STATE_IDLE,
AP_SM_STATE_WORKING,
AP_SM_STATE_QUEUE_FULL,
AP_SM_STATE_ASSOC_WAIT,
NR_AP_SM_STATES
};
/*
* AP queue state machine events
*/
enum ap_sm_event {
AP_SM_EVENT_POLL,
AP_SM_EVENT_TIMEOUT,
NR_AP_SM_EVENTS
};
/*
* AP queue state wait behaviour
*/
enum ap_sm_wait {
AP_SM_WAIT_AGAIN = 0, /* retry immediately */
AP_SM_WAIT_HIGH_TIMEOUT, /* poll high freq, wait for timeout */
AP_SM_WAIT_LOW_TIMEOUT, /* poll low freq, wait for timeout */
AP_SM_WAIT_INTERRUPT, /* wait for thin interrupt (if available) */
AP_SM_WAIT_NONE, /* no wait */
NR_AP_SM_WAIT
};
/*
* AP queue device states
*/
enum ap_dev_state {
AP_DEV_STATE_UNINITIATED = 0, /* fresh and virgin, not touched */
AP_DEV_STATE_OPERATING, /* queue dev is working normal */
AP_DEV_STATE_SHUTDOWN, /* remove/unbind/shutdown in progress */
AP_DEV_STATE_ERROR, /* device is in error state */
NR_AP_DEV_STATES
};
struct ap_device;
struct ap_message;
/*
* The ap driver struct includes a flags field which holds some info for
* the ap bus about the driver. Currently only one flag is supported and
* used: The DEFAULT flag marks an ap driver as a default driver which is
* used together with the apmask and aqmask whitelisting of the ap bus.
*/
#define AP_DRIVER_FLAG_DEFAULT 0x0001
struct ap_driver {
struct device_driver driver;
int (*probe)(struct ap_device *);
void (*remove)(struct ap_device *);
int (*in_use)(unsigned long *apm, unsigned long *aqm);
/*
* Called at the start of the ap bus scan function when
* the crypto config information (qci) has changed.
* This callback is not invoked if there is no AP
* QCI support available.
*/
void (*on_config_changed)(struct ap_config_info *new_config_info,
struct ap_config_info *old_config_info);
/*
* Called at the end of the ap bus scan function when
* the crypto config information (qci) has changed.
* This callback is not invoked if there is no AP
* QCI support available.
*/
void (*on_scan_complete)(struct ap_config_info *new_config_info,
struct ap_config_info *old_config_info);
struct ap_device_id *ids;
unsigned int flags;
};
#define to_ap_drv(x) container_of_const((x), struct ap_driver, driver)
int ap_driver_register(struct ap_driver *, struct module *, char *);
void ap_driver_unregister(struct ap_driver *);
struct ap_device {
struct device device;
int device_type; /* AP device type. */
};
#define to_ap_dev(x) container_of((x), struct ap_device, device)
struct ap_card {
struct ap_device ap_dev;
struct ap_tapq_hwinfo hwinfo; /* TAPQ GR2 content */
atomic64_t total_request_count; /* # requests ever for this AP device.*/
unsigned int maxmsgsize; /* AP msg limit for this card */
int id; /* AP card number. */
bool config; /* configured state */
bool chkstop; /* checkstop state */
};
#define TAPQ_CARD_HWINFO_MASK 0xFFFF0000FFFF0F0FUL
#define ASSOC_IDX_INVALID 0x10000
#define to_ap_card(x) container_of((x), struct ap_card, ap_dev.device)
struct ap_queue {
struct ap_device ap_dev;
struct hlist_node hnode; /* Node for the ap_queues hashtable */
struct ap_card *card; /* Ptr to assoc. AP card. */
spinlock_t lock; /* Per device lock. */
u64 total_request_count; /* # requests ever for this AP device.*/
enum ap_dev_state dev_state; /* queue device state */
ap_qid_t qid; /* AP queue id. */
unsigned int se_bstate; /* SE bind state (BS) */
unsigned int assoc_idx; /* SE association index */
int queue_count; /* # messages currently on AP queue. */
int pendingq_count; /* # requests on pendingq list. */
int requestq_count; /* # requests on requestq list. */
int request_timeout; /* Request timeout in jiffies. */
struct timer_list timeout; /* Timer for request timeouts. */
struct list_head pendingq; /* List of message sent to AP queue. */
struct list_head requestq; /* List of message yet to be sent. */
struct ap_message *reply; /* Per device reply message. */
enum ap_sm_state sm_state; /* ap queue state machine state */
int rapq_fbit; /* fbit arg for next rapq invocation */
int last_err_rc; /* last error state response code */
bool config; /* configured state */
bool chkstop; /* checkstop state */
};
#define to_ap_queue(x) container_of((x), struct ap_queue, ap_dev.device)
typedef enum ap_sm_wait (ap_func_t)(struct ap_queue *queue);
struct ap_response_type {
struct completion work;
int type;
};
struct ap_message {
struct list_head list; /* Request queueing. */
unsigned long psmid; /* Message id. */
void *msg; /* Pointer to message buffer. */
size_t len; /* actual msg len in msg buffer */
size_t bufsize; /* allocated msg buffer size */
/* receive is called from tasklet context */
void (*receive)(struct ap_queue *, struct ap_message *,
struct ap_message *);
struct ap_response_type response;
int rc; /* Return code for this message */
u16 flags; /* Flags, see AP_MSG_FLAG_xxx */
};
#define AP_MSG_FLAG_SPECIAL 0x0001 /* flag msg as 'special' with NQAP */
#define AP_MSG_FLAG_USAGE 0x0002 /* CCA, EP11: usage (no admin) msg */
#define AP_MSG_FLAG_ADMIN 0x0004 /* CCA, EP11: admin (=control) msg */
#define AP_MSG_FLAG_MEMPOOL 0x0008 /* ap msg buffer allocated via mempool */
int ap_init_apmsg(struct ap_message *ap_msg, u32 flags);
void ap_release_apmsg(struct ap_message *ap_msg);
enum ap_sm_wait ap_sm_event(struct ap_queue *aq, enum ap_sm_event event);
enum ap_sm_wait ap_sm_event_loop(struct ap_queue *aq, enum ap_sm_event event);
int ap_queue_message(struct ap_queue *aq, struct ap_message *ap_msg);
void ap_cancel_message(struct ap_queue *aq, struct ap_message *ap_msg);
void ap_flush_queue(struct ap_queue *aq);
bool ap_queue_usable(struct ap_queue *aq);
void *ap_airq_ptr(void);
int ap_sb_available(void);
bool ap_is_se_guest(void);
void ap_wait(enum ap_sm_wait wait);
void ap_request_timeout(struct timer_list *t);
bool ap_bus_force_rescan(void);
int ap_test_config_usage_domain(unsigned int domain);
int ap_test_config_ctrl_domain(unsigned int domain);
void ap_queue_init_reply(struct ap_queue *aq, struct ap_message *ap_msg);
struct ap_queue *ap_queue_create(ap_qid_t qid, struct ap_card *ac);
void ap_queue_prepare_remove(struct ap_queue *aq);
void ap_queue_remove(struct ap_queue *aq);
void ap_queue_init_state(struct ap_queue *aq);
void _ap_queue_init_state(struct ap_queue *aq);
struct ap_card *ap_card_create(int id, struct ap_tapq_hwinfo info,
int comp_type);
#define APMASKSIZE (BITS_TO_LONGS(AP_DEVICES) * sizeof(unsigned long))
#define AQMASKSIZE (BITS_TO_LONGS(AP_DOMAINS) * sizeof(unsigned long))
struct ap_perms {
unsigned long ioctlm[BITS_TO_LONGS(AP_IOCTLS)];
unsigned long apm[BITS_TO_LONGS(AP_DEVICES)];
unsigned long aqm[BITS_TO_LONGS(AP_DOMAINS)];
unsigned long adm[BITS_TO_LONGS(AP_DOMAINS)];
};
extern struct ap_perms ap_perms;
extern bool ap_apmask_aqmask_in_use;
extern int ap_driver_override_ctr;
extern struct mutex ap_attr_mutex;
/*
* Get ap_queue device for this qid.
* Returns ptr to the struct ap_queue device or NULL if there
* was no ap_queue device with this qid found. When something is
* found, the reference count of the embedded device is increased.
* So the caller has to decrease the reference count after use
* with a call to put_device(&aq->ap_dev.device).
*/
struct ap_queue *ap_get_qdev(ap_qid_t qid);
/*
* check APQN for owned/reserved by ap bus and default driver(s).
* Checks if this APQN is or will be in use by the ap bus
* and the default set of drivers.
* If yes, returns 1, if not returns 0. On error a negative
* errno value is returned.
*/
int ap_owned_by_def_drv(int card, int queue);
/*
* check 'matrix' of APQNs for owned/reserved by ap bus and
* default driver(s).
* Checks if there is at least one APQN in the given 'matrix'
* marked as owned/reserved by the ap bus and default driver(s).
* If such an APQN is found the return value is 1, otherwise
* 0 is returned. On error a negative errno value is returned.
* The parameter apm is a bitmask which should be declared
* as DECLARE_BITMAP(apm, AP_DEVICES), the aqm parameter is
* similar, should be declared as DECLARE_BITMAP(aqm, AP_DOMAINS).
*/
int ap_apqn_in_matrix_owned_by_def_drv(unsigned long *apm,
unsigned long *aqm);
/*
* ap_parse_mask_str() - helper function to parse a bitmap string
* and clear/set the bits in the bitmap accordingly. The string may be
* given as absolute value, a hex string like 0x1F2E3D4C5B6A" simple
* overwriting the current content of the bitmap. Or as relative string
* like "+1-16,-32,-0x40,+128" where only single bits or ranges of
* bits are cleared or set. Distinction is done based on the very
* first character which may be '+' or '-' for the relative string
* and otherwise assume to be an absolute value string. If parsing fails
* a negative errno value is returned. All arguments and bitmaps are
* big endian order.
*/
int ap_parse_mask_str(const char *str,
unsigned long *bitmap, int bits,
struct mutex *lock);
/*
* ap_hex2bitmap() - Convert a string containing a hexadecimal number (str)
* into a bitmap (bitmap) with bits set that correspond to the bits represented
* by the hex string. Input and output data is in big endian order.
*
* str - Input hex string of format "0x1234abcd". The leading "0x" is optional.
* At least one digit is required. Must be large enough to hold the number of
* bits represented by the bits parameter.
*
* bitmap - Pointer to a bitmap. Upon successful completion of this function,
* this bitmap will have bits set to match the value of str. If bitmap is longer
* than str, then the rightmost bits of bitmap are padded with zeros. Must be
* large enough to hold the number of bits represented by the bits parameter.
*
* bits - Length, in bits, of the bitmap represented by str. Must be a multiple
* of 8.
*
* Returns: 0 On success
* -EINVAL If str format is invalid or bits is not a multiple of 8.
*/
int ap_hex2bitmap(const char *str, unsigned long *bitmap, int bits);
/*
* Interface to wait for the AP bus to have done one initial ap bus
* scan and all detected APQNs have been bound to device drivers.
* If these both conditions are not fulfilled, this function blocks
* on a condition with wait_for_completion_killable_timeout().
* If these both conditions are fulfilled (before the timeout hits)
* the return value is 0. If the timeout (in jiffies) hits instead
* -ETIME is returned. On failures negative return values are
* returned to the caller.
* It may be that the AP bus scan finds new devices. Then the
* condition that all APQNs are bound to their device drivers
* is reset to false and this call again blocks until either all
* APQNs are bound to a device driver or the timeout hits again.
*/
int ap_wait_apqn_bindings_complete(unsigned long timeout);
void ap_send_config_uevent(struct ap_device *ap_dev, bool cfg);
void ap_send_online_uevent(struct ap_device *ap_dev, int online);
void ap_send_se_bind_uevent(struct ap_device *ap_dev);
void ap_send_se_assoc_uevent(struct ap_device *ap_dev, unsigned int assoc_idx);
#endif /* _AP_BUS_H_ */
+248
View File
@@ -0,0 +1,248 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright IBM Corp. 2016
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*
* Adjunct processor bus, card related code.
*/
#define pr_fmt(fmt) "ap: " fmt
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/facility.h>
#include <asm/sclp.h>
#include "ap_bus.h"
/*
* AP card related attributes.
*/
static ssize_t hwtype_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return sysfs_emit(buf, "%d\n", ac->ap_dev.device_type);
}
static DEVICE_ATTR_RO(hwtype);
static ssize_t raw_hwtype_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return sysfs_emit(buf, "%d\n", ac->hwinfo.at);
}
static DEVICE_ATTR_RO(raw_hwtype);
static ssize_t depth_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return sysfs_emit(buf, "%d\n", ac->hwinfo.qd + 1);
}
static DEVICE_ATTR_RO(depth);
static ssize_t ap_functions_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return sysfs_emit(buf, "0x%08X\n", ac->hwinfo.fac);
}
static DEVICE_ATTR_RO(ap_functions);
static ssize_t request_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ap_card *ac = to_ap_card(dev);
u64 req_cnt;
req_cnt = 0;
spin_lock_bh(&ap_queues_lock);
req_cnt = atomic64_read(&ac->total_request_count);
spin_unlock_bh(&ap_queues_lock);
return sysfs_emit(buf, "%llu\n", req_cnt);
}
static ssize_t request_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int bkt;
struct ap_queue *aq;
struct ap_card *ac = to_ap_card(dev);
spin_lock_bh(&ap_queues_lock);
hash_for_each(ap_queues, bkt, aq, hnode)
if (ac == aq->card)
aq->total_request_count = 0;
spin_unlock_bh(&ap_queues_lock);
atomic64_set(&ac->total_request_count, 0);
return count;
}
static DEVICE_ATTR_RW(request_count);
static ssize_t requestq_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int bkt;
struct ap_queue *aq;
unsigned int reqq_cnt;
struct ap_card *ac = to_ap_card(dev);
reqq_cnt = 0;
spin_lock_bh(&ap_queues_lock);
hash_for_each(ap_queues, bkt, aq, hnode)
if (ac == aq->card)
reqq_cnt += aq->requestq_count;
spin_unlock_bh(&ap_queues_lock);
return sysfs_emit(buf, "%d\n", reqq_cnt);
}
static DEVICE_ATTR_RO(requestq_count);
static ssize_t pendingq_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int bkt;
struct ap_queue *aq;
unsigned int penq_cnt;
struct ap_card *ac = to_ap_card(dev);
penq_cnt = 0;
spin_lock_bh(&ap_queues_lock);
hash_for_each(ap_queues, bkt, aq, hnode)
if (ac == aq->card)
penq_cnt += aq->pendingq_count;
spin_unlock_bh(&ap_queues_lock);
return sysfs_emit(buf, "%d\n", penq_cnt);
}
static DEVICE_ATTR_RO(pendingq_count);
static ssize_t modalias_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "ap:t%02X\n", to_ap_dev(dev)->device_type);
}
static DEVICE_ATTR_RO(modalias);
static ssize_t config_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return sysfs_emit(buf, "%d\n", ac->config ? 1 : 0);
}
static ssize_t config_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int rc = 0, cfg;
struct ap_card *ac = to_ap_card(dev);
if (sscanf(buf, "%d\n", &cfg) != 1 || cfg < 0 || cfg > 1)
return -EINVAL;
if (cfg && !ac->config)
rc = sclp_ap_configure(ac->id);
else if (!cfg && ac->config)
rc = sclp_ap_deconfigure(ac->id);
if (rc)
return rc;
ac->config = cfg ? true : false;
ap_send_config_uevent(&ac->ap_dev, ac->config);
return count;
}
static DEVICE_ATTR_RW(config);
static ssize_t chkstop_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return sysfs_emit(buf, "%d\n", ac->chkstop ? 1 : 0);
}
static DEVICE_ATTR_RO(chkstop);
static ssize_t max_msg_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return sysfs_emit(buf, "%u\n", ac->maxmsgsize);
}
static DEVICE_ATTR_RO(max_msg_size);
static struct attribute *ap_card_dev_attrs[] = {
&dev_attr_hwtype.attr,
&dev_attr_raw_hwtype.attr,
&dev_attr_depth.attr,
&dev_attr_ap_functions.attr,
&dev_attr_request_count.attr,
&dev_attr_requestq_count.attr,
&dev_attr_pendingq_count.attr,
&dev_attr_modalias.attr,
&dev_attr_config.attr,
&dev_attr_chkstop.attr,
&dev_attr_max_msg_size.attr,
NULL
};
static struct attribute_group ap_card_dev_attr_group = {
.attrs = ap_card_dev_attrs
};
static const struct attribute_group *ap_card_dev_attr_groups[] = {
&ap_card_dev_attr_group,
NULL
};
static struct device_type ap_card_type = {
.name = "ap_card",
.groups = ap_card_dev_attr_groups,
};
static void ap_card_device_release(struct device *dev)
{
struct ap_card *ac = to_ap_card(dev);
kfree(ac);
}
struct ap_card *ap_card_create(int id, struct ap_tapq_hwinfo hwinfo,
int comp_type)
{
struct ap_card *ac;
ac = kzalloc_obj(*ac);
if (!ac)
return NULL;
ac->ap_dev.device.release = ap_card_device_release;
ac->ap_dev.device.type = &ap_card_type;
ac->ap_dev.device_type = comp_type;
ac->hwinfo = hwinfo;
ac->id = id;
ac->maxmsgsize = hwinfo.ml > 0 ?
hwinfo.ml * AP_TAPQ_ML_FIELD_CHUNK_SIZE : AP_DEFAULT_MAX_MSG_SIZE;
return ac;
}
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright IBM Corp. 2016
* Author(s): Harald Freudenberger <freude@de.ibm.com>
*/
#ifndef AP_DEBUG_H
#define AP_DEBUG_H
#include <asm/debug.h>
#define DBF_ERR 3 /* error conditions */
#define DBF_WARN 4 /* warning conditions */
#define DBF_INFO 5 /* informational */
#define DBF_DEBUG 6 /* for debugging only */
#define RC2ERR(rc) ((rc) ? DBF_ERR : DBF_INFO)
#define RC2WARN(rc) ((rc) ? DBF_WARN : DBF_INFO)
#define AP_DBF_MAX_SPRINTF_ARGS 6
#define AP_DBF(...) \
debug_sprintf_event(ap_dbf_info, ##__VA_ARGS__)
#define AP_DBF_ERR(...) \
debug_sprintf_event(ap_dbf_info, DBF_ERR, ##__VA_ARGS__)
#define AP_DBF_WARN(...) \
debug_sprintf_event(ap_dbf_info, DBF_WARN, ##__VA_ARGS__)
#define AP_DBF_INFO(...) \
debug_sprintf_event(ap_dbf_info, DBF_INFO, ##__VA_ARGS__)
extern debug_info_t *ap_dbf_info;
#endif /* AP_DEBUG_H */
File diff suppressed because it is too large Load Diff
+795
View File
@@ -0,0 +1,795 @@
// SPDX-License-Identifier: GPL-2.0
/*
* pkey device driver
*
* Copyright IBM Corp. 2017, 2023
*
* Author(s): Harald Freudenberger
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/export.h>
#include <linux/slab.h>
#include "zcrypt_api.h"
#include "zcrypt_ccamisc.h"
#include "pkey_base.h"
/*
* Helper functions
*/
static int key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, size_t keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags)
{
int rc;
/* try the direct way */
rc = pkey_handler_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype, xflags);
/* if this did not work, try the slowpath way */
if (rc == -ENODEV) {
rc = pkey_handler_slowpath_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype, xflags);
if (rc)
rc = -ENODEV;
}
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* In-Kernel function: Transform a key blob (of any type) into a protected key
*/
int pkey_key2protkey(const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype, u32 xflags)
{
int rc;
rc = key2protkey(NULL, 0, key, keylen,
protkey, protkeylen, protkeytype, xflags);
if (rc == -ENODEV) {
pkey_handler_request_modules();
rc = key2protkey(NULL, 0, key, keylen,
protkey, protkeylen, protkeytype, xflags);
}
return rc;
}
EXPORT_SYMBOL(pkey_key2protkey);
/*
* Ioctl functions
*/
static void *_copy_key_from_user(void __user *ukey, size_t keylen)
{
if (!ukey || keylen < MINKEYBLOBBUFSIZE || keylen > KEYBLOBBUFSIZE)
return ERR_PTR(-EINVAL);
return memdup_user(ukey, keylen);
}
static void *_copy_apqns_from_user(void __user *uapqns, size_t nr_apqns)
{
if (!uapqns || nr_apqns == 0)
return NULL;
return memdup_array_user(uapqns, nr_apqns, sizeof(struct pkey_apqn));
}
static int pkey_ioctl_genseck(struct pkey_genseck __user *ugs)
{
struct pkey_genseck kgs;
struct pkey_apqn apqn;
u32 keybuflen;
int rc;
if (copy_from_user(&kgs, ugs, sizeof(kgs)))
return -EFAULT;
apqn.card = kgs.cardnr;
apqn.domain = kgs.domain;
keybuflen = sizeof(kgs.seckey.seckey);
rc = pkey_handler_gen_key(&apqn, 1,
kgs.keytype, PKEY_TYPE_CCA_DATA, 0, 0,
kgs.seckey.seckey, &keybuflen, NULL, 0);
pr_debug("gen_key()=%d\n", rc);
if (!rc && copy_to_user(ugs, &kgs, sizeof(kgs)))
rc = -EFAULT;
memzero_explicit(&kgs, sizeof(kgs));
return rc;
}
static int pkey_ioctl_clr2seck(struct pkey_clr2seck __user *ucs)
{
struct pkey_clr2seck kcs;
struct pkey_apqn apqn;
u32 keybuflen;
int rc;
if (copy_from_user(&kcs, ucs, sizeof(kcs)))
return -EFAULT;
apqn.card = kcs.cardnr;
apqn.domain = kcs.domain;
keybuflen = sizeof(kcs.seckey.seckey);
rc = pkey_handler_clr_to_key(&apqn, 1,
kcs.keytype, PKEY_TYPE_CCA_DATA, 0, 0,
kcs.clrkey.clrkey,
pkey_keytype_aes_to_size(kcs.keytype),
kcs.seckey.seckey, &keybuflen, NULL, 0);
pr_debug("clr_to_key()=%d\n", rc);
if (!rc && copy_to_user(ucs, &kcs, sizeof(kcs)))
rc = -EFAULT;
memzero_explicit(&kcs, sizeof(kcs));
return rc;
}
static int pkey_ioctl_sec2protk(struct pkey_sec2protk __user *usp)
{
struct pkey_sec2protk ksp;
struct pkey_apqn apqn;
int rc;
if (copy_from_user(&ksp, usp, sizeof(ksp)))
return -EFAULT;
apqn.card = ksp.cardnr;
apqn.domain = ksp.domain;
ksp.protkey.len = sizeof(ksp.protkey.protkey);
rc = pkey_handler_key_to_protkey(&apqn, 1,
ksp.seckey.seckey,
sizeof(ksp.seckey.seckey),
ksp.protkey.protkey,
&ksp.protkey.len, &ksp.protkey.type,
0);
pr_debug("key_to_protkey()=%d\n", rc);
if (!rc && copy_to_user(usp, &ksp, sizeof(ksp)))
rc = -EFAULT;
memzero_explicit(&ksp, sizeof(ksp));
return rc;
}
static int pkey_ioctl_clr2protk(struct pkey_clr2protk __user *ucp)
{
struct pkey_clr2protk kcp;
struct clearkeytoken *t;
u32 keylen;
u8 *tmpbuf;
int rc;
if (copy_from_user(&kcp, ucp, sizeof(kcp)))
return -EFAULT;
/* build a 'clear key token' from the clear key value */
keylen = pkey_keytype_aes_to_size(kcp.keytype);
if (!keylen) {
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n",
__func__, kcp.keytype);
memzero_explicit(&kcp, sizeof(kcp));
return -EINVAL;
}
tmpbuf = kzalloc(sizeof(*t) + keylen, GFP_KERNEL);
if (!tmpbuf) {
memzero_explicit(&kcp, sizeof(kcp));
return -ENOMEM;
}
t = (struct clearkeytoken *)tmpbuf;
t->type = TOKTYPE_NON_CCA;
t->version = TOKVER_CLEAR_KEY;
t->keytype = (keylen - 8) >> 3;
t->len = keylen;
memcpy(t->clearkey, kcp.clrkey.clrkey, keylen);
kcp.protkey.len = sizeof(kcp.protkey.protkey);
rc = key2protkey(NULL, 0,
tmpbuf, sizeof(*t) + keylen,
kcp.protkey.protkey,
&kcp.protkey.len, &kcp.protkey.type, 0);
pr_debug("key2protkey()=%d\n", rc);
kfree_sensitive(tmpbuf);
if (!rc && copy_to_user(ucp, &kcp, sizeof(kcp)))
rc = -EFAULT;
memzero_explicit(&kcp, sizeof(kcp));
return rc;
}
static int pkey_ioctl_findcard(struct pkey_findcard __user *ufc)
{
struct pkey_findcard kfc;
struct pkey_apqn *apqns;
size_t nr_apqns;
int rc;
if (copy_from_user(&kfc, ufc, sizeof(kfc)))
return -EFAULT;
nr_apqns = MAXAPQNSINLIST;
apqns = kmalloc_objs(struct pkey_apqn, nr_apqns);
if (!apqns)
return -ENOMEM;
rc = pkey_handler_apqns_for_key(kfc.seckey.seckey,
sizeof(kfc.seckey.seckey),
PKEY_FLAGS_MATCH_CUR_MKVP,
apqns, &nr_apqns, 0);
if (rc == -ENODEV)
rc = pkey_handler_apqns_for_key(kfc.seckey.seckey,
sizeof(kfc.seckey.seckey),
PKEY_FLAGS_MATCH_ALT_MKVP,
apqns, &nr_apqns, 0);
pr_debug("apqns_for_key()=%d\n", rc);
if (rc) {
kfree(apqns);
return rc;
}
kfc.cardnr = apqns[0].card;
kfc.domain = apqns[0].domain;
kfree(apqns);
if (copy_to_user(ufc, &kfc, sizeof(kfc)))
return -EFAULT;
return 0;
}
static int pkey_ioctl_skey2pkey(struct pkey_skey2pkey __user *usp)
{
struct pkey_skey2pkey ksp;
int rc;
if (copy_from_user(&ksp, usp, sizeof(ksp)))
return -EFAULT;
ksp.protkey.len = sizeof(ksp.protkey.protkey);
rc = pkey_handler_key_to_protkey(NULL, 0,
ksp.seckey.seckey,
sizeof(ksp.seckey.seckey),
ksp.protkey.protkey,
&ksp.protkey.len,
&ksp.protkey.type, 0);
pr_debug("key_to_protkey()=%d\n", rc);
if (!rc && copy_to_user(usp, &ksp, sizeof(ksp)))
rc = -EFAULT;
memzero_explicit(&ksp, sizeof(ksp));
return rc;
}
static int pkey_ioctl_verifykey(struct pkey_verifykey __user *uvk)
{
u32 keytype, keybitsize, flags;
struct pkey_verifykey kvk;
int rc;
if (copy_from_user(&kvk, uvk, sizeof(kvk)))
return -EFAULT;
kvk.cardnr = 0xFFFF;
kvk.domain = 0xFFFF;
rc = pkey_handler_verify_key(kvk.seckey.seckey,
sizeof(kvk.seckey.seckey),
&kvk.cardnr, &kvk.domain,
&keytype, &keybitsize, &flags, 0);
pr_debug("verify_key()=%d\n", rc);
if (!rc && keytype != PKEY_TYPE_CCA_DATA)
rc = -EINVAL;
kvk.attributes = PKEY_VERIFY_ATTR_AES;
kvk.keysize = (u16)keybitsize;
if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
kvk.attributes |= PKEY_VERIFY_ATTR_OLD_MKVP;
if (!rc && copy_to_user(uvk, &kvk, sizeof(kvk)))
rc = -EFAULT;
memzero_explicit(&kvk, sizeof(kvk));
return rc;
}
static int pkey_ioctl_genprotk(struct pkey_genprotk __user *ugp)
{
struct pkey_genprotk kgp;
int rc;
if (copy_from_user(&kgp, ugp, sizeof(kgp)))
return -EFAULT;
kgp.protkey.len = sizeof(kgp.protkey.protkey);
rc = pkey_handler_gen_key(NULL, 0, kgp.keytype,
PKEY_TYPE_PROTKEY, 0, 0,
kgp.protkey.protkey, &kgp.protkey.len,
&kgp.protkey.type, 0);
pr_debug("gen_key()=%d\n", rc);
if (!rc && copy_to_user(ugp, &kgp, sizeof(kgp)))
rc = -EFAULT;
memzero_explicit(&kgp, sizeof(kgp));
return rc;
}
static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp)
{
struct pkey_verifyprotk kvp;
struct protaeskeytoken *t;
u8 *tmpbuf;
int rc;
if (copy_from_user(&kvp, uvp, sizeof(kvp)))
return -EFAULT;
if (kvp.protkey.len > sizeof(kvp.protkey.protkey)) {
PKEY_DBF_ERR("%s protkey length %u exceeds protkey buffer size\n",
__func__, kvp.protkey.len);
memzero_explicit(&kvp, sizeof(kvp));
return -EINVAL;
}
/* build a 'protected key token' from the raw protected key */
tmpbuf = kzalloc(sizeof(*t), GFP_KERNEL);
if (!tmpbuf) {
memzero_explicit(&kvp, sizeof(kvp));
return -ENOMEM;
}
t = (struct protaeskeytoken *)tmpbuf;
t->type = TOKTYPE_NON_CCA;
t->version = TOKVER_PROTECTED_KEY;
t->keytype = kvp.protkey.type;
t->len = kvp.protkey.len;
memcpy(t->protkey, kvp.protkey.protkey, kvp.protkey.len);
rc = pkey_handler_verify_key(tmpbuf, sizeof(*t),
NULL, NULL, NULL, NULL, NULL, 0);
pr_debug("verify_key()=%d\n", rc);
kfree_sensitive(tmpbuf);
memzero_explicit(&kvp, sizeof(kvp));
return rc;
}
static int pkey_ioctl_kblob2protk(struct pkey_kblob2pkey __user *utp)
{
struct pkey_kblob2pkey ktp;
u8 *kkey;
int rc;
if (copy_from_user(&ktp, utp, sizeof(ktp)))
return -EFAULT;
kkey = _copy_key_from_user(ktp.key, ktp.keylen);
if (IS_ERR(kkey))
return PTR_ERR(kkey);
ktp.protkey.len = sizeof(ktp.protkey.protkey);
rc = key2protkey(NULL, 0, kkey, ktp.keylen,
ktp.protkey.protkey, &ktp.protkey.len,
&ktp.protkey.type, 0);
pr_debug("key2protkey()=%d\n", rc);
kfree_sensitive(kkey);
if (!rc && copy_to_user(utp, &ktp, sizeof(ktp)))
rc = -EFAULT;
memzero_explicit(&ktp, sizeof(ktp));
return rc;
}
static int pkey_ioctl_genseck2(struct pkey_genseck2 __user *ugs)
{
u32 klen = KEYBLOBBUFSIZE;
struct pkey_genseck2 kgs;
struct pkey_apqn *apqns;
u8 *kkey;
int rc;
u32 u;
if (copy_from_user(&kgs, ugs, sizeof(kgs)))
return -EFAULT;
u = pkey_aes_bitsize_to_keytype(kgs.size);
if (!u) {
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
__func__, kgs.size);
return -EINVAL;
}
apqns = _copy_apqns_from_user(kgs.apqns, kgs.apqn_entries);
if (IS_ERR(apqns))
return PTR_ERR(apqns);
kkey = kzalloc(klen, GFP_KERNEL);
if (!kkey) {
kfree(apqns);
return -ENOMEM;
}
rc = pkey_handler_gen_key(apqns, kgs.apqn_entries,
u, kgs.type, kgs.size, kgs.keygenflags,
kkey, &klen, NULL, 0);
pr_debug("gen_key()=%d\n", rc);
kfree(apqns);
if (rc) {
kfree_sensitive(kkey);
return rc;
}
if (kgs.key) {
if (kgs.keylen < klen) {
kfree_sensitive(kkey);
return -EINVAL;
}
if (copy_to_user(kgs.key, kkey, klen)) {
kfree_sensitive(kkey);
return -EFAULT;
}
}
kgs.keylen = klen;
if (copy_to_user(ugs, &kgs, sizeof(kgs)))
rc = -EFAULT;
kfree_sensitive(kkey);
return rc;
}
static int pkey_ioctl_clr2seck2(struct pkey_clr2seck2 __user *ucs)
{
u32 klen = KEYBLOBBUFSIZE;
struct pkey_clr2seck2 kcs;
struct pkey_apqn *apqns;
u8 *kkey;
int rc;
u32 u;
if (copy_from_user(&kcs, ucs, sizeof(kcs)))
return -EFAULT;
u = pkey_aes_bitsize_to_keytype(kcs.size);
if (!u) {
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
__func__, kcs.size);
memzero_explicit(&kcs, sizeof(kcs));
return -EINVAL;
}
apqns = _copy_apqns_from_user(kcs.apqns, kcs.apqn_entries);
if (IS_ERR(apqns)) {
memzero_explicit(&kcs, sizeof(kcs));
return PTR_ERR(apqns);
}
kkey = kzalloc(klen, GFP_KERNEL);
if (!kkey) {
kfree(apqns);
memzero_explicit(&kcs, sizeof(kcs));
return -ENOMEM;
}
rc = pkey_handler_clr_to_key(apqns, kcs.apqn_entries,
u, kcs.type, kcs.size, kcs.keygenflags,
kcs.clrkey.clrkey, kcs.size / 8,
kkey, &klen, NULL, 0);
pr_debug("clr_to_key()=%d\n", rc);
kfree(apqns);
if (rc) {
kfree_sensitive(kkey);
memzero_explicit(&kcs, sizeof(kcs));
return rc;
}
if (kcs.key) {
if (kcs.keylen < klen) {
kfree_sensitive(kkey);
memzero_explicit(&kcs, sizeof(kcs));
return -EINVAL;
}
if (copy_to_user(kcs.key, kkey, klen)) {
kfree_sensitive(kkey);
memzero_explicit(&kcs, sizeof(kcs));
return -EFAULT;
}
}
kcs.keylen = klen;
if (copy_to_user(ucs, &kcs, sizeof(kcs)))
rc = -EFAULT;
memzero_explicit(&kcs, sizeof(kcs));
kfree_sensitive(kkey);
return rc;
}
static int pkey_ioctl_verifykey2(struct pkey_verifykey2 __user *uvk)
{
struct pkey_verifykey2 kvk;
u8 *kkey;
int rc;
if (copy_from_user(&kvk, uvk, sizeof(kvk)))
return -EFAULT;
kkey = _copy_key_from_user(kvk.key, kvk.keylen);
if (IS_ERR(kkey))
return PTR_ERR(kkey);
rc = pkey_handler_verify_key(kkey, kvk.keylen,
&kvk.cardnr, &kvk.domain,
&kvk.type, &kvk.size, &kvk.flags, 0);
pr_debug("verify_key()=%d\n", rc);
kfree_sensitive(kkey);
if (!rc && copy_to_user(uvk, &kvk, sizeof(kvk)))
return -EFAULT;
return rc;
}
static int pkey_ioctl_kblob2protk2(struct pkey_kblob2pkey2 __user *utp)
{
struct pkey_apqn *apqns = NULL;
struct pkey_kblob2pkey2 ktp;
u8 *kkey;
int rc;
if (copy_from_user(&ktp, utp, sizeof(ktp)))
return -EFAULT;
apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries);
if (IS_ERR(apqns))
return PTR_ERR(apqns);
kkey = _copy_key_from_user(ktp.key, ktp.keylen);
if (IS_ERR(kkey)) {
kfree(apqns);
return PTR_ERR(kkey);
}
ktp.protkey.len = sizeof(ktp.protkey.protkey);
rc = key2protkey(apqns, ktp.apqn_entries, kkey, ktp.keylen,
ktp.protkey.protkey, &ktp.protkey.len,
&ktp.protkey.type, 0);
pr_debug("key2protkey()=%d\n", rc);
kfree(apqns);
kfree_sensitive(kkey);
if (!rc && copy_to_user(utp, &ktp, sizeof(ktp)))
rc = -EFAULT;
memzero_explicit(&ktp, sizeof(ktp));
return rc;
}
static int pkey_ioctl_apqns4k(struct pkey_apqns4key __user *uak)
{
struct pkey_apqn *apqns = NULL;
struct pkey_apqns4key kak;
size_t nr_apqns, len;
u8 *kkey;
int rc;
if (copy_from_user(&kak, uak, sizeof(kak)))
return -EFAULT;
nr_apqns = kak.apqn_entries;
if (nr_apqns) {
apqns = kmalloc_objs(struct pkey_apqn, nr_apqns);
if (!apqns)
return -ENOMEM;
}
kkey = _copy_key_from_user(kak.key, kak.keylen);
if (IS_ERR(kkey)) {
kfree(apqns);
return PTR_ERR(kkey);
}
rc = pkey_handler_apqns_for_key(kkey, kak.keylen, kak.flags,
apqns, &nr_apqns, 0);
pr_debug("apqns_for_key()=%d\n", rc);
kfree_sensitive(kkey);
if (rc && rc != -ENOSPC) {
kfree(apqns);
return rc;
}
if (!rc && kak.apqns) {
if (nr_apqns > kak.apqn_entries) {
kfree(apqns);
return -EINVAL;
}
len = nr_apqns * sizeof(struct pkey_apqn);
if (len) {
if (copy_to_user(kak.apqns, apqns, len)) {
kfree(apqns);
return -EFAULT;
}
}
}
kak.apqn_entries = nr_apqns;
if (copy_to_user(uak, &kak, sizeof(kak)))
rc = -EFAULT;
kfree(apqns);
return rc;
}
static int pkey_ioctl_apqns4kt(struct pkey_apqns4keytype __user *uat)
{
struct pkey_apqn *apqns = NULL;
struct pkey_apqns4keytype kat;
size_t nr_apqns, len;
int rc;
if (copy_from_user(&kat, uat, sizeof(kat)))
return -EFAULT;
nr_apqns = kat.apqn_entries;
if (nr_apqns) {
apqns = kmalloc_objs(struct pkey_apqn, nr_apqns);
if (!apqns)
return -ENOMEM;
}
rc = pkey_handler_apqns_for_keytype(kat.type,
kat.cur_mkvp, kat.alt_mkvp,
kat.flags, apqns, &nr_apqns, 0);
pr_debug("apqns_for_keytype()=%d\n", rc);
if (rc && rc != -ENOSPC) {
kfree(apqns);
return rc;
}
if (!rc && kat.apqns) {
if (nr_apqns > kat.apqn_entries) {
kfree(apqns);
return -EINVAL;
}
len = nr_apqns * sizeof(struct pkey_apqn);
if (len) {
if (copy_to_user(kat.apqns, apqns, len)) {
kfree(apqns);
return -EFAULT;
}
}
}
kat.apqn_entries = nr_apqns;
if (copy_to_user(uat, &kat, sizeof(kat)))
rc = -EFAULT;
kfree(apqns);
return rc;
}
static int pkey_ioctl_kblob2protk3(struct pkey_kblob2pkey3 __user *utp)
{
u32 protkeylen = PROTKEYBLOBBUFSIZE;
struct pkey_apqn *apqns = NULL;
struct pkey_kblob2pkey3 ktp;
u8 *kkey, *protkey;
int rc;
if (copy_from_user(&ktp, utp, sizeof(ktp)))
return -EFAULT;
apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries);
if (IS_ERR(apqns))
return PTR_ERR(apqns);
kkey = _copy_key_from_user(ktp.key, ktp.keylen);
if (IS_ERR(kkey)) {
kfree(apqns);
return PTR_ERR(kkey);
}
protkey = kmalloc(protkeylen, GFP_KERNEL);
if (!protkey) {
kfree(apqns);
kfree_sensitive(kkey);
return -ENOMEM;
}
rc = key2protkey(apqns, ktp.apqn_entries, kkey, ktp.keylen,
protkey, &protkeylen, &ktp.pkeytype, 0);
pr_debug("key2protkey()=%d\n", rc);
kfree(apqns);
kfree_sensitive(kkey);
if (rc) {
kfree_sensitive(protkey);
return rc;
}
if (ktp.pkey && ktp.pkeylen) {
if (protkeylen > ktp.pkeylen) {
kfree_sensitive(protkey);
return -EINVAL;
}
if (copy_to_user(ktp.pkey, protkey, protkeylen)) {
kfree_sensitive(protkey);
return -EFAULT;
}
}
kfree_sensitive(protkey);
ktp.pkeylen = protkeylen;
if (copy_to_user(utp, &ktp, sizeof(ktp)))
return -EFAULT;
return 0;
}
static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int rc;
switch (cmd) {
case PKEY_GENSECK:
rc = pkey_ioctl_genseck((struct pkey_genseck __user *)arg);
break;
case PKEY_CLR2SECK:
rc = pkey_ioctl_clr2seck((struct pkey_clr2seck __user *)arg);
break;
case PKEY_SEC2PROTK:
rc = pkey_ioctl_sec2protk((struct pkey_sec2protk __user *)arg);
break;
case PKEY_CLR2PROTK:
rc = pkey_ioctl_clr2protk((struct pkey_clr2protk __user *)arg);
break;
case PKEY_FINDCARD:
rc = pkey_ioctl_findcard((struct pkey_findcard __user *)arg);
break;
case PKEY_SKEY2PKEY:
rc = pkey_ioctl_skey2pkey((struct pkey_skey2pkey __user *)arg);
break;
case PKEY_VERIFYKEY:
rc = pkey_ioctl_verifykey((struct pkey_verifykey __user *)arg);
break;
case PKEY_GENPROTK:
rc = pkey_ioctl_genprotk((struct pkey_genprotk __user *)arg);
break;
case PKEY_VERIFYPROTK:
rc = pkey_ioctl_verifyprotk((struct pkey_verifyprotk __user *)arg);
break;
case PKEY_KBLOB2PROTK:
rc = pkey_ioctl_kblob2protk((struct pkey_kblob2pkey __user *)arg);
break;
case PKEY_GENSECK2:
rc = pkey_ioctl_genseck2((struct pkey_genseck2 __user *)arg);
break;
case PKEY_CLR2SECK2:
rc = pkey_ioctl_clr2seck2((struct pkey_clr2seck2 __user *)arg);
break;
case PKEY_VERIFYKEY2:
rc = pkey_ioctl_verifykey2((struct pkey_verifykey2 __user *)arg);
break;
case PKEY_KBLOB2PROTK2:
rc = pkey_ioctl_kblob2protk2((struct pkey_kblob2pkey2 __user *)arg);
break;
case PKEY_APQNS4K:
rc = pkey_ioctl_apqns4k((struct pkey_apqns4key __user *)arg);
break;
case PKEY_APQNS4KT:
rc = pkey_ioctl_apqns4kt((struct pkey_apqns4keytype __user *)arg);
break;
case PKEY_KBLOB2PROTK3:
rc = pkey_ioctl_kblob2protk3((struct pkey_kblob2pkey3 __user *)arg);
break;
default:
/* unknown/unsupported ioctl cmd */
return -ENOTTY;
}
return rc;
}
/*
* File io operations
*/
static const struct file_operations pkey_fops = {
.owner = THIS_MODULE,
.open = nonseekable_open,
.unlocked_ioctl = pkey_unlocked_ioctl,
};
static struct miscdevice pkey_dev = {
.name = "pkey",
.minor = MISC_DYNAMIC_MINOR,
.mode = 0666,
.fops = &pkey_fops,
.groups = pkey_attr_groups,
};
int __init pkey_api_init(void)
{
/* register as a misc device */
return misc_register(&pkey_dev);
}
void __exit pkey_api_exit(void)
{
misc_deregister(&pkey_dev);
}
+387
View File
@@ -0,0 +1,387 @@
// SPDX-License-Identifier: GPL-2.0
/*
* pkey base: debug feature, pkey handler registry
*
* Copyright IBM Corp. 2024
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/cpufeature.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/rculist.h>
#include "pkey_base.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 protected key base and api");
/*
* pkey debug feature
*/
debug_info_t *pkey_dbf_info;
EXPORT_SYMBOL(pkey_dbf_info);
/*
* pkey handler registry
*/
static DEFINE_SPINLOCK(handler_list_write_lock);
static LIST_HEAD(handler_list);
int pkey_handler_register(struct pkey_handler *handler)
{
const struct pkey_handler *h;
if (!handler ||
!handler->is_supported_key ||
!handler->is_supported_keytype)
return -EINVAL;
if (!try_module_get(handler->module))
return -ENXIO;
spin_lock(&handler_list_write_lock);
rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (h == handler) {
rcu_read_unlock();
spin_unlock(&handler_list_write_lock);
module_put(handler->module);
return -EEXIST;
}
}
rcu_read_unlock();
list_add_rcu(&handler->list, &handler_list);
spin_unlock(&handler_list_write_lock);
/*
* Fast path to push the info about the updated list to the other
* CPUs. If removed, the other CPUs may get the updated list when the
* RCU context is synched. As this code is in general not performance
* critical and the list update mostly only occurs at the early time in
* system startup the focus is on concurrency versus performance.
*/
synchronize_rcu();
module_put(handler->module);
PKEY_DBF_INFO("%s pkey handler '%s' registered\n", __func__,
handler->name ?: "<no name>");
return 0;
}
EXPORT_SYMBOL(pkey_handler_register);
int pkey_handler_unregister(struct pkey_handler *handler)
{
spin_lock(&handler_list_write_lock);
list_del_rcu(&handler->list);
INIT_LIST_HEAD_RCU(&handler->list);
spin_unlock(&handler_list_write_lock);
synchronize_rcu();
PKEY_DBF_INFO("%s pkey handler '%s' unregistered\n", __func__,
handler->name ?: "<no name>");
return 0;
}
EXPORT_SYMBOL(pkey_handler_unregister);
/*
* Handler invocation functions.
*/
const struct pkey_handler *pkey_handler_get_keybased(const u8 *key, u32 keylen)
{
const struct pkey_handler *h;
rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (!try_module_get(h->module))
continue;
if (h->is_supported_key(key, keylen)) {
rcu_read_unlock();
return h;
}
module_put(h->module);
}
rcu_read_unlock();
return NULL;
}
EXPORT_SYMBOL(pkey_handler_get_keybased);
const struct pkey_handler *pkey_handler_get_keytypebased(enum pkey_key_type kt)
{
const struct pkey_handler *h;
rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (!try_module_get(h->module))
continue;
if (h->is_supported_keytype(kt)) {
rcu_read_unlock();
return h;
}
module_put(h->module);
}
rcu_read_unlock();
return NULL;
}
EXPORT_SYMBOL(pkey_handler_get_keytypebased);
void pkey_handler_put(const struct pkey_handler *handler)
{
const struct pkey_handler *h;
if (!handler)
return;
rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (h == handler) {
module_put(h->module);
break;
}
}
rcu_read_unlock();
}
EXPORT_SYMBOL(pkey_handler_put);
int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags)
{
const struct pkey_handler *h;
int rc = -ENODEV;
h = pkey_handler_get_keybased(key, keylen);
if (h && h->key_to_protkey) {
rc = h->key_to_protkey(apqns, nr_apqns, key, keylen,
protkey, protkeylen,
protkeytype, xflags);
}
pkey_handler_put(h);
return rc;
}
EXPORT_SYMBOL(pkey_handler_key_to_protkey);
/*
* This handler invocation is special as there may be more than
* one handler providing support for the very same key (type).
* And the handler may not respond true on is_supported_key(),
* so simple try and check return value here.
*/
int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype, u32 xflags)
{
const struct pkey_handler *h, *htmp[10];
int i, n = 0, rc = -ENODEV;
rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (!try_module_get(h->module))
continue;
if (h->slowpath_key_to_protkey && n < ARRAY_SIZE(htmp))
htmp[n++] = h;
else
module_put(h->module);
}
rcu_read_unlock();
for (i = 0; i < n; i++) {
h = htmp[i];
if (rc)
rc = h->slowpath_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype, xflags);
module_put(h->module);
}
return rc;
}
EXPORT_SYMBOL(pkey_handler_slowpath_key_to_protkey);
int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo, u32 xflags)
{
const struct pkey_handler *h;
int rc = -ENODEV;
h = pkey_handler_get_keytypebased(keysubtype);
if (h && h->gen_key) {
rc = h->gen_key(apqns, nr_apqns, keytype, keysubtype,
keybitsize, flags,
keybuf, keybuflen, keyinfo, xflags);
}
pkey_handler_put(h);
return rc;
}
EXPORT_SYMBOL(pkey_handler_gen_key);
int pkey_handler_clr_to_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
const u8 *clrkey, u32 clrkeylen,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo,
u32 xflags)
{
const struct pkey_handler *h;
int rc = -ENODEV;
h = pkey_handler_get_keytypebased(keysubtype);
if (h && h->clr_to_key) {
rc = h->clr_to_key(apqns, nr_apqns, keytype, keysubtype,
keybitsize, flags, clrkey, clrkeylen,
keybuf, keybuflen, keyinfo, xflags);
}
pkey_handler_put(h);
return rc;
}
EXPORT_SYMBOL(pkey_handler_clr_to_key);
int pkey_handler_verify_key(const u8 *key, u32 keylen,
u16 *card, u16 *dom,
u32 *keytype, u32 *keybitsize, u32 *flags,
u32 xflags)
{
const struct pkey_handler *h;
int rc = -ENODEV;
h = pkey_handler_get_keybased(key, keylen);
if (h && h->verify_key) {
rc = h->verify_key(key, keylen, card, dom,
keytype, keybitsize, flags, xflags);
}
pkey_handler_put(h);
return rc;
}
EXPORT_SYMBOL(pkey_handler_verify_key);
int pkey_handler_apqns_for_key(const u8 *key, u32 keylen, u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns,
u32 xflags)
{
const struct pkey_handler *h;
int rc = -ENODEV;
h = pkey_handler_get_keybased(key, keylen);
if (h && h->apqns_for_key)
rc = h->apqns_for_key(key, keylen, flags, apqns, nr_apqns,
xflags);
pkey_handler_put(h);
return rc;
}
EXPORT_SYMBOL(pkey_handler_apqns_for_key);
int pkey_handler_apqns_for_keytype(enum pkey_key_type keysubtype,
u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns,
u32 xflags)
{
const struct pkey_handler *h;
int rc = -ENODEV;
h = pkey_handler_get_keytypebased(keysubtype);
if (h && h->apqns_for_keytype) {
rc = h->apqns_for_keytype(keysubtype,
cur_mkvp, alt_mkvp, flags,
apqns, nr_apqns, xflags);
}
pkey_handler_put(h);
return rc;
}
EXPORT_SYMBOL(pkey_handler_apqns_for_keytype);
void pkey_handler_request_modules(void)
{
#ifdef CONFIG_MODULES
static const char * const pkey_handler_modules[] = {
#if IS_MODULE(CONFIG_PKEY_CCA)
"pkey_cca",
#endif
#if IS_MODULE(CONFIG_PKEY_EP11)
"pkey_ep11",
#endif
#if IS_MODULE(CONFIG_PKEY_PCKMO)
"pkey_pckmo",
#endif
#if IS_MODULE(CONFIG_PKEY_UV)
"pkey_uv",
#endif
};
int i;
for (i = 0; i < ARRAY_SIZE(pkey_handler_modules); i++) {
const struct pkey_handler *h;
bool found = false;
rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (h->module &&
!strcmp(h->module->name, pkey_handler_modules[i])) {
found = true;
break;
}
}
rcu_read_unlock();
if (!found) {
pr_debug("request_module(%s)\n", pkey_handler_modules[i]);
request_module(pkey_handler_modules[i]);
}
}
#endif
}
EXPORT_SYMBOL(pkey_handler_request_modules);
/*
* Module init
*/
static int __init pkey_init(void)
{
int rc;
/* init debug feature */
pkey_dbf_info = debug_register("pkey", 1, 1, 5 * sizeof(long));
debug_register_view(pkey_dbf_info, &debug_sprintf_view);
debug_set_level(pkey_dbf_info, 4);
/* the handler registry does not need any init */
rc = pkey_api_init();
if (rc)
debug_unregister(pkey_dbf_info);
return rc;
}
/*
* Module exit
*/
static void __exit pkey_exit(void)
{
pkey_api_exit();
}
module_cpu_feature_match(S390_CPU_FEATURE_MSA, pkey_init);
module_exit(pkey_exit);
+240
View File
@@ -0,0 +1,240 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2024
*
* Pkey base: debug feature, defines and structs
* common to all pkey code.
*/
#ifndef _PKEY_BASE_H_
#define _PKEY_BASE_H_
#include <linux/types.h>
#include <asm/debug.h>
#include <asm/pkey.h>
/*
* pkey debug feature
*/
extern debug_info_t *pkey_dbf_info;
#define PKEY_DBF_INFO(...) debug_sprintf_event(pkey_dbf_info, 5, ##__VA_ARGS__)
#define PKEY_DBF_WARN(...) debug_sprintf_event(pkey_dbf_info, 4, ##__VA_ARGS__)
#define PKEY_DBF_ERR(...) debug_sprintf_event(pkey_dbf_info, 3, ##__VA_ARGS__)
/*
* common defines and common structs
*/
#define KEYBLOBBUFSIZE 8192 /* key buffer size used for internal processing */
#define MINKEYBLOBBUFSIZE (sizeof(struct keytoken_header))
#define PROTKEYBLOBBUFSIZE 256 /* protected key buffer size used internal */
#define MAXAPQNSINLIST 64 /* max 64 apqns within a apqn list */
#define AES_WK_VP_SIZE 32 /* Size of WK VP block appended to a prot key */
/* inside view of a generic protected key token */
struct protkeytoken {
u8 type; /* 0x00 for PAES specific key tokens */
u8 res0[3];
u8 version; /* should be 0x01 for protected key token */
u8 res1[3];
u32 keytype; /* key type, one of the PKEY_KEYTYPE values */
u32 len; /* bytes actually stored in protkey[] */
u8 protkey[]; /* the protected key blob */
} __packed;
/* inside view of a protected AES key token */
struct protaeskeytoken {
u8 type; /* 0x00 for PAES specific key tokens */
u8 res0[3];
u8 version; /* should be 0x01 for protected key token */
u8 res1[3];
u32 keytype; /* key type, one of the PKEY_KEYTYPE values */
u32 len; /* bytes actually stored in protkey[] */
u8 protkey[MAXPROTKEYSIZE]; /* the protected key blob */
} __packed;
/* inside view of a clear key token (type 0x00 version 0x02) */
struct clearkeytoken {
u8 type; /* 0x00 for PAES specific key tokens */
u8 res0[3];
u8 version; /* 0x02 for clear key token */
u8 res1[3];
u32 keytype; /* key type, one of the PKEY_KEYTYPE_* values */
u32 len; /* bytes actually stored in clearkey[] */
u8 clearkey[]; /* clear key value */
} __packed;
/* helper function which translates the PKEY_KEYTYPE_AES_* to their keysize */
static inline u32 pkey_keytype_aes_to_size(u32 keytype)
{
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
return 16;
case PKEY_KEYTYPE_AES_192:
return 24;
case PKEY_KEYTYPE_AES_256:
return 32;
default:
return 0;
}
}
/* helper function which translates AES key bit size into PKEY_KEYTYPE_AES_* */
static inline u32 pkey_aes_bitsize_to_keytype(u32 keybitsize)
{
switch (keybitsize) {
case 128:
return PKEY_KEYTYPE_AES_128;
case 192:
return PKEY_KEYTYPE_AES_192;
case 256:
return PKEY_KEYTYPE_AES_256;
default:
return 0;
}
}
/*
* helper function which translates the PKEY_KEYTYPE_*
* to the protected key size minus the WK VP length
*/
static inline u32 pkey_keytype_to_size(u32 keytype)
{
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
return 16;
case PKEY_KEYTYPE_AES_192:
return 24;
case PKEY_KEYTYPE_AES_256:
return 32;
case PKEY_KEYTYPE_ECC_P256:
return 32;
case PKEY_KEYTYPE_ECC_P384:
return 48;
case PKEY_KEYTYPE_ECC_P521:
return 80;
case PKEY_KEYTYPE_ECC_ED25519:
return 32;
case PKEY_KEYTYPE_ECC_ED448:
return 54;
case PKEY_KEYTYPE_AES_XTS_128:
return 32;
case PKEY_KEYTYPE_AES_XTS_256:
return 64;
case PKEY_KEYTYPE_HMAC_512:
return 64;
case PKEY_KEYTYPE_HMAC_1024:
return 128;
default:
return 0;
}
}
/*
* pkey_api.c:
*/
int __init pkey_api_init(void);
void __exit pkey_api_exit(void);
/*
* pkey_sysfs.c:
*/
extern const struct attribute_group *pkey_attr_groups[];
/*
* pkey handler registry
*/
struct pkey_handler {
struct module *module;
const char *name;
/*
* is_supported_key() and is_supported_keytype() are called
* within an rcu_read_lock() scope and thus must not sleep!
*/
bool (*is_supported_key)(const u8 *key, u32 keylen);
bool (*is_supported_keytype)(enum pkey_key_type);
int (*key_to_protkey)(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags);
int (*slowpath_key_to_protkey)(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype, u32 xflags);
int (*gen_key)(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo, u32 xflags);
int (*clr_to_key)(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
const u8 *clrkey, u32 clrkeylen,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo, u32 xflags);
int (*verify_key)(const u8 *key, u32 keylen,
u16 *card, u16 *dom,
u32 *keytype, u32 *keybitsize, u32 *flags,
u32 xflags);
int (*apqns_for_key)(const u8 *key, u32 keylen, u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns,
u32 xflags);
int (*apqns_for_keytype)(enum pkey_key_type ktype,
u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns,
u32 xflags);
/* used internal by pkey base */
struct list_head list;
};
int pkey_handler_register(struct pkey_handler *handler);
int pkey_handler_unregister(struct pkey_handler *handler);
/*
* invocation function for the registered pkey handlers
*/
const struct pkey_handler *pkey_handler_get_keybased(const u8 *key, u32 keylen);
const struct pkey_handler *pkey_handler_get_keytypebased(enum pkey_key_type kt);
void pkey_handler_put(const struct pkey_handler *handler);
int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags);
int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype, u32 xflags);
int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo, u32 xflags);
int pkey_handler_clr_to_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
const u8 *clrkey, u32 clrkeylen,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo,
u32 xflags);
int pkey_handler_verify_key(const u8 *key, u32 keylen,
u16 *card, u16 *dom,
u32 *keytype, u32 *keybitsize, u32 *flags,
u32 xflags);
int pkey_handler_apqns_for_key(const u8 *key, u32 keylen, u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns,
u32 xflags);
int pkey_handler_apqns_for_keytype(enum pkey_key_type ktype,
u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns,
u32 xflags);
/*
* Unconditional try to load all handler modules
*/
void pkey_handler_request_modules(void);
#endif /* _PKEY_BASE_H_ */
+629
View File
@@ -0,0 +1,629 @@
// SPDX-License-Identifier: GPL-2.0
/*
* pkey cca specific code
*
* Copyright IBM Corp. 2024
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cpufeature.h>
#include "zcrypt_ccamisc.h"
#include "pkey_base.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 protected key CCA handler");
#if IS_MODULE(CONFIG_PKEY_CCA)
static struct ap_device_id pkey_cca_card_ids[] = {
{ .dev_type = AP_DEVICE_TYPE_CEX4 },
{ .dev_type = AP_DEVICE_TYPE_CEX5 },
{ .dev_type = AP_DEVICE_TYPE_CEX6 },
{ .dev_type = AP_DEVICE_TYPE_CEX7 },
{ .dev_type = AP_DEVICE_TYPE_CEX8 },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(ap, pkey_cca_card_ids);
#endif
/*
* Check key blob for known and supported CCA key.
*/
static bool is_cca_key(const u8 *key, u32 keylen)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
if (keylen < sizeof(*hdr))
return false;
switch (hdr->type) {
case TOKTYPE_CCA_INTERNAL:
switch (hdr->version) {
case TOKVER_CCA_AES:
case TOKVER_CCA_VLSC:
return true;
default:
return false;
}
case TOKTYPE_CCA_INTERNAL_PKA:
return true;
default:
return false;
}
}
static bool is_cca_keytype(enum pkey_key_type key_type)
{
switch (key_type) {
case PKEY_TYPE_CCA_DATA:
case PKEY_TYPE_CCA_CIPHER:
case PKEY_TYPE_CCA_ECC:
return true;
default:
return false;
}
}
static int cca_apqns4key(const u8 *key, u32 keylen, u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns, u32 pflags)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
u32 _apqns[MAXAPQNSINLIST], _nr_apqns = ARRAY_SIZE(_apqns);
u32 xflags;
int rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
if (!flags)
flags = PKEY_FLAGS_MATCH_CUR_MKVP | PKEY_FLAGS_MATCH_ALT_MKVP;
if (keylen < sizeof(struct keytoken_header))
return -EINVAL;
zcrypt_wait_api_operational();
if (hdr->type == TOKTYPE_CCA_INTERNAL) {
const u8 *ptr_cur_mkvp = NULL;
const u8 *ptr_old_mkvp = NULL;
int minhwtype = ZCRYPT_CEX3C;
if (hdr->version == TOKVER_CCA_AES) {
struct secaeskeytoken *t = (struct secaeskeytoken *)key;
if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
ptr_cur_mkvp = t->mkvp;
if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
ptr_old_mkvp = t->mkvp;
} else if (hdr->version == TOKVER_CCA_VLSC) {
struct cipherkeytoken *t = (struct cipherkeytoken *)key;
minhwtype = ZCRYPT_CEX6;
if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
ptr_cur_mkvp = t->mkvp0;
if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
ptr_old_mkvp = t->mkvp0;
} else {
/* unknown CCA internal token type */
return -EINVAL;
}
rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
minhwtype, AES_MK_SET,
ptr_cur_mkvp, ptr_old_mkvp, xflags);
if (rc)
goto out;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
struct eccprivkeytoken *t = (struct eccprivkeytoken *)key;
const u8 *ptr_cur_mkvp = NULL;
const u8 *ptr_old_mkvp = NULL;
if (t->secid == 0x20) {
if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
ptr_cur_mkvp = t->mkvp;
if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
ptr_old_mkvp = t->mkvp;
} else {
/* unknown CCA internal 2 token type */
return -EINVAL;
}
rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
ZCRYPT_CEX7, APKA_MK_SET,
ptr_cur_mkvp, ptr_old_mkvp, xflags);
if (rc)
goto out;
} else {
PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
__func__, hdr->type, hdr->version);
return -EINVAL;
}
if (apqns) {
if (*nr_apqns < _nr_apqns)
rc = -ENOSPC;
else
memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
}
*nr_apqns = _nr_apqns;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int cca_apqns4type(enum pkey_key_type ktype,
u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns,
u32 pflags)
{
u32 _apqns[MAXAPQNSINLIST], _nr_apqns = ARRAY_SIZE(_apqns);
u32 xflags;
int rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
zcrypt_wait_api_operational();
if (ktype == PKEY_TYPE_CCA_DATA || ktype == PKEY_TYPE_CCA_CIPHER) {
const u8 *ptr_cur_mkvp = NULL;
const u8 *ptr_old_mkvp = NULL;
int minhwtype = ZCRYPT_CEX3C;
if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
ptr_cur_mkvp = cur_mkvp;
if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
ptr_old_mkvp = alt_mkvp;
if (ktype == PKEY_TYPE_CCA_CIPHER)
minhwtype = ZCRYPT_CEX6;
rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
minhwtype, AES_MK_SET,
ptr_cur_mkvp, ptr_old_mkvp, xflags);
if (rc)
goto out;
} else if (ktype == PKEY_TYPE_CCA_ECC) {
const u8 *ptr_cur_mkvp = NULL;
const u8 *ptr_old_mkvp = NULL;
if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
ptr_cur_mkvp = cur_mkvp;
if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
ptr_old_mkvp = alt_mkvp;
rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
ZCRYPT_CEX7, APKA_MK_SET,
ptr_cur_mkvp, ptr_old_mkvp, xflags);
if (rc)
goto out;
} else {
PKEY_DBF_ERR("%s unknown/unsupported key type %d",
__func__, (int)ktype);
return -EINVAL;
}
if (apqns) {
if (*nr_apqns < _nr_apqns)
rc = -ENOSPC;
else
memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
}
*nr_apqns = _nr_apqns;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 pflags)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
struct pkey_apqn _apqns[MAXAPQNSINLIST];
u32 xflags;
int i, rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
if (keylen < sizeof(*hdr))
return -EINVAL;
if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_AES) {
/* CCA AES data key */
if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0))
return -EINVAL;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_VLSC) {
/* CCA AES cipher key */
if (cca_check_secaescipherkey(pkey_dbf_info,
3, key, keylen, 0, 1))
return -EINVAL;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
/* CCA ECC (private) key */
if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1))
return -EINVAL;
} else {
PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
__func__, hdr->type, hdr->version);
return -EINVAL;
}
zcrypt_wait_api_operational();
if (!apqns || (nr_apqns == 1 &&
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
nr_apqns = MAXAPQNSINLIST;
rc = cca_apqns4key(key, keylen, 0, _apqns, &nr_apqns, pflags);
if (rc)
goto out;
apqns = _apqns;
}
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_AES) {
rc = cca_sec2protkey(apqns[i].card, apqns[i].domain,
key, protkey,
protkeylen, protkeytype, xflags);
} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_VLSC) {
rc = cca_cipher2protkey(apqns[i].card, apqns[i].domain,
key, protkey,
protkeylen, protkeytype, xflags);
} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
rc = cca_ecc2protkey(apqns[i].card, apqns[i].domain,
key, protkey,
protkeylen, protkeytype, xflags);
} else {
rc = -EINVAL;
break;
}
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Generate CCA secure key.
* As of now only CCA AES Data or Cipher secure keys are
* supported.
* keytype is one of the PKEY_KEYTYPE_* constants,
* subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER,
* keybitsize is the bit size of the key (may be 0 for
* keytype PKEY_KEYTYPE_AES_*).
*/
static int cca_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 subtype,
u32 keybitsize, u32 flags,
u8 *keybuf, u32 *keybuflen, u32 *_keyinfo, u32 pflags)
{
struct pkey_apqn _apqns[MAXAPQNSINLIST];
int i, len, rc;
u32 xflags;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
/* check keytype, subtype, keybitsize */
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
len = pkey_keytype_aes_to_size(keytype);
if (keybitsize && keybitsize != 8 * len) {
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
__func__, keybitsize);
return -EINVAL;
}
keybitsize = 8 * len;
switch (subtype) {
case PKEY_TYPE_CCA_DATA:
case PKEY_TYPE_CCA_CIPHER:
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
__func__, subtype);
return -EINVAL;
}
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
__func__, keytype);
return -EINVAL;
}
zcrypt_wait_api_operational();
if (!apqns || (nr_apqns == 1 &&
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
nr_apqns = MAXAPQNSINLIST;
rc = cca_apqns4type(subtype, NULL, NULL, 0,
_apqns, &nr_apqns, pflags);
if (rc)
goto out;
apqns = _apqns;
}
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
if (subtype == PKEY_TYPE_CCA_CIPHER) {
rc = cca_gencipherkey(apqns[i].card, apqns[i].domain,
keybitsize, flags,
keybuf, keybuflen, xflags);
} else {
/* PKEY_TYPE_CCA_DATA */
rc = cca_genseckey(apqns[i].card, apqns[i].domain,
keybitsize, keybuf, xflags);
*keybuflen = (rc ? 0 : SECKEYBLOBSIZE);
}
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Generate CCA secure key with given clear key value.
* As of now only CCA AES Data or Cipher secure keys are
* supported.
* keytype is one of the PKEY_KEYTYPE_* constants,
* subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER,
* keybitsize is the bit size of the key (may be 0 for
* keytype PKEY_KEYTYPE_AES_*).
*/
static int cca_clr2key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 subtype,
u32 keybitsize, u32 flags,
const u8 *clrkey, u32 clrkeylen,
u8 *keybuf, u32 *keybuflen, u32 *_keyinfo, u32 pflags)
{
struct pkey_apqn _apqns[MAXAPQNSINLIST];
int i, len, rc;
u32 xflags;
if (pflags & PKEY_XFLAG_NOCLEARKEY) {
PKEY_DBF_ERR("%s clear key but xflag NOCLEARKEY\n", __func__);
return -EINVAL;
}
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
/* check keytype, subtype, clrkeylen, keybitsize */
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
len = pkey_keytype_aes_to_size(keytype);
if (keybitsize && keybitsize != 8 * len) {
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
__func__, keybitsize);
return -EINVAL;
}
keybitsize = 8 * len;
if (clrkeylen != len) {
PKEY_DBF_ERR("%s invalid clear key len %d != %d\n",
__func__, clrkeylen, len);
return -EINVAL;
}
switch (subtype) {
case PKEY_TYPE_CCA_DATA:
case PKEY_TYPE_CCA_CIPHER:
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
__func__, subtype);
return -EINVAL;
}
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
__func__, keytype);
return -EINVAL;
}
zcrypt_wait_api_operational();
if (!apqns || (nr_apqns == 1 &&
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
nr_apqns = MAXAPQNSINLIST;
rc = cca_apqns4type(subtype, NULL, NULL, 0,
_apqns, &nr_apqns, pflags);
if (rc)
goto out;
apqns = _apqns;
}
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
if (subtype == PKEY_TYPE_CCA_CIPHER) {
rc = cca_clr2cipherkey(apqns[i].card, apqns[i].domain,
keybitsize, flags, clrkey,
keybuf, keybuflen, xflags);
} else {
/* PKEY_TYPE_CCA_DATA */
rc = cca_clr2seckey(apqns[i].card, apqns[i].domain,
keybitsize, clrkey, keybuf, xflags);
*keybuflen = (rc ? 0 : SECKEYBLOBSIZE);
}
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int cca_verifykey(const u8 *key, u32 keylen,
u16 *card, u16 *dom,
u32 *keytype, u32 *keybitsize, u32 *flags, u32 pflags)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
u32 apqns[MAXAPQNSINLIST], nr_apqns = ARRAY_SIZE(apqns);
u32 xflags;
int rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
if (keylen < sizeof(*hdr))
return -EINVAL;
zcrypt_wait_api_operational();
if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_AES) {
struct secaeskeytoken *t = (struct secaeskeytoken *)key;
rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0);
if (rc)
goto out;
*keytype = PKEY_TYPE_CCA_DATA;
*keybitsize = t->bitsize;
rc = cca_findcard2(apqns, &nr_apqns, *card, *dom,
ZCRYPT_CEX3C, AES_MK_SET,
t->mkvp, NULL, xflags);
if (!rc)
*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
if (rc == -ENODEV) {
nr_apqns = ARRAY_SIZE(apqns);
rc = cca_findcard2(apqns, &nr_apqns, *card, *dom,
ZCRYPT_CEX3C, AES_MK_SET,
NULL, t->mkvp, xflags);
if (!rc)
*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
}
if (rc)
goto out;
*card = ((struct pkey_apqn *)apqns)->card;
*dom = ((struct pkey_apqn *)apqns)->domain;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_VLSC) {
struct cipherkeytoken *t = (struct cipherkeytoken *)key;
rc = cca_check_secaescipherkey(pkey_dbf_info, 3,
key, keylen, 0, 1);
if (rc)
goto out;
*keytype = PKEY_TYPE_CCA_CIPHER;
*keybitsize = PKEY_SIZE_UNKNOWN;
if (!t->plfver && t->wpllen == 512)
*keybitsize = PKEY_SIZE_AES_128;
else if (!t->plfver && t->wpllen == 576)
*keybitsize = PKEY_SIZE_AES_192;
else if (!t->plfver && t->wpllen == 640)
*keybitsize = PKEY_SIZE_AES_256;
rc = cca_findcard2(apqns, &nr_apqns, *card, *dom,
ZCRYPT_CEX6, AES_MK_SET,
t->mkvp0, NULL, xflags);
if (!rc)
*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
if (rc == -ENODEV) {
nr_apqns = ARRAY_SIZE(apqns);
rc = cca_findcard2(apqns, &nr_apqns, *card, *dom,
ZCRYPT_CEX6, AES_MK_SET,
NULL, t->mkvp0, xflags);
if (!rc)
*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
}
if (rc)
goto out;
*card = ((struct pkey_apqn *)apqns)->card;
*dom = ((struct pkey_apqn *)apqns)->domain;
} else {
/* unknown/unsupported key blob */
rc = -EINVAL;
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* This function provides an alternate but usually slow way
* to convert a 'clear key token' with AES key material into
* a protected key. This is done via an intermediate step
* which creates a CCA AES DATA secure key first and then
* derives the protected key from this secure key.
*/
static int cca_slowpath_key2protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype, u32 pflags)
{
const struct keytoken_header *hdr = (const struct keytoken_header *)key;
const struct clearkeytoken *t = (const struct clearkeytoken *)key;
u8 tmpbuf[SECKEYBLOBSIZE]; /* 64 bytes */
u32 tmplen, keysize = 0;
int i, rc;
if (keylen < sizeof(*hdr))
return -EINVAL;
if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_CLEAR_KEY)
keysize = pkey_keytype_aes_to_size(t->keytype);
if (!keysize || t->len != keysize)
return -EINVAL;
/* try two times in case of failure */
for (i = 0, rc = -ENODEV; i < 2 && rc; i++) {
tmplen = SECKEYBLOBSIZE;
rc = cca_clr2key(NULL, 0, t->keytype, PKEY_TYPE_CCA_DATA,
8 * keysize, 0, t->clearkey, t->len,
tmpbuf, &tmplen, NULL, pflags);
pr_debug("cca_clr2key()=%d\n", rc);
if (rc)
continue;
rc = cca_key2protkey(NULL, 0, tmpbuf, tmplen,
protkey, protkeylen, protkeytype, pflags);
pr_debug("cca_key2protkey()=%d\n", rc);
}
pr_debug("rc=%d\n", rc);
return rc;
}
static struct pkey_handler cca_handler = {
.module = THIS_MODULE,
.name = "PKEY CCA handler",
.is_supported_key = is_cca_key,
.is_supported_keytype = is_cca_keytype,
.key_to_protkey = cca_key2protkey,
.slowpath_key_to_protkey = cca_slowpath_key2protkey,
.gen_key = cca_gen_key,
.clr_to_key = cca_clr2key,
.verify_key = cca_verifykey,
.apqns_for_key = cca_apqns4key,
.apqns_for_keytype = cca_apqns4type,
};
/*
* Module init
*/
static int __init pkey_cca_init(void)
{
/* register this module as pkey handler for all the cca stuff */
return pkey_handler_register(&cca_handler);
}
/*
* Module exit
*/
static void __exit pkey_cca_exit(void)
{
/* unregister this module as pkey handler */
pkey_handler_unregister(&cca_handler);
}
module_init(pkey_cca_init);
module_exit(pkey_cca_exit);
+576
View File
@@ -0,0 +1,576 @@
// SPDX-License-Identifier: GPL-2.0
/*
* pkey ep11 specific code
*
* Copyright IBM Corp. 2024
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cpufeature.h>
#include "zcrypt_ccamisc.h"
#include "zcrypt_ep11misc.h"
#include "pkey_base.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 protected key EP11 handler");
#if IS_MODULE(CONFIG_PKEY_EP11)
static struct ap_device_id pkey_ep11_card_ids[] = {
{ .dev_type = AP_DEVICE_TYPE_CEX4 },
{ .dev_type = AP_DEVICE_TYPE_CEX5 },
{ .dev_type = AP_DEVICE_TYPE_CEX6 },
{ .dev_type = AP_DEVICE_TYPE_CEX7 },
{ .dev_type = AP_DEVICE_TYPE_CEX8 },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(ap, pkey_ep11_card_ids);
#endif
/*
* Check key blob for known and supported EP11 key.
*/
static bool is_ep11_key(const u8 *key, u32 keylen)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
if (keylen < sizeof(*hdr))
return false;
switch (hdr->type) {
case TOKTYPE_NON_CCA:
switch (hdr->version) {
case TOKVER_EP11_AES:
case TOKVER_EP11_AES_WITH_HEADER:
case TOKVER_EP11_ECC_WITH_HEADER:
return true;
default:
return false;
}
default:
return false;
}
}
static bool is_ep11_keytype(enum pkey_key_type key_type)
{
switch (key_type) {
case PKEY_TYPE_EP11:
case PKEY_TYPE_EP11_AES:
case PKEY_TYPE_EP11_ECC:
return true;
default:
return false;
}
}
static int ep11_apqns4key(const u8 *key, u32 keylen, u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns, u32 pflags)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
u32 _apqns[MAXAPQNSINLIST], _nr_apqns = ARRAY_SIZE(_apqns);
u32 xflags;
int rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
if (!flags)
flags = PKEY_FLAGS_MATCH_CUR_MKVP;
if (keylen < sizeof(struct keytoken_header) || flags == 0)
return -EINVAL;
zcrypt_wait_api_operational();
if (hdr->type == TOKTYPE_NON_CCA &&
(hdr->version == TOKVER_EP11_AES_WITH_HEADER ||
hdr->version == TOKVER_EP11_ECC_WITH_HEADER) &&
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
struct ep11keyblob *kb = (struct ep11keyblob *)
(key + sizeof(struct ep11kblob_header));
int minhwtype = 0, api = 0;
if (flags != PKEY_FLAGS_MATCH_CUR_MKVP)
return -EINVAL;
if (kb->attr & EP11_BLOB_PKEY_EXTRACTABLE) {
minhwtype = ZCRYPT_CEX7;
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
}
rc = ep11_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
minhwtype, api, kb->wkvp, xflags);
if (rc)
goto out;
} else if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_AES &&
is_ep11_keyblob(key)) {
struct ep11keyblob *kb = (struct ep11keyblob *)key;
int minhwtype = 0, api = 0;
if (flags != PKEY_FLAGS_MATCH_CUR_MKVP)
return -EINVAL;
if (kb->attr & EP11_BLOB_PKEY_EXTRACTABLE) {
minhwtype = ZCRYPT_CEX7;
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
}
rc = ep11_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
minhwtype, api, kb->wkvp, xflags);
if (rc)
goto out;
} else {
PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
__func__, hdr->type, hdr->version);
return -EINVAL;
}
if (apqns) {
if (*nr_apqns < _nr_apqns)
rc = -ENOSPC;
else
memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
}
*nr_apqns = _nr_apqns;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int ep11_apqns4type(enum pkey_key_type ktype,
u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
struct pkey_apqn *apqns, size_t *nr_apqns, u32 pflags)
{
u32 _apqns[MAXAPQNSINLIST], _nr_apqns = ARRAY_SIZE(_apqns);
u32 xflags;
int rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
zcrypt_wait_api_operational();
if (ktype == PKEY_TYPE_EP11 ||
ktype == PKEY_TYPE_EP11_AES ||
ktype == PKEY_TYPE_EP11_ECC) {
u8 *wkvp = NULL;
int api;
if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
wkvp = cur_mkvp;
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
rc = ep11_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
ZCRYPT_CEX7, api, wkvp, xflags);
if (rc)
goto out;
} else {
PKEY_DBF_ERR("%s unknown/unsupported key type %d\n",
__func__, (int)ktype);
return -EINVAL;
}
if (apqns) {
if (*nr_apqns < _nr_apqns)
rc = -ENOSPC;
else
memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
}
*nr_apqns = _nr_apqns;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int ep11_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 pflags)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
struct pkey_apqn _apqns[MAXAPQNSINLIST];
u32 xflags;
int i, rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
if (keylen < sizeof(*hdr))
return -EINVAL;
if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_AES_WITH_HEADER &&
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
/* EP11 AES key blob with header */
if (ep11_check_aes_key_with_hdr(pkey_dbf_info,
3, key, keylen, 1))
return -EINVAL;
} else if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_ECC_WITH_HEADER &&
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
/* EP11 ECC key blob with header */
if (ep11_check_ecc_key_with_hdr(pkey_dbf_info,
3, key, keylen, 1))
return -EINVAL;
} else if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_AES &&
is_ep11_keyblob(key)) {
/* EP11 AES key blob with header in session field */
if (ep11_check_aes_key(pkey_dbf_info, 3, key, keylen, 1))
return -EINVAL;
} else {
PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
__func__, hdr->type, hdr->version);
return -EINVAL;
}
zcrypt_wait_api_operational();
if (!apqns || (nr_apqns == 1 &&
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
nr_apqns = MAXAPQNSINLIST;
rc = ep11_apqns4key(key, keylen, 0, _apqns, &nr_apqns, pflags);
if (rc)
goto out;
apqns = _apqns;
}
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_AES_WITH_HEADER &&
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
key, hdr->len, protkey,
protkeylen, protkeytype, xflags);
} else if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_ECC_WITH_HEADER &&
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
key, hdr->len, protkey,
protkeylen, protkeytype, xflags);
} else if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_AES &&
is_ep11_keyblob(key)) {
rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
key, hdr->len, protkey,
protkeylen, protkeytype, xflags);
} else {
rc = -EINVAL;
break;
}
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Generate EP11 secure key.
* As of now only EP11 AES secure keys are supported.
* keytype is one of the PKEY_KEYTYPE_* constants,
* subtype may be PKEY_TYPE_EP11 or PKEY_TYPE_EP11_AES
* or 0 (results in subtype PKEY_TYPE_EP11_AES),
* keybitsize is the bit size of the key (may be 0 for
* keytype PKEY_KEYTYPE_AES_*).
*/
static int ep11_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 subtype,
u32 keybitsize, u32 flags,
u8 *keybuf, u32 *keybuflen, u32 *_keyinfo, u32 pflags)
{
struct pkey_apqn _apqns[MAXAPQNSINLIST];
int i, len, rc;
u32 xflags;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
/* check keytype, subtype, keybitsize */
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
len = pkey_keytype_aes_to_size(keytype);
if (keybitsize && keybitsize != 8 * len) {
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
__func__, keybitsize);
return -EINVAL;
}
keybitsize = 8 * len;
switch (subtype) {
case PKEY_TYPE_EP11:
case PKEY_TYPE_EP11_AES:
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
__func__, subtype);
return -EINVAL;
}
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
__func__, keytype);
return -EINVAL;
}
zcrypt_wait_api_operational();
if (!apqns || (nr_apqns == 1 &&
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
nr_apqns = MAXAPQNSINLIST;
rc = ep11_apqns4type(subtype, NULL, NULL, 0,
_apqns, &nr_apqns, pflags);
if (rc)
goto out;
apqns = _apqns;
}
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
rc = ep11_genaeskey(apqns[i].card, apqns[i].domain,
keybitsize, flags,
keybuf, keybuflen, subtype, xflags);
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Generate EP11 secure key with given clear key value.
* As of now only EP11 AES secure keys are supported.
* keytype is one of the PKEY_KEYTYPE_* constants,
* subtype may be PKEY_TYPE_EP11 or PKEY_TYPE_EP11_AES
* or 0 (assumes PKEY_TYPE_EP11_AES then).
* keybitsize is the bit size of the key (may be 0 for
* keytype PKEY_KEYTYPE_AES_*).
*/
static int ep11_clr2key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 subtype,
u32 keybitsize, u32 flags,
const u8 *clrkey, u32 clrkeylen,
u8 *keybuf, u32 *keybuflen, u32 *_keyinfo, u32 pflags)
{
struct pkey_apqn _apqns[MAXAPQNSINLIST];
int i, len, rc;
u32 xflags;
if (pflags & PKEY_XFLAG_NOCLEARKEY) {
PKEY_DBF_ERR("%s clear key but xflag NOCLEARKEY\n", __func__);
return -EINVAL;
}
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
/* check keytype, subtype, clrkeylen, keybitsize */
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
len = pkey_keytype_aes_to_size(keytype);
if (keybitsize && keybitsize != 8 * len) {
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
__func__, keybitsize);
return -EINVAL;
}
keybitsize = 8 * len;
if (clrkeylen != len) {
PKEY_DBF_ERR("%s invalid clear key len %d != %d\n",
__func__, clrkeylen, len);
return -EINVAL;
}
switch (subtype) {
case PKEY_TYPE_EP11:
case PKEY_TYPE_EP11_AES:
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
__func__, subtype);
return -EINVAL;
}
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
__func__, keytype);
return -EINVAL;
}
zcrypt_wait_api_operational();
if (!apqns || (nr_apqns == 1 &&
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
nr_apqns = MAXAPQNSINLIST;
rc = ep11_apqns4type(subtype, NULL, NULL, 0,
_apqns, &nr_apqns, pflags);
if (rc)
goto out;
apqns = _apqns;
}
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
rc = ep11_clr2keyblob(apqns[i].card, apqns[i].domain,
keybitsize, flags, clrkey,
keybuf, keybuflen, subtype, xflags);
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int ep11_verifykey(const u8 *key, u32 keylen,
u16 *card, u16 *dom,
u32 *keytype, u32 *keybitsize, u32 *flags, u32 pflags)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
u32 apqns[MAXAPQNSINLIST], nr_apqns = ARRAY_SIZE(apqns);
u32 xflags;
int rc;
xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;
if (keylen < sizeof(*hdr))
return -EINVAL;
zcrypt_wait_api_operational();
if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_AES) {
struct ep11keyblob *kb = (struct ep11keyblob *)key;
int api;
rc = ep11_check_aes_key(pkey_dbf_info, 3, key, keylen, 1);
if (rc)
goto out;
*keytype = PKEY_TYPE_EP11;
*keybitsize = kb->head.bitlen;
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
rc = ep11_findcard2(apqns, &nr_apqns, *card, *dom,
ZCRYPT_CEX7, api,
ep11_kb_wkvp(key, keylen), xflags);
if (rc)
goto out;
*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
*card = ((struct pkey_apqn *)apqns)->card;
*dom = ((struct pkey_apqn *)apqns)->domain;
} else if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_EP11_AES_WITH_HEADER) {
struct ep11kblob_header *kh = (struct ep11kblob_header *)key;
int api;
rc = ep11_check_aes_key_with_hdr(pkey_dbf_info,
3, key, keylen, 1);
if (rc)
goto out;
*keytype = PKEY_TYPE_EP11_AES;
*keybitsize = kh->bitlen;
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
rc = ep11_findcard2(apqns, &nr_apqns, *card, *dom,
ZCRYPT_CEX7, api,
ep11_kb_wkvp(key, keylen), xflags);
if (rc)
goto out;
*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
*card = ((struct pkey_apqn *)apqns)->card;
*dom = ((struct pkey_apqn *)apqns)->domain;
} else {
/* unknown/unsupported key blob */
rc = -EINVAL;
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* This function provides an alternate but usually slow way
* to convert a 'clear key token' with AES key material into
* a protected key. That is done via an intermediate step
* which creates an EP11 AES secure key first and then derives
* the protected key from this secure key.
*/
static int ep11_slowpath_key2protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype, u32 pflags)
{
const struct keytoken_header *hdr = (const struct keytoken_header *)key;
const struct clearkeytoken *t = (const struct clearkeytoken *)key;
u8 tmpbuf[MAXEP11AESKEYBLOBSIZE]; /* 336 bytes */
u32 tmplen, keysize = 0;
int i, rc;
if (keylen < sizeof(*hdr))
return -EINVAL;
if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_CLEAR_KEY)
keysize = pkey_keytype_aes_to_size(t->keytype);
if (!keysize || t->len != keysize)
return -EINVAL;
/* try two times in case of failure */
for (i = 0, rc = -ENODEV; i < 2 && rc; i++) {
tmplen = MAXEP11AESKEYBLOBSIZE;
rc = ep11_clr2key(NULL, 0, t->keytype, PKEY_TYPE_EP11,
8 * keysize, 0, t->clearkey, t->len,
tmpbuf, &tmplen, NULL, pflags);
pr_debug("ep11_clr2key()=%d\n", rc);
if (rc)
continue;
rc = ep11_key2protkey(NULL, 0, tmpbuf, tmplen,
protkey, protkeylen, protkeytype, pflags);
pr_debug("ep11_key2protkey()=%d\n", rc);
}
pr_debug("rc=%d\n", rc);
return rc;
}
static struct pkey_handler ep11_handler = {
.module = THIS_MODULE,
.name = "PKEY EP11 handler",
.is_supported_key = is_ep11_key,
.is_supported_keytype = is_ep11_keytype,
.key_to_protkey = ep11_key2protkey,
.slowpath_key_to_protkey = ep11_slowpath_key2protkey,
.gen_key = ep11_gen_key,
.clr_to_key = ep11_clr2key,
.verify_key = ep11_verifykey,
.apqns_for_key = ep11_apqns4key,
.apqns_for_keytype = ep11_apqns4type,
};
/*
* Module init
*/
static int __init pkey_ep11_init(void)
{
/* register this module as pkey handler for all the ep11 stuff */
return pkey_handler_register(&ep11_handler);
}
/*
* Module exit
*/
static void __exit pkey_ep11_exit(void)
{
/* unregister this module as pkey handler */
pkey_handler_unregister(&ep11_handler);
}
module_init(pkey_ep11_init);
module_exit(pkey_ep11_exit);
@@ -0,0 +1,483 @@
// SPDX-License-Identifier: GPL-2.0
/*
* pkey pckmo specific code
*
* Copyright IBM Corp. 2024
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cpufeature.h>
#include <asm/cpacf.h>
#include <crypto/aes.h>
#include <linux/random.h>
#include "zcrypt_ccamisc.h"
#include "pkey_base.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 protected key PCKMO handler");
/*
* Check key blob for known and supported here.
*/
static bool is_pckmo_key(const u8 *key, u32 keylen)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
struct clearkeytoken *t = (struct clearkeytoken *)key;
if (keylen < sizeof(*hdr))
return false;
switch (hdr->type) {
case TOKTYPE_NON_CCA:
switch (hdr->version) {
case TOKVER_CLEAR_KEY:
if (pkey_keytype_to_size(t->keytype))
return true;
return false;
case TOKVER_PROTECTED_KEY:
return true;
default:
return false;
}
default:
return false;
}
}
static bool is_pckmo_keytype(enum pkey_key_type keytype)
{
switch (keytype) {
case PKEY_TYPE_PROTKEY:
return true;
default:
return false;
}
}
/*
* Create a protected key from a clear key value via PCKMO instruction.
*/
static int pckmo_clr2protkey(u32 keytype, const u8 *clrkey, u32 clrkeylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
{
/* mask of available pckmo subfunctions */
static cpacf_mask_t pckmo_functions;
int keysize, rc = -EINVAL;
u8 paramblock[160];
u32 pkeytype = 0;
unsigned int fc;
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
fc = CPACF_PCKMO_ENC_AES_128_KEY;
break;
case PKEY_KEYTYPE_AES_192:
fc = CPACF_PCKMO_ENC_AES_192_KEY;
break;
case PKEY_KEYTYPE_AES_256:
fc = CPACF_PCKMO_ENC_AES_256_KEY;
break;
case PKEY_KEYTYPE_ECC_P256:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_P256_KEY;
break;
case PKEY_KEYTYPE_ECC_P384:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_P384_KEY;
break;
case PKEY_KEYTYPE_ECC_P521:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_P521_KEY;
break;
case PKEY_KEYTYPE_ECC_ED25519:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_ED25519_KEY;
break;
case PKEY_KEYTYPE_ECC_ED448:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_ED448_KEY;
break;
case PKEY_KEYTYPE_AES_XTS_128:
fc = CPACF_PCKMO_ENC_AES_XTS_128_DOUBLE_KEY;
break;
case PKEY_KEYTYPE_AES_XTS_256:
fc = CPACF_PCKMO_ENC_AES_XTS_256_DOUBLE_KEY;
break;
case PKEY_KEYTYPE_HMAC_512:
fc = CPACF_PCKMO_ENC_HMAC_512_KEY;
break;
case PKEY_KEYTYPE_HMAC_1024:
fc = CPACF_PCKMO_ENC_HMAC_1024_KEY;
break;
default:
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n",
__func__, keytype);
goto out;
}
keysize = pkey_keytype_to_size(keytype);
pkeytype = pkeytype ?: keytype;
if (clrkeylen && clrkeylen < keysize) {
PKEY_DBF_ERR("%s clear key size too small: %u < %d\n",
__func__, clrkeylen, keysize);
goto out;
}
if (*protkeylen < keysize + AES_WK_VP_SIZE) {
PKEY_DBF_ERR("%s prot key buffer size too small: %u < %d\n",
__func__, *protkeylen, keysize + AES_WK_VP_SIZE);
goto out;
}
/* Did we already check for PCKMO ? */
if (!pckmo_functions.bytes[0]) {
/* no, so check now */
if (!cpacf_query(CPACF_PCKMO, &pckmo_functions)) {
PKEY_DBF_ERR("%s cpacf_query() failed\n", __func__);
rc = -ENODEV;
goto out;
}
}
/* check for the pckmo subfunction we need now */
if (!cpacf_test_func(&pckmo_functions, fc)) {
PKEY_DBF_ERR("%s pckmo fc 0x%02x not available\n",
__func__, fc);
rc = -ENODEV;
goto out;
}
/* prepare param block */
memset(paramblock, 0, sizeof(paramblock));
memcpy(paramblock, clrkey, keysize);
/* call the pckmo instruction */
cpacf_pckmo(fc, paramblock);
/* copy created protected key to key buffer including the wkvp block */
*protkeylen = keysize + AES_WK_VP_SIZE;
memcpy(protkey, paramblock, *protkeylen);
*protkeytype = pkeytype;
rc = 0;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Verify a raw protected key blob.
*/
static int pckmo_verify_protkey(const u8 *protkey, u32 protkeylen,
u32 protkeytype)
{
u8 clrkey[16] = { 0 }, tmpkeybuf[16 + AES_WK_VP_SIZE];
u32 tmpkeybuflen, tmpkeytype;
int keysize, rc = -EINVAL;
u8 *wkvp;
/* check protkey type and size */
keysize = pkey_keytype_to_size(protkeytype);
if (!keysize) {
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n", __func__,
protkeytype);
goto out;
}
if (protkeylen < keysize + AES_WK_VP_SIZE)
goto out;
/* generate a dummy AES 128 protected key */
tmpkeybuflen = sizeof(tmpkeybuf);
rc = pckmo_clr2protkey(PKEY_KEYTYPE_AES_128,
clrkey, sizeof(clrkey),
tmpkeybuf, &tmpkeybuflen, &tmpkeytype);
if (rc)
goto out;
memzero_explicit(tmpkeybuf, 16);
wkvp = tmpkeybuf + 16;
/* compare WK VP from the temp key with that of the given prot key */
if (memcmp(wkvp, protkey + keysize, AES_WK_VP_SIZE)) {
PKEY_DBF_ERR("%s protected key WK VP mismatch\n", __func__);
rc = -EKEYREJECTED;
goto out;
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int pckmo_key2protkey(const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
int rc = -EINVAL;
if (keylen < sizeof(*hdr))
return -EINVAL;
if (hdr->type != TOKTYPE_NON_CCA)
return -EINVAL;
switch (hdr->version) {
case TOKVER_PROTECTED_KEY: {
struct protkeytoken *t = (struct protkeytoken *)key;
u32 keysize;
if (keylen < sizeof(*t))
goto out;
keysize = pkey_keytype_to_size(t->keytype);
if (!keysize) {
PKEY_DBF_ERR("%s protected key token: unknown keytype %u\n",
__func__, t->keytype);
goto out;
}
switch (t->keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
if (t->len != keysize + AES_WK_VP_SIZE ||
keylen < sizeof(struct protaeskeytoken))
goto out;
rc = pckmo_verify_protkey(t->protkey, t->len,
t->keytype);
if (rc)
goto out;
break;
default:
if (t->len != keysize + AES_WK_VP_SIZE ||
keylen < sizeof(*t) + keysize + AES_WK_VP_SIZE)
goto out;
break;
}
if (t->len > *protkeylen) {
rc = -EINVAL;
goto out;
}
memcpy(protkey, t->protkey, t->len);
*protkeylen = t->len;
*protkeytype = t->keytype;
rc = 0;
break;
}
case TOKVER_CLEAR_KEY: {
struct clearkeytoken *t = (struct clearkeytoken *)key;
u32 keysize;
if (xflags & PKEY_XFLAG_NOCLEARKEY) {
PKEY_DBF_ERR("%s clear key token but xflag NOCLEARKEY\n",
__func__);
goto out;
}
if (keylen < sizeof(*t) ||
keylen < sizeof(*t) + t->len)
goto out;
keysize = pkey_keytype_to_size(t->keytype);
if (!keysize) {
PKEY_DBF_ERR("%s clear key token: unknown keytype %u\n",
__func__, t->keytype);
goto out;
}
if (t->len != keysize) {
PKEY_DBF_ERR("%s clear key token: invalid key len %u\n",
__func__, t->len);
goto out;
}
rc = pckmo_clr2protkey(t->keytype, t->clearkey, t->len,
protkey, protkeylen, protkeytype);
break;
}
default:
PKEY_DBF_ERR("%s unknown non-CCA token version %d\n",
__func__, hdr->version);
break;
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Generate a random protected key.
*/
static int pckmo_gen_protkey(u32 keytype, u32 subtype,
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
{
u8 clrkey[128];
int keysize;
int rc;
keysize = pkey_keytype_to_size(keytype);
if (!keysize) {
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
__func__, keytype);
return -EINVAL;
}
if (subtype != PKEY_TYPE_PROTKEY) {
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
__func__, subtype);
return -EINVAL;
}
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
case PKEY_KEYTYPE_AES_XTS_128:
case PKEY_KEYTYPE_AES_XTS_256:
case PKEY_KEYTYPE_HMAC_512:
case PKEY_KEYTYPE_HMAC_1024:
break;
default:
PKEY_DBF_ERR("%s unsupported keytype %d\n",
__func__, keytype);
return -EINVAL;
}
/* generate a dummy random clear key */
get_random_bytes(clrkey, keysize);
/* convert it to a dummy protected key */
rc = pckmo_clr2protkey(keytype, clrkey, keysize,
protkey, protkeylen, protkeytype);
if (rc)
goto out;
/* replace the key part of the protected key with random bytes */
get_random_bytes(protkey, keysize);
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Verify a protected key token blob.
*/
static int pckmo_verify_key(const u8 *key, u32 keylen)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
int rc = -EINVAL;
if (keylen < sizeof(*hdr))
return -EINVAL;
if (hdr->type != TOKTYPE_NON_CCA)
return -EINVAL;
switch (hdr->version) {
case TOKVER_PROTECTED_KEY: {
struct protkeytoken *t = (struct protkeytoken *)key;
u32 keysize;
if (keylen < sizeof(*t))
goto out;
keysize = pkey_keytype_to_size(t->keytype);
if (!keysize || t->len != keysize + AES_WK_VP_SIZE)
goto out;
switch (t->keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
if (keylen < sizeof(struct protaeskeytoken))
goto out;
break;
default:
if (keylen < sizeof(*t) + keysize + AES_WK_VP_SIZE)
goto out;
break;
}
rc = pckmo_verify_protkey(t->protkey, t->len, t->keytype);
break;
}
default:
PKEY_DBF_ERR("%s unknown non-CCA token version %d\n",
__func__, hdr->version);
break;
}
out:
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* Wrapper functions used for the pkey handler struct
*/
static int pkey_pckmo_key2protkey(const struct pkey_apqn *_apqns,
size_t _nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *keyinfo,
u32 xflags)
{
return pckmo_key2protkey(key, keylen,
protkey, protkeylen, keyinfo, xflags);
}
static int pkey_pckmo_gen_key(const struct pkey_apqn *_apqns, size_t _nr_apqns,
u32 keytype, u32 keysubtype,
u32 _keybitsize, u32 _flags,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo,
u32 _xflags __always_unused)
{
return pckmo_gen_protkey(keytype, keysubtype,
keybuf, keybuflen, keyinfo);
}
static int pkey_pckmo_verifykey(const u8 *key, u32 keylen,
u16 *_card, u16 *_dom,
u32 *_keytype, u32 *_keybitsize,
u32 *_flags, u32 _xflags __always_unused)
{
return pckmo_verify_key(key, keylen);
}
static struct pkey_handler pckmo_handler = {
.module = THIS_MODULE,
.name = "PKEY PCKMO handler",
.is_supported_key = is_pckmo_key,
.is_supported_keytype = is_pckmo_keytype,
.key_to_protkey = pkey_pckmo_key2protkey,
.gen_key = pkey_pckmo_gen_key,
.verify_key = pkey_pckmo_verifykey,
};
/*
* Module init
*/
static int __init pkey_pckmo_init(void)
{
cpacf_mask_t func_mask;
/*
* The pckmo instruction should be available - even if we don't
* actually invoke it. This instruction comes with MSA 3 which
* is also the minimum level for the kmc instructions which
* are able to work with protected keys.
*/
if (!cpacf_query(CPACF_PCKMO, &func_mask))
return -ENODEV;
/* register this module as pkey handler for all the pckmo stuff */
return pkey_handler_register(&pckmo_handler);
}
/*
* Module exit
*/
static void __exit pkey_pckmo_exit(void)
{
/* unregister this module as pkey handler */
pkey_handler_unregister(&pckmo_handler);
}
module_cpu_feature_match(S390_CPU_FEATURE_MSA, pkey_pckmo_init);
module_exit(pkey_pckmo_exit);
@@ -0,0 +1,646 @@
// SPDX-License-Identifier: GPL-2.0
/*
* pkey module sysfs related functions
*
* Copyright IBM Corp. 2024
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/sysfs.h>
#include "zcrypt_ccamisc.h"
#include "zcrypt_ep11misc.h"
#include "pkey_base.h"
/*
* Wrapper around pkey_handler_gen_key() which deals with the
* ENODEV return code and then tries to enforce a pkey handler
* module load.
*/
static int sys_pkey_handler_gen_key(u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
u8 *keybuf, u32 *keybuflen, u32 *keyinfo)
{
int rc;
rc = pkey_handler_gen_key(NULL, 0,
keytype, keysubtype,
keybitsize, flags,
keybuf, keybuflen, keyinfo, 0);
if (rc == -ENODEV) {
pkey_handler_request_modules();
rc = pkey_handler_gen_key(NULL, 0,
keytype, keysubtype,
keybitsize, flags,
keybuf, keybuflen, keyinfo, 0);
}
return rc;
}
/*
* Sysfs attribute read function for all protected key binary attributes.
* The implementation can not deal with partial reads, because a new random
* protected key blob is generated with each read. In case of partial reads
* (i.e. off != 0 or count < key blob size) -EINVAL is returned.
*/
static ssize_t pkey_protkey_aes_attr_read(u32 keytype, bool is_xts, char *buf,
loff_t off, size_t count)
{
struct protaeskeytoken protkeytoken;
struct pkey_protkey protkey;
int rc;
if (off != 0 || count < sizeof(protkeytoken))
return -EINVAL;
if (is_xts)
if (count < 2 * sizeof(protkeytoken))
return -EINVAL;
memset(&protkeytoken, 0, sizeof(protkeytoken));
protkeytoken.type = TOKTYPE_NON_CCA;
protkeytoken.version = TOKVER_PROTECTED_KEY;
protkeytoken.keytype = keytype;
protkey.len = sizeof(protkey.protkey);
rc = sys_pkey_handler_gen_key(keytype, PKEY_TYPE_PROTKEY, 0, 0,
protkey.protkey, &protkey.len,
&protkey.type);
if (rc)
return rc;
protkeytoken.len = protkey.len;
memcpy(&protkeytoken.protkey, &protkey.protkey, protkey.len);
memcpy(buf, &protkeytoken, sizeof(protkeytoken));
if (is_xts) {
/* xts needs a second protected key, reuse protkey struct */
protkey.len = sizeof(protkey.protkey);
rc = sys_pkey_handler_gen_key(keytype, PKEY_TYPE_PROTKEY, 0, 0,
protkey.protkey, &protkey.len,
&protkey.type);
if (rc)
return rc;
protkeytoken.len = protkey.len;
memcpy(&protkeytoken.protkey, &protkey.protkey, protkey.len);
memcpy(buf + sizeof(protkeytoken), &protkeytoken,
sizeof(protkeytoken));
return 2 * sizeof(protkeytoken);
}
return sizeof(protkeytoken);
}
/*
* Sysfs attribute read function for the AES XTS prot key binary attributes.
* The implementation can not deal with partial reads, because a new random
* protected key blob is generated with each read. In case of partial reads
* (i.e. off != 0 or count < key blob size) -EINVAL is returned.
*/
static ssize_t pkey_protkey_aes_xts_attr_read(u32 keytype, char *buf,
loff_t off, size_t count)
{
struct protkeytoken *t = (struct protkeytoken *)buf;
u32 protlen, prottype;
int rc;
switch (keytype) {
case PKEY_KEYTYPE_AES_XTS_128:
protlen = 64;
break;
case PKEY_KEYTYPE_AES_XTS_256:
protlen = 96;
break;
default:
return -EINVAL;
}
if (off != 0 || count < sizeof(*t) + protlen)
return -EINVAL;
memset(t, 0, sizeof(*t) + protlen);
t->type = TOKTYPE_NON_CCA;
t->version = TOKVER_PROTECTED_KEY;
t->keytype = keytype;
rc = sys_pkey_handler_gen_key(keytype, PKEY_TYPE_PROTKEY, 0, 0,
t->protkey, &protlen, &prottype);
if (rc)
return rc;
t->len = protlen;
return sizeof(*t) + protlen;
}
/*
* Sysfs attribute read function for the HMAC prot key binary attributes.
* The implementation can not deal with partial reads, because a new random
* protected key blob is generated with each read. In case of partial reads
* (i.e. off != 0 or count < key blob size) -EINVAL is returned.
*/
static ssize_t pkey_protkey_hmac_attr_read(u32 keytype, char *buf,
loff_t off, size_t count)
{
struct protkeytoken *t = (struct protkeytoken *)buf;
u32 protlen, prottype;
int rc;
switch (keytype) {
case PKEY_KEYTYPE_HMAC_512:
protlen = 96;
break;
case PKEY_KEYTYPE_HMAC_1024:
protlen = 160;
break;
default:
return -EINVAL;
}
if (off != 0 || count < sizeof(*t) + protlen)
return -EINVAL;
memset(t, 0, sizeof(*t) + protlen);
t->type = TOKTYPE_NON_CCA;
t->version = TOKVER_PROTECTED_KEY;
t->keytype = keytype;
rc = sys_pkey_handler_gen_key(keytype, PKEY_TYPE_PROTKEY, 0, 0,
t->protkey, &protlen, &prottype);
if (rc)
return rc;
t->len = protlen;
return sizeof(*t) + protlen;
}
static ssize_t protkey_aes_128_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_128, false, buf,
off, count);
}
static ssize_t protkey_aes_192_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_192, false, buf,
off, count);
}
static ssize_t protkey_aes_256_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_256, false, buf,
off, count);
}
static ssize_t protkey_aes_128_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_128, true, buf,
off, count);
}
static ssize_t protkey_aes_256_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_256, true, buf,
off, count);
}
static ssize_t protkey_aes_xts_128_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_aes_xts_attr_read(PKEY_KEYTYPE_AES_XTS_128,
buf, off, count);
}
static ssize_t protkey_aes_xts_256_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_aes_xts_attr_read(PKEY_KEYTYPE_AES_XTS_256,
buf, off, count);
}
static ssize_t protkey_hmac_512_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_hmac_attr_read(PKEY_KEYTYPE_HMAC_512,
buf, off, count);
}
static ssize_t protkey_hmac_1024_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_protkey_hmac_attr_read(PKEY_KEYTYPE_HMAC_1024,
buf, off, count);
}
static const BIN_ATTR_RO(protkey_aes_128, sizeof(struct protaeskeytoken));
static const BIN_ATTR_RO(protkey_aes_192, sizeof(struct protaeskeytoken));
static const BIN_ATTR_RO(protkey_aes_256, sizeof(struct protaeskeytoken));
static const BIN_ATTR_RO(protkey_aes_128_xts, 2 * sizeof(struct protaeskeytoken));
static const BIN_ATTR_RO(protkey_aes_256_xts, 2 * sizeof(struct protaeskeytoken));
static const BIN_ATTR_RO(protkey_aes_xts_128, sizeof(struct protkeytoken) + 64);
static const BIN_ATTR_RO(protkey_aes_xts_256, sizeof(struct protkeytoken) + 96);
static const BIN_ATTR_RO(protkey_hmac_512, sizeof(struct protkeytoken) + 96);
static const BIN_ATTR_RO(protkey_hmac_1024, sizeof(struct protkeytoken) + 160);
static const struct bin_attribute *const protkey_attrs[] = {
&bin_attr_protkey_aes_128,
&bin_attr_protkey_aes_192,
&bin_attr_protkey_aes_256,
&bin_attr_protkey_aes_128_xts,
&bin_attr_protkey_aes_256_xts,
&bin_attr_protkey_aes_xts_128,
&bin_attr_protkey_aes_xts_256,
&bin_attr_protkey_hmac_512,
&bin_attr_protkey_hmac_1024,
NULL
};
static const struct attribute_group protkey_attr_group = {
.name = "protkey",
.bin_attrs = protkey_attrs,
};
/*
* Sysfs attribute read function for all secure key ccadata binary attributes.
* The implementation can not deal with partial reads, because a new random
* protected key blob is generated with each read. In case of partial reads
* (i.e. off != 0 or count < key blob size) -EINVAL is returned.
*/
static ssize_t pkey_ccadata_aes_attr_read(u32 keytype, bool is_xts, char *buf,
loff_t off, size_t count)
{
struct pkey_seckey *seckey = (struct pkey_seckey *)buf;
u32 buflen;
int rc;
if (off != 0 || count < sizeof(struct secaeskeytoken))
return -EINVAL;
if (is_xts)
if (count < 2 * sizeof(struct secaeskeytoken))
return -EINVAL;
buflen = sizeof(seckey->seckey);
rc = sys_pkey_handler_gen_key(keytype, PKEY_TYPE_CCA_DATA, 0, 0,
seckey->seckey, &buflen, NULL);
if (rc)
return rc;
if (is_xts) {
seckey++;
buflen = sizeof(seckey->seckey);
rc = sys_pkey_handler_gen_key(keytype, PKEY_TYPE_CCA_DATA, 0, 0,
seckey->seckey, &buflen, NULL);
if (rc)
return rc;
return 2 * sizeof(struct secaeskeytoken);
}
return sizeof(struct secaeskeytoken);
}
static ssize_t ccadata_aes_128_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_128, false, buf,
off, count);
}
static ssize_t ccadata_aes_192_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_192, false, buf,
off, count);
}
static ssize_t ccadata_aes_256_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_256, false, buf,
off, count);
}
static ssize_t ccadata_aes_128_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_128, true, buf,
off, count);
}
static ssize_t ccadata_aes_256_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_256, true, buf,
off, count);
}
static const BIN_ATTR_RO(ccadata_aes_128, sizeof(struct secaeskeytoken));
static const BIN_ATTR_RO(ccadata_aes_192, sizeof(struct secaeskeytoken));
static const BIN_ATTR_RO(ccadata_aes_256, sizeof(struct secaeskeytoken));
static const BIN_ATTR_RO(ccadata_aes_128_xts, 2 * sizeof(struct secaeskeytoken));
static const BIN_ATTR_RO(ccadata_aes_256_xts, 2 * sizeof(struct secaeskeytoken));
static const struct bin_attribute *const ccadata_attrs[] = {
&bin_attr_ccadata_aes_128,
&bin_attr_ccadata_aes_192,
&bin_attr_ccadata_aes_256,
&bin_attr_ccadata_aes_128_xts,
&bin_attr_ccadata_aes_256_xts,
NULL
};
static const struct attribute_group ccadata_attr_group = {
.name = "ccadata",
.bin_attrs = ccadata_attrs,
};
#define CCACIPHERTOKENSIZE (sizeof(struct cipherkeytoken) + 80)
/*
* Sysfs attribute read function for all secure key ccacipher binary attributes.
* The implementation can not deal with partial reads, because a new random
* secure key blob is generated with each read. In case of partial reads
* (i.e. off != 0 or count < key blob size) -EINVAL is returned.
*/
static ssize_t pkey_ccacipher_aes_attr_read(enum pkey_key_size keybits,
bool is_xts, char *buf, loff_t off,
size_t count)
{
u32 keysize = CCACIPHERTOKENSIZE;
int rc;
if (off != 0 || count < CCACIPHERTOKENSIZE)
return -EINVAL;
if (is_xts)
if (count < 2 * CCACIPHERTOKENSIZE)
return -EINVAL;
memset(buf, 0, is_xts ? 2 * keysize : keysize);
rc = sys_pkey_handler_gen_key(pkey_aes_bitsize_to_keytype(keybits),
PKEY_TYPE_CCA_CIPHER, keybits, 0,
buf, &keysize, NULL);
if (rc)
return rc;
if (is_xts) {
keysize = CCACIPHERTOKENSIZE;
buf += CCACIPHERTOKENSIZE;
rc = sys_pkey_handler_gen_key(
pkey_aes_bitsize_to_keytype(keybits),
PKEY_TYPE_CCA_CIPHER, keybits, 0,
buf, &keysize, NULL);
if (rc)
return rc;
return 2 * CCACIPHERTOKENSIZE;
}
return CCACIPHERTOKENSIZE;
}
static ssize_t ccacipher_aes_128_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_128, false, buf,
off, count);
}
static ssize_t ccacipher_aes_192_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_192, false, buf,
off, count);
}
static ssize_t ccacipher_aes_256_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_256, false, buf,
off, count);
}
static ssize_t ccacipher_aes_128_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_128, true, buf,
off, count);
}
static ssize_t ccacipher_aes_256_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_256, true, buf,
off, count);
}
static const BIN_ATTR_RO(ccacipher_aes_128, CCACIPHERTOKENSIZE);
static const BIN_ATTR_RO(ccacipher_aes_192, CCACIPHERTOKENSIZE);
static const BIN_ATTR_RO(ccacipher_aes_256, CCACIPHERTOKENSIZE);
static const BIN_ATTR_RO(ccacipher_aes_128_xts, 2 * CCACIPHERTOKENSIZE);
static const BIN_ATTR_RO(ccacipher_aes_256_xts, 2 * CCACIPHERTOKENSIZE);
static const struct bin_attribute *const ccacipher_attrs[] = {
&bin_attr_ccacipher_aes_128,
&bin_attr_ccacipher_aes_192,
&bin_attr_ccacipher_aes_256,
&bin_attr_ccacipher_aes_128_xts,
&bin_attr_ccacipher_aes_256_xts,
NULL
};
static const struct attribute_group ccacipher_attr_group = {
.name = "ccacipher",
.bin_attrs = ccacipher_attrs,
};
/*
* Sysfs attribute read function for all ep11 aes key binary attributes.
* The implementation can not deal with partial reads, because a new random
* secure key blob is generated with each read. In case of partial reads
* (i.e. off != 0 or count < key blob size) -EINVAL is returned.
* This function and the sysfs attributes using it provide EP11 key blobs
* padded to the upper limit of MAXEP11AESKEYBLOBSIZE which is currently
* 336 bytes.
*/
static ssize_t pkey_ep11_aes_attr_read(enum pkey_key_size keybits,
bool is_xts, char *buf, loff_t off,
size_t count)
{
u32 keysize = MAXEP11AESKEYBLOBSIZE;
int rc;
if (off != 0 || count < MAXEP11AESKEYBLOBSIZE)
return -EINVAL;
if (is_xts)
if (count < 2 * MAXEP11AESKEYBLOBSIZE)
return -EINVAL;
memset(buf, 0, is_xts ? 2 * keysize : keysize);
rc = sys_pkey_handler_gen_key(pkey_aes_bitsize_to_keytype(keybits),
PKEY_TYPE_EP11_AES, keybits, 0,
buf, &keysize, NULL);
if (rc)
return rc;
if (is_xts) {
keysize = MAXEP11AESKEYBLOBSIZE;
buf += MAXEP11AESKEYBLOBSIZE;
rc = sys_pkey_handler_gen_key(
pkey_aes_bitsize_to_keytype(keybits),
PKEY_TYPE_EP11_AES, keybits, 0,
buf, &keysize, NULL);
if (rc)
return rc;
return 2 * MAXEP11AESKEYBLOBSIZE;
}
return MAXEP11AESKEYBLOBSIZE;
}
static ssize_t ep11_aes_128_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ep11_aes_attr_read(PKEY_SIZE_AES_128, false, buf,
off, count);
}
static ssize_t ep11_aes_192_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ep11_aes_attr_read(PKEY_SIZE_AES_192, false, buf,
off, count);
}
static ssize_t ep11_aes_256_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ep11_aes_attr_read(PKEY_SIZE_AES_256, false, buf,
off, count);
}
static ssize_t ep11_aes_128_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ep11_aes_attr_read(PKEY_SIZE_AES_128, true, buf,
off, count);
}
static ssize_t ep11_aes_256_xts_read(struct file *filp,
struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{
return pkey_ep11_aes_attr_read(PKEY_SIZE_AES_256, true, buf,
off, count);
}
static const BIN_ATTR_RO(ep11_aes_128, MAXEP11AESKEYBLOBSIZE);
static const BIN_ATTR_RO(ep11_aes_192, MAXEP11AESKEYBLOBSIZE);
static const BIN_ATTR_RO(ep11_aes_256, MAXEP11AESKEYBLOBSIZE);
static const BIN_ATTR_RO(ep11_aes_128_xts, 2 * MAXEP11AESKEYBLOBSIZE);
static const BIN_ATTR_RO(ep11_aes_256_xts, 2 * MAXEP11AESKEYBLOBSIZE);
static const struct bin_attribute *const ep11_attrs[] = {
&bin_attr_ep11_aes_128,
&bin_attr_ep11_aes_192,
&bin_attr_ep11_aes_256,
&bin_attr_ep11_aes_128_xts,
&bin_attr_ep11_aes_256_xts,
NULL
};
static const struct attribute_group ep11_attr_group = {
.name = "ep11",
.bin_attrs = ep11_attrs,
};
const struct attribute_group *pkey_attr_groups[] = {
&protkey_attr_group,
&ccadata_attr_group,
&ccacipher_attr_group,
&ep11_attr_group,
NULL,
};
+317
View File
@@ -0,0 +1,317 @@
// SPDX-License-Identifier: GPL-2.0
/*
* pkey uv specific code
*
* Copyright IBM Corp. 2024
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/cpufeature.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uv.h>
#include "zcrypt_ccamisc.h"
#include "pkey_base.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 protected key UV handler");
/*
* One pre-allocated uv_secret_list for use with uv_find_secret()
*/
static struct uv_secret_list *uv_list;
static DEFINE_MUTEX(uv_list_mutex);
/*
* UV secret token struct and defines.
*/
#define TOKVER_UV_SECRET 0x09
struct uvsecrettoken {
u8 type; /* 0x00 = TOKTYPE_NON_CCA */
u8 res0[3];
u8 version; /* 0x09 = TOKVER_UV_SECRET */
u8 res1[3];
u16 secret_type; /* one of enum uv_secret_types from uv.h */
u16 secret_len; /* length in bytes of the secret */
u8 secret_id[UV_SECRET_ID_LEN]; /* the secret id for this secret */
} __packed;
/*
* Check key blob for known and supported UV key.
*/
static bool is_uv_key(const u8 *key, u32 keylen)
{
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
if (keylen < sizeof(*t))
return false;
switch (t->type) {
case TOKTYPE_NON_CCA:
switch (t->version) {
case TOKVER_UV_SECRET:
switch (t->secret_type) {
case UV_SECRET_AES_128:
case UV_SECRET_AES_192:
case UV_SECRET_AES_256:
case UV_SECRET_AES_XTS_128:
case UV_SECRET_AES_XTS_256:
case UV_SECRET_HMAC_SHA_256:
case UV_SECRET_HMAC_SHA_512:
case UV_SECRET_ECDSA_P256:
case UV_SECRET_ECDSA_P384:
case UV_SECRET_ECDSA_P521:
case UV_SECRET_ECDSA_ED25519:
case UV_SECRET_ECDSA_ED448:
return true;
default:
return false;
}
default:
return false;
}
default:
return false;
}
}
static bool is_uv_keytype(enum pkey_key_type keytype)
{
switch (keytype) {
case PKEY_TYPE_UVSECRET:
return true;
default:
return false;
}
}
static int get_secret_metadata(const u8 secret_id[UV_SECRET_ID_LEN],
struct uv_secret_list_item_hdr *secret)
{
int rc;
mutex_lock(&uv_list_mutex);
memset(uv_list, 0, sizeof(*uv_list));
rc = uv_find_secret(secret_id, uv_list, secret);
mutex_unlock(&uv_list_mutex);
return rc;
}
static int retrieve_secret(const u8 secret_id[UV_SECRET_ID_LEN],
u16 *secret_type, u8 *buf, u32 *buflen)
{
struct uv_secret_list_item_hdr secret_meta_data;
int rc;
rc = get_secret_metadata(secret_id, &secret_meta_data);
if (rc)
return rc;
if (*buflen < secret_meta_data.length)
return -EINVAL;
rc = uv_retrieve_secret(secret_meta_data.index,
buf, secret_meta_data.length);
if (rc)
return rc;
*secret_type = secret_meta_data.type;
*buflen = secret_meta_data.length;
return 0;
}
static int uv_get_size_and_type(u16 secret_type, u32 *pkeysize, u32 *pkeytype)
{
int rc = 0;
switch (secret_type) {
case UV_SECRET_AES_128:
*pkeysize = 16 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_128;
break;
case UV_SECRET_AES_192:
*pkeysize = 24 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_192;
break;
case UV_SECRET_AES_256:
*pkeysize = 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_256;
break;
case UV_SECRET_AES_XTS_128:
*pkeysize = 16 + 16 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_XTS_128;
break;
case UV_SECRET_AES_XTS_256:
*pkeysize = 32 + 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_XTS_256;
break;
case UV_SECRET_HMAC_SHA_256:
*pkeysize = 64 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_HMAC_512;
break;
case UV_SECRET_HMAC_SHA_512:
*pkeysize = 128 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_HMAC_1024;
break;
case UV_SECRET_ECDSA_P256:
*pkeysize = 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_P256;
break;
case UV_SECRET_ECDSA_P384:
*pkeysize = 48 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_P384;
break;
case UV_SECRET_ECDSA_P521:
*pkeysize = 80 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_P521;
break;
case UV_SECRET_ECDSA_ED25519:
*pkeysize = 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_ED25519;
break;
case UV_SECRET_ECDSA_ED448:
*pkeysize = 64 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_ED448;
break;
default:
rc = -EINVAL;
}
return rc;
}
static int uv_key2protkey(const struct pkey_apqn *_apqns __always_unused,
size_t _nr_apqns __always_unused,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *keyinfo,
u32 _xflags __always_unused)
{
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
u32 pkeysize, pkeytype;
u16 secret_type;
int rc;
rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
if (rc)
goto out;
if (*protkeylen < pkeysize) {
PKEY_DBF_ERR("%s prot key buffer size too small: %u < %u\n",
__func__, *protkeylen, pkeysize);
rc = -EINVAL;
goto out;
}
rc = retrieve_secret(t->secret_id, &secret_type, protkey, protkeylen);
if (rc) {
PKEY_DBF_ERR("%s retrieve_secret() failed with %d\n",
__func__, rc);
goto out;
}
if (secret_type != t->secret_type) {
PKEY_DBF_ERR("%s retrieved secret type %u != expected type %u\n",
__func__, secret_type, t->secret_type);
rc = -EINVAL;
goto out;
}
if (keyinfo)
*keyinfo = pkeytype;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int uv_verifykey(const u8 *key, u32 keylen,
u16 *_card __always_unused,
u16 *_dom __always_unused,
u32 *keytype, u32 *keybitsize, u32 *flags,
u32 xflags __always_unused)
{
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
struct uv_secret_list_item_hdr secret_meta_data;
u32 pkeysize, pkeytype, bitsize;
int rc;
rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
if (rc)
goto out;
rc = get_secret_metadata(t->secret_id, &secret_meta_data);
if (rc)
goto out;
if (secret_meta_data.type != t->secret_type) {
rc = -EINVAL;
goto out;
}
/* set keytype; keybitsize and flags are not supported */
if (keytype)
*keytype = PKEY_TYPE_UVSECRET;
if (keybitsize) {
bitsize = 8 * pkey_keytype_to_size(pkeytype);
*keybitsize = bitsize ?: PKEY_SIZE_UNKNOWN;
}
if (flags)
*flags = pkeytype;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static struct pkey_handler uv_handler = {
.module = THIS_MODULE,
.name = "PKEY UV handler",
.is_supported_key = is_uv_key,
.is_supported_keytype = is_uv_keytype,
.key_to_protkey = uv_key2protkey,
.verify_key = uv_verifykey,
};
/*
* Module init
*/
static int __init pkey_uv_init(void)
{
int rc;
if (!is_prot_virt_guest())
return -ENODEV;
if (!test_bit_inv(BIT_UVC_CMD_RETR_SECRET, uv_info.inst_calls_list))
return -ENODEV;
uv_list = kmalloc_obj(*uv_list);
if (!uv_list)
return -ENOMEM;
rc = pkey_handler_register(&uv_handler);
if (rc)
kfree(uv_list);
return rc;
}
/*
* Module exit
*/
static void __exit pkey_uv_exit(void)
{
pkey_handler_unregister(&uv_handler);
mutex_lock(&uv_list_mutex);
kvfree(uv_list);
mutex_unlock(&uv_list_mutex);
}
module_cpu_feature_match(S390_CPU_FEATURE_UV, pkey_uv_init);
module_exit(pkey_uv_exit);
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright IBM Corp. 2022
*
* Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
*/
#ifndef VFIO_AP_DEBUG_H
#define VFIO_AP_DEBUG_H
#include <asm/debug.h>
#define DBF_ERR 3 /* error conditions */
#define DBF_WARN 4 /* warning conditions */
#define DBF_INFO 5 /* informational */
#define DBF_DEBUG 6 /* for debugging only */
#define DBF_MAX_SPRINTF_ARGS 10
#define VFIO_AP_DBF(...) \
debug_sprintf_event(vfio_ap_dbf_info, ##__VA_ARGS__)
#define VFIO_AP_DBF_ERR(...) \
debug_sprintf_event(vfio_ap_dbf_info, DBF_ERR, ##__VA_ARGS__)
#define VFIO_AP_DBF_WARN(...) \
debug_sprintf_event(vfio_ap_dbf_info, DBF_WARN, ##__VA_ARGS__)
#define VFIO_AP_DBF_INFO(...) \
debug_sprintf_event(vfio_ap_dbf_info, DBF_INFO, ##__VA_ARGS__)
#define VFIO_AP_DBF_DBG(...) \
debug_sprintf_event(vfio_ap_dbf_info, DBF_DEBUG, ##__VA_ARGS__)
extern debug_info_t *vfio_ap_dbf_info;
#endif /* VFIO_AP_DEBUG_H */
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* VFIO based AP device driver
*
* Copyright IBM Corp. 2018
*
* Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
* Pierre Morel <pmorel@linux.ibm.com>
*/
#include <linux/module.h>
#include <linux/device-id/ap.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/facility.h>
#include "vfio_ap_private.h"
#include "vfio_ap_debug.h"
#define VFIO_AP_ROOT_NAME "vfio_ap"
#define VFIO_AP_DEV_NAME "matrix"
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("VFIO AP device driver, Copyright IBM Corp. 2018");
MODULE_LICENSE("GPL v2");
struct ap_matrix_dev *matrix_dev;
debug_info_t *vfio_ap_dbf_info;
static ssize_t features_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "guest_matrix hotplug ap_config\n");
}
static DEVICE_ATTR_RO(features);
static struct attribute *matrix_dev_attrs[] = {
&dev_attr_features.attr,
NULL,
};
ATTRIBUTE_GROUPS(matrix_dev);
/* Only type 10 adapters (CEX4 and later) are supported
* by the AP matrix device driver
*/
static struct ap_device_id ap_queue_ids[] = {
{ .dev_type = AP_DEVICE_TYPE_CEX4,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX5,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX6,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX7,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX8,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ /* end of sibling */ },
};
static struct ap_driver vfio_ap_drv = {
.probe = vfio_ap_mdev_probe_queue,
.remove = vfio_ap_mdev_remove_queue,
.in_use = vfio_ap_mdev_resource_in_use,
.on_config_changed = vfio_ap_on_cfg_changed,
.on_scan_complete = vfio_ap_on_scan_complete,
.ids = ap_queue_ids,
};
static void vfio_ap_matrix_dev_release(struct device *dev)
{
struct ap_matrix_dev *matrix_dev;
matrix_dev = container_of(dev, struct ap_matrix_dev, device);
kfree(matrix_dev);
}
static const struct bus_type matrix_bus = {
.name = "matrix",
};
static struct device_driver matrix_driver = {
.name = "vfio_ap",
.bus = &matrix_bus,
.suppress_bind_attrs = true,
.dev_groups = matrix_dev_groups,
};
static int vfio_ap_matrix_dev_create(void)
{
int ret;
struct device *root_device;
root_device = root_device_register(VFIO_AP_ROOT_NAME);
if (IS_ERR(root_device))
return PTR_ERR(root_device);
ret = bus_register(&matrix_bus);
if (ret)
goto bus_register_err;
matrix_dev = kzalloc_obj(*matrix_dev);
if (!matrix_dev) {
ret = -ENOMEM;
goto matrix_alloc_err;
}
/* Fill in config info via PQAP(QCI), if available */
if (test_facility(12)) {
ret = ap_qci(&matrix_dev->info);
if (ret)
goto matrix_alloc_err;
}
mutex_init(&matrix_dev->mdevs_lock);
INIT_LIST_HEAD(&matrix_dev->mdev_list);
mutex_init(&matrix_dev->guests_lock);
dev_set_name(&matrix_dev->device, "%s", VFIO_AP_DEV_NAME);
matrix_dev->device.parent = root_device;
matrix_dev->device.bus = &matrix_bus;
matrix_dev->device.release = vfio_ap_matrix_dev_release;
matrix_dev->vfio_ap_drv = &vfio_ap_drv;
ret = device_register(&matrix_dev->device);
if (ret)
goto matrix_reg_err;
ret = driver_register(&matrix_driver);
if (ret)
goto matrix_drv_err;
return 0;
matrix_drv_err:
device_del(&matrix_dev->device);
matrix_reg_err:
put_device(&matrix_dev->device);
matrix_alloc_err:
bus_unregister(&matrix_bus);
bus_register_err:
root_device_unregister(root_device);
return ret;
}
static void vfio_ap_matrix_dev_destroy(void)
{
struct device *root_device = matrix_dev->device.parent;
driver_unregister(&matrix_driver);
device_unregister(&matrix_dev->device);
bus_unregister(&matrix_bus);
root_device_unregister(root_device);
}
static int __init vfio_ap_dbf_info_init(void)
{
vfio_ap_dbf_info = debug_register("vfio_ap", 1, 1,
DBF_MAX_SPRINTF_ARGS * sizeof(long));
if (!vfio_ap_dbf_info)
return -ENOENT;
debug_register_view(vfio_ap_dbf_info, &debug_sprintf_view);
debug_set_level(vfio_ap_dbf_info, DBF_WARN);
return 0;
}
static int __init vfio_ap_init(void)
{
int ret;
ret = vfio_ap_dbf_info_init();
if (ret)
return ret;
/* If there are no AP instructions, there is nothing to pass through. */
if (!ap_instructions_available())
return -ENODEV;
ret = vfio_ap_matrix_dev_create();
if (ret)
return ret;
ret = ap_driver_register(&vfio_ap_drv, THIS_MODULE, VFIO_AP_DRV_NAME);
if (ret) {
vfio_ap_matrix_dev_destroy();
return ret;
}
ret = vfio_ap_mdev_register();
if (ret) {
ap_driver_unregister(&vfio_ap_drv);
vfio_ap_matrix_dev_destroy();
return ret;
}
return 0;
}
static void __exit vfio_ap_exit(void)
{
vfio_ap_mdev_unregister();
ap_driver_unregister(&vfio_ap_drv);
vfio_ap_matrix_dev_destroy();
debug_unregister(vfio_ap_dbf_info);
}
module_init(vfio_ap_init);
module_exit(vfio_ap_exit);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,168 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Private data and functions for adjunct processor VFIO matrix driver.
*
* Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
* Halil Pasic <pasic@linux.ibm.com>
* Pierre Morel <pmorel@linux.ibm.com>
*
* Copyright IBM Corp. 2018
*/
#ifndef _VFIO_AP_PRIVATE_H_
#define _VFIO_AP_PRIVATE_H_
#include <linux/types.h>
#include <linux/mdev.h>
#include <linux/delay.h>
#include <linux/eventfd.h>
#include <linux/mutex.h>
#include <linux/kvm_host.h>
#include <linux/vfio.h>
#include <linux/hashtable.h>
#include "ap_bus.h"
#define VFIO_AP_MODULE_NAME "vfio_ap"
#define VFIO_AP_DRV_NAME "vfio_ap"
/**
* struct ap_matrix_dev - Contains the data for the matrix device.
*
* @device: generic device structure associated with the AP matrix device
* @info: the struct containing the output from the PQAP(QCI) instruction
* @mdev_list: the list of mediated matrix devices created
* @mdevs_lock: mutex for locking the AP matrix device. This lock will be
* taken every time we fiddle with state managed by the vfio_ap
* driver, be it using @mdev_list or writing the state of a
* single ap_matrix_mdev device. It's quite coarse but we don't
* expect much contention.
* @vfio_ap_drv: the vfio_ap device driver
* @guests_lock: mutex for controlling access to a guest that is using AP
* devices passed through by the vfio_ap device driver. This lock
* will be taken when the AP devices are plugged into or unplugged
* from a guest, and when an ap_matrix_mdev device is added to or
* removed from @mdev_list or the list is iterated.
*/
struct ap_matrix_dev {
struct device device;
struct ap_config_info info;
struct list_head mdev_list;
struct mutex mdevs_lock; /* serializes access to each ap_matrix_mdev */
struct ap_driver *vfio_ap_drv;
struct mutex guests_lock; /* serializes access to each KVM guest */
struct mdev_parent parent;
struct mdev_type mdev_type;
struct mdev_type *mdev_types;
};
extern struct ap_matrix_dev *matrix_dev;
/**
* struct ap_matrix - matrix of adapters, domains and control domains
*
* @apm_max: max adapter number in @apm
* @apm: identifies the AP adapters in the matrix
* @aqm_max: max domain number in @aqm
* @aqm: identifies the AP queues (domains) in the matrix
* @adm_max: max domain number in @adm
* @adm: identifies the AP control domains in the matrix
*
* The AP matrix is comprised of three bit masks identifying the adapters,
* queues (domains) and control domains that belong to an AP matrix. The bits in
* each mask, from left to right, correspond to IDs 0 to 255. When a bit is set
* the corresponding ID belongs to the matrix.
*/
struct ap_matrix {
unsigned long apm_max;
DECLARE_BITMAP(apm, AP_DEVICES);
unsigned long aqm_max;
DECLARE_BITMAP(aqm, AP_DOMAINS);
unsigned long adm_max;
DECLARE_BITMAP(adm, AP_DOMAINS);
};
/**
* struct ap_queue_table - a table of queue objects.
*
* @queues: a hashtable of queues (struct vfio_ap_queue).
*/
struct ap_queue_table {
DECLARE_HASHTABLE(queues, 8);
};
/**
* struct ap_matrix_mdev - Contains the data associated with a matrix mediated
* device.
* @vdev: the vfio device
* @node: allows the ap_matrix_mdev struct to be added to a list
* @matrix: the adapters, usage domains and control domains assigned to the
* mediated matrix device.
* @shadow_apcb: the shadow copy of the APCB field of the KVM guest's CRYCB
* @kvm: the struct holding guest's state
* @pqap_hook: the function pointer to the interception handler for the
* PQAP(AQIC) instruction.
* @mdev: the mediated device
* @qtable: table of queues (struct vfio_ap_queue) assigned to the mdev
* @req_trigger eventfd ctx for signaling userspace to return a device
* @cfg_chg_trigger eventfd ctx to signal AP config changed to userspace
* @apm_add: bitmap of APIDs added to the host's AP configuration
* @aqm_add: bitmap of APQIs added to the host's AP configuration
* @adm_add: bitmap of control domain numbers added to the host's AP
* configuration
*/
struct ap_matrix_mdev {
struct vfio_device vdev;
struct list_head node;
struct ap_matrix matrix;
struct ap_matrix shadow_apcb;
struct kvm *kvm;
crypto_hook pqap_hook;
struct mdev_device *mdev;
struct ap_queue_table qtable;
struct eventfd_ctx *req_trigger;
struct eventfd_ctx *cfg_chg_trigger;
DECLARE_BITMAP(apm_add, AP_DEVICES);
DECLARE_BITMAP(aqm_add, AP_DOMAINS);
DECLARE_BITMAP(adm_add, AP_DOMAINS);
};
/**
* struct vfio_ap_queue - contains the data associated with a queue bound to the
* vfio_ap device driver
* @matrix_mdev: the matrix mediated device
* @saved_iova: the notification indicator byte (nib) address
* @apqn: the APQN of the AP queue device
* @saved_isc: the guest ISC registered with the GIB interface
* @mdev_qnode: allows the vfio_ap_queue struct to be added to a hashtable
* @reset_qnode: allows the vfio_ap_queue struct to be added to a list of queues
* that need to be reset
* @reset_status: the status from the last reset of the queue
* @reset_work: work to wait for queue reset to complete
*/
struct vfio_ap_queue {
struct ap_matrix_mdev *matrix_mdev;
dma_addr_t saved_iova;
int apqn;
#define VFIO_AP_ISC_INVALID 0xff
unsigned char saved_isc;
struct hlist_node mdev_qnode;
struct list_head reset_qnode;
struct ap_queue_status reset_status;
struct work_struct reset_work;
};
int vfio_ap_mdev_register(void);
void vfio_ap_mdev_unregister(void);
int vfio_ap_mdev_probe_queue(struct ap_device *queue);
void vfio_ap_mdev_remove_queue(struct ap_device *queue);
int vfio_ap_mdev_resource_in_use(unsigned long *apm, unsigned long *aqm);
void vfio_ap_on_cfg_changed(struct ap_config_info *new_config_info,
struct ap_config_info *old_config_info);
void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
struct ap_config_info *old_config_info);
#endif /* _VFIO_AP_PRIVATE_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,199 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2001, 2019
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
* Cornelia Huck <cornelia.huck@de.ibm.com>
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
* MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*/
#ifndef _ZCRYPT_API_H_
#define _ZCRYPT_API_H_
#include <linux/atomic.h>
#include <asm/debug.h>
#include <asm/zcrypt.h>
#include "ap_bus.h"
/**
* Supported device types
*/
#define ZCRYPT_CEX2C 5
#define ZCRYPT_CEX2A 6
#define ZCRYPT_CEX3C 7
#define ZCRYPT_CEX3A 8
#define ZCRYPT_CEX4 10
#define ZCRYPT_CEX5 11
#define ZCRYPT_CEX6 12
#define ZCRYPT_CEX7 13
/**
* Large random numbers are pulled in 4096 byte chunks from the crypto cards
* and stored in a page. Be careful when increasing this buffer due to size
* limitations for AP requests.
*/
#define ZCRYPT_RNG_BUFFER_SIZE 4096
/**
* The zcrypt_wait_api_operational() function waits this
* amount in milliseconds for ap_wait_aqpn_bindings_complete().
* Also on a cprb send failure with ENODEV the send functions
* trigger an ap bus rescan and wait this time in milliseconds
* for ap_wait_aqpn_bindings_complete() before resending.
*/
#define ZCRYPT_WAIT_BINDINGS_COMPLETE_MS 30000
/*
* Identifier for Crypto Request Performance Index
*/
enum crypto_ops {
MEX_1K,
MEX_2K,
MEX_4K,
CRT_1K,
CRT_2K,
CRT_4K,
HWRNG,
SECKEY,
NUM_OPS
};
struct zcrypt_queue;
/* struct to hold tracking information for a userspace request/response */
struct zcrypt_track {
int again_counter; /* retry attempts counter */
int last_qid; /* last qid used */
int last_rc; /* last return code */
};
/* defines related to message tracking */
#define TRACK_AGAIN_MAX 10
#define TRACK_AGAIN_CARD_WEIGHT_PENALTY 1000
#define TRACK_AGAIN_QUEUE_WEIGHT_PENALTY 10000
/*
* xflags - to be used with zcrypt_send_cprb() and
* zcrypt_send_ep11_cprb() for the xflags parameter.
*/
#define ZCRYPT_XFLAG_USERSPACE 0x0001 /* data ptrs address userspace */
#define ZCRYPT_XFLAG_NOMEMALLOC 0x0002 /* do not allocate memory via kmalloc */
struct zcrypt_ops {
long (*rsa_modexpo)(struct zcrypt_queue *, struct ica_rsa_modexpo *,
struct ap_message *);
long (*rsa_modexpo_crt)(struct zcrypt_queue *,
struct ica_rsa_modexpo_crt *,
struct ap_message *);
long (*send_cprb)(bool userspace, struct zcrypt_queue *, struct ica_xcRB *,
struct ap_message *);
long (*send_ep11_cprb)(bool userspace, struct zcrypt_queue *, struct ep11_urb *,
struct ap_message *);
long (*rng)(struct zcrypt_queue *, char *, struct ap_message *);
struct list_head list; /* zcrypt ops list. */
struct module *owner;
int variant;
char name[128];
};
struct zcrypt_card {
struct list_head list; /* Device list. */
struct list_head zqueues; /* List of zcrypt queues */
struct kref refcount; /* device refcounting */
int online; /* User online/offline */
struct ap_card *card; /* The "real" ap card device. */
char *type_string; /* User space device name. */
int user_space_type; /* User space device id. */
int min_mod_size; /* Min number of bits. */
int max_mod_size; /* Max number of bits. */
int max_exp_bit_length;
const int *speed_rating; /* Speed idx of crypto ops. */
atomic_t load; /* Utilization of the crypto device */
int request_count; /* # current requests. */
};
struct zcrypt_queue {
struct list_head list; /* Device list. */
struct kref refcount; /* device refcounting */
int online; /* User online/offline */
struct zcrypt_card *zcard;
struct zcrypt_ops *ops; /* Crypto operations. */
struct ap_queue *queue; /* The "real" ap queue device. */
struct ap_message reply; /* Per-device reply structure. */
atomic_t load; /* Utilization of the crypto device */
int request_count; /* # current requests. */
};
/* transport layer rescanning */
extern atomic_t zcrypt_rescan_req;
extern spinlock_t zcrypt_list_lock;
extern struct list_head zcrypt_card_list;
extern unsigned int zcrypt_mempool_threshold;
#define for_each_zcrypt_card(_zc) \
list_for_each_entry(_zc, &zcrypt_card_list, list)
#define for_each_zcrypt_queue(_zq, _zc) \
list_for_each_entry(_zq, &(_zc)->zqueues, list)
struct zcrypt_card *zcrypt_card_alloc(void);
void zcrypt_card_free(struct zcrypt_card *);
void zcrypt_card_get(struct zcrypt_card *);
int zcrypt_card_put(struct zcrypt_card *);
int zcrypt_card_register(struct zcrypt_card *);
void zcrypt_card_unregister(struct zcrypt_card *);
struct zcrypt_queue *zcrypt_queue_alloc(size_t);
void zcrypt_queue_free(struct zcrypt_queue *);
void zcrypt_queue_get(struct zcrypt_queue *);
int zcrypt_queue_put(struct zcrypt_queue *);
int zcrypt_queue_register(struct zcrypt_queue *);
void zcrypt_queue_unregister(struct zcrypt_queue *);
bool zcrypt_queue_force_online(struct zcrypt_queue *zq, int online);
int zcrypt_rng_device_add(void);
void zcrypt_rng_device_remove(void);
void zcrypt_msgtype_register(struct zcrypt_ops *);
void zcrypt_msgtype_unregister(struct zcrypt_ops *);
struct zcrypt_ops *zcrypt_msgtype(unsigned char *, int);
int zcrypt_api_init(void);
void zcrypt_api_exit(void);
long zcrypt_send_cprb(struct ica_xcRB *xcRB, u32 xflags);
long zcrypt_send_ep11_cprb(struct ep11_urb *urb, u32 xflags);
void zcrypt_device_status_mask_ext(struct zcrypt_device_status_ext *devstatus,
int maxcard, int maxqueue);
int zcrypt_device_status_ext(int card, int queue,
struct zcrypt_device_status_ext *devstatus);
int zcrypt_wait_api_operational(void);
static inline unsigned long z_copy_from_user(bool userspace,
void *to,
const void __user *from,
unsigned long n)
{
if (likely(userspace))
return copy_from_user(to, from, n);
memcpy(to, (void __force *)from, n);
return 0;
}
static inline unsigned long z_copy_to_user(bool userspace,
void __user *to,
const void *from,
unsigned long n)
{
if (likely(userspace))
return copy_to_user(to, from, n);
memcpy((void __force *)to, from, n);
return 0;
}
#endif /* _ZCRYPT_API_H_ */
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
* Cornelia Huck <cornelia.huck@de.ibm.com>
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
* MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/hw_random.h>
#include <linux/debugfs.h>
#include <asm/debug.h>
#include "zcrypt_debug.h"
#include "zcrypt_api.h"
#include "zcrypt_msgtype6.h"
#include "zcrypt_msgtype50.h"
/*
* Device attributes common for all crypto card devices.
*/
static ssize_t type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct zcrypt_card *zc = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", zc->type_string);
}
static DEVICE_ATTR_RO(type);
static ssize_t online_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct zcrypt_card *zc = dev_get_drvdata(dev);
struct ap_card *ac = to_ap_card(dev);
int online = ac->config && !ac->chkstop && zc->online ? 1 : 0;
return sysfs_emit(buf, "%d\n", online);
}
static ssize_t online_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct zcrypt_card *zc = dev_get_drvdata(dev);
struct ap_card *ac = to_ap_card(dev);
struct zcrypt_queue *zq;
int online, id, i = 0, maxzqs = 0;
struct zcrypt_queue **zq_uelist = NULL;
if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
return -EINVAL;
if (online && (!ac->config || ac->chkstop))
return -ENODEV;
zc->online = online;
id = zc->card->id;
ZCRYPT_DBF_INFO("%s card=%02x online=%d\n", __func__, id, online);
ap_send_online_uevent(&ac->ap_dev, online);
spin_lock(&zcrypt_list_lock);
/*
* As we are in atomic context here, directly sending uevents
* does not work. So collect the zqueues in a dynamic array
* and process them after zcrypt_list_lock release. As we get/put
* the zqueue objects, we make sure they exist after lock release.
*/
list_for_each_entry(zq, &zc->zqueues, list)
maxzqs++;
if (maxzqs > 0)
zq_uelist = kzalloc_objs(*zq_uelist, maxzqs + 1, GFP_ATOMIC);
list_for_each_entry(zq, &zc->zqueues, list)
if (zcrypt_queue_force_online(zq, online))
if (zq_uelist) {
zcrypt_queue_get(zq);
zq_uelist[i++] = zq;
}
spin_unlock(&zcrypt_list_lock);
if (zq_uelist) {
for (i = 0; zq_uelist[i]; i++) {
zq = zq_uelist[i];
ap_send_online_uevent(&zq->queue->ap_dev, online);
zcrypt_queue_put(zq);
}
kfree(zq_uelist);
}
return count;
}
static DEVICE_ATTR_RW(online);
static ssize_t load_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct zcrypt_card *zc = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", atomic_read(&zc->load));
}
static DEVICE_ATTR_RO(load);
static struct attribute *zcrypt_card_attrs[] = {
&dev_attr_type.attr,
&dev_attr_online.attr,
&dev_attr_load.attr,
NULL,
};
static const struct attribute_group zcrypt_card_attr_group = {
.attrs = zcrypt_card_attrs,
};
struct zcrypt_card *zcrypt_card_alloc(void)
{
struct zcrypt_card *zc;
zc = kzalloc_obj(*zc);
if (!zc)
return NULL;
INIT_LIST_HEAD(&zc->list);
INIT_LIST_HEAD(&zc->zqueues);
kref_init(&zc->refcount);
return zc;
}
EXPORT_SYMBOL(zcrypt_card_alloc);
void zcrypt_card_free(struct zcrypt_card *zc)
{
kfree(zc);
}
EXPORT_SYMBOL(zcrypt_card_free);
static void zcrypt_card_release(struct kref *kref)
{
struct zcrypt_card *zdev =
container_of(kref, struct zcrypt_card, refcount);
zcrypt_card_free(zdev);
}
void zcrypt_card_get(struct zcrypt_card *zc)
{
kref_get(&zc->refcount);
}
EXPORT_SYMBOL(zcrypt_card_get);
int zcrypt_card_put(struct zcrypt_card *zc)
{
return kref_put(&zc->refcount, zcrypt_card_release);
}
EXPORT_SYMBOL(zcrypt_card_put);
/**
* zcrypt_card_register() - Register a crypto card device.
* @zc: Pointer to a crypto card device
*
* Register a crypto card device. Returns 0 if successful.
*/
int zcrypt_card_register(struct zcrypt_card *zc)
{
int rc;
spin_lock(&zcrypt_list_lock);
list_add_tail(&zc->list, &zcrypt_card_list);
spin_unlock(&zcrypt_list_lock);
zc->online = 1;
ZCRYPT_DBF_INFO("%s card=%02x register online=1\n",
__func__, zc->card->id);
rc = sysfs_create_group(&zc->card->ap_dev.device.kobj,
&zcrypt_card_attr_group);
if (rc) {
spin_lock(&zcrypt_list_lock);
list_del_init(&zc->list);
spin_unlock(&zcrypt_list_lock);
}
return rc;
}
EXPORT_SYMBOL(zcrypt_card_register);
/**
* zcrypt_card_unregister(): Unregister a crypto card device.
* @zc: Pointer to crypto card device
*
* Unregister a crypto card device.
*/
void zcrypt_card_unregister(struct zcrypt_card *zc)
{
ZCRYPT_DBF_INFO("%s card=%02x unregister\n",
__func__, zc->card->id);
spin_lock(&zcrypt_list_lock);
list_del_init(&zc->list);
spin_unlock(&zcrypt_list_lock);
sysfs_remove_group(&zc->card->ap_dev.device.kobj,
&zcrypt_card_attr_group);
zcrypt_card_put(zc);
}
EXPORT_SYMBOL(zcrypt_card_unregister);
@@ -0,0 +1,237 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2001, 2006
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef _ZCRYPT_CCA_KEY_H_
#define _ZCRYPT_CCA_KEY_H_
struct t6_keyblock_hdr {
unsigned short blen;
unsigned short ulen;
unsigned short flags;
};
/**
* mapping for the cca private ME key token.
* Three parts of interest here: the header, the private section and
* the public section.
*
* mapping for the cca key token header
*/
struct cca_token_hdr {
unsigned char token_identifier;
unsigned char version;
unsigned short token_length;
unsigned char reserved[4];
} __packed;
#define CCA_TKN_HDR_ID_EXT 0x1E
#define CCA_PVT_USAGE_ALL 0x80
/**
* mapping for the cca public section
* In a private key, the modulus doesn't appear in the public
* section. So, an arbitrary public exponent of 0x010001 will be
* used, for a section length of 0x0F always.
*/
struct cca_public_sec {
unsigned char section_identifier;
unsigned char version;
unsigned short section_length;
unsigned char reserved[2];
unsigned short exponent_len;
unsigned short modulus_bit_len;
unsigned short modulus_byte_len; /* In a private key, this is 0 */
} __packed;
/**
* mapping for the cca private CRT key 'token'
* The first three parts (the only parts considered in this release)
* are: the header, the private section and the public section.
* The header and public section are the same as for the
* struct cca_private_ext_ME
*
* Following the structure are the quantities p, q, dp, dq, u, pad,
* and modulus, in that order, where pad_len is the modulo 8
* complement of the residue modulo 8 of the sum of
* (p_len + q_len + dp_len + dq_len + u_len).
*/
struct cca_pvt_ext_crt_sec {
unsigned char section_identifier;
unsigned char version;
unsigned short section_length;
unsigned char private_key_hash[20];
unsigned char reserved1[4];
unsigned char key_format;
unsigned char reserved2;
unsigned char key_name_hash[20];
unsigned char key_use_flags[4];
unsigned short p_len;
unsigned short q_len;
unsigned short dp_len;
unsigned short dq_len;
unsigned short u_len;
unsigned short mod_len;
unsigned char reserved3[4];
unsigned short pad_len;
unsigned char reserved4[52];
unsigned char confounder[8];
} __packed;
#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08
#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40
/**
* Set up private key fields of a type6 MEX message.
*
* @mex: pointer to user input data
* @p: pointer to memory area for the key
*
* Returns the size of the key area or negative errno value.
*/
static inline int zcrypt_type6_mex_key_en(struct ica_rsa_modexpo *mex, void *p)
{
static struct cca_token_hdr static_pub_hdr = {
.token_identifier = 0x1E,
};
static struct cca_public_sec static_pub_sec = {
.section_identifier = 0x04,
};
struct {
struct t6_keyblock_hdr t6_hdr;
struct cca_token_hdr pubhdr;
struct cca_public_sec pubsec;
char exponent[];
} __packed *key = p;
unsigned char *ptr;
/*
* The inputdatalength was a selection criteria in the dispatching
* function zcrypt_rsa_modexpo(). However, do a plausibility check
* here to make sure the following copy_from_user() can't be utilized
* to compromise the system.
*/
if (WARN_ON_ONCE(mex->inputdatalength > 512))
return -EINVAL;
memset(key, 0, sizeof(*key));
key->pubhdr = static_pub_hdr;
key->pubsec = static_pub_sec;
/* key parameter block */
ptr = key->exponent;
if (copy_from_user(ptr, mex->b_key, mex->inputdatalength))
return -EFAULT;
ptr += mex->inputdatalength;
/* modulus */
if (copy_from_user(ptr, mex->n_modulus, mex->inputdatalength))
return -EFAULT;
key->pubsec.modulus_bit_len = 8 * mex->inputdatalength;
key->pubsec.modulus_byte_len = mex->inputdatalength;
key->pubsec.exponent_len = mex->inputdatalength;
key->pubsec.section_length = sizeof(key->pubsec) +
2 * mex->inputdatalength;
key->pubhdr.token_length =
key->pubsec.section_length + sizeof(key->pubhdr);
key->t6_hdr.ulen = key->pubhdr.token_length + 4;
key->t6_hdr.blen = key->pubhdr.token_length + 6;
return sizeof(*key) + 2 * mex->inputdatalength;
}
/**
* Set up private key fields of a type6 CRT message.
*
* @mex: pointer to user input data
* @p: pointer to memory area for the key
*
* Returns the size of the key area or -EFAULT
*/
static inline int zcrypt_type6_crt_key(struct ica_rsa_modexpo_crt *crt, void *p)
{
static struct cca_public_sec static_cca_pub_sec = {
.section_identifier = 4,
.section_length = 0x000f,
.exponent_len = 0x0003,
};
static char pk_exponent[3] = { 0x01, 0x00, 0x01 };
struct {
struct t6_keyblock_hdr t6_hdr;
struct cca_token_hdr token;
struct cca_pvt_ext_crt_sec pvt;
char key_parts[];
} __packed *key = p;
struct cca_public_sec *pub;
int short_len, long_len, pad_len, key_len, size;
/*
* The inputdatalength was a selection criteria in the dispatching
* function zcrypt_rsa_crt(). However, do a plausibility check
* here to make sure the following copy_from_user() can't be utilized
* to compromise the system.
*/
if (WARN_ON_ONCE(crt->inputdatalength > 512))
return -EINVAL;
memset(key, 0, sizeof(*key));
short_len = (crt->inputdatalength + 1) / 2;
long_len = short_len + 8;
pad_len = -(3 * long_len + 2 * short_len) & 7;
key_len = 3 * long_len + 2 * short_len + pad_len + crt->inputdatalength;
size = sizeof(*key) + key_len + sizeof(*pub) + 3;
/* parameter block.key block */
key->t6_hdr.blen = size;
key->t6_hdr.ulen = size - 2;
/* key token header */
key->token.token_identifier = CCA_TKN_HDR_ID_EXT;
key->token.token_length = size - 6;
/* private section */
key->pvt.section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
key->pvt.section_length = sizeof(key->pvt) + key_len;
key->pvt.key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
key->pvt.key_use_flags[0] = CCA_PVT_USAGE_ALL;
key->pvt.p_len = key->pvt.dp_len = key->pvt.u_len = long_len;
key->pvt.q_len = key->pvt.dq_len = short_len;
key->pvt.mod_len = crt->inputdatalength;
key->pvt.pad_len = pad_len;
/* key parts */
if (copy_from_user(key->key_parts, crt->np_prime, long_len) ||
copy_from_user(key->key_parts + long_len,
crt->nq_prime, short_len) ||
copy_from_user(key->key_parts + long_len + short_len,
crt->bp_key, long_len) ||
copy_from_user(key->key_parts + 2 * long_len + short_len,
crt->bq_key, short_len) ||
copy_from_user(key->key_parts + 2 * long_len + 2 * short_len,
crt->u_mult_inv, long_len))
return -EFAULT;
memset(key->key_parts + 3 * long_len + 2 * short_len + pad_len,
0xff, crt->inputdatalength);
pub = (struct cca_public_sec *)(key->key_parts + key_len);
*pub = static_cca_pub_sec;
pub->modulus_bit_len = 8 * crt->inputdatalength;
/*
* In a private key, the modulus doesn't appear in the public
* section. So, an arbitrary public exponent of 0x010001 will be
* used.
*/
memcpy((char *)(pub + 1), pk_exponent, 3);
return size;
}
#endif /* _ZCRYPT_CCA_KEY_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,268 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2019
* Author(s): Harald Freudenberger <freude@linux.ibm.com>
* Ingo Franzki <ifranzki@linux.ibm.com>
*
* Collection of CCA misc functions used by zcrypt and pkey
*/
#ifndef _ZCRYPT_CCAMISC_H_
#define _ZCRYPT_CCAMISC_H_
#include <asm/zcrypt.h>
#include <asm/pkey.h>
#include "zcrypt_api.h"
/* Key token types */
#define TOKTYPE_NON_CCA 0x00 /* Non-CCA key token */
#define TOKTYPE_CCA_INTERNAL 0x01 /* CCA internal sym key token */
#define TOKTYPE_CCA_INTERNAL_PKA 0x1f /* CCA internal asym key token */
/* For TOKTYPE_NON_CCA: */
#define TOKVER_PROTECTED_KEY 0x01 /* Protected key token */
#define TOKVER_CLEAR_KEY 0x02 /* Clear key token */
/* For TOKTYPE_CCA_INTERNAL: */
#define TOKVER_CCA_AES 0x04 /* CCA AES key token */
#define TOKVER_CCA_VLSC 0x05 /* var length sym cipher key token */
/* Max size of a cca variable length cipher key token */
#define MAXCCAVLSCTOKENSIZE 725
/* header part of a CCA key token */
struct keytoken_header {
u8 type; /* one of the TOKTYPE values */
u8 res0[1];
u16 len; /* vlsc token: total length in bytes */
u8 version; /* one of the TOKVER values */
u8 res1[3];
} __packed;
/* inside view of a CCA secure key token (only type 0x01 version 0x04) */
struct secaeskeytoken {
u8 type; /* 0x01 for internal key token */
u8 res0[3];
u8 version; /* should be 0x04 */
u8 res1[1];
u8 flag; /* key flags */
u8 res2[1];
u8 mkvp[8]; /* master key verification pattern */
u8 key[32]; /* key value (encrypted) */
u8 cv[8]; /* control vector */
u16 bitsize; /* key bit size */
u16 keysize; /* key byte size */
u8 tvv[4]; /* token validation value */
} __packed;
/* inside view of a variable length symmetric cipher AES key token */
struct cipherkeytoken {
u8 type; /* 0x01 for internal key token */
u8 res0[1];
u16 len; /* total key token length in bytes */
u8 version; /* should be 0x05 */
u8 res1[3];
u8 kms; /* key material state, 0x03 means wrapped with MK */
u8 kvpt; /* key verification pattern type, should be 0x01 */
u8 mkvp0[8]; /* master key verification pattern, lo part */
u8 mkvp1[8]; /* master key verification pattern, hi part (unused) */
u8 eskwm; /* encrypted section key wrapping method */
u8 hashalg; /* hash algorithmus used for wrapping key */
u8 plfver; /* pay load format version */
u8 res2[1];
u8 adsver; /* associated data section version */
u8 res3[1];
u16 adslen; /* associated data section length */
u8 kllen; /* optional key label length */
u8 ieaslen; /* optional extended associated data length */
u8 uadlen; /* optional user definable associated data length */
u8 res4[1];
u16 wpllen; /* wrapped payload length in bits: */
/* plfver 0x00 0x01 */
/* AES-128 512 640 */
/* AES-192 576 640 */
/* AES-256 640 640 */
u8 res5[1];
u8 algtype; /* 0x02 for AES cipher */
u16 keytype; /* 0x0001 for 'cipher' */
u8 kufc; /* key usage field count */
u16 kuf1; /* key usage field 1 */
u16 kuf2; /* key usage field 2 */
u8 kmfc; /* key management field count */
u16 kmf1; /* key management field 1 */
u16 kmf2; /* key management field 2 */
u16 kmf3; /* key management field 3 */
u8 vdata[]; /* variable part data follows */
} __packed;
/* inside view of an CCA secure ECC private key */
struct eccprivkeytoken {
u8 type; /* 0x1f for internal asym key token */
u8 version; /* should be 0x00 */
u16 len; /* total key token length in bytes */
u8 res1[4];
u8 secid; /* 0x20 for ECC priv key section marker */
u8 secver; /* section version */
u16 seclen; /* section length */
u8 wtype; /* wrapping method, 0x00 clear, 0x01 AES */
u8 htype; /* hash method, 0x02 for SHA-256 */
u8 res2[2];
u8 kutc; /* key usage and translation control */
u8 ctype; /* curve type */
u8 kfs; /* key format and security */
u8 ksrc; /* key source */
u16 pbitlen; /* length of prime p in bits */
u16 ibmadlen; /* IBM associated data length in bytes */
u8 mkvp[8]; /* master key verification pattern */
u8 opk[48]; /* encrypted object protection key data */
u16 adatalen; /* associated data length in bytes */
u16 fseclen; /* formatted section length in bytes */
u8 more_data[]; /* more data follows */
} __packed;
/* Some defines for the CCA AES cipherkeytoken kmf1 field */
#define KMF1_XPRT_SYM 0x8000
#define KMF1_XPRT_UASY 0x4000
#define KMF1_XPRT_AASY 0x2000
#define KMF1_XPRT_RAW 0x1000
#define KMF1_XPRT_CPAC 0x0800
#define KMF1_XPRT_DES 0x0080
#define KMF1_XPRT_AES 0x0040
#define KMF1_XPRT_RSA 0x0008
/*
* Simple check if the token is a valid CCA secure AES data key
* token. If keybitsize is given, the bitsize of the key is
* also checked. Returns 0 on success or errno value on failure.
*/
int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
const u8 *token, u32 keysize, int keybitsize);
/*
* Simple check if the token is a valid CCA secure AES cipher key
* token. If keybitsize is given, the bitsize of the key is
* also checked. If checkcpacfexport is enabled, the key is also
* checked for the export flag to allow CPACF export.
* Returns 0 on success or errno value on failure.
*/
int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
const u8 *token, u32 keysize,
int keybitsize, int checkcpacfexport);
/*
* Simple check if the token is a valid CCA secure ECC private
* key token. Returns 0 on success or errno value on failure.
*/
int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
const u8 *token, u32 keysize,
int checkcpacfexport);
/*
* Generate (random) CCA AES DATA secure key.
*/
int cca_genseckey(u16 cardnr, u16 domain, u32 keybitsize, u8 *seckey,
u32 xflags);
/*
* Generate CCA AES DATA secure key with given clear key value.
*/
int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
const u8 *clrkey, u8 *seckey, u32 xflags);
/*
* Derive proteced key from an CCA AES DATA secure key.
*/
int cca_sec2protkey(u16 cardnr, u16 domain,
const u8 *seckey, u8 *protkey, u32 *protkeylen,
u32 *protkeytype, u32 xflags);
/*
* Generate (random) CCA AES CIPHER secure key.
*/
int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
u8 *keybuf, u32 *keybufsize, u32 xflags);
/*
* Derive proteced key from CCA AES cipher secure key.
*/
int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags);
/*
* Build CCA AES CIPHER secure key with a given clear key value.
*/
int cca_clr2cipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
const u8 *clrkey, u8 *keybuf, u32 *keybufsize,
u32 xflags);
/*
* Derive proteced key from CCA ECC secure private key.
*/
int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
u8 *protkey, u32 *protkeylen, u32 *protkeytype, u32 xflags);
/*
* Query cryptographic facility from CCA adapter
*/
int cca_query_crypto_facility(u16 cardnr, u16 domain,
const char *keyword,
u8 *rarray, size_t *rarraylen,
u8 *varray, size_t *varraylen,
u32 xflags);
/*
* Build a list of cca apqns meeting the following constrains:
* - apqn is online and is in fact a CCA apqn
* - if cardnr is not FFFF only apqns with this cardnr
* - if domain is not FFFF only apqns with this domainnr
* - if minhwtype > 0 only apqns with hwtype >= minhwtype
* - if cur_mkvp != 0 only apqns where cur_mkvp == mkvp
* - if old_mkvp != 0 only apqns where old_mkvp == mkvp
* The mktype determines which set of master keys to use:
* 0 = AES_MK_SET - AES MK set, 1 = APKA MK_SET - APKA MK set
* The caller should set *nr_apqns to the nr of elements available in *apqns.
* On return *nr_apqns is then updated with the nr of apqns filled into *apqns.
* The return value is either 0 for success or a negative errno value.
* If no apqn meeting the criteria is found, -ENODEV is returned.
*/
int cca_findcard2(u32 *apqns, u32 *nr_apqns, u16 cardnr, u16 domain,
int minhwtype, int mktype,
const u8 *cur_mkvp, const u8 *old_mkvp, u32 xflags);
#define AES_MK_SET 0
#define APKA_MK_SET 1
/* struct to hold info for each CCA queue */
struct cca_info {
u8 new_asym_mkvp[16]; /* verify pattern of new asym master key */
u8 cur_asym_mkvp[16]; /* verify pattern of current asym master key */
u8 old_asym_mkvp[16]; /* verify pattern of old asym master key */
u8 new_aes_mkvp[8]; /* truncated sha256 of new aes master key */
u8 cur_aes_mkvp[8]; /* truncated sha256 of current aes master key */
u8 old_aes_mkvp[8]; /* truncated sha256 of old aes master key */
u8 new_apka_mkvp[8]; /* truncated sha256 of new apka master key */
u8 cur_apka_mkvp[8]; /* truncated sha256 of current apka mk */
u8 old_apka_mkvp[8]; /* truncated sha256 of old apka mk */
char serial[9]; /* serial number (8 ascii numbers + 0x00) */
char new_aes_mk_state; /* '1' empty, '2' partially full, '3' full */
char cur_aes_mk_state; /* '1' invalid, '2' valid */
char old_aes_mk_state; /* '1' invalid, '2' valid */
char new_apka_mk_state; /* '1' empty, '2' partially full, '3' full */
char cur_apka_mk_state; /* '1' invalid, '2' valid */
char old_apka_mk_state; /* '1' invalid, '2' valid */
char new_asym_mk_state; /* '1' empty, '2' partially full, '3' full */
char cur_asym_mk_state; /* '1' invalid, '2' valid */
char old_asym_mk_state; /* '1' invalid, '2' valid */
int hwtype; /* one of the defined AP_DEVICE_TYPE_* */
};
/*
* Fetch cca information about an CCA queue.
*/
int cca_get_info(u16 card, u16 dom, struct cca_info *ci, u32 xflags);
int zcrypt_ccamisc_init(void);
void zcrypt_ccamisc_exit(void);
#endif /* _ZCRYPT_CCAMISC_H_ */
@@ -0,0 +1,779 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright IBM Corp. 2012, 2022
* Author(s): Holger Dengler <hd@linux.vnet.ibm.com>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hex.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/device-id/ap.h>
#include "ap_bus.h"
#include "zcrypt_api.h"
#include "zcrypt_msgtype6.h"
#include "zcrypt_msgtype50.h"
#include "zcrypt_error.h"
#include "zcrypt_cex4.h"
#include "zcrypt_ccamisc.h"
#include "zcrypt_ep11misc.h"
#define CEX4A_MIN_MOD_SIZE 1 /* 8 bits */
#define CEX4A_MAX_MOD_SIZE_2K 256 /* 2048 bits */
#define CEX4A_MAX_MOD_SIZE_4K 512 /* 4096 bits */
#define CEX4C_MIN_MOD_SIZE 16 /* 256 bits */
#define CEX4C_MAX_MOD_SIZE 512 /* 4096 bits */
/* Waiting time for requests to be processed.
* Currently there are some types of request which are not deterministic.
* But the maximum time limit managed by the stomper code is set to 60sec.
* Hence we have to wait at least that time period.
*/
#define CEX4_CLEANUP_TIME (900 * HZ)
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("CEX[45678] Cryptographic Card device driver, " \
"Copyright IBM Corp. 2022");
MODULE_LICENSE("GPL");
static struct ap_device_id zcrypt_cex4_card_ids[] = {
{ .dev_type = AP_DEVICE_TYPE_CEX4,
.match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX5,
.match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX6,
.match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX7,
.match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX8,
.match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(ap, zcrypt_cex4_card_ids);
static struct ap_device_id zcrypt_cex4_queue_ids[] = {
{ .dev_type = AP_DEVICE_TYPE_CEX4,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX5,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX6,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX7,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ .dev_type = AP_DEVICE_TYPE_CEX8,
.match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(ap, zcrypt_cex4_queue_ids);
/*
* CCA card additional device attributes
*/
static ssize_t cca_serialnr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ap_card *ac = to_ap_card(dev);
struct cca_info ci;
memset(&ci, 0, sizeof(ci));
cca_get_info(ac->id, AUTOSEL_DOM, &ci, 0);
return sysfs_emit(buf, "%s\n", ci.serial);
}
static struct device_attribute dev_attr_cca_serialnr =
__ATTR(serialnr, 0444, cca_serialnr_show, NULL);
static struct attribute *cca_card_attrs[] = {
&dev_attr_cca_serialnr.attr,
NULL,
};
static const struct attribute_group cca_card_attr_grp = {
.attrs = cca_card_attrs,
};
/*
* Simple helper macro to format raw mkvp byte array into hex
*/
#define MKVP_TO_HEXBUF(mkvp, buf) \
do { \
BUILD_BUG_ON(sizeof(buf) <= 2 * sizeof(mkvp)); \
bin2hex(buf, mkvp, sizeof(mkvp)); \
buf[2 * sizeof(mkvp)] = '\0'; \
} while (0)
/*
* CCA queue additional device attributes
*/
static ssize_t cca_mkvps_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
static const char * const new_state[] = { "empty", "partial", "full" };
static const char * const cao_state[] = { "invalid", "valid" };
struct zcrypt_queue *zq = dev_get_drvdata(dev);
struct cca_info ci;
char hexbuf[2 * 16 + 1];
int n = 0;
memset(&ci, 0, sizeof(ci));
cca_get_info(AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid),
&ci, 0);
if (ci.new_aes_mk_state >= '1' && ci.new_aes_mk_state <= '3') {
MKVP_TO_HEXBUF(ci.new_aes_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "AES NEW: %s 0x%s\n",
new_state[ci.new_aes_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "AES NEW: - -\n");
}
if (ci.cur_aes_mk_state >= '1' && ci.cur_aes_mk_state <= '2') {
MKVP_TO_HEXBUF(ci.cur_aes_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "AES CUR: %s 0x%s\n",
cao_state[ci.cur_aes_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "AES CUR: - -\n");
}
if (ci.old_aes_mk_state >= '1' && ci.old_aes_mk_state <= '2') {
MKVP_TO_HEXBUF(ci.old_aes_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "AES OLD: %s 0x%s\n",
cao_state[ci.old_aes_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "AES OLD: - -\n");
}
if (ci.new_apka_mk_state >= '1' && ci.new_apka_mk_state <= '3') {
MKVP_TO_HEXBUF(ci.new_apka_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "APKA NEW: %s 0x%s\n",
new_state[ci.new_apka_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "APKA NEW: - -\n");
}
if (ci.cur_apka_mk_state >= '1' && ci.cur_apka_mk_state <= '2') {
MKVP_TO_HEXBUF(ci.cur_apka_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "APKA CUR: %s 0x%s\n",
cao_state[ci.cur_apka_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "APKA CUR: - -\n");
}
if (ci.old_apka_mk_state >= '1' && ci.old_apka_mk_state <= '2') {
MKVP_TO_HEXBUF(ci.old_apka_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "APKA OLD: %s 0x%s\n",
cao_state[ci.old_apka_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "APKA OLD: - -\n");
}
if (ci.new_asym_mk_state >= '1' && ci.new_asym_mk_state <= '3') {
MKVP_TO_HEXBUF(ci.new_asym_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "ASYM NEW: %s 0x%s\n",
new_state[ci.new_asym_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "ASYM NEW: - -\n");
}
if (ci.cur_asym_mk_state >= '1' && ci.cur_asym_mk_state <= '2') {
MKVP_TO_HEXBUF(ci.cur_asym_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "ASYM CUR: %s 0x%s\n",
cao_state[ci.cur_asym_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "ASYM CUR: - -\n");
}
if (ci.old_asym_mk_state >= '1' && ci.old_asym_mk_state <= '2') {
MKVP_TO_HEXBUF(ci.old_asym_mkvp, hexbuf);
n += sysfs_emit_at(buf, n, "ASYM OLD: %s 0x%s\n",
cao_state[ci.old_asym_mk_state - '1'],
hexbuf);
} else {
n += sysfs_emit_at(buf, n, "ASYM OLD: - -\n");
}
return n;
}
static struct device_attribute dev_attr_cca_mkvps =
__ATTR(mkvps, 0444, cca_mkvps_show, NULL);
static struct attribute *cca_queue_attrs[] = {
&dev_attr_cca_mkvps.attr,
NULL,
};
static const struct attribute_group cca_queue_attr_grp = {
.attrs = cca_queue_attrs,
};
/*
* EP11 card additional device attributes
*/
static ssize_t ep11_api_ordinalnr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ap_card *ac = to_ap_card(dev);
struct ep11_card_info ci;
memset(&ci, 0, sizeof(ci));
ep11_get_card_info(ac->id, &ci, 0);
if (ci.API_ord_nr > 0)
return sysfs_emit(buf, "%u\n", ci.API_ord_nr);
else
return sysfs_emit(buf, "\n");
}
static struct device_attribute dev_attr_ep11_api_ordinalnr =
__ATTR(API_ordinalnr, 0444, ep11_api_ordinalnr_show, NULL);
static ssize_t ep11_fw_version_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ap_card *ac = to_ap_card(dev);
struct ep11_card_info ci;
memset(&ci, 0, sizeof(ci));
ep11_get_card_info(ac->id, &ci, 0);
if (ci.FW_version > 0)
return sysfs_emit(buf, "%d.%d\n",
(int)(ci.FW_version >> 8),
(int)(ci.FW_version & 0xFF));
else
return sysfs_emit(buf, "\n");
}
static struct device_attribute dev_attr_ep11_fw_version =
__ATTR(FW_version, 0444, ep11_fw_version_show, NULL);
static ssize_t ep11_serialnr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ap_card *ac = to_ap_card(dev);
struct ep11_card_info ci;
memset(&ci, 0, sizeof(ci));
ep11_get_card_info(ac->id, &ci, 0);
if (ci.serial[0])
return sysfs_emit(buf, "%16.16s\n", ci.serial);
else
return sysfs_emit(buf, "\n");
}
static struct device_attribute dev_attr_ep11_serialnr =
__ATTR(serialnr, 0444, ep11_serialnr_show, NULL);
static const struct {
int mode_bit;
const char *mode_txt;
} ep11_op_modes[] = {
{ 0, "FIPS2009" },
{ 1, "BSI2009" },
{ 2, "FIPS2011" },
{ 3, "BSI2011" },
{ 4, "SIGG-IMPORT" },
{ 5, "SIGG" },
{ 6, "BSICC2017" },
{ 7, "FIPS2021" },
{ 8, "FIPS2024" },
{ 0, NULL }
};
static ssize_t ep11_card_op_modes_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ap_card *ac = to_ap_card(dev);
struct ep11_card_info ci;
int i, n = 0;
memset(&ci, 0, sizeof(ci));
ep11_get_card_info(ac->id, &ci, 0);
for (i = 0; ep11_op_modes[i].mode_txt; i++) {
if (ci.op_mode & (1ULL << ep11_op_modes[i].mode_bit)) {
if (n > 0)
buf[n++] = ' ';
n += sysfs_emit_at(buf, n, "%s",
ep11_op_modes[i].mode_txt);
}
}
n += sysfs_emit_at(buf, n, "\n");
return n;
}
static struct device_attribute dev_attr_ep11_card_op_modes =
__ATTR(op_modes, 0444, ep11_card_op_modes_show, NULL);
static struct attribute *ep11_card_attrs[] = {
&dev_attr_ep11_api_ordinalnr.attr,
&dev_attr_ep11_fw_version.attr,
&dev_attr_ep11_serialnr.attr,
&dev_attr_ep11_card_op_modes.attr,
NULL,
};
static const struct attribute_group ep11_card_attr_grp = {
.attrs = ep11_card_attrs,
};
/*
* EP11 queue additional device attributes
*/
static ssize_t ep11_mkvps_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct zcrypt_queue *zq = dev_get_drvdata(dev);
int n = 0;
struct ep11_domain_info di;
static const char * const cwk_state[] = { "invalid", "valid" };
static const char * const nwk_state[] = { "empty", "uncommitted",
"committed" };
memset(&di, 0, sizeof(di));
if (zq->online)
ep11_get_domain_info(AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid),
&di, 0);
if (di.cur_wk_state == '0') {
n = sysfs_emit(buf, "WK CUR: %s -\n",
cwk_state[di.cur_wk_state - '0']);
} else if (di.cur_wk_state == '1') {
n = sysfs_emit(buf, "WK CUR: %s 0x",
cwk_state[di.cur_wk_state - '0']);
bin2hex(buf + n, di.cur_wkvp, sizeof(di.cur_wkvp));
n += 2 * sizeof(di.cur_wkvp);
n += sysfs_emit_at(buf, n, "\n");
} else {
n = sysfs_emit(buf, "WK CUR: - -\n");
}
if (di.new_wk_state == '0') {
n += sysfs_emit_at(buf, n, "WK NEW: %s -\n",
nwk_state[di.new_wk_state - '0']);
} else if (di.new_wk_state >= '1' && di.new_wk_state <= '2') {
n += sysfs_emit_at(buf, n, "WK NEW: %s 0x",
nwk_state[di.new_wk_state - '0']);
bin2hex(buf + n, di.new_wkvp, sizeof(di.new_wkvp));
n += 2 * sizeof(di.new_wkvp);
n += sysfs_emit_at(buf, n, "\n");
} else {
n += sysfs_emit_at(buf, n, "WK NEW: - -\n");
}
return n;
}
static struct device_attribute dev_attr_ep11_mkvps =
__ATTR(mkvps, 0444, ep11_mkvps_show, NULL);
static ssize_t ep11_queue_op_modes_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct zcrypt_queue *zq = dev_get_drvdata(dev);
int i, n = 0;
struct ep11_domain_info di;
memset(&di, 0, sizeof(di));
if (zq->online)
ep11_get_domain_info(AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid),
&di, 0);
for (i = 0; ep11_op_modes[i].mode_txt; i++) {
if (di.op_mode & (1ULL << ep11_op_modes[i].mode_bit)) {
if (n > 0)
buf[n++] = ' ';
n += sysfs_emit_at(buf, n, "%s",
ep11_op_modes[i].mode_txt);
}
}
n += sysfs_emit_at(buf, n, "\n");
return n;
}
static struct device_attribute dev_attr_ep11_queue_op_modes =
__ATTR(op_modes, 0444, ep11_queue_op_modes_show, NULL);
static struct attribute *ep11_queue_attrs[] = {
&dev_attr_ep11_mkvps.attr,
&dev_attr_ep11_queue_op_modes.attr,
NULL,
};
static const struct attribute_group ep11_queue_attr_grp = {
.attrs = ep11_queue_attrs,
};
/*
* Probe function for CEX[45678] card device. It always
* accepts the AP device since the bus_match already checked
* the hardware type.
* @ap_dev: pointer to the AP device.
*/
static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
{
/*
* Normalized speed ratings per crypto adapter
* MEX_1k, MEX_2k, MEX_4k, CRT_1k, CRT_2k, CRT_4k, RNG, SECKEY
*/
static const int CEX4A_SPEED_IDX[NUM_OPS] = {
14, 19, 249, 42, 228, 1458, 0, 0};
static const int CEX5A_SPEED_IDX[NUM_OPS] = {
8, 9, 20, 18, 66, 458, 0, 0};
static const int CEX6A_SPEED_IDX[NUM_OPS] = {
6, 9, 20, 17, 65, 438, 0, 0};
static const int CEX7A_SPEED_IDX[NUM_OPS] = {
6, 8, 17, 15, 54, 362, 0, 0};
static const int CEX8A_SPEED_IDX[NUM_OPS] = {
6, 8, 17, 15, 54, 362, 0, 0};
static const int CEX4C_SPEED_IDX[NUM_OPS] = {
59, 69, 308, 83, 278, 2204, 209, 40};
static const int CEX5C_SPEED_IDX[] = {
24, 31, 50, 37, 90, 479, 27, 10};
static const int CEX6C_SPEED_IDX[NUM_OPS] = {
16, 20, 32, 27, 77, 455, 24, 9};
static const int CEX7C_SPEED_IDX[NUM_OPS] = {
14, 16, 26, 23, 64, 376, 23, 8};
static const int CEX8C_SPEED_IDX[NUM_OPS] = {
14, 16, 26, 23, 64, 376, 23, 8};
static const int CEX4P_SPEED_IDX[NUM_OPS] = {
0, 0, 0, 0, 0, 0, 0, 50};
static const int CEX5P_SPEED_IDX[NUM_OPS] = {
0, 0, 0, 0, 0, 0, 0, 10};
static const int CEX6P_SPEED_IDX[NUM_OPS] = {
0, 0, 0, 0, 0, 0, 0, 9};
static const int CEX7P_SPEED_IDX[NUM_OPS] = {
0, 0, 0, 0, 0, 0, 0, 8};
static const int CEX8P_SPEED_IDX[NUM_OPS] = {
0, 0, 0, 0, 0, 0, 0, 8};
struct ap_card *ac = to_ap_card(&ap_dev->device);
struct zcrypt_card *zc;
int rc = 0;
zc = zcrypt_card_alloc();
if (!zc)
return -ENOMEM;
zc->card = ac;
dev_set_drvdata(&ap_dev->device, zc);
if (ac->hwinfo.accel) {
if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
zc->type_string = "CEX4A";
zc->user_space_type = ZCRYPT_CEX4;
zc->speed_rating = CEX4A_SPEED_IDX;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX5) {
zc->type_string = "CEX5A";
zc->user_space_type = ZCRYPT_CEX5;
zc->speed_rating = CEX5A_SPEED_IDX;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX6) {
zc->type_string = "CEX6A";
zc->user_space_type = ZCRYPT_CEX6;
zc->speed_rating = CEX6A_SPEED_IDX;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX7) {
zc->type_string = "CEX7A";
zc->speed_rating = CEX7A_SPEED_IDX;
/* wrong user space type, just for compatibility
* with the ZCRYPT_STATUS_MASK ioctl.
*/
zc->user_space_type = ZCRYPT_CEX6;
} else {
zc->type_string = "CEX8A";
zc->speed_rating = CEX8A_SPEED_IDX;
/* wrong user space type, just for compatibility
* with the ZCRYPT_STATUS_MASK ioctl.
*/
zc->user_space_type = ZCRYPT_CEX6;
}
zc->min_mod_size = CEX4A_MIN_MOD_SIZE;
if (ac->hwinfo.mex4k && ac->hwinfo.crt4k) {
zc->max_mod_size = CEX4A_MAX_MOD_SIZE_4K;
zc->max_exp_bit_length =
CEX4A_MAX_MOD_SIZE_4K;
} else {
zc->max_mod_size = CEX4A_MAX_MOD_SIZE_2K;
zc->max_exp_bit_length =
CEX4A_MAX_MOD_SIZE_2K;
}
} else if (ac->hwinfo.cca) {
if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
zc->type_string = "CEX4C";
zc->speed_rating = CEX4C_SPEED_IDX;
/* wrong user space type, must be CEX3C
* just keep it for cca compatibility
*/
zc->user_space_type = ZCRYPT_CEX3C;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX5) {
zc->type_string = "CEX5C";
zc->speed_rating = CEX5C_SPEED_IDX;
/* wrong user space type, must be CEX3C
* just keep it for cca compatibility
*/
zc->user_space_type = ZCRYPT_CEX3C;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX6) {
zc->type_string = "CEX6C";
zc->speed_rating = CEX6C_SPEED_IDX;
/* wrong user space type, must be CEX3C
* just keep it for cca compatibility
*/
zc->user_space_type = ZCRYPT_CEX3C;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX7) {
zc->type_string = "CEX7C";
zc->speed_rating = CEX7C_SPEED_IDX;
/* wrong user space type, must be CEX3C
* just keep it for cca compatibility
*/
zc->user_space_type = ZCRYPT_CEX3C;
} else {
zc->type_string = "CEX8C";
zc->speed_rating = CEX8C_SPEED_IDX;
/* wrong user space type, must be CEX3C
* just keep it for cca compatibility
*/
zc->user_space_type = ZCRYPT_CEX3C;
}
zc->min_mod_size = CEX4C_MIN_MOD_SIZE;
zc->max_mod_size = CEX4C_MAX_MOD_SIZE;
zc->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
} else if (ac->hwinfo.ep11) {
if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
zc->type_string = "CEX4P";
zc->user_space_type = ZCRYPT_CEX4;
zc->speed_rating = CEX4P_SPEED_IDX;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX5) {
zc->type_string = "CEX5P";
zc->user_space_type = ZCRYPT_CEX5;
zc->speed_rating = CEX5P_SPEED_IDX;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX6) {
zc->type_string = "CEX6P";
zc->user_space_type = ZCRYPT_CEX6;
zc->speed_rating = CEX6P_SPEED_IDX;
} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX7) {
zc->type_string = "CEX7P";
zc->speed_rating = CEX7P_SPEED_IDX;
/* wrong user space type, just for compatibility
* with the ZCRYPT_STATUS_MASK ioctl.
*/
zc->user_space_type = ZCRYPT_CEX6;
} else {
zc->type_string = "CEX8P";
zc->speed_rating = CEX8P_SPEED_IDX;
/* wrong user space type, just for compatibility
* with the ZCRYPT_STATUS_MASK ioctl.
*/
zc->user_space_type = ZCRYPT_CEX6;
}
zc->min_mod_size = CEX4C_MIN_MOD_SIZE;
zc->max_mod_size = CEX4C_MAX_MOD_SIZE;
zc->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
} else {
zcrypt_card_free(zc);
return -ENODEV;
}
zc->online = 1;
rc = zcrypt_card_register(zc);
if (rc) {
zcrypt_card_free(zc);
return rc;
}
if (ac->hwinfo.cca) {
rc = sysfs_create_group(&ap_dev->device.kobj,
&cca_card_attr_grp);
if (rc) {
zcrypt_card_unregister(zc);
zcrypt_card_free(zc);
}
} else if (ac->hwinfo.ep11) {
rc = sysfs_create_group(&ap_dev->device.kobj,
&ep11_card_attr_grp);
if (rc) {
zcrypt_card_unregister(zc);
zcrypt_card_free(zc);
}
}
return rc;
}
/*
* This is called to remove the CEX[45678] card driver
* information if an AP card device is removed.
*/
static void zcrypt_cex4_card_remove(struct ap_device *ap_dev)
{
struct zcrypt_card *zc = dev_get_drvdata(&ap_dev->device);
struct ap_card *ac = to_ap_card(&ap_dev->device);
if (ac->hwinfo.cca)
sysfs_remove_group(&ap_dev->device.kobj, &cca_card_attr_grp);
else if (ac->hwinfo.ep11)
sysfs_remove_group(&ap_dev->device.kobj, &ep11_card_attr_grp);
zcrypt_card_unregister(zc);
}
static struct ap_driver zcrypt_cex4_card_driver = {
.probe = zcrypt_cex4_card_probe,
.remove = zcrypt_cex4_card_remove,
.ids = zcrypt_cex4_card_ids,
.flags = AP_DRIVER_FLAG_DEFAULT,
};
/*
* Probe function for CEX[45678] queue device. It always
* accepts the AP device since the bus_match already checked
* the hardware type.
* @ap_dev: pointer to the AP device.
*/
static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev)
{
struct ap_queue *aq = to_ap_queue(&ap_dev->device);
struct zcrypt_queue *zq;
int rc;
if (aq->card->hwinfo.accel) {
zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
if (!zq)
return -ENOMEM;
zq->ops = zcrypt_msgtype(MSGTYPE50_NAME,
MSGTYPE50_VARIANT_DEFAULT);
} else if (aq->card->hwinfo.cca) {
zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
if (!zq)
return -ENOMEM;
zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
MSGTYPE06_VARIANT_DEFAULT);
} else if (aq->card->hwinfo.ep11) {
zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
if (!zq)
return -ENOMEM;
zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
MSGTYPE06_VARIANT_EP11);
} else {
return -ENODEV;
}
zq->queue = aq;
zq->online = 1;
atomic_set(&zq->load, 0);
ap_queue_init_state(aq);
ap_queue_init_reply(aq, &zq->reply);
aq->request_timeout = CEX4_CLEANUP_TIME;
dev_set_drvdata(&ap_dev->device, zq);
rc = zcrypt_queue_register(zq);
if (rc) {
zcrypt_queue_free(zq);
return rc;
}
if (aq->card->hwinfo.cca) {
rc = sysfs_create_group(&ap_dev->device.kobj,
&cca_queue_attr_grp);
if (rc) {
zcrypt_queue_unregister(zq);
zcrypt_queue_free(zq);
}
} else if (aq->card->hwinfo.ep11) {
rc = sysfs_create_group(&ap_dev->device.kobj,
&ep11_queue_attr_grp);
if (rc) {
zcrypt_queue_unregister(zq);
zcrypt_queue_free(zq);
}
}
return rc;
}
/*
* This is called to remove the CEX[45678] queue driver
* information if an AP queue device is removed.
*/
static void zcrypt_cex4_queue_remove(struct ap_device *ap_dev)
{
struct zcrypt_queue *zq = dev_get_drvdata(&ap_dev->device);
struct ap_queue *aq = to_ap_queue(&ap_dev->device);
if (aq->card->hwinfo.cca)
sysfs_remove_group(&ap_dev->device.kobj, &cca_queue_attr_grp);
else if (aq->card->hwinfo.ep11)
sysfs_remove_group(&ap_dev->device.kobj, &ep11_queue_attr_grp);
zcrypt_queue_unregister(zq);
}
static struct ap_driver zcrypt_cex4_queue_driver = {
.probe = zcrypt_cex4_queue_probe,
.remove = zcrypt_cex4_queue_remove,
.ids = zcrypt_cex4_queue_ids,
.flags = AP_DRIVER_FLAG_DEFAULT,
};
int __init zcrypt_cex4_init(void)
{
int rc;
rc = ap_driver_register(&zcrypt_cex4_card_driver,
THIS_MODULE, "cex4card");
if (rc)
return rc;
rc = ap_driver_register(&zcrypt_cex4_queue_driver,
THIS_MODULE, "cex4queue");
if (rc)
ap_driver_unregister(&zcrypt_cex4_card_driver);
return rc;
}
void __exit zcrypt_cex4_exit(void)
{
ap_driver_unregister(&zcrypt_cex4_queue_driver);
ap_driver_unregister(&zcrypt_cex4_card_driver);
}
module_init(zcrypt_cex4_init);
module_exit(zcrypt_cex4_exit);
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright IBM Corp. 2012
* Author(s): Holger Dengler <hd@linux.vnet.ibm.com>
*/
#ifndef _ZCRYPT_CEX4_H_
#define _ZCRYPT_CEX4_H_
int zcrypt_cex4_init(void);
void zcrypt_cex4_exit(void);
#endif /* _ZCRYPT_CEX4_H_ */
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright IBM Corp. 2016
* Author(s): Holger Dengler (hd@linux.vnet.ibm.com)
* Harald Freudenberger <freude@de.ibm.com>
*/
#ifndef ZCRYPT_DEBUG_H
#define ZCRYPT_DEBUG_H
#include <asm/debug.h>
#define DBF_ERR 3 /* error conditions */
#define DBF_WARN 4 /* warning conditions */
#define DBF_INFO 5 /* informational */
#define DBF_DEBUG 6 /* for debugging only */
#define RC2ERR(rc) ((rc) ? DBF_ERR : DBF_INFO)
#define RC2WARN(rc) ((rc) ? DBF_WARN : DBF_INFO)
#define ZCRYPT_DBF_MAX_SPRINTF_ARGS 6
#define ZCRYPT_DBF(...) \
debug_sprintf_event(zcrypt_dbf_info, ##__VA_ARGS__)
#define ZCRYPT_DBF_ERR(...) \
debug_sprintf_event(zcrypt_dbf_info, DBF_ERR, ##__VA_ARGS__)
#define ZCRYPT_DBF_WARN(...) \
debug_sprintf_event(zcrypt_dbf_info, DBF_WARN, ##__VA_ARGS__)
#define ZCRYPT_DBF_INFO(...) \
debug_sprintf_event(zcrypt_dbf_info, DBF_INFO, ##__VA_ARGS__)
extern debug_info_t *zcrypt_dbf_info;
int zcrypt_debug_init(void);
void zcrypt_debug_exit(void);
#endif /* ZCRYPT_DEBUG_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,158 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2019
* Author(s): Harald Freudenberger <freude@linux.ibm.com>
*
* Collection of EP11 misc functions used by zcrypt and pkey
*/
#ifndef _ZCRYPT_EP11MISC_H_
#define _ZCRYPT_EP11MISC_H_
#include <asm/zcrypt.h>
#include <asm/pkey.h>
#define EP11_API_V1 1 /* min EP11 API, default if no higher api required */
#define EP11_API_V4 4 /* supported EP11 API for the ep11misc cprbs */
#define EP11_API_V6 6 /* min EP11 API for some cprbs in SE environment */
#define EP11_STRUCT_MAGIC 0x1234
#define EP11_BLOB_PKEY_EXTRACTABLE 0x00200000
/*
* Internal used values for the version field of the key header.
* Should match to the enum pkey_key_type in pkey.h.
*/
#define TOKVER_EP11_AES 0x03 /* EP11 AES key blob (old style) */
#define TOKVER_EP11_AES_WITH_HEADER 0x06 /* EP11 AES key blob with header */
#define TOKVER_EP11_ECC_WITH_HEADER 0x07 /* EP11 ECC key blob with header */
/* inside view of an EP11 secure key blob */
struct ep11keyblob {
union {
u8 session[32];
/* only used for PKEY_TYPE_EP11: */
struct ep11kblob_header head;
};
u8 wkvp[16]; /* wrapping key verification pattern */
u64 attr; /* boolean key attributes */
u64 mode; /* mode bits */
u16 version; /* 0x1234, EP11_STRUCT_MAGIC */
u8 iv[14];
u8 encrypted_key_data[144];
u8 mac[32];
} __packed;
/* check ep11 key magic to find out if this is an ep11 key blob */
static inline bool is_ep11_keyblob(const u8 *key)
{
struct ep11keyblob *kb = (struct ep11keyblob *)key;
return (kb->version == EP11_STRUCT_MAGIC);
}
/*
* For valid ep11 keyblobs, returns a reference to the wrappingkey verification
* pattern. Otherwise NULL.
*/
const u8 *ep11_kb_wkvp(const u8 *kblob, u32 kbloblen);
/*
* Simple check if the key blob is a valid EP11 AES key blob with header.
* If checkcpacfexport is enabled, the key is also checked for the
* attributes needed to export this key for CPACF use.
* Returns 0 on success or errno value on failure.
*/
int ep11_check_aes_key_with_hdr(debug_info_t *dbg, int dbflvl,
const u8 *key, u32 keylen, int checkcpacfexp);
/*
* Simple check if the key blob is a valid EP11 ECC key blob with header.
* If checkcpacfexport is enabled, the key is also checked for the
* attributes needed to export this key for CPACF use.
* Returns 0 on success or errno value on failure.
*/
int ep11_check_ecc_key_with_hdr(debug_info_t *dbg, int dbflvl,
const u8 *key, u32 keylen, int checkcpacfexp);
/*
* Simple check if the key blob is a valid EP11 AES key blob with
* the header in the session field (old style EP11 AES key).
* If checkcpacfexport is enabled, the key is also checked for the
* attributes needed to export this key for CPACF use.
* Returns 0 on success or errno value on failure.
*/
int ep11_check_aes_key(debug_info_t *dbg, int dbflvl,
const u8 *key, u32 keylen, int checkcpacfexp);
/* EP11 card info struct */
struct ep11_card_info {
u64 op_mode; /* card operational mode(s) */
char serial[16]; /* serial number string (16 ascii, no 0x00 !) */
u32 API_ord_nr; /* API ordinal number */
u16 FW_version; /* Firmware major and minor version */
};
/* EP11 domain info struct */
struct ep11_domain_info {
u64 op_mode; /* domain operational mode(s) */
u8 cur_wkvp[32]; /* current wrapping key verification pattern */
u8 new_wkvp[32]; /* new wrapping key verification pattern */
char cur_wk_state; /* '0' invalid, '1' valid */
char new_wk_state; /* '0' empty, '1' uncommitted, '2' committed */
};
/*
* Provide information about an EP11 card.
*/
int ep11_get_card_info(u16 card, struct ep11_card_info *info, u32 xflags);
/*
* Provide information about a domain within an EP11 card.
*/
int ep11_get_domain_info(u16 card, u16 domain,
struct ep11_domain_info *info, u32 xflags);
/*
* Generate (random) EP11 AES secure key.
*/
int ep11_genaeskey(u16 card, u16 domain, u32 keybitsize, u32 keygenflags,
u8 *keybuf, u32 *keybufsize, u32 keybufver, u32 xflags);
/*
* Generate EP11 AES secure key with given clear key value.
*/
int ep11_clr2keyblob(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
const u8 *clrkey, u8 *keybuf, u32 *keybufsize,
u32 keytype, u32 xflags);
/*
* Build a list of ep11 apqns meeting the following constrains:
* - apqn is online and is in fact an EP11 apqn
* - if cardnr is not FFFF only apqns with this cardnr
* - if domain is not FFFF only apqns with this domainnr
* - if minhwtype > 0 only apqns with hwtype >= minhwtype
* - if minapi > 0 only apqns with API_ord_nr >= minapi
* - if wkvp != NULL only apqns where the wkvp (EP11_WKVPLEN bytes) matches
* to the first EP11_WKVPLEN bytes of the wkvp of the current wrapping
* key for this domain. When a wkvp is given there will always be a re-fetch
* of the domain info for the potential apqn - so this triggers an request
* reply to each apqn eligible.
* The caller should set *nr_apqns to the nr of elements available in *apqns.
* On return *nr_apqns is then updated with the nr of apqns filled into *apqns.
* The return value is either 0 for success or a negative errno value.
* If no apqn meeting the criteria is found, -ENODEV is returned.
*/
int ep11_findcard2(u32 *apqns, u32 *nr_apqns, u16 cardnr, u16 domain,
int minhwtype, int minapi, const u8 *wkvp, u32 xflags);
/*
* Derive proteced key from EP11 key blob (AES and ECC keys).
*/
int ep11_kblob2protkey(u16 card, u16 dom, const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags);
int zcrypt_ep11misc_init(void);
void zcrypt_ep11misc_exit(void);
#endif /* _ZCRYPT_EP11MISC_H_ */
@@ -0,0 +1,143 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2001, 2006
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef _ZCRYPT_ERROR_H_
#define _ZCRYPT_ERROR_H_
#include <linux/atomic.h>
#include "zcrypt_debug.h"
#include "zcrypt_api.h"
#include "zcrypt_msgtype6.h"
/**
* Reply Messages
*
* Error reply messages are of two types:
* 82: Error (see below)
* 88: Error (see below)
* Both type 82 and type 88 have the same structure in the header.
*
* Request reply messages are of three known types:
* 80: Reply from a Type 50 Request (see CEX2A-RELATED STRUCTS)
* 84: Reply from a Type 4 Request (see PCICA-RELATED STRUCTS)
* 86: Reply from a Type 6 Request (see PCICC/PCIXCC/CEX2C-RELATED STRUCTS)
*
*/
struct error_hdr {
unsigned char reserved1; /* 0x00 */
unsigned char type; /* 0x82 or 0x88 */
unsigned char reserved2[2]; /* 0x0000 */
unsigned char reply_code; /* reply code */
unsigned char reserved3[3]; /* 0x000000 */
};
#define TYPE82_RSP_CODE 0x82
#define TYPE88_RSP_CODE 0x88
#define REP82_ERROR_MACHINE_FAILURE 0x10
#define REP82_ERROR_PREEMPT_FAILURE 0x12
#define REP82_ERROR_CHECKPT_FAILURE 0x14
#define REP82_ERROR_MESSAGE_TYPE 0x20
#define REP82_ERROR_INVALID_COMM_CD 0x21 /* Type 84 */
#define REP82_ERROR_INVALID_MSG_LEN 0x23
#define REP82_ERROR_RESERVD_FIELD 0x24 /* was 0x50 */
#define REP82_ERROR_FORMAT_FIELD 0x29
#define REP82_ERROR_INVALID_COMMAND 0x30
#define REP82_ERROR_MALFORMED_MSG 0x40
#define REP82_ERROR_INVALID_SPECIAL_CMD 0x41
#define REP82_ERROR_RESERVED_FIELDO 0x50 /* old value */
#define REP82_ERROR_WORD_ALIGNMENT 0x60
#define REP82_ERROR_MESSAGE_LENGTH 0x80
#define REP82_ERROR_OPERAND_INVALID 0x82
#define REP82_ERROR_OPERAND_SIZE 0x84
#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85
#define REP82_ERROR_RESERVED_FIELD 0x88
#define REP82_ERROR_INVALID_DOMAIN_PENDING 0x8A
#define REP82_ERROR_FILTERED_BY_HYPERVISOR 0x8B
#define REP82_ERROR_TRANSPORT_FAIL 0x90
#define REP82_ERROR_PACKET_TRUNCATED 0xA0
#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0
#define REP88_ERROR_MODULE_FAILURE 0x10
#define REP88_ERROR_MESSAGE_TYPE 0x20
#define REP88_ERROR_MESSAGE_MALFORMD 0x22
#define REP88_ERROR_MESSAGE_LENGTH 0x23
#define REP88_ERROR_RESERVED_FIELD 0x24
#define REP88_ERROR_KEY_TYPE 0x34
#define REP88_ERROR_INVALID_KEY 0x82 /* CEX2A */
#define REP88_ERROR_OPERAND 0x84 /* CEX2A */
#define REP88_ERROR_OPERAND_EVEN_MOD 0x85 /* CEX2A */
static inline int convert_error(struct zcrypt_queue *zq,
struct ap_message *reply)
{
int queue = AP_QID_QUEUE(zq->queue->qid);
int card = AP_QID_CARD(zq->queue->qid);
struct error_hdr *ehdr = reply->msg;
struct {
struct type86_hdr hdr;
struct type86_fmt2_ext fmt2;
} __packed * t86hdr = reply->msg;
switch (ehdr->reply_code) {
case REP82_ERROR_INVALID_MSG_LEN: /* 0x23 */
case REP82_ERROR_RESERVD_FIELD: /* 0x24 */
case REP82_ERROR_FORMAT_FIELD: /* 0x29 */
case REP82_ERROR_MALFORMED_MSG: /* 0x40 */
case REP82_ERROR_INVALID_SPECIAL_CMD: /* 0x41 */
case REP82_ERROR_MESSAGE_LENGTH: /* 0x80 */
case REP82_ERROR_OPERAND_INVALID: /* 0x82 */
case REP82_ERROR_OPERAND_SIZE: /* 0x84 */
case REP82_ERROR_EVEN_MOD_IN_OPND: /* 0x85 */
case REP82_ERROR_INVALID_DOMAIN_PENDING: /* 0x8A */
case REP82_ERROR_FILTERED_BY_HYPERVISOR: /* 0x8B */
case REP82_ERROR_PACKET_TRUNCATED: /* 0xA0 */
case REP88_ERROR_MESSAGE_MALFORMD: /* 0x22 */
case REP88_ERROR_KEY_TYPE: /* 0x34 */
/* RY indicates malformed request */
if (ehdr->reply_code == REP82_ERROR_FILTERED_BY_HYPERVISOR &&
ehdr->type == TYPE86_RSP_CODE) {
ZCRYPT_DBF_WARN("%s dev=%02x.%04x RY=0x%02x apfs=0x%x => rc=EINVAL\n",
__func__, card, queue,
ehdr->reply_code, t86hdr->fmt2.apfs);
} else {
ZCRYPT_DBF_WARN("%s dev=%02x.%04x RY=0x%02x => rc=EINVAL\n",
__func__, card, queue, ehdr->reply_code);
}
return -EINVAL;
case REP82_ERROR_MACHINE_FAILURE: /* 0x10 */
case REP82_ERROR_MESSAGE_TYPE: /* 0x20 */
case REP82_ERROR_TRANSPORT_FAIL: /* 0x90 */
/*
* Msg to wrong type or card/infrastructure failure. Return
* EAGAIN, the upper layer may do a retry on the request.
*/
/* For type 86 response show the apfs value (failure reason) */
if (ehdr->reply_code == REP82_ERROR_TRANSPORT_FAIL &&
ehdr->type == TYPE86_RSP_CODE) {
ZCRYPT_DBF_WARN(
"%s dev=%02x.%04x RY=0x%02x apfs=0x%x => bus rescan, rc=EAGAIN\n",
__func__, card, queue, ehdr->reply_code,
t86hdr->fmt2.apfs);
} else {
ZCRYPT_DBF_WARN("%s dev=%02x.%04x RY=0x%02x => bus rescan, rc=EAGAIN\n",
__func__, card, queue,
ehdr->reply_code);
}
return -EAGAIN;
default:
/* Assume request is valid and a retry will be worth it */
ZCRYPT_DBF_WARN("%s dev=%02x.%04x RY=0x%02x => rc=EAGAIN\n",
__func__, card, queue, ehdr->reply_code);
return -EAGAIN;
}
}
#endif /* _ZCRYPT_ERROR_H_ */
@@ -0,0 +1,562 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright IBM Corp. 2001, 2023
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
* MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*/
#define pr_fmt(fmt) "zcrypt: " fmt
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include "ap_bus.h"
#include "zcrypt_api.h"
#include "zcrypt_error.h"
#include "zcrypt_msgtype50.h"
/* >= CEX3A: 4096 bits */
#define CEX3A_MAX_MOD_SIZE 512
/* >= CEX3A: 512 bit modulus, (max outputdatalength) + type80_hdr */
#define CEX3A_MAX_RESPONSE_SIZE 0x210
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("Cryptographic Accelerator (message type 50), " \
"Copyright IBM Corp. 2001, 2023");
MODULE_LICENSE("GPL");
/*
* The type 50 message family is associated with a CEXxA cards.
*
* The four members of the family are described below.
*
* Note that all unsigned char arrays are right-justified and left-padded
* with zeroes.
*
* Note that all reserved fields must be zeroes.
*/
struct type50_hdr {
unsigned char reserved1;
unsigned char msg_type_code; /* 0x50 */
unsigned short msg_len;
unsigned char reserved2;
unsigned char ignored;
unsigned short reserved3;
} __packed;
#define TYPE50_TYPE_CODE 0x50
#define TYPE50_MEB1_FMT 0x0001
#define TYPE50_MEB2_FMT 0x0002
#define TYPE50_MEB3_FMT 0x0003
#define TYPE50_CRB1_FMT 0x0011
#define TYPE50_CRB2_FMT 0x0012
#define TYPE50_CRB3_FMT 0x0013
/* Mod-Exp, with a small modulus */
struct type50_meb1_msg {
struct type50_hdr header;
unsigned short keyblock_type; /* 0x0001 */
unsigned char reserved[6];
unsigned char exponent[128];
unsigned char modulus[128];
unsigned char message[128];
} __packed;
/* Mod-Exp, with a large modulus */
struct type50_meb2_msg {
struct type50_hdr header;
unsigned short keyblock_type; /* 0x0002 */
unsigned char reserved[6];
unsigned char exponent[256];
unsigned char modulus[256];
unsigned char message[256];
} __packed;
/* Mod-Exp, with a larger modulus */
struct type50_meb3_msg {
struct type50_hdr header;
unsigned short keyblock_type; /* 0x0003 */
unsigned char reserved[6];
unsigned char exponent[512];
unsigned char modulus[512];
unsigned char message[512];
} __packed;
/* CRT, with a small modulus */
struct type50_crb1_msg {
struct type50_hdr header;
unsigned short keyblock_type; /* 0x0011 */
unsigned char reserved[6];
unsigned char p[64];
unsigned char q[64];
unsigned char dp[64];
unsigned char dq[64];
unsigned char u[64];
unsigned char message[128];
} __packed;
/* CRT, with a large modulus */
struct type50_crb2_msg {
struct type50_hdr header;
unsigned short keyblock_type; /* 0x0012 */
unsigned char reserved[6];
unsigned char p[128];
unsigned char q[128];
unsigned char dp[128];
unsigned char dq[128];
unsigned char u[128];
unsigned char message[256];
} __packed;
/* CRT, with a larger modulus */
struct type50_crb3_msg {
struct type50_hdr header;
unsigned short keyblock_type; /* 0x0013 */
unsigned char reserved[6];
unsigned char p[256];
unsigned char q[256];
unsigned char dp[256];
unsigned char dq[256];
unsigned char u[256];
unsigned char message[512];
} __packed;
/*
* The type 80 response family is associated with a CEXxA cards.
*
* Note that all unsigned char arrays are right-justified and left-padded
* with zeroes.
*
* Note that all reserved fields must be zeroes.
*/
#define TYPE80_RSP_CODE 0x80
struct type80_hdr {
unsigned char reserved1;
unsigned char type; /* 0x80 */
unsigned short len;
unsigned char code; /* 0x00 */
unsigned char reserved2[3];
unsigned char reserved3[8];
} __packed;
int get_rsa_modex_fc(struct ica_rsa_modexpo *mex, int *fcode)
{
if (!mex->inputdatalength)
return -EINVAL;
if (mex->inputdatalength <= 128) /* 1024 bit */
*fcode = MEX_1K;
else if (mex->inputdatalength <= 256) /* 2048 bit */
*fcode = MEX_2K;
else /* 4096 bit */
*fcode = MEX_4K;
return 0;
}
int get_rsa_crt_fc(struct ica_rsa_modexpo_crt *crt, int *fcode)
{
if (!crt->inputdatalength)
return -EINVAL;
if (crt->inputdatalength <= 128) /* 1024 bit */
*fcode = CRT_1K;
else if (crt->inputdatalength <= 256) /* 2048 bit */
*fcode = CRT_2K;
else /* 4096 bit */
*fcode = CRT_4K;
return 0;
}
/*
* Convert a ICAMEX message to a type50 MEX message.
*
* @zq: crypto queue pointer
* @ap_msg: crypto request pointer
* @mex: pointer to user input data
*
* Returns 0 on success or -EFAULT.
*/
static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_queue *zq,
struct ap_message *ap_msg,
struct ica_rsa_modexpo *mex)
{
unsigned char *mod, *exp, *inp;
int mod_len;
mod_len = mex->inputdatalength;
if (mod_len <= 128) {
struct type50_meb1_msg *meb1 = ap_msg->msg;
memset(meb1, 0, sizeof(*meb1));
ap_msg->len = sizeof(*meb1);
meb1->header.msg_type_code = TYPE50_TYPE_CODE;
meb1->header.msg_len = sizeof(*meb1);
meb1->keyblock_type = TYPE50_MEB1_FMT;
mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
inp = meb1->message + sizeof(meb1->message) - mod_len;
} else if (mod_len <= 256) {
struct type50_meb2_msg *meb2 = ap_msg->msg;
memset(meb2, 0, sizeof(*meb2));
ap_msg->len = sizeof(*meb2);
meb2->header.msg_type_code = TYPE50_TYPE_CODE;
meb2->header.msg_len = sizeof(*meb2);
meb2->keyblock_type = TYPE50_MEB2_FMT;
mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
inp = meb2->message + sizeof(meb2->message) - mod_len;
} else if (mod_len <= 512) {
struct type50_meb3_msg *meb3 = ap_msg->msg;
memset(meb3, 0, sizeof(*meb3));
ap_msg->len = sizeof(*meb3);
meb3->header.msg_type_code = TYPE50_TYPE_CODE;
meb3->header.msg_len = sizeof(*meb3);
meb3->keyblock_type = TYPE50_MEB3_FMT;
mod = meb3->modulus + sizeof(meb3->modulus) - mod_len;
exp = meb3->exponent + sizeof(meb3->exponent) - mod_len;
inp = meb3->message + sizeof(meb3->message) - mod_len;
} else {
return -EINVAL;
}
if (copy_from_user(mod, mex->n_modulus, mod_len) ||
copy_from_user(exp, mex->b_key, mod_len) ||
copy_from_user(inp, mex->inputdata, mod_len))
return -EFAULT;
return 0;
}
/*
* Convert a ICACRT message to a type50 CRT message.
*
* @zq: crypto queue pointer
* @ap_msg: crypto request pointer
* @crt: pointer to user input data
*
* Returns 0 on success or -EFAULT.
*/
static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_queue *zq,
struct ap_message *ap_msg,
struct ica_rsa_modexpo_crt *crt)
{
int mod_len, short_len;
unsigned char *p, *q, *dp, *dq, *u, *inp;
mod_len = crt->inputdatalength;
short_len = (mod_len + 1) / 2;
/*
* CEX2A and CEX3A w/o FW update can handle requests up to
* 256 byte modulus (2k keys).
* CEX3A with FW update and newer CEXxA cards are able to handle
* 512 byte modulus (4k keys).
*/
if (mod_len <= 128) { /* up to 1024 bit key size */
struct type50_crb1_msg *crb1 = ap_msg->msg;
memset(crb1, 0, sizeof(*crb1));
ap_msg->len = sizeof(*crb1);
crb1->header.msg_type_code = TYPE50_TYPE_CODE;
crb1->header.msg_len = sizeof(*crb1);
crb1->keyblock_type = TYPE50_CRB1_FMT;
p = crb1->p + sizeof(crb1->p) - short_len;
q = crb1->q + sizeof(crb1->q) - short_len;
dp = crb1->dp + sizeof(crb1->dp) - short_len;
dq = crb1->dq + sizeof(crb1->dq) - short_len;
u = crb1->u + sizeof(crb1->u) - short_len;
inp = crb1->message + sizeof(crb1->message) - mod_len;
} else if (mod_len <= 256) { /* up to 2048 bit key size */
struct type50_crb2_msg *crb2 = ap_msg->msg;
memset(crb2, 0, sizeof(*crb2));
ap_msg->len = sizeof(*crb2);
crb2->header.msg_type_code = TYPE50_TYPE_CODE;
crb2->header.msg_len = sizeof(*crb2);
crb2->keyblock_type = TYPE50_CRB2_FMT;
p = crb2->p + sizeof(crb2->p) - short_len;
q = crb2->q + sizeof(crb2->q) - short_len;
dp = crb2->dp + sizeof(crb2->dp) - short_len;
dq = crb2->dq + sizeof(crb2->dq) - short_len;
u = crb2->u + sizeof(crb2->u) - short_len;
inp = crb2->message + sizeof(crb2->message) - mod_len;
} else if ((mod_len <= 512) && /* up to 4096 bit key size */
(zq->zcard->max_mod_size == CEX3A_MAX_MOD_SIZE)) {
struct type50_crb3_msg *crb3 = ap_msg->msg;
memset(crb3, 0, sizeof(*crb3));
ap_msg->len = sizeof(*crb3);
crb3->header.msg_type_code = TYPE50_TYPE_CODE;
crb3->header.msg_len = sizeof(*crb3);
crb3->keyblock_type = TYPE50_CRB3_FMT;
p = crb3->p + sizeof(crb3->p) - short_len;
q = crb3->q + sizeof(crb3->q) - short_len;
dp = crb3->dp + sizeof(crb3->dp) - short_len;
dq = crb3->dq + sizeof(crb3->dq) - short_len;
u = crb3->u + sizeof(crb3->u) - short_len;
inp = crb3->message + sizeof(crb3->message) - mod_len;
} else {
return -EINVAL;
}
/*
* correct the offset of p, bp and mult_inv according zcrypt.h
* block size right aligned (skip the first byte)
*/
if (copy_from_user(p, crt->np_prime + MSGTYPE_ADJUSTMENT, short_len) ||
copy_from_user(q, crt->nq_prime, short_len) ||
copy_from_user(dp, crt->bp_key + MSGTYPE_ADJUSTMENT, short_len) ||
copy_from_user(dq, crt->bq_key, short_len) ||
copy_from_user(u, crt->u_mult_inv + MSGTYPE_ADJUSTMENT, short_len) ||
copy_from_user(inp, crt->inputdata, mod_len))
return -EFAULT;
return 0;
}
/*
* Copy results from a type 80 reply message back to user space.
*
* @zq: crypto device pointer
* @reply: reply AP message.
* @data: pointer to user output data
* @length: size of user output data
*
* Returns 0 on success or -EFAULT.
*/
static int convert_type80(struct zcrypt_queue *zq,
struct ap_message *reply,
char __user *outputdata,
unsigned int outputdatalength)
{
struct type80_hdr *t80h = reply->msg;
unsigned char *data;
if (t80h->len < sizeof(*t80h) + outputdatalength) {
/* The result is too short, the CEXxA card may not do that.. */
zq->online = 0;
pr_err("Crypto dev=%02x.%04x code=0x%02x => online=0 rc=EAGAIN\n",
AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), t80h->code);
ZCRYPT_DBF_ERR("%s dev=%02x.%04x code=0x%02x => online=0 rc=EAGAIN\n",
__func__, AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), t80h->code);
ap_send_online_uevent(&zq->queue->ap_dev, zq->online);
return -EAGAIN;
}
BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
data = reply->msg + t80h->len - outputdatalength;
if (copy_to_user(outputdata, data, outputdatalength))
return -EFAULT;
return 0;
}
static int convert_response(struct zcrypt_queue *zq,
struct ap_message *reply,
char __user *outputdata,
unsigned int outputdatalength)
{
/* Response type byte is the second byte in the response. */
unsigned char rtype = ((unsigned char *)reply->msg)[1];
switch (rtype) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
return convert_error(zq, reply);
case TYPE80_RSP_CODE:
return convert_type80(zq, reply,
outputdata, outputdatalength);
default: /* Unknown response type, this should NEVER EVER happen */
zq->online = 0;
pr_err("Crypto dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n",
AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid),
(int)rtype);
ZCRYPT_DBF_ERR(
"%s dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n",
__func__, AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), (int)rtype);
ap_send_online_uevent(&zq->queue->ap_dev, zq->online);
return -EAGAIN;
}
}
/*
* This function is called from the AP bus code after a crypto request
* "msg" has finished with the reply message "reply".
* It is called from tasklet context.
* @aq: pointer to the AP device
* @msg: pointer to the AP message
* @reply: pointer to the AP reply message
*/
static void zcrypt_msgtype50_receive(struct ap_queue *aq,
struct ap_message *msg,
struct ap_message *reply)
{
static struct error_hdr error_reply = {
.type = TYPE82_RSP_CODE,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
struct type80_hdr *t80h;
int len;
/* Copy the reply message to the request message buffer. */
if (!reply)
goto out; /* ap_msg->rc indicates the error */
t80h = reply->msg;
if (t80h->type == TYPE80_RSP_CODE) {
len = t80h->len;
if (len > reply->bufsize || len > msg->bufsize ||
len != reply->len) {
pr_debug("len mismatch => EMSGSIZE\n");
msg->rc = -EMSGSIZE;
goto out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
} else {
memcpy(msg->msg, reply->msg, sizeof(error_reply));
msg->len = sizeof(error_reply);
}
out:
complete(&msg->response.work);
}
static atomic_t zcrypt_step = ATOMIC_INIT(0);
/*
* The request distributor calls this function if it picked the CEXxA
* device to handle a modexpo request.
* @zq: pointer to zcrypt_queue structure that identifies the
* CEXxA device to the request distributor
* @mex: pointer to the modexpo request buffer
* This function assumes that ap_msg has been initialized with
* ap_init_apmsg() and thus a valid buffer with the size of
* ap_msg->bufsize is available within ap_msg. Also the caller has
* to make sure ap_release_apmsg() is always called even on failure.
*/
static long zcrypt_msgtype50_modexpo(struct zcrypt_queue *zq,
struct ica_rsa_modexpo *mex,
struct ap_message *ap_msg)
{
int rc;
if (ap_msg->bufsize < MSGTYPE50_CRB3_MAX_MSG_SIZE)
return -EMSGSIZE;
ap_msg->receive = zcrypt_msgtype50_receive;
ap_msg->psmid = (((unsigned long)current->pid) << 32) +
atomic_inc_return(&zcrypt_step);
rc = ICAMEX_msg_to_type50MEX_msg(zq, ap_msg, mex);
if (rc)
goto out;
init_completion(&ap_msg->response.work);
rc = ap_queue_message(zq->queue, ap_msg);
if (rc)
goto out;
rc = wait_for_completion_interruptible(&ap_msg->response.work);
if (rc == 0) {
rc = ap_msg->rc;
if (rc == 0)
rc = convert_response(zq, ap_msg,
mex->outputdata,
mex->outputdatalength);
} else {
/* Signal pending. */
ap_cancel_message(zq->queue, ap_msg);
}
out:
if (rc)
pr_debug("send me cprb at dev=%02x.%04x rc=%d\n",
AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), rc);
return rc;
}
/*
* The request distributor calls this function if it picked the CEXxA
* device to handle a modexpo_crt request.
* @zq: pointer to zcrypt_queue structure that identifies the
* CEXxA device to the request distributor
* @crt: pointer to the modexpoc_crt request buffer
* This function assumes that ap_msg has been initialized with
* ap_init_apmsg() and thus a valid buffer with the size of
* ap_msg->bufsize is available within ap_msg. Also the caller has
* to make sure ap_release_apmsg() is always called even on failure.
*/
static long zcrypt_msgtype50_modexpo_crt(struct zcrypt_queue *zq,
struct ica_rsa_modexpo_crt *crt,
struct ap_message *ap_msg)
{
int rc;
if (ap_msg->bufsize < MSGTYPE50_CRB3_MAX_MSG_SIZE)
return -EMSGSIZE;
ap_msg->receive = zcrypt_msgtype50_receive;
ap_msg->psmid = (((unsigned long)current->pid) << 32) +
atomic_inc_return(&zcrypt_step);
rc = ICACRT_msg_to_type50CRT_msg(zq, ap_msg, crt);
if (rc)
goto out;
init_completion(&ap_msg->response.work);
rc = ap_queue_message(zq->queue, ap_msg);
if (rc)
goto out;
rc = wait_for_completion_interruptible(&ap_msg->response.work);
if (rc == 0) {
rc = ap_msg->rc;
if (rc == 0)
rc = convert_response(zq, ap_msg,
crt->outputdata,
crt->outputdatalength);
} else {
/* Signal pending. */
ap_cancel_message(zq->queue, ap_msg);
}
out:
if (rc)
pr_debug("send crt cprb at dev=%02x.%04x rc=%d\n",
AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), rc);
return rc;
}
/*
* The crypto operations for message type 50.
*/
static struct zcrypt_ops zcrypt_msgtype50_ops = {
.rsa_modexpo = zcrypt_msgtype50_modexpo,
.rsa_modexpo_crt = zcrypt_msgtype50_modexpo_crt,
.owner = THIS_MODULE,
.name = MSGTYPE50_NAME,
.variant = MSGTYPE50_VARIANT_DEFAULT,
};
void __init zcrypt_msgtype50_init(void)
{
zcrypt_msgtype_register(&zcrypt_msgtype50_ops);
}
void __exit zcrypt_msgtype50_exit(void)
{
zcrypt_msgtype_unregister(&zcrypt_msgtype50_ops);
}
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2001, 2023
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*/
#ifndef _ZCRYPT_MSGTYPE50_H_
#define _ZCRYPT_MSGTYPE50_H_
#define MSGTYPE50_NAME "zcrypt_msgtype50"
#define MSGTYPE50_VARIANT_DEFAULT 0
#define MSGTYPE50_CRB3_MAX_MSG_SIZE 0x710 /* sizeof(struct type50_crb3_msg) */
#define MSGTYPE_ADJUSTMENT 0x08 /* type04 extension (not needed in type50) */
int get_rsa_modex_fc(struct ica_rsa_modexpo *mex, int *fc);
int get_rsa_crt_fc(struct ica_rsa_modexpo_crt *crt, int *fc);
void zcrypt_msgtype50_init(void);
void zcrypt_msgtype50_exit(void);
#endif /* _ZCRYPT_MSGTYPE50_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,116 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*/
#ifndef _ZCRYPT_MSGTYPE6_H_
#define _ZCRYPT_MSGTYPE6_H_
#include <asm/zcrypt.h>
#define MSGTYPE06_NAME "zcrypt_msgtype6"
#define MSGTYPE06_VARIANT_DEFAULT 0
#define MSGTYPE06_VARIANT_NORNG 1
#define MSGTYPE06_VARIANT_EP11 2
/**
* The type 6 message family is associated with CEXxC/CEXxP cards.
*
* It contains a message header followed by a CPRB, both of which
* are described below.
*
* Note that all reserved fields must be zeroes.
*/
struct type6_hdr {
unsigned char reserved1; /* 0x00 */
unsigned char type; /* 0x06 */
unsigned char reserved2[2]; /* 0x0000 */
unsigned char right[4]; /* 0x00000000 */
unsigned char reserved3[2]; /* 0x0000 */
unsigned char reserved4[2]; /* 0x0000 */
unsigned int apfs; /* 0x00000000 */
unsigned int offset1; /* 0x00000058 (offset to CPRB) */
unsigned int offset2; /* 0x00000000 */
unsigned int offset3; /* 0x00000000 */
unsigned int offset4; /* 0x00000000 */
unsigned char agent_id[16]; /* 0x4341000000000000 */
/* 0x0000000000000000 */
unsigned char rqid[2]; /* rqid. internal to 603 */
unsigned char reserved5[2]; /* 0x0000 */
unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */
unsigned char reserved6[2]; /* 0x0000 */
unsigned int tocardlen1; /* (request CPRB len + 3) & -4 */
unsigned int tocardlen2; /* db len 0x00000000 for PKD */
unsigned int tocardlen3; /* 0x00000000 */
unsigned int tocardlen4; /* 0x00000000 */
unsigned int fromcardlen1; /* response buffer length */
unsigned int fromcardlen2; /* db len 0x00000000 for PKD */
unsigned int fromcardlen3; /* 0x00000000 */
unsigned int fromcardlen4; /* 0x00000000 */
} __packed;
/**
* The type 86 message family is associated with CEXxC/CEXxP cards.
*
* It contains a message header followed by a CPRB. The CPRB is
* the same as the request CPRB, which is described above.
*
* If format is 1, an error condition exists and no data beyond
* the 8-byte message header is of interest.
*
* The non-error message is shown below.
*
* Note that all reserved fields must be zeroes.
*/
struct type86_hdr {
unsigned char reserved1; /* 0x00 */
unsigned char type; /* 0x86 */
unsigned char format; /* 0x01 (error) or 0x02 (ok) */
unsigned char reserved2; /* 0x00 */
unsigned char reply_code; /* reply code (see above) */
unsigned char reserved3[3]; /* 0x000000 */
} __packed;
#define TYPE86_RSP_CODE 0x86
#define TYPE87_RSP_CODE 0x87
#define TYPE86_FMT2 0x02
struct type86_fmt2_ext {
unsigned char reserved[4]; /* 0x00000000 */
unsigned int apfs; /* final status */
unsigned int count1; /* length of CPRB + parameters */
unsigned int offset1; /* offset to CPRB */
unsigned int count2; /* 0x00000000 */
unsigned int offset2; /* db offset 0x00000000 for PKD */
unsigned int count3; /* 0x00000000 */
unsigned int offset3; /* 0x00000000 */
unsigned int count4; /* 0x00000000 */
unsigned int offset4; /* 0x00000000 */
} __packed;
int prep_cca_ap_msg(bool userspace, struct ica_xcRB *xcrb,
struct ap_message *ap_msg,
unsigned int *fc, unsigned int *dom);
int prep_ep11_ap_msg(bool userspace, struct ep11_urb *xcrb,
struct ap_message *ap_msg,
unsigned int *fc, unsigned int *dom);
int prep_rng_ap_msg(struct ap_message *ap_msg,
int *fc, unsigned int *dom);
#define LOW 10
#define MEDIUM 100
#define HIGH 500
int speed_idx_cca(int);
int speed_idx_ep11(int);
void zcrypt_msgtype6_init(void);
void zcrypt_msgtype6_exit(void);
#endif /* _ZCRYPT_MSGTYPE6_H_ */
@@ -0,0 +1,234 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
* Cornelia Huck <cornelia.huck@de.ibm.com>
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
* MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/hw_random.h>
#include <linux/debugfs.h>
#include <asm/debug.h>
#include "zcrypt_debug.h"
#include "zcrypt_api.h"
#include "zcrypt_msgtype6.h"
#include "zcrypt_msgtype50.h"
/*
* Device attributes common for all crypto queue devices.
*/
static ssize_t online_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct zcrypt_queue *zq = dev_get_drvdata(dev);
struct ap_queue *aq = to_ap_queue(dev);
int online = aq->config && !aq->chkstop && zq->online ? 1 : 0;
return sysfs_emit(buf, "%d\n", online);
}
static ssize_t online_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct zcrypt_queue *zq = dev_get_drvdata(dev);
struct ap_queue *aq = to_ap_queue(dev);
struct zcrypt_card *zc = zq->zcard;
int online;
if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
return -EINVAL;
if (online && (!aq->config || !aq->card->config ||
aq->chkstop || aq->card->chkstop))
return -ENODEV;
if (online && !zc->online)
return -EINVAL;
zq->online = online;
ZCRYPT_DBF_INFO("%s queue=%02x.%04x online=%d\n",
__func__, AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), online);
ap_send_online_uevent(&aq->ap_dev, online);
if (!online)
ap_flush_queue(zq->queue);
return count;
}
static DEVICE_ATTR_RW(online);
static ssize_t load_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct zcrypt_queue *zq = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", atomic_read(&zq->load));
}
static DEVICE_ATTR_RO(load);
static struct attribute *zcrypt_queue_attrs[] = {
&dev_attr_online.attr,
&dev_attr_load.attr,
NULL,
};
static const struct attribute_group zcrypt_queue_attr_group = {
.attrs = zcrypt_queue_attrs,
};
bool zcrypt_queue_force_online(struct zcrypt_queue *zq, int online)
{
if (!!zq->online != !!online) {
zq->online = online;
if (!online)
ap_flush_queue(zq->queue);
return true;
}
return false;
}
struct zcrypt_queue *zcrypt_queue_alloc(size_t reply_buf_size)
{
struct zcrypt_queue *zq;
zq = kzalloc_obj(*zq);
if (!zq)
return NULL;
zq->reply.msg = kmalloc(reply_buf_size, GFP_KERNEL);
if (!zq->reply.msg)
goto out_free;
zq->reply.bufsize = reply_buf_size;
INIT_LIST_HEAD(&zq->list);
kref_init(&zq->refcount);
return zq;
out_free:
kfree(zq);
return NULL;
}
EXPORT_SYMBOL(zcrypt_queue_alloc);
void zcrypt_queue_free(struct zcrypt_queue *zq)
{
kfree(zq->reply.msg);
kfree(zq);
}
EXPORT_SYMBOL(zcrypt_queue_free);
static void zcrypt_queue_release(struct kref *kref)
{
struct zcrypt_queue *zq =
container_of(kref, struct zcrypt_queue, refcount);
zcrypt_queue_free(zq);
}
void zcrypt_queue_get(struct zcrypt_queue *zq)
{
kref_get(&zq->refcount);
}
EXPORT_SYMBOL(zcrypt_queue_get);
int zcrypt_queue_put(struct zcrypt_queue *zq)
{
return kref_put(&zq->refcount, zcrypt_queue_release);
}
EXPORT_SYMBOL(zcrypt_queue_put);
/**
* zcrypt_queue_register() - Register a crypto queue device.
* @zq: Pointer to a crypto queue device
*
* Register a crypto queue device. Returns 0 if successful.
*/
int zcrypt_queue_register(struct zcrypt_queue *zq)
{
struct zcrypt_card *zc;
int rc;
spin_lock(&zcrypt_list_lock);
zc = dev_get_drvdata(&zq->queue->card->ap_dev.device);
zcrypt_card_get(zc);
zq->zcard = zc;
zq->online = 1; /* New devices are online by default. */
ZCRYPT_DBF_INFO("%s queue=%02x.%04x register online=1\n",
__func__, AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid));
list_add_tail(&zq->list, &zc->zqueues);
spin_unlock(&zcrypt_list_lock);
rc = sysfs_create_group(&zq->queue->ap_dev.device.kobj,
&zcrypt_queue_attr_group);
if (rc)
goto out;
if (zq->ops->rng) {
rc = zcrypt_rng_device_add();
if (rc)
goto out_unregister;
}
return 0;
out_unregister:
sysfs_remove_group(&zq->queue->ap_dev.device.kobj,
&zcrypt_queue_attr_group);
out:
spin_lock(&zcrypt_list_lock);
list_del_init(&zq->list);
spin_unlock(&zcrypt_list_lock);
zcrypt_card_put(zc);
return rc;
}
EXPORT_SYMBOL(zcrypt_queue_register);
/**
* zcrypt_queue_unregister(): Unregister a crypto queue device.
* @zq: Pointer to crypto queue device
*
* Unregister a crypto queue device.
*/
void zcrypt_queue_unregister(struct zcrypt_queue *zq)
{
struct zcrypt_card *zc;
ZCRYPT_DBF_INFO("%s queue=%02x.%04x unregister\n",
__func__, AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid));
zc = zq->zcard;
spin_lock(&zcrypt_list_lock);
list_del_init(&zq->list);
spin_unlock(&zcrypt_list_lock);
if (zq->ops->rng)
zcrypt_rng_device_remove();
sysfs_remove_group(&zq->queue->ap_dev.device.kobj,
&zcrypt_queue_attr_group);
zcrypt_card_put(zc);
zcrypt_queue_put(zq);
}
EXPORT_SYMBOL(zcrypt_queue_unregister);