[PATCH] picoxcell_crypto: add support for the picoxcell crypto engines

Jamie Iles jamie at jamieiles.com
Fri Feb 11 06:57:47 EST 2011


On Thu, Feb 10, 2011 at 10:09:16PM -0600, Kim Phillips wrote:
> On Tue, 8 Feb 2011 15:56:16 +0000
> Jamie Iles <jamie at jamieiles.com> wrote:
> 
> > Picochip picoXcell devices have two crypto engines, one targeted
> > at IPSEC offload and the other at WCDMA layer 2 ciphering.
> >
> > Cc: Herbert Xu <herbert at gondor.apana.org.au>
> > Signed-off-by: Jamie Iles <jamie at jamieiles.com>
> > ---
> 
> nice driver ;).  Have a couple of comments though.

Hi Kim,

Thanks for the great review!

> 
> > +     help
> > +       This option enables support for the hardware offload engines in the
> > +       Picochip picoXcell SoC devices. Select this for IPSEC ESP offload
> > +       and for 3gpp Layer 2 ciphering support.
> 
> it'd be nice to mention what name the module will have.

Ok, will add.

> 
> > +#define SPACC_CRYPTO_AES_MAX_KEY_LEN 32
> > +#define SPACC_CRYPTO_AES_IV_LEN              16
> > +#define SPACC_CRYPTO_DES_IV_LEN              8
> 
> these are identical to algorithm-generic symbolic constants
> AES_MAX_KEY_SIZE, [AD]ES_BLOCK_SIZE - why not use them instead?

I wasn't aware of these constants but yes, that's much better.
> 
> > +struct spacc_generic_ctx;
> 
> this declaration isn't used prior to its definition, so it's not needed.

Ok, will remove.

> > +/* DDT format. This must match the hardware DDT format exactly. */
> > +struct spacc_ddt {
> > +     u32 p, len;
> 
> type-consistency: p should be a dma_addr_t

The reason I used a u32 was the the engine descriptor format is two 32 
bit words so I was trying to be explicit but I'll change this to 
dma_addr_t.

> 
> > +     /* AEAD specifc bits. */
> 
> specific

Good spot!

> > +static inline struct spacc_ablk_ctx *
> > +to_spacc_ablk_ctx(struct spacc_generic_ctx *ctx)
> > +{
> > +     return ctx ? container_of(ctx, struct spacc_ablk_ctx, generic) : NULL;
> > +}
> > +
> > +static inline struct spacc_aead_ctx *
> > +to_spacc_aead_ctx(struct spacc_generic_ctx *ctx)
> > +{
> > +     return ctx ? container_of(ctx, struct spacc_aead_ctx, generic) : NULL;
> > +}
> 
> these aren't being used anywhere.

Ok, will remove.

> 
> > +static inline struct spacc_alg *to_spacc_alg(struct crypto_alg *alg);
> 
> define it here - forward declarations should only be necessary when
> dealing with circular dependencies.

Hmm, not sure why I didn't do that originally!  Will change.

> 
> > +/*
> > + * Take a crypto request and scatterlists for the data and turn them into DDTs
> > + * for passing to the crypto engines. This also DMA maps the data so that the
> > + * crypto engines can DMA to/from them.
> > + */
> > +static struct spacc_ddt *spacc_sg_to_ddt(struct spacc_engine *engine,
> > +                                      struct scatterlist *payload,
> > +                                      unsigned nbytes,
> > +                                      enum dma_data_direction dir,
> > +                                      dma_addr_t *ddt_phys)
> > +{
> > +     unsigned nents, mapped_ents;
> > +     struct scatterlist *cur;
> > +     struct spacc_ddt *ddt;
> > +     int i;
> > +
> > +     nents = sg_count(payload, nbytes);
> > +     mapped_ents = dma_map_sg(engine->dev, payload, nents, dir);
> > +
> > +     if (mapped_ents + 1 > MAX_DDT_LEN) {
> > +             dma_unmap_sg(engine->dev, payload, nents, dir);
> > +             return NULL;
> > +     }
> > +
> > +     ddt = dma_pool_alloc(engine->req_pool, GFP_ATOMIC, ddt_phys);
> > +     if (ddt) {
> > +             for_each_sg(payload, cur, mapped_ents, i) {
> > +                     ddt[i].p = sg_dma_address(cur);
> > +                     ddt[i].len = sg_dma_len(cur);
> > +             }
> > +
> > +             ddt[mapped_ents].p = 0;
> > +             ddt[mapped_ents].len = 0;
> > +     } else {
> > +             dma_unmap_sg(engine->dev, payload, nents, dir);
> > +             ddt = NULL;
> > +     }
> > +
> > +     return ddt;
> > +}
> 
> easier to read would be:
> 
>         if (mapped_ents + 1 > MAX_DDT_LEN)
>                 goto out;
> 
>         ddt = dma_pool_alloc(engine->req_pool, GFP_ATOMIC, ddt_phys);
>         if (!ddt)
>                 goto out;
> 
>         for_each_sg(payload, cur, mapped_ents, i) {
>                 ddt[i].p = sg_dma_address(cur);
>                 ddt[i].len = sg_dma_len(cur);
>         }
>         ddt[mapped_ents].p = 0;
>         ddt[mapped_ents].len = 0;
> 
>         return ddt;
> 
> out:
>         dma_unmap_sg(engine->dev, payload, nents, dir);
>         return NULL;
> }
> 
> even more so by moving ddt_set() above it, and then using ddt_set() to
> assign the p, len pairs.

Yes, that's much cleaner.

> 
> > +static inline void ddt_set(struct spacc_ddt *ddt, unsigned long phys,
> 
> phys should be dma_addr_t

Ok.

> 
> > +static int spacc_aead_make_ddts(struct spacc_req *req, u8 *giv)
> > +{
> > +     struct aead_request *areq = container_of(req->req, struct aead_request,
> > +                                              base);
> > +     struct spacc_alg *alg = to_spacc_alg(req->req->tfm->__crt_alg);
> > +     struct spacc_engine *engine = req->engine;
> > +     struct spacc_ddt *src_ddt, *dst_ddt;
> > +     unsigned ivsize = alg->alg.cra_aead.ivsize;
> 
> no need to go through all those hoops to get to the ivsize - use helper
> fns crypto_aead_reqtfm() and crypto_aead_ivsize(), as is done at the
> callsite, or just pass it in from there.

Ok, will do.

> 
> > +static int spacc_aead_des_setkey(struct crypto_aead *aead, const u8 *key,
> > +                              unsigned int len)
> > +{
> > +     struct crypto_tfm *tfm = crypto_aead_tfm(aead);
> > +     struct spacc_aead_ctx *ctx = crypto_tfm_ctx(tfm);
> > +     int err = 0;
> > +     u32 tmp[DES_EXPKEY_WORDS];
> > +
> > +     err = des_ekey(tmp, key);
> > +     if (unlikely(!err) &&
> 
> might want to change the name of the variable err here to something
> like ret or is_weak so as to not mislead the reader.
> 
> > +         (crypto_aead_get_flags(aead)) & CRYPTO_TFM_REQ_WEAK_KEY) {
> > +             tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
> > +             return -EINVAL;
> > +     }
> > +     err = 0;
> > +
> > +     memcpy(ctx->cipher_key, key, len);
> > +     ctx->cipher_key_len = len;
> > +
> > +     return err;
> 
> actually, it doesn't look like this fn needs a return variable
> at all.

Ok, I'll get rid of err and put the des_ekey() call into the 
conditional.

> > +/* Set the key for the AES block cipher component of the AEAD 
> > transform. */
> > +static int spacc_aead_aes_setkey(struct crypto_aead *aead, const u8 *key,
> > +                              unsigned int len)
> > +{
> > +     struct crypto_tfm *tfm = crypto_aead_tfm(aead);
> > +     struct spacc_aead_ctx *ctx = crypto_tfm_ctx(tfm);
> > +     int err;
> > +
> > +     /*
> > +      * IPSec engine only supports 128 and 256 bit AES keys. If we get a
> > +      * request for any other size (192 bits) then we need to do a software
> > +      * fallback.
> > +      */
> > +     if (!(16 == len || 32 == len)) {
> 
> if (len != AES_KEYSIZE_128 && len != AES_KEYSIZE_256)

Ok, I'll clean up all of these uses.

> > +             /*
> > +              * Set the fallback transform to use the same request flags as
> > +              * the hardware transform.
> > +              */
> > +             ctx->sw_cipher->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
> > +             ctx->sw_cipher->base.crt_flags |=
> > +                     (tfm->crt_flags & CRYPTO_TFM_REQ_MASK);
> 
> parens not needed.

Ok.

> > +             err = crypto_aead_setkey(ctx->sw_cipher, key, len);
> > +     } else {
> > +             memcpy(ctx->cipher_key, key, len);
> > +             ctx->cipher_key_len = len;
> > +             err = 0;
> > +     }
> > +
> > +     return err;
> 
> 	return crypto_aead_setkey(ctx->sw_cipher, key, len);
> }
> memcpy(ctx->cipher_key, key, len);
> ctx->cipher_key_len = len;
> 
> return 0;

Ok.

> > +static int spacc_aead_setkey(struct crypto_aead *tfm, const u8 *key,
> > +                          unsigned int keylen)
> > +{
> > +     struct spacc_aead_ctx *ctx = crypto_aead_ctx(tfm);
> > +     struct spacc_alg *alg = to_spacc_alg(tfm->base.__crt_alg);
> > +     struct rtattr *rta = (void *)key;
> > +     struct crypto_authenc_key_param *param;
> > +     unsigned int authkeylen, enckeylen;
> > +     int err = -EINVAL;
> > +
> > +     if (!RTA_OK(rta, keylen))
> > +             goto badkey;
> > +
> > +     if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
> > +             goto badkey;
> > +
> > +     if (RTA_PAYLOAD(rta) < sizeof(*param))
> > +             goto badkey;
> 
> I'm not sure, but it should be safe to remove the above three checks -
> they cause a false badkey failure if the keys aren't within an rtattr
> struct, which, e.g., something like testmgr.c wouldn't do.
> 
> > +     param = RTA_DATA(rta);
> > +     enckeylen = be32_to_cpu(param->enckeylen);
> > +
> > +     key += RTA_ALIGN(rta->rta_len);
> > +     keylen -= RTA_ALIGN(rta->rta_len);
> 
> actually, I doubt crypto drivers should be including rtnetlink.h at
> all...but it's probably ok for now - talitos still does :)

Yes, it doesn't seem the nicest way to pass the keys.  ixp4xx and 
crypto/authenc.c do the same thing (including the first 3 checks).  
Perhaps this is worth refactoring into a generic 
crypto_authenc_get_keys() helper?

> > +     if ((spacc_alg->ctrl_default & SPACC_CRYPTO_ALG_MASK) ==
> > +         SPA_CTRL_CIPH_ALG_AES &&
> > +         !(16 == ctx->cipher_key_len || 32 == ctx->cipher_key_len))
> 
> as above, please use symbolic equivalents

Ok.

> > +static void spacc_aead_complete(struct spacc_req *req)
> > +{
> > +     spacc_aead_free_ddts(req);
> > +
> > +     if (req->req->complete)
> > +             req->req->complete(req->req, req->result);
> 
> when is there not a completion function?

Ok, I'll remove that check.

> > +     /* Set the source and destination DDT pointers. */
> > +     writel((u32)req->src_addr, engine->regs + SPA_SRC_PTR_REG_OFFSET);
> > +     writel((u32)req->dst_addr, engine->regs + SPA_DST_PTR_REG_OFFSET);
> 
> cast necessary?

No, probably not.  I'll double check and remove if ok.

> > +     ctrl = spacc_alg->ctrl_default;
> > +     ctrl |= ((req->ctx_id << SPA_CTRL_CTX_IDX) |
> > +              (1 << SPA_CTRL_ICV_APPEND) |
> > +              (req->is_encrypt ? (1 << SPA_CTRL_ENCRYPT_IDX) : 0) |
> > +              (req->is_encrypt ? (1 << SPA_CTRL_AAD_COPY) : 0));
> > +     if (!req->is_encrypt)
> > +             ctrl |= (1 << SPA_CTRL_KEY_EXP);
> 
> ctrl = spacc_alg->ctrl_default | (req->ctx_id << SPA_CTRL_CTX_IDX) |
>        (1 << SPA_CTRL_ICV_APPEND);
> 
> if (req->is_encrypt)
> 	ctrl |= (1 << SPA_CTRL_ENCRYPT_IDX) | (1 << SPA_CTRL_AAD_COPY);
> else
> 	ctrl |= (1 << SPA_CTRL_KEY_EXP);

Yes, that's nicer.

> > +static int spacc_des_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
> > +                         unsigned int len)
> > +{
> > +     struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
> > +     struct spacc_ablk_ctx *ctx = crypto_tfm_ctx(tfm);
> > +     int err;
> > +     u32 tmp[DES_EXPKEY_WORDS];
> > +
> > +     if (len > SPACC_CRYPTO_AES_MAX_KEY_LEN) {
> 
> AES left overs in a DES setkey

Good spot, will fix.

> > +static int spacc_aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
> > +                         unsigned int len)
> > +{
> > +     struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
> > +     struct spacc_ablk_ctx *ctx = crypto_tfm_ctx(tfm);
> > +     int err = 0;
> > +
> > +     if (len > SPACC_CRYPTO_AES_MAX_KEY_LEN) {
> > +             crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /*
> > +      * IPSec engine only supports 128 and 256 bit AES keys. If we get a
> > +      * request for any other size (192 bits) then we need to do a software
> > +      * fallback.
> > +      */
> > +     if (!(16 == len || 32 == len) && ctx->sw_cipher) {
> 
> symbolic constants

Ok.

> > +             /*
> > +              * Set the fallback transform to use the same request flags as
> > +              * the hardware transform.
> > +              */
> > +             ctx->sw_cipher->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
> > +             ctx->sw_cipher->base.crt_flags |=
> > +                 (cipher->base.crt_flags & CRYPTO_TFM_REQ_MASK);
> 
> parens not necessary

Ok.

> > +static int spacc_ablk_need_fallback(struct spacc_req *req)
> > +{
> > +     struct spacc_ablk_ctx *ctx;
> > +     struct crypto_tfm *tfm = req->req->tfm;
> > +     struct crypto_alg *alg = req->req->tfm->__crt_alg;
> > +     struct spacc_alg *spacc_alg = to_spacc_alg(alg);
> > +
> > +     ctx = crypto_tfm_ctx(tfm);
> > +
> > +     return (spacc_alg->ctrl_default & SPACC_CRYPTO_ALG_MASK) ==
> > +                     SPA_CTRL_CIPH_ALG_AES &&
> > +             !(16 == ctx->key_len || 32 == ctx->key_len);
> 
> symbolic constants

Ok.

> > +static ssize_t spacc_stat_irq_thresh_store(struct device *dev,
> > +                                        struct device_attribute *attr,
> > +                                        const char *buf, size_t len)
> > +{
> > +     struct spacc_engine *engine = spacc_dev_to_engine(dev);
> > +     unsigned thresh = simple_strtoul(buf, NULL, 0);
> 
> consider using strict_strtoul (checkpatch)

Ok, will change.

> > +static struct spacc_alg ipsec_engine_algs[] = {
> > +     {
> > +             .ctrl_default = SPA_CTRL_CIPH_ALG_AES | SPA_CTRL_CIPH_MODE_CBC,
> > +             .key_offs = 0,
> > +             .iv_offs = SPACC_CRYPTO_AES_MAX_KEY_LEN,
> > +             .alg = {
> > +                     .cra_name = "cbc(aes)",
> > +                     .cra_driver_name = "cbc-aes-picoxcell",
> > +                     .cra_priority = SPACC_CRYPTO_ALG_PRIORITY,
> > +                     .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
> > +                                  CRYPTO_ALG_ASYNC |
> > +                                  CRYPTO_ALG_NEED_FALLBACK,
> > +                     .cra_blocksize = 16,
> 
> symbolic constant, here and throughout the rest of this section.

Ok.

Thanks again for taking the time to review Kim!

Jamie



More information about the linux-arm-kernel mailing list