]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - block/sed-opal.c
Merge tag 'irqchip-fixes-5.6-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / block / sed-opal.c
index b4c761973ac102fda9f75a3f92c56e77f5a718cd..880cc57a5f6bd34ed26bc25df6ac091316bc117c 100644 (file)
@@ -149,6 +149,8 @@ static const u8 opaluid[][OPAL_UID_LENGTH] = {
                { 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x01 },
        [OPAL_ENTERPRISE_LOCKING_INFO_TABLE] =
                { 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00 },
+       [OPAL_DATASTORE] =
+               { 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00 },
 
        /* C_PIN_TABLE object ID's */
        [OPAL_C_PIN_MSID] =
@@ -1139,11 +1141,11 @@ static int generic_get_column(struct opal_dev *dev, const u8 *table,
  *
  * the result is provided in dev->resp->tok[4]
  */
-static int generic_get_table_info(struct opal_dev *dev, enum opal_uid table,
+static int generic_get_table_info(struct opal_dev *dev, const u8 *table_uid,
                                  u64 column)
 {
        u8 uid[OPAL_UID_LENGTH];
-       const unsigned int half = OPAL_UID_LENGTH/2;
+       const unsigned int half = OPAL_UID_LENGTH_HALF;
 
        /* sed-opal UIDs can be split in two halves:
         *  first:  actual table index
@@ -1152,7 +1154,7 @@ static int generic_get_table_info(struct opal_dev *dev, enum opal_uid table,
         * first part of the target table as relative index into that table
         */
        memcpy(uid, opaluid[OPAL_TABLE_TABLE], half);
-       memcpy(uid+half, opaluid[table], half);
+       memcpy(uid + half, table_uid, half);
 
        return generic_get_column(dev, uid, column);
 }
@@ -1221,6 +1223,75 @@ static int get_active_key(struct opal_dev *dev, void *data)
        return get_active_key_cont(dev);
 }
 
+static int generic_table_write_data(struct opal_dev *dev, const u64 data,
+                                   u64 offset, u64 size, const u8 *uid)
+{
+       const u8 __user *src = (u8 __user *)(uintptr_t)data;
+       u8 *dst;
+       u64 len;
+       size_t off = 0;
+       int err;
+
+       /* do we fit in the available space? */
+       err = generic_get_table_info(dev, uid, OPAL_TABLE_ROWS);
+       if (err) {
+               pr_debug("Couldn't get the table size\n");
+               return err;
+       }
+
+       len = response_get_u64(&dev->parsed, 4);
+       if (size > len || offset > len - size) {
+               pr_debug("Does not fit in the table (%llu vs. %llu)\n",
+                         offset + size, len);
+               return -ENOSPC;
+       }
+
+       /* do the actual transmission(s) */
+       while (off < size) {
+               err = cmd_start(dev, uid, opalmethod[OPAL_SET]);
+               add_token_u8(&err, dev, OPAL_STARTNAME);
+               add_token_u8(&err, dev, OPAL_WHERE);
+               add_token_u64(&err, dev, offset + off);
+               add_token_u8(&err, dev, OPAL_ENDNAME);
+
+               add_token_u8(&err, dev, OPAL_STARTNAME);
+               add_token_u8(&err, dev, OPAL_VALUES);
+
+               /*
+                * The bytestring header is either 1 or 2 bytes, so assume 2.
+                * There also needs to be enough space to accommodate the
+                * trailing OPAL_ENDNAME (1 byte) and tokens added by
+                * cmd_finalize.
+                */
+               len = min(remaining_size(dev) - (2+1+CMD_FINALIZE_BYTES_NEEDED),
+                         (size_t)(size - off));
+               pr_debug("Write bytes %zu+%llu/%llu\n", off, len, size);
+
+               dst = add_bytestring_header(&err, dev, len);
+               if (!dst)
+                       break;
+
+               if (copy_from_user(dst, src + off, len)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               dev->pos += len;
+
+               add_token_u8(&err, dev, OPAL_ENDNAME);
+               if (err)
+                       break;
+
+               err = finalize_and_send(dev, parse_and_check_status);
+               if (err)
+                       break;
+
+               off += len;
+       }
+
+       return err;
+}
+
 static int generic_lr_enable_disable(struct opal_dev *dev,
                                     u8 *uid, bool rle, bool wle,
                                     bool rl, bool wl)
@@ -1583,68 +1654,9 @@ static int set_mbr_enable_disable(struct opal_dev *dev, void *data)
 static int write_shadow_mbr(struct opal_dev *dev, void *data)
 {
        struct opal_shadow_mbr *shadow = data;
-       const u8 __user *src;
-       u8 *dst;
-       size_t off = 0;
-       u64 len;
-       int err = 0;
-
-       /* do we fit in the available shadow mbr space? */
-       err = generic_get_table_info(dev, OPAL_MBR, OPAL_TABLE_ROWS);
-       if (err) {
-               pr_debug("MBR: could not get shadow size\n");
-               return err;
-       }
-
-       len = response_get_u64(&dev->parsed, 4);
-       if (shadow->size > len || shadow->offset > len - shadow->size) {
-               pr_debug("MBR: does not fit in shadow (%llu vs. %llu)\n",
-                        shadow->offset + shadow->size, len);
-               return -ENOSPC;
-       }
-
-       /* do the actual transmission(s) */
-       src = (u8 __user *)(uintptr_t)shadow->data;
-       while (off < shadow->size) {
-               err = cmd_start(dev, opaluid[OPAL_MBR], opalmethod[OPAL_SET]);
-               add_token_u8(&err, dev, OPAL_STARTNAME);
-               add_token_u8(&err, dev, OPAL_WHERE);
-               add_token_u64(&err, dev, shadow->offset + off);
-               add_token_u8(&err, dev, OPAL_ENDNAME);
-
-               add_token_u8(&err, dev, OPAL_STARTNAME);
-               add_token_u8(&err, dev, OPAL_VALUES);
-
-               /*
-                * The bytestring header is either 1 or 2 bytes, so assume 2.
-                * There also needs to be enough space to accommodate the
-                * trailing OPAL_ENDNAME (1 byte) and tokens added by
-                * cmd_finalize.
-                */
-               len = min(remaining_size(dev) - (2+1+CMD_FINALIZE_BYTES_NEEDED),
-                         (size_t)(shadow->size - off));
-               pr_debug("MBR: write bytes %zu+%llu/%llu\n",
-                        off, len, shadow->size);
-
-               dst = add_bytestring_header(&err, dev, len);
-               if (!dst)
-                       break;
-               if (copy_from_user(dst, src + off, len))
-                       err = -EFAULT;
-               dev->pos += len;
-
-               add_token_u8(&err, dev, OPAL_ENDNAME);
-               if (err)
-                       break;
-
-               err = finalize_and_send(dev, parse_and_check_status);
-               if (err)
-                       break;
-
-               off += len;
-       }
 
-       return err;
+       return generic_table_write_data(dev, shadow->data, shadow->offset,
+                                       shadow->size, opaluid[OPAL_MBR]);
 }
 
 static int generic_pw_cmd(u8 *key, size_t key_len, u8 *cpin_uid,
@@ -1874,7 +1886,6 @@ static int activate_lsp(struct opal_dev *dev, void *data)
 {
        struct opal_lr_act *opal_act = data;
        u8 user_lr[OPAL_UID_LENGTH];
-       u8 uint_3 = 0x83;
        int err, i;
 
        err = cmd_start(dev, opaluid[OPAL_LOCKINGSP_UID],
@@ -1887,10 +1898,7 @@ static int activate_lsp(struct opal_dev *dev, void *data)
                        return err;
 
                add_token_u8(&err, dev, OPAL_STARTNAME);
-               add_token_u8(&err, dev, uint_3);
-               add_token_u8(&err, dev, 6);
-               add_token_u8(&err, dev, 0);
-               add_token_u8(&err, dev, 0);
+               add_token_u64(&err, dev, OPAL_SUM_SET_LIST);
 
                add_token_u8(&err, dev, OPAL_STARTLIST);
                add_token_bytestring(&err, dev, user_lr, OPAL_UID_LENGTH);
@@ -1957,6 +1965,113 @@ static int get_msid_cpin_pin(struct opal_dev *dev, void *data)
        return 0;
 }
 
+static int write_table_data(struct opal_dev *dev, void *data)
+{
+       struct opal_read_write_table *write_tbl = data;
+
+       return generic_table_write_data(dev, write_tbl->data, write_tbl->offset,
+                                       write_tbl->size, write_tbl->table_uid);
+}
+
+static int read_table_data_cont(struct opal_dev *dev)
+{
+       int err;
+       const char *data_read;
+
+       err = parse_and_check_status(dev);
+       if (err)
+               return err;
+
+       dev->prev_d_len = response_get_string(&dev->parsed, 1, &data_read);
+       dev->prev_data = (void *)data_read;
+       if (!dev->prev_data) {
+               pr_debug("%s: Couldn't read data from the table.\n", __func__);
+               return OPAL_INVAL_PARAM;
+       }
+
+       return 0;
+}
+
+/*
+ * IO_BUFFER_LENGTH = 2048
+ * sizeof(header) = 56
+ * No. of Token Bytes in the Response = 11
+ * MAX size of data that can be carried in response buffer
+ * at a time is : 2048 - (56 + 11) = 1981 = 0x7BD.
+ */
+#define OPAL_MAX_READ_TABLE (0x7BD)
+
+static int read_table_data(struct opal_dev *dev, void *data)
+{
+       struct opal_read_write_table *read_tbl = data;
+       int err;
+       size_t off = 0, max_read_size = OPAL_MAX_READ_TABLE;
+       u64 table_len, len;
+       u64 offset = read_tbl->offset, read_size = read_tbl->size - 1;
+       u8 __user *dst;
+
+       err = generic_get_table_info(dev, read_tbl->table_uid, OPAL_TABLE_ROWS);
+       if (err) {
+               pr_debug("Couldn't get the table size\n");
+               return err;
+       }
+
+       table_len = response_get_u64(&dev->parsed, 4);
+
+       /* Check if the user is trying to read from the table limits */
+       if (read_size > table_len || offset > table_len - read_size) {
+               pr_debug("Read size exceeds the Table size limits (%llu vs. %llu)\n",
+                         offset + read_size, table_len);
+               return -EINVAL;
+       }
+
+       while (off < read_size) {
+               err = cmd_start(dev, read_tbl->table_uid, opalmethod[OPAL_GET]);
+
+               add_token_u8(&err, dev, OPAL_STARTLIST);
+               add_token_u8(&err, dev, OPAL_STARTNAME);
+               add_token_u8(&err, dev, OPAL_STARTROW);
+               add_token_u64(&err, dev, offset + off); /* start row value */
+               add_token_u8(&err, dev, OPAL_ENDNAME);
+
+               add_token_u8(&err, dev, OPAL_STARTNAME);
+               add_token_u8(&err, dev, OPAL_ENDROW);
+
+               len = min(max_read_size, (size_t)(read_size - off));
+               add_token_u64(&err, dev, offset + off + len); /* end row value
+                                                              */
+               add_token_u8(&err, dev, OPAL_ENDNAME);
+               add_token_u8(&err, dev, OPAL_ENDLIST);
+
+               if (err) {
+                       pr_debug("Error building read table data command.\n");
+                       break;
+               }
+
+               err = finalize_and_send(dev, read_table_data_cont);
+               if (err)
+                       break;
+
+               /* len+1: This includes the NULL terminator at the end*/
+               if (dev->prev_d_len > len + 1) {
+                       err = -EOVERFLOW;
+                       break;
+               }
+
+               dst = (u8 __user *)(uintptr_t)read_tbl->data;
+               if (copy_to_user(dst + off, dev->prev_data, dev->prev_d_len)) {
+                       pr_debug("Error copying data to userspace\n");
+                       err = -EFAULT;
+                       break;
+               }
+               dev->prev_data = NULL;
+
+               off += len;
+       }
+
+       return err;
+}
+
 static int end_opal_session(struct opal_dev *dev, void *data)
 {
        int err = 0;
@@ -2443,6 +2558,68 @@ bool opal_unlock_from_suspend(struct opal_dev *dev)
 }
 EXPORT_SYMBOL(opal_unlock_from_suspend);
 
+static int opal_read_table(struct opal_dev *dev,
+                          struct opal_read_write_table *rw_tbl)
+{
+       const struct opal_step read_table_steps[] = {
+               { start_admin1LSP_opal_session, &rw_tbl->key },
+               { read_table_data, rw_tbl },
+               { end_opal_session, }
+       };
+       int ret = 0;
+
+       if (!rw_tbl->size)
+               return ret;
+
+       return execute_steps(dev, read_table_steps,
+                            ARRAY_SIZE(read_table_steps));
+}
+
+static int opal_write_table(struct opal_dev *dev,
+                           struct opal_read_write_table *rw_tbl)
+{
+       const struct opal_step write_table_steps[] = {
+               { start_admin1LSP_opal_session, &rw_tbl->key },
+               { write_table_data, rw_tbl },
+               { end_opal_session, }
+       };
+       int ret = 0;
+
+       if (!rw_tbl->size)
+               return ret;
+
+       return execute_steps(dev, write_table_steps,
+                            ARRAY_SIZE(write_table_steps));
+}
+
+static int opal_generic_read_write_table(struct opal_dev *dev,
+                                        struct opal_read_write_table *rw_tbl)
+{
+       int ret, bit_set;
+
+       mutex_lock(&dev->dev_lock);
+       setup_opal_dev(dev);
+
+       bit_set = fls64(rw_tbl->flags) - 1;
+       switch (bit_set) {
+       case OPAL_READ_TABLE:
+               ret = opal_read_table(dev, rw_tbl);
+               break;
+       case OPAL_WRITE_TABLE:
+               ret = opal_write_table(dev, rw_tbl);
+               break;
+       default:
+               pr_debug("Invalid bit set in the flag (%016llx).\n",
+                        rw_tbl->flags);
+               ret = -EINVAL;
+               break;
+       }
+
+       mutex_unlock(&dev->dev_lock);
+
+       return ret;
+}
+
 int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
 {
        void *p;
@@ -2505,6 +2682,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
        case IOC_OPAL_PSID_REVERT_TPR:
                ret = opal_reverttper(dev, p, true);
                break;
+       case IOC_OPAL_GENERIC_TABLE_RW:
+               ret = opal_generic_read_write_table(dev, p);
+               break;
        default:
                break;
        }