diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 223 |
1 files changed, 146 insertions, 77 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 30d7898910ea..ec891bc7d10a 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -857,9 +857,13 @@ void ap_bus_force_rescan(void) EXPORT_SYMBOL(ap_bus_force_rescan); /* - * AP bus attributes. + * hex2bitmap() - parse hex mask string and set bitmap. + * Valid strings are "0x012345678" with at least one valid hex number. + * Rest of the bitmap to the right is padded with 0. No spaces allowed + * within the string, the leading 0x may be omitted. + * Returns the bitmask with exactly the bits set as given by the hex + * string (both in big endian order). */ - static int hex2bitmap(const char *str, unsigned long *bitmap, int bits) { int i, n, b; @@ -883,12 +887,133 @@ static int hex2bitmap(const char *str, unsigned long *bitmap, int bits) i += 4; } - if (i < 4 || isxdigit(*str)) + if (*str == '\n') + str++; + if (*str) + return -EINVAL; + return 0; +} + +/* + * str2clrsetmasks() - parse bitmask argument and set the clear and + * the set bitmap mask. A concatenation (done with ',') of these terms + * is recognized: + * +<bitnr>[-<bitnr>] or -<bitnr>[-<bitnr>] + * <bitnr> may be any valid number (hex, decimal or octal) in the range + * 0...bits-1; the leading + or - is required. Here are some examples: + * +0-15,+32,-128,-0xFF + * -0-255,+1-16,+0x128 + * +1,+2,+3,+4,-5,-7-10 + * Returns a clear and a set bitmask. Every positive value in the string + * results in a bit set in the set mask and every negative value in the + * string results in a bit SET in the clear mask. As a bit may be touched + * more than once, the last 'operation' wins: +0-255,-128 = all but bit + * 128 set in the set mask, only bit 128 set in the clear mask. + */ +static int str2clrsetmasks(const char *str, + unsigned long *clrmap, + unsigned long *setmap, + int bits) +{ + int a, i, z; + char *np, sign; + + /* bits needs to be a multiple of 8 */ + if (bits & 0x07) return -EINVAL; + memset(clrmap, 0, bits / 8); + memset(setmap, 0, bits / 8); + + while (*str) { + sign = *str++; + if (sign != '+' && sign != '-') + return -EINVAL; + a = z = simple_strtoul(str, &np, 0); + if (str == np || a >= bits) + return -EINVAL; + str = np; + if (*str == '-') { + z = simple_strtoul(++str, &np, 0); + if (str == np || a > z || z >= bits) + return -EINVAL; + str = np; + } + for (i = a; i <= z; i++) + if (sign == '+') { + set_bit_inv(i, setmap); + clear_bit_inv(i, clrmap); + } else { + clear_bit_inv(i, setmap); + set_bit_inv(i, clrmap); + } + while (*str == ',' || *str == '\n') + str++; + } + return 0; } +/* + * process_mask_arg() - 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 over- + * writing 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 othewise assume to be an absolute value string. If parsing fails + * a negative errno value is returned. All arguments and bitmaps are + * big endian order. + */ +static int process_mask_arg(const char *str, + unsigned long *bitmap, int bits, + struct mutex *lock) +{ + int i; + + /* bits needs to be a multiple of 8 */ + if (bits & 0x07) + return -EINVAL; + + if (*str == '+' || *str == '-') { + DECLARE_BITMAP(clrm, bits); + DECLARE_BITMAP(setm, bits); + + i = str2clrsetmasks(str, clrm, setm, bits); + if (i) + return i; + if (mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + for (i = 0; i < bits; i++) { + if (test_bit_inv(i, clrm)) + clear_bit_inv(i, bitmap); + if (test_bit_inv(i, setm)) + set_bit_inv(i, bitmap); + } + } else { + DECLARE_BITMAP(setm, bits); + + i = hex2bitmap(str, setm, bits); + if (i) + return i; + if (mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + for (i = 0; i < bits; i++) + if (test_bit_inv(i, setm)) + set_bit_inv(i, bitmap); + else + clear_bit_inv(i, bitmap); + } + mutex_unlock(lock); + + return 0; +} + +/* + * AP bus attributes. + */ + static ssize_t ap_domain_show(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index); @@ -1054,34 +1179,11 @@ static ssize_t apmask_show(struct bus_type *bus, char *buf) 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); + int rc; - 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); + rc = process_mask_arg(buf, ap_perms.apm, AP_DEVICES, &ap_perms_mutex); + if (rc) + return rc; ap_bus_revise_bindings(); @@ -1108,34 +1210,11 @@ static ssize_t aqmask_show(struct bus_type *bus, char *buf) 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); + int rc; - 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); + rc = process_mask_arg(buf, ap_perms.aqm, AP_DOMAINS, &ap_perms_mutex); + if (rc) + return rc; ap_bus_revise_bindings(); @@ -1436,32 +1515,22 @@ static int __init ap_debug_init(void) static void __init ap_perms_init(void) { - int i, rc; - - /* start with all resources useable */ + /* all resources useable if no kernel parameter string given */ 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 */ + /* apm kernel parameter string */ 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); - } + memset(&ap_perms.apm, 0, sizeof(ap_perms.apm)); + process_mask_arg(apm_str, ap_perms.apm, AP_DEVICES, + &ap_perms_mutex); } - 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); - } + /* aqm kernel parameter string */ + if (aqm_str) { + memset(&ap_perms.aqm, 0, sizeof(ap_perms.aqm)); + process_mask_arg(aqm_str, ap_perms.aqm, AP_DOMAINS, + &ap_perms_mutex); } } |