Merge tag 'x86_cache_for_6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
[linux-block.git] / block / sed-opal.c
index 38cc02b708ac7ba24f9f22a6f632e2da697b0f20..c18339446ef3796139249e1943020b1e99e6191e 100644 (file)
@@ -83,8 +83,10 @@ struct opal_dev {
        u16 comid;
        u32 hsn;
        u32 tsn;
-       u64 align;
+       u64 align; /* alignment granularity */
        u64 lowest_lba;
+       u32 logical_block_size;
+       u8  align_required; /* ALIGN: 0 or 1 */
 
        size_t pos;
        u8 *cmd;
@@ -409,6 +411,8 @@ static void check_geometry(struct opal_dev *dev, const void *data)
 
        dev->align = be64_to_cpu(geo->alignment_granularity);
        dev->lowest_lba = be64_to_cpu(geo->lowest_aligned_lba);
+       dev->logical_block_size = be32_to_cpu(geo->logical_block_size);
+       dev->align_required = geo->reserved01 & 1;
 }
 
 static int execute_step(struct opal_dev *dev,
@@ -1149,12 +1153,8 @@ static int finalize_and_send(struct opal_dev *dev, cont_fn cont)
        return opal_send_recv(dev, cont);
 }
 
-/*
- * request @column from table @table on device @dev. On success, the column
- * data will be available in dev->resp->tok[4]
- */
-static int generic_get_column(struct opal_dev *dev, const u8 *table,
-                             u64 column)
+static int generic_get_columns(struct opal_dev *dev, const u8 *table,
+                              u64 start_column, u64 end_column)
 {
        int err;
 
@@ -1164,12 +1164,12 @@ static int generic_get_column(struct opal_dev *dev, const u8 *table,
 
        add_token_u8(&err, dev, OPAL_STARTNAME);
        add_token_u8(&err, dev, OPAL_STARTCOLUMN);
-       add_token_u64(&err, dev, column);
+       add_token_u64(&err, dev, start_column);
        add_token_u8(&err, dev, OPAL_ENDNAME);
 
        add_token_u8(&err, dev, OPAL_STARTNAME);
        add_token_u8(&err, dev, OPAL_ENDCOLUMN);
-       add_token_u64(&err, dev, column);
+       add_token_u64(&err, dev, end_column);
        add_token_u8(&err, dev, OPAL_ENDNAME);
 
        add_token_u8(&err, dev, OPAL_ENDLIST);
@@ -1180,6 +1180,16 @@ static int generic_get_column(struct opal_dev *dev, const u8 *table,
        return finalize_and_send(dev, parse_and_check_status);
 }
 
+/*
+ * request @column from table @table on device @dev. On success, the column
+ * data will be available in dev->resp->tok[4]
+ */
+static int generic_get_column(struct opal_dev *dev, const u8 *table,
+                             u64 column)
+{
+       return generic_get_columns(dev, table, column, column);
+}
+
 /*
  * see TCG SAS 5.3.2.3 for a description of the available columns
  *
@@ -1439,6 +1449,129 @@ static int setup_locking_range(struct opal_dev *dev, void *data)
        return finalize_and_send(dev, parse_and_check_status);
 }
 
+static int response_get_column(const struct parsed_resp *resp,
+                              int *iter,
+                              u8 column,
+                              u64 *value)
+{
+       const struct opal_resp_tok *tok;
+       int n = *iter;
+       u64 val;
+
+       tok = response_get_token(resp, n);
+       if (IS_ERR(tok))
+               return PTR_ERR(tok);
+
+       if (!response_token_matches(tok, OPAL_STARTNAME)) {
+               pr_debug("Unexpected response token type %d.\n", n);
+               return OPAL_INVAL_PARAM;
+       }
+       n++;
+
+       if (response_get_u64(resp, n) != column) {
+               pr_debug("Token %d does not match expected column %u.\n",
+                        n, column);
+               return OPAL_INVAL_PARAM;
+       }
+       n++;
+
+       val = response_get_u64(resp, n);
+       n++;
+
+       tok = response_get_token(resp, n);
+       if (IS_ERR(tok))
+               return PTR_ERR(tok);
+
+       if (!response_token_matches(tok, OPAL_ENDNAME)) {
+               pr_debug("Unexpected response token type %d.\n", n);
+               return OPAL_INVAL_PARAM;
+       }
+       n++;
+
+       *value = val;
+       *iter = n;
+
+       return 0;
+}
+
+static int locking_range_status(struct opal_dev *dev, void *data)
+{
+       u8 lr_buffer[OPAL_UID_LENGTH];
+       u64 resp;
+       bool rlocked, wlocked;
+       int err, tok_n = 2;
+       struct opal_lr_status *lrst = data;
+
+       err = build_locking_range(lr_buffer, sizeof(lr_buffer),
+                                 lrst->session.opal_key.lr);
+       if (err)
+               return err;
+
+       err = generic_get_columns(dev, lr_buffer, OPAL_RANGESTART,
+                                 OPAL_WRITELOCKED);
+       if (err) {
+               pr_debug("Couldn't get lr %u table columns %d to %d.\n",
+                        lrst->session.opal_key.lr, OPAL_RANGESTART,
+                        OPAL_WRITELOCKED);
+               return err;
+       }
+
+       /* range start */
+       err = response_get_column(&dev->parsed, &tok_n, OPAL_RANGESTART,
+                                 &lrst->range_start);
+       if (err)
+               return err;
+
+       /* range length */
+       err = response_get_column(&dev->parsed, &tok_n, OPAL_RANGELENGTH,
+                                 &lrst->range_length);
+       if (err)
+               return err;
+
+       /* RLE */
+       err = response_get_column(&dev->parsed, &tok_n, OPAL_READLOCKENABLED,
+                                 &resp);
+       if (err)
+               return err;
+
+       lrst->RLE = !!resp;
+
+       /* WLE */
+       err = response_get_column(&dev->parsed, &tok_n, OPAL_WRITELOCKENABLED,
+                                 &resp);
+       if (err)
+               return err;
+
+       lrst->WLE = !!resp;
+
+       /* read locked */
+       err = response_get_column(&dev->parsed, &tok_n, OPAL_READLOCKED, &resp);
+       if (err)
+               return err;
+
+       rlocked = !!resp;
+
+       /* write locked */
+       err = response_get_column(&dev->parsed, &tok_n, OPAL_WRITELOCKED, &resp);
+       if (err)
+               return err;
+
+       wlocked = !!resp;
+
+       /* opal_lock_state can not map 'read locked' only state. */
+       lrst->l_state = OPAL_RW;
+       if (rlocked && wlocked)
+               lrst->l_state = OPAL_LK;
+       else if (wlocked)
+               lrst->l_state = OPAL_RO;
+       else if (rlocked) {
+               pr_debug("Can not report read locked only state.\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int start_generic_opal_session(struct opal_dev *dev,
                                      enum opal_uid auth,
                                      enum opal_uid sp_type,
@@ -2636,6 +2769,33 @@ static int opal_setup_locking_range(struct opal_dev *dev,
        return ret;
 }
 
+static int opal_locking_range_status(struct opal_dev *dev,
+                         struct opal_lr_status *opal_lrst,
+                         void __user *data)
+{
+       const struct opal_step lr_steps[] = {
+               { start_auth_opal_session, &opal_lrst->session },
+               { locking_range_status, opal_lrst },
+               { end_opal_session, }
+       };
+       int ret;
+
+       mutex_lock(&dev->dev_lock);
+       setup_opal_dev(dev);
+       ret = execute_steps(dev, lr_steps, ARRAY_SIZE(lr_steps));
+       mutex_unlock(&dev->dev_lock);
+
+       /* skip session info when copying back to uspace */
+       if (!ret && copy_to_user(data + offsetof(struct opal_lr_status, range_start),
+                               (void *)opal_lrst + offsetof(struct opal_lr_status, range_start),
+                               sizeof(*opal_lrst) - offsetof(struct opal_lr_status, range_start))) {
+               pr_debug("Error copying status to userspace\n");
+               return -EFAULT;
+       }
+
+       return ret;
+}
+
 static int opal_set_new_pw(struct opal_dev *dev, struct opal_new_pw *opal_pw)
 {
        const struct opal_step pw_steps[] = {
@@ -2800,6 +2960,26 @@ static int opal_get_status(struct opal_dev *dev, void __user *data)
        return 0;
 }
 
+static int opal_get_geometry(struct opal_dev *dev, void __user *data)
+{
+       struct opal_geometry geo = {0};
+
+       if (check_opal_support(dev))
+               return -EINVAL;
+
+       geo.align = dev->align_required;
+       geo.logical_block_size = dev->logical_block_size;
+       geo.alignment_granularity =  dev->align;
+       geo.lowest_aligned_lba = dev->lowest_lba;
+
+       if (copy_to_user(data, &geo, sizeof(geo))) {
+               pr_debug("Error copying geometry data to userspace\n");
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
 int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
 {
        void *p;
@@ -2870,6 +3050,12 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
        case IOC_OPAL_GET_STATUS:
                ret = opal_get_status(dev, arg);
                break;
+       case IOC_OPAL_GET_LR_STATUS:
+               ret = opal_locking_range_status(dev, p, arg);
+               break;
+       case IOC_OPAL_GET_GEOMETRY:
+               ret = opal_get_geometry(dev, arg);
+               break;
        default:
                break;
        }