diff options
Diffstat (limited to 'drivers/s390/crypto/ap_bus.c')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 288 |
1 files changed, 284 insertions, 4 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index bf720ae74d61..30d7898910ea 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -34,6 +34,7 @@ #include <linux/crypto.h> #include <linux/mod_devicetable.h> #include <linux/debugfs.h> +#include <linux/ctype.h> #include "ap_bus.h" #include "ap_debug.h" @@ -51,11 +52,26 @@ static int ap_thread_flag; module_param_named(poll_thread, ap_thread_flag, int, 0440); MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off)."); +static char *apm_str; +module_param_named(apmask, apm_str, charp, 0440); +MODULE_PARM_DESC(apmask, "AP bus adapter mask."); + +static char *aqm_str; +module_param_named(aqmask, aqm_str, charp, 0440); +MODULE_PARM_DESC(aqmask, "AP bus domain mask."); + static struct device *ap_root_device; DEFINE_SPINLOCK(ap_list_lock); LIST_HEAD(ap_card_list); +/* Default permissions (card and domain masking) */ +static struct ap_perms { + DECLARE_BITMAP(apm, AP_DEVICES); + DECLARE_BITMAP(aqm, AP_DOMAINS); +} ap_perms; +static DEFINE_MUTEX(ap_perms_mutex); + static struct ap_config_info *ap_configuration; static bool initialised; @@ -670,11 +686,97 @@ static struct bus_type ap_bus_type = { .pm = &ap_bus_pm_ops, }; +static int __ap_revise_reserved(struct device *dev, void *dummy) +{ + int rc, card, queue, devres, drvres; + + if (is_queue_dev(dev)) { + card = AP_QID_CARD(to_ap_queue(dev)->qid); + queue = AP_QID_QUEUE(to_ap_queue(dev)->qid); + mutex_lock(&ap_perms_mutex); + devres = test_bit_inv(card, ap_perms.apm) + && test_bit_inv(queue, ap_perms.aqm); + mutex_unlock(&ap_perms_mutex); + drvres = to_ap_drv(dev->driver)->flags + & AP_DRIVER_FLAG_DEFAULT; + if (!!devres != !!drvres) { + AP_DBF(DBF_DEBUG, "reprobing queue=%02x.%04x\n", + card, queue); + rc = device_reprobe(dev); + } + } + + return 0; +} + +static void ap_bus_revise_bindings(void) +{ + bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_revise_reserved); +} + +int ap_owned_by_def_drv(int card, int queue) +{ + int rc = 0; + + if (card < 0 || card >= AP_DEVICES || queue < 0 || queue >= AP_DOMAINS) + return -EINVAL; + + mutex_lock(&ap_perms_mutex); + + if (test_bit_inv(card, ap_perms.apm) + && test_bit_inv(queue, ap_perms.aqm)) + rc = 1; + + mutex_unlock(&ap_perms_mutex); + + return rc; +} +EXPORT_SYMBOL(ap_owned_by_def_drv); + +int ap_apqn_in_matrix_owned_by_def_drv(unsigned long *apm, + unsigned long *aqm) +{ + int card, queue, rc = 0; + + mutex_lock(&ap_perms_mutex); + + for (card = 0; !rc && card < AP_DEVICES; card++) + if (test_bit_inv(card, apm) && + test_bit_inv(card, ap_perms.apm)) + for (queue = 0; !rc && queue < AP_DOMAINS; queue++) + if (test_bit_inv(queue, aqm) && + test_bit_inv(queue, ap_perms.aqm)) + rc = 1; + + mutex_unlock(&ap_perms_mutex); + + return rc; +} +EXPORT_SYMBOL(ap_apqn_in_matrix_owned_by_def_drv); + static int ap_device_probe(struct device *dev) { struct ap_device *ap_dev = to_ap_dev(dev); struct ap_driver *ap_drv = to_ap_drv(dev->driver); - int rc; + int card, queue, devres, drvres, rc; + + if (is_queue_dev(dev)) { + /* + * If the apqn is marked as reserved/used by ap bus and + * default drivers, only probe with drivers with the default + * flag set. If it is not marked, only probe with drivers + * with the default flag not set. + */ + card = AP_QID_CARD(to_ap_queue(dev)->qid); + queue = AP_QID_QUEUE(to_ap_queue(dev)->qid); + mutex_lock(&ap_perms_mutex); + devres = test_bit_inv(card, ap_perms.apm) + && test_bit_inv(queue, ap_perms.aqm); + mutex_unlock(&ap_perms_mutex); + drvres = ap_drv->flags & AP_DRIVER_FLAG_DEFAULT; + if (!!devres != !!drvres) + return -ENODEV; + } /* Add queue/card to list of active queues/cards */ spin_lock_bh(&ap_list_lock); @@ -757,6 +859,36 @@ EXPORT_SYMBOL(ap_bus_force_rescan); /* * AP bus attributes. */ + +static int hex2bitmap(const char *str, unsigned long *bitmap, int bits) +{ + int i, n, b; + + /* bits needs to be a multiple of 8 */ + if (bits & 0x07) + return -EINVAL; + + memset(bitmap, 0, bits / 8); + + if (str[0] == '0' && str[1] == 'x') + str++; + if (*str == 'x') + str++; + + for (i = 0; isxdigit(*str) && i < bits; str++) { + b = hex_to_bin(*str); + for (n = 0; n < 4; n++) + if (b & (0x08 >> n)) + set_bit_inv(i + n, bitmap); + i += 4; + } + + if (i < 4 || isxdigit(*str)) + return -EINVAL; + + return 0; +} + static ssize_t ap_domain_show(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index); @@ -768,7 +900,8 @@ static ssize_t ap_domain_store(struct bus_type *bus, int domain; if (sscanf(buf, "%i\n", &domain) != 1 || - domain < 0 || domain > ap_max_domain_id) + domain < 0 || domain > ap_max_domain_id || + !test_bit_inv(domain, ap_perms.aqm)) return -EINVAL; spin_lock_bh(&ap_domain_lock); ap_domain_index = domain; @@ -903,6 +1036,114 @@ static ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf) static BUS_ATTR_RO(ap_max_domain_id); +static ssize_t apmask_show(struct bus_type *bus, char *buf) +{ + int rc; + + if (mutex_lock_interruptible(&ap_perms_mutex)) + return -ERESTARTSYS; + rc = snprintf(buf, PAGE_SIZE, + "0x%016lx%016lx%016lx%016lx\n", + ap_perms.apm[0], ap_perms.apm[1], + ap_perms.apm[2], ap_perms.apm[3]); + mutex_unlock(&ap_perms_mutex); + + return rc; +} + +static ssize_t apmask_store(struct bus_type *bus, const char *buf, + size_t count) +{ + int i; + + if (*buf == '+' || *buf == '-') { + if (kstrtoint(buf, 0, &i)) + return -EINVAL; + if (i <= -AP_DEVICES || i >= AP_DEVICES) + return -EINVAL; + if (mutex_lock_interruptible(&ap_perms_mutex)) + return -ERESTARTSYS; + if (*buf == '-') + clear_bit_inv(-i, ap_perms.apm); + else + set_bit_inv(i, ap_perms.apm); + } else { + DECLARE_BITMAP(apm, AP_DEVICES); + + i = hex2bitmap(buf, apm, AP_DEVICES); + if (i) + return i; + if (mutex_lock_interruptible(&ap_perms_mutex)) + return -ERESTARTSYS; + for (i = 0; i < AP_DEVICES; i++) + if (test_bit_inv(i, apm)) + set_bit_inv(i, ap_perms.apm); + else + clear_bit_inv(i, ap_perms.apm); + } + mutex_unlock(&ap_perms_mutex); + + ap_bus_revise_bindings(); + + return count; +} + +static BUS_ATTR_RW(apmask); + +static ssize_t aqmask_show(struct bus_type *bus, char *buf) +{ + int rc; + + if (mutex_lock_interruptible(&ap_perms_mutex)) + return -ERESTARTSYS; + rc = snprintf(buf, PAGE_SIZE, + "0x%016lx%016lx%016lx%016lx\n", + ap_perms.aqm[0], ap_perms.aqm[1], + ap_perms.aqm[2], ap_perms.aqm[3]); + mutex_unlock(&ap_perms_mutex); + + return rc; +} + +static ssize_t aqmask_store(struct bus_type *bus, const char *buf, + size_t count) +{ + int i; + + if (*buf == '+' || *buf == '-') { + if (kstrtoint(buf, 0, &i)) + return -EINVAL; + if (i <= -AP_DEVICES || i >= AP_DEVICES) + return -EINVAL; + if (mutex_lock_interruptible(&ap_perms_mutex)) + return -ERESTARTSYS; + if (*buf == '-') + clear_bit_inv(-i, ap_perms.aqm); + else + set_bit_inv(i, ap_perms.aqm); + } else { + DECLARE_BITMAP(aqm, AP_DEVICES); + + i = hex2bitmap(buf, aqm, AP_DEVICES); + if (i) + return i; + if (mutex_lock_interruptible(&ap_perms_mutex)) + return -ERESTARTSYS; + for (i = 0; i < AP_DEVICES; i++) + if (test_bit_inv(i, aqm)) + set_bit_inv(i, ap_perms.aqm); + else + clear_bit_inv(i, ap_perms.aqm); + } + mutex_unlock(&ap_perms_mutex); + + ap_bus_revise_bindings(); + + return count; +} + +static BUS_ATTR_RW(aqmask); + static struct bus_attribute *const ap_bus_attrs[] = { &bus_attr_ap_domain, &bus_attr_ap_control_domain_mask, @@ -912,6 +1153,8 @@ static struct bus_attribute *const ap_bus_attrs[] = { &bus_attr_ap_interrupts, &bus_attr_poll_timeout, &bus_attr_ap_max_domain_id, + &bus_attr_apmask, + &bus_attr_aqmask, NULL, }; @@ -940,7 +1183,8 @@ static int ap_select_domain(void) best_domain = -1; max_count = 0; for (i = 0; i < AP_DOMAINS; i++) { - if (!ap_test_config_domain(i)) + if (!ap_test_config_domain(i) || + !test_bit_inv(i, ap_perms.aqm)) continue; count = 0; for (j = 0; j < AP_DEVICES; j++) { @@ -1190,6 +1434,37 @@ static int __init ap_debug_init(void) return 0; } +static void __init ap_perms_init(void) +{ + int i, rc; + + /* start with all resources useable */ + memset(&ap_perms.apm, 0xFF, sizeof(ap_perms.apm)); + memset(&ap_perms.aqm, 0xFF, sizeof(ap_perms.aqm)); + + /* process kernel parameters apm and aqm if given */ + if (apm_str) { + DECLARE_BITMAP(apm, AP_DEVICES); + + rc = hex2bitmap(apm_str, apm, AP_DEVICES); + if (rc == 0) { + for (i = 0; i < AP_DEVICES; i++) + if (!test_bit_inv(i, apm)) + clear_bit_inv(i, ap_perms.apm); + } + } + if (aqm_str) { + DECLARE_BITMAP(aqm, AP_DOMAINS); + + rc = hex2bitmap(aqm_str, aqm, AP_DOMAINS); + if (rc == 0) { + for (i = 0; i < AP_DOMAINS; i++) + if (!test_bit_inv(i, aqm)) + clear_bit_inv(i, ap_perms.aqm); + } + } +} + /** * ap_module_init(): The module initialization code. * @@ -1209,6 +1484,9 @@ static int __init ap_module_init(void) return -ENODEV; } + /* set up the AP permissions (ap and aq masks) */ + ap_perms_init(); + /* Get AP configuration data if available */ ap_init_configuration(); @@ -1217,7 +1495,9 @@ static int __init ap_module_init(void) ap_max_domain_id ? ap_max_domain_id : AP_DOMAINS - 1; else max_domain_id = 15; - if (ap_domain_index < -1 || ap_domain_index > max_domain_id) { + if (ap_domain_index < -1 || ap_domain_index > max_domain_id || + (ap_domain_index >= 0 && + !test_bit_inv(ap_domain_index, ap_perms.aqm))) { pr_warn("%d is not a valid cryptographic domain\n", ap_domain_index); ap_domain_index = -1; |