Merge tag 'libnvdimm-for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 21 Sep 2019 17:55:29 +0000 (10:55 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 21 Sep 2019 17:55:29 +0000 (10:55 -0700)
Pull libnvdimm updates from Dan Williams:
 "Some reworks to better support nvdimms on powerpc and an nvdimm
  security interface update:

   - Rework the nvdimm core to accommodate architectures with different
     page sizes and ones that can change supported huge page sizes at
     boot time rather than a compile time constant.

   - Introduce a distinct 'frozen' attribute for the nvdimm security
     state since it is independent of the locked state.

   - Miscellaneous fixups"

* tag 'libnvdimm-for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
  libnvdimm: Use PAGE_SIZE instead of SZ_4K for align check
  libnvdimm/label: Remove the dpa align check
  libnvdimm/pfn_dev: Add page size and struct page size to pfn superblock
  libnvdimm/pfn_dev: Add a build check to make sure we notice when struct page size change
  libnvdimm/pmem: Advance namespace seed for specific probe errors
  libnvdimm/region: Rewrite _probe_success() to _advance_seeds()
  libnvdimm/security: Consolidate 'security' operations
  libnvdimm/security: Tighten scope of nvdimm->busy vs security operations
  libnvdimm/security: Introduce a 'frozen' attribute
  libnvdimm, region: Use struct_size() in kzalloc()
  tools/testing/nvdimm: Fix fallthrough warning
  libnvdimm/of_pmem: Provide a unique name for bus provider

16 files changed:
drivers/acpi/nfit/intel.c
drivers/nvdimm/bus.c
drivers/nvdimm/dimm_devs.c
drivers/nvdimm/label.c
drivers/nvdimm/namespace_devs.c
drivers/nvdimm/nd-core.h
drivers/nvdimm/nd.h
drivers/nvdimm/of_pmem.c
drivers/nvdimm/pfn.h
drivers/nvdimm/pfn_devs.c
drivers/nvdimm/pmem.c
drivers/nvdimm/region_devs.c
drivers/nvdimm/security.c
include/linux/libnvdimm.h
tools/testing/nvdimm/dimm_devs.c
tools/testing/nvdimm/test/nfit.c

index cddd0fcf622c3314f7115e86124e9dde5a5f98ff..1113b679cd7b9288a536ff91ac39796f70663adb 100644 (file)
@@ -7,10 +7,11 @@
 #include "intel.h"
 #include "nfit.h"
 
-static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
+static unsigned long intel_security_flags(struct nvdimm *nvdimm,
                enum nvdimm_passphrase_type ptype)
 {
        struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+       unsigned long security_flags = 0;
        struct {
                struct nd_cmd_pkg pkg;
                struct nd_intel_get_security_state cmd;
@@ -27,7 +28,7 @@ static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
        int rc;
 
        if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask))
-               return -ENXIO;
+               return 0;
 
        /*
         * Short circuit the state retrieval while we are doing overwrite.
@@ -35,38 +36,42 @@ static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm,
         * until the overwrite DSM completes.
         */
        if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER)
-               return NVDIMM_SECURITY_OVERWRITE;
+               return BIT(NVDIMM_SECURITY_OVERWRITE);
 
        rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
-       if (rc < 0)
-               return rc;
-       if (nd_cmd.cmd.status)
-               return -EIO;
+       if (rc < 0 || nd_cmd.cmd.status) {
+               pr_err("%s: security state retrieval failed (%d:%#x)\n",
+                               nvdimm_name(nvdimm), rc, nd_cmd.cmd.status);
+               return 0;
+       }
 
        /* check and see if security is enabled and locked */
        if (ptype == NVDIMM_MASTER) {
                if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED)
-                       return NVDIMM_SECURITY_UNLOCKED;
-               else if (nd_cmd.cmd.extended_state &
-                               ND_INTEL_SEC_ESTATE_PLIMIT)
-                       return NVDIMM_SECURITY_FROZEN;
-       } else {
-               if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
-                       return -ENXIO;
-               else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
-                       if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
-                               return NVDIMM_SECURITY_LOCKED;
-                       else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN
-                                       || nd_cmd.cmd.state &
-                                       ND_INTEL_SEC_STATE_PLIMIT)
-                               return NVDIMM_SECURITY_FROZEN;
-                       else
-                               return NVDIMM_SECURITY_UNLOCKED;
-               }
+                       set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
+               else
+                       set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
+               if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT)
+                       set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
+               return security_flags;
        }
 
-       /* this should cover master security disabled as well */
-       return NVDIMM_SECURITY_DISABLED;
+       if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
+               return 0;
+
+       if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
+               if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN ||
+                   nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT)
+                       set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
+
+               if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
+                       set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);
+               else
+                       set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
+       } else
+               set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
+
+       return security_flags;
 }
 
 static int intel_security_freeze(struct nvdimm *nvdimm)
@@ -371,7 +376,7 @@ static void nvdimm_invalidate_cache(void)
 #endif
 
 static const struct nvdimm_security_ops __intel_security_ops = {
-       .state = intel_security_state,
+       .get_flags = intel_security_flags,
        .freeze = intel_security_freeze,
        .change_key = intel_security_change_key,
        .disable = intel_security_disable,
index 798c5c4aea9ca3ac77e18a7c62e3cb83c68c3c86..75a58a6e961577e1111f7dbcae36f6005805376e 100644 (file)
@@ -95,10 +95,9 @@ static int nvdimm_bus_probe(struct device *dev)
        rc = nd_drv->probe(dev);
        debug_nvdimm_unlock(dev);
 
-       if (rc == 0)
-               nd_region_probe_success(nvdimm_bus, dev);
-       else
-               nd_region_disable(nvdimm_bus, dev);
+       if ((rc == 0 || rc == -EOPNOTSUPP) &&
+                       dev->parent && is_nd_region(dev->parent))
+               nd_region_advance_seeds(to_nd_region(dev->parent), dev);
        nvdimm_bus_probe_end(nvdimm_bus);
 
        dev_dbg(&nvdimm_bus->dev, "END: %s.probe(%s) = %d\n", dev->driver->name,
@@ -121,7 +120,6 @@ static int nvdimm_bus_remove(struct device *dev)
                rc = nd_drv->remove(dev);
                debug_nvdimm_unlock(dev);
        }
-       nd_region_disable(nvdimm_bus, dev);
 
        dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name,
                        dev_name(dev), rc);
@@ -400,7 +398,7 @@ static int child_unregister(struct device *dev, void *data)
 
                /* We are shutting down. Make state frozen artificially. */
                nvdimm_bus_lock(dev);
-               nvdimm->sec.state = NVDIMM_SECURITY_FROZEN;
+               set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags);
                if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags))
                        dev_put = true;
                nvdimm_bus_unlock(dev);
index 29a065e769ea897359778dba417a78d7a54c7053..196aa44c4936a74a0e0a1147d19d740fca801120 100644 (file)
@@ -372,106 +372,26 @@ __weak ssize_t security_show(struct device *dev,
 {
        struct nvdimm *nvdimm = to_nvdimm(dev);
 
-       switch (nvdimm->sec.state) {
-       case NVDIMM_SECURITY_DISABLED:
+       if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
                return sprintf(buf, "disabled\n");
-       case NVDIMM_SECURITY_UNLOCKED:
+       if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
                return sprintf(buf, "unlocked\n");
-       case NVDIMM_SECURITY_LOCKED:
+       if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
                return sprintf(buf, "locked\n");
-       case NVDIMM_SECURITY_FROZEN:
-               return sprintf(buf, "frozen\n");
-       case NVDIMM_SECURITY_OVERWRITE:
+       if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
                return sprintf(buf, "overwrite\n");
-       default:
-               return -ENOTTY;
-       }
-
        return -ENOTTY;
 }
 
-#define OPS                                                    \
-       C( OP_FREEZE,           "freeze",               1),     \
-       C( OP_DISABLE,          "disable",              2),     \
-       C( OP_UPDATE,           "update",               3),     \
-       C( OP_ERASE,            "erase",                2),     \
-       C( OP_OVERWRITE,        "overwrite",            2),     \
-       C( OP_MASTER_UPDATE,    "master_update",        3),     \
-       C( OP_MASTER_ERASE,     "master_erase",         2)
-#undef C
-#define C(a, b, c) a
-enum nvdimmsec_op_ids { OPS };
-#undef C
-#define C(a, b, c) { b, c }
-static struct {
-       const char *name;
-       int args;
-} ops[] = { OPS };
-#undef C
-
-#define SEC_CMD_SIZE 32
-#define KEY_ID_SIZE 10
-
-static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
+static ssize_t frozen_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
 {
        struct nvdimm *nvdimm = to_nvdimm(dev);
-       ssize_t rc;
-       char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
-               nkeystr[KEY_ID_SIZE+1];
-       unsigned int key, newkey;
-       int i;
 
-       if (atomic_read(&nvdimm->busy))
-               return -EBUSY;
-
-       rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
-                       " %"__stringify(KEY_ID_SIZE)"s"
-                       " %"__stringify(KEY_ID_SIZE)"s",
-                       cmd, keystr, nkeystr);
-       if (rc < 1)
-               return -EINVAL;
-       for (i = 0; i < ARRAY_SIZE(ops); i++)
-               if (sysfs_streq(cmd, ops[i].name))
-                       break;
-       if (i >= ARRAY_SIZE(ops))
-               return -EINVAL;
-       if (ops[i].args > 1)
-               rc = kstrtouint(keystr, 0, &key);
-       if (rc >= 0 && ops[i].args > 2)
-               rc = kstrtouint(nkeystr, 0, &newkey);
-       if (rc < 0)
-               return rc;
-
-       if (i == OP_FREEZE) {
-               dev_dbg(dev, "freeze\n");
-               rc = nvdimm_security_freeze(nvdimm);
-       } else if (i == OP_DISABLE) {
-               dev_dbg(dev, "disable %u\n", key);
-               rc = nvdimm_security_disable(nvdimm, key);
-       } else if (i == OP_UPDATE) {
-               dev_dbg(dev, "update %u %u\n", key, newkey);
-               rc = nvdimm_security_update(nvdimm, key, newkey, NVDIMM_USER);
-       } else if (i == OP_ERASE) {
-               dev_dbg(dev, "erase %u\n", key);
-               rc = nvdimm_security_erase(nvdimm, key, NVDIMM_USER);
-       } else if (i == OP_OVERWRITE) {
-               dev_dbg(dev, "overwrite %u\n", key);
-               rc = nvdimm_security_overwrite(nvdimm, key);
-       } else if (i == OP_MASTER_UPDATE) {
-               dev_dbg(dev, "master_update %u %u\n", key, newkey);
-               rc = nvdimm_security_update(nvdimm, key, newkey,
-                               NVDIMM_MASTER);
-       } else if (i == OP_MASTER_ERASE) {
-               dev_dbg(dev, "master_erase %u\n", key);
-               rc = nvdimm_security_erase(nvdimm, key,
-                               NVDIMM_MASTER);
-       } else
-               return -EINVAL;
-
-       if (rc == 0)
-               rc = len;
-       return rc;
+       return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN,
+                               &nvdimm->sec.flags));
 }
+static DEVICE_ATTR_RO(frozen);
 
 static ssize_t security_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t len)
@@ -487,7 +407,7 @@ static ssize_t security_store(struct device *dev,
        nd_device_lock(dev);
        nvdimm_bus_lock(dev);
        wait_nvdimm_bus_probe_idle(dev);
-       rc = __security_store(dev, buf, len);
+       rc = nvdimm_security_store(dev, buf, len);
        nvdimm_bus_unlock(dev);
        nd_device_unlock(dev);
 
@@ -501,6 +421,7 @@ static struct attribute *nvdimm_attributes[] = {
        &dev_attr_commands.attr,
        &dev_attr_available_slots.attr,
        &dev_attr_security.attr,
+       &dev_attr_frozen.attr,
        NULL,
 };
 
@@ -509,17 +430,24 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
        struct device *dev = container_of(kobj, typeof(*dev), kobj);
        struct nvdimm *nvdimm = to_nvdimm(dev);
 
-       if (a != &dev_attr_security.attr)
+       if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr)
                return a->mode;
-       if (nvdimm->sec.state < 0)
+       if (!nvdimm->sec.flags)
                return 0;
-       /* Are there any state mutation ops? */
-       if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
-                       || nvdimm->sec.ops->change_key
-                       || nvdimm->sec.ops->erase
-                       || nvdimm->sec.ops->overwrite)
+
+       if (a == &dev_attr_security.attr) {
+               /* Are there any state mutation ops (make writable)? */
+               if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
+                               || nvdimm->sec.ops->change_key
+                               || nvdimm->sec.ops->erase
+                               || nvdimm->sec.ops->overwrite)
+                       return a->mode;
+               return 0444;
+       }
+
+       if (nvdimm->sec.ops->freeze)
                return a->mode;
-       return 0444;
+       return 0;
 }
 
 struct attribute_group nvdimm_attribute_group = {
@@ -569,8 +497,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
         * attribute visibility.
         */
        /* get security state and extended (master) state */
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
-       nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
+       nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
        nd_device_register(dev);
 
        return nvdimm;
@@ -588,7 +516,7 @@ int nvdimm_security_setup_events(struct device *dev)
 {
        struct nvdimm *nvdimm = to_nvdimm(dev);
 
-       if (nvdimm->sec.state < 0 || !nvdimm->sec.ops
+       if (!nvdimm->sec.flags || !nvdimm->sec.ops
                        || !nvdimm->sec.ops->overwrite)
                return 0;
        nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security");
@@ -614,7 +542,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm)
        if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze)
                return -EOPNOTSUPP;
 
-       if (nvdimm->sec.state < 0)
+       if (!nvdimm->sec.flags)
                return -EIO;
 
        if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
@@ -623,7 +551,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm)
        }
 
        rc = nvdimm->sec.ops->freeze(nvdimm);
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
 
        return rc;
 }
index 73e197babc2ffb27e954234cf2c1aec64b5cb817..47a4828b8b310df76e5a19339cdc0036770182a9 100644 (file)
@@ -353,11 +353,6 @@ static bool slot_valid(struct nvdimm_drvdata *ndd,
        if (slot != __le32_to_cpu(nd_label->slot))
                return false;
 
-       /* check that DPA allocations are page aligned */
-       if ((__le64_to_cpu(nd_label->dpa)
-                               | __le64_to_cpu(nd_label->rawsize)) % SZ_4K)
-               return false;
-
        /* check checksum */
        if (namespace_label_has(ndd, checksum)) {
                u64 sum, sum_save;
index a16e52251a3052a0a75dac512abff13b316c16a0..43401325c874440823bbbce1ade81353bc0d6a23 100644 (file)
@@ -1006,10 +1006,10 @@ static ssize_t __size_store(struct device *dev, unsigned long long val)
                return -ENXIO;
        }
 
-       div_u64_rem(val, SZ_4K * nd_region->ndr_mappings, &remainder);
+       div_u64_rem(val, PAGE_SIZE * nd_region->ndr_mappings, &remainder);
        if (remainder) {
-               dev_dbg(dev, "%llu is not %dK aligned\n", val,
-                               (SZ_4K * nd_region->ndr_mappings) / SZ_1K);
+               dev_dbg(dev, "%llu is not %ldK aligned\n", val,
+                               (PAGE_SIZE * nd_region->ndr_mappings) / SZ_1K);
                return -EINVAL;
        }
 
@@ -2462,6 +2462,27 @@ static struct device **create_namespaces(struct nd_region *nd_region)
        return devs;
 }
 
+static void deactivate_labels(void *region)
+{
+       struct nd_region *nd_region = region;
+       int i;
+
+       for (i = 0; i < nd_region->ndr_mappings; i++) {
+               struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+               struct nvdimm_drvdata *ndd = nd_mapping->ndd;
+               struct nvdimm *nvdimm = nd_mapping->nvdimm;
+
+               mutex_lock(&nd_mapping->lock);
+               nd_mapping_free_labels(nd_mapping);
+               mutex_unlock(&nd_mapping->lock);
+
+               put_ndd(ndd);
+               nd_mapping->ndd = NULL;
+               if (ndd)
+                       atomic_dec(&nvdimm->busy);
+       }
+}
+
 static int init_active_labels(struct nd_region *nd_region)
 {
        int i;
@@ -2519,16 +2540,17 @@ static int init_active_labels(struct nd_region *nd_region)
                        mutex_unlock(&nd_mapping->lock);
                }
 
-               if (j >= count)
-                       continue;
+               if (j < count)
+                       break;
+       }
 
-               mutex_lock(&nd_mapping->lock);
-               nd_mapping_free_labels(nd_mapping);
-               mutex_unlock(&nd_mapping->lock);
+       if (i < nd_region->ndr_mappings) {
+               deactivate_labels(nd_region);
                return -ENOMEM;
        }
 
-       return 0;
+       return devm_add_action_or_reset(&nd_region->dev, deactivate_labels,
+                       nd_region);
 }
 
 int nd_region_register_namespaces(struct nd_region *nd_region, int *err)
index 0ac52b6eb00e114677168d5c1e9597d6bb8b6f8b..25fa121104d04363e35dcad09e7de9a4d57e7956 100644 (file)
@@ -39,53 +39,40 @@ struct nvdimm {
        const char *dimm_id;
        struct {
                const struct nvdimm_security_ops *ops;
-               enum nvdimm_security_state state;
-               enum nvdimm_security_state ext_state;
+               unsigned long flags;
+               unsigned long ext_flags;
                unsigned int overwrite_tmo;
                struct kernfs_node *overwrite_state;
        } sec;
        struct delayed_work dwork;
 };
 
-static inline enum nvdimm_security_state nvdimm_security_state(
+static inline unsigned long nvdimm_security_flags(
                struct nvdimm *nvdimm, enum nvdimm_passphrase_type ptype)
 {
+       u64 flags;
+       const u64 state_flags = 1UL << NVDIMM_SECURITY_DISABLED
+               | 1UL << NVDIMM_SECURITY_LOCKED
+               | 1UL << NVDIMM_SECURITY_UNLOCKED
+               | 1UL << NVDIMM_SECURITY_OVERWRITE;
+
        if (!nvdimm->sec.ops)
-               return -ENXIO;
+               return 0;
 
-       return nvdimm->sec.ops->state(nvdimm, ptype);
+       flags = nvdimm->sec.ops->get_flags(nvdimm, ptype);
+       /* disabled, locked, unlocked, and overwrite are mutually exclusive */
+       dev_WARN_ONCE(&nvdimm->dev, hweight64(flags & state_flags) > 1,
+                       "reported invalid security state: %#llx\n",
+                       (unsigned long long) flags);
+       return flags;
 }
 int nvdimm_security_freeze(struct nvdimm *nvdimm);
 #if IS_ENABLED(CONFIG_NVDIMM_KEYS)
-int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid);
-int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
-               unsigned int new_keyid,
-               enum nvdimm_passphrase_type pass_type);
-int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
-               enum nvdimm_passphrase_type pass_type);
-int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid);
+ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len);
 void nvdimm_security_overwrite_query(struct work_struct *work);
 #else
-static inline int nvdimm_security_disable(struct nvdimm *nvdimm,
-               unsigned int keyid)
-{
-       return -EOPNOTSUPP;
-}
-static inline int nvdimm_security_update(struct nvdimm *nvdimm,
-               unsigned int keyid,
-               unsigned int new_keyid,
-               enum nvdimm_passphrase_type pass_type)
-{
-       return -EOPNOTSUPP;
-}
-static inline int nvdimm_security_erase(struct nvdimm *nvdimm,
-               unsigned int keyid,
-               enum nvdimm_passphrase_type pass_type)
-{
-       return -EOPNOTSUPP;
-}
-static inline int nvdimm_security_overwrite(struct nvdimm *nvdimm,
-               unsigned int keyid)
+static inline ssize_t nvdimm_security_store(struct device *dev,
+               const char *buf, size_t len)
 {
        return -EOPNOTSUPP;
 }
@@ -128,13 +115,12 @@ int __init nvdimm_bus_init(void);
 void nvdimm_bus_exit(void);
 void nvdimm_devs_exit(void);
 void nd_region_devs_exit(void);
-void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev);
 struct nd_region;
+void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev);
 void nd_region_create_ns_seed(struct nd_region *nd_region);
 void nd_region_create_btt_seed(struct nd_region *nd_region);
 void nd_region_create_pfn_seed(struct nd_region *nd_region);
 void nd_region_create_dax_seed(struct nd_region *nd_region);
-void nd_region_disable(struct nvdimm_bus *nvdimm_bus, struct device *dev);
 int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus);
 void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus);
 void nd_synchronize(void);
index 1b9955651379a8f3803b42d2b49cc65fd8c57481..e89af4b2d8e9aa5d3fe8ff52a066bee604fb5779 100644 (file)
@@ -375,6 +375,10 @@ unsigned int pmem_sector_size(struct nd_namespace_common *ndns);
 void nvdimm_badblocks_populate(struct nd_region *nd_region,
                struct badblocks *bb, const struct resource *res);
 #if IS_ENABLED(CONFIG_ND_CLAIM)
+
+/* max struct page size independent of kernel config */
+#define MAX_STRUCT_PAGE_SIZE 64
+
 int nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap);
 int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio);
 void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio);
index a0c8dcfa0bf923cc1e8a1b2ffacb74f2f801d681..97187d6c0bdb00ee2de3e085edea9f8337546863 100644 (file)
@@ -42,7 +42,7 @@ static int of_pmem_region_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        priv->bus_desc.attr_groups = bus_attr_groups;
-       priv->bus_desc.provider_name = "of_pmem";
+       priv->bus_desc.provider_name = kstrdup(pdev->name, GFP_KERNEL);
        priv->bus_desc.module = THIS_MODULE;
        priv->bus_desc.of_node = np;
 
index 7381673b7b70b9804b00ed982ddac2153d44f305..acb19517f678b05020fdb3e5af0d5c04d4bb8eae 100644 (file)
@@ -29,7 +29,10 @@ struct nd_pfn_sb {
        /* minor-version-2 record the base alignment of the mapping */
        __le32 align;
        /* minor-version-3 guarantee the padding and flags are zero */
-       u8 padding[4000];
+       /* minor-version-4 record the page size and struct page size */
+       __le32 page_size;
+       __le16 page_struct_size;
+       u8 padding[3994];
        __le64 checksum;
 };
 
index cb98b8fe786e2232bbdb1d0a371ce5df0e2e13a3..bb9cc5cf087385647f51d4522981d47f317cc45e 100644 (file)
@@ -460,6 +460,11 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig)
        if (__le16_to_cpu(pfn_sb->version_minor) < 2)
                pfn_sb->align = 0;
 
+       if (__le16_to_cpu(pfn_sb->version_minor) < 4) {
+               pfn_sb->page_struct_size = cpu_to_le16(64);
+               pfn_sb->page_size = cpu_to_le32(PAGE_SIZE);
+       }
+
        switch (le32_to_cpu(pfn_sb->mode)) {
        case PFN_MODE_RAM:
        case PFN_MODE_PMEM:
@@ -475,6 +480,22 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig)
                align = 1UL << ilog2(offset);
        mode = le32_to_cpu(pfn_sb->mode);
 
+       if ((le32_to_cpu(pfn_sb->page_size) > PAGE_SIZE) &&
+                       (mode == PFN_MODE_PMEM)) {
+               dev_err(&nd_pfn->dev,
+                               "init failed, page size mismatch %d\n",
+                               le32_to_cpu(pfn_sb->page_size));
+               return -EOPNOTSUPP;
+       }
+
+       if ((le16_to_cpu(pfn_sb->page_struct_size) < sizeof(struct page)) &&
+                       (mode == PFN_MODE_PMEM)) {
+               dev_err(&nd_pfn->dev,
+                               "init failed, struct page size mismatch %d\n",
+                               le16_to_cpu(pfn_sb->page_struct_size));
+               return -EOPNOTSUPP;
+       }
+
        if (!nd_pfn->uuid) {
                /*
                 * When probing a namepace via nd_pfn_probe() the uuid
@@ -703,8 +724,16 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
                 * The altmap should be padded out to the block size used
                 * when populating the vmemmap. This *should* be equal to
                 * PMD_SIZE for most architectures.
+                *
+                * Also make sure size of struct page is less than 64. We
+                * want to make sure we use large enough size here so that
+                * we don't have a dynamic reserve space depending on
+                * struct page size. But we also want to make sure we notice
+                * when we end up adding new elements to struct page.
                 */
-               offset = ALIGN(start + SZ_8K + 64 * npfns, align) - start;
+               BUILD_BUG_ON(sizeof(struct page) > MAX_STRUCT_PAGE_SIZE);
+               offset = ALIGN(start + SZ_8K + MAX_STRUCT_PAGE_SIZE * npfns, align)
+                       - start;
        } else if (nd_pfn->mode == PFN_MODE_RAM)
                offset = ALIGN(start + SZ_8K, align) - start;
        else
@@ -724,9 +753,11 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
        memcpy(pfn_sb->uuid, nd_pfn->uuid, 16);
        memcpy(pfn_sb->parent_uuid, nd_dev_to_uuid(&ndns->dev), 16);
        pfn_sb->version_major = cpu_to_le16(1);
-       pfn_sb->version_minor = cpu_to_le16(3);
+       pfn_sb->version_minor = cpu_to_le16(4);
        pfn_sb->end_trunc = cpu_to_le32(end_trunc);
        pfn_sb->align = cpu_to_le32(nd_pfn->align);
+       pfn_sb->page_struct_size = cpu_to_le16(MAX_STRUCT_PAGE_SIZE);
+       pfn_sb->page_size = cpu_to_le32(PAGE_SIZE);
        checksum = nd_sb_checksum((struct nd_gen_sb *) pfn_sb);
        pfn_sb->checksum = cpu_to_le64(checksum);
 
index 4c121dd03dd91b75af3ec9b55d84ae8cc3e80fe2..f9f76f6ba07b1ba3283d1d48e6eebcf631e0acc5 100644 (file)
@@ -490,6 +490,7 @@ static int pmem_attach_disk(struct device *dev,
 
 static int nd_pmem_probe(struct device *dev)
 {
+       int ret;
        struct nd_namespace_common *ndns;
 
        ndns = nvdimm_namespace_common_probe(dev);
@@ -505,12 +506,32 @@ static int nd_pmem_probe(struct device *dev)
        if (is_nd_pfn(dev))
                return pmem_attach_disk(dev, ndns);
 
-       /* if we find a valid info-block we'll come back as that personality */
-       if (nd_btt_probe(dev, ndns) == 0 || nd_pfn_probe(dev, ndns) == 0
-                       || nd_dax_probe(dev, ndns) == 0)
+       ret = nd_btt_probe(dev, ndns);
+       if (ret == 0)
                return -ENXIO;
 
-       /* ...otherwise we're just a raw pmem device */
+       /*
+        * We have two failure conditions here, there is no
+        * info reserver block or we found a valid info reserve block
+        * but failed to initialize the pfn superblock.
+        *
+        * For the first case consider namespace as a raw pmem namespace
+        * and attach a disk.
+        *
+        * For the latter, consider this a success and advance the namespace
+        * seed.
+        */
+       ret = nd_pfn_probe(dev, ndns);
+       if (ret == 0)
+               return -ENXIO;
+       else if (ret == -EOPNOTSUPP)
+               return ret;
+
+       ret = nd_dax_probe(dev, ndns);
+       if (ret == 0)
+               return -ENXIO;
+       else if (ret == -EOPNOTSUPP)
+               return ret;
        return pmem_attach_disk(dev, ndns);
 }
 
index af30cbe7a8ea26fd5eb643af56e1a9bffaeda7ff..3fd6b59abd33049293df0ddc3c98d023f1c7cd8d 100644 (file)
@@ -715,85 +715,37 @@ void nd_mapping_free_labels(struct nd_mapping *nd_mapping)
 }
 
 /*
- * Upon successful probe/remove, take/release a reference on the
- * associated interleave set (if present), and plant new btt + namespace
- * seeds.  Also, on the removal of a BLK region, notify the provider to
- * disable the region.
+ * When a namespace is activated create new seeds for the next
+ * namespace, or namespace-personality to be configured.
  */
-static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus,
-               struct device *dev, bool probe)
+void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev)
 {
-       struct nd_region *nd_region;
-
-       if (!probe && is_nd_region(dev)) {
-               int i;
-
-               nd_region = to_nd_region(dev);
-               for (i = 0; i < nd_region->ndr_mappings; i++) {
-                       struct nd_mapping *nd_mapping = &nd_region->mapping[i];
-                       struct nvdimm_drvdata *ndd = nd_mapping->ndd;
-                       struct nvdimm *nvdimm = nd_mapping->nvdimm;
-
-                       mutex_lock(&nd_mapping->lock);
-                       nd_mapping_free_labels(nd_mapping);
-                       mutex_unlock(&nd_mapping->lock);
-
-                       put_ndd(ndd);
-                       nd_mapping->ndd = NULL;
-                       if (ndd)
-                               atomic_dec(&nvdimm->busy);
-               }
-       }
-       if (dev->parent && is_nd_region(dev->parent) && probe) {
-               nd_region = to_nd_region(dev->parent);
-               nvdimm_bus_lock(dev);
-               if (nd_region->ns_seed == dev)
-                       nd_region_create_ns_seed(nd_region);
-               nvdimm_bus_unlock(dev);
-       }
-       if (is_nd_btt(dev) && probe) {
+       nvdimm_bus_lock(dev);
+       if (nd_region->ns_seed == dev) {
+               nd_region_create_ns_seed(nd_region);
+       } else if (is_nd_btt(dev)) {
                struct nd_btt *nd_btt = to_nd_btt(dev);
 
-               nd_region = to_nd_region(dev->parent);
-               nvdimm_bus_lock(dev);
                if (nd_region->btt_seed == dev)
                        nd_region_create_btt_seed(nd_region);
                if (nd_region->ns_seed == &nd_btt->ndns->dev)
                        nd_region_create_ns_seed(nd_region);
-               nvdimm_bus_unlock(dev);
-       }
-       if (is_nd_pfn(dev) && probe) {
+       } else if (is_nd_pfn(dev)) {
                struct nd_pfn *nd_pfn = to_nd_pfn(dev);
 
-               nd_region = to_nd_region(dev->parent);
-               nvdimm_bus_lock(dev);
                if (nd_region->pfn_seed == dev)
                        nd_region_create_pfn_seed(nd_region);
                if (nd_region->ns_seed == &nd_pfn->ndns->dev)
                        nd_region_create_ns_seed(nd_region);
-               nvdimm_bus_unlock(dev);
-       }
-       if (is_nd_dax(dev) && probe) {
+       } else if (is_nd_dax(dev)) {
                struct nd_dax *nd_dax = to_nd_dax(dev);
 
-               nd_region = to_nd_region(dev->parent);
-               nvdimm_bus_lock(dev);
                if (nd_region->dax_seed == dev)
                        nd_region_create_dax_seed(nd_region);
                if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev)
                        nd_region_create_ns_seed(nd_region);
-               nvdimm_bus_unlock(dev);
        }
-}
-
-void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev)
-{
-       nd_region_notify_driver_action(nvdimm_bus, dev, true);
-}
-
-void nd_region_disable(struct nvdimm_bus *nvdimm_bus, struct device *dev)
-{
-       nd_region_notify_driver_action(nvdimm_bus, dev, false);
+       nvdimm_bus_unlock(dev);
 }
 
 static ssize_t mappingN(struct device *dev, char *buf, int n)
@@ -992,10 +944,10 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
                struct nd_mapping_desc *mapping = &ndr_desc->mapping[i];
                struct nvdimm *nvdimm = mapping->nvdimm;
 
-               if ((mapping->start | mapping->size) % SZ_4K) {
-                       dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not 4K aligned\n",
-                                       caller, dev_name(&nvdimm->dev), i);
-
+               if ((mapping->start | mapping->size) % PAGE_SIZE) {
+                       dev_err(&nvdimm_bus->dev,
+                               "%s: %s mapping%d is not %ld aligned\n",
+                               caller, dev_name(&nvdimm->dev), i, PAGE_SIZE);
                        return NULL;
                }
 
@@ -1025,10 +977,9 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
                }
                region_buf = ndbr;
        } else {
-               nd_region = kzalloc(sizeof(struct nd_region)
-                               + sizeof(struct nd_mapping)
-                               * ndr_desc->num_mappings,
-                               GFP_KERNEL);
+               nd_region = kzalloc(struct_size(nd_region, mapping,
+                                               ndr_desc->num_mappings),
+                                   GFP_KERNEL);
                region_buf = nd_region;
        }
 
index a570f2263a424e96908c559750454a086a3df3e2..9e45b207ff0169f0c820a91c0d80eed141500004 100644 (file)
@@ -158,7 +158,7 @@ static int nvdimm_key_revalidate(struct nvdimm *nvdimm)
        }
 
        nvdimm_put_key(key);
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
        return 0;
 }
 
@@ -174,7 +174,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
        lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
 
        if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock
-                       || nvdimm->sec.state < 0)
+                       || !nvdimm->sec.flags)
                return -EIO;
 
        if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
@@ -189,7 +189,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
         * freeze of the security configuration. I.e. if the OS does not
         * have the key, security is being managed pre-OS.
         */
-       if (nvdimm->sec.state == NVDIMM_SECURITY_UNLOCKED) {
+       if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) {
                if (!key_revalidate)
                        return 0;
 
@@ -202,7 +202,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
                        rc == 0 ? "success" : "fail");
 
        nvdimm_put_key(key);
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
        return rc;
 }
 
@@ -217,7 +217,25 @@ int nvdimm_security_unlock(struct device *dev)
        return rc;
 }
 
-int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
+static int check_security_state(struct nvdimm *nvdimm)
+{
+       struct device *dev = &nvdimm->dev;
+
+       if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) {
+               dev_dbg(dev, "Incorrect security state: %#lx\n",
+                               nvdimm->sec.flags);
+               return -EIO;
+       }
+
+       if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
+               dev_dbg(dev, "Security operation in progress.\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int security_disable(struct nvdimm *nvdimm, unsigned int keyid)
 {
        struct device *dev = &nvdimm->dev;
        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
@@ -229,19 +247,12 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
        lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
 
        if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable
-                       || nvdimm->sec.state < 0)
+                       || !nvdimm->sec.flags)
                return -EOPNOTSUPP;
 
-       if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
-               dev_dbg(dev, "Incorrect security state: %d\n",
-                               nvdimm->sec.state);
-               return -EIO;
-       }
-
-       if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
-               dev_dbg(dev, "Security operation in progress.\n");
-               return -EBUSY;
-       }
+       rc = check_security_state(nvdimm);
+       if (rc)
+               return rc;
 
        data = nvdimm_get_user_key_payload(nvdimm, keyid,
                        NVDIMM_BASE_KEY, &key);
@@ -253,11 +264,11 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
                        rc == 0 ? "success" : "fail");
 
        nvdimm_put_key(key);
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
        return rc;
 }
 
-int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
+static int security_update(struct nvdimm *nvdimm, unsigned int keyid,
                unsigned int new_keyid,
                enum nvdimm_passphrase_type pass_type)
 {
@@ -271,14 +282,12 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
        lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
 
        if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key
-                       || nvdimm->sec.state < 0)
+                       || !nvdimm->sec.flags)
                return -EOPNOTSUPP;
 
-       if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
-               dev_dbg(dev, "Incorrect security state: %d\n",
-                               nvdimm->sec.state);
-               return -EIO;
-       }
+       rc = check_security_state(nvdimm);
+       if (rc)
+               return rc;
 
        data = nvdimm_get_user_key_payload(nvdimm, keyid,
                        NVDIMM_BASE_KEY, &key);
@@ -301,15 +310,15 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid,
        nvdimm_put_key(newkey);
        nvdimm_put_key(key);
        if (pass_type == NVDIMM_MASTER)
-               nvdimm->sec.ext_state = nvdimm_security_state(nvdimm,
+               nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm,
                                NVDIMM_MASTER);
        else
-               nvdimm->sec.state = nvdimm_security_state(nvdimm,
+               nvdimm->sec.flags = nvdimm_security_flags(nvdimm,
                                NVDIMM_USER);
        return rc;
 }
 
-int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
+static int security_erase(struct nvdimm *nvdimm, unsigned int keyid,
                enum nvdimm_passphrase_type pass_type)
 {
        struct device *dev = &nvdimm->dev;
@@ -322,26 +331,14 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
        lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
 
        if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase
-                       || nvdimm->sec.state < 0)
+                       || !nvdimm->sec.flags)
                return -EOPNOTSUPP;
 
-       if (atomic_read(&nvdimm->busy)) {
-               dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
-               return -EBUSY;
-       }
-
-       if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
-               dev_dbg(dev, "Incorrect security state: %d\n",
-                               nvdimm->sec.state);
-               return -EIO;
-       }
-
-       if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
-               dev_dbg(dev, "Security operation in progress.\n");
-               return -EBUSY;
-       }
+       rc = check_security_state(nvdimm);
+       if (rc)
+               return rc;
 
-       if (nvdimm->sec.ext_state != NVDIMM_SECURITY_UNLOCKED
+       if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags)
                        && pass_type == NVDIMM_MASTER) {
                dev_dbg(dev,
                        "Attempt to secure erase in wrong master state.\n");
@@ -359,11 +356,11 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
                        rc == 0 ? "success" : "fail");
 
        nvdimm_put_key(key);
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
        return rc;
 }
 
-int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
+static int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
 {
        struct device *dev = &nvdimm->dev;
        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
@@ -375,29 +372,17 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
        lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
 
        if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite
-                       || nvdimm->sec.state < 0)
+                       || !nvdimm->sec.flags)
                return -EOPNOTSUPP;
 
-       if (atomic_read(&nvdimm->busy)) {
-               dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
-               return -EBUSY;
-       }
-
        if (dev->driver == NULL) {
                dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
                return -EINVAL;
        }
 
-       if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
-               dev_dbg(dev, "Incorrect security state: %d\n",
-                               nvdimm->sec.state);
-               return -EIO;
-       }
-
-       if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
-               dev_dbg(dev, "Security operation in progress.\n");
-               return -EBUSY;
-       }
+       rc = check_security_state(nvdimm);
+       if (rc)
+               return rc;
 
        data = nvdimm_get_user_key_payload(nvdimm, keyid,
                        NVDIMM_BASE_KEY, &key);
@@ -412,7 +397,7 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
        if (rc == 0) {
                set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
                set_bit(NDD_WORK_PENDING, &nvdimm->flags);
-               nvdimm->sec.state = NVDIMM_SECURITY_OVERWRITE;
+               set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags);
                /*
                 * Make sure we don't lose device while doing overwrite
                 * query.
@@ -443,7 +428,7 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
        tmo = nvdimm->sec.overwrite_tmo;
 
        if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite
-                       || nvdimm->sec.state < 0)
+                       || !nvdimm->sec.flags)
                return;
 
        rc = nvdimm->sec.ops->query_overwrite(nvdimm);
@@ -467,8 +452,8 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
        clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
        clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
        put_device(&nvdimm->dev);
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
-       nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
 }
 
 void nvdimm_security_overwrite_query(struct work_struct *work)
@@ -480,3 +465,85 @@ void nvdimm_security_overwrite_query(struct work_struct *work)
        __nvdimm_security_overwrite_query(nvdimm);
        nvdimm_bus_unlock(&nvdimm->dev);
 }
+
+#define OPS                                                    \
+       C( OP_FREEZE,           "freeze",               1),     \
+       C( OP_DISABLE,          "disable",              2),     \
+       C( OP_UPDATE,           "update",               3),     \
+       C( OP_ERASE,            "erase",                2),     \
+       C( OP_OVERWRITE,        "overwrite",            2),     \
+       C( OP_MASTER_UPDATE,    "master_update",        3),     \
+       C( OP_MASTER_ERASE,     "master_erase",         2)
+#undef C
+#define C(a, b, c) a
+enum nvdimmsec_op_ids { OPS };
+#undef C
+#define C(a, b, c) { b, c }
+static struct {
+       const char *name;
+       int args;
+} ops[] = { OPS };
+#undef C
+
+#define SEC_CMD_SIZE 32
+#define KEY_ID_SIZE 10
+
+ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len)
+{
+       struct nvdimm *nvdimm = to_nvdimm(dev);
+       ssize_t rc;
+       char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
+               nkeystr[KEY_ID_SIZE+1];
+       unsigned int key, newkey;
+       int i;
+
+       rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
+                       " %"__stringify(KEY_ID_SIZE)"s"
+                       " %"__stringify(KEY_ID_SIZE)"s",
+                       cmd, keystr, nkeystr);
+       if (rc < 1)
+               return -EINVAL;
+       for (i = 0; i < ARRAY_SIZE(ops); i++)
+               if (sysfs_streq(cmd, ops[i].name))
+                       break;
+       if (i >= ARRAY_SIZE(ops))
+               return -EINVAL;
+       if (ops[i].args > 1)
+               rc = kstrtouint(keystr, 0, &key);
+       if (rc >= 0 && ops[i].args > 2)
+               rc = kstrtouint(nkeystr, 0, &newkey);
+       if (rc < 0)
+               return rc;
+
+       if (i == OP_FREEZE) {
+               dev_dbg(dev, "freeze\n");
+               rc = nvdimm_security_freeze(nvdimm);
+       } else if (i == OP_DISABLE) {
+               dev_dbg(dev, "disable %u\n", key);
+               rc = security_disable(nvdimm, key);
+       } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
+               dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
+               rc = security_update(nvdimm, key, newkey, i == OP_UPDATE
+                               ? NVDIMM_USER : NVDIMM_MASTER);
+       } else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
+               dev_dbg(dev, "%s %u\n", ops[i].name, key);
+               if (atomic_read(&nvdimm->busy)) {
+                       dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
+                       return -EBUSY;
+               }
+               rc = security_erase(nvdimm, key, i == OP_ERASE
+                               ? NVDIMM_USER : NVDIMM_MASTER);
+       } else if (i == OP_OVERWRITE) {
+               dev_dbg(dev, "overwrite %u\n", key);
+               if (atomic_read(&nvdimm->busy)) {
+                       dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
+                       return -EBUSY;
+               }
+               rc = security_overwrite(nvdimm, key);
+       } else
+               return -EINVAL;
+
+       if (rc == 0)
+               rc = len;
+       return rc;
+}
index 7a64b3ddb40840aadf70c74595f16eb6a45fc19f..b6eddf912568555e36b78e4c383acd36d798fb71 100644 (file)
@@ -160,8 +160,11 @@ static inline struct nd_blk_region_desc *to_blk_region_desc(
 
 }
 
-enum nvdimm_security_state {
-       NVDIMM_SECURITY_ERROR = -1,
+/*
+ * Note that separate bits for locked + unlocked are defined so that
+ * 'flags == 0' corresponds to an error / not-supported state.
+ */
+enum nvdimm_security_bits {
        NVDIMM_SECURITY_DISABLED,
        NVDIMM_SECURITY_UNLOCKED,
        NVDIMM_SECURITY_LOCKED,
@@ -182,7 +185,7 @@ enum nvdimm_passphrase_type {
 };
 
 struct nvdimm_security_ops {
-       enum nvdimm_security_state (*state)(struct nvdimm *nvdimm,
+       unsigned long (*get_flags)(struct nvdimm *nvdimm,
                        enum nvdimm_passphrase_type pass_type);
        int (*freeze)(struct nvdimm *nvdimm);
        int (*change_key)(struct nvdimm *nvdimm,
index 2d4baf57822fae8e7111a89e63fa047f4d0638ec..57bd27dedf1f0c0410ce8d08693955f0e0a76ec8 100644 (file)
@@ -18,24 +18,13 @@ ssize_t security_show(struct device *dev,
         * For the test version we need to poll the "hardware" in order
         * to get the updated status for unlock testing.
         */
-       nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
-       nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
+       nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
 
-       switch (nvdimm->sec.state) {
-       case NVDIMM_SECURITY_DISABLED:
+       if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
                return sprintf(buf, "disabled\n");
-       case NVDIMM_SECURITY_UNLOCKED:
+       if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
                return sprintf(buf, "unlocked\n");
-       case NVDIMM_SECURITY_LOCKED:
+       if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
                return sprintf(buf, "locked\n");
-       case NVDIMM_SECURITY_FROZEN:
-               return sprintf(buf, "frozen\n");
-       case NVDIMM_SECURITY_OVERWRITE:
-               return sprintf(buf, "overwrite\n");
-       default:
-               return -ENOTTY;
-       }
-
        return -ENOTTY;
 }
-
index 507e6f4cbb53431bdcd2fcbbddc4c5a3f28790ad..bf6422a6af7ffb30dc0fea13d546f1f39446f010 100644 (file)
@@ -428,10 +428,9 @@ static int nd_intel_test_finish_query(struct nfit_test *t,
                        dev_dbg(dev, "%s: still verifying\n", __func__);
                        break;
                }
-
                dev_dbg(dev, "%s: transition out verify\n", __func__);
                fw->state = FW_STATE_UPDATED;
-               /* we are going to fall through if it's "done" */
+               /* fall through */
        case FW_STATE_UPDATED:
                nd_cmd->status = 0;
                /* bogus test version */