|
|
@@ -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"
|
|
|
@@ -43,19 +44,34 @@
|
|
|
*/
|
|
|
int ap_domain_index = -1; /* Adjunct Processor Domain Index */
|
|
|
static DEFINE_SPINLOCK(ap_domain_lock);
|
|
|
-module_param_named(domain, ap_domain_index, int, S_IRUSR|S_IRGRP);
|
|
|
+module_param_named(domain, ap_domain_index, int, 0440);
|
|
|
MODULE_PARM_DESC(domain, "domain index for ap devices");
|
|
|
EXPORT_SYMBOL(ap_domain_index);
|
|
|
|
|
|
-static int ap_thread_flag = 0;
|
|
|
-module_param_named(poll_thread, ap_thread_flag, int, S_IRUSR|S_IRGRP);
|
|
|
+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;
|
|
|
|
|
|
@@ -78,22 +94,26 @@ static DECLARE_WORK(ap_scan_work, ap_scan_bus);
|
|
|
static void ap_tasklet_fn(unsigned long);
|
|
|
static DECLARE_TASKLET(ap_tasklet, ap_tasklet_fn, 0);
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
|
|
|
-static struct task_struct *ap_poll_kthread = NULL;
|
|
|
+static struct task_struct *ap_poll_kthread;
|
|
|
static DEFINE_MUTEX(ap_poll_thread_mutex);
|
|
|
static DEFINE_SPINLOCK(ap_poll_timer_lock);
|
|
|
static struct hrtimer ap_poll_timer;
|
|
|
-/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
|
|
|
- * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
|
|
|
+/*
|
|
|
+ * In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
|
|
|
+ * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.
|
|
|
+ */
|
|
|
static unsigned long long poll_timeout = 250000;
|
|
|
|
|
|
/* Suspend flag */
|
|
|
static int ap_suspend_flag;
|
|
|
/* Maximum domain id */
|
|
|
static int ap_max_domain_id;
|
|
|
-/* Flag to check if domain was set through module parameter domain=. This is
|
|
|
+/*
|
|
|
+ * Flag to check if domain was set through module parameter domain=. This is
|
|
|
* important when supsend and resume is done in a z/VM environment where the
|
|
|
- * domain might change. */
|
|
|
-static int user_set_domain = 0;
|
|
|
+ * domain might change.
|
|
|
+ */
|
|
|
+static int user_set_domain;
|
|
|
static struct bus_type ap_bus_type;
|
|
|
|
|
|
/* Adapter interrupt definitions */
|
|
|
@@ -531,7 +551,7 @@ static int ap_bus_match(struct device *dev, struct device_driver *drv)
|
|
|
* It sets up a single environment variable DEV_TYPE which contains the
|
|
|
* hardware device type.
|
|
|
*/
|
|
|
-static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
|
|
|
+static int ap_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|
|
{
|
|
|
struct ap_device *ap_dev = to_ap_dev(dev);
|
|
|
int retval = 0;
|
|
|
@@ -570,7 +590,7 @@ static int ap_dev_resume(struct device *dev)
|
|
|
|
|
|
static void ap_bus_suspend(void)
|
|
|
{
|
|
|
- AP_DBF(DBF_DEBUG, "ap_bus_suspend running\n");
|
|
|
+ AP_DBF(DBF_DEBUG, "%s running\n", __func__);
|
|
|
|
|
|
ap_suspend_flag = 1;
|
|
|
/*
|
|
|
@@ -607,7 +627,7 @@ static void ap_bus_resume(void)
|
|
|
{
|
|
|
int rc;
|
|
|
|
|
|
- AP_DBF(DBF_DEBUG, "ap_bus_resume running\n");
|
|
|
+ AP_DBF(DBF_DEBUG, "%s running\n", __func__);
|
|
|
|
|
|
/* remove all queue devices */
|
|
|
bus_for_each_dev(&ap_bus_type, NULL, NULL,
|
|
|
@@ -666,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);
|
|
|
@@ -750,9 +856,164 @@ void ap_bus_force_rescan(void)
|
|
|
}
|
|
|
EXPORT_SYMBOL(ap_bus_force_rescan);
|
|
|
|
|
|
+/*
|
|
|
+ * 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;
|
|
|
+
|
|
|
+ /* 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 (*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);
|
|
|
@@ -764,7 +1025,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;
|
|
|
@@ -775,7 +1037,7 @@ static ssize_t ap_domain_store(struct bus_type *bus,
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(ap_domain, 0644, ap_domain_show, ap_domain_store);
|
|
|
+static BUS_ATTR_RW(ap_domain);
|
|
|
|
|
|
static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
|
|
|
{
|
|
|
@@ -790,8 +1052,7 @@ static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
|
|
|
ap_configuration->adm[6], ap_configuration->adm[7]);
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(ap_control_domain_mask, 0444,
|
|
|
- ap_control_domain_mask_show, NULL);
|
|
|
+static BUS_ATTR_RO(ap_control_domain_mask);
|
|
|
|
|
|
static ssize_t ap_usage_domain_mask_show(struct bus_type *bus, char *buf)
|
|
|
{
|
|
|
@@ -806,13 +1067,7 @@ static ssize_t ap_usage_domain_mask_show(struct bus_type *bus, char *buf)
|
|
|
ap_configuration->aqm[6], ap_configuration->aqm[7]);
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(ap_usage_domain_mask, 0444,
|
|
|
- ap_usage_domain_mask_show, NULL);
|
|
|
-
|
|
|
-static ssize_t ap_config_time_show(struct bus_type *bus, char *buf)
|
|
|
-{
|
|
|
- return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
|
|
|
-}
|
|
|
+static BUS_ATTR_RO(ap_usage_domain_mask);
|
|
|
|
|
|
static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf)
|
|
|
{
|
|
|
@@ -820,10 +1075,15 @@ static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf)
|
|
|
ap_using_interrupts() ? 1 : 0);
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(ap_interrupts, 0444, ap_interrupts_show, NULL);
|
|
|
+static BUS_ATTR_RO(ap_interrupts);
|
|
|
+
|
|
|
+static ssize_t config_time_show(struct bus_type *bus, char *buf)
|
|
|
+{
|
|
|
+ return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
|
|
|
+}
|
|
|
|
|
|
-static ssize_t ap_config_time_store(struct bus_type *bus,
|
|
|
- const char *buf, size_t count)
|
|
|
+static ssize_t config_time_store(struct bus_type *bus,
|
|
|
+ const char *buf, size_t count)
|
|
|
{
|
|
|
int time;
|
|
|
|
|
|
@@ -834,15 +1094,15 @@ static ssize_t ap_config_time_store(struct bus_type *bus,
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(config_time, 0644, ap_config_time_show, ap_config_time_store);
|
|
|
+static BUS_ATTR_RW(config_time);
|
|
|
|
|
|
-static ssize_t ap_poll_thread_show(struct bus_type *bus, char *buf)
|
|
|
+static ssize_t poll_thread_show(struct bus_type *bus, char *buf)
|
|
|
{
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", ap_poll_kthread ? 1 : 0);
|
|
|
}
|
|
|
|
|
|
-static ssize_t ap_poll_thread_store(struct bus_type *bus,
|
|
|
- const char *buf, size_t count)
|
|
|
+static ssize_t poll_thread_store(struct bus_type *bus,
|
|
|
+ const char *buf, size_t count)
|
|
|
{
|
|
|
int flag, rc;
|
|
|
|
|
|
@@ -857,7 +1117,7 @@ static ssize_t ap_poll_thread_store(struct bus_type *bus,
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store);
|
|
|
+static BUS_ATTR_RW(poll_thread);
|
|
|
|
|
|
static ssize_t poll_timeout_show(struct bus_type *bus, char *buf)
|
|
|
{
|
|
|
@@ -886,7 +1146,7 @@ static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store);
|
|
|
+static BUS_ATTR_RW(poll_timeout);
|
|
|
|
|
|
static ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf)
|
|
|
{
|
|
|
@@ -899,7 +1159,69 @@ static ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf)
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", max_domain_id);
|
|
|
}
|
|
|
|
|
|
-static BUS_ATTR(ap_max_domain_id, 0444, ap_max_domain_id_show, NULL);
|
|
|
+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 rc;
|
|
|
+
|
|
|
+ rc = process_mask_arg(buf, ap_perms.apm, AP_DEVICES, &ap_perms_mutex);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ 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 rc;
|
|
|
+
|
|
|
+ rc = process_mask_arg(buf, ap_perms.aqm, AP_DOMAINS, &ap_perms_mutex);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ ap_bus_revise_bindings();
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static BUS_ATTR_RW(aqmask);
|
|
|
|
|
|
static struct bus_attribute *const ap_bus_attrs[] = {
|
|
|
&bus_attr_ap_domain,
|
|
|
@@ -910,6 +1232,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,
|
|
|
};
|
|
|
|
|
|
@@ -938,7 +1262,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++) {
|
|
|
@@ -956,7 +1281,7 @@ static int ap_select_domain(void)
|
|
|
best_domain = i;
|
|
|
}
|
|
|
}
|
|
|
- if (best_domain >= 0){
|
|
|
+ if (best_domain >= 0) {
|
|
|
ap_domain_index = best_domain;
|
|
|
AP_DBF(DBF_DEBUG, "new ap_domain_index=%d\n", ap_domain_index);
|
|
|
spin_unlock_bh(&ap_domain_lock);
|
|
|
@@ -1038,7 +1363,7 @@ static void ap_scan_bus(struct work_struct *unused)
|
|
|
unsigned int func = 0;
|
|
|
int rc, id, dom, borked, domains, defdomdevs = 0;
|
|
|
|
|
|
- AP_DBF(DBF_DEBUG, "ap_scan_bus running\n");
|
|
|
+ AP_DBF(DBF_DEBUG, "%s running\n", __func__);
|
|
|
|
|
|
ap_query_configuration(ap_configuration);
|
|
|
if (ap_select_domain() != 0)
|
|
|
@@ -1163,7 +1488,8 @@ static void ap_scan_bus(struct work_struct *unused)
|
|
|
} /* end device loop */
|
|
|
|
|
|
if (defdomdevs < 1)
|
|
|
- AP_DBF(DBF_INFO, "no queue device with default domain %d available\n",
|
|
|
+ AP_DBF(DBF_INFO,
|
|
|
+ "no queue device with default domain %d available\n",
|
|
|
ap_domain_index);
|
|
|
|
|
|
out:
|
|
|
@@ -1187,6 +1513,27 @@ static int __init ap_debug_init(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void __init ap_perms_init(void)
|
|
|
+{
|
|
|
+ /* 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));
|
|
|
+
|
|
|
+ /* apm kernel parameter string */
|
|
|
+ if (apm_str) {
|
|
|
+ memset(&ap_perms.apm, 0, sizeof(ap_perms.apm));
|
|
|
+ process_mask_arg(apm_str, ap_perms.apm, AP_DEVICES,
|
|
|
+ &ap_perms_mutex);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 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);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ap_module_init(): The module initialization code.
|
|
|
*
|
|
|
@@ -1201,11 +1548,14 @@ static int __init ap_module_init(void)
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
|
- if (ap_instructions_available() != 0) {
|
|
|
+ if (!ap_instructions_available()) {
|
|
|
pr_warn("The hardware system does not support AP instructions\n");
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
+ /* set up the AP permissions (ap and aq masks) */
|
|
|
+ ap_perms_init();
|
|
|
+
|
|
|
/* Get AP configuration data if available */
|
|
|
ap_init_configuration();
|
|
|
|
|
|
@@ -1214,7 +1564,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;
|