]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/crypto/caam/ctrl.c
crypto: caam - enable instantiation of all RNG4 state handles
[karo-tx-linux.git] / drivers / crypto / caam / ctrl.c
index 29cbec18a3a8d416afdbfc7b3aa72f48302c122e..26438cd126859e75638c893ad6e959c3baf9e9ef 100644 (file)
  * Descriptor to instantiate RNG State Handle 0 in normal mode and
  * load the JDKEK, TDKEK and TDSK registers
  */
-static void build_instantiation_desc(u32 *desc)
+static void build_instantiation_desc(u32 *desc, int handle, int do_sk)
 {
-       u32 *jump_cmd;
+       u32 *jump_cmd, op_flags;
 
        init_job_desc(desc, 0);
 
+       op_flags = OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
+                       (handle << OP_ALG_AAI_SHIFT) | OP_ALG_AS_INIT;
+
        /* INIT RNG in non-test mode */
-       append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
-                        OP_ALG_AS_INIT);
+       append_operation(desc, op_flags);
 
-       /* wait for done */
-       jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1);
-       set_jump_tgt_here(desc, jump_cmd);
+       if (!handle && do_sk) {
+               /*
+                * For SH0, Secure Keys must be generated as well
+                */
 
-       /*
-        * load 1 to clear written reg:
-        * resets the done interrupt and returns the RNG to idle.
-        */
-       append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW);
+               /* wait for done */
+               jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1);
+               set_jump_tgt_here(desc, jump_cmd);
 
-       /* generate secure keys (non-test) */
-       append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
-                        OP_ALG_AAI_RNG4_SK);
+               /*
+                * load 1 to clear written reg:
+                * resets the done interrrupt and returns the RNG to idle.
+                */
+               append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW);
+
+               /* Initialize State Handle  */
+               append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
+                                OP_ALG_AAI_RNG4_SK);
+       }
 
        append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TYPE_HALT);
 }
 
 /* Descriptor for deinstantiation of State Handle 0 of the RNG block. */
-static void build_deinstantiation_desc(u32 *desc)
+static void build_deinstantiation_desc(u32 *desc, int handle)
 {
        init_job_desc(desc, 0);
 
        /* Uninstantiate State Handle 0 */
        append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
-                        OP_ALG_AS_INITFINAL);
+                        (handle << OP_ALG_AAI_SHIFT) | OP_ALG_AS_INITFINAL);
 
        append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TYPE_HALT);
 }
@@ -60,11 +68,14 @@ static void build_deinstantiation_desc(u32 *desc)
  * run_descriptor_deco0 - runs a descriptor on DECO0, under direct control of
  *                       the software (no JR/QI used).
  * @ctrldev - pointer to device
+ * @status - descriptor status, after being run
+ *
  * Return: - 0 if no error occurred
  *        - -ENODEV if the DECO couldn't be acquired
  *        - -EAGAIN if an error occurred while executing the descriptor
  */
-static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc)
+static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc,
+                                       u32 *status)
 {
        struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev);
        struct caam_full __iomem *topregs;
@@ -113,6 +124,9 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc)
                cpu_relax();
        } while ((deco_dbg_reg & DESC_DBG_DECO_STAT_VALID) && --timeout);
 
+       *status = rd_reg32(&topregs->deco.op_status_hi) &
+                 DECO_OP_STATUS_HI_ERR_MASK;
+
        /* Mark the DECO as free */
        clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE);
 
@@ -126,6 +140,14 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc)
  * instantiate_rng - builds and executes a descriptor on DECO0,
  *                  which initializes the RNG block.
  * @ctrldev - pointer to device
+ * @state_handle_mask - bitmask containing the instantiation status
+ *                     for the RNG4 state handles which exist in
+ *                     the RNG4 block: 1 if it's been instantiated
+ *                     by an external entry, 0 otherwise.
+ * @gen_sk  - generate data to be loaded into the JDKEK, TDKEK and TDSK;
+ *           Caution: this can be done only once; if the keys need to be
+ *           regenerated, a POR is required
+ *
  * Return: - 0 if no error occurred
  *        - -ENOMEM if there isn't enough memory to allocate the descriptor
  *        - -ENODEV if DECO0 couldn't be acquired
@@ -133,19 +155,56 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc)
  *           f.i. there was a RNG hardware error due to not "good enough"
  *           entropy being aquired.
  */
-static int instantiate_rng(struct device *ctrldev)
+static int instantiate_rng(struct device *ctrldev, int state_handle_mask,
+                          int gen_sk)
 {
-       u32 *desc;
-       int ret = 0;
+       struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev);
+       struct caam_full __iomem *topregs;
+       struct rng4tst __iomem *r4tst;
+       u32 *desc, status, rdsta_val;
+       int ret = 0, sh_idx;
+
+       topregs = (struct caam_full __iomem *)ctrlpriv->ctrl;
+       r4tst = &topregs->ctrl.r4tst[0];
 
        desc = kmalloc(CAAM_CMD_SZ * 7, GFP_KERNEL);
        if (!desc)
                return -ENOMEM;
-       /* Create the descriptor for instantiating RNG State Handle 0 */
-       build_instantiation_desc(desc);
 
-       /* Try to run it through DECO0 */
-       ret = run_descriptor_deco0(ctrldev, desc);
+       for (sh_idx = 0; sh_idx < RNG4_MAX_HANDLES; sh_idx++) {
+               /*
+                * If the corresponding bit is set, this state handle
+                * was initialized by somebody else, so it's left alone.
+                */
+               if ((1 << sh_idx) & state_handle_mask)
+                       continue;
+
+               /* Create the descriptor for instantiating RNG State Handle */
+               build_instantiation_desc(desc, sh_idx, gen_sk);
+
+               /* Try to run it through DECO0 */
+               ret = run_descriptor_deco0(ctrldev, desc, &status);
+
+               /*
+                * If ret is not 0, or descriptor status is not 0, then
+                * something went wrong. No need to try the next state
+                * handle (if available), bail out here.
+                * Also, if for some reason, the State Handle didn't get
+                * instantiated although the descriptor has finished
+                * without any error (HW optimizations for later
+                * CAAM eras), then try again.
+                */
+               rdsta_val =
+                       rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & RDSTA_IFMASK;
+               if (status || !(rdsta_val & (1 << sh_idx)))
+                       ret = -EAGAIN;
+               if (ret)
+                       break;
+
+               dev_info(ctrldev, "Instantiated RNG4 SH%d\n", sh_idx);
+               /* Clear the contents before recreating the descriptor */
+               memset(desc, 0x00, CAAM_CMD_SZ * 7);
+       }
 
        kfree(desc);
 
@@ -156,29 +215,49 @@ static int instantiate_rng(struct device *ctrldev)
  * deinstantiate_rng - builds and executes a descriptor on DECO0,
  *                    which deinitializes the RNG block.
  * @ctrldev - pointer to device
+ * @state_handle_mask - bitmask containing the instantiation status
+ *                     for the RNG4 state handles which exist in
+ *                     the RNG4 block: 1 if it's been instantiated
  *
  * Return: - 0 if no error occurred
  *        - -ENOMEM if there isn't enough memory to allocate the descriptor
  *        - -ENODEV if DECO0 couldn't be acquired
  *        - -EAGAIN if an error occurred when executing the descriptor
  */
-static int deinstantiate_rng(struct device *ctrldev)
+static int deinstantiate_rng(struct device *ctrldev, int state_handle_mask)
 {
-       u32 *desc;
-       int i, ret = 0;
+       u32 *desc, status;
+       int sh_idx, ret = 0;
 
        desc = kmalloc(CAAM_CMD_SZ * 3, GFP_KERNEL);
        if (!desc)
                return -ENOMEM;
 
-       /* Create the descriptor for deinstantating RNG State Handle 0 */
-       build_deinstantiation_desc(desc);
-
-       /* Try to run it through DECO0 */
-       ret = run_descriptor_deco0(ctrldev, desc);
-
-       if (ret)
-               dev_err(ctrldev, "failed to deinstantiate RNG\n");
+       for (sh_idx = 0; sh_idx < RNG4_MAX_HANDLES; sh_idx++) {
+               /*
+                * If the corresponding bit is set, then it means the state
+                * handle was initialized by us, and thus it needs to be
+                * deintialized as well
+                */
+               if ((1 << sh_idx) & state_handle_mask) {
+                       /*
+                        * Create the descriptor for deinstantating this state
+                        * handle
+                        */
+                       build_deinstantiation_desc(desc, sh_idx);
+
+                       /* Try to run it through DECO0 */
+                       ret = run_descriptor_deco0(ctrldev, desc, &status);
+
+                       if (ret || status) {
+                               dev_err(ctrldev,
+                                       "Failed to deinstantiate RNG4 SH%d\n",
+                                       sh_idx);
+                               break;
+                       }
+                       dev_info(ctrldev, "Deinstantiated RNG4 SH%d\n", sh_idx);
+               }
+       }
 
        kfree(desc);
 
@@ -204,9 +283,9 @@ static int caam_remove(struct platform_device *pdev)
                irq_dispose_mapping(jrpriv->irq);
        }
 
-       /* De-initialize RNG if it was initialized by this driver. */
-       if (ctrlpriv->rng4_init)
-               deinstantiate_rng(ctrldev);
+       /* De-initialize RNG state handles initialized by this driver. */
+       if (ctrlpriv->rng4_sh_init)
+               deinstantiate_rng(ctrldev, ctrlpriv->rng4_sh_init);
 
        /* Shut down debug views */
 #ifdef CONFIG_DEBUG_FS
@@ -306,7 +385,7 @@ EXPORT_SYMBOL(caam_get_era);
 /* Probe routine for CAAM top (controller) level */
 static int caam_probe(struct platform_device *pdev)
 {
-       int ret, ring, rspec, ent_delay = RTSDCTL_ENT_DLY_MIN;
+       int ret, ring, rspec, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN;
        u64 caam_id;
        struct device *dev;
        struct device_node *nprop, *np;
@@ -414,20 +493,53 @@ static int caam_probe(struct platform_device *pdev)
         * If SEC has RNG version >= 4 and RNG state handle has not been
         * already instantiated, do RNG instantiation
         */
-       if ((cha_vid & CHA_ID_RNG_MASK) >> CHA_ID_RNG_SHIFT >= 4 &&
-           !(rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & RDSTA_IF0)) {
+       if ((cha_vid & CHA_ID_RNG_MASK) >> CHA_ID_RNG_SHIFT >= 4) {
+               ctrlpriv->rng4_sh_init =
+                       rd_reg32(&topregs->ctrl.r4tst[0].rdsta);
+               /*
+                * If the secure keys (TDKEK, JDKEK, TDSK), were already
+                * generated, signal this to the function that is instantiating
+                * the state handles. An error would occur if RNG4 attempts
+                * to regenerate these keys before the next POR.
+                */
+               gen_sk = ctrlpriv->rng4_sh_init & RDSTA_SKVN ? 0 : 1;
+               ctrlpriv->rng4_sh_init &= RDSTA_IFMASK;
                do {
-                       kick_trng(pdev, ent_delay);
-                       ret = instantiate_rng(dev);
-                       ent_delay += 400;
+                       int inst_handles =
+                               rd_reg32(&topregs->ctrl.r4tst[0].rdsta) &
+                                                               RDSTA_IFMASK;
+                       /*
+                        * If either SH were instantiated by somebody else
+                        * (e.g. u-boot) then it is assumed that the entropy
+                        * parameters are properly set and thus the function
+                        * setting these (kick_trng(...)) is skipped.
+                        * Also, if a handle was instantiated, do not change
+                        * the TRNG parameters.
+                        */
+                       if (!(ctrlpriv->rng4_sh_init || inst_handles)) {
+                               kick_trng(pdev, ent_delay);
+                               ent_delay += 400;
+                       }
+                       /*
+                        * if instantiate_rng(...) fails, the loop will rerun
+                        * and the kick_trng(...) function will modfiy the
+                        * upper and lower limits of the entropy sampling
+                        * interval, leading to a sucessful initialization of
+                        * the RNG.
+                        */
+                       ret = instantiate_rng(dev, inst_handles,
+                                             gen_sk);
                } while ((ret == -EAGAIN) && (ent_delay < RTSDCTL_ENT_DLY_MAX));
                if (ret) {
                        dev_err(dev, "failed to instantiate RNG");
                        caam_remove(pdev);
                        return ret;
                }
-
-               ctrlpriv->rng4_init = 1;
+               /*
+                * Set handles init'ed by this module as the complement of the
+                * already initialized ones
+                */
+               ctrlpriv->rng4_sh_init = ~ctrlpriv->rng4_sh_init & RDSTA_IFMASK;
 
                /* Enable RDB bit so that RNG works faster */
                setbits32(&topregs->ctrl.scfgr, SCFGR_RDBENABLE);