* [PATCH v3 02/12] crypto: atmel-sha: update request queue management to make it more generic
From: Cyrille Pitchen @ 2017-01-26 16:07 UTC (permalink / raw)
To: herbert, davem, nicolas.ferre
Cc: smueller, linux-crypto, linux-kernel, linux-arm-kernel,
Cyrille Pitchen
In-Reply-To: <cover.1485443478.git.cyrille.pitchen@atmel.com>
This patch is a transitional patch. It splits the atmel_sha_handle_queue()
function. Now atmel_sha_handle_queue() only manages the request queue and
calls a new .start() hook from the atmel_sha_ctx structure.
This hook allows to implement different kind of requests still handled by
a single queue.
Also when the req parameter of atmel_sha_handle_queue() refers to the very
same request as the one returned by crypto_dequeue_request(), the queue
management now gives a chance to this crypto request to be handled
synchronously, hence reducing latencies. The .start() hook returns 0 if
the crypto request was handled synchronously and -EINPROGRESS if the
crypto request still need to be handled asynchronously.
Besides, the new .is_async member of the atmel_sha_dev structure helps
tagging this asynchronous state. Indeed, the req->base.complete() callback
should not be called if the crypto request is handled synchronously.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/crypto/atmel-sha.c | 74 +++++++++++++++++++++++++++++++++-------------
1 file changed, 54 insertions(+), 20 deletions(-)
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 33a36e667547..2dbed8bb8d26 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -105,8 +105,11 @@ struct atmel_sha_reqctx {
u8 buffer[SHA_BUFFER_LEN + SHA512_BLOCK_SIZE] __aligned(sizeof(u32));
};
+typedef int (*atmel_sha_fn_t)(struct atmel_sha_dev *);
+
struct atmel_sha_ctx {
struct atmel_sha_dev *dd;
+ atmel_sha_fn_t start;
unsigned long flags;
};
@@ -134,6 +137,7 @@ struct atmel_sha_dev {
unsigned long flags;
struct crypto_queue queue;
struct ahash_request *req;
+ bool is_async;
struct atmel_sha_dma dma_lch_in;
@@ -163,6 +167,24 @@ static inline void atmel_sha_write(struct atmel_sha_dev *dd,
writel_relaxed(value, dd->io_base + offset);
}
+static inline int atmel_sha_complete(struct atmel_sha_dev *dd, int err)
+{
+ struct ahash_request *req = dd->req;
+
+ dd->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL | SHA_FLAGS_CPU |
+ SHA_FLAGS_DMA_READY | SHA_FLAGS_OUTPUT_READY);
+
+ clk_disable(dd->iclk);
+
+ if (dd->is_async && req->base.complete)
+ req->base.complete(&req->base, err);
+
+ /* handle new request */
+ tasklet_schedule(&dd->queue_task);
+
+ return err;
+}
+
static size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx)
{
size_t count;
@@ -474,6 +496,8 @@ static void atmel_sha_dma_callback(void *data)
{
struct atmel_sha_dev *dd = data;
+ dd->is_async = true;
+
/* dma_lch_in - completed - wait DATRDY */
atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY);
}
@@ -509,7 +533,7 @@ static int atmel_sha_xmit_dma(struct atmel_sha_dev *dd, dma_addr_t dma_addr1,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
if (!in_desc)
- return -EINVAL;
+ atmel_sha_complete(dd, -EINVAL);
in_desc->callback = atmel_sha_dma_callback;
in_desc->callback_param = dd;
@@ -566,7 +590,7 @@ static int atmel_sha_xmit_dma_map(struct atmel_sha_dev *dd,
if (dma_mapping_error(dd->dev, ctx->dma_addr)) {
dev_err(dd->dev, "dma %u bytes error\n", ctx->buflen +
ctx->block_size);
- return -EINVAL;
+ atmel_sha_complete(dd, -EINVAL);
}
ctx->flags &= ~SHA_FLAGS_SG;
@@ -657,7 +681,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
if (dma_mapping_error(dd->dev, ctx->dma_addr)) {
dev_err(dd->dev, "dma %u bytes error\n",
ctx->buflen + ctx->block_size);
- return -EINVAL;
+ atmel_sha_complete(dd, -EINVAL);
}
if (length == 0) {
@@ -671,7 +695,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
if (!dma_map_sg(dd->dev, ctx->sg, 1,
DMA_TO_DEVICE)) {
dev_err(dd->dev, "dma_map_sg error\n");
- return -EINVAL;
+ atmel_sha_complete(dd, -EINVAL);
}
ctx->flags |= SHA_FLAGS_SG;
@@ -685,7 +709,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
if (!dma_map_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE)) {
dev_err(dd->dev, "dma_map_sg error\n");
- return -EINVAL;
+ atmel_sha_complete(dd, -EINVAL);
}
ctx->flags |= SHA_FLAGS_SG;
@@ -843,16 +867,7 @@ static void atmel_sha_finish_req(struct ahash_request *req, int err)
}
/* atomic operation is not needed here */
- dd->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL | SHA_FLAGS_CPU |
- SHA_FLAGS_DMA_READY | SHA_FLAGS_OUTPUT_READY);
-
- clk_disable(dd->iclk);
-
- if (req->base.complete)
- req->base.complete(&req->base, err);
-
- /* handle new request */
- tasklet_schedule(&dd->queue_task);
+ (void)atmel_sha_complete(dd, err);
}
static int atmel_sha_hw_init(struct atmel_sha_dev *dd)
@@ -893,8 +908,9 @@ static int atmel_sha_handle_queue(struct atmel_sha_dev *dd,
struct ahash_request *req)
{
struct crypto_async_request *async_req, *backlog;
- struct atmel_sha_reqctx *ctx;
+ struct atmel_sha_ctx *ctx;
unsigned long flags;
+ bool start_async;
int err = 0, ret = 0;
spin_lock_irqsave(&dd->lock, flags);
@@ -919,9 +935,22 @@ static int atmel_sha_handle_queue(struct atmel_sha_dev *dd,
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
- req = ahash_request_cast(async_req);
- dd->req = req;
- ctx = ahash_request_ctx(req);
+ ctx = crypto_tfm_ctx(async_req->tfm);
+
+ dd->req = ahash_request_cast(async_req);
+ start_async = (dd->req != req);
+ dd->is_async = start_async;
+
+ /* WARNING: ctx->start() MAY change dd->is_async. */
+ err = ctx->start(dd);
+ return (start_async) ? ret : err;
+}
+
+static int atmel_sha_start(struct atmel_sha_dev *dd)
+{
+ struct ahash_request *req = dd->req;
+ struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
+ int err;
dev_dbg(dd->dev, "handling new req, op: %lu, nbytes: %d\n",
ctx->op, req->nbytes);
@@ -947,7 +976,7 @@ static int atmel_sha_handle_queue(struct atmel_sha_dev *dd,
dev_dbg(dd->dev, "exit, err: %d\n", err);
- return ret;
+ return err;
}
static int atmel_sha_enqueue(struct ahash_request *req, unsigned int op)
@@ -1043,8 +1072,11 @@ static int atmel_sha_import(struct ahash_request *req, const void *in)
static int atmel_sha_cra_init(struct crypto_tfm *tfm)
{
+ struct atmel_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
sizeof(struct atmel_sha_reqctx));
+ ctx->start = atmel_sha_start;
return 0;
}
@@ -1188,6 +1220,8 @@ static void atmel_sha_done_task(unsigned long data)
struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data;
int err = 0;
+ dd->is_async = true;
+
if (SHA_FLAGS_CPU & dd->flags) {
if (SHA_FLAGS_OUTPUT_READY & dd->flags) {
dd->flags &= ~SHA_FLAGS_OUTPUT_READY;
--
2.7.4
^ permalink raw reply related
* [PATCH v3 01/12] crypto: atmel-sha: create function to get an Atmel SHA device
From: Cyrille Pitchen @ 2017-01-26 16:07 UTC (permalink / raw)
To: herbert, davem, nicolas.ferre
Cc: smueller, Cyrille Pitchen, linux-crypto, linux-arm-kernel,
linux-kernel
In-Reply-To: <cover.1485443478.git.cyrille.pitchen@atmel.com>
This is a transitional patch: it creates the atmel_sha_find_dev() function,
which will be used in further patches to share the source code responsible
for finding a Atmel SHA device.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/crypto/atmel-sha.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 97e34799e077..33a36e667547 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -260,11 +260,8 @@ static void atmel_sha_fill_padding(struct atmel_sha_reqctx *ctx, int length)
}
}
-static int atmel_sha_init(struct ahash_request *req)
+static struct atmel_sha_dev *atmel_sha_find_dev(struct atmel_sha_ctx *tctx)
{
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct atmel_sha_ctx *tctx = crypto_ahash_ctx(tfm);
- struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
struct atmel_sha_dev *dd = NULL;
struct atmel_sha_dev *tmp;
@@ -281,6 +278,16 @@ static int atmel_sha_init(struct ahash_request *req)
spin_unlock_bh(&atmel_sha.lock);
+ return dd;
+}
+
+static int atmel_sha_init(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct atmel_sha_ctx *tctx = crypto_ahash_ctx(tfm);
+ struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
+ struct atmel_sha_dev *dd = atmel_sha_find_dev(tctx);
+
ctx->dd = dd;
ctx->flags = 0;
--
2.7.4
^ permalink raw reply related
* Re: [PATCH] crypto: doc - Fix hash export state information
From: Rabin Vincent @ 2017-01-26 15:33 UTC (permalink / raw)
To: Stephan Müller; +Cc: herbert, linux-crypto
In-Reply-To: <5671933.EqcuFpxzUF@tauon.atsec.com>
On Wed, Jan 25, 2017 at 05:18:05PM +0100, Stephan Müller wrote:
> Am Mittwoch, 25. Januar 2017, 16:55:06 CET schrieb Rabin Vincent:
> > The documentation states that crypto_ahash_reqsize() provides the size
> > of the state structure used by crypto_ahash_export(). But it's actually
> > crypto_ahash_statesize() which provides this size.
>
> ack for the statesize documentation addition.
>
> In addition, can you please add crypto_ahash_statesize to Documentation/
> crypto/api-digest.rst.
OK.
> nack - The documentation is quite right for reqsize: it returns the *request
> data structure* size as documented.
It's only that one line which is correct, the rest of the documentation
talked about the state size. I'll add back that one line, since similar
functions like crypto_aead_reqsize() have it.
^ permalink raw reply
* [PATCHv2] crypto: doc - Fix hash export state information
From: Rabin Vincent @ 2017-01-26 15:33 UTC (permalink / raw)
To: herbert; +Cc: linux-crypto, smueller, Rabin Vincent
From: Rabin Vincent <rabinv@axis.com>
The documentation states that crypto_ahash_reqsize() provides the size
of the state structure used by crypto_ahash_export(). But it's actually
crypto_ahash_statesize() which provides this size.
Signed-off-by: Rabin Vincent <rabinv@axis.com>
---
v2: Add crypto_ahash_statesize() to api-digest; don't remove doc for crypto_ahash_reqsize()
Documentation/crypto/api-digest.rst | 2 +-
include/crypto/hash.h | 18 ++++++++++++------
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/Documentation/crypto/api-digest.rst b/Documentation/crypto/api-digest.rst
index 07356fa..7a1e670 100644
--- a/Documentation/crypto/api-digest.rst
+++ b/Documentation/crypto/api-digest.rst
@@ -14,7 +14,7 @@ Asynchronous Message Digest API
:doc: Asynchronous Message Digest API
.. kernel-doc:: include/crypto/hash.h
- :functions: crypto_alloc_ahash crypto_free_ahash crypto_ahash_init crypto_ahash_digestsize crypto_ahash_reqtfm crypto_ahash_reqsize crypto_ahash_setkey crypto_ahash_finup crypto_ahash_final crypto_ahash_digest crypto_ahash_export crypto_ahash_import
+ :functions: crypto_alloc_ahash crypto_free_ahash crypto_ahash_init crypto_ahash_digestsize crypto_ahash_reqtfm crypto_ahash_reqsize crypto_ahash_statesize crypto_ahash_setkey crypto_ahash_finup crypto_ahash_final crypto_ahash_digest crypto_ahash_export crypto_ahash_import
Asynchronous Hash Request Handle
--------------------------------
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
index 216a2b8..b5727bc 100644
--- a/include/crypto/hash.h
+++ b/include/crypto/hash.h
@@ -329,6 +329,16 @@ static inline unsigned int crypto_ahash_digestsize(struct crypto_ahash *tfm)
return crypto_hash_alg_common(tfm)->digestsize;
}
+/**
+ * crypto_ahash_statesize() - obtain size of the ahash state
+ * @tfm: cipher handle
+ *
+ * Return the size of the ahash state. With the crypto_ahash_export()
+ * function, the caller can export the state into a buffer whose size is
+ * defined with this function.
+ *
+ * Return: size of the ahash state
+ */
static inline unsigned int crypto_ahash_statesize(struct crypto_ahash *tfm)
{
return crypto_hash_alg_common(tfm)->statesize;
@@ -369,11 +379,7 @@ static inline struct crypto_ahash *crypto_ahash_reqtfm(
* crypto_ahash_reqsize() - obtain size of the request data structure
* @tfm: cipher handle
*
- * Return the size of the ahash state size. With the crypto_ahash_export
- * function, the caller can export the state into a buffer whose size is
- * defined with this function.
- *
- * Return: size of the ahash state
+ * Return: size of the request data
*/
static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm)
{
@@ -453,7 +459,7 @@ int crypto_ahash_digest(struct ahash_request *req);
*
* This function exports the hash state of the ahash_request handle into the
* caller-allocated output buffer out which must have sufficient size (e.g. by
- * calling crypto_ahash_reqsize).
+ * calling crypto_ahash_statesize()).
*
* Return: 0 if the export was successful; < 0 if an error occurred
*/
--
2.1.4
^ permalink raw reply related
* Re: [PATCH v5 0/5] Update LZ4 compressor module
From: Sven Schmidt @ 2017-01-26 14:15 UTC (permalink / raw)
To: Eric Biggers
Cc: akpm, bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck
In-Reply-To: <20170126091953.GA2829@zzz>
On Thu, Jan 26, 2017 at 01:19:53AM -0800, Eric Biggers wrote:
> On Thu, Jan 26, 2017 at 08:57:30AM +0100, Sven Schmidt wrote:
> >
> > This patchset is for updating the LZ4 compression module to a version based
> > on LZ4 v1.7.3 allowing to use the fast compression algorithm aka LZ4 fast
> > which provides an "acceleration" parameter as a tradeoff between
> > high compression ratio and high compression speed.
> >
> > We want to use LZ4 fast in order to support compression in lustre
> > and (mostly, based on that) investigate data reduction techniques in behalf of
> > storage systems.
> >
> > Also, it will be useful for other users of LZ4 compression, as with LZ4 fast
> > it is possible to enable applications to use fast and/or high compression
> > depending on the usecase.
> > For instance, ZRAM is offering a LZ4 backend and could benefit from an updated
> > LZ4 in the kernel.
> >
>
Hey Eric,
> Hi Sven,
>
> [For some reason I didn't receive patch 1/5 and had to get it from patchwork...
> I'm not sure why. I'm subscribed to linux-crypto but not linux-kernel.]
that's weird. I just experienced the first patch takes a little longer to get delivered because of its size.
Please let me know if the problem occurs again.
> The proposed patch defines LZ4_MEMORY_USAGE to 10 which means that LZ4
> compression will use a hash table of only 1024 bytes, containing only 256
> entries, to find matches. This differs from upstream LZ4 1.7.3, which uses
> LZ4_MEMORY_USAGE of 14, as well as the previous LZ4 included in the Linux
> kernel, both of which specify the hash table size to be 16384 bytes, containing
> 4096 entries.
>
> Given that varying the hash table size is a trade-off between memory usage,
> speed, and compression ratio, is this an intentional difference and has it been
> benchmarked?
>
I believe I had some troubles with LZ4_MEMORY_USAGE of 14. But I may be wrong.
I will test that again and eventually adapt that value.
> Also, in lz4defs.h:
>
> > #if defined(__x86_64__)
> > typedef U64 reg_t; /* 64-bits in x32 mode */
> > #else
> > typedef size_t reg_t; /* 32-bits in x32 mode */
> > #endif
>
> Are you sure this really needed over just always using size_t?
>
No, actually there's just one use of that value and the upstream version uses size_t instead of reg_t
in that particular place. So I will replace it with size_t.
> > #if LZ4_ARCH64
> > #ifdef __BIG_ENDIAN__
> > #define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
> > #else
> > #define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
> > #endif
> > #else
> > #ifdef __BIG_ENDIAN__
> > #define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3)
> > #else
> > #define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3)
> > #endif
> > #endif
>
> LZ4_NBCOMMONBYTES() is defined incorrectly for 64-bit little endian; it should
> be using __builtin_ctzll().
>
Indeed! Using the same values in if and else does not make sense at all.
Thank you for pointing that one out. I will fix it.
> Nit: can you also clean up the weird indentation (e.g. double tabs) in
> lz4defs.h?
>
> Thanks,
>
> Eric
>
I'm wondering why checkpatch does not point out this kind of styling problem?
I did fix that in the other files but I think I missed lz4defs.h. Will fix the indentation.
Thanks,
Sven
^ permalink raw reply
* Re: [PATCH] dm: switch dm-verity to async hash crypto API
From: Ondrej Mosnáček @ 2017-01-26 11:34 UTC (permalink / raw)
To: Gilad Ben-Yossef
Cc: dm-devel, Alasdair Kergon, Mike Snitzer, linux-crypto,
gilad.benyossef, Ofir
In-Reply-To: <1485268704-31033-1-git-send-email-gilad@benyossef.com>
Hi Gilad,
2017-01-24 15:38 GMT+01:00 Gilad Ben-Yossef <gilad@benyossef.com>:
> - v->tfm = crypto_alloc_shash(v->alg_name, 0, 0);
> + v->tfm = crypto_alloc_ahash(v->alg_name, 0, CRYPTO_ALG_ASYNC);
I believe you should pass zero as the mask here. When flags == 0 and
mask == CRYPTO_ALG_ASYNC, you are basically saying "I want only algs
that have flags & CRYPTO_ALG_ASYNC == 0", which means you should only
get ahash tfms that are always synchronous (see [1]). However, since
you set a non-NULL callback in verity_hash_init, I don't think this
was your intention. By setting the mask to zero, you should be able to
get also an actual async tfm.
Thanks,
Ondrej
[1] https://lkml.org/lkml/2016/12/13/904
^ permalink raw reply
* Re: [PATCH v5 0/5] Update LZ4 compressor module
From: Eric Biggers @ 2017-01-26 9:19 UTC (permalink / raw)
To: Sven Schmidt
Cc: akpm, bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck
In-Reply-To: <1485417455-31726-1-git-send-email-4sschmid@informatik.uni-hamburg.de>
On Thu, Jan 26, 2017 at 08:57:30AM +0100, Sven Schmidt wrote:
>
> This patchset is for updating the LZ4 compression module to a version based
> on LZ4 v1.7.3 allowing to use the fast compression algorithm aka LZ4 fast
> which provides an "acceleration" parameter as a tradeoff between
> high compression ratio and high compression speed.
>
> We want to use LZ4 fast in order to support compression in lustre
> and (mostly, based on that) investigate data reduction techniques in behalf of
> storage systems.
>
> Also, it will be useful for other users of LZ4 compression, as with LZ4 fast
> it is possible to enable applications to use fast and/or high compression
> depending on the usecase.
> For instance, ZRAM is offering a LZ4 backend and could benefit from an updated
> LZ4 in the kernel.
>
Hi Sven,
[For some reason I didn't receive patch 1/5 and had to get it from patchwork...
I'm not sure why. I'm subscribed to linux-crypto but not linux-kernel.]
The proposed patch defines LZ4_MEMORY_USAGE to 10 which means that LZ4
compression will use a hash table of only 1024 bytes, containing only 256
entries, to find matches. This differs from upstream LZ4 1.7.3, which uses
LZ4_MEMORY_USAGE of 14, as well as the previous LZ4 included in the Linux
kernel, both of which specify the hash table size to be 16384 bytes, containing
4096 entries.
Given that varying the hash table size is a trade-off between memory usage,
speed, and compression ratio, is this an intentional difference and has it been
benchmarked?
Also, in lz4defs.h:
> #if defined(__x86_64__)
> typedef U64 reg_t; /* 64-bits in x32 mode */
> #else
> typedef size_t reg_t; /* 32-bits in x32 mode */
> #endif
Are you sure this really needed over just always using size_t?
> #if LZ4_ARCH64
> #ifdef __BIG_ENDIAN__
> #define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
> #else
> #define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
> #endif
> #else
> #ifdef __BIG_ENDIAN__
> #define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3)
> #else
> #define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3)
> #endif
> #endif
LZ4_NBCOMMONBYTES() is defined incorrectly for 64-bit little endian; it should
be using __builtin_ctzll().
Nit: can you also clean up the weird indentation (e.g. double tabs) in
lz4defs.h?
Thanks,
Eric
^ permalink raw reply
* [PATCH v5 3/5] crypto: Change LZ4 modules to work with new LZ4 module version
From: Sven Schmidt @ 2017-01-26 7:57 UTC (permalink / raw)
To: akpm
Cc: bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck, Sven Schmidt
In-Reply-To: <1485417455-31726-1-git-send-email-4sschmid@informatik.uni-hamburg.de>
This patch updates the crypto modules using LZ4 compression
to work with the new LZ4 module version.
Signed-off-by: Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
---
crypto/lz4.c | 21 ++++++++-------------
crypto/lz4hc.c | 21 ++++++++-------------
2 files changed, 16 insertions(+), 26 deletions(-)
diff --git a/crypto/lz4.c b/crypto/lz4.c
index 99c1b2c..40fd2c2 100644
--- a/crypto/lz4.c
+++ b/crypto/lz4.c
@@ -66,15 +66,13 @@ static void lz4_exit(struct crypto_tfm *tfm)
static int __lz4_compress_crypto(const u8 *src, unsigned int slen,
u8 *dst, unsigned int *dlen, void *ctx)
{
- size_t tmp_len = *dlen;
- int err;
+ int out_len = LZ4_compress_default(src, dst,
+ slen, (int)((size_t)dlen), ctx);
- err = lz4_compress(src, slen, dst, &tmp_len, ctx);
-
- if (err < 0)
+ if (!out_len)
return -EINVAL;
- *dlen = tmp_len;
+ *dlen = out_len;
return 0;
}
@@ -96,16 +94,13 @@ static int lz4_compress_crypto(struct crypto_tfm *tfm, const u8 *src,
static int __lz4_decompress_crypto(const u8 *src, unsigned int slen,
u8 *dst, unsigned int *dlen, void *ctx)
{
- int err;
- size_t tmp_len = *dlen;
- size_t __slen = slen;
+ int out_len = LZ4_decompress_safe(src, dst, slen, (int)((size_t)dlen));
- err = lz4_decompress_unknownoutputsize(src, __slen, dst, &tmp_len);
- if (err < 0)
+ if (out_len < 0)
return -EINVAL;
- *dlen = tmp_len;
- return err;
+ *dlen = out_len;
+ return out_len;
}
static int lz4_sdecompress(struct crypto_scomp *tfm, const u8 *src,
diff --git a/crypto/lz4hc.c b/crypto/lz4hc.c
index 75ffc4a..6f16f96 100644
--- a/crypto/lz4hc.c
+++ b/crypto/lz4hc.c
@@ -65,15 +65,13 @@ static void lz4hc_exit(struct crypto_tfm *tfm)
static int __lz4hc_compress_crypto(const u8 *src, unsigned int slen,
u8 *dst, unsigned int *dlen, void *ctx)
{
- size_t tmp_len = *dlen;
- int err;
+ int out_len = LZ4_compress_HC(src, dst, slen,
+ (int)((size_t)dlen), LZ4HC_DEFAULT_CLEVEL, ctx);
- err = lz4hc_compress(src, slen, dst, &tmp_len, ctx);
-
- if (err < 0)
+ if (out_len == 0)
return -EINVAL;
- *dlen = tmp_len;
+ *dlen = out_len;
return 0;
}
@@ -97,16 +95,13 @@ static int lz4hc_compress_crypto(struct crypto_tfm *tfm, const u8 *src,
static int __lz4hc_decompress_crypto(const u8 *src, unsigned int slen,
u8 *dst, unsigned int *dlen, void *ctx)
{
- int err;
- size_t tmp_len = *dlen;
- size_t __slen = slen;
+ int out_len = LZ4_decompress_safe(src, dst, slen, (int)((size_t)dlen));
- err = lz4_decompress_unknownoutputsize(src, __slen, dst, &tmp_len);
- if (err < 0)
+ if (out_len < 0)
return -EINVAL;
- *dlen = tmp_len;
- return err;
+ *dlen = out_len;
+ return out_len;
}
static int lz4hc_sdecompress(struct crypto_scomp *tfm, const u8 *src,
--
2.1.4
^ permalink raw reply related
* [PATCH v5 0/5] Update LZ4 compressor module
From: Sven Schmidt @ 2017-01-26 7:57 UTC (permalink / raw)
To: akpm
Cc: bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck
In-Reply-To: <1482259992-16680-1-git-send-email-4sschmid@informatik.uni-hamburg.de>
This patchset is for updating the LZ4 compression module to a version based
on LZ4 v1.7.3 allowing to use the fast compression algorithm aka LZ4 fast
which provides an "acceleration" parameter as a tradeoff between
high compression ratio and high compression speed.
We want to use LZ4 fast in order to support compression in lustre
and (mostly, based on that) investigate data reduction techniques in behalf of
storage systems.
Also, it will be useful for other users of LZ4 compression, as with LZ4 fast
it is possible to enable applications to use fast and/or high compression
depending on the usecase.
For instance, ZRAM is offering a LZ4 backend and could benefit from an updated
LZ4 in the kernel.
LZ4 homepage: http://www.lz4.org/
LZ4 source repository: https://github.com/lz4/lz4
Source version: 1.7.3
Benchmark (taken from [1], Core i5-4300U @1.9GHz):
----------------|--------------|----------------|----------
Compressor | Compression | Decompression | Ratio
----------------|--------------|----------------|----------
memcpy | 4200 MB/s | 4200 MB/s | 1.000
LZ4 fast 50 | 1080 MB/s | 2650 MB/s | 1.375
LZ4 fast 17 | 680 MB/s | 2220 MB/s | 1.607
LZ4 fast 5 | 475 MB/s | 1920 MB/s | 1.886
LZ4 default | 385 MB/s | 1850 MB/s | 2.101
[1] http://fastcompression.blogspot.de/2015/04/sampling-or-faster-lz4.html
fs/pstore: fs/squashfs: Change usage of LZ4 to work with new LZ4 version
[PATCH 1/5] lib: Update LZ4 compressor module
[PATCH 2/5] lib/decompress_unlz4: Change module to work with new LZ4 module version
[PATCH 3/5] crypto: Change LZ4 modules to work with new LZ4 module version
[PATCH 4/5] fs/pstore: fs/squashfs: Change usage of LZ4 to work with new LZ4 version
[PATCH 5/5] lib/lz4: Remove back-compat wrappers
v2:
- Changed order of the patches since in the initial patchset the lz4.h was in the
last patch but was referenced by the other ones
- Split lib/decompress_unlz4.c in an own patch
- Fixed errors reported by the buildbot
- Further refactorings
- Added more appropriate copyright note to include/linux/lz4.h
v3:
- Adjusted the code to satisfy kernel coding style (checkpatch.pl)
- Made sure the changes to LZ4 in Kernel (overflow checks etc.)
are included in the new module (they are)
- Removed the second LZ4_compressBound function with related name but
different return type
- Corrected version number (was LZ4 1.7.3)
- Added missing LZ4 streaming functions
v4:
- Fixed kbuild errors
- Re-added lz4_compressbound as alias for LZ4_compressBound
to ensure backwards compatibility
- Wrapped LZ4_hash5 with check for LZ4_ARCH64 since it is only used there
and triggers an unused function warning when false
v5:
- Added a fifth patch to remove the back-compat wrappers introduced
to ensure bisectibility between the patches (the functions are no longer
needed since there's no callers left)
^ permalink raw reply
* [PATCH v5 5/5] lib/lz4: Remove back-compat wrappers
From: Sven Schmidt @ 2017-01-26 7:57 UTC (permalink / raw)
To: akpm
Cc: bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck, Sven Schmidt
In-Reply-To: <1485417455-31726-1-git-send-email-4sschmid@informatik.uni-hamburg.de>
This patch removes the functions introduced as wrappers for providing
backwards compatibility to the prior LZ4 version.
They're not needed anymore since there's no callers left.
Signed-off-by: Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
---
include/linux/lz4.h | 73 ------------------------------------------------
lib/lz4/lz4_compress.c | 22 ---------------
lib/lz4/lz4_decompress.c | 42 ----------------------------
lib/lz4/lz4hc_compress.c | 23 ---------------
4 files changed, 160 deletions(-)
diff --git a/include/linux/lz4.h b/include/linux/lz4.h
index 2072255..5958f7d 100644
--- a/include/linux/lz4.h
+++ b/include/linux/lz4.h
@@ -173,14 +173,6 @@ static inline int LZ4_compressBound(size_t isize)
}
/*
- * For backward compatibility
- */
-static inline int lz4_compressbound(size_t isize)
-{
- return LZ4_COMPRESSBOUND(isize);
-}
-
-/*
* LZ4_compress_default()
* Compresses 'sourceSize' bytes from buffer 'source'
* into already allocated 'dest' buffer of size 'maxOutputSize'.
@@ -249,23 +241,6 @@ int LZ4_compress_fast(const char *source, char *dest, int inputSize,
int LZ4_compress_destSize(const char *source, char *dest, int *sourceSizePtr,
int targetDestSize, void *wrkmem);
-/*
- * lz4_compress()
- * src : source address of the original data
- * src_len: size of the original data
- * dst : output buffer address of the compressed data
- * This requires 'dst' of size LZ4_COMPRESSBOUND.
- * dst_len: is the output size, which is returned after compress done
- * workmem: address of the working memory.
- * This requires 'workmem' of size LZ4_MEM_COMPRESS.
- * return : Success if return 0
- * Error if return (< 0)
- * note : Destination buffer and workmem must be already allocated with
- * the defined size.
- */
-int lz4_compress(const unsigned char *src, size_t src_len, unsigned char *dst,
- size_t *dst_len, void *wrkmem);
-
/*-************************************************************************
* Decompression Functions
**************************************************************************/
@@ -340,37 +315,6 @@ int LZ4_decompress_safe(const char *source, char *dest, int compressedSize,
int LZ4_decompress_safe_partial(const char *source, char *dest,
int compressedSize, int targetOutputSize, int maxDecompressedSize);
-
-/*
- * lz4_decompress_unknownoutputsize() :
- * src : source address of the compressed data
- * src_len : is the input size, therefore the compressed size
- * dest : output buffer address of the decompressed data
- * dest_len: is the max size of the destination buffer, which is
- * returned with actual size of decompressed data after
- * decompress done
- * return: Success if return 0
- * Error if return (< 0)
- * note: Destination buffer must be already allocated.
- */
-int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len,
- unsigned char *dest, size_t *dest_len);
-
-/*
- * lz4_decompress() :
- * src : source address of the compressed data
- * src_len : is the input size,
- * which is returned after decompress done
- * dest : output buffer address of the decompressed data
- * actual_dest_len: is the size of uncompressed data, supposing it's known
- * return: Success if return 0
- * Error if return (< 0)
- * note : Destination buffer must be already allocated.
- * slightly faster than lz4_decompress_unknownoutputsize()
- */
-int lz4_decompress(const unsigned char *src, size_t *src_len,
- unsigned char *dest, size_t actual_dest_len);
-
/*-************************************************************************
* LZ4 HC Compression
**************************************************************************/
@@ -399,23 +343,6 @@ int LZ4_compress_HC(const char *src, char *dst, int srcSize, int dstCapacity,
int compressionLevel, void *wrkmem);
/*
- * lz4hc_compress()
- * src : source address of the original data
- * src_len: size of the original data
- * dst : output buffer address of the compressed data
- * This requires 'dst' of size LZ4_COMPRESSBOUND.
- * dst_len: is the output size, which is returned after compress done
- * workmem: address of the working memory.
- * This requires 'workmem' of size LZ4HC_MEM_COMPRESS.
- * return : Success if return 0
- * Error if return (< 0)
- * note : Destination buffer and workmem must be already allocated with
- * the defined size.
- */
-int lz4hc_compress(const unsigned char *src, size_t src_len, unsigned char *dst,
- size_t *dst_len, void *wrkmem);
-
-/*
* These functions compress data in successive blocks of any size,
* using previous blocks as dictionary. One key assumption is that previous
* blocks (up to 64 KB) remain read-accessible while
diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c
index e595896..b625226 100644
--- a/lib/lz4/lz4_compress.c
+++ b/lib/lz4/lz4_compress.c
@@ -872,27 +872,5 @@ int LZ4_compress_fast_continue(LZ4_stream_t *LZ4_stream, const char *source,
}
EXPORT_SYMBOL(LZ4_compress_fast_continue);
-/*-******************************
- * For backwards compatibility
- ********************************/
-int lz4_compress(const unsigned char *src, size_t src_len, unsigned char *dst,
- size_t *dst_len, void *wrkmem) {
- *dst_len = LZ4_compress_default(src, dst, (int)src_len,
- (int)((size_t)dst_len), wrkmem);
-
- /*
- * Prior lz4_compress will return -1 in case of error
- * and 0 on success
- * while new LZ4_compress_fast/default
- * returns 0 in case of error
- * and the output length on success
- */
- if (!dst_len)
- return -1;
- else
- return 0;
-}
-EXPORT_SYMBOL(lz4_compress);
-
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4 compressor");
diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c
index ca71375..053d09d 100644
--- a/lib/lz4/lz4_decompress.c
+++ b/lib/lz4/lz4_decompress.c
@@ -483,47 +483,5 @@ int LZ4_decompress_fast_usingDict(const char *source, char *dest,
}
EXPORT_SYMBOL(LZ4_decompress_fast_usingDict);
-/*-******************************
- * For backwards compatibility
- ********************************/
-int lz4_decompress_unknownoutputsize(const unsigned char *src,
- size_t src_len, unsigned char *dest, size_t *dest_len) {
- *dest_len = LZ4_decompress_safe(src, dest,
- (int)src_len, (int)((size_t)dest_len));
-
- /*
- * Prior lz4_decompress_unknownoutputsize will return
- * 0 for success and a negative result for error
- * new LZ4_decompress_safe returns
- * - the length of data read on success
- * - and also a negative result on error
- * meaning when result > 0, we just return 0 here
- */
- if (src_len > 0) {
- return 0;
- } else
- return -1;
-}
-EXPORT_SYMBOL(lz4_decompress_unknownoutputsize);
-
-int lz4_decompress(const unsigned char *src, size_t *src_len,
- unsigned char *dest, size_t actual_dest_len) {
- *src_len = LZ4_decompress_fast(src, dest, (int)actual_dest_len);
-
- /*
- * Prior lz4_decompress will return
- * 0 for success and a negative result for error
- * new LZ4_decompress_fast returns
- * - the length of data read on success
- * - and also a negative result on error
- * meaning when result > 0, we just return 0 here
- */
- if ((int)((size_t)src_len) > 0)
- return 0;
- else
- return -1;
-}
-EXPORT_SYMBOL(lz4_decompress);
-
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4 decompressor");
diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c
index 880c778..a29dad0 100644
--- a/lib/lz4/lz4hc_compress.c
+++ b/lib/lz4/lz4hc_compress.c
@@ -603,29 +603,6 @@ int LZ4_compress_HC(const char *src, char *dst, int srcSize,
}
EXPORT_SYMBOL(LZ4_compress_HC);
-/*-******************************
- * For backwards compatibility
- ********************************/
-int lz4hc_compress(const unsigned char *src, size_t src_len,
- unsigned char *dst, size_t *dst_len, void *wrkmem)
-{
- *dst_len = LZ4_compress_HC(src, dst, (int)src_len,
- (int)((size_t)dst_len), LZ4HC_DEFAULT_CLEVEL, wrkmem);
-
- /*
- * Prior lz4hc_compress will return -1 in case of error
- * and 0 on success
- * while new LZ4_compress_HC
- * returns 0 in case of error
- * and the output length on success
- */
- if (!dst_len)
- return -1;
- else
- return 0;
-}
-EXPORT_SYMBOL(lz4hc_compress);
-
/**************************************
* Streaming Functions
**************************************/
--
2.1.4
^ permalink raw reply related
* [PATCH v5 4/5] fs/pstore: fs/squashfs: Change usage of LZ4 to work with new LZ4 version
From: Sven Schmidt @ 2017-01-26 7:57 UTC (permalink / raw)
To: akpm
Cc: bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck, Sven Schmidt
In-Reply-To: <1485417455-31726-1-git-send-email-4sschmid@informatik.uni-hamburg.de>
This patch updates fs/pstore and fs/squashfs to use the updated
functions from the new LZ4 module.
Signed-off-by: Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
---
fs/pstore/platform.c | 14 +++++++-------
fs/squashfs/lz4_wrapper.c | 12 ++++++------
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 729677e..85c4c58 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -342,31 +342,31 @@ static int compress_lz4(const void *in, void *out, size_t inlen, size_t outlen)
{
int ret;
- ret = lz4_compress(in, inlen, out, &outlen, workspace);
- if (ret) {
+ ret = LZ4_compress_default(in, out, inlen, outlen, workspace);
+ if (!ret) {
pr_err("lz4_compress error, ret = %d!\n", ret);
return -EIO;
}
- return outlen;
+ return ret;
}
static int decompress_lz4(void *in, void *out, size_t inlen, size_t outlen)
{
int ret;
- ret = lz4_decompress_unknownoutputsize(in, inlen, out, &outlen);
- if (ret) {
+ ret = LZ4_decompress_safe(in, out, inlen, outlen);
+ if (ret < 0) {
pr_err("lz4_decompress error, ret = %d!\n", ret);
return -EIO;
}
- return outlen;
+ return ret;
}
static void allocate_lz4(void)
{
- big_oops_buf_sz = lz4_compressbound(psinfo->bufsize);
+ big_oops_buf_sz = LZ4_compressBound(psinfo->bufsize);
big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
if (big_oops_buf) {
workspace = kmalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c
index ff4468b..95da653 100644
--- a/fs/squashfs/lz4_wrapper.c
+++ b/fs/squashfs/lz4_wrapper.c
@@ -97,7 +97,6 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
struct squashfs_lz4 *stream = strm;
void *buff = stream->input, *data;
int avail, i, bytes = length, res;
- size_t dest_len = output->length;
for (i = 0; i < b; i++) {
avail = min(bytes, msblk->devblksize - offset);
@@ -108,12 +107,13 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
put_bh(bh[i]);
}
- res = lz4_decompress_unknownoutputsize(stream->input, length,
- stream->output, &dest_len);
- if (res)
+ res = LZ4_decompress_safe(stream->input, stream->output,
+ length, output->length);
+
+ if (res < 0)
return -EIO;
- bytes = dest_len;
+ bytes = res;
data = squashfs_first_page(output);
buff = stream->output;
while (data) {
@@ -128,7 +128,7 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
}
squashfs_finish_page(output);
- return dest_len;
+ return res;
}
const struct squashfs_decompressor squashfs_lz4_comp_ops = {
--
2.1.4
^ permalink raw reply related
* [PATCH v5 2/5] lib/decompress_unlz4: Change module to work with new LZ4 module version
From: Sven Schmidt @ 2017-01-26 7:57 UTC (permalink / raw)
To: akpm
Cc: bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck, Sven Schmidt
In-Reply-To: <1485417455-31726-1-git-send-email-4sschmid@informatik.uni-hamburg.de>
This patch updates the unlz4 wrapper to work with the
updated LZ4 kernel module version.
Signed-off-by: Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
---
lib/decompress_unlz4.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c
index 036fc88..1b0baf3 100644
--- a/lib/decompress_unlz4.c
+++ b/lib/decompress_unlz4.c
@@ -72,7 +72,7 @@ STATIC inline int INIT unlz4(u8 *input, long in_len,
error("NULL input pointer and missing fill function");
goto exit_1;
} else {
- inp = large_malloc(lz4_compressbound(uncomp_chunksize));
+ inp = large_malloc(LZ4_compressBound(uncomp_chunksize));
if (!inp) {
error("Could not allocate input buffer");
goto exit_1;
@@ -136,7 +136,7 @@ STATIC inline int INIT unlz4(u8 *input, long in_len,
inp += 4;
size -= 4;
} else {
- if (chunksize > lz4_compressbound(uncomp_chunksize)) {
+ if (chunksize > LZ4_compressBound(uncomp_chunksize)) {
error("chunk length is longer than allocated");
goto exit_2;
}
@@ -152,11 +152,14 @@ STATIC inline int INIT unlz4(u8 *input, long in_len,
out_len -= dest_len;
} else
dest_len = out_len;
- ret = lz4_decompress(inp, &chunksize, outp, dest_len);
+
+ ret = LZ4_decompress_fast(inp, outp, dest_len);
+ chunksize = ret;
#else
dest_len = uncomp_chunksize;
- ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp,
- &dest_len);
+
+ ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len);
+ dest_len = ret;
#endif
if (ret < 0) {
error("Decoding failed");
--
2.1.4
^ permalink raw reply related
* [PATCH v5 1/5] lib: Update LZ4 compressor module
From: Sven Schmidt @ 2017-01-26 7:57 UTC (permalink / raw)
To: akpm
Cc: bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck, Sven Schmidt
In-Reply-To: <1485417455-31726-1-git-send-email-4sschmid@informatik.uni-hamburg.de>
This patch updates LZ4 kernel module to LZ4 v1.7.3 by Yann Collet.
The kernel module is inspired by the previous work by Chanho Min.
The updated LZ4 module will not break existing code since the patchset
contains appropriate changes.
API changes:
New method LZ4_compress_fast which differs from the variant available
in kernel by the new acceleration parameter,
allowing to trade compression ratio for more compression speed
and vice versa.
LZ4_decompress_fast is the respective decompression method, featuring a very
fast decoder (multiple GB/s per core), able to reach RAM speed in multi-core
systems. The decompressor allows to decompress data compressed with
LZ4 fast as well as the LZ4 HC (high compression) algorithm.
Also the useful functions LZ4_decompress_safe_partial
LZ4_compress_destsize were added. The latter reverses the logic by trying to
compress as much data as possible from source to dest while the former aims
to decompress partial blocks of data.
A bunch of streaming functions were also added
which allow compressig/decompressing data in multiple steps
(so called "streaming mode").
The methods lz4_compress and lz4_decompress_unknownoutputsize
are now known as LZ4_compress_default respectivley LZ4_decompress_safe.
The old methods will be removed since there's no callers left in the code.
Signed-off-by: Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
---
include/linux/lz4.h | 589 ++++++++++++++++++++++---
lib/lz4/lz4_compress.c | 1103 ++++++++++++++++++++++++++++++++--------------
lib/lz4/lz4_decompress.c | 694 ++++++++++++++++++-----------
lib/lz4/lz4defs.h | 320 ++++++++------
lib/lz4/lz4hc_compress.c | 855 ++++++++++++++++++++++-------------
5 files changed, 2486 insertions(+), 1075 deletions(-)
diff --git a/include/linux/lz4.h b/include/linux/lz4.h
index 6b784c5..2072255 100644
--- a/include/linux/lz4.h
+++ b/include/linux/lz4.h
@@ -1,87 +1,554 @@
-#ifndef __LZ4_H__
-#define __LZ4_H__
-/*
- * LZ4 Kernel Interface
+/* LZ4 Kernel Interface
*
* Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
+ * Copyright (C) 2016, Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
+ *
+ * This file is based on the original header file
+ * for LZ4 - Fast LZ compression algorithm.
+ *
+ * LZ4 - Fast LZ compression algorithm
+ * Copyright (C) 2011-2016, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * You can contact the author at :
+ * - LZ4 homepage : http://www.lz4.org
+ * - LZ4 source repository : https://github.com/lz4/lz4
+ */
+
+#ifndef __LZ4_H__
+#define __LZ4_H__
+
+#include <linux/types.h>
+#include <linux/string.h> /* memset, memcpy */
+
+/*-************************************************************************
+ * CONSTANTS
+ **************************************************************************/
+/*
+ * LZ4_MEMORY_USAGE :
+ * Memory usage formula : N->2^N Bytes
+ * (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+ * Increasing memory usage improves compression ratio
+ * Reduced memory usage can improve speed, due to cache effect
+ * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+ */
+#define LZ4_MEMORY_USAGE 10
+
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize) (\
+ (unsigned int)(isize) > (unsigned int)LZ4_MAX_INPUT_SIZE \
+ ? 0 \
+ : (isize) + ((isize)/255) + 16)
+
+#define LZ4_ACCELERATION_DEFAULT 1
+#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG)
+
+#define LZ4HC_MIN_CLEVEL 3
+#define LZ4HC_DEFAULT_CLEVEL 9
+#define LZ4HC_MAX_CLEVEL 16
+
+#define LZ4HC_DICTIONARY_LOGSIZE 16
+#define LZ4HC_MAXD (1<<LZ4HC_DICTIONARY_LOGSIZE)
+#define LZ4HC_MAXD_MASK (LZ4HC_MAXD - 1)
+#define LZ4HC_HASH_LOG (LZ4HC_DICTIONARY_LOGSIZE-1)
+#define LZ4HC_HASHTABLESIZE (1 << LZ4HC_HASH_LOG)
+#define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1)
+
+/*-************************************************************************
+ * STREAMING CONSTANTS AND STRUCTURES
+ **************************************************************************/
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
+
+#define LZ4_STREAMHCSIZE 262192
+#define LZ4_STREAMHCSIZE_SIZET (262192 / sizeof(size_t))
+
+#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * \
+ sizeof(unsigned long long))
+
+/*
+ * LZ4_stream_t :
+ * information structure to track an LZ4 stream.
+ */
+typedef struct {
+ uint32_t hashTable[LZ4_HASH_SIZE_U32];
+ uint32_t currentOffset;
+ uint32_t initCheck;
+ const uint8_t *dictionary;
+ uint8_t *bufferStart;
+ uint32_t dictSize;
+} LZ4_stream_t_internal;
+typedef union {
+ unsigned long long table[LZ4_STREAMSIZE_U64];
+ LZ4_stream_t_internal internal_donotuse;
+} LZ4_stream_t;
+
+/*
+ * LZ4_streamHC_t :
+ * information structure to track an LZ4HC stream.
*/
-#define LZ4_MEM_COMPRESS (16384)
-#define LZ4HC_MEM_COMPRESS (262144 + (2 * sizeof(unsigned char *)))
+typedef struct {
+ unsigned int hashTable[LZ4HC_HASHTABLESIZE];
+ unsigned short chainTable[LZ4HC_MAXD];
+ /* next block to continue on current prefix */
+ const unsigned char *end;
+ /* All index relative to this position */
+ const unsigned char *base;
+ /* alternate base for extDict */
+ const unsigned char *dictBase;
+ /* below that point, need extDict */
+ unsigned int dictLimit;
+ /* below that point, no more dict */
+ unsigned int lowLimit;
+ /* index from which to continue dict update */
+ unsigned int nextToUpdate;
+ unsigned int compressionLevel;
+} LZ4HC_CCtx_internal;
+typedef union {
+ size_t table[LZ4_STREAMHCSIZE_SIZET];
+ LZ4HC_CCtx_internal internal_donotuse;
+} LZ4_streamHC_t;
/*
- * lz4_compressbound()
- * Provides the maximum size that LZ4 may output in a "worst case" scenario
- * (input data not compressible)
+ * LZ4_streamDecode_t :
+ * information structure to track an LZ4 stream during decompression.
+ * init this structure using LZ4_setStreamDecode
+ * (or memset()) before first use
*/
-static inline size_t lz4_compressbound(size_t isize)
+typedef struct {
+ const uint8_t *externalDict;
+ size_t extDictSize;
+ const uint8_t *prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+typedef union {
+ unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+ LZ4_streamDecode_t_internal internal_donotuse;
+} LZ4_streamDecode_t;
+
+/*-************************************************************************
+ * SIZE OF STATE
+ **************************************************************************/
+#define LZ4_MEM_COMPRESS LZ4_STREAMSIZE
+#define LZ4HC_MEM_COMPRESS LZ4_STREAMHCSIZE
+
+/*-************************************************************************
+ * Compression Functions
+ **************************************************************************/
+
+/*
+ * LZ4_compressBound() :
+ * Provides the maximum size that LZ4 may output in a "worst case" scenario
+ * (input data not compressible)
+ */
+static inline int LZ4_compressBound(size_t isize)
+{
+ return LZ4_COMPRESSBOUND(isize);
+}
+
+/*
+ * For backward compatibility
+ */
+static inline int lz4_compressbound(size_t isize)
{
- return isize + (isize / 255) + 16;
+ return LZ4_COMPRESSBOUND(isize);
}
/*
+ * LZ4_compress_default()
+ * Compresses 'sourceSize' bytes from buffer 'source'
+ * into already allocated 'dest' buffer of size 'maxOutputSize'.
+ * Compression is guaranteed to succeed if
+ * 'maxOutputSize' >= LZ4_compressBound(inputSize).
+ * It also runs faster, so it's a recommended setting.
+ * If the function cannot compress 'source'
+ * into a more limited 'dest' budget,
+ * compression stops *immediately*,
+ * and the function result is zero.
+ * As a consequence, 'dest' content is not valid.
+ *
+ * source : source address of the original data
+ * dest : output buffer address
+ * of the compressed data
+ * inputSize : Max supported value is
+ * LZ4_MAX_INPUT_SIZE
+ * maxOutputSize: full or partial size of buffer 'dest'
+ * (which must be already allocated)
+ * workmem : address of the working memory.
+ * This requires 'workmem' of size LZ4_MEM_COMPRESS.
+ * return : the number of bytes written into buffer 'dest'
+ * (necessarily <= maxOutputSize) or 0 if compression fails
+ */
+int LZ4_compress_default(const char *source, char *dest, int inputSize,
+ int maxOutputSize, void *wrkmem);
+
+/*
+ * LZ4_compress_fast() :
+ * Same as LZ4_compress_default(),
+ * but allows to select an "acceleration" factor.
+ * The larger the acceleration value,
+ * the faster the algorithm,
+ * but also the lesser the compression.
+ * It's a trade-off. It can be fine tuned,
+ * with each successive value
+ * providing roughly +~3% to speed.
+ * An acceleration value of "1"
+ * is the same as regular LZ4_compress_default()
+ * Values <= 0 will be replaced by
+ * LZ4_ACCELERATION_DEFAULT, which is 1.
+ */
+int LZ4_compress_fast(const char *source, char *dest, int inputSize,
+ int maxOutputSize, void *wrkmem, int acceleration);
+
+/*
+ * LZ4_compress_destSize()
+ * Reverse the logic, by compressing as much data as possible
+ * from 'source' buffer into already allocated buffer 'dest'
+ * of size 'targetDestSize'.
+ * This function either compresses the entire 'source' content into 'dest'
+ * if it's large enough, or fill 'dest' buffer completely with as much data as
+ * ossible from 'source'.
+ *
+ * source : source address of the original data
+ * dest : output buffer address of the compressed data
+ * inputSize : Max supported value is LZ4_MAX_INPUT_SIZE
+ * *sourceSizePtr: will be modified to indicate how many bytes where read
+ * from 'source' to fill 'dest'.
+ * New value is necessarily <= old value.
+ * workmem : address of the working memory.
+ * This requires 'workmem' of size LZ4_MEM_COMPRESS.
+ * return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
+ or 0 if compression fails
+ */
+int LZ4_compress_destSize(const char *source, char *dest, int *sourceSizePtr,
+ int targetDestSize, void *wrkmem);
+
+/*
* lz4_compress()
- * src : source address of the original data
- * src_len : size of the original data
- * dst : output buffer address of the compressed data
- * This requires 'dst' of size LZ4_COMPRESSBOUND.
- * dst_len : is the output size, which is returned after compress done
- * workmem : address of the working memory.
- * This requires 'workmem' of size LZ4_MEM_COMPRESS.
- * return : Success if return 0
- * Error if return (< 0)
- * note : Destination buffer and workmem must be already allocated with
+ * src : source address of the original data
+ * src_len: size of the original data
+ * dst : output buffer address of the compressed data
+ * This requires 'dst' of size LZ4_COMPRESSBOUND.
+ * dst_len: is the output size, which is returned after compress done
+ * workmem: address of the working memory.
+ * This requires 'workmem' of size LZ4_MEM_COMPRESS.
+ * return : Success if return 0
+ * Error if return (< 0)
+ * note : Destination buffer and workmem must be already allocated with
* the defined size.
*/
-int lz4_compress(const unsigned char *src, size_t src_len,
- unsigned char *dst, size_t *dst_len, void *wrkmem);
-
- /*
- * lz4hc_compress()
- * src : source address of the original data
- * src_len : size of the original data
- * dst : output buffer address of the compressed data
- * This requires 'dst' of size LZ4_COMPRESSBOUND.
- * dst_len : is the output size, which is returned after compress done
- * workmem : address of the working memory.
- * This requires 'workmem' of size LZ4HC_MEM_COMPRESS.
- * return : Success if return 0
- * Error if return (< 0)
- * note : Destination buffer and workmem must be already allocated with
- * the defined size.
- */
-int lz4hc_compress(const unsigned char *src, size_t src_len,
- unsigned char *dst, size_t *dst_len, void *wrkmem);
-
-/*
- * lz4_decompress()
- * src : source address of the compressed data
- * src_len : is the input size, whcih is returned after decompress done
- * dest : output buffer address of the decompressed data
- * actual_dest_len: is the size of uncompressed data, supposing it's known
- * return : Success if return 0
- * Error if return (< 0)
- * note : Destination buffer must be already allocated.
- * slightly faster than lz4_decompress_unknownoutputsize()
+int lz4_compress(const unsigned char *src, size_t src_len, unsigned char *dst,
+ size_t *dst_len, void *wrkmem);
+
+/*-************************************************************************
+ * Decompression Functions
+ **************************************************************************/
+
+/*
+ * LZ4_decompress_fast()
+ * source : source address of the compressed data
+ * dst : output buffer address of the decompressed data
+ * originalSize: is the original and therefore uncompressed size
+ * return : the number of bytes read from the source buffer
+ * (in other words, the compressed size)
+ * If the source stream is detected malformed, the function will
+ * stop decoding and return a negative result.
+ * Destination buffer must be already allocated.
+ * Its size must be a minimum of 'originalSize' bytes.
+ * note : This function fully respect memory boundaries
+ * for properly formed compressed data.
+ * It is a bit faster than LZ4_decompress_safe().
+ * However, it does not provide any protection against intentionally
+ * modified data stream (malicious input).
+ * Use this function in trusted environment only
+ * (data to decode comes from a trusted source).
*/
-int lz4_decompress(const unsigned char *src, size_t *src_len,
- unsigned char *dest, size_t actual_dest_len);
+int LZ4_decompress_fast(const char *source, char *dest, int originalSize);
+
+/*
+ * LZ4_decompress_safe()
+ * source : source address of the compressed data
+ * dest : output buffer address of the decompressed data
+ * compressedSize : is the precise full size of the compressed block.
+ * maxDecompressedSize: is the size of destination buffer,
+ * which must be already allocated.
+ * return : the number of bytes decompressed into destination buffer
+ * (necessarily <= maxDecompressedSize)
+ * If destination buffer is not large enough, decoding will stop
+ * and output an error code (<0).
+ * If the source stream is detected malformed, the function will
+ * stop decoding and return a negative result.
+ * This function is protected against buffer overflow exploits,
+ * including malicious data packets. It never writes outside
+ * output buffer, nor reads outside input buffer.
+ */
+int LZ4_decompress_safe(const char *source, char *dest, int compressedSize,
+ int maxDecompressedSize);
/*
- * lz4_decompress_unknownoutputsize()
+ * LZ4_decompress_safe_partial() :
+ * This function decompress a compressed block of size 'compressedSize'
+ * at position 'source' into destination buffer 'dest'
+ * of size 'maxDecompressedSize'.
+ * The function tries to stop decompressing operation as soon as
+ * 'targetOutputSize' has been reached, reducing decompression time.
+ *
+ * source : source address of the compressed data
+ * dest : output buffer address of the decompressed data
+ * compressedSize : is the precise full size of the compressed block.
+ * targetOutputSize : the decompression operation will try
+ * to stop as soon as 'targetOutputSize'
+ * has been reached
+ * maxDecompressedSize: is the size of destination buffer,
+ * which must be already allocated.
+ * return : the number of bytes decoded in the destination buffer
+ * (necessarily <= maxDecompressedSize)
+ * Note : this number can be < 'targetOutputSize' should the compressed
+ * block to decode be smaller. Always control how many bytes
+ * were decoded. If the source stream is detected malformed,
+ * the function will stop decoding and return a negative result.
+ * This function never writes outside of output buffer,
+ * and never reads outside of input buffer.
+ * It is therefore protected against malicious data packets
+ */
+int LZ4_decompress_safe_partial(const char *source, char *dest,
+ int compressedSize, int targetOutputSize, int maxDecompressedSize);
+
+
+/*
+ * lz4_decompress_unknownoutputsize() :
* src : source address of the compressed data
* src_len : is the input size, therefore the compressed size
- * dest : output buffer address of the decompressed data
+ * dest : output buffer address of the decompressed data
* dest_len: is the max size of the destination buffer, which is
- * returned with actual size of decompressed data after
- * decompress done
- * return : Success if return 0
- * Error if return (< 0)
- * note : Destination buffer must be already allocated.
+ * returned with actual size of decompressed data after
+ * decompress done
+ * return: Success if return 0
+ * Error if return (< 0)
+ * note: Destination buffer must be already allocated.
*/
int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len,
- unsigned char *dest, size_t *dest_len);
+ unsigned char *dest, size_t *dest_len);
+
+/*
+ * lz4_decompress() :
+ * src : source address of the compressed data
+ * src_len : is the input size,
+ * which is returned after decompress done
+ * dest : output buffer address of the decompressed data
+ * actual_dest_len: is the size of uncompressed data, supposing it's known
+ * return: Success if return 0
+ * Error if return (< 0)
+ * note : Destination buffer must be already allocated.
+ * slightly faster than lz4_decompress_unknownoutputsize()
+ */
+int lz4_decompress(const unsigned char *src, size_t *src_len,
+ unsigned char *dest, size_t actual_dest_len);
+
+/*-************************************************************************
+ * LZ4 HC Compression
+ **************************************************************************/
+
+/*! LZ4_compress_HC() :
+ * Compress data from `src` into `dst`, using the more powerful
+ * but slower "HC" algorithm. dst` must be already allocated.
+ * Compression is guaranteed to succeed if
+ * `dstCapacity >= LZ4_compressBound(srcSize)
+ * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE
+ *
+ * src : source address of the original data
+ * dst : output buffer address of the compressed data
+ * srcSize : Max supported value is LZ4_MAX_INPUT_SIZE
+ * dstCapacity : full or partial size of buffer 'dst'
+ * (which must be already allocated)
+ * compressionLevel: Recommended values are between 4 and 9, although any
+ * value between 1 and LZ4HC_MAX_CLEVEL will work.
+ * Values >LZ4HC_MAX_CLEVEL behave the same as 16.
+ * wrkmem : address of the working memory.
+ * This requires 'wrkmem' of size LZ4HC_MEM_COMPRESS.
+ * return : the number of bytes written into 'dst'
+ * or 0 if compression fails.
+ */
+int LZ4_compress_HC(const char *src, char *dst, int srcSize, int dstCapacity,
+ int compressionLevel, void *wrkmem);
+
+/*
+ * lz4hc_compress()
+ * src : source address of the original data
+ * src_len: size of the original data
+ * dst : output buffer address of the compressed data
+ * This requires 'dst' of size LZ4_COMPRESSBOUND.
+ * dst_len: is the output size, which is returned after compress done
+ * workmem: address of the working memory.
+ * This requires 'workmem' of size LZ4HC_MEM_COMPRESS.
+ * return : Success if return 0
+ * Error if return (< 0)
+ * note : Destination buffer and workmem must be already allocated with
+ * the defined size.
+ */
+int lz4hc_compress(const unsigned char *src, size_t src_len, unsigned char *dst,
+ size_t *dst_len, void *wrkmem);
+
+/*
+ * These functions compress data in successive blocks of any size,
+ * using previous blocks as dictionary. One key assumption is that previous
+ * blocks (up to 64 KB) remain read-accessible while
+ * compressing next blocks.
+ * There is an exception for ring buffers, which can be smaller than 64 KB.
+ * Ring buffers scenario is automatically detected and handled by
+ * LZ4_compress_HC_continue().
+ * Before starting compression, state must be properly initialized,
+ * using LZ4_resetStreamHC().
+ * A first "fictional block" can then be designated as initial dictionary,
+ * using LZ4_loadDictHC() (Optional).
+ * Then, use LZ4_compress_HC_continue() to compress each successive block.
+ * Previous memory blocks (including initial dictionary when present) must
+ * remain accessible and unmodified during compression.
+ * 'dst' buffer should be sized to handle worst case scenarios, using
+ * LZ4_compressBound(), to ensure operation success.
+ * If, for any reason, previous data blocks can't be preserved unmodified
+ * in memory during next compression block,
+ * you must save it to a safer memory space, using LZ4_saveDictHC().
+ * Return value of LZ4_saveDictHC() is the size of dictionary
+ * effectively saved into 'safeBuffer'.
+ */
+void LZ4_resetStreamHC(LZ4_streamHC_t *streamHCPtr, int compressionLevel);
+int LZ4_loadDictHC(LZ4_streamHC_t *streamHCPtr, const char *dictionary,
+ int dictSize);
+int LZ4_compress_HC_continue(LZ4_streamHC_t *streamHCPtr, const char *src,
+ char *dst, int srcSize, int maxDstSize);
+int LZ4_saveDictHC(LZ4_streamHC_t *streamHCPtr, char *safeBuffer,
+ int maxDictSize);
+
+/*-*********************************************
+ * Streaming Compression Functions
+ ***********************************************/
+/*
+ * LZ4_resetStream() :
+ * An LZ4_stream_t structure can be allocated once
+ * and re-used multiple times.
+ * Use this function to init an allocated `LZ4_stream_t` structure
+ * and start a new compression.
+ */
+void LZ4_resetStream(LZ4_stream_t *LZ4_stream);
+
+/*
+ * LZ4_loadDict() :
+ * Use this function to load a static dictionary into LZ4_stream.
+ * Any previous data will be forgotten, only 'dictionary'
+ * will remain in memory.
+ * Loading a size of 0 is allowed.
+ * Return : dictionary size, in bytes (necessarily <= 64 KB)
+ */
+int LZ4_loadDict(LZ4_stream_t *streamPtr, const char *dictionary,
+ int dictSize);
+
+/*
+ * LZ4_compress_fast_continue() :
+ * Compress buffer content 'src',
+ * using data from previously compressed blocks
+ * as dictionary to improve compression ratio.
+ * Important : Previous data blocks are assumed to still
+ * be present and unmodified !
+ * 'dst' buffer must be already allocated.
+ * If maxDstSize >= LZ4_compressBound(srcSize),
+ * compression is guaranteed to succeed, and runs faster.
+ * If not, and if compressed data cannot fit into 'dst' buffer size,
+ * compression stops, and function returns a zero.
+ */
+int LZ4_compress_fast_continue(LZ4_stream_t *streamPtr, const char *src,
+ char *dst, int srcSize, int maxDstSize, int acceleration);
+
+/*
+ * LZ4_saveDict() :
+ * If previously compressed data block is not guaranteed
+ * to remain available at its memory location,
+ * save it into a safer place (char *safeBuffer).
+ * Note : you don't need to call LZ4_loadDict() afterwards,
+ * dictionary is immediately usable, you can therefore call
+ * LZ4_compress_fast_continue().
+ * Return : saved dictionary size in bytes (necessarily <= dictSize),
+ * or 0 if error.
+ */
+int LZ4_saveDict(LZ4_stream_t *streamPtr, char *safeBuffer, int dictSize);
+
+/*
+ * LZ4_setStreamDecode() :
+ * Use this function to instruct where to find the dictionary.
+ * Setting a size of 0 is allowed (same effect as reset).
+ * @return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode(LZ4_streamDecode_t *LZ4_streamDecode,
+ const char *dictionary, int dictSize);
+
+/*
+ * LZ4_decompress_*_continue() :
+ * These decoding functions allow decompression of multiple blocks
+ * in "streaming" mode.
+ * Previously decoded blocks *must* remain available at the memory position
+ * where they were decoded (up to 64 KB)
+ * In the case of a ring buffers, decoding buffer must be either :
+ * - Exactly same size as encoding buffer, with same update rule
+ * (block boundaries at same positions) In which case,
+ * the decoding & encoding ring buffer can have any size,
+ * including very small ones ( < 64 KB).
+ * - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+ * maxBlockSize is implementation dependent.
+ * It's the maximum size you intend to compress into a single block.
+ * In which case, encoding and decoding buffers do not need
+ * to be synchronized, and encoding ring buffer can have any size,
+ * including small ones ( < 64 KB).
+ * - _At least_ 64 KB + 8 bytes + maxBlockSize.
+ * In which case, encoding and decoding buffers do not need to be
+ * synchronized, and encoding ring buffer can have any size,
+ * including larger than decoding buffer. W
+ * Whenever these conditions are not possible, save the last 64KB of decoded
+ * data into a safe buffer, and indicate where it is saved
+ * using LZ4_setStreamDecode()
+ */
+int LZ4_decompress_safe_continue(LZ4_streamDecode_t *LZ4_streamDecode,
+ const char *source, char *dest, int compressedSize,
+ int maxDecompressedSize);
+int LZ4_decompress_fast_continue(LZ4_streamDecode_t *LZ4_streamDecode,
+ const char *source, char *dest, int originalSize);
+
+/*
+ * LZ4_decompress_*_usingDict() :
+ * These decoding functions work the same as
+ * a combination of LZ4_setStreamDecode() followed by
+ * LZ4_decompress_*_continue()
+ * They are stand-alone, and don't need an LZ4_streamDecode_t structure.
+ */
+int LZ4_decompress_safe_usingDict(const char *source, char *dest,
+ int compressedSize, int maxDecompressedSize, const char *dictStart,
+ int dictSize);
+int LZ4_decompress_fast_usingDict(const char *source, char *dest,
+ int originalSize, const char *dictStart, int dictSize);
+
#endif
diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c
index 28321d8..e595896 100644
--- a/lib/lz4/lz4_compress.c
+++ b/lib/lz4/lz4_compress.c
@@ -1,19 +1,16 @@
/*
* LZ4 - Fast LZ compression algorithm
- * Copyright (C) 2011-2012, Yann Collet.
- * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
+ * Copyright (C) 2011 - 2016, Yann Collet.
+ * BSD 2 - Clause License (http://www.opensource.org/licenses/bsd - license.php)
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
- *
- * * Redistributions of source code must retain the above copyright
+ * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
+ * * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
- *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -25,417 +22,875 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
* You can contact the author at :
- * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
- * - LZ4 source repository : http://code.google.com/p/lz4/
+ * - LZ4 homepage : http://www.lz4.org
+ * - LZ4 source repository : https://github.com/lz4/lz4
*
- * Changed for kernel use by:
- * Chanho Min <chanho.min@lge.com>
+ * Changed for kernel usage by:
+ * Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
*/
+/*-************************************
+ * Dependencies
+ **************************************/
+#include <linux/lz4.h>
+#include "lz4defs.h"
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/lz4.h>
#include <asm/unaligned.h>
-#include "lz4defs.h"
-/*
- * LZ4_compressCtx :
- * -----------------
- * Compress 'isize' bytes from 'source' into an output buffer 'dest' of
- * maximum size 'maxOutputSize'. * If it cannot achieve it, compression
- * will stop, and result of the function will be zero.
- * return : the number of bytes written in buffer 'dest', or 0 if the
- * compression fails
- */
-static inline int lz4_compressctx(void *ctx,
- const char *source,
- char *dest,
- int isize,
- int maxoutputsize)
+/*-******************************
+ * Compression functions
+ ********************************/
+static U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
{
- HTYPE *hashtable = (HTYPE *)ctx;
- const u8 *ip = (u8 *)source;
+ if (tableType == byU16)
+ return ((sequence * 2654435761U)
+ >> ((MINMATCH*8) - (LZ4_HASHLOG + 1)));
+ else
+ return ((sequence * 2654435761U)
+ >> ((MINMATCH*8) - LZ4_HASHLOG));
+}
+
#if LZ4_ARCH64
- const BYTE * const base = ip;
+static U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+ const U32 hashLog = (tableType == byU16)
+ ? LZ4_HASHLOG + 1
+ : LZ4_HASHLOG;
+
+#ifdef __LITTLE_ENDIAN__
+ static const U64 prime5bytes = 889523592379ULL;
+
+ return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
#else
- const int base = 0;
+ static const U64 prime8bytes = 11400714785074694791ULL;
+
+ return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
#endif
- const u8 *anchor = ip;
- const u8 *const iend = ip + isize;
- const u8 *const mflimit = iend - MFLIMIT;
- #define MATCHLIMIT (iend - LASTLITERALS)
-
- u8 *op = (u8 *) dest;
- u8 *const oend = op + maxoutputsize;
- int length;
- const int skipstrength = SKIPSTRENGTH;
- u32 forwardh;
- int lastrun;
-
- /* Init */
- if (isize < MINLENGTH)
- goto _last_literals;
+}
+#endif
+
+static U32 LZ4_hashPosition(const void *p, tableType_t tableType)
+{
+#if LZ4_ARCH64
+ if (tableType == byU32)
+ return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+#endif
+
+ return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+static void LZ4_putPositionOnHash(const BYTE *p, U32 h, void *tableBase,
+ tableType_t const tableType, const BYTE *srcBase)
+{
+ switch (tableType) {
+ case byPtr:
+ {
+ const BYTE **hashTable = (const BYTE **)tableBase;
+
+ hashTable[h] = p;
+ return;
+ }
+ case byU32:
+ {
+ U32 *hashTable = (U32 *) tableBase;
+
+ hashTable[h] = (U32)(p - srcBase);
+ return;
+ }
+ case byU16:
+ {
+ U16 *hashTable = (U16 *) tableBase;
+
+ hashTable[h] = (U16)(p - srcBase);
+ return;
+ }
+ }
+}
+
+static inline void LZ4_putPosition(const BYTE *p, void *tableBase,
+ tableType_t tableType, const BYTE *srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+
+ LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+static const BYTE *LZ4_getPositionOnHash(U32 h, void *tableBase,
+ tableType_t tableType, const BYTE *srcBase)
+{
+ if (tableType == byPtr) {
+ const BYTE **hashTable = (const BYTE **) tableBase;
+
+ return hashTable[h];
+ }
+
+ if (tableType == byU32) {
+ const U32 * const hashTable = (U32 *) tableBase;
+
+ return hashTable[h] + srcBase;
+ }
+
+ {
+ /* default, to ensure a return */
+ const U16 * const hashTable = (U16 *) tableBase;
+ return hashTable[h] + srcBase;
+ }
+}
+
+static inline const BYTE *LZ4_getPosition(const BYTE *p, void *tableBase,
+ tableType_t tableType, const BYTE *srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+
+ return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+
+/*
+ * LZ4_compress_generic() :
+ * inlined, to ensure branches are decided at compilation time
+ */
+static inline int LZ4_compress_generic(
+ LZ4_stream_t_internal * const dictPtr,
+ const char * const source,
+ char * const dest,
+ const int inputSize,
+ const int maxOutputSize,
+ const limitedOutput_directive outputLimited,
+ const tableType_t tableType,
+ const dict_directive dict,
+ const dictIssue_directive dictIssue,
+ const U32 acceleration)
+{
+ const BYTE *ip = (const BYTE *) source;
+ const BYTE *base;
+ const BYTE *lowLimit;
+ const BYTE * const lowRefLimit = ip - dictPtr->dictSize;
+ const BYTE * const dictionary = dictPtr->dictionary;
+ const BYTE * const dictEnd = dictionary + dictPtr->dictSize;
+ const size_t dictDelta = dictEnd - (const BYTE *)source;
+ const BYTE *anchor = (const BYTE *) source;
+ const BYTE * const iend = ip + inputSize;
+ const BYTE * const mflimit = iend - MFLIMIT;
+ const BYTE * const matchlimit = iend - LASTLITERALS;
+
+ BYTE *op = (BYTE *) dest;
+ BYTE * const olimit = op + maxOutputSize;
+
+ U32 forwardH;
+ size_t refDelta = 0;
+
+ /* Init conditions */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) {
+ /* Unsupported inputSize, too large (or negative) */
+ return 0;
+ }
+ switch (dict) {
+ case noDict:
+ default:
+ base = (const BYTE *)source;
+ lowLimit = (const BYTE *)source;
+ break;
+ case withPrefix64k:
+ base = (const BYTE *)source - dictPtr->currentOffset;
+ lowLimit = (const BYTE *)source - dictPtr->dictSize;
+ break;
+ case usingExtDict:
+ base = (const BYTE *)source - dictPtr->currentOffset;
+ lowLimit = (const BYTE *)source;
+ break;
+ }
+
+ if ((tableType == byU16)
+ && (inputSize >= LZ4_64Klimit)) {
+ /* Size too large (not within 64K limit) */
+ return 0;
+ }
- memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
+ if (inputSize < LZ4_minLength) {
+ /* Input too small, no compression (all literals) */
+ goto _last_literals;
+ }
/* First Byte */
- hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
- ip++;
- forwardh = LZ4_HASH_VALUE(ip);
+ LZ4_putPosition(ip, dictPtr->hashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
/* Main Loop */
- for (;;) {
- int findmatchattempts = (1U << skipstrength) + 3;
- const u8 *forwardip = ip;
- const u8 *ref;
- u8 *token;
+ for ( ; ; ) {
+ const BYTE *match;
+ BYTE *token;
/* Find a match */
- do {
- u32 h = forwardh;
- int step = findmatchattempts++ >> skipstrength;
- ip = forwardip;
- forwardip = ip + step;
-
- if (unlikely(forwardip > mflimit))
- goto _last_literals;
-
- forwardh = LZ4_HASH_VALUE(forwardip);
- ref = base + hashtable[h];
- hashtable[h] = ip - base;
- } while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip)));
+ { const BYTE *forwardIp = ip;
+ unsigned int step = 1;
+ unsigned int searchMatchNb = acceleration
+ << LZ4_skipTrigger;
+
+ do {
+ U32 const h = forwardH;
+
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit))
+ goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h,
+ dictPtr->hashTable,
+ tableType, base);
+ if (dict == usingExtDict) {
+ if (match < (const BYTE *)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE *)source;
+ } }
+ forwardH = LZ4_hashPosition(forwardIp,
+ tableType);
+ LZ4_putPositionOnHash(ip, h, dictPtr->hashTable,
+ tableType, base);
+
+ } while (((dictIssue == dictSmall)
+ ? (match < lowRefLimit)
+ : 0)
+ || ((tableType == byU16)
+ ? 0
+ : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match + refDelta)
+ != LZ4_read32(ip)));
+ }
/* Catch up */
- while ((ip > anchor) && (ref > (u8 *)source) &&
- unlikely(ip[-1] == ref[-1])) {
+ while (((ip > anchor) & (match + refDelta > lowLimit))
+ && (unlikely(ip[-1] == match[refDelta - 1]))) {
ip--;
- ref--;
- }
+ match--;
+ }
- /* Encode Literal length */
- length = (int)(ip - anchor);
- token = op++;
- /* check output limit */
- if (unlikely(op + length + (2 + 1 + LASTLITERALS) +
- (length >> 8) > oend))
- return 0;
+ /* Encode Literals */
+ { unsigned const int litLength = (unsigned int)(ip - anchor);
- if (length >= (int)RUN_MASK) {
- int len;
- *token = (RUN_MASK << ML_BITS);
- len = length - RUN_MASK;
- for (; len > 254 ; len -= 255)
- *op++ = 255;
- *op++ = (u8)len;
- } else
- *token = (length << ML_BITS);
+ token = op++;
+ if ((outputLimited) &&
+ /* Check output buffer overflow */
+ (unlikely(op + litLength +
+ (2 + 1 + LASTLITERALS) +
+ (litLength/255) > olimit)))
+ return 0;
+ if (litLength >= RUN_MASK) {
+ int len = (int)litLength - RUN_MASK;
+
+ *token = (RUN_MASK<<ML_BITS);
+ for (; len >= 255 ; len -= 255)
+ *op++ = 255;
+ *op++ = (BYTE)len;
+ } else
+ *token = (BYTE)(litLength<<ML_BITS);
+
+ /* Copy Literals */
+ LZ4_wildCopy(op, anchor, op + litLength);
+ op += litLength;
+ }
- /* Copy Literals */
- LZ4_BLINDCOPY(anchor, op, length);
_next_match:
/* Encode Offset */
- LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
+ LZ4_writeLE16(op, (U16)(ip - match)); op += 2;
- /* Start Counting */
- ip += MINMATCH;
- /* MinMatch verified */
- ref += MINMATCH;
- anchor = ip;
- while (likely(ip < MATCHLIMIT - (STEPSIZE - 1))) {
- #if LZ4_ARCH64
- u64 diff = A64(ref) ^ A64(ip);
- #else
- u32 diff = A32(ref) ^ A32(ip);
- #endif
- if (!diff) {
- ip += STEPSIZE;
- ref += STEPSIZE;
- continue;
- }
- ip += LZ4_NBCOMMONBYTES(diff);
- goto _endcount;
- }
- #if LZ4_ARCH64
- if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
- ip += 4;
- ref += 4;
- }
- #endif
- if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
- ip += 2;
- ref += 2;
- }
- if ((ip < MATCHLIMIT) && (*ref == *ip))
- ip++;
-_endcount:
/* Encode MatchLength */
- length = (int)(ip - anchor);
- /* Check output limit */
- if (unlikely(op + (1 + LASTLITERALS) + (length >> 8) > oend))
- return 0;
- if (length >= (int)ML_MASK) {
- *token += ML_MASK;
- length -= ML_MASK;
- for (; length > 509 ; length -= 510) {
- *op++ = 255;
- *op++ = 255;
- }
- if (length > 254) {
- length -= 255;
- *op++ = 255;
+ { unsigned int matchCode;
+
+ if ((dict == usingExtDict)
+ && (lowLimit == dictionary)) {
+ const BYTE *limit;
+
+ match += refDelta;
+ limit = ip + (dictEnd - match);
+ if (limit > matchlimit)
+ limit = matchlimit;
+ matchCode = LZ4_count(ip + MINMATCH,
+ match + MINMATCH, limit);
+ ip += MINMATCH + matchCode;
+ if (ip == limit) {
+ unsigned const int more = LZ4_count(ip,
+ (const BYTE *)source,
+ matchlimit);
+
+ matchCode += more;
+ ip += more;
+ }
+ } else {
+ matchCode = LZ4_count(ip + MINMATCH,
+ match + MINMATCH, matchlimit);
+ ip += MINMATCH + matchCode;
}
- *op++ = (u8)length;
- } else
- *token += length;
+
+ if (outputLimited &&
+ /* Check output buffer overflow */
+ (unlikely(op +
+ (1 + LASTLITERALS) +
+ (matchCode>>8) > olimit)))
+ return 0;
+ if (matchCode >= ML_MASK) {
+ *token += ML_MASK;
+ matchCode -= ML_MASK;
+ LZ4_write32(op, 0xFFFFFFFF);
+ while (matchCode >= 4*255) {
+ op += 4;
+ LZ4_write32(op, 0xFFFFFFFF);
+ matchCode -= 4*255;
+ }
+ op += matchCode / 255;
+ *op++ = (BYTE)(matchCode % 255);
+ } else
+ *token += (BYTE)(matchCode);
+ }
+
+ anchor = ip;
/* Test end of chunk */
- if (ip > mflimit) {
- anchor = ip;
+ if (ip > mflimit)
break;
- }
/* Fill table */
- hashtable[LZ4_HASH_VALUE(ip-2)] = ip - 2 - base;
+ LZ4_putPosition(ip - 2, dictPtr->hashTable, tableType, base);
/* Test next position */
- ref = base + hashtable[LZ4_HASH_VALUE(ip)];
- hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
- if ((ref > ip - (MAX_DISTANCE + 1)) && (A32(ref) == A32(ip))) {
+ match = LZ4_getPosition(ip, dictPtr->hashTable,
+ tableType, base);
+ if (dict == usingExtDict) {
+ if (match < (const BYTE *)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE *)source;
+ }
+ }
+ LZ4_putPosition(ip, dictPtr->hashTable, tableType, base);
+ if (((dictIssue == dictSmall) ? (match >= lowRefLimit) : 1)
+ && (match + MAX_DISTANCE >= ip)
+ && (LZ4_read32(match + refDelta) == LZ4_read32(ip))) {
token = op++;
*token = 0;
goto _next_match;
}
/* Prepare next loop */
- anchor = ip++;
- forwardh = LZ4_HASH_VALUE(ip);
+ forwardH = LZ4_hashPosition(++ip, tableType);
}
_last_literals:
/* Encode Last Literals */
- lastrun = (int)(iend - anchor);
- if (((char *)op - dest) + lastrun + 1
- + ((lastrun + 255 - RUN_MASK) / 255) > (u32)maxoutputsize)
- return 0;
-
- if (lastrun >= (int)RUN_MASK) {
- *op++ = (RUN_MASK << ML_BITS);
- lastrun -= RUN_MASK;
- for (; lastrun > 254 ; lastrun -= 255)
- *op++ = 255;
- *op++ = (u8)lastrun;
- } else
- *op++ = (lastrun << ML_BITS);
- memcpy(op, anchor, iend - anchor);
- op += iend - anchor;
+ { size_t const lastRun = (size_t)(iend - anchor);
+ if ((outputLimited) &&
+ /* Check output buffer overflow */
+ ((op - (BYTE *)dest) + lastRun + 1 +
+ ((lastRun + 255 - RUN_MASK)/255) > (U32)maxOutputSize))
+ return 0;
+ if (lastRun >= RUN_MASK) {
+ size_t accumulator = lastRun - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for (; accumulator >= 255 ; accumulator -= 255)
+ *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRun<<ML_BITS);
+ }
+ memcpy(op, anchor, lastRun);
+ op += lastRun;
+ }
/* End */
- return (int)(((char *)op) - dest);
+ return (int) (((char *)op) - dest);
}
-static inline int lz4_compress64kctx(void *ctx,
- const char *source,
- char *dest,
- int isize,
- int maxoutputsize)
+int LZ4_compress_fast_extState(void *state, const char *source, char *dest,
+ int inputSize, int maxOutputSize, int acceleration)
{
- u16 *hashtable = (u16 *)ctx;
- const u8 *ip = (u8 *) source;
- const u8 *anchor = ip;
- const u8 *const base = ip;
- const u8 *const iend = ip + isize;
- const u8 *const mflimit = iend - MFLIMIT;
- #define MATCHLIMIT (iend - LASTLITERALS)
-
- u8 *op = (u8 *) dest;
- u8 *const oend = op + maxoutputsize;
- int len, length;
- const int skipstrength = SKIPSTRENGTH;
- u32 forwardh;
- int lastrun;
-
- /* Init */
- if (isize < MINLENGTH)
- goto _last_literals;
+ #if LZ4_ARCH64
+ tableType_t tableType = byU32;
+ #else
+ tableType_t tableType = byPtr;
+ #endif
+
+ LZ4_stream_t_internal *ctx = &((LZ4_stream_t *)state)->internal_donotuse;
+
+ LZ4_resetStream((LZ4_stream_t *)state);
+
+ if (acceleration < 1)
+ acceleration = LZ4_ACCELERATION_DEFAULT;
+
+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source,
+ dest, inputSize, 0,
+ noLimit, byU16, noDict,
+ noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source,
+ dest, inputSize, 0,
+ noLimit, tableType, noDict,
+ noDictIssue, acceleration);
+ } else {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source,
+ dest, inputSize,
+ maxOutputSize, limitedOutput, byU16, noDict,
+ noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source,
+ dest, inputSize,
+ maxOutputSize, limitedOutput, tableType, noDict,
+ noDictIssue, acceleration);
+ }
+}
- memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
+int LZ4_compress_fast(const char *source, char *dest, int inputSize,
+ int maxOutputSize, void *wrkmem, int acceleration)
+{
+ return LZ4_compress_fast_extState(wrkmem, source, dest, inputSize,
+ maxOutputSize, acceleration);
+}
+EXPORT_SYMBOL(LZ4_compress_fast);
+
+int LZ4_compress_default(const char *source, char *dest, int inputSize,
+ int maxOutputSize, void *wrkmem)
+{
+ return LZ4_compress_fast(source, dest, inputSize,
+ maxOutputSize, wrkmem, 1);
+}
+EXPORT_SYMBOL(LZ4_compress_default);
+
+/*-******************************
+ * *_destSize() variant
+ ********************************/
+
+static int LZ4_compress_destSize_generic(
+ LZ4_stream_t_internal * const ctx,
+ const char * const src,
+ char * const dst,
+ int * const srcSizePtr,
+ const int targetDstSize,
+ const tableType_t tableType)
+{
+ const BYTE *ip = (const BYTE *) src;
+ const BYTE *base = (const BYTE *) src;
+ const BYTE *lowLimit = (const BYTE *) src;
+ const BYTE *anchor = ip;
+ const BYTE * const iend = ip + *srcSizePtr;
+ const BYTE * const mflimit = iend - MFLIMIT;
+ const BYTE * const matchlimit = iend - LASTLITERALS;
+
+ BYTE *op = (BYTE *) dst;
+ BYTE * const oend = op + targetDstSize;
+ BYTE * const oMaxLit = op + targetDstSize - 2 /* offset */
+ - 8 /* because 8 + MINMATCH == MFLIMIT */ - 1 /* token */;
+ BYTE * const oMaxMatch = op + targetDstSize
+ - (LASTLITERALS + 1 /* token */);
+ BYTE * const oMaxSeq = oMaxLit - 1 /* token */;
+
+ U32 forwardH;
+
+ /* Init conditions */
+ /* Impossible to store anything */
+ if (targetDstSize < 1)
+ return 0;
+ /* Unsupported input size, too large (or negative) */
+ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE)
+ return 0;
+ /* Size too large (not within 64K limit) */
+ if ((tableType == byU16) && (*srcSizePtr >= LZ4_64Klimit))
+ return 0;
+ /* Input too small, no compression (all literals) */
+ if (*srcSizePtr < LZ4_minLength)
+ goto _last_literals;
/* First Byte */
- ip++;
- forwardh = LZ4_HASH64K_VALUE(ip);
+ *srcSizePtr = 0;
+ LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
/* Main Loop */
- for (;;) {
- int findmatchattempts = (1U << skipstrength) + 3;
- const u8 *forwardip = ip;
- const u8 *ref;
- u8 *token;
+ for ( ; ; ) {
+ const BYTE *match;
+ BYTE *token;
/* Find a match */
- do {
- u32 h = forwardh;
- int step = findmatchattempts++ >> skipstrength;
- ip = forwardip;
- forwardip = ip + step;
-
- if (forwardip > mflimit)
- goto _last_literals;
-
- forwardh = LZ4_HASH64K_VALUE(forwardip);
- ref = base + hashtable[h];
- hashtable[h] = (u16)(ip - base);
- } while (A32(ref) != A32(ip));
+ { const BYTE *forwardIp = ip;
+ unsigned int step = 1;
+ unsigned int searchMatchNb = 1 << LZ4_skipTrigger;
+
+ do {
+ U32 h = forwardH;
+
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit))
+ goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, ctx->hashTable,
+ tableType, base);
+ forwardH = LZ4_hashPosition(forwardIp,
+ tableType);
+ LZ4_putPositionOnHash(ip, h,
+ ctx->hashTable, tableType,
+ base);
+
+ } while (((tableType == byU16)
+ ? 0
+ : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match) != LZ4_read32(ip)));
+ }
/* Catch up */
- while ((ip > anchor) && (ref > (u8 *)source)
- && (ip[-1] == ref[-1])) {
- ip--;
- ref--;
- }
+ while ((ip > anchor)
+ && (match > lowLimit)
+ && (unlikely(ip[-1] == match[-1]))) {
+ ip--; match--;
+ }
/* Encode Literal length */
- length = (int)(ip - anchor);
- token = op++;
- /* Check output limit */
- if (unlikely(op + length + (2 + 1 + LASTLITERALS)
- + (length >> 8) > oend))
- return 0;
- if (length >= (int)RUN_MASK) {
- *token = (RUN_MASK << ML_BITS);
- len = length - RUN_MASK;
- for (; len > 254 ; len -= 255)
- *op++ = 255;
- *op++ = (u8)len;
- } else
- *token = (length << ML_BITS);
+ { unsigned int litLength = (unsigned int)(ip - anchor);
- /* Copy Literals */
- LZ4_BLINDCOPY(anchor, op, length);
+ token = op++;
+ if (op + ((litLength + 240)/255)
+ + litLength > oMaxLit) {
+ /* Not enough space for a last match */
+ op--;
+ goto _last_literals;
+ }
+ if (litLength >= RUN_MASK) {
+ unsigned int len = litLength - RUN_MASK;
+ *token = (RUN_MASK<<ML_BITS);
+ for (; len >= 255 ; len -= 255)
+ *op++ = 255;
+ *op++ = (BYTE)len;
+ } else
+ *token = (BYTE)(litLength<<ML_BITS);
+
+ /* Copy Literals */
+ LZ4_wildCopy(op, anchor, op + litLength);
+ op += litLength;
+ }
_next_match:
/* Encode Offset */
- LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
+ LZ4_writeLE16(op, (U16)(ip - match)); op += 2;
- /* Start Counting */
- ip += MINMATCH;
- /* MinMatch verified */
- ref += MINMATCH;
- anchor = ip;
+ /* Encode MatchLength */
+ { size_t matchLength = LZ4_count(ip + MINMATCH,
+ match + MINMATCH, matchlimit);
- while (ip < MATCHLIMIT - (STEPSIZE - 1)) {
- #if LZ4_ARCH64
- u64 diff = A64(ref) ^ A64(ip);
- #else
- u32 diff = A32(ref) ^ A32(ip);
- #endif
-
- if (!diff) {
- ip += STEPSIZE;
- ref += STEPSIZE;
- continue;
+ if (op + ((matchLength + 240)/255) > oMaxMatch) {
+ /* Match description too long : reduce it */
+ matchLength = (15 - 1) + (oMaxMatch - op) * 255;
}
- ip += LZ4_NBCOMMONBYTES(diff);
- goto _endcount;
- }
- #if LZ4_ARCH64
- if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
- ip += 4;
- ref += 4;
+ ip += MINMATCH + matchLength;
+
+ if (matchLength >= ML_MASK) {
+ *token += ML_MASK;
+ matchLength -= ML_MASK;
+ while (matchLength >= 255) {
+ matchLength -= 255; *op++ = 255;
+ }
+ *op++ = (BYTE)matchLength;
+ } else
+ *token += (BYTE)(matchLength);
}
- #endif
- if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
- ip += 2;
- ref += 2;
- }
- if ((ip < MATCHLIMIT) && (*ref == *ip))
- ip++;
-_endcount:
- /* Encode MatchLength */
- len = (int)(ip - anchor);
- /* Check output limit */
- if (unlikely(op + (1 + LASTLITERALS) + (len >> 8) > oend))
- return 0;
- if (len >= (int)ML_MASK) {
- *token += ML_MASK;
- len -= ML_MASK;
- for (; len > 509 ; len -= 510) {
- *op++ = 255;
- *op++ = 255;
- }
- if (len > 254) {
- len -= 255;
- *op++ = 255;
- }
- *op++ = (u8)len;
- } else
- *token += len;
+ anchor = ip;
- /* Test end of chunk */
- if (ip > mflimit) {
- anchor = ip;
+ /* Test end of block */
+ if (ip > mflimit)
+ break;
+ if (op > oMaxSeq)
break;
- }
/* Fill table */
- hashtable[LZ4_HASH64K_VALUE(ip-2)] = (u16)(ip - 2 - base);
+ LZ4_putPosition(ip - 2, ctx->hashTable, tableType, base);
/* Test next position */
- ref = base + hashtable[LZ4_HASH64K_VALUE(ip)];
- hashtable[LZ4_HASH64K_VALUE(ip)] = (u16)(ip - base);
- if (A32(ref) == A32(ip)) {
- token = op++;
- *token = 0;
+ match = LZ4_getPosition(ip, ctx->hashTable, tableType, base);
+ LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+
+ if ((match + MAX_DISTANCE >= ip)
+ && (LZ4_read32(match) == LZ4_read32(ip))) {
+ token = op++; *token = 0;
goto _next_match;
}
/* Prepare next loop */
- anchor = ip++;
- forwardh = LZ4_HASH64K_VALUE(ip);
+ forwardH = LZ4_hashPosition(++ip, tableType);
}
_last_literals:
/* Encode Last Literals */
- lastrun = (int)(iend - anchor);
- if (op + lastrun + 1 + (lastrun - RUN_MASK + 255) / 255 > oend)
- return 0;
- if (lastrun >= (int)RUN_MASK) {
- *op++ = (RUN_MASK << ML_BITS);
- lastrun -= RUN_MASK;
- for (; lastrun > 254 ; lastrun -= 255)
- *op++ = 255;
- *op++ = (u8)lastrun;
- } else
- *op++ = (lastrun << ML_BITS);
- memcpy(op, anchor, iend - anchor);
- op += iend - anchor;
+ { size_t lastRunSize = (size_t)(iend - anchor);
+
+ if (op + 1 /* token */
+ + ((lastRunSize + 240)/255) /* litLength */
+ + lastRunSize /* literals */ > oend) {
+ /* adapt lastRunSize to fill 'dst' */
+ lastRunSize = (oend - op) - 1;
+ lastRunSize -= (lastRunSize + 240)/255;
+ }
+ ip = anchor + lastRunSize;
+
+ if (lastRunSize >= RUN_MASK) {
+ size_t accumulator = lastRunSize - RUN_MASK;
+
+ *op++ = RUN_MASK << ML_BITS;
+ for (; accumulator >= 255 ; accumulator -= 255)
+ *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRunSize<<ML_BITS);
+ }
+ memcpy(op, anchor, lastRunSize);
+ op += lastRunSize;
+ }
+
/* End */
- return (int)(((char *)op) - dest);
+ *srcSizePtr = (int) (((const char *)ip) - src);
+ return (int) (((char *)op) - dst);
}
-int lz4_compress(const unsigned char *src, size_t src_len,
- unsigned char *dst, size_t *dst_len, void *wrkmem)
+static int LZ4_compress_destSize_extState(LZ4_stream_t *state, const char *src,
+ char *dst, int *srcSizePtr, int targetDstSize)
{
- int ret = -1;
- int out_len = 0;
+ #if LZ4_ARCH64
+ tableType_t tableType = byU32;
+ #else
+ tableType_t tableType = byPtr;
+ #endif
+
+ LZ4_resetStream(state);
+
+ if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) {
+ /* compression success is guaranteed */
+ return LZ4_compress_fast_extState(
+ state, src, dst, *srcSizePtr,
+ targetDstSize, 1);
+ } else {
+ if (*srcSizePtr < LZ4_64Klimit)
+ return LZ4_compress_destSize_generic(
+ &state->internal_donotuse,
+ src, dst, srcSizePtr,
+ targetDstSize, byU16);
+ else
+ return LZ4_compress_destSize_generic(
+ &state->internal_donotuse,
+ src, dst, srcSizePtr,
+ targetDstSize, tableType);
+ }
+}
- if (src_len < LZ4_64KLIMIT)
- out_len = lz4_compress64kctx(wrkmem, src, dst, src_len,
- lz4_compressbound(src_len));
- else
- out_len = lz4_compressctx(wrkmem, src, dst, src_len,
- lz4_compressbound(src_len));
- if (out_len < 0)
- goto exit;
+int LZ4_compress_destSize(const char *src, char *dst, int *srcSizePtr,
+ int targetDstSize, void *wrkmem)
+{
+ return LZ4_compress_destSize_extState(wrkmem, src, dst, srcSizePtr,
+ targetDstSize);
+}
+EXPORT_SYMBOL(LZ4_compress_destSize);
+
+/*-******************************
+ * Streaming functions
+ ********************************/
+void LZ4_resetStream(LZ4_stream_t *LZ4_stream)
+{
+ memset(LZ4_stream, 0, sizeof(LZ4_stream_t));
+}
+
+#define HASH_UNIT sizeof(reg_t)
+int LZ4_loadDict(LZ4_stream_t *LZ4_dict,
+ const char *dictionary, int dictSize)
+{
+ LZ4_stream_t_internal *dict = &LZ4_dict->internal_donotuse;
+ const BYTE *p = (const BYTE *)dictionary;
+ const BYTE * const dictEnd = p + dictSize;
+ const BYTE *base;
+
+ if ((dict->initCheck)
+ || (dict->currentOffset > 1 * GB)) {
+ /* Uninitialized structure, or reuse overflow */
+ LZ4_resetStream(LZ4_dict);
+ }
+
+ if (dictSize < (int)HASH_UNIT) {
+ dict->dictionary = NULL;
+ dict->dictSize = 0;
+ return 0;
+ }
+
+ if ((dictEnd - p) > 64 * KB)
+ p = dictEnd - 64 * KB;
+ dict->currentOffset += 64 * KB;
+ base = p - dict->currentOffset;
+ dict->dictionary = p;
+ dict->dictSize = (U32)(dictEnd - p);
+ dict->currentOffset += dict->dictSize;
+
+ while (p <= dictEnd - HASH_UNIT) {
+ LZ4_putPosition(p, dict->hashTable, byU32, base);
+ p += 3;
+ }
- *dst_len = out_len;
+ return dict->dictSize;
+}
+EXPORT_SYMBOL(LZ4_loadDict);
+
+static void LZ4_renormDictT(LZ4_stream_t_internal *LZ4_dict,
+ const BYTE *src)
+{
+ if ((LZ4_dict->currentOffset > 0x80000000) ||
+ ((uptrval)LZ4_dict->currentOffset > (uptrval)src)) {
+ /* address space overflow */
+ /* rescale hash table */
+ U32 const delta = LZ4_dict->currentOffset - 64 * KB;
+ const BYTE *dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
+ int i;
+
+ for (i = 0; i < LZ4_HASH_SIZE_U32; i++) {
+ if (LZ4_dict->hashTable[i] < delta)
+ LZ4_dict->hashTable[i] = 0;
+ else
+ LZ4_dict->hashTable[i] -= delta;
+ }
+ LZ4_dict->currentOffset = 64 * KB;
+ if (LZ4_dict->dictSize > 64 * KB)
+ LZ4_dict->dictSize = 64 * KB;
+ LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
+ }
+}
+
+int LZ4_saveDict(LZ4_stream_t *LZ4_dict, char *safeBuffer, int dictSize)
+{
+ LZ4_stream_t_internal * const dict = &LZ4_dict->internal_donotuse;
+ const BYTE * const previousDictEnd = dict->dictionary + dict->dictSize;
+
+ if ((U32)dictSize > 64 * KB) {
+ /* useless to define a dictionary > 64 * KB */
+ dictSize = 64 * KB;
+ }
+ if ((U32)dictSize > dict->dictSize)
+ dictSize = dict->dictSize;
- return 0;
-exit:
- return ret;
+ memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
+
+ dict->dictionary = (const BYTE *)safeBuffer;
+ dict->dictSize = (U32)dictSize;
+
+ return dictSize;
+}
+EXPORT_SYMBOL(LZ4_saveDict);
+
+int LZ4_compress_fast_continue(LZ4_stream_t *LZ4_stream, const char *source,
+ char *dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ LZ4_stream_t_internal *streamPtr = &LZ4_stream->internal_donotuse;
+ const BYTE * const dictEnd = streamPtr->dictionary
+ + streamPtr->dictSize;
+
+ const BYTE *smallest = (const BYTE *) source;
+
+ if (streamPtr->initCheck) {
+ /* Uninitialized structure detected */
+ return 0;
+ }
+
+ if ((streamPtr->dictSize > 0) && (smallest > dictEnd))
+ smallest = dictEnd;
+
+ LZ4_renormDictT(streamPtr, smallest);
+
+ if (acceleration < 1)
+ acceleration = LZ4_ACCELERATION_DEFAULT;
+
+ /* Check overlapping input/dictionary space */
+ { const BYTE *sourceEnd = (const BYTE *) source + inputSize;
+
+ if ((sourceEnd > streamPtr->dictionary)
+ && (sourceEnd < dictEnd)) {
+ streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
+ if (streamPtr->dictSize > 64 * KB)
+ streamPtr->dictSize = 64 * KB;
+ if (streamPtr->dictSize < 4)
+ streamPtr->dictSize = 0;
+ streamPtr->dictionary = dictEnd - streamPtr->dictSize;
+ }
+ }
+
+ /* prefix mode : source data follows dictionary */
+ if (dictEnd == (const BYTE *)source) {
+ int result;
+
+ if ((streamPtr->dictSize < 64 * KB) &&
+ (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(
+ streamPtr, source, dest, inputSize,
+ maxOutputSize, limitedOutput, byU32,
+ withPrefix64k, dictSmall, acceleration);
+ } else {
+ result = LZ4_compress_generic(
+ streamPtr, source, dest, inputSize,
+ maxOutputSize, limitedOutput, byU32,
+ withPrefix64k, noDictIssue, acceleration);
+ }
+ streamPtr->dictSize += (U32)inputSize;
+ streamPtr->currentOffset += (U32)inputSize;
+ return result;
+ }
+
+ /* external dictionary mode */
+ { int result;
+
+ if ((streamPtr->dictSize < 64 * KB) &&
+ (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(
+ streamPtr, source, dest, inputSize,
+ maxOutputSize, limitedOutput, byU32,
+ usingExtDict, dictSmall, acceleration);
+ } else {
+ result = LZ4_compress_generic(
+ streamPtr, source, dest, inputSize,
+ maxOutputSize, limitedOutput, byU32,
+ usingExtDict, noDictIssue, acceleration);
+ }
+ streamPtr->dictionary = (const BYTE *)source;
+ streamPtr->dictSize = (U32)inputSize;
+ streamPtr->currentOffset += (U32)inputSize;
+ return result;
+ }
+}
+EXPORT_SYMBOL(LZ4_compress_fast_continue);
+
+/*-******************************
+ * For backwards compatibility
+ ********************************/
+int lz4_compress(const unsigned char *src, size_t src_len, unsigned char *dst,
+ size_t *dst_len, void *wrkmem) {
+ *dst_len = LZ4_compress_default(src, dst, (int)src_len,
+ (int)((size_t)dst_len), wrkmem);
+
+ /*
+ * Prior lz4_compress will return -1 in case of error
+ * and 0 on success
+ * while new LZ4_compress_fast/default
+ * returns 0 in case of error
+ * and the output length on success
+ */
+ if (!dst_len)
+ return -1;
+ else
+ return 0;
}
EXPORT_SYMBOL(lz4_compress);
diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c
index 6d940c7..ca71375 100644
--- a/lib/lz4/lz4_decompress.c
+++ b/lib/lz4/lz4_decompress.c
@@ -1,25 +1,16 @@
/*
- * LZ4 Decompressor for Linux kernel
- *
- * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
- *
- * Based on LZ4 implementation by Yann Collet.
- *
* LZ4 - Fast LZ compression algorithm
- * Copyright (C) 2011-2012, Yann Collet.
- * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
- *
+ * Copyright (C) 2011 - 2016, Yann Collet.
+ * BSD 2 - Clause License (http://www.opensource.org/licenses/bsd - license.php)
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
- *
- * * Redistributions of source code must retain the above copyright
+ * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
+ * * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
- *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -31,313 +22,508 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * You can contact the author at :
+ * - LZ4 homepage : http://www.lz4.org
+ * - LZ4 source repository : https://github.com/lz4/lz4
*
- * You can contact the author at :
- * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
- * - LZ4 source repository : http://code.google.com/p/lz4/
+ * Changed for kernel usage by:
+ * Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
*/
-#ifndef STATIC
+/*-************************************
+ * Dependencies
+ **************************************/
+#include <linux/lz4.h>
+#include "lz4defs.h"
+#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#endif
-#include <linux/lz4.h>
-
#include <asm/unaligned.h>
-#include "lz4defs.h"
-
-static const int dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};
-#if LZ4_ARCH64
-static const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
-#endif
-
-static int lz4_uncompress(const char *source, char *dest, int osize)
+/*-*****************************
+ * Decompression functions
+ *******************************/
+/* LZ4_decompress_generic() :
+ * This generic decompression function cover all use cases.
+ * It shall be instantiated several times, using different sets of directives
+ * Note that it is important this generic function is really inlined,
+ * in order to remove useless branches during compilation optimization.
+ */
+static inline int LZ4_decompress_generic(
+ const char *const source,
+ char * const dest,
+ int inputSize,
+ /*
+ * If endOnInput == endOnInputSize,
+ * this value is the max size of Output Buffer.
+ */
+ int outputSize,
+ /* endOnOutputSize, endOnInputSize */
+ int endOnInput,
+ /* full, partial */
+ int partialDecoding,
+ /* only used if partialDecoding == partial */
+ int targetOutputSize,
+ /* noDict, withPrefix64k, usingExtDict */
+ int dict,
+ /* == dest when no prefix */
+ const BYTE * const lowPrefix,
+ /* only if dict == usingExtDict */
+ const BYTE * const dictStart,
+ /* note : = 0 if noDict */
+ const size_t dictSize
+ )
{
+ /* Local Variables */
const BYTE *ip = (const BYTE *) source;
- const BYTE *ref;
+ const BYTE * const iend = ip + inputSize;
+
BYTE *op = (BYTE *) dest;
- BYTE * const oend = op + osize;
+ BYTE * const oend = op + outputSize;
BYTE *cpy;
- unsigned token;
- size_t length;
+ BYTE *oexit = op + targetOutputSize;
+ const BYTE * const lowLimit = lowPrefix - dictSize;
+
+ const BYTE * const dictEnd = (const BYTE *)dictStart + dictSize;
+ const unsigned int dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4};
+ const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+
+ const int safeDecode = (endOnInput == endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 * KB)));
+ /* Special cases */
+ /* targetOutputSize too high => decode everything */
+ if ((partialDecoding) && (oexit > oend - MFLIMIT))
+ oexit = oend - MFLIMIT;
+
+ /* Empty output buffer */
+ if ((endOnInput) && (unlikely(outputSize == 0)))
+ return ((inputSize == 1) && (*ip == 0)) ? 0 : -1;
+
+ if ((!endOnInput) && (unlikely(outputSize == 0)))
+ return (*ip == 0 ? 1 : -1);
+
+ /* Main Loop : decode sequences */
while (1) {
+ size_t length;
+ const BYTE *match;
+ size_t offset;
+
+ /* get literal length */
+ unsigned int const token = *ip++;
+
+ length = token>>ML_BITS;
- /* get runlength */
- token = *ip++;
- length = (token >> ML_BITS);
if (length == RUN_MASK) {
- size_t len;
+ unsigned int s;
- len = *ip++;
- for (; len == 255; length += 255)
- len = *ip++;
- if (unlikely(length > (size_t)(length + len)))
+ do {
+ s = *ip++;
+ length += s;
+ } while (likely(endOnInput
+ ? ip < iend - RUN_MASK
+ : 1) & (s == 255));
+
+ if ((safeDecode)
+ && unlikely(
+ (size_t)(op + length) < (size_t)(op))) {
+ /* overflow detection */
goto _output_error;
- length += len;
+ }
+ if ((safeDecode)
+ && unlikely(
+ (size_t)(ip + length) < (size_t)(ip))) {
+ /* overflow detection */
+ goto _output_error;
+ }
}
/* copy literals */
cpy = op + length;
- if (unlikely(cpy > oend - COPYLENGTH)) {
- /*
- * Error: not enough place for another match
- * (min 4) + 5 literals
- */
- if (cpy != oend)
- goto _output_error;
-
+ if (((endOnInput) && ((cpy > (partialDecoding ? oexit : oend - MFLIMIT))
+ || (ip + length > iend - (2 + 1 + LASTLITERALS))))
+ || ((!endOnInput) && (cpy > oend - WILDCOPYLENGTH))) {
+ if (partialDecoding) {
+ if (cpy > oend) {
+ /*
+ * Error :
+ * write attempt beyond end of output buffer
+ */
+ goto _output_error;
+ }
+ if ((endOnInput)
+ && (ip + length > iend)) {
+ /*
+ * Error :
+ * read attempt beyond
+ * end of input buffer
+ */
+ goto _output_error;
+ }
+ } else {
+ if ((!endOnInput)
+ && (cpy != oend)) {
+ /*
+ * Error :
+ * block decoding must
+ * stop exactly there
+ */
+ goto _output_error;
+ }
+ if ((endOnInput)
+ && ((ip + length != iend)
+ || (cpy > oend))) {
+ /*
+ * Error :
+ * input must be consumed
+ */
+ goto _output_error;
+ }
+ }
memcpy(op, ip, length);
ip += length;
- break; /* EOF */
+ op += length;
+ /* Necessarily EOF, due to parsing restrictions */
+ break;
}
- LZ4_WILDCOPY(ip, op, cpy);
- ip -= (op - cpy);
- op = cpy;
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
/* get offset */
- LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
- ip += 2;
-
- /* Error: offset create reference outside destination buffer */
- if (unlikely(ref < (BYTE *const) dest))
+ offset = LZ4_readLE16(ip); ip += 2;
+ match = op - offset;
+ if ((checkOffset) && (unlikely(match < lowLimit))) {
+ /* Error : offset outside buffers */
goto _output_error;
+ }
+ /* costs ~1%; silence an msan warning when offset == 0 */
+ LZ4_write32(op, (U32)offset);
/* get matchlength */
length = token & ML_MASK;
if (length == ML_MASK) {
- for (; *ip == 255; length += 255)
- ip++;
- if (unlikely(length > (size_t)(length + *ip)))
+ unsigned int s;
+
+ do {
+ s = *ip++;
+ if ((endOnInput) && (ip > iend - LASTLITERALS))
+ goto _output_error;
+ length += s;
+ } while (s == 255);
+ if ((safeDecode)
+ && unlikely(
+ (size_t)(op + length) < (size_t)op)) {
+ /* overflow detection */
goto _output_error;
- length += *ip++;
+ }
}
+ length += MINMATCH;
+
+ /* check external dictionary */
+ if ((dict == usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op + length > oend - LASTLITERALS)) {
+ /* doesn't respect parsing restriction */
+ goto _output_error;
+ }
- /* copy repeated sequence */
- if (unlikely((op - ref) < STEPSIZE)) {
-#if LZ4_ARCH64
- int dec64 = dec64table[op - ref];
-#else
- const int dec64 = 0;
-#endif
- op[0] = ref[0];
- op[1] = ref[1];
- op[2] = ref[2];
- op[3] = ref[3];
- op += 4;
- ref += 4;
- ref -= dec32table[op-ref];
- PUT4(ref, op);
- op += STEPSIZE - 4;
- ref -= dec64;
+ if (length <= (size_t)(lowPrefix - match)) {
+ /*
+ * match can be copied as a single segment
+ * from external dictionary
+ */
+ memmove(op, dictEnd - (lowPrefix - match), length);
+ op += length;
+ } else {
+ /*
+ * match encompass external
+ * dictionary and current block
+ */
+ size_t const copySize = (size_t)(lowPrefix - match);
+ size_t const restSize = length - copySize;
+
+ memcpy(op, dictEnd - copySize, copySize);
+ op += copySize;
+
+ if (restSize > (size_t)(op - lowPrefix)) {
+ /* overlap copy */
+ BYTE * const endOfMatch = op + restSize;
+ const BYTE *copyFrom = lowPrefix;
+
+ while (op < endOfMatch)
+ *op++ = *copyFrom++;
+ } else {
+ memcpy(op, lowPrefix, restSize);
+ op += restSize;
+ }
+ }
+ continue;
+ }
+
+ /* copy match within block */
+ cpy = op + length;
+ if (unlikely(offset < 8)) {
+ const int dec64 = dec64table[offset];
+
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[offset];
+ memcpy(op + 4, match, 4);
+ match -= dec64;
} else {
- LZ4_COPYSTEP(ref, op);
+ LZ4_copy8(op, match); match += 8;
}
- cpy = op + length - (STEPSIZE - 4);
- if (cpy > (oend - COPYLENGTH)) {
- /* Error: request to write beyond destination buffer */
- if (cpy > oend)
- goto _output_error;
-#if LZ4_ARCH64
- if ((ref + COPYLENGTH) > oend)
-#else
- if ((ref + COPYLENGTH) > oend ||
- (op + COPYLENGTH) > oend)
-#endif
+ op += 8;
+
+ if (unlikely(cpy > oend - 12)) {
+ BYTE * const oCopyLimit = oend - (WILDCOPYLENGTH - 1);
+
+ if (cpy > oend - LASTLITERALS) {
+ /*
+ * Error : last LASTLITERALS bytes
+ * must be literals (uncompressed)
+ */
goto _output_error;
- LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
+ }
+ if (op < oCopyLimit) {
+ LZ4_wildCopy(op, match, oCopyLimit);
+ match += oCopyLimit - op;
+ op = oCopyLimit;
+ }
while (op < cpy)
- *op++ = *ref++;
- op = cpy;
- /*
- * Check EOF (should never happen, since last 5 bytes
- * are supposed to be literals)
- */
- if (op == oend)
- goto _output_error;
- continue;
+ *op++ = *match++;
+ } else {
+ LZ4_copy8(op, match);
+ if (length > 16)
+ LZ4_wildCopy(op + 8, match + 8, cpy);
}
- LZ4_SECURECOPY(ref, op, cpy);
op = cpy; /* correction */
}
+
/* end of decoding */
- return (int) (((char *)ip) - source);
+ if (endOnInput) {
+ /* Nb of output bytes decoded */
+ return (int) (((char *)op) - dest);
+ } else {
+ /* Nb of input bytes read */
+ return (int) (((const char *)ip) - source);
+ }
- /* write overflow error detected */
+ /* Overflow error detected */
_output_error:
return -1;
}
-static int lz4_uncompress_unknownoutputsize(const char *source, char *dest,
- int isize, size_t maxoutputsize)
+int LZ4_decompress_safe(const char *source, char *dest,
+ int compressedSize, int maxDecompressedSize)
{
- const BYTE *ip = (const BYTE *) source;
- const BYTE *const iend = ip + isize;
- const BYTE *ref;
-
+ return LZ4_decompress_generic(source, dest, compressedSize,
+ maxDecompressedSize, endOnInputSize, full, 0,
+ noDict, (BYTE *)dest, NULL, 0);
+}
+EXPORT_SYMBOL(LZ4_decompress_safe);
- BYTE *op = (BYTE *) dest;
- BYTE * const oend = op + maxoutputsize;
- BYTE *cpy;
+int LZ4_decompress_safe_partial(const char *source, char *dest,
+ int compressedSize, int targetOutputSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize,
+ maxDecompressedSize, endOnInputSize, partial,
+ targetOutputSize, noDict, (BYTE *)dest, NULL, 0);
+}
+EXPORT_SYMBOL(LZ4_decompress_safe_partial);
- /* Main Loop */
- while (ip < iend) {
+int LZ4_decompress_fast(const char *source, char *dest, int originalSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0, withPrefix64k,
+ (BYTE *)(dest - 64 * KB), NULL, 64 * KB);
+}
+EXPORT_SYMBOL(LZ4_decompress_fast);
- unsigned token;
- size_t length;
+int LZ4_setStreamDecode(LZ4_streamDecode_t *LZ4_streamDecode,
+ const char *dictionary, int dictSize)
+{
+ LZ4_streamDecode_t_internal *lz4sd = (LZ4_streamDecode_t_internal *) LZ4_streamDecode;
- /* get runlength */
- token = *ip++;
- length = (token >> ML_BITS);
- if (length == RUN_MASK) {
- int s = 255;
- while ((ip < iend) && (s == 255)) {
- s = *ip++;
- if (unlikely(length > (size_t)(length + s)))
- goto _output_error;
- length += s;
- }
- }
- /* copy literals */
- cpy = op + length;
- if ((cpy > oend - COPYLENGTH) ||
- (ip + length > iend - COPYLENGTH)) {
-
- if (cpy > oend)
- goto _output_error;/* writes beyond buffer */
-
- if (ip + length != iend)
- goto _output_error;/*
- * Error: LZ4 format requires
- * to consume all input
- * at this stage
- */
- memcpy(op, ip, length);
- op += length;
- break;/* Necessarily EOF, due to parsing restrictions */
- }
- LZ4_WILDCOPY(ip, op, cpy);
- ip -= (op - cpy);
- op = cpy;
+ lz4sd->prefixSize = (size_t) dictSize;
+ lz4sd->prefixEnd = (const BYTE *) dictionary + dictSize;
+ lz4sd->externalDict = NULL;
+ lz4sd->extDictSize = 0;
+ return 1;
+}
+EXPORT_SYMBOL(LZ4_setStreamDecode);
- /* get offset */
- LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
- ip += 2;
- if (ref < (BYTE * const) dest)
- goto _output_error;
- /*
- * Error : offset creates reference
- * outside of destination buffer
- */
+/*
+ * *_continue() :
+ * These decoding functions allow decompression of multiple blocks
+ * in "streaming" mode.
+ * Previously decoded blocks must still be available at the memory
+ * position where they were decoded.
+ * If it's not possible, save the relevant part of
+ * decoded data into a safe buffer,
+ * and indicate where it stands using LZ4_setStreamDecode()
+ */
+int LZ4_decompress_safe_continue(LZ4_streamDecode_t *LZ4_streamDecode,
+ const char *source, char *dest, int compressedSize, int maxOutputSize)
+{
+ LZ4_streamDecode_t_internal *lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE *)dest) {
+ result = LZ4_decompress_generic(source, dest,
+ compressedSize,
+ maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize,
+ lz4sd->externalDict,
+ lz4sd->extDictSize);
+
+ if (result <= 0)
+ return result;
+
+ lz4sd->prefixSize += result;
+ lz4sd->prefixEnd += result;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest,
+ compressedSize, maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, (BYTE *)dest,
+ lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0)
+ return result;
+ lz4sd->prefixSize = result;
+ lz4sd->prefixEnd = (BYTE *)dest + result;
+ }
- /* get matchlength */
- length = (token & ML_MASK);
- if (length == ML_MASK) {
- while (ip < iend) {
- int s = *ip++;
- if (unlikely(length > (size_t)(length + s)))
- goto _output_error;
- length += s;
- if (s == 255)
- continue;
- break;
- }
- }
+ return result;
+}
+EXPORT_SYMBOL(LZ4_decompress_safe_continue);
- /* copy repeated sequence */
- if (unlikely((op - ref) < STEPSIZE)) {
-#if LZ4_ARCH64
- int dec64 = dec64table[op - ref];
-#else
- const int dec64 = 0;
-#endif
- op[0] = ref[0];
- op[1] = ref[1];
- op[2] = ref[2];
- op[3] = ref[3];
- op += 4;
- ref += 4;
- ref -= dec32table[op - ref];
- PUT4(ref, op);
- op += STEPSIZE - 4;
- ref -= dec64;
- } else {
- LZ4_COPYSTEP(ref, op);
- }
- cpy = op + length - (STEPSIZE-4);
- if (cpy > oend - COPYLENGTH) {
- if (cpy > oend)
- goto _output_error; /* write outside of buf */
-#if LZ4_ARCH64
- if ((ref + COPYLENGTH) > oend)
-#else
- if ((ref + COPYLENGTH) > oend ||
- (op + COPYLENGTH) > oend)
-#endif
- goto _output_error;
- LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
- while (op < cpy)
- *op++ = *ref++;
- op = cpy;
- /*
- * Check EOF (should never happen, since last 5 bytes
- * are supposed to be literals)
- */
- if (op == oend)
- goto _output_error;
- continue;
- }
- LZ4_SECURECOPY(ref, op, cpy);
- op = cpy; /* correction */
+int LZ4_decompress_fast_continue(LZ4_streamDecode_t *LZ4_streamDecode,
+ const char *source, char *dest, int originalSize)
+{
+ LZ4_streamDecode_t_internal *lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE *)dest) {
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict,
+ lz4sd->prefixEnd - lz4sd->prefixSize,
+ lz4sd->externalDict, lz4sd->extDictSize);
+
+ if (result <= 0)
+ return result;
+
+ lz4sd->prefixSize += originalSize;
+ lz4sd->prefixEnd += originalSize;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict, (BYTE *)dest,
+ lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0)
+ return result;
+ lz4sd->prefixSize = originalSize;
+ lz4sd->prefixEnd = (BYTE *)dest + originalSize;
}
- /* end of decoding */
- return (int) (((char *) op) - dest);
- /* write overflow error detected */
-_output_error:
- return -1;
+ return result;
}
+EXPORT_SYMBOL(LZ4_decompress_fast_continue);
-int lz4_decompress(const unsigned char *src, size_t *src_len,
- unsigned char *dest, size_t actual_dest_len)
+/*
+ * Advanced decoding functions :
+ * *_usingDict() :
+ * These decoding functions work the same as "_continue" ones,
+ * the dictionary must be explicitly provided within parameters
+ */
+static inline int LZ4_decompress_usingDict_generic(const char *source,
+ char *dest, int compressedSize, int maxOutputSize, int safe,
+ const char *dictStart, int dictSize)
{
- int ret = -1;
- int input_len = 0;
-
- input_len = lz4_uncompress(src, dest, actual_dest_len);
- if (input_len < 0)
- goto exit_0;
- *src_len = input_len;
+ if (dictSize == 0)
+ return LZ4_decompress_generic(source, dest,
+ compressedSize, maxOutputSize, safe, full, 0,
+ noDict, (BYTE *)dest, NULL, 0);
+ if (dictStart + dictSize == dest) {
+ if (dictSize >= (int)(64 * KB - 1))
+ return LZ4_decompress_generic(source, dest,
+ compressedSize, maxOutputSize, safe, full, 0,
+ withPrefix64k, (BYTE *)dest - 64 * KB, NULL, 0);
+ return LZ4_decompress_generic(source, dest, compressedSize,
+ maxOutputSize, safe, full, 0, noDict,
+ (BYTE *)dest - dictSize, NULL, 0);
+ }
+ return LZ4_decompress_generic(source, dest, compressedSize,
+ maxOutputSize, safe, full, 0, usingExtDict,
+ (BYTE *)dest, (const BYTE *)dictStart, dictSize);
+}
- return 0;
-exit_0:
- return ret;
+int LZ4_decompress_safe_usingDict(const char *source, char *dest,
+ int compressedSize, int maxOutputSize,
+ const char *dictStart, int dictSize)
+{
+ return LZ4_decompress_usingDict_generic(source, dest,
+ compressedSize, maxOutputSize, 1, dictStart, dictSize);
}
-#ifndef STATIC
-EXPORT_SYMBOL(lz4_decompress);
-#endif
+EXPORT_SYMBOL(LZ4_decompress_safe_usingDict);
-int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len,
- unsigned char *dest, size_t *dest_len)
+int LZ4_decompress_fast_usingDict(const char *source, char *dest,
+ int originalSize, const char *dictStart, int dictSize)
{
- int ret = -1;
- int out_len = 0;
-
- out_len = lz4_uncompress_unknownoutputsize(src, dest, src_len,
- *dest_len);
- if (out_len < 0)
- goto exit_0;
- *dest_len = out_len;
-
- return 0;
-exit_0:
- return ret;
+ return LZ4_decompress_usingDict_generic(source, dest, 0,
+ originalSize, 0, dictStart, dictSize);
+}
+EXPORT_SYMBOL(LZ4_decompress_fast_usingDict);
+
+/*-******************************
+ * For backwards compatibility
+ ********************************/
+int lz4_decompress_unknownoutputsize(const unsigned char *src,
+ size_t src_len, unsigned char *dest, size_t *dest_len) {
+ *dest_len = LZ4_decompress_safe(src, dest,
+ (int)src_len, (int)((size_t)dest_len));
+
+ /*
+ * Prior lz4_decompress_unknownoutputsize will return
+ * 0 for success and a negative result for error
+ * new LZ4_decompress_safe returns
+ * - the length of data read on success
+ * - and also a negative result on error
+ * meaning when result > 0, we just return 0 here
+ */
+ if (src_len > 0) {
+ return 0;
+ } else
+ return -1;
}
-#ifndef STATIC
EXPORT_SYMBOL(lz4_decompress_unknownoutputsize);
+int lz4_decompress(const unsigned char *src, size_t *src_len,
+ unsigned char *dest, size_t actual_dest_len) {
+ *src_len = LZ4_decompress_fast(src, dest, (int)actual_dest_len);
+
+ /*
+ * Prior lz4_decompress will return
+ * 0 for success and a negative result for error
+ * new LZ4_decompress_fast returns
+ * - the length of data read on success
+ * - and also a negative result on error
+ * meaning when result > 0, we just return 0 here
+ */
+ if ((int)((size_t)src_len) > 0)
+ return 0;
+ else
+ return -1;
+}
+EXPORT_SYMBOL(lz4_decompress);
+
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION("LZ4 Decompressor");
-#endif
+MODULE_DESCRIPTION("LZ4 decompressor");
diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h
index c79d7ea..b0d21b5 100644
--- a/lib/lz4/lz4defs.h
+++ b/lib/lz4/lz4defs.h
@@ -1,157 +1,219 @@
+#ifndef __LZ4DEFS_H__
+#define __LZ4DEFS_H__
+
/*
- * lz4defs.h -- architecture specific defines
- *
- * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
+ * lz4defs.h -- common and architecture specific defines for the kernel usage
+
+ * LZ4 - Fast LZ compression algorithm
+ * Copyright (C) 2011-2016, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * You can contact the author at :
+ * - LZ4 homepage : http://www.lz4.org
+ * - LZ4 source repository : https://github.com/lz4/lz4
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * Changed for kernel usage by:
+ * Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
*/
+#include <asm/unaligned.h>
+#include <linux/string.h> /* memset, memcpy */
+
/*
* Detects 64 bits mode
- */
+*/
#if defined(CONFIG_64BIT)
#define LZ4_ARCH64 1
#else
#define LZ4_ARCH64 0
#endif
-/*
- * Architecture-specific macros
- */
-#define BYTE u8
-typedef struct _U16_S { u16 v; } U16_S;
-typedef struct _U32_S { u32 v; } U32_S;
-typedef struct _U64_S { u64 v; } U64_S;
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-
-#define A16(x) (((U16_S *)(x))->v)
-#define A32(x) (((U32_S *)(x))->v)
-#define A64(x) (((U64_S *)(x))->v)
-
-#define PUT4(s, d) (A32(d) = A32(s))
-#define PUT8(s, d) (A64(d) = A64(s))
-
-#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
- (d = s - A16(p))
-
-#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \
- do { \
- A16(p) = v; \
- p += 2; \
- } while (0)
-#else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */
-
-#define A64(x) get_unaligned((u64 *)&(((U16_S *)(x))->v))
-#define A32(x) get_unaligned((u32 *)&(((U16_S *)(x))->v))
-#define A16(x) get_unaligned((u16 *)&(((U16_S *)(x))->v))
-
-#define PUT4(s, d) \
- put_unaligned(get_unaligned((const u32 *) s), (u32 *) d)
-#define PUT8(s, d) \
- put_unaligned(get_unaligned((const u64 *) s), (u64 *) d)
-
-#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
- (d = s - get_unaligned_le16(p))
-
-#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \
- do { \
- put_unaligned_le16(v, (u16 *)(p)); \
- p += 2; \
- } while (0)
+/*-************************************
+ * Basic Types
+ **************************************/
+#include <linux/types.h>
+
+typedef uint8_t BYTE;
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+typedef uintptr_t uptrval;
+
+#if defined(__x86_64__)
+ typedef U64 reg_t; /* 64-bits in x32 mode */
+#else
+ typedef size_t reg_t; /* 32-bits in x32 mode */
#endif
-#define COPYLENGTH 8
-#define ML_BITS 4
-#define ML_MASK ((1U << ML_BITS) - 1)
-#define RUN_BITS (8 - ML_BITS)
-#define RUN_MASK ((1U << RUN_BITS) - 1)
-#define MEMORY_USAGE 14
-#define MINMATCH 4
-#define SKIPSTRENGTH 6
-#define LASTLITERALS 5
-#define MFLIMIT (COPYLENGTH + MINMATCH)
-#define MINLENGTH (MFLIMIT + 1)
-#define MAXD_LOG 16
-#define MAXD (1 << MAXD_LOG)
-#define MAXD_MASK (u32)(MAXD - 1)
-#define MAX_DISTANCE (MAXD - 1)
-#define HASH_LOG (MAXD_LOG - 1)
-#define HASHTABLESIZE (1 << HASH_LOG)
-#define MAX_NB_ATTEMPTS 256
-#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)
-#define LZ4_64KLIMIT ((1<<16) + (MFLIMIT - 1))
-#define HASHLOG64K ((MEMORY_USAGE - 2) + 1)
-#define HASH64KTABLESIZE (1U << HASHLOG64K)
-#define LZ4_HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \
- ((MINMATCH * 8) - (MEMORY_USAGE-2)))
-#define LZ4_HASH64K_VALUE(p) (((A32(p)) * 2654435761U) >> \
- ((MINMATCH * 8) - HASHLOG64K))
-#define HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \
- ((MINMATCH * 8) - HASH_LOG))
-
-#if LZ4_ARCH64/* 64-bit */
-#define STEPSIZE 8
-
-#define LZ4_COPYSTEP(s, d) \
- do { \
- PUT8(s, d); \
- d += 8; \
- s += 8; \
- } while (0)
-
-#define LZ4_COPYPACKET(s, d) LZ4_COPYSTEP(s, d)
-
-#define LZ4_SECURECOPY(s, d, e) \
- do { \
- if (d < e) { \
- LZ4_WILDCOPY(s, d, e); \
- } \
- } while (0)
-#define HTYPE u32
-
-#ifdef __BIG_ENDIAN
-#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
+/*-************************************
+ * Constants
+ **************************************/
+#define MINMATCH 4
+
+#define WILDCOPYLENGTH 8
+#define LASTLITERALS 5
+#define MFLIMIT (WILDCOPYLENGTH+MINMATCH)
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB (1<<10)
+#define MB (1<<20)
+#define GB (1U<<30)
+
+#define MAXD_LOG 16
+#define MAX_DISTANCE ((1<<MAXD_LOG) - 1)
+#define STEPSIZE sizeof(size_t)
+
+#define ML_BITS 4
+#define ML_MASK ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+static const int LZ4_64Klimit = ((64 * KB) + (MFLIMIT-1));
+static const U32 LZ4_skipTrigger = 6;
+
+/*-************************************
+ * Reading and writing into memory
+ **************************************/
+
+static inline U16 LZ4_read16(const void *memPtr)
+{
+ U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static inline U32 LZ4_read32(const void *memPtr)
+{
+ U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static inline size_t LZ4_read_ARCH(const void *memPtr)
+{
+ size_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static inline void LZ4_write16(void *memPtr, U16 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+static inline void LZ4_write32(void *memPtr, U32 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+static inline U16 LZ4_readLE16(const void *memPtr)
+{
+#ifdef __LITTLE_ENDIAN__
+ return LZ4_read16(memPtr);
#else
-#define LZ4_NBCOMMONBYTES(val) (__builtin_ctzll(val) >> 3)
+ const BYTE *p = (const BYTE *)memPtr;
+
+ return (U16)((U16)p[0] + (p[1] << 8));
#endif
+}
+
+static inline void LZ4_writeLE16(void *memPtr, U16 value)
+{
+#ifdef __LITTLE_ENDIAN__
+ LZ4_write16(memPtr, value);
+#else
+ BYTE *p = (BYTE *)memPtr;
-#else /* 32-bit */
-#define STEPSIZE 4
+ p[0] = (BYTE) value;
+ p[1] = (BYTE)(value>>8);
+#endif
+}
-#define LZ4_COPYSTEP(s, d) \
- do { \
- PUT4(s, d); \
- d += 4; \
- s += 4; \
- } while (0)
+static inline void LZ4_copy8(void *dst, const void *src)
+{
+ memcpy(dst, src, 8);
+}
-#define LZ4_COPYPACKET(s, d) \
- do { \
- LZ4_COPYSTEP(s, d); \
- LZ4_COPYSTEP(s, d); \
- } while (0)
+/*
+ * customized variant of memcpy,
+ * which can overwrite up to 7 bytes beyond dstEnd
+ */
+static inline void LZ4_wildCopy(void *dstPtr, const void *srcPtr, void *dstEnd)
+{
+ BYTE *d = (BYTE *)dstPtr;
+ const BYTE *s = (const BYTE *)srcPtr;
+ BYTE *const e = (BYTE *)dstEnd;
-#define LZ4_SECURECOPY LZ4_WILDCOPY
-#define HTYPE const u8*
+ do { LZ4_copy8(d, s); d += 8; s += 8; } while (d < e);
+}
-#ifdef __BIG_ENDIAN
+#if LZ4_ARCH64
+#ifdef __BIG_ENDIAN__
+#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
+#else
+#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
+#endif
+#else
+#ifdef __BIG_ENDIAN__
#define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3)
#else
#define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3)
#endif
+#endif
+static inline unsigned int LZ4_count(const BYTE *pIn, const BYTE *pMatch,
+ const BYTE *pInLimit)
+{
+ const BYTE *const pStart = pIn;
+
+ while (likely(pIn < pInLimit-(STEPSIZE-1))) {
+ size_t diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+
+ if (!diff) {
+ pIn += STEPSIZE;
+ pMatch += STEPSIZE;
+ continue;
+ }
+ pIn += LZ4_NBCOMMONBYTES(diff);
+ return (unsigned int)(pIn - pStart);
+ }
+
+#ifdef LZ4_ARCH64
+ if ((pIn < (pInLimit-3))
+ && (LZ4_read32(pMatch) == LZ4_read32(pIn))) {
+ pIn += 4; pMatch += 4;
+ }
#endif
+ if ((pIn < (pInLimit-1))
+ && (LZ4_read16(pMatch) == LZ4_read16(pIn))) {
+ pIn += 2; pMatch += 2;
+ }
+ if ((pIn < pInLimit) && (*pMatch == *pIn))
+ pIn++;
+ return (unsigned int)(pIn - pStart);
+}
+
+typedef enum { noLimit = 0, limitedOutput = 1 } limitedOutput_directive;
+typedef enum { byPtr, byU32, byU16 } tableType_t;
-#define LZ4_WILDCOPY(s, d, e) \
- do { \
- LZ4_COPYPACKET(s, d); \
- } while (d < e)
-
-#define LZ4_BLINDCOPY(s, d, l) \
- do { \
- u8 *e = (d) + l; \
- LZ4_WILDCOPY(s, d, e); \
- d = e; \
- } while (0)
+typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+#endif
diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c
index f344f76..880c778 100644
--- a/lib/lz4/lz4hc_compress.c
+++ b/lib/lz4/lz4hc_compress.c
@@ -1,19 +1,17 @@
/*
* LZ4 HC - High Compression Mode of LZ4
- * Copyright (C) 2011-2012, Yann Collet.
- * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ * Copyright (C) 2011-2015, Yann Collet.
*
+ * BSD 2 - Clause License (http://www.opensource.org/licenses/bsd - license.php)
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
- *
- * * Redistributions of source code must retain the above copyright
+ * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
+ * * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
- *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -25,323 +23,353 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
* You can contact the author at :
- * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
- * - LZ4 source repository : http://code.google.com/p/lz4/
+ * - LZ4 homepage : http://www.lz4.org
+ * - LZ4 source repository : https://github.com/lz4/lz4
*
- * Changed for kernel use by:
- * Chanho Min <chanho.min@lge.com>
+ * Changed for kernel usage by:
+ * Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
*/
-#include <linux/module.h>
-#include <linux/kernel.h>
+/*-************************************
+ * Dependencies
+ **************************************/
#include <linux/lz4.h>
-#include <asm/unaligned.h>
#include "lz4defs.h"
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h> /* memset */
-struct lz4hc_data {
- const u8 *base;
- HTYPE hashtable[HASHTABLESIZE];
- u16 chaintable[MAXD];
- const u8 *nexttoupdate;
-} __attribute__((__packed__));
+/* *************************************
+ * Local Constants and types
+ ***************************************/
-static inline int lz4hc_init(struct lz4hc_data *hc4, const u8 *base)
+#define OPTIMAL_ML (int)((ML_MASK - 1) + MINMATCH)
+
+#define HASH_FUNCTION(i) (((i) * 2654435761U) \
+ >> ((MINMATCH*8) - LZ4HC_HASH_LOG))
+#define DELTANEXTU16(p) chainTable[(U16)(p)] /* faster */
+
+static U32 LZ4HC_hashPtr(const void *ptr)
{
- memset((void *)hc4->hashtable, 0, sizeof(hc4->hashtable));
- memset(hc4->chaintable, 0xFF, sizeof(hc4->chaintable));
-
-#if LZ4_ARCH64
- hc4->nexttoupdate = base + 1;
-#else
- hc4->nexttoupdate = base;
-#endif
- hc4->base = base;
- return 1;
+ return HASH_FUNCTION(LZ4_read32(ptr));
+}
+
+/**************************************
+ * HC Compression
+ **************************************/
+static void LZ4HC_init(LZ4HC_CCtx_internal *hc4, const BYTE *start)
+{
+ memset((void *)hc4->hashTable, 0, sizeof(hc4->hashTable));
+ memset(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));
+ hc4->nextToUpdate = 64 * KB;
+ hc4->base = start - 64 * KB;
+ hc4->end = start;
+ hc4->dictBase = start - 64 * KB;
+ hc4->dictLimit = 64 * KB;
+ hc4->lowLimit = 64 * KB;
}
/* Update chains up to ip (excluded) */
-static inline void lz4hc_insert(struct lz4hc_data *hc4, const u8 *ip)
+static inline void LZ4HC_Insert(LZ4HC_CCtx_internal *hc4,
+ const BYTE *ip)
{
- u16 *chaintable = hc4->chaintable;
- HTYPE *hashtable = hc4->hashtable;
-#if LZ4_ARCH64
+ U16 * const chainTable = hc4->chainTable;
+ U32 * const hashTable = hc4->hashTable;
const BYTE * const base = hc4->base;
-#else
- const int base = 0;
-#endif
+ U32 const target = (U32)(ip - base);
+ U32 idx = hc4->nextToUpdate;
+
+ while (idx < target) {
+ U32 const h = LZ4HC_hashPtr(base + idx);
+ size_t delta = idx - hashTable[h];
- while (hc4->nexttoupdate < ip) {
- const u8 *p = hc4->nexttoupdate;
- size_t delta = p - (hashtable[HASH_VALUE(p)] + base);
if (delta > MAX_DISTANCE)
delta = MAX_DISTANCE;
- chaintable[(size_t)(p) & MAXD_MASK] = (u16)delta;
- hashtable[HASH_VALUE(p)] = (p) - base;
- hc4->nexttoupdate++;
- }
-}
-static inline size_t lz4hc_commonlength(const u8 *p1, const u8 *p2,
- const u8 *const matchlimit)
-{
- const u8 *p1t = p1;
-
- while (p1t < matchlimit - (STEPSIZE - 1)) {
-#if LZ4_ARCH64
- u64 diff = A64(p2) ^ A64(p1t);
-#else
- u32 diff = A32(p2) ^ A32(p1t);
-#endif
- if (!diff) {
- p1t += STEPSIZE;
- p2 += STEPSIZE;
- continue;
- }
- p1t += LZ4_NBCOMMONBYTES(diff);
- return p1t - p1;
- }
-#if LZ4_ARCH64
- if ((p1t < (matchlimit-3)) && (A32(p2) == A32(p1t))) {
- p1t += 4;
- p2 += 4;
- }
-#endif
+ DELTANEXTU16(idx) = (U16)delta;
- if ((p1t < (matchlimit - 1)) && (A16(p2) == A16(p1t))) {
- p1t += 2;
- p2 += 2;
+ hashTable[h] = idx;
+ idx++;
}
- if ((p1t < matchlimit) && (*p2 == *p1t))
- p1t++;
- return p1t - p1;
+
+ hc4->nextToUpdate = target;
}
-static inline int lz4hc_insertandfindbestmatch(struct lz4hc_data *hc4,
- const u8 *ip, const u8 *const matchlimit, const u8 **matchpos)
+static inline int LZ4HC_InsertAndFindBestMatch(
+ LZ4HC_CCtx_internal *hc4, /* Index table will be updated */
+ const BYTE *ip,
+ const BYTE * const iLimit,
+ const BYTE **matchpos,
+ const int maxNbAttempts)
{
- u16 *const chaintable = hc4->chaintable;
- HTYPE *const hashtable = hc4->hashtable;
- const u8 *ref;
-#if LZ4_ARCH64
+ U16 * const chainTable = hc4->chainTable;
+ U32 * const HashTable = hc4->hashTable;
const BYTE * const base = hc4->base;
-#else
- const int base = 0;
-#endif
- int nbattempts = MAX_NB_ATTEMPTS;
- size_t repl = 0, ml = 0;
- u16 delta;
+ const BYTE * const dictBase = hc4->dictBase;
+ const U32 dictLimit = hc4->dictLimit;
+ const U32 lowLimit = (hc4->lowLimit + 64 * KB > (U32)(ip - base))
+ ? hc4->lowLimit
+ : (U32)(ip - base) - (64 * KB - 1);
+ U32 matchIndex;
+ int nbAttempts = maxNbAttempts;
+ size_t ml = 0;
/* HC4 match finder */
- lz4hc_insert(hc4, ip);
- ref = hashtable[HASH_VALUE(ip)] + base;
-
- /* potential repetition */
- if (ref >= ip-4) {
- /* confirmed */
- if (A32(ref) == A32(ip)) {
- delta = (u16)(ip-ref);
- repl = ml = lz4hc_commonlength(ip + MINMATCH,
- ref + MINMATCH, matchlimit) + MINMATCH;
- *matchpos = ref;
- }
- ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
- }
+ LZ4HC_Insert(hc4, ip);
+ matchIndex = HashTable[LZ4HC_hashPtr(ip)];
+
+ while ((matchIndex >= lowLimit)
+ && (nbAttempts)) {
+ nbAttempts--;
+ if (matchIndex >= dictLimit) {
+ const BYTE * const match = base + matchIndex;
+
+ if (*(match + ml) == *(ip + ml)
+ && (LZ4_read32(match) == LZ4_read32(ip))) {
+ size_t const mlt = LZ4_count(ip + MINMATCH,
+ match + MINMATCH, iLimit) + MINMATCH;
- while ((ref >= ip - MAX_DISTANCE) && nbattempts) {
- nbattempts--;
- if (*(ref + ml) == *(ip + ml)) {
- if (A32(ref) == A32(ip)) {
- size_t mlt =
- lz4hc_commonlength(ip + MINMATCH,
- ref + MINMATCH, matchlimit) + MINMATCH;
if (mlt > ml) {
ml = mlt;
- *matchpos = ref;
+ *matchpos = match;
+ }
+ }
+ } else {
+ const BYTE * const match = dictBase + matchIndex;
+
+ if (LZ4_read32(match) == LZ4_read32(ip)) {
+ size_t mlt;
+ const BYTE *vLimit = ip
+ + (dictLimit - matchIndex);
+
+ if (vLimit > iLimit)
+ vLimit = iLimit;
+ mlt = LZ4_count(ip + MINMATCH,
+ match + MINMATCH, vLimit) + MINMATCH;
+ if ((ip + mlt == vLimit)
+ && (vLimit < iLimit))
+ mlt += LZ4_count(ip + mlt,
+ base + dictLimit,
+ iLimit);
+ if (mlt > ml) {
+ /* virtual matchpos */
+ ml = mlt;
+ *matchpos = base + matchIndex;
}
}
}
- ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
- }
-
- /* Complete table */
- if (repl) {
- const BYTE *ptr = ip;
- const BYTE *end;
- end = ip + repl - (MINMATCH-1);
- /* Pre-Load */
- while (ptr < end - delta) {
- chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
- ptr++;
- }
- do {
- chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
- /* Head of chain */
- hashtable[HASH_VALUE(ptr)] = (ptr) - base;
- ptr++;
- } while (ptr < end);
- hc4->nexttoupdate = end;
+ matchIndex -= DELTANEXTU16(matchIndex);
}
return (int)ml;
}
-static inline int lz4hc_insertandgetwidermatch(struct lz4hc_data *hc4,
- const u8 *ip, const u8 *startlimit, const u8 *matchlimit, int longest,
- const u8 **matchpos, const u8 **startpos)
+static inline int LZ4HC_InsertAndGetWiderMatch(
+ LZ4HC_CCtx_internal *hc4,
+ const BYTE * const ip,
+ const BYTE * const iLowLimit,
+ const BYTE * const iHighLimit,
+ int longest,
+ const BYTE **matchpos,
+ const BYTE **startpos,
+ const int maxNbAttempts)
{
- u16 *const chaintable = hc4->chaintable;
- HTYPE *const hashtable = hc4->hashtable;
-#if LZ4_ARCH64
+ U16 * const chainTable = hc4->chainTable;
+ U32 * const HashTable = hc4->hashTable;
const BYTE * const base = hc4->base;
-#else
- const int base = 0;
-#endif
- const u8 *ref;
- int nbattempts = MAX_NB_ATTEMPTS;
- int delta = (int)(ip - startlimit);
+ const U32 dictLimit = hc4->dictLimit;
+ const BYTE * const lowPrefixPtr = base + dictLimit;
+ const U32 lowLimit = (hc4->lowLimit + 64 * KB > (U32)(ip - base))
+ ? hc4->lowLimit
+ : (U32)(ip - base) - (64 * KB - 1);
+ const BYTE * const dictBase = hc4->dictBase;
+ U32 matchIndex;
+ int nbAttempts = maxNbAttempts;
+ int delta = (int)(ip - iLowLimit);
/* First Match */
- lz4hc_insert(hc4, ip);
- ref = hashtable[HASH_VALUE(ip)] + base;
-
- while ((ref >= ip - MAX_DISTANCE) && (ref >= hc4->base)
- && (nbattempts)) {
- nbattempts--;
- if (*(startlimit + longest) == *(ref - delta + longest)) {
- if (A32(ref) == A32(ip)) {
- const u8 *reft = ref + MINMATCH;
- const u8 *ipt = ip + MINMATCH;
- const u8 *startt = ip;
-
- while (ipt < matchlimit-(STEPSIZE - 1)) {
- #if LZ4_ARCH64
- u64 diff = A64(reft) ^ A64(ipt);
- #else
- u32 diff = A32(reft) ^ A32(ipt);
- #endif
-
- if (!diff) {
- ipt += STEPSIZE;
- reft += STEPSIZE;
- continue;
+ LZ4HC_Insert(hc4, ip);
+ matchIndex = HashTable[LZ4HC_hashPtr(ip)];
+
+ while ((matchIndex >= lowLimit)
+ && (nbAttempts)) {
+ nbAttempts--;
+ if (matchIndex >= dictLimit) {
+ const BYTE *matchPtr = base + matchIndex;
+
+ if (*(iLowLimit + longest)
+ == *(matchPtr - delta + longest)) {
+ if (LZ4_read32(matchPtr) == LZ4_read32(ip)) {
+ int mlt = MINMATCH +
+ LZ4_count(ip + MINMATCH,
+ matchPtr + MINMATCH,
+ iHighLimit);
+ int back = 0;
+
+ while ((ip + back > iLowLimit)
+ && (matchPtr + back > lowPrefixPtr)
+ && (ip[back - 1] == matchPtr[back - 1]))
+ back--;
+
+ mlt -= back;
+
+ if (mlt > longest) {
+ longest = (int)mlt;
+ *matchpos = matchPtr + back;
+ *startpos = ip + back;
}
- ipt += LZ4_NBCOMMONBYTES(diff);
- goto _endcount;
- }
- #if LZ4_ARCH64
- if ((ipt < (matchlimit - 3))
- && (A32(reft) == A32(ipt))) {
- ipt += 4;
- reft += 4;
- }
- ipt += 2;
- #endif
- if ((ipt < (matchlimit - 1))
- && (A16(reft) == A16(ipt))) {
- reft += 2;
}
- if ((ipt < matchlimit) && (*reft == *ipt))
- ipt++;
-_endcount:
- reft = ref;
-
- while ((startt > startlimit)
- && (reft > hc4->base)
- && (startt[-1] == reft[-1])) {
- startt--;
- reft--;
- }
-
- if ((ipt - startt) > longest) {
- longest = (int)(ipt - startt);
- *matchpos = reft;
- *startpos = startt;
+ }
+ } else {
+ const BYTE * const matchPtr = dictBase + matchIndex;
+
+ if (LZ4_read32(matchPtr) == LZ4_read32(ip)) {
+ size_t mlt;
+ int back = 0;
+ const BYTE *vLimit = ip + (dictLimit - matchIndex);
+
+ if (vLimit > iHighLimit)
+ vLimit = iHighLimit;
+
+ mlt = LZ4_count(ip + MINMATCH,
+ matchPtr + MINMATCH, vLimit) + MINMATCH;
+
+ if ((ip + mlt == vLimit) && (vLimit < iHighLimit))
+ mlt += LZ4_count(ip + mlt, base + dictLimit,
+ iHighLimit);
+ while ((ip + back > iLowLimit)
+ && (matchIndex + back > lowLimit)
+ && (ip[back - 1] == matchPtr[back - 1]))
+ back--;
+ mlt -= back;
+ if ((int)mlt > longest) {
+ longest = (int)mlt;
+ *matchpos = base + matchIndex + back;
+ *startpos = ip + back;
}
}
}
- ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
+ matchIndex -= DELTANEXTU16(matchIndex);
}
+
return longest;
}
-static inline int lz4_encodesequence(const u8 **ip, u8 **op, const u8 **anchor,
- int ml, const u8 *ref)
+static inline int LZ4HC_encodeSequence(
+ const BYTE **ip,
+ BYTE **op,
+ const BYTE **anchor,
+ int matchLength,
+ const BYTE * const match,
+ limitedOutput_directive limitedOutputBuffer,
+ BYTE *oend)
{
- int length, len;
- u8 *token;
+ int length;
+ BYTE *token;
/* Encode Literal length */
length = (int)(*ip - *anchor);
token = (*op)++;
+
+ if ((limitedOutputBuffer)
+ && ((*op + (length>>8)
+ + length + (2 + 1 + LASTLITERALS)) > oend)) {
+ /* Check output limit */
+ return 1;
+ }
if (length >= (int)RUN_MASK) {
- *token = (RUN_MASK << ML_BITS);
+ int len;
+
+ *token = (RUN_MASK<<ML_BITS);
len = length - RUN_MASK;
- for (; len > 254 ; len -= 255)
+ for (; len > 254 ; len -= 255) {
*(*op)++ = 255;
- *(*op)++ = (u8)len;
+ *(*op)++ = (BYTE)len;
+ }
} else
- *token = (length << ML_BITS);
+ *token = (BYTE)(length<<ML_BITS);
/* Copy Literals */
- LZ4_BLINDCOPY(*anchor, *op, length);
+ LZ4_wildCopy(*op, *anchor, (*op) + length);
+ *op += length;
/* Encode Offset */
- LZ4_WRITE_LITTLEENDIAN_16(*op, (u16)(*ip - ref));
+ LZ4_writeLE16(*op, (U16)(*ip - match)); *op += 2;
/* Encode MatchLength */
- len = (int)(ml - MINMATCH);
- if (len >= (int)ML_MASK) {
+ length = (int)(matchLength - MINMATCH);
+ if ((limitedOutputBuffer)
+ && (*op + (length>>8)
+ + (1 + LASTLITERALS) > oend)) {
+ /* Check output limit */
+ return 1;
+ }
+ if (length >= (int)ML_MASK) {
*token += ML_MASK;
- len -= ML_MASK;
- for (; len > 509 ; len -= 510) {
+ length -= ML_MASK;
+ for (; length > 509 ; length -= 510) {
*(*op)++ = 255;
*(*op)++ = 255;
}
- if (len > 254) {
- len -= 255;
+ if (length > 254) {
+ length -= 255;
*(*op)++ = 255;
}
- *(*op)++ = (u8)len;
+ *(*op)++ = (BYTE)length;
} else
- *token += len;
+ *token += (BYTE)(length);
/* Prepare next loop */
- *ip += ml;
+ *ip += matchLength;
*anchor = *ip;
return 0;
}
-static int lz4_compresshcctx(struct lz4hc_data *ctx,
- const char *source,
- char *dest,
- int isize)
+static int LZ4HC_compress_generic(
+ LZ4HC_CCtx_internal *const ctx,
+ const char * const source,
+ char * const dest,
+ int const inputSize,
+ int const maxOutputSize,
+ int compressionLevel,
+ limitedOutput_directive limit
+ )
{
- const u8 *ip = (const u8 *)source;
- const u8 *anchor = ip;
- const u8 *const iend = ip + isize;
- const u8 *const mflimit = iend - MFLIMIT;
- const u8 *const matchlimit = (iend - LASTLITERALS);
+ const BYTE *ip = (const BYTE *) source;
+ const BYTE *anchor = ip;
+ const BYTE * const iend = ip + inputSize;
+ const BYTE * const mflimit = iend - MFLIMIT;
+ const BYTE * const matchlimit = (iend - LASTLITERALS);
- u8 *op = (u8 *)dest;
+ BYTE *op = (BYTE *) dest;
+ BYTE * const oend = op + maxOutputSize;
+ unsigned int maxNbAttempts;
int ml, ml2, ml3, ml0;
- const u8 *ref = NULL;
- const u8 *start2 = NULL;
- const u8 *ref2 = NULL;
- const u8 *start3 = NULL;
- const u8 *ref3 = NULL;
- const u8 *start0;
- const u8 *ref0;
- int lastrun;
+ const BYTE *ref = NULL;
+ const BYTE *start2 = NULL;
+ const BYTE *ref2 = NULL;
+ const BYTE *start3 = NULL;
+ const BYTE *ref3 = NULL;
+ const BYTE *start0;
+ const BYTE *ref0;
+
+ /* init */
+ if (compressionLevel > LZ4HC_MAX_CLEVEL)
+ compressionLevel = LZ4HC_MAX_CLEVEL;
+ if (compressionLevel < 1)
+ compressionLevel = LZ4HC_DEFAULT_CLEVEL;
+ maxNbAttempts = 1 << (compressionLevel - 1);
+ ctx->end += inputSize;
ip++;
/* Main Loop */
while (ip < mflimit) {
- ml = lz4hc_insertandfindbestmatch(ctx, ip, matchlimit, (&ref));
+ ml = LZ4HC_InsertAndFindBestMatch(ctx, ip,
+ matchlimit, (&ref), maxNbAttempts);
if (!ml) {
ip++;
continue;
@@ -351,46 +379,52 @@ static int lz4_compresshcctx(struct lz4hc_data *ctx,
start0 = ip;
ref0 = ref;
ml0 = ml;
-_search2:
- if (ip+ml < mflimit)
- ml2 = lz4hc_insertandgetwidermatch(ctx, ip + ml - 2,
- ip + 1, matchlimit, ml, &ref2, &start2);
+
+_Search2:
+ if (ip + ml < mflimit)
+ ml2 = LZ4HC_InsertAndGetWiderMatch(ctx,
+ ip + ml - 2, ip + 0,
+ matchlimit, ml, &ref2,
+ &start2, maxNbAttempts);
else
ml2 = ml;
- /* No better match */
+
if (ml2 == ml) {
- lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+ /* No better match */
+ if (LZ4HC_encodeSequence(&ip, &op,
+ &anchor, ml, ref, limit, oend))
+ return 0;
continue;
}
if (start0 < ip) {
- /* empirical */
if (start2 < ip + ml0) {
+ /* empirical */
ip = start0;
ref = ref0;
ml = ml0;
}
}
- /*
- * Here, start0==ip
- * First Match too small : removed
- */
+
+ /* Here, start0 == ip */
if ((start2 - ip) < 3) {
+ /* First Match too small : removed */
ml = ml2;
ip = start2;
ref = ref2;
- goto _search2;
+ goto _Search2;
}
-_search3:
+_Search3:
/*
- * Currently we have :
- * ml2 > ml1, and
- * ip1+3 <= ip2 (usually < ip1+ml1)
- */
+ * Currently we have :
+ * ml2 > ml1, and
+ * ip1 + 3 <= ip2 (usually < ip1 + ml1)
+ */
if ((start2 - ip) < OPTIMAL_ML) {
int correction;
int new_ml = ml;
+
if (new_ml > OPTIMAL_ML)
new_ml = OPTIMAL_ML;
if (ip + new_ml > start2 + ml2 - MINMATCH)
@@ -403,39 +437,44 @@ static int lz4_compresshcctx(struct lz4hc_data *ctx,
}
}
/*
- * Now, we have start2 = ip+new_ml,
- * with new_ml=min(ml, OPTIMAL_ML=18)
+ * Now, we have start2 = ip + new_ml,
+ * with new_ml = min(ml, OPTIMAL_ML = 18)
*/
+
if (start2 + ml2 < mflimit)
- ml3 = lz4hc_insertandgetwidermatch(ctx,
- start2 + ml2 - 3, start2, matchlimit,
- ml2, &ref3, &start3);
+ ml3 = LZ4HC_InsertAndGetWiderMatch(ctx,
+ start2 + ml2 - 3, start2,
+ matchlimit, ml2, &ref3, &start3,
+ maxNbAttempts);
else
ml3 = ml2;
- /* No better match : 2 sequences to encode */
if (ml3 == ml2) {
+ /* No better match : 2 sequences to encode */
/* ip & ref are known; Now for ml */
- if (start2 < ip+ml)
+ if (start2 < ip + ml)
ml = (int)(start2 - ip);
-
/* Now, encode 2 sequences */
- lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor,
+ ml, ref, limit, oend))
+ return 0;
ip = start2;
- lz4_encodesequence(&ip, &op, &anchor, ml2, ref2);
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor,
+ ml2, ref2, limit, oend))
+ return 0;
continue;
}
- /* Not enough space for match 2 : remove it */
if (start3 < ip + ml + 3) {
- /*
- * can write Seq1 immediately ==> Seq2 is removed,
- * so Seq3 becomes Seq1
- */
+ /* Not enough space for match 2 : remove it */
if (start3 >= (ip + ml)) {
+ /* can write Seq1 immediately
+ * ==> Seq2 is removed,
+ * so Seq3 becomes Seq1
+ */
if (start2 < ip + ml) {
- int correction =
- (int)(ip + ml - start2);
+ int correction = (int)(ip + ml - start2);
+
start2 += correction;
ref2 += correction;
ml2 -= correction;
@@ -446,35 +485,38 @@ static int lz4_compresshcctx(struct lz4hc_data *ctx,
}
}
- lz4_encodesequence(&ip, &op, &anchor, ml, ref);
- ip = start3;
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor,
+ ml, ref, limit, oend))
+ return 0;
+ ip = start3;
ref = ref3;
- ml = ml3;
+ ml = ml3;
start0 = start2;
ref0 = ref2;
ml0 = ml2;
- goto _search2;
+ goto _Search2;
}
start2 = start3;
ref2 = ref3;
ml2 = ml3;
- goto _search3;
+ goto _Search3;
}
/*
- * OK, now we have 3 ascending matches; let's write at least
- * the first one ip & ref are known; Now for ml
- */
+ * OK, now we have 3 ascending matches;
+ * let's write at least the first one
+ * ip & ref are known; Now for ml
+ */
if (start2 < ip + ml) {
if ((start2 - ip) < (int)ML_MASK) {
int correction;
+
if (ml > OPTIMAL_ML)
ml = OPTIMAL_ML;
if (ip + ml > start2 + ml2 - MINMATCH)
- ml = (int)(start2 - ip) + ml2
- - MINMATCH;
+ ml = (int)(start2 - ip) + ml2 - MINMATCH;
correction = ml - (int)(start2 - ip);
if (correction > 0) {
start2 += correction;
@@ -484,7 +526,9 @@ static int lz4_compresshcctx(struct lz4hc_data *ctx,
} else
ml = (int)(start2 - ip);
}
- lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml,
+ ref, limit, oend))
+ return 0;
ip = start2;
ref = ref2;
@@ -494,46 +538,243 @@ static int lz4_compresshcctx(struct lz4hc_data *ctx,
ref2 = ref3;
ml2 = ml3;
- goto _search3;
+ goto _Search3;
}
/* Encode Last Literals */
- lastrun = (int)(iend - anchor);
- if (lastrun >= (int)RUN_MASK) {
- *op++ = (RUN_MASK << ML_BITS);
- lastrun -= RUN_MASK;
- for (; lastrun > 254 ; lastrun -= 255)
- *op++ = 255;
- *op++ = (u8) lastrun;
- } else
- *op++ = (lastrun << ML_BITS);
- memcpy(op, anchor, iend - anchor);
- op += iend - anchor;
+ { int lastRun = (int)(iend - anchor);
+
+ if ((limit)
+ && (((char *)op - dest) + lastRun + 1
+ + ((lastRun + 255 - RUN_MASK)/255)
+ > (U32)maxOutputSize)) {
+ /* Check output limit */
+ return 0;
+ }
+ if (lastRun >= (int)RUN_MASK) {
+ *op++ = (RUN_MASK<<ML_BITS);
+ lastRun -= RUN_MASK;
+ for (; lastRun > 254 ; lastRun -= 255) {
+ *op++ = 255;
+ *op++ = (BYTE) lastRun;
+ }
+ } else
+ *op++ = (BYTE)(lastRun<<ML_BITS);
+ memcpy(op, anchor, iend - anchor);
+ op += iend - anchor;
+ }
+
/* End */
return (int) (((char *)op) - dest);
}
-int lz4hc_compress(const unsigned char *src, size_t src_len,
- unsigned char *dst, size_t *dst_len, void *wrkmem)
+static int LZ4_compress_HC_extStateHC(
+ void *state,
+ const char *src,
+ char *dst,
+ int srcSize,
+ int maxDstSize,
+ int compressionLevel)
{
- int ret = -1;
- int out_len = 0;
+ LZ4HC_CCtx_internal *ctx = &((LZ4_streamHC_t *)state)->internal_donotuse;
- struct lz4hc_data *hc4 = (struct lz4hc_data *)wrkmem;
- lz4hc_init(hc4, (const u8 *)src);
- out_len = lz4_compresshcctx((struct lz4hc_data *)hc4, (const u8 *)src,
- (char *)dst, (int)src_len);
+ if (((size_t)(state)&(sizeof(void *) - 1)) != 0) {
+ /* Error : state is not aligned
+ * for pointers (32 or 64 bits)
+ */
+ return 0;
+ }
- if (out_len < 0)
- goto exit;
+ LZ4HC_init(ctx, (const BYTE *)src);
- *dst_len = out_len;
- return 0;
+ if (maxDstSize < LZ4_compressBound(srcSize))
+ return LZ4HC_compress_generic(ctx, src, dst,
+ srcSize, maxDstSize, compressionLevel, limitedOutput);
+ else
+ return LZ4HC_compress_generic(ctx, src, dst,
+ srcSize, maxDstSize, compressionLevel, noLimit);
+}
-exit:
- return ret;
+int LZ4_compress_HC(const char *src, char *dst, int srcSize,
+ int maxDstSize, int compressionLevel, void *wrkmem)
+{
+ return LZ4_compress_HC_extStateHC(wrkmem, src, dst,
+ srcSize, maxDstSize, compressionLevel);
+}
+EXPORT_SYMBOL(LZ4_compress_HC);
+
+/*-******************************
+ * For backwards compatibility
+ ********************************/
+int lz4hc_compress(const unsigned char *src, size_t src_len,
+ unsigned char *dst, size_t *dst_len, void *wrkmem)
+{
+ *dst_len = LZ4_compress_HC(src, dst, (int)src_len,
+ (int)((size_t)dst_len), LZ4HC_DEFAULT_CLEVEL, wrkmem);
+
+ /*
+ * Prior lz4hc_compress will return -1 in case of error
+ * and 0 on success
+ * while new LZ4_compress_HC
+ * returns 0 in case of error
+ * and the output length on success
+ */
+ if (!dst_len)
+ return -1;
+ else
+ return 0;
}
EXPORT_SYMBOL(lz4hc_compress);
+/**************************************
+ * Streaming Functions
+ **************************************/
+void LZ4_resetStreamHC(LZ4_streamHC_t *LZ4_streamHCPtr, int compressionLevel)
+{
+ LZ4_streamHCPtr->internal_donotuse.base = NULL;
+ LZ4_streamHCPtr->internal_donotuse.compressionLevel = (unsigned int)compressionLevel;
+}
+
+int LZ4_loadDictHC(LZ4_streamHC_t *LZ4_streamHCPtr,
+ const char *dictionary,
+ int dictSize)
+{
+ LZ4HC_CCtx_internal *ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
+
+ if (dictSize > 64 * KB) {
+ dictionary += dictSize - 64 * KB;
+ dictSize = 64 * KB;
+ }
+ LZ4HC_init(ctxPtr, (const BYTE *)dictionary);
+ if (dictSize >= 4)
+ LZ4HC_Insert(ctxPtr, (const BYTE *)dictionary + (dictSize - 3));
+ ctxPtr->end = (const BYTE *)dictionary + dictSize;
+ return dictSize;
+}
+EXPORT_SYMBOL(LZ4_loadDictHC);
+
+/* compression */
+
+static void LZ4HC_setExternalDict(
+ LZ4HC_CCtx_internal *ctxPtr,
+ const BYTE *newBlock)
+{
+ if (ctxPtr->end >= ctxPtr->base + 4) {
+ /* Referencing remaining dictionary content */
+ LZ4HC_Insert(ctxPtr, ctxPtr->end - 3);
+ }
+
+ /*
+ * Only one memory segment for extDict,
+ * so any previous extDict is lost at this stage
+ */
+ ctxPtr->lowLimit = ctxPtr->dictLimit;
+ ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base);
+ ctxPtr->dictBase = ctxPtr->base;
+ ctxPtr->base = newBlock - ctxPtr->dictLimit;
+ ctxPtr->end = newBlock;
+ /* match referencing will resume from there */
+ ctxPtr->nextToUpdate = ctxPtr->dictLimit;
+}
+EXPORT_SYMBOL(LZ4HC_setExternalDict);
+
+static int LZ4_compressHC_continue_generic(
+ LZ4_streamHC_t *LZ4_streamHCPtr,
+ const char *source,
+ char *dest,
+ int inputSize,
+ int maxOutputSize,
+ limitedOutput_directive limit)
+{
+ LZ4HC_CCtx_internal *ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
+
+ /* auto - init if forgotten */
+ if (ctxPtr->base == NULL)
+ LZ4HC_init(ctxPtr, (const BYTE *) source);
+
+ /* Check overflow */
+ if ((size_t)(ctxPtr->end - ctxPtr->base) > 2 * GB) {
+ size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->base)
+ - ctxPtr->dictLimit;
+ if (dictSize > 64 * KB)
+ dictSize = 64 * KB;
+ LZ4_loadDictHC(LZ4_streamHCPtr,
+ (const char *)(ctxPtr->end) - dictSize, (int)dictSize);
+ }
+
+ /* Check if blocks follow each other */
+ if ((const BYTE *)source != ctxPtr->end)
+ LZ4HC_setExternalDict(ctxPtr, (const BYTE *)source);
+
+ /* Check overlapping input/dictionary space */
+ { const BYTE *sourceEnd = (const BYTE *) source + inputSize;
+ const BYTE * const dictBegin = ctxPtr->dictBase + ctxPtr->lowLimit;
+ const BYTE * const dictEnd = ctxPtr->dictBase + ctxPtr->dictLimit;
+
+ if ((sourceEnd > dictBegin)
+ && ((const BYTE *)source < dictEnd)) {
+ if (sourceEnd > dictEnd)
+ sourceEnd = dictEnd;
+ ctxPtr->lowLimit = (U32)(sourceEnd - ctxPtr->dictBase);
+
+ if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4)
+ ctxPtr->lowLimit = ctxPtr->dictLimit;
+ }
+ }
+
+ return LZ4HC_compress_generic(ctxPtr, source, dest,
+ inputSize, maxOutputSize, ctxPtr->compressionLevel, limit);
+}
+
+int LZ4_compress_HC_continue(
+ LZ4_streamHC_t *LZ4_streamHCPtr,
+ const char *source,
+ char *dest,
+ int inputSize,
+ int maxOutputSize)
+{
+ if (maxOutputSize < LZ4_compressBound(inputSize))
+ return LZ4_compressHC_continue_generic(LZ4_streamHCPtr,
+ source, dest, inputSize, maxOutputSize, limitedOutput);
+ else
+ return LZ4_compressHC_continue_generic(LZ4_streamHCPtr,
+ source, dest, inputSize, maxOutputSize, noLimit);
+}
+EXPORT_SYMBOL(LZ4_compress_HC_continue);
+
+/* dictionary saving */
+
+int LZ4_saveDictHC(
+ LZ4_streamHC_t *LZ4_streamHCPtr,
+ char *safeBuffer,
+ int dictSize)
+{
+ LZ4HC_CCtx_internal *const streamPtr = &LZ4_streamHCPtr->internal_donotuse;
+ int const prefixSize = (int)(streamPtr->end
+ - (streamPtr->base + streamPtr->dictLimit));
+
+ if (dictSize > 64 * KB)
+ dictSize = 64 * KB;
+ if (dictSize < 4)
+ dictSize = 0;
+ if (dictSize > prefixSize)
+ dictSize = prefixSize;
+
+ memmove(safeBuffer, streamPtr->end - dictSize, dictSize);
+
+ { U32 const endIndex = (U32)(streamPtr->end - streamPtr->base);
+
+ streamPtr->end = (const BYTE *)safeBuffer + dictSize;
+ streamPtr->base = streamPtr->end - endIndex;
+ streamPtr->dictLimit = endIndex - dictSize;
+ streamPtr->lowLimit = endIndex - dictSize;
+
+ if (streamPtr->nextToUpdate < streamPtr->dictLimit)
+ streamPtr->nextToUpdate = streamPtr->dictLimit;
+ }
+ return dictSize;
+}
+EXPORT_SYMBOL(LZ4_saveDictHC);
+
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION("LZ4HC compressor");
+MODULE_DESCRIPTION("LZ4 HC compressor");
--
2.1.4
^ permalink raw reply related
* Re: [PATCH 0/6] Add support for ECDSA algorithm
From: Stephan Müller @ 2017-01-26 7:44 UTC (permalink / raw)
To: Nitin Kumbhar; +Cc: Herbert Xu, davem, linux-crypto
In-Reply-To: <113d3ef8-ef34-0651-71e7-be09ef55727e@nvidia.com>
Am Donnerstag, 26. Januar 2017, 11:30:04 CET schrieb Nitin Kumbhar:
Hi Nitin,
> > Who is going to use this in the kernel?
>
> This ECDSA implementation is analogous to the RSA kernel implementation
> for signature generation / verification. It extends ECC family of
> algorithms like ECDH to support signature verification using akcipher.
> This will be used in a way similar to RSA.
So, I guess that I should dust off algif_akcipher now.
Ciao
Stephan
^ permalink raw reply
* Re: [PATCH 0/6] Add support for ECDSA algorithm
From: Nitin Kumbhar @ 2017-01-26 6:00 UTC (permalink / raw)
To: Herbert Xu; +Cc: davem, linux-crypto
In-Reply-To: <20170123142417.GD19957@gondor.apana.org.au>
Hello Herbert,
On 1/23/2017 7:54 PM, Herbert Xu wrote:
> On Fri, Jan 20, 2017 at 05:05:55PM +0530, Nitin Kumbhar wrote:
>> Hello,
>>
>> This patch series adds support for Elliptic Curve Digital Signature
>> Algorithm (ECDSA). To reuse existing ECC functionality, which is
>> added as part of ECDH, it separates out ECC and ECDH so that
>> only ECC functionality is available for ECDSA even when ECDH is in
>> a disabled state.
>>
>> Patch #1 restructures ECC and ECDH code such that ECC is not
>> dependent on ECDH config.
>>
>> Patches #2 & #3 add vli and ecc functions which are required
>> for other Elliptic curve algorithms like ECDSA and ECIES.
>>
>> Patch #4 adds support for ECDSA. This has been validated for P192
>> and P256 elliptic curves.
>>
>> Patches #5 and #6 add ECDSA tests to validate ECDSA functionality
>> and measure ECDSA performance.
>
> Who is going to use this in the kernel?
This ECDSA implementation is analogous to the RSA kernel implementation
for signature generation / verification. It extends ECC family of
algorithms like ECDH to support signature verification using akcipher.
This will be used in a way similar to RSA.
>
> Thanks,
>
Regards,
- Nitin
-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information. Any unauthorized review, use, disclosure or distribution
is prohibited. If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
^ permalink raw reply
* Re: [PATCH 5/6] crypto: testmgr: add ECDSA tests
From: Nitin Kumbhar @ 2017-01-26 5:58 UTC (permalink / raw)
To: Stephan Müller; +Cc: herbert, davem, linux-crypto
In-Reply-To: <3632665.JG6sEKLG9p@positron.chronox.de>
Hello Stephan,
On 1/20/2017 6:49 PM, Stephan Müller wrote:
> Am Freitag, 20. Januar 2017, 17:06:00 CET schrieb Nitin Kumbhar:
>
> Hi Nitin,
>
>> Update crypto test manager to include NIST ECDSA
>> test vectors and various ECDSA tests. These include
>> tests for ECDSA signing, ECDSA sign-verification,
>> ECDSA signing and verifying generated signatures and
>> invalidation of incorrect signatures.
>>
>> Signed-off-by: Nitin Kumbhar <nkumbhar@nvidia.com>
>> ---
>> crypto/testmgr.c | 452
>> +++++++++++++++++++++++++++++++++++++++++++++++++++++- crypto/testmgr.h |
>> 140 +++++++++++++++++
>> 2 files changed, 589 insertions(+), 3 deletions(-)
>>
>> diff --git a/crypto/testmgr.c b/crypto/testmgr.c
>> index 98eb09782db8..a1db28cbc32d 100644
>> --- a/crypto/testmgr.c
>> +++ b/crypto/testmgr.c
>> @@ -5,6 +5,7 @@
>> * Copyright (c) 2002 Jean-Francois Dive <jef@linuxbe.org>
>> * Copyright (c) 2007 Nokia Siemens Networks
>> * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
>> + * Copyright (c) 2017 NVIDIA Corporation
>> *
>> * Updated RFC4106 AES-GCM testing.
>> * Authors: Aidan O'Mahony (aidan.o.mahony@intel.com)
>> @@ -2085,6 +2086,436 @@ static int alg_test_kpp(const struct alg_test_desc
>> *desc, const char *driver, return err;
>> }
>>
>> +static int do_test_ecdsa_verify(struct crypto_akcipher *tfm,
>> + struct akcipher_testvec *vec)
>> +{
>> + struct akcipher_request *req = NULL;
>> + u8 *r_str = NULL, *s_str = NULL;
>> + u8 *m_str = NULL;
>> + struct scatterlist src_tab[3], dst;
>> + struct tcrypt_result result;
>> + unsigned int outbuf_maxlen;
>> + u8 *outbuf = NULL;
>> + unsigned int nbytes;
>> + int err;
>> +
>> + /* Alloc akcipher request */
>> + req = akcipher_request_alloc(tfm, GFP_KERNEL);
>> + if (!req)
>> + return -ENOMEM;
>> +
>> + /* Set private key */
>> + err = crypto_akcipher_set_pub_key(tfm, vec->key, vec->key_len);
>> + if (err)
>> + goto error;
>> +
>> + /*
>> + * vec->c always contains k, R and S in that order. All are
>> + * of same size and are equal to n i.e. the order of
>> + * an elliptic curve.
>> + */
>> + nbytes = vec->c_size / 3;
>> +
>> + r_str = kzalloc(nbytes, GFP_KERNEL);
>> + s_str = kzalloc(nbytes, GFP_KERNEL);
>> + m_str = kzalloc(vec->m_size, GFP_KERNEL);
>> + if (!r_str || !s_str || !m_str) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> + memcpy(r_str, (u8 *)vec->c + nbytes, nbytes);
>> + memcpy(s_str, (u8 *)vec->c + 2 * nbytes, nbytes);
>> + memcpy(m_str, vec->m, vec->m_size);
>> +
>> + outbuf_maxlen = crypto_akcipher_maxsize(tfm);
>> + if (outbuf_maxlen < 0) {
>> + err = outbuf_maxlen;
>> + goto error;
>> + }
>> + outbuf = kzalloc(outbuf_maxlen, GFP_KERNEL);
>> + if (!outbuf) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> +
>> + /* Set src and dst buffers */
>> + sg_init_table(src_tab, 3);
>> + sg_set_buf(&src_tab[0], m_str, vec->m_size);
>> + sg_set_buf(&src_tab[1], r_str, nbytes);
>> + sg_set_buf(&src_tab[2], s_str, nbytes);
>> + sg_init_one(&dst, outbuf, outbuf_maxlen);
>> +
>> + akcipher_request_set_crypt(req, src_tab, &dst,
>> + vec->m_size + 2 * nbytes, outbuf_maxlen);
>> +
>> + /* Set up result callback */
>> + init_completion(&result.completion);
>> + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> + tcrypt_complete, &result);
>> +
>> + /* Run ecdsa verify operation on sig (r,s) */
>> + err = wait_async_op(&result, crypto_akcipher_verify(req));
>> + if (err) {
>> + pr_err("alg: ecdsa: verify(rs) test failed. err %d\n", err);
>> + goto error;
>> + }
>> +error:
>> + akcipher_request_free(req);
>> + kfree(r_str);
>> + kfree(s_str);
>> + kfree(m_str);
>> + kfree(outbuf);
>> + return err;
>> +}
>> +
>> +static int do_test_ecdsa_invalid_verify(struct crypto_akcipher *tfm,
>> + struct akcipher_testvec *vec)
>> +{
>> + struct akcipher_request *req = NULL;
>> + u8 *r_str = NULL, *s_str = NULL;
>> + u8 *m_str = NULL;
>> + struct scatterlist src_tab[3], dst;
>> + struct tcrypt_result result;
>> + unsigned int outbuf_maxlen;
>> + u8 *outbuf = NULL;
>> + unsigned int nbytes;
>> + int err;
>> +
>> + /* Alloc akcipher request */
>> + req = akcipher_request_alloc(tfm, GFP_KERNEL);
>> + if (!req)
>> + return -ENOMEM;
>> +
>> + /* Set private key */
>> + err = crypto_akcipher_set_pub_key(tfm, vec->key, vec->key_len);
>> + if (err)
>> + goto error;
>> +
>> + /*
>> + * vec->c always contains k, R and S in that order. All are
>> + * of same size and are equal to n i.e. the order of
>> + * an elliptic curve.
>> + */
>> + nbytes = vec->c_size / 3;
>> +
>> + r_str = kzalloc(nbytes, GFP_KERNEL);
>> + s_str = kzalloc(nbytes, GFP_KERNEL);
>> + m_str = kzalloc(vec->m_size, GFP_KERNEL);
>> + if (!r_str || !s_str || !m_str) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> + memcpy(r_str, (u8 *)vec->c + 1 * nbytes, nbytes);
>> + memcpy(s_str, (u8 *)vec->c + 2 * nbytes, nbytes);
>> + memcpy(m_str, vec->m, vec->m_size);
>> +
>> + outbuf_maxlen = crypto_akcipher_maxsize(tfm);
>> + if (outbuf_maxlen < 0) {
>> + err = outbuf_maxlen;
>> + goto error;
>> + }
>> + outbuf = kzalloc(outbuf_maxlen, GFP_KERNEL);
>> + if (!outbuf) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> +
>> + /* Set src and dst buffers */
>> + sg_init_table(src_tab, 3);
>> + /* Intentionally set m_size to 8 to have invalid hash */
>> + sg_set_buf(&src_tab[0], m_str, 8);
>> + sg_set_buf(&src_tab[1], r_str, nbytes);
>> + sg_set_buf(&src_tab[2], s_str, nbytes);
>> + sg_init_one(&dst, outbuf, outbuf_maxlen);
>> +
>> + akcipher_request_set_crypt(req, src_tab, &dst,
>> + vec->m_size + 2 * nbytes, outbuf_maxlen);
>> +
>> + /* Set up result callback */
>> + init_completion(&result.completion);
>> + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> + tcrypt_complete, &result);
>> +
>> + /* Run ecdsa verify operation on sig (r,s) */
>> + err = wait_async_op(&result, crypto_akcipher_verify(req));
>> + if (err != -EBADMSG) {
>> + pr_err("alg: ecdsa: invalid verify test failed. err %d\n", err);
>> + goto error;
>> + }
>> + err = 0;
>> +error:
>> + akcipher_request_free(req);
>> + kfree(r_str);
>> + kfree(s_str);
>> + kfree(m_str);
>> + kfree(outbuf);
>> + return err;
>> +}
>
> There seems to be a lot of code duplication between
> do_test_ecdsa_invalid_verify and do_test_ecdsa_verify -- can this be
> eliminated?
I will reorg the code to reuse test subroutines.
>> +
>> +static int do_test_ecdsa_sign_verify(struct crypto_akcipher *tfm,
>> + struct akcipher_testvec *vec)
>> +{
>> + struct akcipher_request *req = NULL;
>> + u8 *r_str = NULL, *s_str = NULL;
>> + u8 *m_str = NULL;
>> + struct scatterlist src_tab[3];
>> + struct scatterlist src, dst;
>> + struct tcrypt_result result;
>> + unsigned int outbuf_maxlen;
>> + void *outbuf = NULL;
>> + unsigned int nbytes;
>> + int err;
>> +
>> + /* Alloc akcipher request */
>> + req = akcipher_request_alloc(tfm, GFP_KERNEL);
>> + if (!req)
>> + return -ENOMEM;
>> +
>> + /* Set private key */
>> + err = crypto_akcipher_set_priv_key(tfm, vec->key, vec->key_len);
>> + if (err)
>> + goto error;
>> +
>> + /* Set private key */
>> + err = crypto_akcipher_set_pub_key(tfm, vec->key, vec->key_len);
>> + if (err)
>> + goto error;
>> +
>> + /*
>> + * vec->c always contains k, R and S in that order. All are
>> + * of same size and are equal to n i.e. the order of
>> + * an elliptic curve.
>> + */
>> + nbytes = vec->c_size / 3;
>> +
>> + m_str = kzalloc(vec->m_size, GFP_KERNEL);
>> + if (!m_str) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> + memcpy(m_str, vec->m, vec->m_size);
>> +
>> + outbuf_maxlen = crypto_akcipher_maxsize(tfm);
>> + if (outbuf_maxlen < 0) {
>> + err = outbuf_maxlen;
>> + goto error;
>> + }
>> + outbuf = kzalloc(outbuf_maxlen, GFP_KERNEL);
>> + if (!outbuf) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> +
>> + sg_init_one(&src, m_str, vec->m_size);
>> + sg_init_one(&dst, outbuf, outbuf_maxlen);
>> +
>> + akcipher_request_set_crypt(req, &src, &dst,
>> + vec->m_size, outbuf_maxlen);
>> +
>> + /* Set up result callback */
>> + init_completion(&result.completion);
>> + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> + tcrypt_complete, &result);
>> +
>> + /* Run ecdsa sign operation on message digest */
>> + err = wait_async_op(&result, crypto_akcipher_sign(req));
>> + if (err) {
>> + pr_err("alg: ecdsa: sign test failed. err %d\n", err);
>> + goto error;
>> + }
>> +
>> + /* verify that signature (r,s) is valid */
>> + if (req->dst_len != 2 * nbytes) {
>> + pr_err("alg: ecdsa: sign test failed. Invalid sig len\n");
>> + err = -EINVAL;
>> + goto error;
>> + }
>> +
>> + /* output contains r and s */
>> + r_str = outbuf;
>> + s_str = (u8 *)outbuf + nbytes;
>> +
>> + /* Set src and dst buffers */
>> + sg_init_table(src_tab, 3);
>> + sg_set_buf(&src_tab[0], m_str, vec->m_size);
>> + sg_set_buf(&src_tab[1], r_str, nbytes);
>> + sg_set_buf(&src_tab[2], s_str, nbytes);
>> + sg_init_one(&dst, outbuf, outbuf_maxlen);
>> +
>> + akcipher_request_set_crypt(req, src_tab, &dst,
>> + vec->m_size + 2 * nbytes, outbuf_maxlen);
>> +
>> + /* Set up result callback */
>> + init_completion(&result.completion);
>> + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> + tcrypt_complete, &result);
>> +
>> + /* Run ecdsa verify operation on sig (r,s) */
>> + err = wait_async_op(&result, crypto_akcipher_verify(req));
>> + if (err) {
>> + pr_err("alg: ecdsa: verify test failed. err %d\n", err);
>> + goto error;
>> + }
>> +error:
>> + akcipher_request_free(req);
>> + kfree(m_str);
>> + kfree(outbuf);
>> + return err;
>> +}
>> +
>> +static int do_test_ecdsa_sign(struct crypto_akcipher *tfm,
>> + struct akcipher_testvec *vec)
>> +{
>> + struct akcipher_request *req = NULL;
>> + u8 *r_str = NULL, *s_str = NULL;
>> + u8 *k_str = NULL, *m_str = NULL;
>> + struct scatterlist src, dst;
>> + struct tcrypt_result result;
>> + unsigned int outbuf_maxlen;
>> + void *outbuf = NULL;
>> + unsigned int nbytes;
>> + int err;
>> +
>> + /* Alloc akcipher request */
>> + req = akcipher_request_alloc(tfm, GFP_KERNEL);
>> + if (!req)
>> + return -ENOMEM;
>> +
>> + /* Set private key */
>> + err = crypto_akcipher_set_priv_key(tfm, vec->key, vec->key_len);
>> + if (err)
>> + goto error;
>> +
>> + /*
>> + * vec->c always contains k, R and S in that order. All are
>> + * of same size and are equal to n i.e. the order of
>> + * an elliptic curve.
>> + */
>> + nbytes = vec->c_size / 3;
>> +
>> + k_str = kzalloc(nbytes, GFP_KERNEL);
>> + r_str = kzalloc(nbytes, GFP_KERNEL);
>> + s_str = kzalloc(nbytes, GFP_KERNEL);
>> + m_str = kzalloc(vec->m_size, GFP_KERNEL);
>> + if (!k_str || !r_str || !s_str || !m_str) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> + memcpy(k_str, (u8 *)vec->c + 0 * nbytes, nbytes);
>> + memcpy(r_str, (u8 *)vec->c + 1 * nbytes, nbytes);
>> + memcpy(s_str, (u8 *)vec->c + 2 * nbytes, nbytes);
>> + memcpy(m_str, vec->m, vec->m_size);
>> +
>> + outbuf_maxlen = crypto_akcipher_maxsize(tfm);
>> + if (outbuf_maxlen < 0) {
>> + err = outbuf_maxlen;
>> + goto error;
>> + }
>> + outbuf = kzalloc(outbuf_maxlen, GFP_KERNEL);
>> + if (!outbuf) {
>> + err = -ENOMEM;
>> + goto error;
>> + }
>> +
>> + /* Set src and dst buffers */
>> + sg_init_one(&src, m_str, vec->m_size);
>> + sg_init_one(&dst, outbuf, outbuf_maxlen);
>> +
>> + akcipher_request_set_crypt(req, &src, &dst,
>> + vec->m_size, outbuf_maxlen);
>> +
>> + /* Set up result callback */
>> + init_completion(&result.completion);
>> + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> + tcrypt_complete, &result);
>> +
>> + /* Set K in request for signing */
>> + req->info = k_str;
>> +
>> + /* Run ecdsa sign operation on message digest */
>> + err = wait_async_op(&result, crypto_akcipher_sign(req));
>> + if (err) {
>> + pr_err("alg: ecdsa: sign(k) test failed. err %d\n", err);
>> + goto error;
>> + }
>> +
>> + /* verify that signature (r,s) is valid */
>> + if (req->dst_len != 2 * nbytes) {
>> + pr_err("alg: ecdsa: sign(k) test failed. Invalid sig len\n");
>> + err = -EINVAL;
>> + goto error;
>> + }
>> +
>> + if (memcmp(r_str, sg_virt(req->dst), nbytes)) {
>> + pr_err("alg: ecdsa: sign(k) test failed. Invalid sig(r)\n");
>> + err = -EINVAL;
>> + goto error;
>> + }
>> +
>> + if (memcmp(s_str, (u8 *)sg_virt(req->dst) + nbytes, nbytes)) {
>> + pr_err("alg: ecdsa: sign(k) test failed. Invalid sig(s)\n");
>> + err = -EINVAL;
>> + goto error;
>> + }
>> +error:
>> + akcipher_request_free(req);
>> + kfree(k_str);
>> + kfree(r_str);
>> + kfree(s_str);
>> + kfree(m_str);
>> + kfree(outbuf);
>> + return err;
>> +}
>
> Same here -- there seem to be a lot of code duplication -- can this be
> reduced?
Yes. I will update.
>> +
>> +static int test_ecdsa_akcipher(struct crypto_akcipher *tfm, const char
>> *alg, + struct akcipher_testvec *vecs, unsigned int tcount)
>> +{
>> + int i, err = 0;
>> +
>> + for (i = 0; i < tcount; i++) {
>> + err = do_test_ecdsa_verify(tfm, &vecs[i]);
>> + if (!err)
>> + continue;
>> +
>> + pr_err("ecdsa: verify failed on vec %d, err=%d\n",
>> + i + 1, err);
>
> All of these pr_err logs here and below should be removed as these errors seem
> to be already logged.
Sure. I will remove these error logs.
>
>> + goto exit;
>> + }
>> +
>> + for (i = 0; i < tcount; i++) {
>> + err = do_test_ecdsa_invalid_verify(tfm, &vecs[i]);
>> + if (!err)
>> + continue;
>> +
>> + pr_err("ecdsa: verify(invl) failed on vec %d, err=%d\n",
>> + i + 1, err);
>> + goto exit;
>> + }
>> +
>> + for (i = 0; i < tcount; i++) {
>> + err = do_test_ecdsa_sign_verify(tfm, &vecs[i]);
>> + if (!err)
>> + continue;
>> +
>> + pr_err("ecdsa: sign/verify failed on vec %d, err=%d\n",
>> + i + 1, err);
>> + goto exit;
>> + }
>> +
>> + for (i = 0; i < tcount; i++) {
>> + err = do_test_ecdsa_sign(tfm, &vecs[i]);
>> + if (!err)
>> + continue;
>> +
>> + pr_err("ecdsa: sign failed on vec %d, err=%d\n",
>> + i + 1, err);
>> + goto exit;
>> + }
>> + exit:
>> + pr_info("test_ecdsa: %s\n", err ? "FAILED" : "PASSED");
>
> This log message should go away.
Okay.
>
>> + return err;
>> +}
>> +
>> static int test_akcipher_one(struct crypto_akcipher *tfm,
>> struct akcipher_testvec *vecs)
>> {
>> @@ -2236,9 +2667,17 @@ static int alg_test_akcipher(const struct
>> alg_test_desc *desc, driver, PTR_ERR(tfm));
>> return PTR_ERR(tfm);
>> }
>> - if (desc->suite.akcipher.vecs)
>> - err = test_akcipher(tfm, desc->alg, desc->suite.akcipher.vecs,
>> - desc->suite.akcipher.count);
>> +
>> + if (desc->suite.akcipher.vecs) {
>> + if (strncmp(desc->alg, "ecdsa", 5) == 0)
>> + err = test_ecdsa_akcipher(tfm, desc->alg,
>> + desc->suite.akcipher.vecs,
>> + desc->suite.akcipher.count);
>> + else
>> + err = test_akcipher(tfm, desc->alg,
>> + desc->suite.akcipher.vecs,
>> + desc->suite.akcipher.count);
>> + }
>>
>> crypto_free_akcipher(tfm);
>> return err;
>> @@ -2982,6 +3421,13 @@ static int alg_test_null(const struct alg_test_desc
>> *desc, .kpp = __VECS(ecdh_tv_template)
>> }
>> }, {
>> + .alg = "ecdsa",
>> + .test = alg_test_akcipher,
>> + .fips_allowed = 1,
>> + .suite = {
>> + .akcipher = __VECS(ecdsa_tv_template)
>> + }
>> + }, {
>
>
>
> Ciao
> Stephan
>
Thanks,
- Nitin
-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information. Any unauthorized review, use, disclosure or distribution
is prohibited. If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
^ permalink raw reply
* Re: [PATCH 4/6] crypto: ecdsa: add ECDSA SW implementation
From: Nitin Kumbhar @ 2017-01-26 5:52 UTC (permalink / raw)
To: Stephan Müller; +Cc: herbert, davem, linux-crypto
In-Reply-To: <1915611.yMS8t8GbjV@positron.chronox.de>
Hello Stephan,
On 1/20/2017 6:36 PM, Stephan Müller wrote:
> Am Freitag, 20. Januar 2017, 17:05:59 CET schrieb Nitin Kumbhar:
>
> Hi Nitin,
>
>> This adds support for ECDSA algorithm. This implementation supports
>> sign and verify functions for ECDSA algorithm using akcipher. As ECDSA
>> is a signing algorithm dummy functions are added for encrypt() and
>> decrypt().
>>
>> Helper routines for parsing public and private ECC keys for ECDSA are
>> added as well.
>>
>> Signed-off-by: Nitin Kumbhar <nkumbhar@nvidia.com>
>> ---
>> crypto/Kconfig | 7 +
>> crypto/Makefile | 3 +
>> crypto/ecdsa.c | 331
>> +++++++++++++++++++++++++++++++++++++++++++++ crypto/ecdsa_helper.c |
>> 116 ++++++++++++++++
>> include/crypto/akcipher.h | 5 +-
>> include/crypto/ecdsa.h | 29 ++++
>> 6 files changed, 490 insertions(+), 1 deletions(-)
>> create mode 100644 crypto/ecdsa.c
>> create mode 100644 crypto/ecdsa_helper.c
>> create mode 100644 include/crypto/ecdsa.h
>>
>> diff --git a/crypto/Kconfig b/crypto/Kconfig
>> index e240075d6f46..1c5c236b3bbc 100644
>> --- a/crypto/Kconfig
>> +++ b/crypto/Kconfig
>> @@ -140,6 +140,13 @@ config CRYPTO_ECDH
>> help
>> Generic implementation of the ECDH algorithm
>>
>> +config CRYPTO_ECDSA
>> + tristate "ECDSA algorithm"
>> + select CRYPTO_AKCIPHER
>> + select CRYPTO_ECC
>> + help
>> + Generic implementation of the ECDSA algorithm
>> +
>> config CRYPTO_MANAGER
>> tristate "Cryptographic algorithm manager"
>> select CRYPTO_MANAGER2
>> diff --git a/crypto/Makefile b/crypto/Makefile
>> index 827740a47a37..9c13eb2ade6a 100644
>> --- a/crypto/Makefile
>> +++ b/crypto/Makefile
>> @@ -38,6 +38,9 @@ obj-$(CONFIG_CRYPTO_ECC) += ecc.o
>> ecdh_generic-y := ecdh.o
>> ecdh_generic-y += ecdh_helper.o
>> obj-$(CONFIG_CRYPTO_ECDH) += ecdh_generic.o
>> +ecdsa_generic-y := ecdsa.o
>> +ecdsa_generic-y += ecdsa_helper.o
>> +obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o
>>
>> $(obj)/rsapubkey-asn1.o: $(obj)/rsapubkey-asn1.c $(obj)/rsapubkey-asn1.h
>> $(obj)/rsaprivkey-asn1.o: $(obj)/rsaprivkey-asn1.c $(obj)/rsaprivkey-asn1.h
>> diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c
>> new file mode 100644
>> index 000000000000..d415900af3bd
>> --- /dev/null
>> +++ b/crypto/ecdsa.c
>> @@ -0,0 +1,331 @@
>> +/*
>> + * ECDSA generic algorithm
>> + *
>> + * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published by the
>> Free + * Software Foundation; either version 2 of the License, or (at your
>> option) + * any later version.
>> + *
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/scatterlist.h>
>> +#include <linux/random.h>
>> +#include <crypto/internal/akcipher.h>
>> +#include <crypto/akcipher.h>
>> +#include <crypto/ecdsa.h>
>> +
>> +#include "ecc.h"
>> +
>> +struct ecdsa_ctx {
>> + unsigned int curve_id;
>> + unsigned int ndigits;
>> + u64 private_key[ECC_MAX_DIGITS];
>> + u64 public_key[2 * ECC_MAX_DIGITS];
>> +};
>> +
>> +static inline struct ecdsa_ctx *ecdsa_get_ctx(struct crypto_akcipher *tfm)
>> +{
>> + return akcipher_tfm_ctx(tfm);
>> +}
>> +
>> +static void ecdsa_parse_msg_hash(struct akcipher_request *req, u64 *msg,
>> + unsigned int ndigits)
>> +{
>> + unsigned int nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> + unsigned int hash_len, hash_off;
>> + unsigned char *hash, *msg_ptr;
>> + int i;
>> +
>> + /*
>> + * If hash_len == nbytes:
>> + * copy nbytes from req
>> + * If hash_len > nbytes:
>> + * copy left most nbytes from hash ignoring LSBs
>> + * If hash_len < nbytes:
>> + * copy hash_len from req and zero remaining bytes
>> + * (nbytes - hash_len)
>> + */
>> + hash_len = req->src[0].length;
>> + hash_off = hash_len <= nbytes ? 0 : hash_len - nbytes;
>> +
>> + msg_ptr = (unsigned char *)msg;
>> + hash = sg_virt(&req->src[0]);
>> +
>> + for (i = hash_off; i < hash_len; i++)
>> + *msg_ptr++ = hash[i];
>> + for (; i < nbytes; i++)
>> + *msg_ptr++ = 0;
>> +}
>> +
>> +int ecdsa_sign(struct akcipher_request *req)
>> +{
>> + struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
>> + struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
>> + unsigned int ndigits = ctx->ndigits;
>> + unsigned int nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> + unsigned int curve_id = ctx->curve_id;
>> + const struct ecc_curve *curve = ecc_get_curve(curve_id);
>> + struct ecc_point *x1y1 = NULL;
>> + u64 z[ndigits], d[ndigits];
>> + u64 k[ndigits], k_inv[ndigits];
>> + u64 r[ndigits], s[ndigits];
>> + u64 dr[ndigits], zdr[ndigits];
>> + u8 *r_ptr, *s_ptr;
>> +
>> + if (req->dst_len < 2 * nbytes) {
>> + req->dst_len = 2 * nbytes;
>> + return -EINVAL;
>> + }
>> +
>> + ecdsa_parse_msg_hash(req, z, ndigits);
>> +
>> + /* d */
>> + vli_set(d, (const u64 *)ctx->private_key, ndigits);
>> +
>> + /* k */
>> +#if defined(CONFIG_CRYPTO_MANAGER2)
>> + if (req->info) {
>> + vli_copy_from_buf(k, ndigits, req->info, nbytes);
>> + } else
>> +#endif
>> + get_random_bytes(k, nbytes);
>
> Please use crypto_get_default_rng /crypto_rng_get_bytes /
> crypto_put_default_rng here.
Sure. I will update that in next version of the patch.
>> +
>> + x1y1 = ecc_alloc_point(ndigits);
>> + if (!x1y1)
>> + return -ENOMEM;
>> +
>> + /* (x1, y1) = k x G */
>> + ecc_point_mult(x1y1, &curve->g, k, NULL, curve->p, ndigits);
>> +
>> + /* r = x1 mod n */
>> + vli_mod(r, x1y1->x, curve->n, ndigits);
>> +
>> + /* k^-1 */
>> + vli_mod_inv(k_inv, k, curve->n, ndigits);
>> +
>> + /* d . r mod n */
>> + vli_mod_mult(dr, d, r, curve->n, ndigits);
>> +
>> + /* z + dr mod n */
>> + vli_mod_add(zdr, z, dr, curve->n, ndigits);
>> +
>> + /* k^-1 . ( z + dr) mod n */
>> + vli_mod_mult(s, k_inv, zdr, curve->n, ndigits);
>> +
>> + /* write signature (r,s) in dst */
>> + r_ptr = sg_virt(req->dst);
>> + s_ptr = (u8 *)sg_virt(req->dst) + nbytes;
>> +
>> + vli_copy_to_buf(r_ptr, nbytes, r, ndigits);
>> + vli_copy_to_buf(s_ptr, nbytes, s, ndigits);
>> +
>> + req->dst_len = 2 * nbytes;
>> +
>> + ecc_free_point(x1y1);
>> + return 0;
>> +}
>> +
>> +int ecdsa_verify(struct akcipher_request *req)
>> +{
>> + struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
>> + struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
>> + unsigned int ndigits = ctx->ndigits;
>> + unsigned int nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> + unsigned int curve_id = ctx->curve_id;
>> + const struct ecc_curve *curve = ecc_get_curve(curve_id);
>> + struct ecc_point *x1y1 = NULL, *x2y2 = NULL, *Q = NULL;
>> + u64 r[ndigits], s[ndigits], v[ndigits];
>> + u64 z[ndigits], w[ndigits];
>> + u64 u1[ndigits], u2[ndigits];
>> + u64 x1[ndigits], x2[ndigits];
>> + u64 y1[ndigits], y2[ndigits];
>> + u64 *ctx_qx, *ctx_qy;
>> + int ret;
>> +
>> + x1y1 = ecc_alloc_point(ndigits);
>> + x2y2 = ecc_alloc_point(ndigits);
>> + Q = ecc_alloc_point(ndigits);
>> + if (!x1y1 || !x2y2 || !Q) {
>> + ret = -ENOMEM;
>> + goto exit;
>> + }
>> +
>> + ecdsa_parse_msg_hash(req, z, ndigits);
>> +
>> + /* Signature r,s */
>> + vli_copy_from_buf(r, ndigits, sg_virt(&req->src[1]), nbytes);
>> + vli_copy_from_buf(s, ndigits, sg_virt(&req->src[2]), nbytes);
>> +
>> + /* w = s^-1 mod n */
>> + vli_mod_inv(w, s, curve->n, ndigits);
>> +
>> + /* u1 = zw mod n */
>> + vli_mod_mult(u1, z, w, curve->n, ndigits);
>> +
>> + /* u2 = rw mod n */
>> + vli_mod_mult(u2, r, w, curve->n, ndigits);
>> +
>> + /* u1 . G */
>> + ecc_point_mult(x1y1, &curve->g, u1, NULL, curve->p, ndigits);
>> +
>> + /* Q=(Qx,Qy) */
>> + ctx_qx = ctx->public_key;
>> + ctx_qy = ctx_qx + ECC_MAX_DIGITS;
>> + vli_set(Q->x, ctx_qx, ndigits);
>> + vli_set(Q->y, ctx_qy, ndigits);
>> +
>> + /* u2 x Q */
>> + ecc_point_mult(x2y2, Q, u2, NULL, curve->p, ndigits);
>> +
>> + vli_set(x1, x1y1->x, ndigits);
>> + vli_set(y1, x1y1->y, ndigits);
>> + vli_set(x2, x2y2->x, ndigits);
>> + vli_set(y2, x2y2->y, ndigits);
>> +
>> + /* x1y1 + x2y2 => P + Q; P + Q in x2 y2 */
>> + ecc_point_add(x1, y1, x2, y2, curve->p, ndigits);
>> +
>> + /* v = x mod n */
>> + vli_mod(v, x2, curve->n, ndigits);
>> +
>> + /* validate signature */
>> + ret = vli_cmp(v, r, ndigits) == 0 ? 0 : -EBADMSG;
>> + exit:
>> + ecc_free_point(x1y1);
>> + ecc_free_point(x2y2);
>> + ecc_free_point(Q);
>> + return ret;
>> +}
>> +
>> +int ecdsa_dummy_enc(struct akcipher_request *req)
>> +{
>> + return -EINVAL;
>> +}
>> +
>> +int ecdsa_dummy_dec(struct akcipher_request *req)
>> +{
>> + return -EINVAL;
>> +}
>> +
>> +int ecdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key,
>> + unsigned int keylen)
>> +{
>> + struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
>> + struct ecdsa params;
>> + unsigned int ndigits;
>> + unsigned int nbytes;
>> + u8 *params_qx, *params_qy;
>> + u64 *ctx_qx, *ctx_qy;
>> + int ret = 0;
>> +
>> + if (crypto_ecdsa_parse_pub_key(key, keylen, ¶ms))
>> + return -EINVAL;
>> +
>> + ndigits = ecdsa_supported_curve(params.curve_id);
>> +
>> + ctx->curve_id = params.curve_id;
>> + ctx->ndigits = ndigits;
>> + nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> +
>> + params_qx = params.key;
>> + params_qy = params_qx + ECC_MAX_DIGIT_BYTES;
>> +
>> + ctx_qx = ctx->public_key;
>> + ctx_qy = ctx_qx + ECC_MAX_DIGITS;
>> +
>> + vli_copy_from_buf(ctx_qx, ndigits, params_qx, nbytes);
>> + vli_copy_from_buf(ctx_qy, ndigits, params_qy, nbytes);
>> +
>> + memset(¶ms, 0, sizeof(params));
>> + return ret;
>
> Shouldn't there be a check that the point is on the curve? As I think the same
> issue is applicable to ECDH, I guess a common service function should be used
> that we can also invoke from ECDH.
Yes. I'll add checks for public key.
>> +}
>> +
>> +int ecdsa_set_priv_key(struct crypto_akcipher *tfm, const void *key,
>> + unsigned int keylen)
>> +{
>> + struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
>> + struct ecdsa params;
>> + unsigned int ndigits;
>> + unsigned int nbytes;
>> +
>> + if (crypto_ecdsa_parse_priv_key(key, keylen, ¶ms))
>> + return -EINVAL;
>> +
>> + ndigits = ecdsa_supported_curve(params.curve_id);
>> +
>> + ctx->curve_id = params.curve_id;
>> + ctx->ndigits = ndigits;
>> + nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> +
>> + if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
>> + (const u8 *)params.key, params.key_size) < 0)
>> + return -EINVAL;
>> +
>> + vli_copy_from_buf(ctx->private_key, ndigits, params.key, nbytes);
>> +
>> + memset(¶ms, 0, sizeof(params));
>> + return 0;
>> +}
>> +
>> +int ecdsa_max_size(struct crypto_akcipher *tfm)
>> +{
>> + struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
>> + int nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> +
>> + /* For r,s */
>> + return 2 * nbytes;
>> +}
>> +
>> +int ecdsa_init_tfm(struct crypto_akcipher *tfm)
>> +{
>> + return 0;
>> +}
>> +
>> +void ecdsa_exit_tfm(struct crypto_akcipher *tfm)
>> +{
>> +}
>> +
>> +static struct akcipher_alg ecdsa_alg = {
>> + .sign = ecdsa_sign,
>> + .verify = ecdsa_verify,
>> + .encrypt = ecdsa_dummy_enc,
>> + .decrypt = ecdsa_dummy_dec,
>> + .set_priv_key = ecdsa_set_priv_key,
>> + .set_pub_key = ecdsa_set_pub_key,
>> + .max_size = ecdsa_max_size,
>> + .init = ecdsa_init_tfm,
>> + .exit = ecdsa_exit_tfm,
>> + .base = {
>> + .cra_name = "ecdsa",
>> + .cra_driver_name = "ecdsa-generic",
>> + .cra_priority = 100,
>> + .cra_module = THIS_MODULE,
>> + .cra_ctxsize = sizeof(struct ecdsa_ctx),
>> + },
>> +};
>> +
>> +static int ecdsa_init(void)
>> +{
>> + int ret;
>> +
>> + ret = crypto_register_akcipher(&ecdsa_alg);
>> + if (ret)
>> + pr_err("ecdsa alg register failed. err:%d\n", ret);
>> + return ret;
>> +}
>> +
>> +static void ecdsa_exit(void)
>> +{
>> + crypto_unregister_akcipher(&ecdsa_alg);
>> +}
>> +
>> +module_init(ecdsa_init);
>> +module_exit(ecdsa_exit);
>> +
>> +MODULE_ALIAS_CRYPTO("ecdsa");
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("ECDSA Generic Algorithm");
>> +MODULE_AUTHOR("NVIDIA Corporation");
>> diff --git a/crypto/ecdsa_helper.c b/crypto/ecdsa_helper.c
>> new file mode 100644
>> index 000000000000..d31eb54431a9
>> --- /dev/null
>> +++ b/crypto/ecdsa_helper.c
>> @@ -0,0 +1,116 @@
>> +/*
>> + * ECDSA helper routines
>> + *
>> + * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published by the
>> Free + * Software Foundation; either version 2 of the License, or (at your
>> option) + * any later version.
>> + *
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/export.h>
>> +#include <linux/err.h>
>> +#include <linux/string.h>
>> +#include <crypto/ecdsa.h>
>> +
>> +#include "ecc.h"
>> +
>> +#define ECDSA_KEY_MIN_SIZE (1 + 1 + 24) /* ver + cid + n (P-192) */
>> +
>> +unsigned int ecdsa_supported_curve(unsigned int curve_id)
>> +{
>> + switch (curve_id) {
>> + case ECC_CURVE_NIST_P192: return 3;
>> + case ECC_CURVE_NIST_P256: return 4;
>> + default: return 0;
>> + }
>> +}
>
> Wouldn't it be better to have a common function with ECDH as this has almost
> the same code in ecdh_supported_curve?
A separate function is added as ECDH and ECDSA may not support all
elliptic curves. This allows to handle the differences.
>> +
>> +static inline u8 *ecdsa_pack_data(void *dst, const void *src, size_t sz)
>> +{
>> + memcpy(dst, src, sz);
>> + return dst + sz;
>> +}
>> +
>> +static inline const u8 *ecdsa_unpack_data(void *dst, const void *src,
>> size_t sz) +{
>> + memcpy(dst, src, sz);
>> + return src + sz;
>> +}
>> +
>> +int crypto_ecdsa_parse_pub_key(const char *buf, unsigned int len,
>> + struct ecdsa *params)
>> +{
>> + unsigned char version;
>> + unsigned int ndigits;
>> + unsigned int nbytes;
>> + const u8 *ptr = buf;
>> + u8 *qx, *qy;
>> +
>> + if (unlikely(!buf) || len < ECDSA_KEY_MIN_SIZE)
>> + return -EINVAL;
>> +
>> + ptr = ecdsa_unpack_data(&version, ptr, sizeof(version));
>> + if (version != 1)
>> + return -EINVAL;
>> +
>> + ptr = ecdsa_unpack_data(¶ms->curve_id, ptr,
>> + sizeof(params->curve_id));
>> +
>> + ndigits = ecdsa_supported_curve(params->curve_id);
>> + if (!ndigits)
>> + return -EINVAL;
>> +
>> + nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> +
>> + /* skip private key */
>> + ptr = ecdsa_unpack_data(¶ms->key, ptr, nbytes);
>> +
>> + /* copy public key */
>> + qx = params->key;
>> + qy = qx + ECC_MAX_DIGIT_BYTES;
>> +
>> + ptr = ecdsa_unpack_data(qx, ptr, nbytes);
>> + ptr = ecdsa_unpack_data(qy, ptr, nbytes);
>> +
>> + params->key_size = 2 * nbytes;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(crypto_ecdsa_parse_pub_key);
>> +
>> +int crypto_ecdsa_parse_priv_key(const char *buf, unsigned int len,
>> + struct ecdsa *params)
>> +{
>> + unsigned char version;
>> + unsigned int ndigits;
>> + unsigned int nbytes;
>> + const u8 *ptr = buf;
>> +
>> + if (unlikely(!buf) || len < ECDSA_KEY_MIN_SIZE)
>> + return -EINVAL;
>> +
>> + ptr = ecdsa_unpack_data(&version, ptr, sizeof(version));
>> + if (version != 1)
>> + return -EINVAL;
>> +
>> + ptr = ecdsa_unpack_data(¶ms->curve_id, ptr,
>> + sizeof(params->curve_id));
>> +
>> + ndigits = ecdsa_supported_curve(params->curve_id);
>> + if (!ndigits)
>> + return -EINVAL;
>> +
>> + nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
>> +
>> + params->key_size = nbytes;
>> +
>> + /* copy private key */
>> + ptr = ecdsa_unpack_data(¶ms->key, ptr, nbytes);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(crypto_ecdsa_parse_priv_key);
>> diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
>> index c37cc59e9bf2..6b34e9043a6f 100644
>> --- a/include/crypto/akcipher.h
>> +++ b/include/crypto/akcipher.h
>> @@ -3,6 +3,7 @@
>> *
>> * Copyright (c) 2015, Intel Corporation
>> * Authors: Tadeusz Struk <tadeusz.struk@intel.com>
>> + * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
>> *
>> * This program is free software; you can redistribute it and/or modify it
>> * under the terms of the GNU General Public License as published by the
>> Free @@ -27,6 +28,7 @@
>> * result.
>> * In case of error where the dst sgl size was insufficient,
>> * it will be updated to the size required for the operation.
>> + * @info: Any request specific data needed to process the request.
>> * @__ctx: Start of private context data
>> */
>> struct akcipher_request {
>> @@ -35,6 +37,7 @@ struct akcipher_request {
>> struct scatterlist *dst;
>> unsigned int src_len;
>> unsigned int dst_len;
>> + void *info;
>> void *__ctx[] CRYPTO_MINALIGN_ATTR;
>> };
>>
>> @@ -193,7 +196,7 @@ static inline void crypto_free_akcipher(struct
>> crypto_akcipher *tfm) {
>> struct akcipher_request *req;
>>
>> - req = kmalloc(sizeof(*req) + crypto_akcipher_reqsize(tfm), gfp);
>> + req = kzalloc(sizeof(*req) + crypto_akcipher_reqsize(tfm), gfp);
>> if (likely(req))
>> akcipher_request_set_tfm(req, tfm);
>>
>> diff --git a/include/crypto/ecdsa.h b/include/crypto/ecdsa.h
>> new file mode 100644
>> index 000000000000..c71d21e654b9
>> --- /dev/null
>> +++ b/include/crypto/ecdsa.h
>> @@ -0,0 +1,29 @@
>> +/*
>> + * ECC parameters for ECDSA
>> + *
>> + * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published by the
>> Free + * Software Foundation; either version 2 of the License, or (at your
>> option) + * any later version.
>> + *
>> + */
>> +
>> +#ifndef _CRYPTO_ECDSA_
>> +#define _CRYPTO_ECDSA_
>> +
>> +#include <crypto/ecc.h>
>> +
>> +struct ecdsa {
>> + unsigned char curve_id;
>> + unsigned char key[2 * ECC_MAX_DIGIT_BYTES];
>> + unsigned short key_size;
>> +};
>> +
>> +unsigned int ecdsa_supported_curve(unsigned int curve_id);
>> +int crypto_ecdsa_parse_pub_key(const char *buf, unsigned int len,
>> + struct ecdsa *params);
>> +int crypto_ecdsa_parse_priv_key(const char *buf, unsigned int len,
>> + struct ecdsa *params);
>> +#endif /* _CRYPTO_ECDSA_ */
>
> I would like to see soem documentation here similar to the ecdh.h. Especially,
> can you please add documentation which type of point representation you expect
> as input (e.g. affine representation)?
I'll include documentation on the lines of ecdh.h for these functions in
next patch series.
> Ciao
> Stephan
>
Regards,
- Nitin
-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information. Any unauthorized review, use, disclosure or distribution
is prohibited. If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
^ permalink raw reply
* Re: [PATCH] x86/crypto: fix %progbits -> @progbits
From: H. Peter Anvin @ 2017-01-25 22:37 UTC (permalink / raw)
To: Denys Vlasenko, Herbert Xu
Cc: Josh Poimboeuf, Xiaodong Liu, Megha Dey, George Spelvin,
linux-crypto, x86, linux-kernel
In-Reply-To: <20170119212805.18049-1-dvlasenk@redhat.com>
On 01/19/17 13:28, Denys Vlasenko wrote:
> %progbits form is used on ARM (where @ is a comment char).
>
> x86 consistently uses @progbits everywhere else.
However, it looks like %progbits works on all architectures (at least
include/linux/init.h seems to imply so.) Perhaps a tree-wide
replacement the other way would make more sense.
Personally I would also like to see these parameters macroized, to keep
someone from getting them wrong, just like we have __INIT, __INITRODATA
etc already, we should just have plain __TEXT __DATA __BSS...
-hpa
^ permalink raw reply
* [PATCH v3 3/3] arm64: dts: ns2: Add Broadcom SPU driver DT entry.
From: Rob Rice @ 2017-01-25 16:44 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Rob Herring, Mark Rutland,
linux-crypto, devicetree, linux-kernel, bcm-kernel-feedback-list,
Catalin Marinas, Will Deacon, linux-arm-kernel
Cc: Rob Rice, Steve Lin
In-Reply-To: <1485362689-11294-1-git-send-email-rob.rice@broadcom.com>
Add Northstar2 device tree entry for Broadcom Secure Processing
Unit (SPU) crypto hardware.
Signed-off-by: Steve Lin <steven.lin1@broadcom.com>
Signed-off-by: Rob Rice <rob.rice@broadcom.com>
---
arch/arm64/boot/dts/broadcom/ns2.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/arch/arm64/boot/dts/broadcom/ns2.dtsi b/arch/arm64/boot/dts/broadcom/ns2.dtsi
index 4fcdeca..ec1a628 100644
--- a/arch/arm64/boot/dts/broadcom/ns2.dtsi
+++ b/arch/arm64/boot/dts/broadcom/ns2.dtsi
@@ -218,6 +218,12 @@
brcm,use-bcm-hdr;
};
+ crypto0: crypto@612d0000 {
+ compatible = "brcm,spum-crypto";
+ reg = <0x612d0000 0x900>;
+ mboxes = <&pdc0 0>;
+ };
+
pdc1: iproc-pdc1@612e0000 {
compatible = "brcm,iproc-pdc-mbox";
reg = <0x612e0000 0x445>; /* PDC FS1 regs */
@@ -227,6 +233,12 @@
brcm,use-bcm-hdr;
};
+ crypto1: crypto@612f0000 {
+ compatible = "brcm,spum-crypto";
+ reg = <0x612f0000 0x900>;
+ mboxes = <&pdc1 0>;
+ };
+
pdc2: iproc-pdc2@61300000 {
compatible = "brcm,iproc-pdc-mbox";
reg = <0x61300000 0x445>; /* PDC FS2 regs */
@@ -236,6 +248,12 @@
brcm,use-bcm-hdr;
};
+ crypto2: crypto@61310000 {
+ compatible = "brcm,spum-crypto";
+ reg = <0x61310000 0x900>;
+ mboxes = <&pdc2 0>;
+ };
+
pdc3: iproc-pdc3@61320000 {
compatible = "brcm,iproc-pdc-mbox";
reg = <0x61320000 0x445>; /* PDC FS3 regs */
@@ -245,6 +263,12 @@
brcm,use-bcm-hdr;
};
+ crypto3: crypto@61330000 {
+ compatible = "brcm,spum-crypto";
+ reg = <0x61330000 0x900>;
+ mboxes = <&pdc3 0>;
+ };
+
dma0: dma@61360000 {
compatible = "arm,pl330", "arm,primecell";
reg = <0x61360000 0x1000>;
--
2.1.0
^ permalink raw reply related
* [PATCH v3 2/3] crypto: brcm: Add Broadcom SPU driver
From: Rob Rice @ 2017-01-25 16:44 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Rob Herring, Mark Rutland,
linux-crypto-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, Catalin Marinas,
Will Deacon, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Rob Rice, Steve Lin
In-Reply-To: <1485362689-11294-1-git-send-email-rob.rice-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
Add Broadcom Secure Processing Unit (SPU) crypto driver for SPU
hardware crypto offload. The driver supports ablkcipher, ahash,
and aead symmetric crypto operations.
Signed-off-by: Steve Lin <steven.lin1-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
Signed-off-by: Rob Rice <rob.rice-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
drivers/crypto/Kconfig | 15 +
drivers/crypto/Makefile | 2 +
drivers/crypto/bcm/Makefile | 15 +
drivers/crypto/bcm/cipher.c | 4955 +++++++++++++++++++++++++++++++++++++++++++
drivers/crypto/bcm/cipher.h | 475 +++++
drivers/crypto/bcm/spu.c | 1252 +++++++++++
drivers/crypto/bcm/spu.h | 288 +++
drivers/crypto/bcm/spu2.c | 1402 ++++++++++++
drivers/crypto/bcm/spu2.h | 228 ++
drivers/crypto/bcm/spum.h | 174 ++
drivers/crypto/bcm/util.c | 581 +++++
drivers/crypto/bcm/util.h | 116 +
12 files changed, 9503 insertions(+)
create mode 100644 drivers/crypto/bcm/Makefile
create mode 100644 drivers/crypto/bcm/cipher.c
create mode 100644 drivers/crypto/bcm/cipher.h
create mode 100644 drivers/crypto/bcm/spu.c
create mode 100644 drivers/crypto/bcm/spu.h
create mode 100644 drivers/crypto/bcm/spu2.c
create mode 100644 drivers/crypto/bcm/spu2.h
create mode 100644 drivers/crypto/bcm/spum.h
create mode 100644 drivers/crypto/bcm/util.c
create mode 100644 drivers/crypto/bcm/util.h
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index ee5057a..c952f0a 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -571,4 +571,19 @@ source "drivers/crypto/chelsio/Kconfig"
source "drivers/crypto/virtio/Kconfig"
+config CRYPTO_DEV_BCM_SPU
+ tristate "Broadcom symmetric crypto/hash acceleration support"
+ depends on ARCH_BCM_IPROC
+ depends on BCM_PDC_MBOX
+ default m
+ select CRYPTO_DES
+ select CRYPTO_MD5
+ select CRYPTO_SHA1
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ help
+ This driver provides support for Broadcom crypto acceleration using the
+ Secure Processing Unit (SPU). The SPU driver registers ablkcipher,
+ ahash, and aead algorithms with the kernel cryptographic API.
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 8891ccc..ed2d536 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -34,3 +34,5 @@ obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
obj-$(CONFIG_CRYPTO_DEV_VIRTIO) += virtio/
obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/
+obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) += bcm/
+
diff --git a/drivers/crypto/bcm/Makefile b/drivers/crypto/bcm/Makefile
new file mode 100644
index 0000000..13cb80e
--- /dev/null
+++ b/drivers/crypto/bcm/Makefile
@@ -0,0 +1,15 @@
+# File: drivers/crypto/bcm/Makefile
+#
+# Makefile for crypto acceleration files for Broadcom SPU driver
+#
+# Uncomment to enable debug tracing in the SPU driver.
+# CFLAGS_util.o := -DDEBUG
+# CFLAGS_cipher.o := -DDEBUG
+# CFLAGS_spu.o := -DDEBUG
+# CFLAGS_spu2.o := -DDEBUG
+
+obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) := bcm_crypto_spu.o
+
+bcm_crypto_spu-objs := util.o spu.o spu2.o cipher.o
+
+ccflags-y += -I. -DBCMDRIVER
diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c
new file mode 100644
index 0000000..3066b2a
--- /dev/null
+++ b/drivers/crypto/bcm/cipher.c
@@ -0,0 +1,4955 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/kthread.h>
+#include <linux/rtnetlink.h>
+#include <linux/sched.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#include <crypto/algapi.h>
+#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
+#include <crypto/aes.h>
+#include <crypto/des.h>
+#include <crypto/sha.h>
+#include <crypto/md5.h>
+#include <crypto/authenc.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include <crypto/aes.h>
+#include <crypto/sha3.h>
+
+#include "util.h"
+#include "cipher.h"
+#include "spu.h"
+#include "spum.h"
+#include "spu2.h"
+
+/* ================= Device Structure ================== */
+
+struct device_private iproc_priv;
+
+/* ==================== Parameters ===================== */
+
+int flow_debug_logging;
+module_param(flow_debug_logging, int, 0644);
+MODULE_PARM_DESC(flow_debug_logging, "Enable Flow Debug Logging");
+
+int packet_debug_logging;
+module_param(packet_debug_logging, int, 0644);
+MODULE_PARM_DESC(packet_debug_logging, "Enable Packet Debug Logging");
+
+int debug_logging_sleep;
+module_param(debug_logging_sleep, int, 0644);
+MODULE_PARM_DESC(debug_logging_sleep, "Packet Debug Logging Sleep");
+
+/*
+ * The value of these module parameters is used to set the priority for each
+ * algo type when this driver registers algos with the kernel crypto API.
+ * To use a priority other than the default, set the priority in the insmod or
+ * modprobe. Changing the module priority after init time has no effect.
+ *
+ * The default priorities are chosen to be lower (less preferred) than ARMv8 CE
+ * algos, but more preferred than generic software algos.
+ */
+static int cipher_pri = 150;
+module_param(cipher_pri, int, 0644);
+MODULE_PARM_DESC(cipher_pri, "Priority for cipher algos");
+
+static int hash_pri = 100;
+module_param(hash_pri, int, 0644);
+MODULE_PARM_DESC(hash_pri, "Priority for hash algos");
+
+static int aead_pri = 150;
+module_param(aead_pri, int, 0644);
+MODULE_PARM_DESC(aead_pri, "Priority for AEAD algos");
+
+#define MAX_SPUS 16
+
+/* A type 3 BCM header, expected to precede the SPU header for SPU-M.
+ * Bits 3 and 4 in the first byte encode the channel number (the dma ringset).
+ * 0x60 - ring 0
+ * 0x68 - ring 1
+ * 0x70 - ring 2
+ * 0x78 - ring 3
+ */
+char BCMHEADER[] = { 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28 };
+/*
+ * Some SPU hw does not use BCM header on SPU messages. So BCM_HDR_LEN
+ * is set dynamically after reading SPU type from device tree.
+ */
+#define BCM_HDR_LEN iproc_priv.bcm_hdr_len
+
+/* min and max time to sleep before retrying when mbox queue is full. usec */
+#define MBOX_SLEEP_MIN 800
+#define MBOX_SLEEP_MAX 1000
+
+/**
+ * select_channel() - Select a SPU channel to handle a crypto request. Selects
+ * channel in round robin order.
+ *
+ * Return: channel index
+ */
+static u8 select_channel(void)
+{
+ u8 chan_idx = atomic_inc_return(&iproc_priv.next_chan);
+
+ return chan_idx % iproc_priv.spu.num_spu;
+}
+
+/**
+ * spu_ablkcipher_rx_sg_create() - Build up the scatterlist of buffers used to
+ * receive a SPU response message for an ablkcipher request. Includes buffers to
+ * catch SPU message headers and the response data.
+ * @mssg: mailbox message containing the receive sg
+ * @rctx: crypto request context
+ * @rx_frag_num: number of scatterlist elements required to hold the
+ * SPU response message
+ * @chunksize: Number of bytes of response data expected
+ * @stat_pad_len: Number of bytes required to pad the STAT field to
+ * a 4-byte boundary
+ *
+ * The scatterlist that gets allocated here is freed in spu_chunk_cleanup()
+ * when the request completes, whether the request is handled successfully or
+ * there is an error.
+ *
+ * Returns:
+ * 0 if successful
+ * < 0 if an error
+ */
+static int
+spu_ablkcipher_rx_sg_create(struct brcm_message *mssg,
+ struct iproc_reqctx_s *rctx,
+ u8 rx_frag_num,
+ unsigned int chunksize, u32 stat_pad_len)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct scatterlist *sg; /* used to build sgs in mbox message */
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ u32 datalen; /* Number of bytes of response data expected */
+
+ mssg->spu.dst = kcalloc(rx_frag_num, sizeof(struct scatterlist),
+ rctx->gfp);
+ if (!mssg->spu.dst)
+ return -ENOMEM;
+
+ sg = mssg->spu.dst;
+ sg_init_table(sg, rx_frag_num);
+ /* Space for SPU message header */
+ sg_set_buf(sg++, rctx->msg_buf.spu_resp_hdr, ctx->spu_resp_hdr_len);
+
+ /* If XTS tweak in payload, add buffer to receive encrypted tweak */
+ if ((ctx->cipher.mode == CIPHER_MODE_XTS) &&
+ spu->spu_xts_tweak_in_payload())
+ sg_set_buf(sg++, rctx->msg_buf.c.supdt_tweak,
+ SPU_XTS_TWEAK_SIZE);
+
+ /* Copy in each dst sg entry from request, up to chunksize */
+ datalen = spu_msg_sg_add(&sg, &rctx->dst_sg, &rctx->dst_skip,
+ rctx->dst_nents, chunksize);
+ if (datalen < chunksize) {
+ pr_err("%s(): failed to copy dst sg to mbox msg. chunksize %u, datalen %u",
+ __func__, chunksize, datalen);
+ return -EFAULT;
+ }
+
+ if (ctx->cipher.alg == CIPHER_ALG_RC4)
+ /* Add buffer to catch 260-byte SUPDT field for RC4 */
+ sg_set_buf(sg++, rctx->msg_buf.c.supdt_tweak, SPU_SUPDT_LEN);
+
+ if (stat_pad_len)
+ sg_set_buf(sg++, rctx->msg_buf.rx_stat_pad, stat_pad_len);
+
+ memset(rctx->msg_buf.rx_stat, 0, SPU_RX_STATUS_LEN);
+ sg_set_buf(sg, rctx->msg_buf.rx_stat, spu->spu_rx_status_len());
+
+ return 0;
+}
+
+/**
+ * spu_ablkcipher_tx_sg_create() - Build up the scatterlist of buffers used to
+ * send a SPU request message for an ablkcipher request. Includes SPU message
+ * headers and the request data.
+ * @mssg: mailbox message containing the transmit sg
+ * @rctx: crypto request context
+ * @tx_frag_num: number of scatterlist elements required to construct the
+ * SPU request message
+ * @chunksize: Number of bytes of request data
+ * @pad_len: Number of pad bytes
+ *
+ * The scatterlist that gets allocated here is freed in spu_chunk_cleanup()
+ * when the request completes, whether the request is handled successfully or
+ * there is an error.
+ *
+ * Returns:
+ * 0 if successful
+ * < 0 if an error
+ */
+static int
+spu_ablkcipher_tx_sg_create(struct brcm_message *mssg,
+ struct iproc_reqctx_s *rctx,
+ u8 tx_frag_num, unsigned int chunksize, u32 pad_len)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct scatterlist *sg; /* used to build sgs in mbox message */
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ u32 datalen; /* Number of bytes of response data expected */
+ u32 stat_len;
+
+ mssg->spu.src = kcalloc(tx_frag_num, sizeof(struct scatterlist),
+ rctx->gfp);
+ if (unlikely(!mssg->spu.src))
+ return -ENOMEM;
+
+ sg = mssg->spu.src;
+ sg_init_table(sg, tx_frag_num);
+
+ sg_set_buf(sg++, rctx->msg_buf.bcm_spu_req_hdr,
+ BCM_HDR_LEN + ctx->spu_req_hdr_len);
+
+ /* if XTS tweak in payload, copy from IV (where crypto API puts it) */
+ if ((ctx->cipher.mode == CIPHER_MODE_XTS) &&
+ spu->spu_xts_tweak_in_payload())
+ sg_set_buf(sg++, rctx->msg_buf.iv_ctr, SPU_XTS_TWEAK_SIZE);
+
+ /* Copy in each src sg entry from request, up to chunksize */
+ datalen = spu_msg_sg_add(&sg, &rctx->src_sg, &rctx->src_skip,
+ rctx->src_nents, chunksize);
+ if (unlikely(datalen < chunksize)) {
+ pr_err("%s(): failed to copy src sg to mbox msg",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (pad_len)
+ sg_set_buf(sg++, rctx->msg_buf.spu_req_pad, pad_len);
+
+ stat_len = spu->spu_tx_status_len();
+ if (stat_len) {
+ memset(rctx->msg_buf.tx_stat, 0, stat_len);
+ sg_set_buf(sg, rctx->msg_buf.tx_stat, stat_len);
+ }
+ return 0;
+}
+
+/**
+ * handle_ablkcipher_req() - Submit as much of a block cipher request as fits in
+ * a single SPU request message, starting at the current position in the request
+ * data.
+ * @rctx: Crypto request context
+ *
+ * This may be called on the crypto API thread, or, when a request is so large
+ * it must be broken into multiple SPU messages, on the thread used to invoke
+ * the response callback. When requests are broken into multiple SPU
+ * messages, we assume subsequent messages depend on previous results, and
+ * thus always wait for previous results before submitting the next message.
+ * Because requests are submitted in lock step like this, there is no need
+ * to synchronize access to request data structures.
+ *
+ * Return: -EINPROGRESS: request has been accepted and result will be returned
+ * asynchronously
+ * Any other value indicates an error
+ */
+static int handle_ablkcipher_req(struct iproc_reqctx_s *rctx)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct crypto_async_request *areq = rctx->parent;
+ struct ablkcipher_request *req =
+ container_of(areq, struct ablkcipher_request, base);
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ struct spu_cipher_parms cipher_parms;
+ int err = 0;
+ unsigned int chunksize = 0; /* Num bytes of request to submit */
+ int remaining = 0; /* Bytes of request still to process */
+ int chunk_start; /* Beginning of data for current SPU msg */
+
+ /* IV or ctr value to use in this SPU msg */
+ u8 local_iv_ctr[MAX_IV_SIZE];
+ u32 stat_pad_len; /* num bytes to align status field */
+ u32 pad_len; /* total length of all padding */
+ bool update_key = false;
+ struct brcm_message *mssg; /* mailbox message */
+ int retry_cnt = 0;
+
+ /* number of entries in src and dst sg in mailbox message. */
+ u8 rx_frag_num = 2; /* response header and STATUS */
+ u8 tx_frag_num = 1; /* request header */
+
+ flow_log("%s\n", __func__);
+
+ cipher_parms.alg = ctx->cipher.alg;
+ cipher_parms.mode = ctx->cipher.mode;
+ cipher_parms.type = ctx->cipher_type;
+ cipher_parms.key_len = ctx->enckeylen;
+ cipher_parms.key_buf = ctx->enckey;
+ cipher_parms.iv_buf = local_iv_ctr;
+ cipher_parms.iv_len = rctx->iv_ctr_len;
+
+ mssg = &rctx->mb_mssg;
+ chunk_start = rctx->src_sent;
+ remaining = rctx->total_todo - chunk_start;
+
+ /* determine the chunk we are breaking off and update the indexes */
+ if ((ctx->max_payload != SPU_MAX_PAYLOAD_INF) &&
+ (remaining > ctx->max_payload))
+ chunksize = ctx->max_payload;
+ else
+ chunksize = remaining;
+
+ rctx->src_sent += chunksize;
+ rctx->total_sent = rctx->src_sent;
+
+ /* Count number of sg entries to be included in this request */
+ rctx->src_nents = spu_sg_count(rctx->src_sg, rctx->src_skip, chunksize);
+ rctx->dst_nents = spu_sg_count(rctx->dst_sg, rctx->dst_skip, chunksize);
+
+ if ((ctx->cipher.mode == CIPHER_MODE_CBC) &&
+ rctx->is_encrypt && chunk_start)
+ /*
+ * Encrypting non-first first chunk. Copy last block of
+ * previous result to IV for this chunk.
+ */
+ sg_copy_part_to_buf(req->dst, rctx->msg_buf.iv_ctr,
+ rctx->iv_ctr_len,
+ chunk_start - rctx->iv_ctr_len);
+
+ if (rctx->iv_ctr_len) {
+ /* get our local copy of the iv */
+ __builtin_memcpy(local_iv_ctr, rctx->msg_buf.iv_ctr,
+ rctx->iv_ctr_len);
+
+ /* generate the next IV if possible */
+ if ((ctx->cipher.mode == CIPHER_MODE_CBC) &&
+ !rctx->is_encrypt) {
+ /*
+ * CBC Decrypt: next IV is the last ciphertext block in
+ * this chunk
+ */
+ sg_copy_part_to_buf(req->src, rctx->msg_buf.iv_ctr,
+ rctx->iv_ctr_len,
+ rctx->src_sent - rctx->iv_ctr_len);
+ } else if (ctx->cipher.mode == CIPHER_MODE_CTR) {
+ /*
+ * The SPU hardware increments the counter once for
+ * each AES block of 16 bytes. So update the counter
+ * for the next chunk, if there is one. Note that for
+ * this chunk, the counter has already been copied to
+ * local_iv_ctr. We can assume a block size of 16,
+ * because we only support CTR mode for AES, not for
+ * any other cipher alg.
+ */
+ add_to_ctr(rctx->msg_buf.iv_ctr, chunksize >> 4);
+ }
+ }
+
+ if (ctx->cipher.alg == CIPHER_ALG_RC4) {
+ rx_frag_num++;
+ if (chunk_start) {
+ /*
+ * for non-first RC4 chunks, use SUPDT from previous
+ * response as key for this chunk.
+ */
+ cipher_parms.key_buf = rctx->msg_buf.c.supdt_tweak;
+ update_key = true;
+ cipher_parms.type = CIPHER_TYPE_UPDT;
+ } else if (!rctx->is_encrypt) {
+ /*
+ * First RC4 chunk. For decrypt, key in pre-built msg
+ * header may have been changed if encrypt required
+ * multiple chunks. So revert the key to the
+ * ctx->enckey value.
+ */
+ update_key = true;
+ cipher_parms.type = CIPHER_TYPE_INIT;
+ }
+ }
+
+ if (ctx->max_payload == SPU_MAX_PAYLOAD_INF)
+ flow_log("max_payload infinite\n");
+ else
+ flow_log("max_payload %u\n", ctx->max_payload);
+
+ flow_log("sent:%u start:%u remains:%u size:%u\n",
+ rctx->src_sent, chunk_start, remaining, chunksize);
+
+ /* Copy SPU header template created at setkey time */
+ memcpy(rctx->msg_buf.bcm_spu_req_hdr, ctx->bcm_spu_req_hdr,
+ sizeof(rctx->msg_buf.bcm_spu_req_hdr));
+
+ /*
+ * Pass SUPDT field as key. Key field in finish() call is only used
+ * when update_key has been set above for RC4. Will be ignored in
+ * all other cases.
+ */
+ spu->spu_cipher_req_finish(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN,
+ ctx->spu_req_hdr_len, !(rctx->is_encrypt),
+ &cipher_parms, update_key, chunksize);
+
+ atomic64_add(chunksize, &iproc_priv.bytes_out);
+
+ stat_pad_len = spu->spu_wordalign_padlen(chunksize);
+ if (stat_pad_len)
+ rx_frag_num++;
+ pad_len = stat_pad_len;
+ if (pad_len) {
+ tx_frag_num++;
+ spu->spu_request_pad(rctx->msg_buf.spu_req_pad, 0,
+ 0, ctx->auth.alg, ctx->auth.mode,
+ rctx->total_sent, stat_pad_len);
+ }
+
+ spu->spu_dump_msg_hdr(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN,
+ ctx->spu_req_hdr_len);
+ packet_log("payload:\n");
+ dump_sg(rctx->src_sg, rctx->src_skip, chunksize);
+ packet_dump(" pad: ", rctx->msg_buf.spu_req_pad, pad_len);
+
+ /*
+ * Build mailbox message containing SPU request msg and rx buffers
+ * to catch response message
+ */
+ memset(mssg, 0, sizeof(*mssg));
+ mssg->type = BRCM_MESSAGE_SPU;
+ mssg->ctx = rctx; /* Will be returned in response */
+
+ /* Create rx scatterlist to catch result */
+ rx_frag_num += rctx->dst_nents;
+
+ if ((ctx->cipher.mode == CIPHER_MODE_XTS) &&
+ spu->spu_xts_tweak_in_payload())
+ rx_frag_num++; /* extra sg to insert tweak */
+
+ err = spu_ablkcipher_rx_sg_create(mssg, rctx, rx_frag_num, chunksize,
+ stat_pad_len);
+ if (err)
+ return err;
+
+ /* Create tx scatterlist containing SPU request message */
+ tx_frag_num += rctx->src_nents;
+ if (spu->spu_tx_status_len())
+ tx_frag_num++;
+
+ if ((ctx->cipher.mode == CIPHER_MODE_XTS) &&
+ spu->spu_xts_tweak_in_payload())
+ tx_frag_num++; /* extra sg to insert tweak */
+
+ err = spu_ablkcipher_tx_sg_create(mssg, rctx, tx_frag_num, chunksize,
+ pad_len);
+ if (err)
+ return err;
+
+ err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], mssg);
+ if (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) {
+ while ((err == -ENOBUFS) && (retry_cnt < SPU_MB_RETRY_MAX)) {
+ /*
+ * Mailbox queue is full. Since MAY_SLEEP is set, assume
+ * not in atomic context and we can wait and try again.
+ */
+ retry_cnt++;
+ usleep_range(MBOX_SLEEP_MIN, MBOX_SLEEP_MAX);
+ err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx],
+ mssg);
+ atomic_inc(&iproc_priv.mb_no_spc);
+ }
+ }
+ if (unlikely(err < 0)) {
+ atomic_inc(&iproc_priv.mb_send_fail);
+ return err;
+ }
+
+ return -EINPROGRESS;
+}
+
+/**
+ * handle_ablkcipher_resp() - Process a block cipher SPU response. Updates the
+ * total received count for the request and updates global stats.
+ * @rctx: Crypto request context
+ */
+static void handle_ablkcipher_resp(struct iproc_reqctx_s *rctx)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+#ifdef DEBUG
+ struct crypto_async_request *areq = rctx->parent;
+ struct ablkcipher_request *req = ablkcipher_request_cast(areq);
+#endif
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ u32 payload_len;
+
+ /* See how much data was returned */
+ payload_len = spu->spu_payload_length(rctx->msg_buf.spu_resp_hdr);
+
+ /*
+ * In XTS mode, the first SPU_XTS_TWEAK_SIZE bytes may be the
+ * encrypted tweak ("i") value; we don't count those.
+ */
+ if ((ctx->cipher.mode == CIPHER_MODE_XTS) &&
+ spu->spu_xts_tweak_in_payload() &&
+ (payload_len >= SPU_XTS_TWEAK_SIZE))
+ payload_len -= SPU_XTS_TWEAK_SIZE;
+
+ atomic64_add(payload_len, &iproc_priv.bytes_in);
+
+ flow_log("%s() offset: %u, bd_len: %u BD:\n",
+ __func__, rctx->total_received, payload_len);
+
+ dump_sg(req->dst, rctx->total_received, payload_len);
+ if (ctx->cipher.alg == CIPHER_ALG_RC4)
+ packet_dump(" supdt ", rctx->msg_buf.c.supdt_tweak,
+ SPU_SUPDT_LEN);
+
+ rctx->total_received += payload_len;
+ if (rctx->total_received == rctx->total_todo) {
+ atomic_inc(&iproc_priv.op_counts[SPU_OP_CIPHER]);
+ atomic_inc(
+ &iproc_priv.cipher_cnt[ctx->cipher.alg][ctx->cipher.mode]);
+ }
+}
+
+/**
+ * spu_ahash_rx_sg_create() - Build up the scatterlist of buffers used to
+ * receive a SPU response message for an ahash request.
+ * @mssg: mailbox message containing the receive sg
+ * @rctx: crypto request context
+ * @rx_frag_num: number of scatterlist elements required to hold the
+ * SPU response message
+ * @digestsize: length of hash digest, in bytes
+ * @stat_pad_len: Number of bytes required to pad the STAT field to
+ * a 4-byte boundary
+ *
+ * The scatterlist that gets allocated here is freed in spu_chunk_cleanup()
+ * when the request completes, whether the request is handled successfully or
+ * there is an error.
+ *
+ * Return:
+ * 0 if successful
+ * < 0 if an error
+ */
+static int
+spu_ahash_rx_sg_create(struct brcm_message *mssg,
+ struct iproc_reqctx_s *rctx,
+ u8 rx_frag_num, unsigned int digestsize,
+ u32 stat_pad_len)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct scatterlist *sg; /* used to build sgs in mbox message */
+ struct iproc_ctx_s *ctx = rctx->ctx;
+
+ mssg->spu.dst = kcalloc(rx_frag_num, sizeof(struct scatterlist),
+ rctx->gfp);
+ if (!mssg->spu.dst)
+ return -ENOMEM;
+
+ sg = mssg->spu.dst;
+ sg_init_table(sg, rx_frag_num);
+ /* Space for SPU message header */
+ sg_set_buf(sg++, rctx->msg_buf.spu_resp_hdr, ctx->spu_resp_hdr_len);
+
+ /* Space for digest */
+ sg_set_buf(sg++, rctx->msg_buf.digest, digestsize);
+
+ if (stat_pad_len)
+ sg_set_buf(sg++, rctx->msg_buf.rx_stat_pad, stat_pad_len);
+
+ memset(rctx->msg_buf.rx_stat, 0, SPU_RX_STATUS_LEN);
+ sg_set_buf(sg, rctx->msg_buf.rx_stat, spu->spu_rx_status_len());
+ return 0;
+}
+
+/**
+ * spu_ahash_tx_sg_create() - Build up the scatterlist of buffers used to send
+ * a SPU request message for an ahash request. Includes SPU message headers and
+ * the request data.
+ * @mssg: mailbox message containing the transmit sg
+ * @rctx: crypto request context
+ * @tx_frag_num: number of scatterlist elements required to construct the
+ * SPU request message
+ * @spu_hdr_len: length in bytes of SPU message header
+ * @hash_carry_len: Number of bytes of data carried over from previous req
+ * @new_data_len: Number of bytes of new request data
+ * @pad_len: Number of pad bytes
+ *
+ * The scatterlist that gets allocated here is freed in spu_chunk_cleanup()
+ * when the request completes, whether the request is handled successfully or
+ * there is an error.
+ *
+ * Return:
+ * 0 if successful
+ * < 0 if an error
+ */
+static int
+spu_ahash_tx_sg_create(struct brcm_message *mssg,
+ struct iproc_reqctx_s *rctx,
+ u8 tx_frag_num,
+ u32 spu_hdr_len,
+ unsigned int hash_carry_len,
+ unsigned int new_data_len, u32 pad_len)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct scatterlist *sg; /* used to build sgs in mbox message */
+ u32 datalen; /* Number of bytes of response data expected */
+ u32 stat_len;
+
+ mssg->spu.src = kcalloc(tx_frag_num, sizeof(struct scatterlist),
+ rctx->gfp);
+ if (!mssg->spu.src)
+ return -ENOMEM;
+
+ sg = mssg->spu.src;
+ sg_init_table(sg, tx_frag_num);
+
+ sg_set_buf(sg++, rctx->msg_buf.bcm_spu_req_hdr,
+ BCM_HDR_LEN + spu_hdr_len);
+
+ if (hash_carry_len)
+ sg_set_buf(sg++, rctx->hash_carry, hash_carry_len);
+
+ if (new_data_len) {
+ /* Copy in each src sg entry from request, up to chunksize */
+ datalen = spu_msg_sg_add(&sg, &rctx->src_sg, &rctx->src_skip,
+ rctx->src_nents, new_data_len);
+ if (datalen < new_data_len) {
+ pr_err("%s(): failed to copy src sg to mbox msg",
+ __func__);
+ return -EFAULT;
+ }
+ }
+
+ if (pad_len)
+ sg_set_buf(sg++, rctx->msg_buf.spu_req_pad, pad_len);
+
+ stat_len = spu->spu_tx_status_len();
+ if (stat_len) {
+ memset(rctx->msg_buf.tx_stat, 0, stat_len);
+ sg_set_buf(sg, rctx->msg_buf.tx_stat, stat_len);
+ }
+
+ return 0;
+}
+
+/**
+ * handle_ahash_req() - Process an asynchronous hash request from the crypto
+ * API.
+ * @rctx: Crypto request context
+ *
+ * Builds a SPU request message embedded in a mailbox message and submits the
+ * mailbox message on a selected mailbox channel. The SPU request message is
+ * constructed as a scatterlist, including entries from the crypto API's
+ * src scatterlist to avoid copying the data to be hashed. This function is
+ * called either on the thread from the crypto API, or, in the case that the
+ * crypto API request is too large to fit in a single SPU request message,
+ * on the thread that invokes the receive callback with a response message.
+ * Because some operations require the response from one chunk before the next
+ * chunk can be submitted, we always wait for the response for the previous
+ * chunk before submitting the next chunk. Because requests are submitted in
+ * lock step like this, there is no need to synchronize access to request data
+ * structures.
+ *
+ * Return:
+ * -EINPROGRESS: request has been submitted to SPU and response will be
+ * returned asynchronously
+ * -EAGAIN: non-final request included a small amount of data, which for
+ * efficiency we did not submit to the SPU, but instead stored
+ * to be submitted to the SPU with the next part of the request
+ * other: an error code
+ */
+static int handle_ahash_req(struct iproc_reqctx_s *rctx)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct crypto_async_request *areq = rctx->parent;
+ struct ahash_request *req = ahash_request_cast(areq);
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct crypto_tfm *tfm = crypto_ahash_tfm(ahash);
+ unsigned int blocksize = crypto_tfm_alg_blocksize(tfm);
+ struct iproc_ctx_s *ctx = rctx->ctx;
+
+ /* number of bytes still to be hashed in this req */
+ unsigned int nbytes_to_hash = 0;
+ int err = 0;
+ unsigned int chunksize = 0; /* length of hash carry + new data */
+ /*
+ * length of new data, not from hash carry, to be submitted in
+ * this hw request
+ */
+ unsigned int new_data_len;
+
+ unsigned int chunk_start = 0;
+ u32 db_size; /* Length of data field, incl gcm and hash padding */
+ int pad_len = 0; /* total pad len, including gcm, hash, stat padding */
+ u32 data_pad_len = 0; /* length of GCM/CCM padding */
+ u32 stat_pad_len = 0; /* length of padding to align STATUS word */
+ struct brcm_message *mssg; /* mailbox message */
+ struct spu_request_opts req_opts;
+ struct spu_cipher_parms cipher_parms;
+ struct spu_hash_parms hash_parms;
+ struct spu_aead_parms aead_parms;
+ unsigned int local_nbuf;
+ u32 spu_hdr_len;
+ unsigned int digestsize;
+ u16 rem = 0;
+ int retry_cnt = 0;
+
+ /*
+ * number of entries in src and dst sg. Always includes SPU msg header.
+ * rx always includes a buffer to catch digest and STATUS.
+ */
+ u8 rx_frag_num = 3;
+ u8 tx_frag_num = 1;
+
+ flow_log("total_todo %u, total_sent %u\n",
+ rctx->total_todo, rctx->total_sent);
+
+ memset(&req_opts, 0, sizeof(req_opts));
+ memset(&cipher_parms, 0, sizeof(cipher_parms));
+ memset(&hash_parms, 0, sizeof(hash_parms));
+ memset(&aead_parms, 0, sizeof(aead_parms));
+
+ req_opts.bd_suppress = true;
+ hash_parms.alg = ctx->auth.alg;
+ hash_parms.mode = ctx->auth.mode;
+ hash_parms.type = HASH_TYPE_NONE;
+ hash_parms.key_buf = (u8 *)ctx->authkey;
+ hash_parms.key_len = ctx->authkeylen;
+
+ /*
+ * For hash algorithms below assignment looks bit odd but
+ * it's needed for AES-XCBC and AES-CMAC hash algorithms
+ * to differentiate between 128, 192, 256 bit key values.
+ * Based on the key values, hash algorithm is selected.
+ * For example for 128 bit key, hash algorithm is AES-128.
+ */
+ cipher_parms.type = ctx->cipher_type;
+
+ mssg = &rctx->mb_mssg;
+ chunk_start = rctx->src_sent;
+
+ /*
+ * Compute the amount remaining to hash. This may include data
+ * carried over from previous requests.
+ */
+ nbytes_to_hash = rctx->total_todo - rctx->total_sent;
+ chunksize = nbytes_to_hash;
+ if ((ctx->max_payload != SPU_MAX_PAYLOAD_INF) &&
+ (chunksize > ctx->max_payload))
+ chunksize = ctx->max_payload;
+
+ /*
+ * If this is not a final request and the request data is not a multiple
+ * of a full block, then simply park the extra data and prefix it to the
+ * data for the next request.
+ */
+ if (!rctx->is_final) {
+ u8 *dest = rctx->hash_carry + rctx->hash_carry_len;
+ u16 new_len; /* len of data to add to hash carry */
+
+ rem = chunksize % blocksize; /* remainder */
+ if (rem) {
+ /* chunksize not a multiple of blocksize */
+ chunksize -= rem;
+ if (chunksize == 0) {
+ /* Don't have a full block to submit to hw */
+ new_len = rem - rctx->hash_carry_len;
+ sg_copy_part_to_buf(req->src, dest, new_len,
+ rctx->src_sent);
+ rctx->hash_carry_len = rem;
+ flow_log("Exiting with hash carry len: %u\n",
+ rctx->hash_carry_len);
+ packet_dump(" buf: ",
+ rctx->hash_carry,
+ rctx->hash_carry_len);
+ return -EAGAIN;
+ }
+ }
+ }
+
+ /* if we have hash carry, then prefix it to the data in this request */
+ local_nbuf = rctx->hash_carry_len;
+ rctx->hash_carry_len = 0;
+ if (local_nbuf)
+ tx_frag_num++;
+ new_data_len = chunksize - local_nbuf;
+
+ /* Count number of sg entries to be used in this request */
+ rctx->src_nents = spu_sg_count(rctx->src_sg, rctx->src_skip,
+ new_data_len);
+
+ /* AES hashing keeps key size in type field, so need to copy it here */
+ if (hash_parms.alg == HASH_ALG_AES)
+ hash_parms.type = cipher_parms.type;
+ else
+ hash_parms.type = spu->spu_hash_type(rctx->total_sent);
+
+ digestsize = spu->spu_digest_size(ctx->digestsize, ctx->auth.alg,
+ hash_parms.type);
+ hash_parms.digestsize = digestsize;
+
+ /* update the indexes */
+ rctx->total_sent += chunksize;
+ /* if you sent a prebuf then that wasn't from this req->src */
+ rctx->src_sent += new_data_len;
+
+ if ((rctx->total_sent == rctx->total_todo) && rctx->is_final)
+ hash_parms.pad_len = spu->spu_hash_pad_len(hash_parms.alg,
+ hash_parms.mode,
+ chunksize,
+ blocksize);
+
+ /*
+ * If a non-first chunk, then include the digest returned from the
+ * previous chunk so that hw can add to it (except for AES types).
+ */
+ if ((hash_parms.type == HASH_TYPE_UPDT) &&
+ (hash_parms.alg != HASH_ALG_AES)) {
+ hash_parms.key_buf = rctx->incr_hash;
+ hash_parms.key_len = digestsize;
+ }
+
+ atomic64_add(chunksize, &iproc_priv.bytes_out);
+
+ flow_log("%s() final: %u nbuf: %u ",
+ __func__, rctx->is_final, local_nbuf);
+
+ if (ctx->max_payload == SPU_MAX_PAYLOAD_INF)
+ flow_log("max_payload infinite\n");
+ else
+ flow_log("max_payload %u\n", ctx->max_payload);
+
+ flow_log("chunk_start: %u chunk_size: %u\n", chunk_start, chunksize);
+
+ /* Prepend SPU header with type 3 BCM header */
+ memcpy(rctx->msg_buf.bcm_spu_req_hdr, BCMHEADER, BCM_HDR_LEN);
+
+ hash_parms.prebuf_len = local_nbuf;
+ spu_hdr_len = spu->spu_create_request(rctx->msg_buf.bcm_spu_req_hdr +
+ BCM_HDR_LEN,
+ &req_opts, &cipher_parms,
+ &hash_parms, &aead_parms,
+ new_data_len);
+
+ if (spu_hdr_len == 0) {
+ pr_err("Failed to create SPU request header\n");
+ return -EFAULT;
+ }
+
+ /*
+ * Determine total length of padding required. Put all padding in one
+ * buffer.
+ */
+ data_pad_len = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode, chunksize);
+ db_size = spu_real_db_size(0, 0, local_nbuf, new_data_len,
+ 0, 0, hash_parms.pad_len);
+ if (spu->spu_tx_status_len())
+ stat_pad_len = spu->spu_wordalign_padlen(db_size);
+ if (stat_pad_len)
+ rx_frag_num++;
+ pad_len = hash_parms.pad_len + data_pad_len + stat_pad_len;
+ if (pad_len) {
+ tx_frag_num++;
+ spu->spu_request_pad(rctx->msg_buf.spu_req_pad, data_pad_len,
+ hash_parms.pad_len, ctx->auth.alg,
+ ctx->auth.mode, rctx->total_sent,
+ stat_pad_len);
+ }
+
+ spu->spu_dump_msg_hdr(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN,
+ spu_hdr_len);
+ packet_dump(" prebuf: ", rctx->hash_carry, local_nbuf);
+ flow_log("Data:\n");
+ dump_sg(rctx->src_sg, rctx->src_skip, new_data_len);
+ packet_dump(" pad: ", rctx->msg_buf.spu_req_pad, pad_len);
+
+ /*
+ * Build mailbox message containing SPU request msg and rx buffers
+ * to catch response message
+ */
+ memset(mssg, 0, sizeof(*mssg));
+ mssg->type = BRCM_MESSAGE_SPU;
+ mssg->ctx = rctx; /* Will be returned in response */
+
+ /* Create rx scatterlist to catch result */
+ err = spu_ahash_rx_sg_create(mssg, rctx, rx_frag_num, digestsize,
+ stat_pad_len);
+ if (err)
+ return err;
+
+ /* Create tx scatterlist containing SPU request message */
+ tx_frag_num += rctx->src_nents;
+ if (spu->spu_tx_status_len())
+ tx_frag_num++;
+ err = spu_ahash_tx_sg_create(mssg, rctx, tx_frag_num, spu_hdr_len,
+ local_nbuf, new_data_len, pad_len);
+ if (err)
+ return err;
+
+ err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], mssg);
+ if (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) {
+ while ((err == -ENOBUFS) && (retry_cnt < SPU_MB_RETRY_MAX)) {
+ /*
+ * Mailbox queue is full. Since MAY_SLEEP is set, assume
+ * not in atomic context and we can wait and try again.
+ */
+ retry_cnt++;
+ usleep_range(MBOX_SLEEP_MIN, MBOX_SLEEP_MAX);
+ err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx],
+ mssg);
+ atomic_inc(&iproc_priv.mb_no_spc);
+ }
+ }
+ if (err < 0) {
+ atomic_inc(&iproc_priv.mb_send_fail);
+ return err;
+ }
+ return -EINPROGRESS;
+}
+
+/**
+ * spu_hmac_outer_hash() - Request synchonous software compute of the outer hash
+ * for an HMAC request.
+ * @req: The HMAC request from the crypto API
+ * @ctx: The session context
+ *
+ * Return: 0 if synchronous hash operation successful
+ * -EINVAL if the hash algo is unrecognized
+ * any other value indicates an error
+ */
+static int spu_hmac_outer_hash(struct ahash_request *req,
+ struct iproc_ctx_s *ctx)
+{
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ unsigned int blocksize =
+ crypto_tfm_alg_blocksize(crypto_ahash_tfm(ahash));
+ int rc;
+
+ switch (ctx->auth.alg) {
+ case HASH_ALG_MD5:
+ rc = do_shash("md5", req->result, ctx->opad, blocksize,
+ req->result, ctx->digestsize, NULL, 0);
+ break;
+ case HASH_ALG_SHA1:
+ rc = do_shash("sha1", req->result, ctx->opad, blocksize,
+ req->result, ctx->digestsize, NULL, 0);
+ break;
+ case HASH_ALG_SHA224:
+ rc = do_shash("sha224", req->result, ctx->opad, blocksize,
+ req->result, ctx->digestsize, NULL, 0);
+ break;
+ case HASH_ALG_SHA256:
+ rc = do_shash("sha256", req->result, ctx->opad, blocksize,
+ req->result, ctx->digestsize, NULL, 0);
+ break;
+ case HASH_ALG_SHA384:
+ rc = do_shash("sha384", req->result, ctx->opad, blocksize,
+ req->result, ctx->digestsize, NULL, 0);
+ break;
+ case HASH_ALG_SHA512:
+ rc = do_shash("sha512", req->result, ctx->opad, blocksize,
+ req->result, ctx->digestsize, NULL, 0);
+ break;
+ default:
+ pr_err("%s() Error : unknown hmac type\n", __func__);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/**
+ * ahash_req_done() - Process a hash result from the SPU hardware.
+ * @rctx: Crypto request context
+ *
+ * Return: 0 if successful
+ * < 0 if an error
+ */
+static int ahash_req_done(struct iproc_reqctx_s *rctx)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct crypto_async_request *areq = rctx->parent;
+ struct ahash_request *req = ahash_request_cast(areq);
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ int err;
+
+ memcpy(req->result, rctx->msg_buf.digest, ctx->digestsize);
+
+ if (spu->spu_type == SPU_TYPE_SPUM) {
+ /* byte swap the output from the UPDT function to network byte
+ * order
+ */
+ if (ctx->auth.alg == HASH_ALG_MD5) {
+ __swab32s((u32 *)req->result);
+ __swab32s(((u32 *)req->result) + 1);
+ __swab32s(((u32 *)req->result) + 2);
+ __swab32s(((u32 *)req->result) + 3);
+ __swab32s(((u32 *)req->result) + 4);
+ }
+ }
+
+ flow_dump(" digest ", req->result, ctx->digestsize);
+
+ /* if this an HMAC then do the outer hash */
+ if (rctx->is_sw_hmac) {
+ err = spu_hmac_outer_hash(req, ctx);
+ if (err < 0)
+ return err;
+ flow_dump(" hmac: ", req->result, ctx->digestsize);
+ }
+
+ if (rctx->is_sw_hmac || ctx->auth.mode == HASH_MODE_HMAC) {
+ atomic_inc(&iproc_priv.op_counts[SPU_OP_HMAC]);
+ atomic_inc(&iproc_priv.hmac_cnt[ctx->auth.alg]);
+ } else {
+ atomic_inc(&iproc_priv.op_counts[SPU_OP_HASH]);
+ atomic_inc(&iproc_priv.hash_cnt[ctx->auth.alg]);
+ }
+
+ return 0;
+}
+
+/**
+ * handle_ahash_resp() - Process a SPU response message for a hash request.
+ * Checks if the entire crypto API request has been processed, and if so,
+ * invokes post processing on the result.
+ * @rctx: Crypto request context
+ */
+static void handle_ahash_resp(struct iproc_reqctx_s *rctx)
+{
+ struct iproc_ctx_s *ctx = rctx->ctx;
+#ifdef DEBUG
+ struct crypto_async_request *areq = rctx->parent;
+ struct ahash_request *req = ahash_request_cast(areq);
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ unsigned int blocksize =
+ crypto_tfm_alg_blocksize(crypto_ahash_tfm(ahash));
+#endif
+ /*
+ * Save hash to use as input to next op if incremental. Might be copying
+ * too much, but that's easier than figuring out actual digest size here
+ */
+ memcpy(rctx->incr_hash, rctx->msg_buf.digest, MAX_DIGEST_SIZE);
+
+ flow_log("%s() blocksize:%u digestsize:%u\n",
+ __func__, blocksize, ctx->digestsize);
+
+ atomic64_add(ctx->digestsize, &iproc_priv.bytes_in);
+
+ if (rctx->is_final && (rctx->total_sent == rctx->total_todo))
+ ahash_req_done(rctx);
+}
+
+/**
+ * spu_aead_rx_sg_create() - Build up the scatterlist of buffers used to receive
+ * a SPU response message for an AEAD request. Includes buffers to catch SPU
+ * message headers and the response data.
+ * @mssg: mailbox message containing the receive sg
+ * @rctx: crypto request context
+ * @rx_frag_num: number of scatterlist elements required to hold the
+ * SPU response message
+ * @assoc_len: Length of associated data included in the crypto request
+ * @ret_iv_len: Length of IV returned in response
+ * @resp_len: Number of bytes of response data expected to be written to
+ * dst buffer from crypto API
+ * @digestsize: Length of hash digest, in bytes
+ * @stat_pad_len: Number of bytes required to pad the STAT field to
+ * a 4-byte boundary
+ *
+ * The scatterlist that gets allocated here is freed in spu_chunk_cleanup()
+ * when the request completes, whether the request is handled successfully or
+ * there is an error.
+ *
+ * Returns:
+ * 0 if successful
+ * < 0 if an error
+ */
+static int spu_aead_rx_sg_create(struct brcm_message *mssg,
+ struct aead_request *req,
+ struct iproc_reqctx_s *rctx,
+ u8 rx_frag_num,
+ unsigned int assoc_len,
+ u32 ret_iv_len, unsigned int resp_len,
+ unsigned int digestsize, u32 stat_pad_len)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct scatterlist *sg; /* used to build sgs in mbox message */
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ u32 datalen; /* Number of bytes of response data expected */
+ u32 assoc_buf_len;
+ u8 data_padlen = 0;
+
+ if (ctx->is_rfc4543) {
+ /* RFC4543: only pad after data, not after AAD */
+ data_padlen = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode,
+ assoc_len + resp_len);
+ assoc_buf_len = assoc_len;
+ } else {
+ data_padlen = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode,
+ resp_len);
+ assoc_buf_len = spu->spu_assoc_resp_len(ctx->cipher.mode,
+ assoc_len, ret_iv_len,
+ rctx->is_encrypt);
+ }
+
+ if (ctx->cipher.mode == CIPHER_MODE_CCM)
+ /* ICV (after data) must be in the next 32-bit word for CCM */
+ data_padlen += spu->spu_wordalign_padlen(assoc_buf_len +
+ resp_len +
+ data_padlen);
+
+ if (data_padlen)
+ /* have to catch gcm pad in separate buffer */
+ rx_frag_num++;
+
+ mssg->spu.dst = kcalloc(rx_frag_num, sizeof(struct scatterlist),
+ rctx->gfp);
+ if (!mssg->spu.dst)
+ return -ENOMEM;
+
+ sg = mssg->spu.dst;
+ sg_init_table(sg, rx_frag_num);
+
+ /* Space for SPU message header */
+ sg_set_buf(sg++, rctx->msg_buf.spu_resp_hdr, ctx->spu_resp_hdr_len);
+
+ if (assoc_buf_len) {
+ /*
+ * Don't write directly to req->dst, because SPU may pad the
+ * assoc data in the response
+ */
+ memset(rctx->msg_buf.a.resp_aad, 0, assoc_buf_len);
+ sg_set_buf(sg++, rctx->msg_buf.a.resp_aad, assoc_buf_len);
+ }
+
+ if (resp_len) {
+ /*
+ * Copy in each dst sg entry from request, up to chunksize.
+ * dst sg catches just the data. digest caught in separate buf.
+ */
+ datalen = spu_msg_sg_add(&sg, &rctx->dst_sg, &rctx->dst_skip,
+ rctx->dst_nents, resp_len);
+ if (datalen < (resp_len)) {
+ pr_err("%s(): failed to copy dst sg to mbox msg. expected len %u, datalen %u",
+ __func__, resp_len, datalen);
+ return -EFAULT;
+ }
+ }
+
+ /* If GCM/CCM data is padded, catch padding in separate buffer */
+ if (data_padlen) {
+ memset(rctx->msg_buf.a.gcmpad, 0, data_padlen);
+ sg_set_buf(sg++, rctx->msg_buf.a.gcmpad, data_padlen);
+ }
+
+ /* Always catch ICV in separate buffer */
+ sg_set_buf(sg++, rctx->msg_buf.digest, digestsize);
+
+ flow_log("stat_pad_len %u\n", stat_pad_len);
+ if (stat_pad_len) {
+ memset(rctx->msg_buf.rx_stat_pad, 0, stat_pad_len);
+ sg_set_buf(sg++, rctx->msg_buf.rx_stat_pad, stat_pad_len);
+ }
+
+ memset(rctx->msg_buf.rx_stat, 0, SPU_RX_STATUS_LEN);
+ sg_set_buf(sg, rctx->msg_buf.rx_stat, spu->spu_rx_status_len());
+
+ return 0;
+}
+
+/**
+ * spu_aead_tx_sg_create() - Build up the scatterlist of buffers used to send a
+ * SPU request message for an AEAD request. Includes SPU message headers and the
+ * request data.
+ * @mssg: mailbox message containing the transmit sg
+ * @rctx: crypto request context
+ * @tx_frag_num: number of scatterlist elements required to construct the
+ * SPU request message
+ * @spu_hdr_len: length of SPU message header in bytes
+ * @assoc: crypto API associated data scatterlist
+ * @assoc_len: length of associated data
+ * @assoc_nents: number of scatterlist entries containing assoc data
+ * @aead_iv_len: length of AEAD IV, if included
+ * @chunksize: Number of bytes of request data
+ * @aad_pad_len: Number of bytes of padding at end of AAD. For GCM/CCM.
+ * @pad_len: Number of pad bytes
+ * @incl_icv: If true, write separate ICV buffer after data and
+ * any padding
+ *
+ * The scatterlist that gets allocated here is freed in spu_chunk_cleanup()
+ * when the request completes, whether the request is handled successfully or
+ * there is an error.
+ *
+ * Return:
+ * 0 if successful
+ * < 0 if an error
+ */
+static int spu_aead_tx_sg_create(struct brcm_message *mssg,
+ struct iproc_reqctx_s *rctx,
+ u8 tx_frag_num,
+ u32 spu_hdr_len,
+ struct scatterlist *assoc,
+ unsigned int assoc_len,
+ int assoc_nents,
+ unsigned int aead_iv_len,
+ unsigned int chunksize,
+ u32 aad_pad_len, u32 pad_len, bool incl_icv)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct scatterlist *sg; /* used to build sgs in mbox message */
+ struct scatterlist *assoc_sg = assoc;
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ u32 datalen; /* Number of bytes of data to write */
+ u32 written; /* Number of bytes of data written */
+ u32 assoc_offset = 0;
+ u32 stat_len;
+
+ mssg->spu.src = kcalloc(tx_frag_num, sizeof(struct scatterlist),
+ rctx->gfp);
+ if (!mssg->spu.src)
+ return -ENOMEM;
+
+ sg = mssg->spu.src;
+ sg_init_table(sg, tx_frag_num);
+
+ sg_set_buf(sg++, rctx->msg_buf.bcm_spu_req_hdr,
+ BCM_HDR_LEN + spu_hdr_len);
+
+ if (assoc_len) {
+ /* Copy in each associated data sg entry from request */
+ written = spu_msg_sg_add(&sg, &assoc_sg, &assoc_offset,
+ assoc_nents, assoc_len);
+ if (written < assoc_len) {
+ pr_err("%s(): failed to copy assoc sg to mbox msg",
+ __func__);
+ return -EFAULT;
+ }
+ }
+
+ if (aead_iv_len)
+ sg_set_buf(sg++, rctx->msg_buf.iv_ctr, aead_iv_len);
+
+ if (aad_pad_len) {
+ memset(rctx->msg_buf.a.req_aad_pad, 0, aad_pad_len);
+ sg_set_buf(sg++, rctx->msg_buf.a.req_aad_pad, aad_pad_len);
+ }
+
+ datalen = chunksize;
+ if ((chunksize > ctx->digestsize) && incl_icv)
+ datalen -= ctx->digestsize;
+ if (datalen) {
+ /* For aead, a single msg should consume the entire src sg */
+ written = spu_msg_sg_add(&sg, &rctx->src_sg, &rctx->src_skip,
+ rctx->src_nents, datalen);
+ if (written < datalen) {
+ pr_err("%s(): failed to copy src sg to mbox msg",
+ __func__);
+ return -EFAULT;
+ }
+ }
+
+ if (pad_len) {
+ memset(rctx->msg_buf.spu_req_pad, 0, pad_len);
+ sg_set_buf(sg++, rctx->msg_buf.spu_req_pad, pad_len);
+ }
+
+ if (incl_icv)
+ sg_set_buf(sg++, rctx->msg_buf.digest, ctx->digestsize);
+
+ stat_len = spu->spu_tx_status_len();
+ if (stat_len) {
+ memset(rctx->msg_buf.tx_stat, 0, stat_len);
+ sg_set_buf(sg, rctx->msg_buf.tx_stat, stat_len);
+ }
+ return 0;
+}
+
+/**
+ * handle_aead_req() - Submit a SPU request message for the next chunk of the
+ * current AEAD request.
+ * @rctx: Crypto request context
+ *
+ * Unlike other operation types, we assume the length of the request fits in
+ * a single SPU request message. aead_enqueue() makes sure this is true.
+ * Comments for other op types regarding threads applies here as well.
+ *
+ * Unlike incremental hash ops, where the spu returns the entire hash for
+ * truncated algs like sha-224, the SPU returns just the truncated hash in
+ * response to aead requests. So digestsize is always ctx->digestsize here.
+ *
+ * Return: -EINPROGRESS: crypto request has been accepted and result will be
+ * returned asynchronously
+ * Any other value indicates an error
+ */
+static int handle_aead_req(struct iproc_reqctx_s *rctx)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct crypto_async_request *areq = rctx->parent;
+ struct aead_request *req = container_of(areq,
+ struct aead_request, base);
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ int err;
+ unsigned int chunksize;
+ unsigned int resp_len;
+ u32 spu_hdr_len;
+ u32 db_size;
+ u32 stat_pad_len;
+ u32 pad_len;
+ struct brcm_message *mssg; /* mailbox message */
+ struct spu_request_opts req_opts;
+ struct spu_cipher_parms cipher_parms;
+ struct spu_hash_parms hash_parms;
+ struct spu_aead_parms aead_parms;
+ int assoc_nents = 0;
+ bool incl_icv = false;
+ unsigned int digestsize = ctx->digestsize;
+ int retry_cnt = 0;
+
+ /* number of entries in src and dst sg. Always includes SPU msg header.
+ */
+ u8 rx_frag_num = 2; /* and STATUS */
+ u8 tx_frag_num = 1;
+
+ /* doing the whole thing at once */
+ chunksize = rctx->total_todo;
+
+ flow_log("%s: chunksize %u\n", __func__, chunksize);
+
+ memset(&req_opts, 0, sizeof(req_opts));
+ memset(&hash_parms, 0, sizeof(hash_parms));
+ memset(&aead_parms, 0, sizeof(aead_parms));
+
+ req_opts.is_inbound = !(rctx->is_encrypt);
+ req_opts.auth_first = ctx->auth_first;
+ req_opts.is_aead = true;
+ req_opts.is_esp = ctx->is_esp;
+
+ cipher_parms.alg = ctx->cipher.alg;
+ cipher_parms.mode = ctx->cipher.mode;
+ cipher_parms.type = ctx->cipher_type;
+ cipher_parms.key_buf = ctx->enckey;
+ cipher_parms.key_len = ctx->enckeylen;
+ cipher_parms.iv_buf = rctx->msg_buf.iv_ctr;
+ cipher_parms.iv_len = rctx->iv_ctr_len;
+
+ hash_parms.alg = ctx->auth.alg;
+ hash_parms.mode = ctx->auth.mode;
+ hash_parms.type = HASH_TYPE_NONE;
+ hash_parms.key_buf = (u8 *)ctx->authkey;
+ hash_parms.key_len = ctx->authkeylen;
+ hash_parms.digestsize = digestsize;
+
+ if ((ctx->auth.alg == HASH_ALG_SHA224) &&
+ (ctx->authkeylen < SHA224_DIGEST_SIZE))
+ hash_parms.key_len = SHA224_DIGEST_SIZE;
+
+ aead_parms.assoc_size = req->assoclen;
+ if (ctx->is_esp && !ctx->is_rfc4543) {
+ /*
+ * 8-byte IV is included assoc data in request. SPU2
+ * expects AAD to include just SPI and seqno. So
+ * subtract off the IV len.
+ */
+ aead_parms.assoc_size -= GCM_ESP_IV_SIZE;
+
+ if (rctx->is_encrypt) {
+ aead_parms.return_iv = true;
+ aead_parms.ret_iv_len = GCM_ESP_IV_SIZE;
+ aead_parms.ret_iv_off = GCM_ESP_SALT_SIZE;
+ }
+ } else {
+ aead_parms.ret_iv_len = 0;
+ }
+
+ /*
+ * Count number of sg entries from the crypto API request that are to
+ * be included in this mailbox message. For dst sg, don't count space
+ * for digest. Digest gets caught in a separate buffer and copied back
+ * to dst sg when processing response.
+ */
+ rctx->src_nents = spu_sg_count(rctx->src_sg, rctx->src_skip, chunksize);
+ rctx->dst_nents = spu_sg_count(rctx->dst_sg, rctx->dst_skip, chunksize);
+ if (aead_parms.assoc_size)
+ assoc_nents = spu_sg_count(rctx->assoc, 0,
+ aead_parms.assoc_size);
+
+ mssg = &rctx->mb_mssg;
+
+ rctx->total_sent = chunksize;
+ rctx->src_sent = chunksize;
+ if (spu->spu_assoc_resp_len(ctx->cipher.mode,
+ aead_parms.assoc_size,
+ aead_parms.ret_iv_len,
+ rctx->is_encrypt))
+ rx_frag_num++;
+
+ aead_parms.iv_len = spu->spu_aead_ivlen(ctx->cipher.mode,
+ rctx->iv_ctr_len);
+
+ if (ctx->auth.alg == HASH_ALG_AES)
+ hash_parms.type = ctx->cipher_type;
+
+ /* General case AAD padding (CCM and RFC4543 special cases below) */
+ aead_parms.aad_pad_len = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode,
+ aead_parms.assoc_size);
+
+ /* General case data padding (CCM decrypt special case below) */
+ aead_parms.data_pad_len = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode,
+ chunksize);
+
+ if (ctx->cipher.mode == CIPHER_MODE_CCM) {
+ /*
+ * for CCM, AAD len + 2 (rather than AAD len) needs to be
+ * 128-bit aligned
+ */
+ aead_parms.aad_pad_len = spu->spu_gcm_ccm_pad_len(
+ ctx->cipher.mode,
+ aead_parms.assoc_size + 2);
+
+ /*
+ * And when decrypting CCM, need to pad without including
+ * size of ICV which is tacked on to end of chunk
+ */
+ if (!rctx->is_encrypt)
+ aead_parms.data_pad_len =
+ spu->spu_gcm_ccm_pad_len(ctx->cipher.mode,
+ chunksize - digestsize);
+
+ /* CCM also requires software to rewrite portions of IV: */
+ spu->spu_ccm_update_iv(digestsize, &cipher_parms, req->assoclen,
+ chunksize, rctx->is_encrypt,
+ ctx->is_esp);
+ }
+
+ if (ctx->is_rfc4543) {
+ /*
+ * RFC4543: data is included in AAD, so don't pad after AAD
+ * and pad data based on both AAD + data size
+ */
+ aead_parms.aad_pad_len = 0;
+ if (!rctx->is_encrypt)
+ aead_parms.data_pad_len = spu->spu_gcm_ccm_pad_len(
+ ctx->cipher.mode,
+ aead_parms.assoc_size + chunksize -
+ digestsize);
+ else
+ aead_parms.data_pad_len = spu->spu_gcm_ccm_pad_len(
+ ctx->cipher.mode,
+ aead_parms.assoc_size + chunksize);
+
+ req_opts.is_rfc4543 = true;
+ }
+
+ if (spu_req_incl_icv(ctx->cipher.mode, rctx->is_encrypt)) {
+ incl_icv = true;
+ tx_frag_num++;
+ /* Copy ICV from end of src scatterlist to digest buf */
+ sg_copy_part_to_buf(req->src, rctx->msg_buf.digest, digestsize,
+ req->assoclen + rctx->total_sent -
+ digestsize);
+ }
+
+ atomic64_add(chunksize, &iproc_priv.bytes_out);
+
+ flow_log("%s()-sent chunksize:%u hmac_offset:%u\n",
+ __func__, chunksize, hash_parms.hmac_offset);
+
+ /* Prepend SPU header with type 3 BCM header */
+ memcpy(rctx->msg_buf.bcm_spu_req_hdr, BCMHEADER, BCM_HDR_LEN);
+
+ spu_hdr_len = spu->spu_create_request(rctx->msg_buf.bcm_spu_req_hdr +
+ BCM_HDR_LEN, &req_opts,
+ &cipher_parms, &hash_parms,
+ &aead_parms, chunksize);
+
+ /* Determine total length of padding. Put all padding in one buffer. */
+ db_size = spu_real_db_size(aead_parms.assoc_size, aead_parms.iv_len, 0,
+ chunksize, aead_parms.aad_pad_len,
+ aead_parms.data_pad_len, 0);
+
+ stat_pad_len = spu->spu_wordalign_padlen(db_size);
+
+ if (stat_pad_len)
+ rx_frag_num++;
+ pad_len = aead_parms.data_pad_len + stat_pad_len;
+ if (pad_len) {
+ tx_frag_num++;
+ spu->spu_request_pad(rctx->msg_buf.spu_req_pad,
+ aead_parms.data_pad_len, 0,
+ ctx->auth.alg, ctx->auth.mode,
+ rctx->total_sent, stat_pad_len);
+ }
+
+ spu->spu_dump_msg_hdr(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN,
+ spu_hdr_len);
+ dump_sg(rctx->assoc, 0, aead_parms.assoc_size);
+ packet_dump(" aead iv: ", rctx->msg_buf.iv_ctr, aead_parms.iv_len);
+ packet_log("BD:\n");
+ dump_sg(rctx->src_sg, rctx->src_skip, chunksize);
+ packet_dump(" pad: ", rctx->msg_buf.spu_req_pad, pad_len);
+
+ /*
+ * Build mailbox message containing SPU request msg and rx buffers
+ * to catch response message
+ */
+ memset(mssg, 0, sizeof(*mssg));
+ mssg->type = BRCM_MESSAGE_SPU;
+ mssg->ctx = rctx; /* Will be returned in response */
+
+ /* Create rx scatterlist to catch result */
+ rx_frag_num += rctx->dst_nents;
+ resp_len = chunksize;
+
+ /*
+ * Always catch ICV in separate buffer. Have to for GCM/CCM because of
+ * padding. Have to for SHA-224 and other truncated SHAs because SPU
+ * sends entire digest back.
+ */
+ rx_frag_num++;
+
+ if (((ctx->cipher.mode == CIPHER_MODE_GCM) ||
+ (ctx->cipher.mode == CIPHER_MODE_CCM)) && !rctx->is_encrypt) {
+ /*
+ * Input is ciphertxt plus ICV, but ICV not incl
+ * in output.
+ */
+ resp_len -= ctx->digestsize;
+ if (resp_len == 0)
+ /* no rx frags to catch output data */
+ rx_frag_num -= rctx->dst_nents;
+ }
+
+ err = spu_aead_rx_sg_create(mssg, req, rctx, rx_frag_num,
+ aead_parms.assoc_size,
+ aead_parms.ret_iv_len, resp_len, digestsize,
+ stat_pad_len);
+ if (err)
+ return err;
+
+ /* Create tx scatterlist containing SPU request message */
+ tx_frag_num += rctx->src_nents;
+ tx_frag_num += assoc_nents;
+ if (aead_parms.aad_pad_len)
+ tx_frag_num++;
+ if (aead_parms.iv_len)
+ tx_frag_num++;
+ if (spu->spu_tx_status_len())
+ tx_frag_num++;
+ err = spu_aead_tx_sg_create(mssg, rctx, tx_frag_num, spu_hdr_len,
+ rctx->assoc, aead_parms.assoc_size,
+ assoc_nents, aead_parms.iv_len, chunksize,
+ aead_parms.aad_pad_len, pad_len, incl_icv);
+ if (err)
+ return err;
+
+ err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], mssg);
+ if (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) {
+ while ((err == -ENOBUFS) && (retry_cnt < SPU_MB_RETRY_MAX)) {
+ /*
+ * Mailbox queue is full. Since MAY_SLEEP is set, assume
+ * not in atomic context and we can wait and try again.
+ */
+ retry_cnt++;
+ usleep_range(MBOX_SLEEP_MIN, MBOX_SLEEP_MAX);
+ err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx],
+ mssg);
+ atomic_inc(&iproc_priv.mb_no_spc);
+ }
+ }
+ if (err < 0) {
+ atomic_inc(&iproc_priv.mb_send_fail);
+ return err;
+ }
+
+ return -EINPROGRESS;
+}
+
+/**
+ * handle_aead_resp() - Process a SPU response message for an AEAD request.
+ * @rctx: Crypto request context
+ */
+static void handle_aead_resp(struct iproc_reqctx_s *rctx)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct crypto_async_request *areq = rctx->parent;
+ struct aead_request *req = container_of(areq,
+ struct aead_request, base);
+ struct iproc_ctx_s *ctx = rctx->ctx;
+ u32 payload_len;
+ unsigned int icv_offset;
+ u32 result_len;
+
+ /* See how much data was returned */
+ payload_len = spu->spu_payload_length(rctx->msg_buf.spu_resp_hdr);
+ flow_log("payload_len %u\n", payload_len);
+
+ /* only count payload */
+ atomic64_add(payload_len, &iproc_priv.bytes_in);
+
+ if (req->assoclen)
+ packet_dump(" assoc_data ", rctx->msg_buf.a.resp_aad,
+ req->assoclen);
+
+ /*
+ * Copy the ICV back to the destination
+ * buffer. In decrypt case, SPU gives us back the digest, but crypto
+ * API doesn't expect ICV in dst buffer.
+ */
+ result_len = req->cryptlen;
+ if (rctx->is_encrypt) {
+ icv_offset = req->assoclen + rctx->total_sent;
+ packet_dump(" ICV: ", rctx->msg_buf.digest, ctx->digestsize);
+ flow_log("copying ICV to dst sg at offset %u\n", icv_offset);
+ sg_copy_part_from_buf(req->dst, rctx->msg_buf.digest,
+ ctx->digestsize, icv_offset);
+ result_len += ctx->digestsize;
+ }
+
+ packet_log("response data: ");
+ dump_sg(req->dst, req->assoclen, result_len);
+
+ atomic_inc(&iproc_priv.op_counts[SPU_OP_AEAD]);
+ if (ctx->cipher.alg == CIPHER_ALG_AES) {
+ if (ctx->cipher.mode == CIPHER_MODE_CCM)
+ atomic_inc(&iproc_priv.aead_cnt[AES_CCM]);
+ else if (ctx->cipher.mode == CIPHER_MODE_GCM)
+ atomic_inc(&iproc_priv.aead_cnt[AES_GCM]);
+ else
+ atomic_inc(&iproc_priv.aead_cnt[AUTHENC]);
+ } else {
+ atomic_inc(&iproc_priv.aead_cnt[AUTHENC]);
+ }
+}
+
+/**
+ * spu_chunk_cleanup() - Do cleanup after processing one chunk of a request
+ * @rctx: request context
+ *
+ * Mailbox scatterlists are allocated for each chunk. So free them after
+ * processing each chunk.
+ */
+static void spu_chunk_cleanup(struct iproc_reqctx_s *rctx)
+{
+ /* mailbox message used to tx request */
+ struct brcm_message *mssg = &rctx->mb_mssg;
+
+ kfree(mssg->spu.src);
+ kfree(mssg->spu.dst);
+ memset(mssg, 0, sizeof(struct brcm_message));
+}
+
+/**
+ * finish_req() - Used to invoke the complete callback from the requester when
+ * a request has been handled asynchronously.
+ * @rctx: Request context
+ * @err: Indicates whether the request was successful or not
+ *
+ * Ensures that cleanup has been done for request
+ */
+static void finish_req(struct iproc_reqctx_s *rctx, int err)
+{
+ struct crypto_async_request *areq = rctx->parent;
+
+ flow_log("%s() err:%d\n\n", __func__, err);
+
+ /* No harm done if already called */
+ spu_chunk_cleanup(rctx);
+
+ if (areq)
+ areq->complete(areq, err);
+}
+
+/**
+ * spu_rx_callback() - Callback from mailbox framework with a SPU response.
+ * @cl: mailbox client structure for SPU driver
+ * @msg: mailbox message containing SPU response
+ */
+static void spu_rx_callback(struct mbox_client *cl, void *msg)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct brcm_message *mssg = msg;
+ struct iproc_reqctx_s *rctx;
+ struct iproc_ctx_s *ctx;
+ struct crypto_async_request *areq;
+ int err = 0;
+
+ rctx = mssg->ctx;
+ if (unlikely(!rctx)) {
+ /* This is fatal */
+ pr_err("%s(): no request context", __func__);
+ err = -EFAULT;
+ goto cb_finish;
+ }
+ areq = rctx->parent;
+ ctx = rctx->ctx;
+
+ /* process the SPU status */
+ err = spu->spu_status_process(rctx->msg_buf.rx_stat);
+ if (err != 0) {
+ if (err == SPU_INVALID_ICV)
+ atomic_inc(&iproc_priv.bad_icv);
+ err = -EBADMSG;
+ goto cb_finish;
+ }
+
+ /* Process the SPU response message */
+ switch (rctx->ctx->alg->type) {
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ handle_ablkcipher_resp(rctx);
+ break;
+ case CRYPTO_ALG_TYPE_AHASH:
+ handle_ahash_resp(rctx);
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ handle_aead_resp(rctx);
+ break;
+ default:
+ err = -EINVAL;
+ goto cb_finish;
+ }
+
+ /*
+ * If this response does not complete the request, then send the next
+ * request chunk.
+ */
+ if (rctx->total_sent < rctx->total_todo) {
+ /* Deallocate anything specific to previous chunk */
+ spu_chunk_cleanup(rctx);
+
+ switch (rctx->ctx->alg->type) {
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ err = handle_ablkcipher_req(rctx);
+ break;
+ case CRYPTO_ALG_TYPE_AHASH:
+ err = handle_ahash_req(rctx);
+ if (err == -EAGAIN)
+ /*
+ * we saved data in hash carry, but tell crypto
+ * API we successfully completed request.
+ */
+ err = 0;
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ err = handle_aead_req(rctx);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (err == -EINPROGRESS)
+ /* Successfully submitted request for next chunk */
+ return;
+ }
+
+cb_finish:
+ finish_req(rctx, err);
+}
+
+/* ==================== Kernel Cryptographic API ==================== */
+
+/**
+ * ablkcipher_enqueue() - Handle ablkcipher encrypt or decrypt request.
+ * @req: Crypto API request
+ * @encrypt: true if encrypting; false if decrypting
+ *
+ * Return: -EINPROGRESS if request accepted and result will be returned
+ * asynchronously
+ * < 0 if an error
+ */
+static int ablkcipher_enqueue(struct ablkcipher_request *req, bool encrypt)
+{
+ struct iproc_reqctx_s *rctx = ablkcipher_request_ctx(req);
+ struct iproc_ctx_s *ctx =
+ crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
+ int err;
+
+ flow_log("%s() enc:%u\n", __func__, encrypt);
+
+ rctx->gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ rctx->parent = &req->base;
+ rctx->is_encrypt = encrypt;
+ rctx->bd_suppress = false;
+ rctx->total_todo = req->nbytes;
+ rctx->src_sent = 0;
+ rctx->total_sent = 0;
+ rctx->total_received = 0;
+ rctx->ctx = ctx;
+
+ /* Initialize current position in src and dst scatterlists */
+ rctx->src_sg = req->src;
+ rctx->src_nents = 0;
+ rctx->src_skip = 0;
+ rctx->dst_sg = req->dst;
+ rctx->dst_nents = 0;
+ rctx->dst_skip = 0;
+
+ if (ctx->cipher.mode == CIPHER_MODE_CBC ||
+ ctx->cipher.mode == CIPHER_MODE_CTR ||
+ ctx->cipher.mode == CIPHER_MODE_OFB ||
+ ctx->cipher.mode == CIPHER_MODE_XTS ||
+ ctx->cipher.mode == CIPHER_MODE_GCM ||
+ ctx->cipher.mode == CIPHER_MODE_CCM) {
+ rctx->iv_ctr_len =
+ crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(req));
+ memcpy(rctx->msg_buf.iv_ctr, req->info, rctx->iv_ctr_len);
+ } else {
+ rctx->iv_ctr_len = 0;
+ }
+
+ /* Choose a SPU to process this request */
+ rctx->chan_idx = select_channel();
+ err = handle_ablkcipher_req(rctx);
+ if (err != -EINPROGRESS)
+ /* synchronous result */
+ spu_chunk_cleanup(rctx);
+
+ return err;
+}
+
+static int des_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 tmp[DES_EXPKEY_WORDS];
+
+ if (keylen == DES_KEY_SIZE) {
+ if (des_ekey(tmp, key) == 0) {
+ if (crypto_ablkcipher_get_flags(cipher) &
+ CRYPTO_TFM_REQ_WEAK_KEY) {
+ u32 flags = CRYPTO_TFM_RES_WEAK_KEY;
+
+ crypto_ablkcipher_set_flags(cipher, flags);
+ return -EINVAL;
+ }
+ }
+
+ ctx->cipher_type = CIPHER_TYPE_DES;
+ } else {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int threedes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher);
+
+ if (keylen == (DES_KEY_SIZE * 3)) {
+ const u32 *K = (const u32 *)key;
+ u32 flags = CRYPTO_TFM_RES_BAD_KEY_SCHED;
+
+ if (!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
+ !((K[2] ^ K[4]) | (K[3] ^ K[5]))) {
+ crypto_ablkcipher_set_flags(cipher, flags);
+ return -EINVAL;
+ }
+
+ ctx->cipher_type = CIPHER_TYPE_3DES;
+ } else {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher);
+
+ if (ctx->cipher.mode == CIPHER_MODE_XTS)
+ /* XTS includes two keys of equal length */
+ keylen = keylen / 2;
+
+ switch (keylen) {
+ case AES_KEYSIZE_128:
+ ctx->cipher_type = CIPHER_TYPE_AES128;
+ break;
+ case AES_KEYSIZE_192:
+ ctx->cipher_type = CIPHER_TYPE_AES192;
+ break;
+ case AES_KEYSIZE_256:
+ ctx->cipher_type = CIPHER_TYPE_AES256;
+ break;
+ default:
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+ WARN_ON((ctx->max_payload != SPU_MAX_PAYLOAD_INF) &&
+ ((ctx->max_payload % AES_BLOCK_SIZE) != 0));
+ return 0;
+}
+
+static int rc4_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher);
+ int i;
+
+ ctx->enckeylen = ARC4_MAX_KEY_SIZE + ARC4_STATE_SIZE;
+
+ ctx->enckey[0] = 0x00; /* 0x00 */
+ ctx->enckey[1] = 0x00; /* i */
+ ctx->enckey[2] = 0x00; /* 0x00 */
+ ctx->enckey[3] = 0x00; /* j */
+ for (i = 0; i < ARC4_MAX_KEY_SIZE; i++)
+ ctx->enckey[i + ARC4_STATE_SIZE] = key[i % keylen];
+
+ ctx->cipher_type = CIPHER_TYPE_INIT;
+
+ return 0;
+}
+
+static int ablkcipher_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int keylen)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher);
+ struct spu_cipher_parms cipher_parms;
+ u32 alloc_len = 0;
+ int err;
+
+ flow_log("ablkcipher_setkey() keylen: %d\n", keylen);
+ flow_dump(" key: ", key, keylen);
+
+ switch (ctx->cipher.alg) {
+ case CIPHER_ALG_DES:
+ err = des_setkey(cipher, key, keylen);
+ break;
+ case CIPHER_ALG_3DES:
+ err = threedes_setkey(cipher, key, keylen);
+ break;
+ case CIPHER_ALG_AES:
+ err = aes_setkey(cipher, key, keylen);
+ break;
+ case CIPHER_ALG_RC4:
+ err = rc4_setkey(cipher, key, keylen);
+ break;
+ default:
+ pr_err("%s() Error: unknown cipher alg\n", __func__);
+ err = -EINVAL;
+ }
+ if (err)
+ return err;
+
+ /* RC4 already populated ctx->enkey */
+ if (ctx->cipher.alg != CIPHER_ALG_RC4) {
+ memcpy(ctx->enckey, key, keylen);
+ ctx->enckeylen = keylen;
+ }
+ /* SPU needs XTS keys in the reverse order the crypto API presents */
+ if ((ctx->cipher.alg == CIPHER_ALG_AES) &&
+ (ctx->cipher.mode == CIPHER_MODE_XTS)) {
+ unsigned int xts_keylen = keylen / 2;
+
+ memcpy(ctx->enckey, key + xts_keylen, xts_keylen);
+ memcpy(ctx->enckey + xts_keylen, key, xts_keylen);
+ }
+
+ if (spu->spu_type == SPU_TYPE_SPUM)
+ alloc_len = BCM_HDR_LEN + SPU_HEADER_ALLOC_LEN;
+ else if (spu->spu_type == SPU_TYPE_SPU2)
+ alloc_len = BCM_HDR_LEN + SPU2_HEADER_ALLOC_LEN;
+ memset(ctx->bcm_spu_req_hdr, 0, alloc_len);
+ cipher_parms.iv_buf = NULL;
+ cipher_parms.iv_len = crypto_ablkcipher_ivsize(cipher);
+ flow_log("%s: iv_len %u\n", __func__, cipher_parms.iv_len);
+
+ cipher_parms.alg = ctx->cipher.alg;
+ cipher_parms.mode = ctx->cipher.mode;
+ cipher_parms.type = ctx->cipher_type;
+ cipher_parms.key_buf = ctx->enckey;
+ cipher_parms.key_len = ctx->enckeylen;
+
+ /* Prepend SPU request message with BCM header */
+ memcpy(ctx->bcm_spu_req_hdr, BCMHEADER, BCM_HDR_LEN);
+ ctx->spu_req_hdr_len =
+ spu->spu_cipher_req_init(ctx->bcm_spu_req_hdr + BCM_HDR_LEN,
+ &cipher_parms);
+
+ ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen,
+ ctx->enckeylen,
+ false);
+
+ atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_CIPHER]);
+
+ return 0;
+}
+
+static int ablkcipher_encrypt(struct ablkcipher_request *req)
+{
+ flow_log("ablkcipher_encrypt() nbytes:%u\n", req->nbytes);
+
+ return ablkcipher_enqueue(req, true);
+}
+
+static int ablkcipher_decrypt(struct ablkcipher_request *req)
+{
+ flow_log("ablkcipher_decrypt() nbytes:%u\n", req->nbytes);
+ return ablkcipher_enqueue(req, false);
+}
+
+static int ahash_enqueue(struct ahash_request *req)
+{
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+ int err = 0;
+ const char *alg_name;
+
+ flow_log("ahash_enqueue() nbytes:%u\n", req->nbytes);
+
+ rctx->gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ rctx->parent = &req->base;
+ rctx->ctx = ctx;
+ rctx->bd_suppress = true;
+ memset(&rctx->mb_mssg, 0, sizeof(struct brcm_message));
+
+ /* Initialize position in src scatterlist */
+ rctx->src_sg = req->src;
+ rctx->src_skip = 0;
+ rctx->src_nents = 0;
+ rctx->dst_sg = NULL;
+ rctx->dst_skip = 0;
+ rctx->dst_nents = 0;
+
+ /* SPU2 hardware does not compute hash of zero length data */
+ if ((rctx->is_final == 1) && (rctx->total_todo == 0) &&
+ (iproc_priv.spu.spu_type == SPU_TYPE_SPU2)) {
+ alg_name = crypto_tfm_alg_name(crypto_ahash_tfm(tfm));
+ flow_log("Doing %sfinal %s zero-len hash request in software\n",
+ rctx->is_final ? "" : "non-", alg_name);
+ err = do_shash((unsigned char *)alg_name, req->result,
+ NULL, 0, NULL, 0, ctx->authkey,
+ ctx->authkeylen);
+ if (err < 0)
+ flow_log("Hash request failed with error %d\n", err);
+ return err;
+ }
+ /* Choose a SPU to process this request */
+ rctx->chan_idx = select_channel();
+
+ err = handle_ahash_req(rctx);
+ if (err != -EINPROGRESS)
+ /* synchronous result */
+ spu_chunk_cleanup(rctx);
+
+ if (err == -EAGAIN)
+ /*
+ * we saved data in hash carry, but tell crypto API
+ * we successfully completed request.
+ */
+ err = 0;
+
+ return err;
+}
+
+static int __ahash_init(struct ahash_request *req)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+
+ flow_log("%s()\n", __func__);
+
+ /* Initialize the context */
+ rctx->hash_carry_len = 0;
+ rctx->is_final = 0;
+
+ rctx->total_todo = 0;
+ rctx->src_sent = 0;
+ rctx->total_sent = 0;
+ rctx->total_received = 0;
+
+ ctx->digestsize = crypto_ahash_digestsize(tfm);
+ /* If we add a hash whose digest is larger, catch it here. */
+ WARN_ON(ctx->digestsize > MAX_DIGEST_SIZE);
+
+ rctx->is_sw_hmac = false;
+
+ ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen, 0,
+ true);
+
+ return 0;
+}
+
+/**
+ * spu_no_incr_hash() - Determine whether incremental hashing is supported.
+ * @ctx: Crypto session context
+ *
+ * SPU-2 does not support incremental hashing (we'll have to revisit and
+ * condition based on chip revision or device tree entry if future versions do
+ * support incremental hash)
+ *
+ * SPU-M also doesn't support incremental hashing of AES-XCBC
+ *
+ * Return: true if incremental hashing is not supported
+ * false otherwise
+ */
+bool spu_no_incr_hash(struct iproc_ctx_s *ctx)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+
+ if (spu->spu_type == SPU_TYPE_SPU2)
+ return true;
+
+ if ((ctx->auth.alg == HASH_ALG_AES) &&
+ (ctx->auth.mode == HASH_MODE_XCBC))
+ return true;
+
+ /* Otherwise, incremental hashing is supported */
+ return false;
+}
+
+static int ahash_init(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+ const char *alg_name;
+ struct crypto_shash *hash;
+ int ret;
+ gfp_t gfp;
+
+ if (spu_no_incr_hash(ctx)) {
+ /*
+ * If we get an incremental hashing request and it's not
+ * supported by the hardware, we need to handle it in software
+ * by calling synchronous hash functions.
+ */
+ alg_name = crypto_tfm_alg_name(crypto_ahash_tfm(tfm));
+ hash = crypto_alloc_shash(alg_name, 0, 0);
+ if (IS_ERR(hash)) {
+ ret = PTR_ERR(hash);
+ goto err;
+ }
+
+ gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ ctx->shash = kmalloc(sizeof(*ctx->shash) +
+ crypto_shash_descsize(hash), gfp);
+ if (!ctx->shash) {
+ ret = -ENOMEM;
+ goto err_hash;
+ }
+ ctx->shash->tfm = hash;
+ ctx->shash->flags = 0;
+
+ /* Set the key using data we already have from setkey */
+ if (ctx->authkeylen > 0) {
+ ret = crypto_shash_setkey(hash, ctx->authkey,
+ ctx->authkeylen);
+ if (ret)
+ goto err_shash;
+ }
+
+ /* Initialize hash w/ this key and other params */
+ ret = crypto_shash_init(ctx->shash);
+ if (ret)
+ goto err_shash;
+ } else {
+ /* Otherwise call the internal function which uses SPU hw */
+ ret = __ahash_init(req);
+ }
+
+ return ret;
+
+err_shash:
+ kfree(ctx->shash);
+err_hash:
+ crypto_free_shash(hash);
+err:
+ return ret;
+}
+
+static int __ahash_update(struct ahash_request *req)
+{
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+
+ flow_log("ahash_update() nbytes:%u\n", req->nbytes);
+
+ if (!req->nbytes)
+ return 0;
+ rctx->total_todo += req->nbytes;
+ rctx->src_sent = 0;
+
+ return ahash_enqueue(req);
+}
+
+static int ahash_update(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+ u8 *tmpbuf;
+ int ret;
+ int nents;
+ gfp_t gfp;
+
+ if (spu_no_incr_hash(ctx)) {
+ /*
+ * If we get an incremental hashing request and it's not
+ * supported by the hardware, we need to handle it in software
+ * by calling synchronous hash functions.
+ */
+ if (req->src)
+ nents = sg_nents(req->src);
+ else
+ return -EINVAL;
+
+ /* Copy data from req scatterlist to tmp buffer */
+ gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ tmpbuf = kmalloc(req->nbytes, gfp);
+ if (!tmpbuf)
+ return -ENOMEM;
+
+ if (sg_copy_to_buffer(req->src, nents, tmpbuf, req->nbytes) !=
+ req->nbytes) {
+ kfree(tmpbuf);
+ return -EINVAL;
+ }
+
+ /* Call synchronous update */
+ ret = crypto_shash_update(ctx->shash, tmpbuf, req->nbytes);
+ kfree(tmpbuf);
+ } else {
+ /* Otherwise call the internal function which uses SPU hw */
+ ret = __ahash_update(req);
+ }
+
+ return ret;
+}
+
+static int __ahash_final(struct ahash_request *req)
+{
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+
+ flow_log("ahash_final() nbytes:%u\n", req->nbytes);
+
+ rctx->is_final = 1;
+
+ return ahash_enqueue(req);
+}
+
+static int ahash_final(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+ int ret;
+
+ if (spu_no_incr_hash(ctx)) {
+ /*
+ * If we get an incremental hashing request and it's not
+ * supported by the hardware, we need to handle it in software
+ * by calling synchronous hash functions.
+ */
+ ret = crypto_shash_final(ctx->shash, req->result);
+
+ /* Done with hash, can deallocate it now */
+ crypto_free_shash(ctx->shash->tfm);
+ kfree(ctx->shash);
+
+ } else {
+ /* Otherwise call the internal function which uses SPU hw */
+ ret = __ahash_final(req);
+ }
+
+ return ret;
+}
+
+static int __ahash_finup(struct ahash_request *req)
+{
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+
+ flow_log("ahash_finup() nbytes:%u\n", req->nbytes);
+
+ rctx->total_todo += req->nbytes;
+ rctx->src_sent = 0;
+ rctx->is_final = 1;
+
+ return ahash_enqueue(req);
+}
+
+static int ahash_finup(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+ u8 *tmpbuf;
+ int ret;
+ int nents;
+ gfp_t gfp;
+
+ if (spu_no_incr_hash(ctx)) {
+ /*
+ * If we get an incremental hashing request and it's not
+ * supported by the hardware, we need to handle it in software
+ * by calling synchronous hash functions.
+ */
+ if (req->src) {
+ nents = sg_nents(req->src);
+ } else {
+ ret = -EINVAL;
+ goto ahash_finup_exit;
+ }
+
+ /* Copy data from req scatterlist to tmp buffer */
+ gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ tmpbuf = kmalloc(req->nbytes, gfp);
+ if (!tmpbuf) {
+ ret = -ENOMEM;
+ goto ahash_finup_exit;
+ }
+
+ if (sg_copy_to_buffer(req->src, nents, tmpbuf, req->nbytes) !=
+ req->nbytes) {
+ ret = -EINVAL;
+ goto ahash_finup_free;
+ }
+
+ /* Call synchronous update */
+ ret = crypto_shash_finup(ctx->shash, tmpbuf, req->nbytes,
+ req->result);
+ kfree(tmpbuf);
+ } else {
+ /* Otherwise call the internal function which uses SPU hw */
+ return __ahash_finup(req);
+ }
+ahash_finup_free:
+ kfree(tmpbuf);
+
+ahash_finup_exit:
+ /* Done with hash, can deallocate it now */
+ crypto_free_shash(ctx->shash->tfm);
+ kfree(ctx->shash);
+ return ret;
+}
+
+static int ahash_digest(struct ahash_request *req)
+{
+ int err = 0;
+
+ flow_log("ahash_digest() nbytes:%u\n", req->nbytes);
+
+ /* whole thing at once */
+ err = __ahash_init(req);
+ if (!err)
+ err = __ahash_finup(req);
+
+ return err;
+}
+
+static int ahash_setkey(struct crypto_ahash *ahash, const u8 *key,
+ unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(ahash);
+
+ flow_log("%s() ahash:%p key:%p keylen:%u\n",
+ __func__, ahash, key, keylen);
+ flow_dump(" key: ", key, keylen);
+
+ if (ctx->auth.alg == HASH_ALG_AES) {
+ switch (keylen) {
+ case AES_KEYSIZE_128:
+ ctx->cipher_type = CIPHER_TYPE_AES128;
+ break;
+ case AES_KEYSIZE_192:
+ ctx->cipher_type = CIPHER_TYPE_AES192;
+ break;
+ case AES_KEYSIZE_256:
+ ctx->cipher_type = CIPHER_TYPE_AES256;
+ break;
+ default:
+ pr_err("%s() Error: Invalid key length\n", __func__);
+ return -EINVAL;
+ }
+ } else {
+ pr_err("%s() Error: unknown hash alg\n", __func__);
+ return -EINVAL;
+ }
+ memcpy(ctx->authkey, key, keylen);
+ ctx->authkeylen = keylen;
+
+ return 0;
+}
+
+static int ahash_export(struct ahash_request *req, void *out)
+{
+ const struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+
+ memcpy(out, rctx, offsetof(struct iproc_reqctx_s, msg_buf));
+ return 0;
+}
+
+static int ahash_import(struct ahash_request *req, const void *in)
+{
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+
+ memcpy(rctx, in, offsetof(struct iproc_reqctx_s, msg_buf));
+ return 0;
+}
+
+static int ahash_hmac_setkey(struct crypto_ahash *ahash, const u8 *key,
+ unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(ahash);
+ unsigned int blocksize =
+ crypto_tfm_alg_blocksize(crypto_ahash_tfm(ahash));
+ unsigned int digestsize = crypto_ahash_digestsize(ahash);
+ unsigned int index;
+ int rc;
+
+ flow_log("%s() ahash:%p key:%p keylen:%u blksz:%u digestsz:%u\n",
+ __func__, ahash, key, keylen, blocksize, digestsize);
+ flow_dump(" key: ", key, keylen);
+
+ if (keylen > blocksize) {
+ switch (ctx->auth.alg) {
+ case HASH_ALG_MD5:
+ rc = do_shash("md5", ctx->authkey, key, keylen, NULL,
+ 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA1:
+ rc = do_shash("sha1", ctx->authkey, key, keylen, NULL,
+ 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA224:
+ rc = do_shash("sha224", ctx->authkey, key, keylen, NULL,
+ 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA256:
+ rc = do_shash("sha256", ctx->authkey, key, keylen, NULL,
+ 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA384:
+ rc = do_shash("sha384", ctx->authkey, key, keylen, NULL,
+ 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA512:
+ rc = do_shash("sha512", ctx->authkey, key, keylen, NULL,
+ 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA3_224:
+ rc = do_shash("sha3-224", ctx->authkey, key, keylen,
+ NULL, 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA3_256:
+ rc = do_shash("sha3-256", ctx->authkey, key, keylen,
+ NULL, 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA3_384:
+ rc = do_shash("sha3-384", ctx->authkey, key, keylen,
+ NULL, 0, NULL, 0);
+ break;
+ case HASH_ALG_SHA3_512:
+ rc = do_shash("sha3-512", ctx->authkey, key, keylen,
+ NULL, 0, NULL, 0);
+ break;
+ default:
+ pr_err("%s() Error: unknown hash alg\n", __func__);
+ return -EINVAL;
+ }
+ if (rc < 0) {
+ pr_err("%s() Error %d computing shash for %s\n",
+ __func__, rc, hash_alg_name[ctx->auth.alg]);
+ return rc;
+ }
+ ctx->authkeylen = digestsize;
+
+ flow_log(" keylen > digestsize... hashed\n");
+ flow_dump(" newkey: ", ctx->authkey, ctx->authkeylen);
+ } else {
+ memcpy(ctx->authkey, key, keylen);
+ ctx->authkeylen = keylen;
+ }
+
+ /*
+ * Full HMAC operation in SPUM is not verified,
+ * So keeping the generation of IPAD, OPAD and
+ * outer hashing in software.
+ */
+ if (iproc_priv.spu.spu_type == SPU_TYPE_SPUM) {
+ memcpy(ctx->ipad, ctx->authkey, ctx->authkeylen);
+ memset(ctx->ipad + ctx->authkeylen, 0,
+ blocksize - ctx->authkeylen);
+ ctx->authkeylen = 0;
+ memcpy(ctx->opad, ctx->ipad, blocksize);
+
+ for (index = 0; index < blocksize; index++) {
+ ctx->ipad[index] ^= 0x36;
+ ctx->opad[index] ^= 0x5c;
+ }
+
+ flow_dump(" ipad: ", ctx->ipad, blocksize);
+ flow_dump(" opad: ", ctx->opad, blocksize);
+ }
+ ctx->digestsize = digestsize;
+ atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_HMAC]);
+
+ return 0;
+}
+
+static int ahash_hmac_init(struct ahash_request *req)
+{
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+ unsigned int blocksize =
+ crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
+
+ flow_log("ahash_hmac_init()\n");
+
+ /* init the context as a hash */
+ ahash_init(req);
+
+ if (!spu_no_incr_hash(ctx)) {
+ /* SPU-M can do incr hashing but needs sw for outer HMAC */
+ rctx->is_sw_hmac = true;
+ ctx->auth.mode = HASH_MODE_HASH;
+ /* start with a prepended ipad */
+ memcpy(rctx->hash_carry, ctx->ipad, blocksize);
+ rctx->hash_carry_len = blocksize;
+ rctx->total_todo += blocksize;
+ }
+
+ return 0;
+}
+
+static int ahash_hmac_update(struct ahash_request *req)
+{
+ flow_log("ahash_hmac_update() nbytes:%u\n", req->nbytes);
+
+ if (!req->nbytes)
+ return 0;
+
+ return ahash_update(req);
+}
+
+static int ahash_hmac_final(struct ahash_request *req)
+{
+ flow_log("ahash_hmac_final() nbytes:%u\n", req->nbytes);
+
+ return ahash_final(req);
+}
+
+static int ahash_hmac_finup(struct ahash_request *req)
+{
+ flow_log("ahash_hmac_finupl() nbytes:%u\n", req->nbytes);
+
+ return ahash_finup(req);
+}
+
+static int ahash_hmac_digest(struct ahash_request *req)
+{
+ struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
+ unsigned int blocksize =
+ crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
+
+ flow_log("ahash_hmac_digest() nbytes:%u\n", req->nbytes);
+
+ /* Perform initialization and then call finup */
+ __ahash_init(req);
+
+ if (iproc_priv.spu.spu_type == SPU_TYPE_SPU2) {
+ /*
+ * SPU2 supports full HMAC implementation in the
+ * hardware, need not to generate IPAD, OPAD and
+ * outer hash in software.
+ * Only for hash key len > hash block size, SPU2
+ * expects to perform hashing on the key, shorten
+ * it to digest size and feed it as hash key.
+ */
+ rctx->is_sw_hmac = false;
+ ctx->auth.mode = HASH_MODE_HMAC;
+ } else {
+ rctx->is_sw_hmac = true;
+ ctx->auth.mode = HASH_MODE_HASH;
+ /* start with a prepended ipad */
+ memcpy(rctx->hash_carry, ctx->ipad, blocksize);
+ rctx->hash_carry_len = blocksize;
+ rctx->total_todo += blocksize;
+ }
+
+ return __ahash_finup(req);
+}
+
+/* aead helpers */
+
+static int aead_need_fallback(struct aead_request *req)
+{
+ struct iproc_reqctx_s *rctx = aead_request_ctx(req);
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(aead);
+ u32 payload_len;
+
+ /*
+ * SPU hardware cannot handle the AES-GCM/CCM case where plaintext
+ * and AAD are both 0 bytes long. So use fallback in this case.
+ */
+ if (((ctx->cipher.mode == CIPHER_MODE_GCM) ||
+ (ctx->cipher.mode == CIPHER_MODE_CCM)) &&
+ (req->assoclen == 0)) {
+ if ((rctx->is_encrypt && (req->cryptlen == 0)) ||
+ (!rctx->is_encrypt && (req->cryptlen == ctx->digestsize))) {
+ flow_log("AES GCM/CCM needs fallback for 0 len req\n");
+ return 1;
+ }
+ }
+
+ /* SPU-M hardware only supports CCM digest size of 8, 12, or 16 bytes */
+ if ((ctx->cipher.mode == CIPHER_MODE_CCM) &&
+ (spu->spu_type == SPU_TYPE_SPUM) &&
+ (ctx->digestsize != 8) && (ctx->digestsize != 12) &&
+ (ctx->digestsize != 16)) {
+ flow_log("%s() AES CCM needs fallbck for digest size %d\n",
+ __func__, ctx->digestsize);
+ return 1;
+ }
+
+ /*
+ * SPU-M on NSP has an issue where AES-CCM hash is not correct
+ * when AAD size is 0
+ */
+ if ((ctx->cipher.mode == CIPHER_MODE_CCM) &&
+ (spu->spu_subtype == SPU_SUBTYPE_SPUM_NSP) &&
+ (req->assoclen == 0)) {
+ flow_log("%s() AES_CCM needs fallback for 0 len AAD on NSP\n",
+ __func__);
+ return 1;
+ }
+
+ payload_len = req->cryptlen;
+ if (spu->spu_type == SPU_TYPE_SPUM)
+ payload_len += req->assoclen;
+
+ flow_log("%s() payload len: %u\n", __func__, payload_len);
+
+ if (ctx->max_payload == SPU_MAX_PAYLOAD_INF)
+ return 0;
+ else
+ return payload_len > ctx->max_payload;
+}
+
+static void aead_complete(struct crypto_async_request *areq, int err)
+{
+ struct aead_request *req =
+ container_of(areq, struct aead_request, base);
+ struct iproc_reqctx_s *rctx = aead_request_ctx(req);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+
+ flow_log("%s() err:%d\n", __func__, err);
+
+ areq->tfm = crypto_aead_tfm(aead);
+
+ areq->complete = rctx->old_complete;
+ areq->data = rctx->old_data;
+
+ areq->complete(areq, err);
+}
+
+static int aead_do_fallback(struct aead_request *req, bool is_encrypt)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct crypto_tfm *tfm = crypto_aead_tfm(aead);
+ struct iproc_reqctx_s *rctx = aead_request_ctx(req);
+ struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm);
+ int err;
+ u32 req_flags;
+
+ flow_log("%s() enc:%u\n", __func__, is_encrypt);
+
+ if (ctx->fallback_cipher) {
+ /* Store the cipher tfm and then use the fallback tfm */
+ rctx->old_tfm = tfm;
+ aead_request_set_tfm(req, ctx->fallback_cipher);
+ /*
+ * Save the callback and chain ourselves in, so we can restore
+ * the tfm
+ */
+ rctx->old_complete = req->base.complete;
+ rctx->old_data = req->base.data;
+ req_flags = aead_request_flags(req);
+ aead_request_set_callback(req, req_flags, aead_complete, req);
+ err = is_encrypt ? crypto_aead_encrypt(req) :
+ crypto_aead_decrypt(req);
+
+ if (err == 0) {
+ /*
+ * fallback was synchronous (did not return
+ * -EINPROGRESS). So restore request state here.
+ */
+ aead_request_set_callback(req, req_flags,
+ rctx->old_complete, req);
+ req->base.data = rctx->old_data;
+ aead_request_set_tfm(req, aead);
+ flow_log("%s() fallback completed successfully\n\n",
+ __func__);
+ }
+ } else {
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static int aead_enqueue(struct aead_request *req, bool is_encrypt)
+{
+ struct iproc_reqctx_s *rctx = aead_request_ctx(req);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(aead);
+ int err;
+
+ flow_log("%s() enc:%u\n", __func__, is_encrypt);
+
+ if (req->assoclen > MAX_ASSOC_SIZE) {
+ pr_err
+ ("%s() Error: associated data too long. (%u > %u bytes)\n",
+ __func__, req->assoclen, MAX_ASSOC_SIZE);
+ return -EINVAL;
+ }
+
+ rctx->gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ rctx->parent = &req->base;
+ rctx->is_encrypt = is_encrypt;
+ rctx->bd_suppress = false;
+ rctx->total_todo = req->cryptlen;
+ rctx->src_sent = 0;
+ rctx->total_sent = 0;
+ rctx->total_received = 0;
+ rctx->is_sw_hmac = false;
+ rctx->ctx = ctx;
+ memset(&rctx->mb_mssg, 0, sizeof(struct brcm_message));
+
+ /* assoc data is at start of src sg */
+ rctx->assoc = req->src;
+
+ /*
+ * Init current position in src scatterlist to be after assoc data.
+ * src_skip set to buffer offset where data begins. (Assoc data could
+ * end in the middle of a buffer.)
+ */
+ if (spu_sg_at_offset(req->src, req->assoclen, &rctx->src_sg,
+ &rctx->src_skip) < 0) {
+ pr_err("%s() Error: Unable to find start of src data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rctx->src_nents = 0;
+ rctx->dst_nents = 0;
+ if (req->dst == req->src) {
+ rctx->dst_sg = rctx->src_sg;
+ rctx->dst_skip = rctx->src_skip;
+ } else {
+ /*
+ * Expect req->dst to have room for assoc data followed by
+ * output data and ICV, if encrypt. So initialize dst_sg
+ * to point beyond assoc len offset.
+ */
+ if (spu_sg_at_offset(req->dst, req->assoclen, &rctx->dst_sg,
+ &rctx->dst_skip) < 0) {
+ pr_err("%s() Error: Unable to find start of dst data\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ if (ctx->cipher.mode == CIPHER_MODE_CBC ||
+ ctx->cipher.mode == CIPHER_MODE_CTR ||
+ ctx->cipher.mode == CIPHER_MODE_OFB ||
+ ctx->cipher.mode == CIPHER_MODE_XTS ||
+ ctx->cipher.mode == CIPHER_MODE_GCM) {
+ rctx->iv_ctr_len =
+ ctx->salt_len +
+ crypto_aead_ivsize(crypto_aead_reqtfm(req));
+ } else if (ctx->cipher.mode == CIPHER_MODE_CCM) {
+ rctx->iv_ctr_len = CCM_AES_IV_SIZE;
+ } else {
+ rctx->iv_ctr_len = 0;
+ }
+
+ rctx->hash_carry_len = 0;
+
+ flow_log(" src sg: %p\n", req->src);
+ flow_log(" rctx->src_sg: %p, src_skip %u\n",
+ rctx->src_sg, rctx->src_skip);
+ flow_log(" assoc: %p, assoclen %u\n", rctx->assoc, req->assoclen);
+ flow_log(" dst sg: %p\n", req->dst);
+ flow_log(" rctx->dst_sg: %p, dst_skip %u\n",
+ rctx->dst_sg, rctx->dst_skip);
+ flow_log(" iv_ctr_len:%u\n", rctx->iv_ctr_len);
+ flow_dump(" iv: ", req->iv, rctx->iv_ctr_len);
+ flow_log(" authkeylen:%u\n", ctx->authkeylen);
+ flow_log(" is_esp: %s\n", ctx->is_esp ? "yes" : "no");
+
+ if (ctx->max_payload == SPU_MAX_PAYLOAD_INF)
+ flow_log(" max_payload infinite");
+ else
+ flow_log(" max_payload: %u\n", ctx->max_payload);
+
+ if (unlikely(aead_need_fallback(req)))
+ return aead_do_fallback(req, is_encrypt);
+
+ /*
+ * Do memory allocations for request after fallback check, because if we
+ * do fallback, we won't call finish_req() to dealloc.
+ */
+ if (rctx->iv_ctr_len) {
+ if (ctx->salt_len)
+ memcpy(rctx->msg_buf.iv_ctr + ctx->salt_offset,
+ ctx->salt, ctx->salt_len);
+ memcpy(rctx->msg_buf.iv_ctr + ctx->salt_offset + ctx->salt_len,
+ req->iv,
+ rctx->iv_ctr_len - ctx->salt_len - ctx->salt_offset);
+ }
+
+ rctx->chan_idx = select_channel();
+ err = handle_aead_req(rctx);
+ if (err != -EINPROGRESS)
+ /* synchronous result */
+ spu_chunk_cleanup(rctx);
+
+ return err;
+}
+
+static int aead_authenc_setkey(struct crypto_aead *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher);
+ struct crypto_tfm *tfm = crypto_aead_tfm(cipher);
+ struct rtattr *rta = (void *)key;
+ struct crypto_authenc_key_param *param;
+ const u8 *origkey = key;
+ const unsigned int origkeylen = keylen;
+
+ int ret = 0;
+
+ flow_log("%s() aead:%p key:%p keylen:%u\n", __func__, cipher, key,
+ keylen);
+ flow_dump(" key: ", key, keylen);
+
+ 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;
+
+ param = RTA_DATA(rta);
+ ctx->enckeylen = be32_to_cpu(param->enckeylen);
+
+ key += RTA_ALIGN(rta->rta_len);
+ keylen -= RTA_ALIGN(rta->rta_len);
+
+ if (keylen < ctx->enckeylen)
+ goto badkey;
+ if (ctx->enckeylen > MAX_KEY_SIZE)
+ goto badkey;
+
+ ctx->authkeylen = keylen - ctx->enckeylen;
+
+ if (ctx->authkeylen > MAX_KEY_SIZE)
+ goto badkey;
+
+ memcpy(ctx->enckey, key + ctx->authkeylen, ctx->enckeylen);
+ /* May end up padding auth key. So make sure it's zeroed. */
+ memset(ctx->authkey, 0, sizeof(ctx->authkey));
+ memcpy(ctx->authkey, key, ctx->authkeylen);
+
+ switch (ctx->alg->cipher_info.alg) {
+ case CIPHER_ALG_DES:
+ if (ctx->enckeylen == DES_KEY_SIZE) {
+ u32 tmp[DES_EXPKEY_WORDS];
+ u32 flags = CRYPTO_TFM_RES_WEAK_KEY;
+
+ if (des_ekey(tmp, key) == 0) {
+ if (crypto_aead_get_flags(cipher) &
+ CRYPTO_TFM_REQ_WEAK_KEY) {
+ crypto_aead_set_flags(cipher, flags);
+ return -EINVAL;
+ }
+ }
+
+ ctx->cipher_type = CIPHER_TYPE_DES;
+ } else {
+ goto badkey;
+ }
+ break;
+ case CIPHER_ALG_3DES:
+ if (ctx->enckeylen == (DES_KEY_SIZE * 3)) {
+ const u32 *K = (const u32 *)key;
+ u32 flags = CRYPTO_TFM_RES_BAD_KEY_SCHED;
+
+ if (!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
+ !((K[2] ^ K[4]) | (K[3] ^ K[5]))) {
+ crypto_aead_set_flags(cipher, flags);
+ return -EINVAL;
+ }
+
+ ctx->cipher_type = CIPHER_TYPE_3DES;
+ } else {
+ crypto_aead_set_flags(cipher,
+ CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+ break;
+ case CIPHER_ALG_AES:
+ switch (ctx->enckeylen) {
+ case AES_KEYSIZE_128:
+ ctx->cipher_type = CIPHER_TYPE_AES128;
+ break;
+ case AES_KEYSIZE_192:
+ ctx->cipher_type = CIPHER_TYPE_AES192;
+ break;
+ case AES_KEYSIZE_256:
+ ctx->cipher_type = CIPHER_TYPE_AES256;
+ break;
+ default:
+ goto badkey;
+ }
+ break;
+ case CIPHER_ALG_RC4:
+ ctx->cipher_type = CIPHER_TYPE_INIT;
+ break;
+ default:
+ pr_err("%s() Error: Unknown cipher alg\n", __func__);
+ return -EINVAL;
+ }
+
+ flow_log(" enckeylen:%u authkeylen:%u\n", ctx->enckeylen,
+ ctx->authkeylen);
+ flow_dump(" enc: ", ctx->enckey, ctx->enckeylen);
+ flow_dump(" auth: ", ctx->authkey, ctx->authkeylen);
+
+ /* setkey the fallback just in case we needto use it */
+ if (ctx->fallback_cipher) {
+ flow_log(" running fallback setkey()\n");
+
+ ctx->fallback_cipher->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ ctx->fallback_cipher->base.crt_flags |=
+ tfm->crt_flags & CRYPTO_TFM_REQ_MASK;
+ ret =
+ crypto_aead_setkey(ctx->fallback_cipher, origkey,
+ origkeylen);
+ if (ret) {
+ flow_log(" fallback setkey() returned:%d\n", ret);
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |=
+ (ctx->fallback_cipher->base.crt_flags &
+ CRYPTO_TFM_RES_MASK);
+ }
+ }
+
+ ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen,
+ ctx->enckeylen,
+ false);
+
+ atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_AEAD]);
+
+ return ret;
+
+badkey:
+ ctx->enckeylen = 0;
+ ctx->authkeylen = 0;
+ ctx->digestsize = 0;
+
+ crypto_aead_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+}
+
+static int aead_gcm_ccm_setkey(struct crypto_aead *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher);
+ struct crypto_tfm *tfm = crypto_aead_tfm(cipher);
+
+ int ret = 0;
+
+ flow_log("%s() keylen:%u\n", __func__, keylen);
+ flow_dump(" key: ", key, keylen);
+
+ if (!ctx->is_esp)
+ ctx->digestsize = keylen;
+
+ ctx->enckeylen = keylen;
+ ctx->authkeylen = 0;
+ memcpy(ctx->enckey, key, ctx->enckeylen);
+
+ switch (ctx->enckeylen) {
+ case AES_KEYSIZE_128:
+ ctx->cipher_type = CIPHER_TYPE_AES128;
+ break;
+ case AES_KEYSIZE_192:
+ ctx->cipher_type = CIPHER_TYPE_AES192;
+ break;
+ case AES_KEYSIZE_256:
+ ctx->cipher_type = CIPHER_TYPE_AES256;
+ break;
+ default:
+ goto badkey;
+ }
+
+ flow_log(" enckeylen:%u authkeylen:%u\n", ctx->enckeylen,
+ ctx->authkeylen);
+ flow_dump(" enc: ", ctx->enckey, ctx->enckeylen);
+ flow_dump(" auth: ", ctx->authkey, ctx->authkeylen);
+
+ /* setkey the fallback just in case we need to use it */
+ if (ctx->fallback_cipher) {
+ flow_log(" running fallback setkey()\n");
+
+ ctx->fallback_cipher->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ ctx->fallback_cipher->base.crt_flags |=
+ tfm->crt_flags & CRYPTO_TFM_REQ_MASK;
+ ret = crypto_aead_setkey(ctx->fallback_cipher, key,
+ keylen + ctx->salt_len);
+ if (ret) {
+ flow_log(" fallback setkey() returned:%d\n", ret);
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |=
+ (ctx->fallback_cipher->base.crt_flags &
+ CRYPTO_TFM_RES_MASK);
+ }
+ }
+
+ ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen,
+ ctx->enckeylen,
+ false);
+
+ atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_AEAD]);
+
+ flow_log(" enckeylen:%u authkeylen:%u\n", ctx->enckeylen,
+ ctx->authkeylen);
+
+ return ret;
+
+badkey:
+ ctx->enckeylen = 0;
+ ctx->authkeylen = 0;
+ ctx->digestsize = 0;
+
+ crypto_aead_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+}
+
+/**
+ * aead_gcm_esp_setkey() - setkey() operation for ESP variant of GCM AES.
+ * @cipher: AEAD structure
+ * @key: Key followed by 4 bytes of salt
+ * @keylen: Length of key plus salt, in bytes
+ *
+ * Extracts salt from key and stores it to be prepended to IV on each request.
+ * Digest is always 16 bytes
+ *
+ * Return: Value from generic gcm setkey.
+ */
+static int aead_gcm_esp_setkey(struct crypto_aead *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher);
+
+ flow_log("%s\n", __func__);
+ ctx->salt_len = GCM_ESP_SALT_SIZE;
+ ctx->salt_offset = GCM_ESP_SALT_OFFSET;
+ memcpy(ctx->salt, key + keylen - GCM_ESP_SALT_SIZE, GCM_ESP_SALT_SIZE);
+ keylen -= GCM_ESP_SALT_SIZE;
+ ctx->digestsize = GCM_ESP_DIGESTSIZE;
+ ctx->is_esp = true;
+ flow_dump("salt: ", ctx->salt, GCM_ESP_SALT_SIZE);
+
+ return aead_gcm_ccm_setkey(cipher, key, keylen);
+}
+
+/**
+ * rfc4543_gcm_esp_setkey() - setkey operation for RFC4543 variant of GCM/GMAC.
+ * cipher: AEAD structure
+ * key: Key followed by 4 bytes of salt
+ * keylen: Length of key plus salt, in bytes
+ *
+ * Extracts salt from key and stores it to be prepended to IV on each request.
+ * Digest is always 16 bytes
+ *
+ * Return: Value from generic gcm setkey.
+ */
+static int rfc4543_gcm_esp_setkey(struct crypto_aead *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher);
+
+ flow_log("%s\n", __func__);
+ ctx->salt_len = GCM_ESP_SALT_SIZE;
+ ctx->salt_offset = GCM_ESP_SALT_OFFSET;
+ memcpy(ctx->salt, key + keylen - GCM_ESP_SALT_SIZE, GCM_ESP_SALT_SIZE);
+ keylen -= GCM_ESP_SALT_SIZE;
+ ctx->digestsize = GCM_ESP_DIGESTSIZE;
+ ctx->is_esp = true;
+ ctx->is_rfc4543 = true;
+ flow_dump("salt: ", ctx->salt, GCM_ESP_SALT_SIZE);
+
+ return aead_gcm_ccm_setkey(cipher, key, keylen);
+}
+
+/**
+ * aead_ccm_esp_setkey() - setkey() operation for ESP variant of CCM AES.
+ * @cipher: AEAD structure
+ * @key: Key followed by 4 bytes of salt
+ * @keylen: Length of key plus salt, in bytes
+ *
+ * Extracts salt from key and stores it to be prepended to IV on each request.
+ * Digest is always 16 bytes
+ *
+ * Return: Value from generic ccm setkey.
+ */
+static int aead_ccm_esp_setkey(struct crypto_aead *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher);
+
+ flow_log("%s\n", __func__);
+ ctx->salt_len = CCM_ESP_SALT_SIZE;
+ ctx->salt_offset = CCM_ESP_SALT_OFFSET;
+ memcpy(ctx->salt, key + keylen - CCM_ESP_SALT_SIZE, CCM_ESP_SALT_SIZE);
+ keylen -= CCM_ESP_SALT_SIZE;
+ ctx->is_esp = true;
+ flow_dump("salt: ", ctx->salt, CCM_ESP_SALT_SIZE);
+
+ return aead_gcm_ccm_setkey(cipher, key, keylen);
+}
+
+static int aead_setauthsize(struct crypto_aead *cipher, unsigned int authsize)
+{
+ struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher);
+ int ret = 0;
+
+ flow_log("%s() authkeylen:%u authsize:%u\n",
+ __func__, ctx->authkeylen, authsize);
+
+ ctx->digestsize = authsize;
+
+ /* setkey the fallback just in case we needto use it */
+ if (ctx->fallback_cipher) {
+ flow_log(" running fallback setauth()\n");
+
+ ret = crypto_aead_setauthsize(ctx->fallback_cipher, authsize);
+ if (ret)
+ flow_log(" fallback setauth() returned:%d\n", ret);
+ }
+
+ return ret;
+}
+
+static int aead_encrypt(struct aead_request *req)
+{
+ flow_log("%s() cryptlen:%u %08x\n", __func__, req->cryptlen,
+ req->cryptlen);
+ dump_sg(req->src, 0, req->cryptlen + req->assoclen);
+ flow_log(" assoc_len:%u\n", req->assoclen);
+
+ return aead_enqueue(req, true);
+}
+
+static int aead_decrypt(struct aead_request *req)
+{
+ flow_log("%s() cryptlen:%u\n", __func__, req->cryptlen);
+ dump_sg(req->src, 0, req->cryptlen + req->assoclen);
+ flow_log(" assoc_len:%u\n", req->assoclen);
+
+ return aead_enqueue(req, false);
+}
+
+/* ==================== Supported Cipher Algorithms ==================== */
+
+static struct iproc_alg_s driver_algs[] = {
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "gcm(aes)",
+ .cra_driver_name = "gcm-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK
+ },
+ .setkey = aead_gcm_ccm_setkey,
+ .ivsize = GCM_AES_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_GCM,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_AES,
+ .mode = HASH_MODE_GCM,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "ccm(aes)",
+ .cra_driver_name = "ccm-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK
+ },
+ .setkey = aead_gcm_ccm_setkey,
+ .ivsize = CCM_AES_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_CCM,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_AES,
+ .mode = HASH_MODE_CCM,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "rfc4106(gcm(aes))",
+ .cra_driver_name = "gcm-aes-esp-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK
+ },
+ .setkey = aead_gcm_esp_setkey,
+ .ivsize = GCM_ESP_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_GCM,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_AES,
+ .mode = HASH_MODE_GCM,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "rfc4309(ccm(aes))",
+ .cra_driver_name = "ccm-aes-esp-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK
+ },
+ .setkey = aead_ccm_esp_setkey,
+ .ivsize = CCM_AES_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_CCM,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_AES,
+ .mode = HASH_MODE_CCM,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "rfc4543(gcm(aes))",
+ .cra_driver_name = "gmac-aes-esp-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK
+ },
+ .setkey = rfc4543_gcm_esp_setkey,
+ .ivsize = GCM_ESP_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_GCM,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_AES,
+ .mode = HASH_MODE_GCM,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(md5),cbc(aes))",
+ .cra_driver_name = "authenc-hmac-md5-cbc-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = MD5_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_MD5,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha1),cbc(aes))",
+ .cra_driver_name = "authenc-hmac-sha1-cbc-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA1,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha256),cbc(aes))",
+ .cra_driver_name = "authenc-hmac-sha256-cbc-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA256,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(md5),cbc(des))",
+ .cra_driver_name = "authenc-hmac-md5-cbc-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES_BLOCK_SIZE,
+ .maxauthsize = MD5_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_MD5,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha1),cbc(des))",
+ .cra_driver_name = "authenc-hmac-sha1-cbc-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA1,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha224),cbc(des))",
+ .cra_driver_name = "authenc-hmac-sha224-cbc-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES_BLOCK_SIZE,
+ .maxauthsize = SHA224_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA224,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha256),cbc(des))",
+ .cra_driver_name = "authenc-hmac-sha256-cbc-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES_BLOCK_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA256,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha384),cbc(des))",
+ .cra_driver_name = "authenc-hmac-sha384-cbc-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES_BLOCK_SIZE,
+ .maxauthsize = SHA384_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA384,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha512),cbc(des))",
+ .cra_driver_name = "authenc-hmac-sha512-cbc-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES_BLOCK_SIZE,
+ .maxauthsize = SHA512_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA512,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(md5),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-md5-cbc-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = MD5_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_MD5,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha1),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-sha1-cbc-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA1,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha224),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-sha224-cbc-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA224_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA224,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha256),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-sha256-cbc-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA256,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha384),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-sha384-cbc-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA384_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA384,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .alg.aead = {
+ .base = {
+ .cra_name = "authenc(hmac(sha512),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-sha512-cbc-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC
+ },
+ .setkey = aead_authenc_setkey,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA512_DIGEST_SIZE,
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA512,
+ .mode = HASH_MODE_HMAC,
+ },
+ .auth_first = 0,
+ },
+
+/* ABLKCIPHER algorithms. */
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ecb(arc4)",
+ .cra_driver_name = "ecb-arc4-iproc",
+ .cra_blocksize = ARC4_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = ARC4_MIN_KEY_SIZE,
+ .max_keysize = ARC4_MAX_KEY_SIZE,
+ .ivsize = 0,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_RC4,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ofb(des)",
+ .cra_driver_name = "ofb-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_OFB,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "cbc(des)",
+ .cra_driver_name = "cbc-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ecb(des)",
+ .cra_driver_name = "ecb-des-iproc",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = 0,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_DES,
+ .mode = CIPHER_MODE_ECB,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ofb(des3_ede)",
+ .cra_driver_name = "ofb-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_OFB,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "cbc(des3_ede)",
+ .cra_driver_name = "cbc-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ecb(des3_ede)",
+ .cra_driver_name = "ecb-des3-iproc",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = 0,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_3DES,
+ .mode = CIPHER_MODE_ECB,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ofb(aes)",
+ .cra_driver_name = "ofb-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_OFB,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_CBC,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "ecb-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = 0,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_ECB,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ctr(aes)",
+ .cra_driver_name = "ctr-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ /* .geniv = "chainiv", */
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_CTR,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+{
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "xts(aes)",
+ .cra_driver_name = "xts-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ablkcipher = {
+ .min_keysize = 2 * AES_MIN_KEY_SIZE,
+ .max_keysize = 2 * AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_AES,
+ .mode = CIPHER_MODE_XTS,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_NONE,
+ .mode = HASH_MODE_NONE,
+ },
+ },
+
+/* AHASH algorithms. */
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = MD5_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "md5",
+ .cra_driver_name = "md5-iproc",
+ .cra_blocksize = MD5_BLOCK_WORDS * 4,
+ .cra_flags = CRYPTO_ALG_TYPE_AHASH |
+ CRYPTO_ALG_ASYNC,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_MD5,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = MD5_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(md5)",
+ .cra_driver_name = "hmac-md5-iproc",
+ .cra_blocksize = MD5_BLOCK_WORDS * 4,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_MD5,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {.type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA1_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha1",
+ .cra_driver_name = "sha1-iproc",
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA1,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {.type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA1_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha1)",
+ .cra_driver_name = "hmac-sha1-iproc",
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA1,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {.type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA224_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha224",
+ .cra_driver_name = "sha224-iproc",
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA224,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {.type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA224_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha224)",
+ .cra_driver_name = "hmac-sha224-iproc",
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA224,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {.type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA256_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha256",
+ .cra_driver_name = "sha256-iproc",
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA256,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {.type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA256_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha256)",
+ .cra_driver_name = "hmac-sha256-iproc",
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA256,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA384_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha384",
+ .cra_driver_name = "sha384-iproc",
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA384,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA384_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha384)",
+ .cra_driver_name = "hmac-sha384-iproc",
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA384,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA512_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha512",
+ .cra_driver_name = "sha512-iproc",
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA512,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA512_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha512)",
+ .cra_driver_name = "hmac-sha512-iproc",
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA512,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_224_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha3-224",
+ .cra_driver_name = "sha3-224-iproc",
+ .cra_blocksize = SHA3_224_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_224,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_224_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha3-224)",
+ .cra_driver_name = "hmac-sha3-224-iproc",
+ .cra_blocksize = SHA3_224_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_224,
+ .mode = HASH_MODE_HMAC
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_256_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha3-256",
+ .cra_driver_name = "sha3-256-iproc",
+ .cra_blocksize = SHA3_256_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_256,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_256_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha3-256)",
+ .cra_driver_name = "hmac-sha3-256-iproc",
+ .cra_blocksize = SHA3_256_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_256,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_384_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha3-384",
+ .cra_driver_name = "sha3-384-iproc",
+ .cra_blocksize = SHA3_224_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_384,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_384_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha3-384)",
+ .cra_driver_name = "hmac-sha3-384-iproc",
+ .cra_blocksize = SHA3_384_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_384,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_512_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha3-512",
+ .cra_driver_name = "sha3-512-iproc",
+ .cra_blocksize = SHA3_512_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_512,
+ .mode = HASH_MODE_HASH,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = SHA3_512_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "hmac(sha3-512)",
+ .cra_driver_name = "hmac-sha3-512-iproc",
+ .cra_blocksize = SHA3_512_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_SHA3_512,
+ .mode = HASH_MODE_HMAC,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = AES_BLOCK_SIZE,
+ .halg.base = {
+ .cra_name = "xcbc(aes)",
+ .cra_driver_name = "xcbc-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_AES,
+ .mode = HASH_MODE_XCBC,
+ },
+ },
+ {
+ .type = CRYPTO_ALG_TYPE_AHASH,
+ .alg.hash = {
+ .halg.digestsize = AES_BLOCK_SIZE,
+ .halg.base = {
+ .cra_name = "cmac(aes)",
+ .cra_driver_name = "cmac-aes-iproc",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ }
+ },
+ .cipher_info = {
+ .alg = CIPHER_ALG_NONE,
+ .mode = CIPHER_MODE_NONE,
+ },
+ .auth_info = {
+ .alg = HASH_ALG_AES,
+ .mode = HASH_MODE_CMAC,
+ },
+ },
+};
+
+static int generic_cra_init(struct crypto_tfm *tfm,
+ struct iproc_alg_s *cipher_alg)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm);
+ unsigned int blocksize = crypto_tfm_alg_blocksize(tfm);
+
+ flow_log("%s()\n", __func__);
+
+ ctx->alg = cipher_alg;
+ ctx->cipher = cipher_alg->cipher_info;
+ ctx->auth = cipher_alg->auth_info;
+ ctx->auth_first = cipher_alg->auth_first;
+ ctx->max_payload = spu->spu_ctx_max_payload(ctx->cipher.alg,
+ ctx->cipher.mode,
+ blocksize);
+ ctx->fallback_cipher = NULL;
+
+ ctx->enckeylen = 0;
+ ctx->authkeylen = 0;
+
+ atomic_inc(&iproc_priv.stream_count);
+ atomic_inc(&iproc_priv.session_count);
+
+ return 0;
+}
+
+static int ablkcipher_cra_init(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct iproc_alg_s *cipher_alg;
+
+ flow_log("%s()\n", __func__);
+
+ tfm->crt_ablkcipher.reqsize = sizeof(struct iproc_reqctx_s);
+
+ cipher_alg = container_of(alg, struct iproc_alg_s, alg.crypto);
+ return generic_cra_init(tfm, cipher_alg);
+}
+
+static int ahash_cra_init(struct crypto_tfm *tfm)
+{
+ int err;
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct iproc_alg_s *cipher_alg;
+
+ cipher_alg = container_of(__crypto_ahash_alg(alg), struct iproc_alg_s,
+ alg.hash);
+
+ err = generic_cra_init(tfm, cipher_alg);
+ flow_log("%s()\n", __func__);
+
+ /*
+ * export state size has to be < 512 bytes. So don't include msg bufs
+ * in state size.
+ */
+ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+ sizeof(struct iproc_reqctx_s));
+
+ return err;
+}
+
+static int aead_cra_init(struct crypto_aead *aead)
+{
+ struct crypto_tfm *tfm = crypto_aead_tfm(aead);
+ struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct aead_alg *aalg = container_of(alg, struct aead_alg, base);
+ struct iproc_alg_s *cipher_alg = container_of(aalg, struct iproc_alg_s,
+ alg.aead);
+
+ int err = generic_cra_init(tfm, cipher_alg);
+
+ flow_log("%s()\n", __func__);
+
+ crypto_aead_set_reqsize(aead, sizeof(struct iproc_reqctx_s));
+ ctx->is_esp = false;
+ ctx->salt_len = 0;
+ ctx->salt_offset = 0;
+
+ /* random first IV */
+ get_random_bytes(ctx->iv, MAX_IV_SIZE);
+ flow_dump(" iv: ", ctx->iv, MAX_IV_SIZE);
+
+ if (!err) {
+ if (alg->cra_flags & CRYPTO_ALG_NEED_FALLBACK) {
+ flow_log("%s() creating fallback cipher\n", __func__);
+
+ ctx->fallback_cipher =
+ crypto_alloc_aead(alg->cra_name, 0,
+ CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(ctx->fallback_cipher)) {
+ pr_err("%s() Error: failed to allocate fallback for %s\n",
+ __func__, alg->cra_name);
+ return PTR_ERR(ctx->fallback_cipher);
+ }
+ }
+ }
+
+ return err;
+}
+
+static void generic_cra_exit(struct crypto_tfm *tfm)
+{
+ atomic_dec(&iproc_priv.session_count);
+}
+
+static void aead_cra_exit(struct crypto_aead *aead)
+{
+ struct crypto_tfm *tfm = crypto_aead_tfm(aead);
+ struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm);
+
+ generic_cra_exit(tfm);
+
+ if (ctx->fallback_cipher) {
+ crypto_free_aead(ctx->fallback_cipher);
+ ctx->fallback_cipher = NULL;
+ }
+}
+
+/**
+ * spu_functions_register() - Specify hardware-specific SPU functions based on
+ * SPU type read from device tree.
+ * @dev: device structure
+ * @spu_type: SPU hardware generation
+ * @spu_subtype: SPU hardware version
+ */
+static void spu_functions_register(struct device *dev,
+ enum spu_spu_type spu_type,
+ enum spu_spu_subtype spu_subtype)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+
+ if (spu_type == SPU_TYPE_SPUM) {
+ dev_dbg(dev, "Registering SPUM functions");
+ spu->spu_dump_msg_hdr = spum_dump_msg_hdr;
+ spu->spu_payload_length = spum_payload_length;
+ spu->spu_response_hdr_len = spum_response_hdr_len;
+ spu->spu_hash_pad_len = spum_hash_pad_len;
+ spu->spu_gcm_ccm_pad_len = spum_gcm_ccm_pad_len;
+ spu->spu_assoc_resp_len = spum_assoc_resp_len;
+ spu->spu_aead_ivlen = spum_aead_ivlen;
+ spu->spu_hash_type = spum_hash_type;
+ spu->spu_digest_size = spum_digest_size;
+ spu->spu_create_request = spum_create_request;
+ spu->spu_cipher_req_init = spum_cipher_req_init;
+ spu->spu_cipher_req_finish = spum_cipher_req_finish;
+ spu->spu_request_pad = spum_request_pad;
+ spu->spu_tx_status_len = spum_tx_status_len;
+ spu->spu_rx_status_len = spum_rx_status_len;
+ spu->spu_status_process = spum_status_process;
+ spu->spu_xts_tweak_in_payload = spum_xts_tweak_in_payload;
+ spu->spu_ccm_update_iv = spum_ccm_update_iv;
+ spu->spu_wordalign_padlen = spum_wordalign_padlen;
+ if (spu_subtype == SPU_SUBTYPE_SPUM_NS2)
+ spu->spu_ctx_max_payload = spum_ns2_ctx_max_payload;
+ else
+ spu->spu_ctx_max_payload = spum_nsp_ctx_max_payload;
+ } else {
+ dev_dbg(dev, "Registering SPU2 functions");
+ spu->spu_dump_msg_hdr = spu2_dump_msg_hdr;
+ spu->spu_ctx_max_payload = spu2_ctx_max_payload;
+ spu->spu_payload_length = spu2_payload_length;
+ spu->spu_response_hdr_len = spu2_response_hdr_len;
+ spu->spu_hash_pad_len = spu2_hash_pad_len;
+ spu->spu_gcm_ccm_pad_len = spu2_gcm_ccm_pad_len;
+ spu->spu_assoc_resp_len = spu2_assoc_resp_len;
+ spu->spu_aead_ivlen = spu2_aead_ivlen;
+ spu->spu_hash_type = spu2_hash_type;
+ spu->spu_digest_size = spu2_digest_size;
+ spu->spu_create_request = spu2_create_request;
+ spu->spu_cipher_req_init = spu2_cipher_req_init;
+ spu->spu_cipher_req_finish = spu2_cipher_req_finish;
+ spu->spu_request_pad = spu2_request_pad;
+ spu->spu_tx_status_len = spu2_tx_status_len;
+ spu->spu_rx_status_len = spu2_rx_status_len;
+ spu->spu_status_process = spu2_status_process;
+ spu->spu_xts_tweak_in_payload = spu2_xts_tweak_in_payload;
+ spu->spu_ccm_update_iv = spu2_ccm_update_iv;
+ spu->spu_wordalign_padlen = spu2_wordalign_padlen;
+ }
+}
+
+/**
+ * spu_mb_init() - Initialize mailbox client. Request ownership of a mailbox
+ * channel for the SPU being probed.
+ * @dev: SPU driver device structure
+ *
+ * Return: 0 if successful
+ * < 0 otherwise
+ */
+static int spu_mb_init(struct device *dev)
+{
+ struct mbox_client *mcl = &iproc_priv.mcl[iproc_priv.spu.num_spu];
+ int err;
+
+ mcl->dev = dev;
+ mcl->tx_block = false;
+ mcl->tx_tout = 0;
+ mcl->knows_txdone = false;
+ mcl->rx_callback = spu_rx_callback;
+ mcl->tx_done = NULL;
+
+ iproc_priv.mbox[iproc_priv.spu.num_spu] =
+ mbox_request_channel(mcl, 0);
+ if (IS_ERR(iproc_priv.mbox[iproc_priv.spu.num_spu])) {
+ err = (int)PTR_ERR(iproc_priv.mbox[iproc_priv.spu.num_spu]);
+ dev_err(dev,
+ "Mbox channel %d request failed with err %d",
+ iproc_priv.spu.num_spu, err);
+ iproc_priv.mbox[iproc_priv.spu.num_spu] = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+static void spu_mb_release(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < iproc_priv.spu.num_spu; i++)
+ mbox_free_channel(iproc_priv.mbox[i]);
+}
+
+static void spu_counters_init(void)
+{
+ int i;
+ int j;
+
+ atomic_set(&iproc_priv.session_count, 0);
+ atomic_set(&iproc_priv.stream_count, 0);
+ atomic_set(&iproc_priv.next_chan, (int)iproc_priv.spu.num_spu);
+ atomic64_set(&iproc_priv.bytes_in, 0);
+ atomic64_set(&iproc_priv.bytes_out, 0);
+ for (i = 0; i < SPU_OP_NUM; i++) {
+ atomic_set(&iproc_priv.op_counts[i], 0);
+ atomic_set(&iproc_priv.setkey_cnt[i], 0);
+ }
+ for (i = 0; i < CIPHER_ALG_LAST; i++)
+ for (j = 0; j < CIPHER_MODE_LAST; j++)
+ atomic_set(&iproc_priv.cipher_cnt[i][j], 0);
+
+ for (i = 0; i < HASH_ALG_LAST; i++) {
+ atomic_set(&iproc_priv.hash_cnt[i], 0);
+ atomic_set(&iproc_priv.hmac_cnt[i], 0);
+ }
+ for (i = 0; i < AEAD_TYPE_LAST; i++)
+ atomic_set(&iproc_priv.aead_cnt[i], 0);
+
+ atomic_set(&iproc_priv.mb_no_spc, 0);
+ atomic_set(&iproc_priv.mb_send_fail, 0);
+ atomic_set(&iproc_priv.bad_icv, 0);
+}
+
+static int spu_register_ablkcipher(struct iproc_alg_s *driver_alg)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct crypto_alg *crypto = &driver_alg->alg.crypto;
+ int err;
+
+ /* SPU2 does not support RC4 */
+ if ((driver_alg->cipher_info.alg == CIPHER_ALG_RC4) &&
+ (spu->spu_type == SPU_TYPE_SPU2))
+ return 0;
+
+ crypto->cra_module = THIS_MODULE;
+ crypto->cra_priority = cipher_pri;
+ crypto->cra_alignmask = 0;
+ crypto->cra_ctxsize = sizeof(struct iproc_ctx_s);
+ INIT_LIST_HEAD(&crypto->cra_list);
+
+ crypto->cra_init = ablkcipher_cra_init;
+ crypto->cra_exit = generic_cra_exit;
+ crypto->cra_type = &crypto_ablkcipher_type;
+ crypto->cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY;
+
+ crypto->cra_ablkcipher.setkey = ablkcipher_setkey;
+ crypto->cra_ablkcipher.encrypt = ablkcipher_encrypt;
+ crypto->cra_ablkcipher.decrypt = ablkcipher_decrypt;
+
+ err = crypto_register_alg(crypto);
+ /* Mark alg as having been registered, if successful */
+ if (err == 0)
+ driver_alg->registered = true;
+ pr_debug(" registered ablkcipher %s\n", crypto->cra_driver_name);
+ return err;
+}
+
+static int spu_register_ahash(struct iproc_alg_s *driver_alg)
+{
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct ahash_alg *hash = &driver_alg->alg.hash;
+ int err;
+
+ /* AES-XCBC is the only AES hash type currently supported on SPU-M */
+ if ((driver_alg->auth_info.alg == HASH_ALG_AES) &&
+ (driver_alg->auth_info.mode != HASH_MODE_XCBC) &&
+ (spu->spu_type == SPU_TYPE_SPUM))
+ return 0;
+
+ /* SHA3 algorithm variants are not registered for SPU-M or SPU2. */
+ if ((driver_alg->auth_info.alg >= HASH_ALG_SHA3_224) &&
+ (spu->spu_subtype != SPU_SUBTYPE_SPU2_V2))
+ return 0;
+
+ hash->halg.base.cra_module = THIS_MODULE;
+ hash->halg.base.cra_priority = hash_pri;
+ hash->halg.base.cra_alignmask = 0;
+ hash->halg.base.cra_ctxsize = sizeof(struct iproc_ctx_s);
+ hash->halg.base.cra_init = ahash_cra_init;
+ hash->halg.base.cra_exit = generic_cra_exit;
+ hash->halg.base.cra_type = &crypto_ahash_type;
+ hash->halg.base.cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC;
+ /*
+ * export state size has to be < 512 bytes. So don't include msg bufs
+ * in state size.
+ */
+ hash->halg.statesize = offsetof(struct iproc_reqctx_s, msg_buf);
+
+ if (driver_alg->auth_info.mode != HASH_MODE_HMAC) {
+ hash->setkey = ahash_setkey;
+ hash->init = ahash_init;
+ hash->update = ahash_update;
+ hash->final = ahash_final;
+ hash->finup = ahash_finup;
+ hash->digest = ahash_digest;
+ } else {
+ hash->setkey = ahash_hmac_setkey;
+ hash->init = ahash_hmac_init;
+ hash->update = ahash_hmac_update;
+ hash->final = ahash_hmac_final;
+ hash->finup = ahash_hmac_finup;
+ hash->digest = ahash_hmac_digest;
+ }
+ hash->export = ahash_export;
+ hash->import = ahash_import;
+
+ err = crypto_register_ahash(hash);
+ /* Mark alg as having been registered, if successful */
+ if (err == 0)
+ driver_alg->registered = true;
+ pr_debug(" registered ahash %s\n",
+ hash->halg.base.cra_driver_name);
+ return err;
+}
+
+static int spu_register_aead(struct iproc_alg_s *driver_alg)
+{
+ struct aead_alg *aead = &driver_alg->alg.aead;
+ int err;
+
+ aead->base.cra_module = THIS_MODULE;
+ aead->base.cra_priority = aead_pri;
+ aead->base.cra_alignmask = 0;
+ aead->base.cra_ctxsize = sizeof(struct iproc_ctx_s);
+ INIT_LIST_HEAD(&aead->base.cra_list);
+
+ aead->base.cra_flags |= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
+ /* setkey set in alg initialization */
+ aead->setauthsize = aead_setauthsize;
+ aead->encrypt = aead_encrypt;
+ aead->decrypt = aead_decrypt;
+ aead->init = aead_cra_init;
+ aead->exit = aead_cra_exit;
+
+ err = crypto_register_aead(aead);
+ /* Mark alg as having been registered, if successful */
+ if (err == 0)
+ driver_alg->registered = true;
+ pr_debug(" registered aead %s\n", aead->base.cra_driver_name);
+ return err;
+}
+
+/* register crypto algorithms the device supports */
+static int spu_algs_register(struct device *dev)
+{
+ int i, j;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(driver_algs); i++) {
+ switch (driver_algs[i].type) {
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ err = spu_register_ablkcipher(&driver_algs[i]);
+ break;
+ case CRYPTO_ALG_TYPE_AHASH:
+ err = spu_register_ahash(&driver_algs[i]);
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ err = spu_register_aead(&driver_algs[i]);
+ break;
+ default:
+ dev_err(dev,
+ "iproc-crypto: unknown alg type: %d",
+ driver_algs[i].type);
+ err = -EINVAL;
+ }
+
+ if (err) {
+ dev_err(dev, "alg registration failed with error %d\n",
+ err);
+ goto err_algs;
+ }
+ }
+
+ return 0;
+
+err_algs:
+ for (j = 0; j < i; j++) {
+ /* Skip any algorithm not registered */
+ if (!driver_algs[j].registered)
+ continue;
+ switch (driver_algs[j].type) {
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ crypto_unregister_alg(&driver_algs[j].alg.crypto);
+ driver_algs[j].registered = false;
+ break;
+ case CRYPTO_ALG_TYPE_AHASH:
+ crypto_unregister_ahash(&driver_algs[j].alg.hash);
+ driver_algs[j].registered = false;
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ crypto_unregister_aead(&driver_algs[j].alg.aead);
+ driver_algs[j].registered = false;
+ break;
+ }
+ }
+ return err;
+}
+
+/* ==================== Kernel Platform API ==================== */
+
+static struct spu_type_subtype spum_ns2_types = {
+ SPU_TYPE_SPUM, SPU_SUBTYPE_SPUM_NS2
+};
+
+static struct spu_type_subtype spum_nsp_types = {
+ SPU_TYPE_SPUM, SPU_SUBTYPE_SPUM_NSP
+};
+
+static struct spu_type_subtype spu2_types = {
+ SPU_TYPE_SPU2, SPU_SUBTYPE_SPU2_V1
+};
+
+static struct spu_type_subtype spu2_v2_types = {
+ SPU_TYPE_SPU2, SPU_SUBTYPE_SPU2_V2
+};
+
+static const struct of_device_id bcm_spu_dt_ids[] = {
+ {
+ .compatible = "brcm,spum-crypto",
+ .data = &spum_ns2_types,
+ },
+ {
+ .compatible = "brcm,spum-nsp-crypto",
+ .data = &spum_nsp_types,
+ },
+ {
+ .compatible = "brcm,spu2-crypto",
+ .data = &spu2_types,
+ },
+ {
+ .compatible = "brcm,spu2-v2-crypto",
+ .data = &spu2_v2_types,
+ },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, bcm_spu_dt_ids);
+
+static int spu_dt_read(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spu_hw *spu = &iproc_priv.spu;
+ struct resource *spu_ctrl_regs;
+ const struct of_device_id *match;
+ const struct spu_type_subtype *matched_spu_type;
+ void __iomem *spu_reg_vbase[MAX_SPUS];
+ int err;
+
+ match = of_match_device(of_match_ptr(bcm_spu_dt_ids), dev);
+ matched_spu_type = match->data;
+
+ if (iproc_priv.spu.num_spu > 1) {
+ /* If this is 2nd or later SPU, make sure it's same type */
+ if ((spu->spu_type != matched_spu_type->type) ||
+ (spu->spu_subtype != matched_spu_type->subtype)) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "Multiple SPU types not allowed");
+ return err;
+ }
+ } else {
+ /* Record type of first SPU */
+ spu->spu_type = matched_spu_type->type;
+ spu->spu_subtype = matched_spu_type->subtype;
+ }
+
+ /* Get and map SPU registers */
+ spu_ctrl_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!spu_ctrl_regs) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "Invalid/missing registers for SPU\n");
+ return err;
+ }
+
+ spu_reg_vbase[iproc_priv.spu.num_spu] =
+ devm_ioremap_resource(dev, spu_ctrl_regs);
+ if (IS_ERR(spu_reg_vbase[iproc_priv.spu.num_spu])) {
+ err = PTR_ERR(spu_reg_vbase[iproc_priv.spu.num_spu]);
+ dev_err(&pdev->dev, "Failed to map registers: %d\n",
+ err);
+ spu_reg_vbase[iproc_priv.spu.num_spu] = NULL;
+ return err;
+ }
+
+ dev_dbg(dev, "SPU %d detected.", iproc_priv.spu.num_spu);
+
+ spu->reg_vbase[iproc_priv.spu.num_spu] = spu_reg_vbase;
+
+ return 0;
+}
+
+int bcm_spu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spu_hw *spu = &iproc_priv.spu;
+ int err = 0;
+
+ iproc_priv.pdev[iproc_priv.spu.num_spu] = pdev;
+ platform_set_drvdata(iproc_priv.pdev[iproc_priv.spu.num_spu],
+ &iproc_priv);
+
+ err = spu_dt_read(pdev);
+ if (err < 0)
+ goto failure;
+
+ err = spu_mb_init(&pdev->dev);
+ if (err < 0)
+ goto failure;
+
+ iproc_priv.spu.num_spu++;
+
+ /* If already initialized, we've just added another SPU and are done */
+ if (iproc_priv.inited)
+ return 0;
+
+ if (spu->spu_type == SPU_TYPE_SPUM)
+ iproc_priv.bcm_hdr_len = 8;
+ else if (spu->spu_type == SPU_TYPE_SPU2)
+ iproc_priv.bcm_hdr_len = 0;
+
+ spu_functions_register(&pdev->dev, spu->spu_type, spu->spu_subtype);
+
+ spu_counters_init();
+
+ spu_setup_debugfs();
+
+ err = spu_algs_register(dev);
+ if (err < 0)
+ goto fail_reg;
+
+ iproc_priv.inited = true;
+
+ return 0;
+
+fail_reg:
+ spu_free_debugfs();
+failure:
+ spu_mb_release(pdev);
+ dev_err(dev, "%s failed with error %d.\n", __func__, err);
+
+ return err;
+}
+
+int bcm_spu_remove(struct platform_device *pdev)
+{
+ int i;
+ struct device *dev = &pdev->dev;
+ char *cdn;
+
+ for (i = 0; i < ARRAY_SIZE(driver_algs); i++) {
+ /*
+ * Not all algorithms were registered, depending on whether
+ * hardware is SPU or SPU2. So here we make sure to skip
+ * those algorithms that were not previously registered.
+ */
+ if (!driver_algs[i].registered)
+ continue;
+
+ switch (driver_algs[i].type) {
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ crypto_unregister_alg(&driver_algs[i].alg.crypto);
+ dev_dbg(dev, " unregistered cipher %s\n",
+ driver_algs[i].alg.crypto.cra_driver_name);
+ driver_algs[i].registered = false;
+ break;
+ case CRYPTO_ALG_TYPE_AHASH:
+ crypto_unregister_ahash(&driver_algs[i].alg.hash);
+ cdn = driver_algs[i].alg.hash.halg.base.cra_driver_name;
+ dev_dbg(dev, " unregistered hash %s\n", cdn);
+ driver_algs[i].registered = false;
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ crypto_unregister_aead(&driver_algs[i].alg.aead);
+ dev_dbg(dev, " unregistered aead %s\n",
+ driver_algs[i].alg.aead.base.cra_driver_name);
+ driver_algs[i].registered = false;
+ break;
+ }
+ }
+ spu_free_debugfs();
+ spu_mb_release(pdev);
+ return 0;
+}
+
+/* ===== Kernel Module API ===== */
+
+static struct platform_driver bcm_spu_pdriver = {
+ .driver = {
+ .name = "brcm-spu-crypto",
+ .of_match_table = of_match_ptr(bcm_spu_dt_ids),
+ },
+ .probe = bcm_spu_probe,
+ .remove = bcm_spu_remove,
+};
+module_platform_driver(bcm_spu_pdriver);
+
+MODULE_AUTHOR("Rob Rice <rob.rice-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>");
+MODULE_DESCRIPTION("Broadcom symmetric crypto offload driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/crypto/bcm/cipher.h b/drivers/crypto/bcm/cipher.h
new file mode 100644
index 0000000..b03b710
--- /dev/null
+++ b/drivers/crypto/bcm/cipher.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#ifndef _CIPHER_H
+#define _CIPHER_H
+
+#include <linux/atomic.h>
+#include <linux/mailbox/brcm-message.h>
+#include <linux/mailbox_client.h>
+#include <crypto/aes.h>
+#include <crypto/internal/hash.h>
+#include <crypto/aead.h>
+#include <crypto/sha.h>
+#include <crypto/sha3.h>
+
+#include "spu.h"
+#include "spum.h"
+#include "spu2.h"
+
+/* Driver supports up to MAX_SPUS SPU blocks */
+#define MAX_SPUS 16
+
+#define ARC4_MIN_KEY_SIZE 1
+#define ARC4_MAX_KEY_SIZE 256
+#define ARC4_BLOCK_SIZE 1
+#define ARC4_STATE_SIZE 4
+
+#define CCM_AES_IV_SIZE 16
+#define GCM_AES_IV_SIZE 12
+#define GCM_ESP_IV_SIZE 8
+#define CCM_ESP_IV_SIZE 8
+#define RFC4543_ICV_SIZE 16
+
+#define MAX_KEY_SIZE ARC4_MAX_KEY_SIZE
+#define MAX_IV_SIZE AES_BLOCK_SIZE
+#define MAX_DIGEST_SIZE SHA3_512_DIGEST_SIZE
+#define MAX_ASSOC_SIZE 512
+
+/* size of salt value for AES-GCM-ESP and AES-CCM-ESP */
+#define GCM_ESP_SALT_SIZE 4
+#define CCM_ESP_SALT_SIZE 3
+#define MAX_SALT_SIZE GCM_ESP_SALT_SIZE
+#define GCM_ESP_SALT_OFFSET 0
+#define CCM_ESP_SALT_OFFSET 1
+
+#define GCM_ESP_DIGESTSIZE 16
+
+#define MAX_HASH_BLOCK_SIZE SHA512_BLOCK_SIZE
+
+/*
+ * Maximum number of bytes from a non-final hash request that can be deferred
+ * until more data is available. With new crypto API framework, this
+ * can be no more than one block of data.
+ */
+#define HASH_CARRY_MAX MAX_HASH_BLOCK_SIZE
+
+/* Force at least 4-byte alignment of all SPU message fields */
+#define SPU_MSG_ALIGN 4
+
+/* Number of times to resend mailbox message if mb queue is full */
+#define SPU_MB_RETRY_MAX 1000
+
+/* op_counts[] indexes */
+enum op_type {
+ SPU_OP_CIPHER,
+ SPU_OP_HASH,
+ SPU_OP_HMAC,
+ SPU_OP_AEAD,
+ SPU_OP_NUM
+};
+
+enum spu_spu_type {
+ SPU_TYPE_SPUM,
+ SPU_TYPE_SPU2,
+};
+
+/*
+ * SPUM_NS2 and SPUM_NSP are the SPU-M block on Northstar 2 and Northstar Plus,
+ * respectively.
+ */
+enum spu_spu_subtype {
+ SPU_SUBTYPE_SPUM_NS2,
+ SPU_SUBTYPE_SPUM_NSP,
+ SPU_SUBTYPE_SPU2_V1,
+ SPU_SUBTYPE_SPU2_V2
+};
+
+struct spu_type_subtype {
+ enum spu_spu_type type;
+ enum spu_spu_subtype subtype;
+};
+
+struct cipher_op {
+ enum spu_cipher_alg alg;
+ enum spu_cipher_mode mode;
+};
+
+struct auth_op {
+ enum hash_alg alg;
+ enum hash_mode mode;
+};
+
+struct iproc_alg_s {
+ u32 type;
+ union {
+ struct crypto_alg crypto;
+ struct ahash_alg hash;
+ struct aead_alg aead;
+ } alg;
+ struct cipher_op cipher_info;
+ struct auth_op auth_info;
+ bool auth_first;
+ bool registered;
+};
+
+/*
+ * Buffers for a SPU request/reply message pair. All part of one structure to
+ * allow a single alloc per request.
+ */
+struct spu_msg_buf {
+ /* Request message fragments */
+
+ /*
+ * SPU request message header. For SPU-M, holds MH, EMH, SCTX, BDESC,
+ * and BD header. For SPU2, holds FMD, OMD.
+ */
+ u8 bcm_spu_req_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)];
+
+ /* IV or counter. Size to include salt. Also used for XTS tweek. */
+ u8 iv_ctr[ALIGN(2 * AES_BLOCK_SIZE, SPU_MSG_ALIGN)];
+
+ /* Hash digest. request and response. */
+ u8 digest[ALIGN(MAX_DIGEST_SIZE, SPU_MSG_ALIGN)];
+
+ /* SPU request message padding */
+ u8 spu_req_pad[ALIGN(SPU_PAD_LEN_MAX, SPU_MSG_ALIGN)];
+
+ /* SPU-M request message STATUS field */
+ u8 tx_stat[ALIGN(SPU_TX_STATUS_LEN, SPU_MSG_ALIGN)];
+
+ /* Response message fragments */
+
+ /* SPU response message header */
+ u8 spu_resp_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)];
+
+ /* SPU response message STATUS field padding */
+ u8 rx_stat_pad[ALIGN(SPU_STAT_PAD_MAX, SPU_MSG_ALIGN)];
+
+ /* SPU response message STATUS field */
+ u8 rx_stat[ALIGN(SPU_RX_STATUS_LEN, SPU_MSG_ALIGN)];
+
+ union {
+ /* Buffers only used for ablkcipher */
+ struct {
+ /*
+ * Field used for either SUPDT when RC4 is used
+ * -OR- tweak value when XTS/AES is used
+ */
+ u8 supdt_tweak[ALIGN(SPU_SUPDT_LEN, SPU_MSG_ALIGN)];
+ } c;
+
+ /* Buffers only used for aead */
+ struct {
+ /* SPU response pad for GCM data */
+ u8 gcmpad[ALIGN(AES_BLOCK_SIZE, SPU_MSG_ALIGN)];
+
+ /* SPU request msg padding for GCM AAD */
+ u8 req_aad_pad[ALIGN(SPU_PAD_LEN_MAX, SPU_MSG_ALIGN)];
+
+ /* SPU response data to be discarded */
+ u8 resp_aad[ALIGN(MAX_ASSOC_SIZE + MAX_IV_SIZE,
+ SPU_MSG_ALIGN)];
+ } a;
+ };
+};
+
+struct iproc_ctx_s {
+ u8 enckey[MAX_KEY_SIZE + ARC4_STATE_SIZE];
+ unsigned int enckeylen;
+
+ u8 authkey[MAX_KEY_SIZE + ARC4_STATE_SIZE];
+ unsigned int authkeylen;
+
+ u8 salt[MAX_SALT_SIZE];
+ unsigned int salt_len;
+ unsigned int salt_offset;
+ u8 iv[MAX_IV_SIZE];
+
+ unsigned int digestsize;
+
+ struct iproc_alg_s *alg;
+ bool is_esp;
+
+ struct cipher_op cipher;
+ enum spu_cipher_type cipher_type;
+
+ struct auth_op auth;
+ bool auth_first;
+
+ /*
+ * The maximum length in bytes of the payload in a SPU message for this
+ * context. For SPU-M, the payload is the combination of AAD and data.
+ * For SPU2, the payload is just data. A value of SPU_MAX_PAYLOAD_INF
+ * indicates that there is no limit to the length of the SPU message
+ * payload.
+ */
+ unsigned int max_payload;
+
+ struct crypto_aead *fallback_cipher;
+
+ /* auth_type is determined during processing of request */
+
+ u8 ipad[MAX_HASH_BLOCK_SIZE];
+ u8 opad[MAX_HASH_BLOCK_SIZE];
+
+ /*
+ * Buffer to hold SPU message header template. Template is created at
+ * setkey time for ablkcipher requests, since most of the fields in the
+ * header are known at that time. At request time, just fill in a few
+ * missing pieces related to length of data in the request and IVs, etc.
+ */
+ u8 bcm_spu_req_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)];
+
+ /* Length of SPU request header */
+ u16 spu_req_hdr_len;
+
+ /* Expected length of SPU response header */
+ u16 spu_resp_hdr_len;
+
+ /*
+ * shash descriptor - needed to perform incremental hashing in
+ * in software, when hw doesn't support it.
+ */
+ struct shash_desc *shash;
+
+ bool is_rfc4543; /* RFC 4543 style of GMAC */
+};
+
+struct iproc_reqctx_s {
+ /* general context */
+ struct crypto_async_request *parent;
+
+ /* only valid after enqueue() */
+ struct iproc_ctx_s *ctx;
+
+ u8 chan_idx; /* Mailbox channel to be used to submit this request */
+
+ /* total todo, rx'd, and sent for this request */
+ unsigned int total_todo;
+ unsigned int total_received; /* only valid for ablkcipher */
+ unsigned int total_sent;
+
+ /*
+ * num bytes sent to hw from the src sg in this request. This can differ
+ * from total_sent for incremental hashing. total_sent includes previous
+ * init() and update() data. src_sent does not.
+ */
+ unsigned int src_sent;
+ unsigned int hmac_offset;
+
+ /*
+ * For AEAD requests, start of associated data. This will typically
+ * point to the beginning of the src scatterlist from the request,
+ * since assoc data is at the beginning of the src scatterlist rather
+ * than in its own sg.
+ */
+ struct scatterlist *assoc;
+
+ /*
+ * scatterlist entry and offset to start of data for next chunk. Crypto
+ * API src scatterlist for AEAD starts with AAD, if present. For first
+ * chunk, src_sg is sg entry at beginning of input data (after AAD).
+ * src_skip begins at the offset in that sg entry where data begins.
+ */
+ struct scatterlist *src_sg;
+ int src_nents; /* Number of src entries with data */
+ u32 src_skip; /* bytes of current sg entry already used */
+
+ /*
+ * Same for destination. For AEAD, if there is AAD, output data must
+ * be written at offset following AAD.
+ */
+ struct scatterlist *dst_sg;
+ int dst_nents; /* Number of dst entries with data */
+ u32 dst_skip; /* bytes of current sg entry already written */
+
+ /* Mailbox message used to send this request to PDC driver */
+ struct brcm_message mb_mssg;
+
+ bool bd_suppress; /* suppress BD field in SPU response? */
+
+ /* cipher context */
+ bool is_encrypt;
+
+ /*
+ * CBC mode: IV. CTR mode: counter. Else empty. Used as a DMA
+ * buffer for AEAD requests. So allocate as DMAable memory. If IV
+ * concatenated with salt, includes the salt.
+ */
+ u8 *iv_ctr;
+ /* Length of IV or counter, in bytes */
+ unsigned int iv_ctr_len;
+
+ /*
+ * Hash requests can be of any size, whether initial, update, or final.
+ * A non-final request must be submitted to the SPU as an integral
+ * number of blocks. This may leave data at the end of the request
+ * that is not a full block. Since the request is non-final, it cannot
+ * be padded. So, we write the remainder to this hash_carry buffer and
+ * hold it until the next request arrives. The carry data is then
+ * submitted at the beginning of the data in the next SPU msg.
+ * hash_carry_len is the number of bytes currently in hash_carry. These
+ * fields are only used for ahash requests.
+ */
+ u8 hash_carry[HASH_CARRY_MAX];
+ unsigned int hash_carry_len;
+ unsigned int is_final; /* is this the final for the hash op? */
+
+ /*
+ * Digest from incremental hash is saved here to include in next hash
+ * operation. Cannot be stored in req->result for truncated hashes,
+ * since result may be sized for final digest. Cannot be saved in
+ * msg_buf because that gets deleted between incremental hash ops
+ * and is not saved as part of export().
+ */
+ u8 incr_hash[MAX_DIGEST_SIZE];
+
+ /* hmac context */
+ bool is_sw_hmac;
+
+ /* aead context */
+ struct crypto_tfm *old_tfm;
+ crypto_completion_t old_complete;
+ void *old_data;
+
+ gfp_t gfp;
+
+ /* Buffers used to build SPU request and response messages */
+ /* MUST BE LAST */
+ struct spu_msg_buf msg_buf;
+};
+
+/*
+ * Structure encapsulates a set of function pointers specific to the type of
+ * SPU hardware running. These functions handling creation and parsing of
+ * SPU request messages and SPU response messages. Includes hardware-specific
+ * values read from device tree.
+ */
+struct spu_hw {
+ void (*spu_dump_msg_hdr)(u8 *buf, unsigned int buf_len);
+ u32 (*spu_ctx_max_payload)(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ unsigned int blocksize);
+ u32 (*spu_payload_length)(u8 *spu_hdr);
+ u16 (*spu_response_hdr_len)(u16 auth_key_len, u16 enc_key_len,
+ bool is_hash);
+ u16 (*spu_hash_pad_len)(enum hash_alg hash_alg,
+ enum hash_mode hash_mode, u32 chunksize,
+ u16 hash_block_size);
+ u32 (*spu_gcm_ccm_pad_len)(enum spu_cipher_mode cipher_mode,
+ unsigned int data_size);
+ u32 (*spu_assoc_resp_len)(enum spu_cipher_mode cipher_mode,
+ unsigned int assoc_len,
+ unsigned int iv_len, bool is_encrypt);
+ u8 (*spu_aead_ivlen)(enum spu_cipher_mode cipher_mode,
+ u16 iv_len);
+ enum hash_type (*spu_hash_type)(u32 src_sent);
+ u32 (*spu_digest_size)(u32 digest_size, enum hash_alg alg,
+ enum hash_type);
+ u32 (*spu_create_request)(u8 *spu_hdr,
+ struct spu_request_opts *req_opts,
+ struct spu_cipher_parms *cipher_parms,
+ struct spu_hash_parms *hash_parms,
+ struct spu_aead_parms *aead_parms,
+ unsigned int data_size);
+ u16 (*spu_cipher_req_init)(u8 *spu_hdr,
+ struct spu_cipher_parms *cipher_parms);
+ void (*spu_cipher_req_finish)(u8 *spu_hdr,
+ u16 spu_req_hdr_len,
+ unsigned int is_inbound,
+ struct spu_cipher_parms *cipher_parms,
+ bool update_key,
+ unsigned int data_size);
+ void (*spu_request_pad)(u8 *pad_start, u32 gcm_padding,
+ u32 hash_pad_len, enum hash_alg auth_alg,
+ enum hash_mode auth_mode,
+ unsigned int total_sent, u32 status_padding);
+ u8 (*spu_xts_tweak_in_payload)(void);
+ u8 (*spu_tx_status_len)(void);
+ u8 (*spu_rx_status_len)(void);
+ int (*spu_status_process)(u8 *statp);
+ void (*spu_ccm_update_iv)(unsigned int digestsize,
+ struct spu_cipher_parms *cipher_parms,
+ unsigned int assoclen, unsigned int chunksize,
+ bool is_encrypt, bool is_esp);
+ u32 (*spu_wordalign_padlen)(u32 data_size);
+
+ /* The base virtual address of the SPU hw registers */
+ void __iomem *reg_vbase[MAX_SPUS];
+
+ /* Version of the SPU hardware */
+ enum spu_spu_type spu_type;
+
+ /* Sub-version of the SPU hardware */
+ enum spu_spu_subtype spu_subtype;
+
+ /* The number of SPUs on this platform */
+ u32 num_spu;
+};
+
+struct device_private {
+ struct platform_device *pdev[MAX_SPUS];
+
+ struct spu_hw spu;
+
+ atomic_t session_count; /* number of streams active */
+ atomic_t stream_count; /* monotonic counter for streamID's */
+
+ /* Length of BCM header. Set to 0 when hw does not expect BCM HEADER. */
+ u8 bcm_hdr_len;
+
+ /* The index of the channel to use for the next crypto request */
+ atomic_t next_chan;
+
+ struct dentry *debugfs_dir;
+ struct dentry *debugfs_stats;
+
+ /* Number of request bytes processed and result bytes returned */
+ atomic64_t bytes_in;
+ atomic64_t bytes_out;
+
+ /* Number of operations of each type */
+ atomic_t op_counts[SPU_OP_NUM];
+
+ atomic_t cipher_cnt[CIPHER_ALG_LAST][CIPHER_MODE_LAST];
+ atomic_t hash_cnt[HASH_ALG_LAST];
+ atomic_t hmac_cnt[HASH_ALG_LAST];
+ atomic_t aead_cnt[AEAD_TYPE_LAST];
+
+ /* Number of calls to setkey() for each operation type */
+ atomic_t setkey_cnt[SPU_OP_NUM];
+
+ /* Number of times request was resubmitted because mb was full */
+ atomic_t mb_no_spc;
+
+ /* Number of mailbox send failures */
+ atomic_t mb_send_fail;
+
+ /* Number of ICV check failures for AEAD messages */
+ atomic_t bad_icv;
+
+ struct mbox_client mcl[MAX_SPUS];
+ /* Array of mailbox channel pointers, one for each channel */
+ struct mbox_chan *mbox[MAX_SPUS];
+
+ /* Driver initialized */
+ bool inited;
+};
+
+extern struct device_private iproc_priv;
+
+#endif
diff --git a/drivers/crypto/bcm/spu.c b/drivers/crypto/bcm/spu.c
new file mode 100644
index 0000000..0331267
--- /dev/null
+++ b/drivers/crypto/bcm/spu.c
@@ -0,0 +1,1252 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "util.h"
+#include "spu.h"
+#include "spum.h"
+#include "cipher.h"
+
+/* This array is based on the hash algo type supported in spu.h */
+char *tag_to_hash_idx[] = { "none", "md5", "sha1", "sha224", "sha256" };
+
+char *hash_alg_name[] = { "None", "md5", "sha1", "sha224", "sha256", "aes",
+ "sha384", "sha512", "sha3_224", "sha3_256", "sha3_384", "sha3_512" };
+
+char *aead_alg_name[] = { "ccm(aes)", "gcm(aes)", "authenc" };
+
+/* Assumes SPU-M messages are in big endian */
+void spum_dump_msg_hdr(u8 *buf, unsigned int buf_len)
+{
+ u8 *ptr = buf;
+ struct SPUHEADER *spuh = (struct SPUHEADER *)buf;
+ unsigned int hash_key_len = 0;
+ unsigned int hash_state_len = 0;
+ unsigned int cipher_key_len = 0;
+ unsigned int iv_len;
+ u32 pflags;
+ u32 cflags;
+ u32 ecf;
+ u32 cipher_alg;
+ u32 cipher_mode;
+ u32 cipher_type;
+ u32 hash_alg;
+ u32 hash_mode;
+ u32 hash_type;
+ u32 sctx_size; /* SCTX length in words */
+ u32 sctx_pl_len; /* SCTX payload length in bytes */
+
+ packet_log("\n");
+ packet_log("SPU Message header %p len: %u\n", buf, buf_len);
+
+ /* ========== Decode MH ========== */
+ packet_log(" MH 0x%08x\n", be32_to_cpu(*((u32 *)ptr)));
+ if (spuh->mh.flags & MH_SCTX_PRES)
+ packet_log(" SCTX present\n");
+ if (spuh->mh.flags & MH_BDESC_PRES)
+ packet_log(" BDESC present\n");
+ if (spuh->mh.flags & MH_MFM_PRES)
+ packet_log(" MFM present\n");
+ if (spuh->mh.flags & MH_BD_PRES)
+ packet_log(" BD present\n");
+ if (spuh->mh.flags & MH_HASH_PRES)
+ packet_log(" HASH present\n");
+ if (spuh->mh.flags & MH_SUPDT_PRES)
+ packet_log(" SUPDT present\n");
+ packet_log(" Opcode 0x%02x\n", spuh->mh.op_code);
+
+ ptr += sizeof(spuh->mh) + sizeof(spuh->emh); /* skip emh. unused */
+
+ /* ========== Decode SCTX ========== */
+ if (spuh->mh.flags & MH_SCTX_PRES) {
+ pflags = be32_to_cpu(spuh->sa.proto_flags);
+ packet_log(" SCTX[0] 0x%08x\n", pflags);
+ sctx_size = pflags & SCTX_SIZE;
+ packet_log(" Size %u words\n", sctx_size);
+
+ cflags = be32_to_cpu(spuh->sa.cipher_flags);
+ packet_log(" SCTX[1] 0x%08x\n", cflags);
+ packet_log(" Inbound:%lu (1:decrypt/vrfy 0:encrypt/auth)\n",
+ (cflags & CIPHER_INBOUND) >> CIPHER_INBOUND_SHIFT);
+ packet_log(" Order:%lu (1:AuthFirst 0:EncFirst)\n",
+ (cflags & CIPHER_ORDER) >> CIPHER_ORDER_SHIFT);
+ packet_log(" ICV_IS_512:%lx\n",
+ (cflags & ICV_IS_512) >> ICV_IS_512_SHIFT);
+ cipher_alg = (cflags & CIPHER_ALG) >> CIPHER_ALG_SHIFT;
+ cipher_mode = (cflags & CIPHER_MODE) >> CIPHER_MODE_SHIFT;
+ cipher_type = (cflags & CIPHER_TYPE) >> CIPHER_TYPE_SHIFT;
+ packet_log(" Crypto Alg:%u Mode:%u Type:%u\n",
+ cipher_alg, cipher_mode, cipher_type);
+ hash_alg = (cflags & HASH_ALG) >> HASH_ALG_SHIFT;
+ hash_mode = (cflags & HASH_MODE) >> HASH_MODE_SHIFT;
+ hash_type = (cflags & HASH_TYPE) >> HASH_TYPE_SHIFT;
+ packet_log(" Hash Alg:%x Mode:%x Type:%x\n",
+ hash_alg, hash_mode, hash_type);
+ packet_log(" UPDT_Offset:%u\n", cflags & UPDT_OFST);
+
+ ecf = be32_to_cpu(spuh->sa.ecf);
+ packet_log(" SCTX[2] 0x%08x\n", ecf);
+ packet_log(" WriteICV:%lu CheckICV:%lu ICV_SIZE:%u ",
+ (ecf & INSERT_ICV) >> INSERT_ICV_SHIFT,
+ (ecf & CHECK_ICV) >> CHECK_ICV_SHIFT,
+ (ecf & ICV_SIZE) >> ICV_SIZE_SHIFT);
+ packet_log("BD_SUPPRESS:%lu\n",
+ (ecf & BD_SUPPRESS) >> BD_SUPPRESS_SHIFT);
+ packet_log(" SCTX_IV:%lu ExplicitIV:%lu GenIV:%lu ",
+ (ecf & SCTX_IV) >> SCTX_IV_SHIFT,
+ (ecf & EXPLICIT_IV) >> EXPLICIT_IV_SHIFT,
+ (ecf & GEN_IV) >> GEN_IV_SHIFT);
+ packet_log("IV_OV_OFST:%lu EXP_IV_SIZE:%u\n",
+ (ecf & IV_OFFSET) >> IV_OFFSET_SHIFT,
+ ecf & EXP_IV_SIZE);
+
+ ptr += sizeof(struct SCTX);
+
+ if (hash_alg && hash_mode) {
+ char *name = "NONE";
+
+ switch (hash_alg) {
+ case HASH_ALG_MD5:
+ hash_key_len = 16;
+ name = "MD5";
+ break;
+ case HASH_ALG_SHA1:
+ hash_key_len = 20;
+ name = "SHA1";
+ break;
+ case HASH_ALG_SHA224:
+ hash_key_len = 28;
+ name = "SHA224";
+ break;
+ case HASH_ALG_SHA256:
+ hash_key_len = 32;
+ name = "SHA256";
+ break;
+ case HASH_ALG_SHA384:
+ hash_key_len = 48;
+ name = "SHA384";
+ break;
+ case HASH_ALG_SHA512:
+ hash_key_len = 64;
+ name = "SHA512";
+ break;
+ case HASH_ALG_AES:
+ hash_key_len = 0;
+ name = "AES";
+ break;
+ case HASH_ALG_NONE:
+ break;
+ }
+
+ packet_log(" Auth Key Type:%s Length:%u Bytes\n",
+ name, hash_key_len);
+ packet_dump(" KEY: ", ptr, hash_key_len);
+ ptr += hash_key_len;
+ } else if ((hash_alg == HASH_ALG_AES) &&
+ (hash_mode == HASH_MODE_XCBC)) {
+ char *name = "NONE";
+
+ switch (cipher_type) {
+ case CIPHER_TYPE_AES128:
+ hash_key_len = 16;
+ name = "AES128-XCBC";
+ break;
+ case CIPHER_TYPE_AES192:
+ hash_key_len = 24;
+ name = "AES192-XCBC";
+ break;
+ case CIPHER_TYPE_AES256:
+ hash_key_len = 32;
+ name = "AES256-XCBC";
+ break;
+ }
+ packet_log(" Auth Key Type:%s Length:%u Bytes\n",
+ name, hash_key_len);
+ packet_dump(" KEY: ", ptr, hash_key_len);
+ ptr += hash_key_len;
+ }
+
+ if (hash_alg && (hash_mode == HASH_MODE_NONE) &&
+ (hash_type == HASH_TYPE_UPDT)) {
+ char *name = "NONE";
+
+ switch (hash_alg) {
+ case HASH_ALG_MD5:
+ hash_state_len = 16;
+ name = "MD5";
+ break;
+ case HASH_ALG_SHA1:
+ hash_state_len = 20;
+ name = "SHA1";
+ break;
+ case HASH_ALG_SHA224:
+ hash_state_len = 32;
+ name = "SHA224";
+ break;
+ case HASH_ALG_SHA256:
+ hash_state_len = 32;
+ name = "SHA256";
+ break;
+ case HASH_ALG_SHA384:
+ hash_state_len = 48;
+ name = "SHA384";
+ break;
+ case HASH_ALG_SHA512:
+ hash_state_len = 64;
+ name = "SHA512";
+ break;
+ case HASH_ALG_AES:
+ hash_state_len = 0;
+ name = "AES";
+ break;
+ case HASH_ALG_NONE:
+ break;
+ }
+
+ packet_log(" Auth State Type:%s Length:%u Bytes\n",
+ name, hash_state_len);
+ packet_dump(" State: ", ptr, hash_state_len);
+ ptr += hash_state_len;
+ }
+
+ if (cipher_alg) {
+ char *name = "NONE";
+
+ switch (cipher_alg) {
+ case CIPHER_ALG_DES:
+ cipher_key_len = 8;
+ name = "DES";
+ break;
+ case CIPHER_ALG_3DES:
+ cipher_key_len = 24;
+ name = "3DES";
+ break;
+ case CIPHER_ALG_RC4:
+ cipher_key_len = 260;
+ name = "ARC4";
+ break;
+ case CIPHER_ALG_AES:
+ switch (cipher_type) {
+ case CIPHER_TYPE_AES128:
+ cipher_key_len = 16;
+ name = "AES128";
+ break;
+ case CIPHER_TYPE_AES192:
+ cipher_key_len = 24;
+ name = "AES192";
+ break;
+ case CIPHER_TYPE_AES256:
+ cipher_key_len = 32;
+ name = "AES256";
+ break;
+ }
+ break;
+ case CIPHER_ALG_NONE:
+ break;
+ }
+
+ packet_log(" Cipher Key Type:%s Length:%u Bytes\n",
+ name, cipher_key_len);
+
+ /* XTS has two keys */
+ if (cipher_mode == CIPHER_MODE_XTS) {
+ packet_dump(" KEY2: ", ptr, cipher_key_len);
+ ptr += cipher_key_len;
+ packet_dump(" KEY1: ", ptr, cipher_key_len);
+ ptr += cipher_key_len;
+
+ cipher_key_len *= 2;
+ } else {
+ packet_dump(" KEY: ", ptr, cipher_key_len);
+ ptr += cipher_key_len;
+ }
+
+ if (ecf & SCTX_IV) {
+ sctx_pl_len = sctx_size * sizeof(u32) -
+ sizeof(struct SCTX);
+ iv_len = sctx_pl_len -
+ (hash_key_len + hash_state_len +
+ cipher_key_len);
+ packet_log(" IV Length:%u Bytes\n", iv_len);
+ packet_dump(" IV: ", ptr, iv_len);
+ ptr += iv_len;
+ }
+ }
+ }
+
+ /* ========== Decode BDESC ========== */
+ if (spuh->mh.flags & MH_BDESC_PRES) {
+#ifdef DEBUG
+ struct BDESC_HEADER *bdesc = (struct BDESC_HEADER *)ptr;
+#endif
+ packet_log(" BDESC[0] 0x%08x\n", be32_to_cpu(*((u32 *)ptr)));
+ packet_log(" OffsetMAC:%u LengthMAC:%u\n",
+ be16_to_cpu(bdesc->offset_mac),
+ be16_to_cpu(bdesc->length_mac));
+ ptr += sizeof(u32);
+
+ packet_log(" BDESC[1] 0x%08x\n", be32_to_cpu(*((u32 *)ptr)));
+ packet_log(" OffsetCrypto:%u LengthCrypto:%u\n",
+ be16_to_cpu(bdesc->offset_crypto),
+ be16_to_cpu(bdesc->length_crypto));
+ ptr += sizeof(u32);
+
+ packet_log(" BDESC[2] 0x%08x\n", be32_to_cpu(*((u32 *)ptr)));
+ packet_log(" OffsetICV:%u OffsetIV:%u\n",
+ be16_to_cpu(bdesc->offset_icv),
+ be16_to_cpu(bdesc->offset_iv));
+ ptr += sizeof(u32);
+ }
+
+ /* ========== Decode BD ========== */
+ if (spuh->mh.flags & MH_BD_PRES) {
+#ifdef DEBUG
+ struct BD_HEADER *bd = (struct BD_HEADER *)ptr;
+#endif
+ packet_log(" BD[0] 0x%08x\n", be32_to_cpu(*((u32 *)ptr)));
+ packet_log(" Size:%ubytes PrevLength:%u\n",
+ be16_to_cpu(bd->size), be16_to_cpu(bd->prev_length));
+ ptr += 4;
+ }
+
+ /* Double check sanity */
+ if (buf + buf_len != ptr) {
+ packet_log(" Packet parsed incorrectly. ");
+ packet_log("buf:%p buf_len:%u buf+buf_len:%p ptr:%p\n",
+ buf, buf_len, buf + buf_len, ptr);
+ }
+
+ packet_log("\n");
+}
+
+/**
+ * spum_ns2_ctx_max_payload() - Determine the max length of the payload for a
+ * SPU message for a given cipher and hash alg context.
+ * @cipher_alg: The cipher algorithm
+ * @cipher_mode: The cipher mode
+ * @blocksize: The size of a block of data for this algo
+ *
+ * The max payload must be a multiple of the blocksize so that if a request is
+ * too large to fit in a single SPU message, the request can be broken into
+ * max_payload sized chunks. Each chunk must be a multiple of blocksize.
+ *
+ * Return: Max payload length in bytes
+ */
+u32 spum_ns2_ctx_max_payload(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ unsigned int blocksize)
+{
+ u32 max_payload = SPUM_NS2_MAX_PAYLOAD;
+ u32 excess;
+
+ /* In XTS on SPU-M, we'll need to insert tweak before input data */
+ if (cipher_mode == CIPHER_MODE_XTS)
+ max_payload -= SPU_XTS_TWEAK_SIZE;
+
+ excess = max_payload % blocksize;
+
+ return max_payload - excess;
+}
+
+/**
+ * spum_nsp_ctx_max_payload() - Determine the max length of the payload for a
+ * SPU message for a given cipher and hash alg context.
+ * @cipher_alg: The cipher algorithm
+ * @cipher_mode: The cipher mode
+ * @blocksize: The size of a block of data for this algo
+ *
+ * The max payload must be a multiple of the blocksize so that if a request is
+ * too large to fit in a single SPU message, the request can be broken into
+ * max_payload sized chunks. Each chunk must be a multiple of blocksize.
+ *
+ * Return: Max payload length in bytes
+ */
+u32 spum_nsp_ctx_max_payload(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ unsigned int blocksize)
+{
+ u32 max_payload = SPUM_NSP_MAX_PAYLOAD;
+ u32 excess;
+
+ /* In XTS on SPU-M, we'll need to insert tweak before input data */
+ if (cipher_mode == CIPHER_MODE_XTS)
+ max_payload -= SPU_XTS_TWEAK_SIZE;
+
+ excess = max_payload % blocksize;
+
+ return max_payload - excess;
+}
+
+/** spum_payload_length() - Given a SPU-M message header, extract the payload
+ * length.
+ * @spu_hdr: Start of SPU header
+ *
+ * Assumes just MH, EMH, BD (no SCTX, BDESC. Works for response frames.
+ *
+ * Return: payload length in bytes
+ */
+u32 spum_payload_length(u8 *spu_hdr)
+{
+ struct BD_HEADER *bd;
+ u32 pl_len;
+
+ /* Find BD header. skip MH, EMH */
+ bd = (struct BD_HEADER *)(spu_hdr + 8);
+ pl_len = be16_to_cpu(bd->size);
+
+ return pl_len;
+}
+
+/**
+ * spum_response_hdr_len() - Given the length of the hash key and encryption
+ * key, determine the expected length of a SPU response header.
+ * @auth_key_len: authentication key length (bytes)
+ * @enc_key_len: encryption key length (bytes)
+ * @is_hash: true if response message is for a hash operation
+ *
+ * Return: length of SPU response header (bytes)
+ */
+u16 spum_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash)
+{
+ if (is_hash)
+ return SPU_HASH_RESP_HDR_LEN;
+ else
+ return SPU_RESP_HDR_LEN;
+}
+
+/**
+ * spum_hash_pad_len() - Calculate the length of hash padding required to extend
+ * data to a full block size.
+ * @hash_alg: hash algorithm
+ * @hash_mode: hash mode
+ * @chunksize: length of data, in bytes
+ * @hash_block_size: size of a block of data for hash algorithm
+ *
+ * Reserve space for 1 byte (0x80) start of pad and the total length as u64
+ *
+ * Return: length of hash pad in bytes
+ */
+u16 spum_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode,
+ u32 chunksize, u16 hash_block_size)
+{
+ unsigned int length_len;
+ unsigned int used_space_last_block;
+ int hash_pad_len;
+
+ /* AES-XCBC hash requires just padding to next block boundary */
+ if ((hash_alg == HASH_ALG_AES) && (hash_mode == HASH_MODE_XCBC)) {
+ used_space_last_block = chunksize % hash_block_size;
+ hash_pad_len = hash_block_size - used_space_last_block;
+ if (hash_pad_len >= hash_block_size)
+ hash_pad_len -= hash_block_size;
+ return hash_pad_len;
+ }
+
+ used_space_last_block = chunksize % hash_block_size + 1;
+ if ((hash_alg == HASH_ALG_SHA384) || (hash_alg == HASH_ALG_SHA512))
+ length_len = 2 * sizeof(u64);
+ else
+ length_len = sizeof(u64);
+
+ used_space_last_block += length_len;
+ hash_pad_len = hash_block_size - used_space_last_block;
+ if (hash_pad_len < 0)
+ hash_pad_len += hash_block_size;
+
+ hash_pad_len += 1 + length_len;
+ return hash_pad_len;
+}
+
+/**
+ * spum_gcm_ccm_pad_len() - Determine the required length of GCM or CCM padding.
+ * @cipher_mode: Algo type
+ * @data_size: Length of plaintext (bytes)
+ *
+ * @Return: Length of padding, in bytes
+ */
+u32 spum_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode,
+ unsigned int data_size)
+{
+ u32 pad_len = 0;
+ u32 m1 = SPU_GCM_CCM_ALIGN - 1;
+
+ if ((cipher_mode == CIPHER_MODE_GCM) ||
+ (cipher_mode == CIPHER_MODE_CCM))
+ pad_len = ((data_size + m1) & ~m1) - data_size;
+
+ return pad_len;
+}
+
+/**
+ * spum_assoc_resp_len() - Determine the size of the receive buffer required to
+ * catch associated data.
+ * @cipher_mode: cipher mode
+ * @assoc_len: length of associated data (bytes)
+ * @iv_len: length of IV (bytes)
+ * @is_encrypt: true if encrypting. false if decrypting.
+ *
+ * Return: length of associated data in response message (bytes)
+ */
+u32 spum_assoc_resp_len(enum spu_cipher_mode cipher_mode,
+ unsigned int assoc_len, unsigned int iv_len,
+ bool is_encrypt)
+{
+ u32 buflen = 0;
+ u32 pad;
+
+ if (assoc_len)
+ buflen = assoc_len;
+
+ if (cipher_mode == CIPHER_MODE_GCM) {
+ /* AAD needs to be padded in responses too */
+ pad = spum_gcm_ccm_pad_len(cipher_mode, buflen);
+ buflen += pad;
+ }
+ if (cipher_mode == CIPHER_MODE_CCM) {
+ /*
+ * AAD needs to be padded in responses too
+ * for CCM, len + 2 needs to be 128-bit aligned.
+ */
+ pad = spum_gcm_ccm_pad_len(cipher_mode, buflen + 2);
+ buflen += pad;
+ }
+
+ return buflen;
+}
+
+/**
+ * spu_aead_ivlen() - Calculate the length of the AEAD IV to be included
+ * in a SPU request after the AAD and before the payload.
+ * @cipher_mode: cipher mode
+ * @iv_ctr_len: initialization vector length in bytes
+ *
+ * In Linux ~4.2 and later, the assoc_data sg includes the IV. So no need
+ * to include the IV as a separate field in the SPU request msg.
+ *
+ * Return: Length of AEAD IV in bytes
+ */
+u8 spum_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len)
+{
+ return 0;
+}
+
+/**
+ * spum_hash_type() - Determine the type of hash operation.
+ * @src_sent: The number of bytes in the current request that have already
+ * been sent to the SPU to be hashed.
+ *
+ * We do not use HASH_TYPE_FULL for requests that fit in a single SPU message.
+ * Using FULL causes failures (such as when the string to be hashed is empty).
+ * For similar reasons, we never use HASH_TYPE_FIN. Instead, submit messages
+ * as INIT or UPDT and do the hash padding in sw.
+ */
+enum hash_type spum_hash_type(u32 src_sent)
+{
+ return src_sent ? HASH_TYPE_UPDT : HASH_TYPE_INIT;
+}
+
+/**
+ * spum_digest_size() - Determine the size of a hash digest to expect the SPU to
+ * return.
+ * alg_digest_size: Number of bytes in the final digest for the given algo
+ * alg: The hash algorithm
+ * htype: Type of hash operation (init, update, full, etc)
+ *
+ * When doing incremental hashing for an algorithm with a truncated hash
+ * (e.g., SHA224), the SPU returns the full digest so that it can be fed back as
+ * a partial result for the next chunk.
+ */
+u32 spum_digest_size(u32 alg_digest_size, enum hash_alg alg,
+ enum hash_type htype)
+{
+ u32 digestsize = alg_digest_size;
+
+ /* SPU returns complete digest when doing incremental hash and truncated
+ * hash algo.
+ */
+ if ((htype == HASH_TYPE_INIT) || (htype == HASH_TYPE_UPDT)) {
+ if (alg == HASH_ALG_SHA224)
+ digestsize = SHA256_DIGEST_SIZE;
+ else if (alg == HASH_ALG_SHA384)
+ digestsize = SHA512_DIGEST_SIZE;
+ }
+ return digestsize;
+}
+
+/**
+ * spum_create_request() - Build a SPU request message header, up to and
+ * including the BD header. Construct the message starting at spu_hdr. Caller
+ * should allocate this buffer in DMA-able memory at least SPU_HEADER_ALLOC_LEN
+ * bytes long.
+ * @spu_hdr: Start of buffer where SPU request header is to be written
+ * @req_opts: SPU request message options
+ * @cipher_parms: Parameters related to cipher algorithm
+ * @hash_parms: Parameters related to hash algorithm
+ * @aead_parms: Parameters related to AEAD operation
+ * @data_size: Length of data to be encrypted or authenticated. If AEAD, does
+ * not include length of AAD.
+
+ * Return: the length of the SPU header in bytes. 0 if an error occurs.
+ */
+u32 spum_create_request(u8 *spu_hdr,
+ struct spu_request_opts *req_opts,
+ struct spu_cipher_parms *cipher_parms,
+ struct spu_hash_parms *hash_parms,
+ struct spu_aead_parms *aead_parms,
+ unsigned int data_size)
+{
+ struct SPUHEADER *spuh;
+ struct BDESC_HEADER *bdesc;
+ struct BD_HEADER *bd;
+
+ u8 *ptr;
+ u32 protocol_bits = 0;
+ u32 cipher_bits = 0;
+ u32 ecf_bits = 0;
+ u8 sctx_words = 0;
+ unsigned int buf_len = 0;
+
+ /* size of the cipher payload */
+ unsigned int cipher_len = hash_parms->prebuf_len + data_size +
+ hash_parms->pad_len;
+
+ /* offset of prebuf or data from end of BD header */
+ unsigned int cipher_offset = aead_parms->assoc_size +
+ aead_parms->iv_len + aead_parms->aad_pad_len;
+
+ /* total size of the DB data (without STAT word padding) */
+ unsigned int real_db_size = spu_real_db_size(aead_parms->assoc_size,
+ aead_parms->iv_len,
+ hash_parms->prebuf_len,
+ data_size,
+ aead_parms->aad_pad_len,
+ aead_parms->data_pad_len,
+ hash_parms->pad_len);
+
+ unsigned int auth_offset = 0;
+ unsigned int offset_iv = 0;
+
+ /* size/offset of the auth payload */
+ unsigned int auth_len;
+
+ auth_len = real_db_size;
+
+ if (req_opts->is_aead && req_opts->is_inbound)
+ cipher_len -= hash_parms->digestsize;
+
+ if (req_opts->is_aead && req_opts->is_inbound)
+ auth_len -= hash_parms->digestsize;
+
+ if ((hash_parms->alg == HASH_ALG_AES) &&
+ (hash_parms->mode == HASH_MODE_XCBC)) {
+ auth_len -= hash_parms->pad_len;
+ cipher_len -= hash_parms->pad_len;
+ }
+
+ flow_log("%s()\n", __func__);
+ flow_log(" in:%u authFirst:%u\n",
+ req_opts->is_inbound, req_opts->auth_first);
+ flow_log(" %s. cipher alg:%u mode:%u type %u\n",
+ spu_alg_name(cipher_parms->alg, cipher_parms->mode),
+ cipher_parms->alg, cipher_parms->mode, cipher_parms->type);
+ flow_log(" key: %d\n", cipher_parms->key_len);
+ flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len);
+ flow_log(" iv: %d\n", cipher_parms->iv_len);
+ flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len);
+ flow_log(" auth alg:%u mode:%u type %u\n",
+ hash_parms->alg, hash_parms->mode, hash_parms->type);
+ flow_log(" digestsize: %u\n", hash_parms->digestsize);
+ flow_log(" authkey: %d\n", hash_parms->key_len);
+ flow_dump(" authkey: ", hash_parms->key_buf, hash_parms->key_len);
+ flow_log(" assoc_size:%u\n", aead_parms->assoc_size);
+ flow_log(" prebuf_len:%u\n", hash_parms->prebuf_len);
+ flow_log(" data_size:%u\n", data_size);
+ flow_log(" hash_pad_len:%u\n", hash_parms->pad_len);
+ flow_log(" real_db_size:%u\n", real_db_size);
+ flow_log(" auth_offset:%u auth_len:%u cipher_offset:%u cipher_len:%u\n",
+ auth_offset, auth_len, cipher_offset, cipher_len);
+ flow_log(" hmac_offset:%u\n", hash_parms->hmac_offset);
+ flow_log(" aead_iv: %u\n", aead_parms->iv_len);
+
+ /* starting out: zero the header (plus some) */
+ ptr = spu_hdr;
+ memset(ptr, 0, sizeof(struct SPUHEADER));
+
+ /* format master header word */
+ /* Do not set the next bit even though the datasheet says to */
+ spuh = (struct SPUHEADER *)ptr;
+ ptr += sizeof(struct SPUHEADER);
+ buf_len += sizeof(struct SPUHEADER);
+
+ spuh->mh.op_code = SPU_CRYPTO_OPERATION_GENERIC;
+ spuh->mh.flags |= (MH_SCTX_PRES | MH_BDESC_PRES | MH_BD_PRES);
+
+ /* Format sctx word 0 (protocol_bits) */
+ sctx_words = 3; /* size in words */
+
+ /* Format sctx word 1 (cipher_bits) */
+ if (req_opts->is_inbound)
+ cipher_bits |= CIPHER_INBOUND;
+ if (req_opts->auth_first)
+ cipher_bits |= CIPHER_ORDER;
+
+ /* Set the crypto parameters in the cipher.flags */
+ cipher_bits |= cipher_parms->alg << CIPHER_ALG_SHIFT;
+ cipher_bits |= cipher_parms->mode << CIPHER_MODE_SHIFT;
+ cipher_bits |= cipher_parms->type << CIPHER_TYPE_SHIFT;
+
+ /* Set the auth parameters in the cipher.flags */
+ cipher_bits |= hash_parms->alg << HASH_ALG_SHIFT;
+ cipher_bits |= hash_parms->mode << HASH_MODE_SHIFT;
+ cipher_bits |= hash_parms->type << HASH_TYPE_SHIFT;
+
+ /*
+ * Format sctx extensions if required, and update main fields if
+ * required)
+ */
+ if (hash_parms->alg) {
+ /* Write the authentication key material if present */
+ if (hash_parms->key_len) {
+ memcpy(ptr, hash_parms->key_buf, hash_parms->key_len);
+ ptr += hash_parms->key_len;
+ buf_len += hash_parms->key_len;
+ sctx_words += hash_parms->key_len / 4;
+ }
+
+ if ((cipher_parms->mode == CIPHER_MODE_GCM) ||
+ (cipher_parms->mode == CIPHER_MODE_CCM))
+ /* unpadded length */
+ offset_iv = aead_parms->assoc_size;
+
+ /* if GCM/CCM we need to write ICV into the payload */
+ if (!req_opts->is_inbound) {
+ if ((cipher_parms->mode == CIPHER_MODE_GCM) ||
+ (cipher_parms->mode == CIPHER_MODE_CCM))
+ ecf_bits |= 1 << INSERT_ICV_SHIFT;
+ } else {
+ ecf_bits |= CHECK_ICV;
+ }
+
+ /* Inform the SPU of the ICV size (in words) */
+ if (hash_parms->digestsize == 64)
+ cipher_bits |= ICV_IS_512;
+ else
+ ecf_bits |=
+ (hash_parms->digestsize / 4) << ICV_SIZE_SHIFT;
+ }
+
+ if (req_opts->bd_suppress)
+ ecf_bits |= BD_SUPPRESS;
+
+ /* copy the encryption keys in the SAD entry */
+ if (cipher_parms->alg) {
+ if (cipher_parms->key_len) {
+ memcpy(ptr, cipher_parms->key_buf,
+ cipher_parms->key_len);
+ ptr += cipher_parms->key_len;
+ buf_len += cipher_parms->key_len;
+ sctx_words += cipher_parms->key_len / 4;
+ }
+
+ /*
+ * if encrypting then set IV size, use SCTX IV unless no IV
+ * given here
+ */
+ if (cipher_parms->iv_buf && cipher_parms->iv_len) {
+ /* Use SCTX IV */
+ ecf_bits |= SCTX_IV;
+
+ /* cipher iv provided so put it in here */
+ memcpy(ptr, cipher_parms->iv_buf, cipher_parms->iv_len);
+
+ ptr += cipher_parms->iv_len;
+ buf_len += cipher_parms->iv_len;
+ sctx_words += cipher_parms->iv_len / 4;
+ }
+ }
+
+ /*
+ * RFC4543 (GMAC/ESP) requires data to be sent as part of AAD
+ * so we need to override the BDESC parameters.
+ */
+ if (req_opts->is_rfc4543) {
+ if (req_opts->is_inbound)
+ data_size -= hash_parms->digestsize;
+ offset_iv = aead_parms->assoc_size + data_size;
+ cipher_len = 0;
+ cipher_offset = offset_iv;
+ auth_len = cipher_offset + aead_parms->data_pad_len;
+ }
+
+ /* write in the total sctx length now that we know it */
+ protocol_bits |= sctx_words;
+
+ /* Endian adjust the SCTX */
+ spuh->sa.proto_flags = cpu_to_be32(protocol_bits);
+ spuh->sa.cipher_flags = cpu_to_be32(cipher_bits);
+ spuh->sa.ecf = cpu_to_be32(ecf_bits);
+
+ /* === create the BDESC section === */
+ bdesc = (struct BDESC_HEADER *)ptr;
+
+ bdesc->offset_mac = cpu_to_be16(auth_offset);
+ bdesc->length_mac = cpu_to_be16(auth_len);
+ bdesc->offset_crypto = cpu_to_be16(cipher_offset);
+ bdesc->length_crypto = cpu_to_be16(cipher_len);
+
+ /*
+ * CCM in SPU-M requires that ICV not be in same 32-bit word as data or
+ * padding. So account for padding as necessary.
+ */
+ if (cipher_parms->mode == CIPHER_MODE_CCM)
+ auth_len += spum_wordalign_padlen(auth_len);
+
+ bdesc->offset_icv = cpu_to_be16(auth_len);
+ bdesc->offset_iv = cpu_to_be16(offset_iv);
+
+ ptr += sizeof(struct BDESC_HEADER);
+ buf_len += sizeof(struct BDESC_HEADER);
+
+ /* === no MFM section === */
+
+ /* === create the BD section === */
+
+ /* add the BD header */
+ bd = (struct BD_HEADER *)ptr;
+ bd->size = cpu_to_be16(real_db_size);
+ bd->prev_length = 0;
+
+ ptr += sizeof(struct BD_HEADER);
+ buf_len += sizeof(struct BD_HEADER);
+
+ packet_dump(" SPU request header: ", spu_hdr, buf_len);
+
+ return buf_len;
+}
+
+/**
+ * spum_cipher_req_init() - Build a SPU request message header, up to and
+ * including the BD header.
+ * @spu_hdr: Start of SPU request header (MH)
+ * @cipher_parms: Parameters that describe the cipher request
+ *
+ * Construct the message starting at spu_hdr. Caller should allocate this buffer
+ * in DMA-able memory at least SPU_HEADER_ALLOC_LEN bytes long.
+ *
+ * Return: the length of the SPU header in bytes. 0 if an error occurs.
+ */
+u16 spum_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms)
+{
+ struct SPUHEADER *spuh;
+ u32 protocol_bits = 0;
+ u32 cipher_bits = 0;
+ u32 ecf_bits = 0;
+ u8 sctx_words = 0;
+ u8 *ptr = spu_hdr;
+
+ flow_log("%s()\n", __func__);
+ flow_log(" cipher alg:%u mode:%u type %u\n", cipher_parms->alg,
+ cipher_parms->mode, cipher_parms->type);
+ flow_log(" cipher_iv_len: %u\n", cipher_parms->iv_len);
+ flow_log(" key: %d\n", cipher_parms->key_len);
+ flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len);
+
+ /* starting out: zero the header (plus some) */
+ memset(spu_hdr, 0, sizeof(struct SPUHEADER));
+ ptr += sizeof(struct SPUHEADER);
+
+ /* format master header word */
+ /* Do not set the next bit even though the datasheet says to */
+ spuh = (struct SPUHEADER *)spu_hdr;
+
+ spuh->mh.op_code = SPU_CRYPTO_OPERATION_GENERIC;
+ spuh->mh.flags |= (MH_SCTX_PRES | MH_BDESC_PRES | MH_BD_PRES);
+
+ /* Format sctx word 0 (protocol_bits) */
+ sctx_words = 3; /* size in words */
+
+ /* copy the encryption keys in the SAD entry */
+ if (cipher_parms->alg) {
+ if (cipher_parms->key_len) {
+ ptr += cipher_parms->key_len;
+ sctx_words += cipher_parms->key_len / 4;
+ }
+
+ /*
+ * if encrypting then set IV size, use SCTX IV unless no IV
+ * given here
+ */
+ if (cipher_parms->iv_len) {
+ /* Use SCTX IV */
+ ecf_bits |= SCTX_IV;
+ ptr += cipher_parms->iv_len;
+ sctx_words += cipher_parms->iv_len / 4;
+ }
+ }
+
+ /* Set the crypto parameters in the cipher.flags */
+ cipher_bits |= cipher_parms->alg << CIPHER_ALG_SHIFT;
+ cipher_bits |= cipher_parms->mode << CIPHER_MODE_SHIFT;
+ cipher_bits |= cipher_parms->type << CIPHER_TYPE_SHIFT;
+
+ /* copy the encryption keys in the SAD entry */
+ if (cipher_parms->alg && cipher_parms->key_len)
+ memcpy(spuh + 1, cipher_parms->key_buf, cipher_parms->key_len);
+
+ /* write in the total sctx length now that we know it */
+ protocol_bits |= sctx_words;
+
+ /* Endian adjust the SCTX */
+ spuh->sa.proto_flags = cpu_to_be32(protocol_bits);
+
+ /* Endian adjust the SCTX */
+ spuh->sa.cipher_flags = cpu_to_be32(cipher_bits);
+ spuh->sa.ecf = cpu_to_be32(ecf_bits);
+
+ packet_dump(" SPU request header: ", spu_hdr,
+ sizeof(struct SPUHEADER));
+
+ return sizeof(struct SPUHEADER) + cipher_parms->key_len +
+ cipher_parms->iv_len + sizeof(struct BDESC_HEADER) +
+ sizeof(struct BD_HEADER);
+}
+
+/**
+ * spum_cipher_req_finish() - Finish building a SPU request message header for a
+ * block cipher request. Assumes much of the header was already filled in at
+ * setkey() time in spu_cipher_req_init().
+ * @spu_hdr: Start of the request message header (MH field)
+ * @spu_req_hdr_len: Length in bytes of the SPU request header
+ * @isInbound: 0 encrypt, 1 decrypt
+ * @cipher_parms: Parameters describing cipher operation to be performed
+ * @update_key: If true, rewrite the cipher key in SCTX
+ * @data_size: Length of the data in the BD field
+ *
+ * Assumes much of the header was already filled in at setkey() time in
+ * spum_cipher_req_init().
+ * spum_cipher_req_init() fills in the encryption key. For RC4, when submitting
+ * a request for a non-first chunk, we use the 260-byte SUPDT field from the
+ * previous response as the key. update_key is true for this case. Unused in all
+ * other cases.
+ */
+void spum_cipher_req_finish(u8 *spu_hdr,
+ u16 spu_req_hdr_len,
+ unsigned int is_inbound,
+ struct spu_cipher_parms *cipher_parms,
+ bool update_key,
+ unsigned int data_size)
+{
+ struct SPUHEADER *spuh;
+ struct BDESC_HEADER *bdesc;
+ struct BD_HEADER *bd;
+ u8 *bdesc_ptr = spu_hdr + spu_req_hdr_len -
+ (sizeof(struct BD_HEADER) + sizeof(struct BDESC_HEADER));
+
+ u32 cipher_bits;
+
+ flow_log("%s()\n", __func__);
+ flow_log(" in: %u\n", is_inbound);
+ flow_log(" cipher alg: %u, cipher_type: %u\n", cipher_parms->alg,
+ cipher_parms->type);
+ if (update_key) {
+ flow_log(" cipher key len: %u\n", cipher_parms->key_len);
+ flow_dump(" key: ", cipher_parms->key_buf,
+ cipher_parms->key_len);
+ }
+
+ /*
+ * In XTS mode, API puts "i" parameter (block tweak) in IV. For
+ * SPU-M, should be in start of the BD; tx_sg_create() copies it there.
+ * IV in SPU msg for SPU-M should be 0, since that's the "j" parameter
+ * (block ctr within larger data unit) - given we can send entire disk
+ * block (<= 4KB) in 1 SPU msg, don't need to use this parameter.
+ */
+ if (cipher_parms->mode == CIPHER_MODE_XTS)
+ memset(cipher_parms->iv_buf, 0, cipher_parms->iv_len);
+
+ flow_log(" iv len: %d\n", cipher_parms->iv_len);
+ flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len);
+ flow_log(" data_size: %u\n", data_size);
+
+ /* format master header word */
+ /* Do not set the next bit even though the datasheet says to */
+ spuh = (struct SPUHEADER *)spu_hdr;
+
+ /* cipher_bits was initialized at setkey time */
+ cipher_bits = be32_to_cpu(spuh->sa.cipher_flags);
+
+ /* Format sctx word 1 (cipher_bits) */
+ if (is_inbound)
+ cipher_bits |= CIPHER_INBOUND;
+ else
+ cipher_bits &= ~CIPHER_INBOUND;
+
+ /* update encryption key for RC4 on non-first chunk */
+ if (update_key) {
+ spuh->sa.cipher_flags |=
+ cipher_parms->type << CIPHER_TYPE_SHIFT;
+ memcpy(spuh + 1, cipher_parms->key_buf, cipher_parms->key_len);
+ }
+
+ if (cipher_parms->alg && cipher_parms->iv_buf && cipher_parms->iv_len)
+ /* cipher iv provided so put it in here */
+ memcpy(bdesc_ptr - cipher_parms->iv_len, cipher_parms->iv_buf,
+ cipher_parms->iv_len);
+
+ spuh->sa.cipher_flags = cpu_to_be32(cipher_bits);
+
+ /* === create the BDESC section === */
+ bdesc = (struct BDESC_HEADER *)bdesc_ptr;
+ bdesc->offset_mac = 0;
+ bdesc->length_mac = 0;
+ bdesc->offset_crypto = 0;
+
+ /* XTS mode, data_size needs to include tweak parameter */
+ if (cipher_parms->mode == CIPHER_MODE_XTS)
+ bdesc->length_crypto = cpu_to_be16(data_size +
+ SPU_XTS_TWEAK_SIZE);
+ else
+ bdesc->length_crypto = cpu_to_be16(data_size);
+
+ bdesc->offset_icv = 0;
+ bdesc->offset_iv = 0;
+
+ /* === no MFM section === */
+
+ /* === create the BD section === */
+ /* add the BD header */
+ bd = (struct BD_HEADER *)(bdesc_ptr + sizeof(struct BDESC_HEADER));
+ bd->size = cpu_to_be16(data_size);
+
+ /* XTS mode, data_size needs to include tweak parameter */
+ if (cipher_parms->mode == CIPHER_MODE_XTS)
+ bd->size = cpu_to_be16(data_size + SPU_XTS_TWEAK_SIZE);
+ else
+ bd->size = cpu_to_be16(data_size);
+
+ bd->prev_length = 0;
+
+ packet_dump(" SPU request header: ", spu_hdr, spu_req_hdr_len);
+}
+
+/**
+ * spum_request_pad() - Create pad bytes at the end of the data.
+ * @pad_start: Start of buffer where pad bytes are to be written
+ * @gcm_ccm_padding: length of GCM/CCM padding, in bytes
+ * @hash_pad_len: Number of bytes of padding extend data to full block
+ * @auth_alg: authentication algorithm
+ * @auth_mode: authentication mode
+ * @total_sent: length inserted at end of hash pad
+ * @status_padding: Number of bytes of padding to align STATUS word
+ *
+ * There may be three forms of pad:
+ * 1. GCM/CCM pad - for GCM/CCM mode ciphers, pad to 16-byte alignment
+ * 2. hash pad - pad to a block length, with 0x80 data terminator and
+ * size at the end
+ * 3. STAT pad - to ensure the STAT field is 4-byte aligned
+ */
+void spum_request_pad(u8 *pad_start,
+ u32 gcm_ccm_padding,
+ u32 hash_pad_len,
+ enum hash_alg auth_alg,
+ enum hash_mode auth_mode,
+ unsigned int total_sent, u32 status_padding)
+{
+ u8 *ptr = pad_start;
+
+ /* fix data alignent for GCM/CCM */
+ if (gcm_ccm_padding > 0) {
+ flow_log(" GCM: padding to 16 byte alignment: %u bytes\n",
+ gcm_ccm_padding);
+ memset(ptr, 0, gcm_ccm_padding);
+ ptr += gcm_ccm_padding;
+ }
+
+ if (hash_pad_len > 0) {
+ /* clear the padding section */
+ memset(ptr, 0, hash_pad_len);
+
+ if ((auth_alg == HASH_ALG_AES) &&
+ (auth_mode == HASH_MODE_XCBC)) {
+ /* AES/XCBC just requires padding to be 0s */
+ ptr += hash_pad_len;
+ } else {
+ /* terminate the data */
+ *ptr = 0x80;
+ ptr += (hash_pad_len - sizeof(u64));
+
+ /* add the size at the end as required per alg */
+ if (auth_alg == HASH_ALG_MD5)
+ *(u64 *)ptr = cpu_to_le64((u64)total_sent * 8);
+ else /* SHA1, SHA2-224, SHA2-256 */
+ *(u64 *)ptr = cpu_to_be64((u64)total_sent * 8);
+ ptr += sizeof(u64);
+ }
+ }
+
+ /* pad to a 4byte alignment for STAT */
+ if (status_padding > 0) {
+ flow_log(" STAT: padding to 4 byte alignment: %u bytes\n",
+ status_padding);
+
+ memset(ptr, 0, status_padding);
+ ptr += status_padding;
+ }
+}
+
+/**
+ * spum_xts_tweak_in_payload() - Indicate that SPUM DOES place the XTS tweak
+ * field in the packet payload (rather than using IV)
+ *
+ * Return: 1
+ */
+u8 spum_xts_tweak_in_payload(void)
+{
+ return 1;
+}
+
+/**
+ * spum_tx_status_len() - Return the length of the STATUS field in a SPU
+ * response message.
+ *
+ * Return: Length of STATUS field in bytes.
+ */
+u8 spum_tx_status_len(void)
+{
+ return SPU_TX_STATUS_LEN;
+}
+
+/**
+ * spum_rx_status_len() - Return the length of the STATUS field in a SPU
+ * response message.
+ *
+ * Return: Length of STATUS field in bytes.
+ */
+u8 spum_rx_status_len(void)
+{
+ return SPU_RX_STATUS_LEN;
+}
+
+/**
+ * spum_status_process() - Process the status from a SPU response message.
+ * @statp: start of STATUS word
+ * Return:
+ * 0 - if status is good and response should be processed
+ * !0 - status indicates an error and response is invalid
+ */
+int spum_status_process(u8 *statp)
+{
+ u32 status;
+
+ status = __be32_to_cpu(*(__be32 *)statp);
+ flow_log("SPU response STATUS %#08x\n", status);
+ if (status & SPU_STATUS_ERROR_FLAG) {
+ pr_err("%s() Warning: Error result from SPU: %#08x\n",
+ __func__, status);
+ if (status & SPU_STATUS_INVALID_ICV)
+ return SPU_INVALID_ICV;
+ return -EBADMSG;
+ }
+ return 0;
+}
+
+/**
+ * spum_ccm_update_iv() - Update the IV as per the requirements for CCM mode.
+ *
+ * @digestsize: Digest size of this request
+ * @cipher_parms: (pointer to) cipher parmaeters, includes IV buf & IV len
+ * @assoclen: Length of AAD data
+ * @chunksize: length of input data to be sent in this req
+ * @is_encrypt: true if this is an output/encrypt operation
+ * @is_esp: true if this is an ESP / RFC4309 operation
+ *
+ */
+void spum_ccm_update_iv(unsigned int digestsize,
+ struct spu_cipher_parms *cipher_parms,
+ unsigned int assoclen,
+ unsigned int chunksize,
+ bool is_encrypt,
+ bool is_esp)
+{
+ u8 L; /* L from CCM algorithm, length of plaintext data */
+ u8 mprime; /* M' from CCM algo, (M - 2) / 2, where M=authsize */
+ u8 adata;
+
+ if (cipher_parms->iv_len != CCM_AES_IV_SIZE) {
+ pr_err("%s(): Invalid IV len %d for CCM mode, should be %d\n",
+ __func__, cipher_parms->iv_len, CCM_AES_IV_SIZE);
+ return;
+ }
+
+ /*
+ * IV needs to be formatted as follows:
+ *
+ * | Byte 0 | Bytes 1 - N | Bytes (N+1) - 15 |
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits 7 - 0 | Bits 7 - 0 |
+ * | 0 |Ad?|(M - 2) / 2| L - 1 | Nonce | Plaintext Length |
+ *
+ * Ad? = 1 if AAD present, 0 if not present
+ * M = size of auth field, 8, 12, or 16 bytes (SPU-M) -or-
+ * 4, 6, 8, 10, 12, 14, 16 bytes (SPU2)
+ * L = Size of Plaintext Length field; Nonce size = 15 - L
+ *
+ * It appears that the crypto API already expects the L-1 portion
+ * to be set in the first byte of the IV, which implicitly determines
+ * the nonce size, and also fills in the nonce. But the other bits
+ * in byte 0 as well as the plaintext length need to be filled in.
+ *
+ * In rfc4309/esp mode, L is not already in the supplied IV and
+ * we need to fill it in, as well as move the IV data to be after
+ * the salt
+ */
+ if (is_esp) {
+ L = CCM_ESP_L_VALUE; /* RFC4309 has fixed L */
+ } else {
+ /* L' = plaintext length - 1 so Plaintext length is L' + 1 */
+ L = ((cipher_parms->iv_buf[0] & CCM_B0_L_PRIME) >>
+ CCM_B0_L_PRIME_SHIFT) + 1;
+ }
+
+ mprime = (digestsize - 2) >> 1; /* M' = (M - 2) / 2 */
+ adata = (assoclen > 0); /* adata = 1 if any associated data */
+
+ cipher_parms->iv_buf[0] = (adata << CCM_B0_ADATA_SHIFT) |
+ (mprime << CCM_B0_M_PRIME_SHIFT) |
+ ((L - 1) << CCM_B0_L_PRIME_SHIFT);
+
+ /* Nonce is already filled in by crypto API, and is 15 - L bytes */
+
+ /* Don't include digest in plaintext size when decrypting */
+ if (!is_encrypt)
+ chunksize -= digestsize;
+
+ /* Fill in length of plaintext, formatted to be L bytes long */
+ format_value_ccm(chunksize, &cipher_parms->iv_buf[15 - L + 1], L);
+}
+
+/**
+ * spum_wordalign_padlen() - Given the length of a data field, determine the
+ * padding required to align the data following this field on a 4-byte boundary.
+ * @data_size: length of data field in bytes
+ *
+ * Return: length of status field padding, in bytes
+ */
+u32 spum_wordalign_padlen(u32 data_size)
+{
+ return ((data_size + 3) & ~3) - data_size;
+}
diff --git a/drivers/crypto/bcm/spu.h b/drivers/crypto/bcm/spu.h
new file mode 100644
index 0000000..e2eb925
--- /dev/null
+++ b/drivers/crypto/bcm/spu.h
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+/*
+ * This file contains the definition of SPU messages. There are currently two
+ * SPU message formats: SPU-M and SPU2. The hardware uses different values to
+ * identify the same things in SPU-M vs SPU2. So this file defines values that
+ * are hardware independent. Software can use these values for any version of
+ * SPU hardware. These values are used in APIs in spu.c. Functions internal to
+ * spu.c and spu2.c convert these to hardware-specific values.
+ */
+
+#ifndef _SPU_H
+#define _SPU_H
+
+#include <linux/types.h>
+#include <linux/scatterlist.h>
+#include <crypto/sha.h>
+
+enum spu_cipher_alg {
+ CIPHER_ALG_NONE = 0x0,
+ CIPHER_ALG_RC4 = 0x1,
+ CIPHER_ALG_DES = 0x2,
+ CIPHER_ALG_3DES = 0x3,
+ CIPHER_ALG_AES = 0x4,
+ CIPHER_ALG_LAST = 0x5
+};
+
+enum spu_cipher_mode {
+ CIPHER_MODE_NONE = 0x0,
+ CIPHER_MODE_ECB = 0x0,
+ CIPHER_MODE_CBC = 0x1,
+ CIPHER_MODE_OFB = 0x2,
+ CIPHER_MODE_CFB = 0x3,
+ CIPHER_MODE_CTR = 0x4,
+ CIPHER_MODE_CCM = 0x5,
+ CIPHER_MODE_GCM = 0x6,
+ CIPHER_MODE_XTS = 0x7,
+ CIPHER_MODE_LAST = 0x8
+};
+
+enum spu_cipher_type {
+ CIPHER_TYPE_NONE = 0x0,
+ CIPHER_TYPE_DES = 0x0,
+ CIPHER_TYPE_3DES = 0x0,
+ CIPHER_TYPE_INIT = 0x0, /* used for ARC4 */
+ CIPHER_TYPE_AES128 = 0x0,
+ CIPHER_TYPE_AES192 = 0x1,
+ CIPHER_TYPE_UPDT = 0x1, /* used for ARC4 */
+ CIPHER_TYPE_AES256 = 0x2,
+};
+
+enum hash_alg {
+ HASH_ALG_NONE = 0x0,
+ HASH_ALG_MD5 = 0x1,
+ HASH_ALG_SHA1 = 0x2,
+ HASH_ALG_SHA224 = 0x3,
+ HASH_ALG_SHA256 = 0x4,
+ HASH_ALG_AES = 0x5,
+ HASH_ALG_SHA384 = 0x6,
+ HASH_ALG_SHA512 = 0x7,
+ /* Keep SHA3 algorithms at the end always */
+ HASH_ALG_SHA3_224 = 0x8,
+ HASH_ALG_SHA3_256 = 0x9,
+ HASH_ALG_SHA3_384 = 0xa,
+ HASH_ALG_SHA3_512 = 0xb,
+ HASH_ALG_LAST
+};
+
+enum hash_mode {
+ HASH_MODE_NONE = 0x0,
+ HASH_MODE_HASH = 0x0,
+ HASH_MODE_XCBC = 0x0,
+ HASH_MODE_CMAC = 0x1,
+ HASH_MODE_CTXT = 0x1,
+ HASH_MODE_HMAC = 0x2,
+ HASH_MODE_RABIN = 0x4,
+ HASH_MODE_FHMAC = 0x6,
+ HASH_MODE_CCM = 0x5,
+ HASH_MODE_GCM = 0x6,
+};
+
+enum hash_type {
+ HASH_TYPE_NONE = 0x0,
+ HASH_TYPE_FULL = 0x0,
+ HASH_TYPE_INIT = 0x1,
+ HASH_TYPE_UPDT = 0x2,
+ HASH_TYPE_FIN = 0x3,
+ HASH_TYPE_AES128 = 0x0,
+ HASH_TYPE_AES192 = 0x1,
+ HASH_TYPE_AES256 = 0x2
+};
+
+enum aead_type {
+ AES_CCM,
+ AES_GCM,
+ AUTHENC,
+ AEAD_TYPE_LAST
+};
+
+extern char *hash_alg_name[HASH_ALG_LAST];
+extern char *aead_alg_name[AEAD_TYPE_LAST];
+
+struct spu_request_opts {
+ bool is_inbound;
+ bool auth_first;
+ bool is_aead;
+ bool is_esp;
+ bool bd_suppress;
+ bool is_rfc4543;
+};
+
+struct spu_cipher_parms {
+ enum spu_cipher_alg alg;
+ enum spu_cipher_mode mode;
+ enum spu_cipher_type type;
+ u8 *key_buf;
+ u16 key_len;
+ /* iv_buf and iv_len include salt, if applicable */
+ u8 *iv_buf;
+ u16 iv_len;
+};
+
+struct spu_hash_parms {
+ enum hash_alg alg;
+ enum hash_mode mode;
+ enum hash_type type;
+ u8 digestsize;
+ u8 *key_buf;
+ u16 key_len;
+ u16 prebuf_len;
+ u16 hmac_offset;
+ /* length of hash pad. signed, needs to handle roll-overs */
+ int pad_len;
+};
+
+struct spu_aead_parms {
+ u32 assoc_size;
+ u16 iv_len; /* length of IV field between assoc data and data */
+ u8 aad_pad_len; /* For AES GCM/CCM, length of padding after AAD */
+ u8 data_pad_len;/* For AES GCM/CCM, length of padding after data */
+ bool return_iv; /* True if SPU should return an IV */
+ u32 ret_iv_len; /* Length in bytes of returned IV */
+ u32 ret_iv_off; /* Offset into full IV if partial IV returned */
+};
+
+/************** SPU sizes ***************/
+
+#define SPU_RX_STATUS_LEN 4
+
+/* Max length of padding for 4-byte alignment of STATUS field */
+#define SPU_STAT_PAD_MAX 4
+
+/* Max length of pad fragment. 4 is for 4-byte alignment of STATUS field */
+#define SPU_PAD_LEN_MAX (SPU_GCM_CCM_ALIGN + MAX_HASH_BLOCK_SIZE + \
+ SPU_STAT_PAD_MAX)
+
+/* GCM and CCM require 16-byte alignment */
+#define SPU_GCM_CCM_ALIGN 16
+
+/* Length up SUPDT field in SPU response message for RC4 */
+#define SPU_SUPDT_LEN 260
+
+/* SPU status error codes. These used as common error codes across all
+ * SPU variants.
+ */
+#define SPU_INVALID_ICV 1
+
+/* Indicates no limit to the length of the payload in a SPU message */
+#define SPU_MAX_PAYLOAD_INF 0xFFFFFFFF
+
+/* Size of XTS tweak ("i" parameter), in bytes */
+#define SPU_XTS_TWEAK_SIZE 16
+
+/* CCM B_0 field definitions, common for SPU-M and SPU2 */
+#define CCM_B0_ADATA 0x40
+#define CCM_B0_ADATA_SHIFT 6
+#define CCM_B0_M_PRIME 0x38
+#define CCM_B0_M_PRIME_SHIFT 3
+#define CCM_B0_L_PRIME 0x07
+#define CCM_B0_L_PRIME_SHIFT 0
+#define CCM_ESP_L_VALUE 4
+
+/**
+ * spu_req_incl_icv() - Return true if SPU request message should include the
+ * ICV as a separate buffer.
+ * @cipher_mode: the cipher mode being requested
+ * @is_encrypt: true if encrypting. false if decrypting.
+ *
+ * Return: true if ICV to be included as separate buffer
+ */
+static __always_inline bool spu_req_incl_icv(enum spu_cipher_mode cipher_mode,
+ bool is_encrypt)
+{
+ if ((cipher_mode == CIPHER_MODE_GCM) && !is_encrypt)
+ return true;
+ if ((cipher_mode == CIPHER_MODE_CCM) && !is_encrypt)
+ return true;
+
+ return false;
+}
+
+static __always_inline u32 spu_real_db_size(u32 assoc_size,
+ u32 aead_iv_buf_len,
+ u32 prebuf_len,
+ u32 data_size,
+ u32 aad_pad_len,
+ u32 gcm_pad_len,
+ u32 hash_pad_len)
+{
+ return assoc_size + aead_iv_buf_len + prebuf_len + data_size +
+ aad_pad_len + gcm_pad_len + hash_pad_len;
+}
+
+/************** SPU Functions Prototypes **************/
+
+void spum_dump_msg_hdr(u8 *buf, unsigned int buf_len);
+
+u32 spum_ns2_ctx_max_payload(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ unsigned int blocksize);
+u32 spum_nsp_ctx_max_payload(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ unsigned int blocksize);
+u32 spum_payload_length(u8 *spu_hdr);
+u16 spum_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash);
+u16 spum_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode,
+ u32 chunksize, u16 hash_block_size);
+u32 spum_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode,
+ unsigned int data_size);
+u32 spum_assoc_resp_len(enum spu_cipher_mode cipher_mode,
+ unsigned int assoc_len, unsigned int iv_len,
+ bool is_encrypt);
+u8 spum_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len);
+bool spu_req_incl_icv(enum spu_cipher_mode cipher_mode, bool is_encrypt);
+enum hash_type spum_hash_type(u32 src_sent);
+u32 spum_digest_size(u32 alg_digest_size, enum hash_alg alg,
+ enum hash_type htype);
+
+u32 spum_create_request(u8 *spu_hdr,
+ struct spu_request_opts *req_opts,
+ struct spu_cipher_parms *cipher_parms,
+ struct spu_hash_parms *hash_parms,
+ struct spu_aead_parms *aead_parms,
+ unsigned int data_size);
+
+u16 spum_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms);
+
+void spum_cipher_req_finish(u8 *spu_hdr,
+ u16 spu_req_hdr_len,
+ unsigned int is_inbound,
+ struct spu_cipher_parms *cipher_parms,
+ bool update_key,
+ unsigned int data_size);
+
+void spum_request_pad(u8 *pad_start,
+ u32 gcm_padding,
+ u32 hash_pad_len,
+ enum hash_alg auth_alg,
+ enum hash_mode auth_mode,
+ unsigned int total_sent, u32 status_padding);
+
+u8 spum_xts_tweak_in_payload(void);
+u8 spum_tx_status_len(void);
+u8 spum_rx_status_len(void);
+int spum_status_process(u8 *statp);
+
+void spum_ccm_update_iv(unsigned int digestsize,
+ struct spu_cipher_parms *cipher_parms,
+ unsigned int assoclen,
+ unsigned int chunksize,
+ bool is_encrypt,
+ bool is_esp);
+u32 spum_wordalign_padlen(u32 data_size);
+#endif
diff --git a/drivers/crypto/bcm/spu2.c b/drivers/crypto/bcm/spu2.c
new file mode 100644
index 0000000..d7b44b6
--- /dev/null
+++ b/drivers/crypto/bcm/spu2.c
@@ -0,0 +1,1402 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+/*
+ * This file works with the SPU2 version of the SPU. SPU2 has different message
+ * formats than the previous version of the SPU. All SPU message format
+ * differences should be hidden in the spux.c,h files.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "util.h"
+#include "spu.h"
+#include "spu2.h"
+
+#define SPU2_TX_STATUS_LEN 0 /* SPU2 has no STATUS in input packet */
+
+/*
+ * Controlled by pkt_stat_cnt field in CRYPTO_SS_SPU0_CORE_SPU2_CONTROL0
+ * register. Defaults to 2.
+ */
+#define SPU2_RX_STATUS_LEN 2
+
+enum spu2_proto_sel {
+ SPU2_PROTO_RESV = 0,
+ SPU2_MACSEC_SECTAG8_ECB = 1,
+ SPU2_MACSEC_SECTAG8_SCB = 2,
+ SPU2_MACSEC_SECTAG16 = 3,
+ SPU2_MACSEC_SECTAG16_8_XPN = 4,
+ SPU2_IPSEC = 5,
+ SPU2_IPSEC_ESN = 6,
+ SPU2_TLS_CIPHER = 7,
+ SPU2_TLS_AEAD = 8,
+ SPU2_DTLS_CIPHER = 9,
+ SPU2_DTLS_AEAD = 10
+};
+
+char *spu2_cipher_type_names[] = { "None", "AES128", "AES192", "AES256",
+ "DES", "3DES"
+};
+
+char *spu2_cipher_mode_names[] = { "ECB", "CBC", "CTR", "CFB", "OFB", "XTS",
+ "CCM", "GCM"
+};
+
+char *spu2_hash_type_names[] = { "None", "AES128", "AES192", "AES256",
+ "Reserved", "Reserved", "MD5", "SHA1", "SHA224", "SHA256", "SHA384",
+ "SHA512", "SHA512/224", "SHA512/256", "SHA3-224", "SHA3-256",
+ "SHA3-384", "SHA3-512"
+};
+
+char *spu2_hash_mode_names[] = { "CMAC", "CBC-MAC", "XCBC-MAC", "HMAC",
+ "Rabin", "CCM", "GCM", "Reserved"
+};
+
+static char *spu2_ciph_type_name(enum spu2_cipher_type cipher_type)
+{
+ if (cipher_type >= SPU2_CIPHER_TYPE_LAST)
+ return "Reserved";
+ return spu2_cipher_type_names[cipher_type];
+}
+
+static char *spu2_ciph_mode_name(enum spu2_cipher_mode cipher_mode)
+{
+ if (cipher_mode >= SPU2_CIPHER_MODE_LAST)
+ return "Reserved";
+ return spu2_cipher_mode_names[cipher_mode];
+}
+
+static char *spu2_hash_type_name(enum spu2_hash_type hash_type)
+{
+ if (hash_type >= SPU2_HASH_TYPE_LAST)
+ return "Reserved";
+ return spu2_hash_type_names[hash_type];
+}
+
+static char *spu2_hash_mode_name(enum spu2_hash_mode hash_mode)
+{
+ if (hash_mode >= SPU2_HASH_MODE_LAST)
+ return "Reserved";
+ return spu2_hash_mode_names[hash_mode];
+}
+
+/*
+ * Convert from a software cipher mode value to the corresponding value
+ * for SPU2.
+ */
+static int spu2_cipher_mode_xlate(enum spu_cipher_mode cipher_mode,
+ enum spu2_cipher_mode *spu2_mode)
+{
+ switch (cipher_mode) {
+ case CIPHER_MODE_ECB:
+ *spu2_mode = SPU2_CIPHER_MODE_ECB;
+ break;
+ case CIPHER_MODE_CBC:
+ *spu2_mode = SPU2_CIPHER_MODE_CBC;
+ break;
+ case CIPHER_MODE_OFB:
+ *spu2_mode = SPU2_CIPHER_MODE_OFB;
+ break;
+ case CIPHER_MODE_CFB:
+ *spu2_mode = SPU2_CIPHER_MODE_CFB;
+ break;
+ case CIPHER_MODE_CTR:
+ *spu2_mode = SPU2_CIPHER_MODE_CTR;
+ break;
+ case CIPHER_MODE_CCM:
+ *spu2_mode = SPU2_CIPHER_MODE_CCM;
+ break;
+ case CIPHER_MODE_GCM:
+ *spu2_mode = SPU2_CIPHER_MODE_GCM;
+ break;
+ case CIPHER_MODE_XTS:
+ *spu2_mode = SPU2_CIPHER_MODE_XTS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * spu2_cipher_xlate() - Convert a cipher {alg/mode/type} triple to a SPU2
+ * cipher type and mode.
+ * @cipher_alg: [in] cipher algorithm value from software enumeration
+ * @cipher_mode: [in] cipher mode value from software enumeration
+ * @cipher_type: [in] cipher type value from software enumeration
+ * @spu2_type: [out] cipher type value used by spu2 hardware
+ * @spu2_mode: [out] cipher mode value used by spu2 hardware
+ *
+ * Return: 0 if successful
+ */
+static int spu2_cipher_xlate(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ enum spu_cipher_type cipher_type,
+ enum spu2_cipher_type *spu2_type,
+ enum spu2_cipher_mode *spu2_mode)
+{
+ int err;
+
+ err = spu2_cipher_mode_xlate(cipher_mode, spu2_mode);
+ if (err) {
+ flow_log("Invalid cipher mode %d\n", cipher_mode);
+ return err;
+ }
+
+ switch (cipher_alg) {
+ case CIPHER_ALG_NONE:
+ *spu2_type = SPU2_CIPHER_TYPE_NONE;
+ break;
+ case CIPHER_ALG_RC4:
+ /* SPU2 does not support RC4 */
+ err = -EINVAL;
+ *spu2_type = SPU2_CIPHER_TYPE_NONE;
+ break;
+ case CIPHER_ALG_DES:
+ *spu2_type = SPU2_CIPHER_TYPE_DES;
+ break;
+ case CIPHER_ALG_3DES:
+ *spu2_type = SPU2_CIPHER_TYPE_3DES;
+ break;
+ case CIPHER_ALG_AES:
+ switch (cipher_type) {
+ case CIPHER_TYPE_AES128:
+ *spu2_type = SPU2_CIPHER_TYPE_AES128;
+ break;
+ case CIPHER_TYPE_AES192:
+ *spu2_type = SPU2_CIPHER_TYPE_AES192;
+ break;
+ case CIPHER_TYPE_AES256:
+ *spu2_type = SPU2_CIPHER_TYPE_AES256;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ break;
+ case CIPHER_ALG_LAST:
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err)
+ flow_log("Invalid cipher alg %d or type %d\n",
+ cipher_alg, cipher_type);
+ return err;
+}
+
+/*
+ * Convert from a software hash mode value to the corresponding value
+ * for SPU2. Note that HASH_MODE_NONE and HASH_MODE_XCBC have the same value.
+ */
+static int spu2_hash_mode_xlate(enum hash_mode hash_mode,
+ enum spu2_hash_mode *spu2_mode)
+{
+ switch (hash_mode) {
+ case HASH_MODE_XCBC:
+ *spu2_mode = SPU2_HASH_MODE_XCBC_MAC;
+ break;
+ case HASH_MODE_CMAC:
+ *spu2_mode = SPU2_HASH_MODE_CMAC;
+ break;
+ case HASH_MODE_HMAC:
+ *spu2_mode = SPU2_HASH_MODE_HMAC;
+ break;
+ case HASH_MODE_CCM:
+ *spu2_mode = SPU2_HASH_MODE_CCM;
+ break;
+ case HASH_MODE_GCM:
+ *spu2_mode = SPU2_HASH_MODE_GCM;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * spu2_hash_xlate() - Convert a hash {alg/mode/type} triple to a SPU2 hash type
+ * and mode.
+ * @hash_alg: [in] hash algorithm value from software enumeration
+ * @hash_mode: [in] hash mode value from software enumeration
+ * @hash_type: [in] hash type value from software enumeration
+ * @ciph_type: [in] cipher type value from software enumeration
+ * @spu2_type: [out] hash type value used by SPU2 hardware
+ * @spu2_mode: [out] hash mode value used by SPU2 hardware
+ *
+ * Return: 0 if successful
+ */
+static int
+spu2_hash_xlate(enum hash_alg hash_alg, enum hash_mode hash_mode,
+ enum hash_type hash_type, enum spu_cipher_type ciph_type,
+ enum spu2_hash_type *spu2_type, enum spu2_hash_mode *spu2_mode)
+{
+ int err;
+
+ err = spu2_hash_mode_xlate(hash_mode, spu2_mode);
+ if (err) {
+ flow_log("Invalid hash mode %d\n", hash_mode);
+ return err;
+ }
+
+ switch (hash_alg) {
+ case HASH_ALG_NONE:
+ *spu2_type = SPU2_HASH_TYPE_NONE;
+ break;
+ case HASH_ALG_MD5:
+ *spu2_type = SPU2_HASH_TYPE_MD5;
+ break;
+ case HASH_ALG_SHA1:
+ *spu2_type = SPU2_HASH_TYPE_SHA1;
+ break;
+ case HASH_ALG_SHA224:
+ *spu2_type = SPU2_HASH_TYPE_SHA224;
+ break;
+ case HASH_ALG_SHA256:
+ *spu2_type = SPU2_HASH_TYPE_SHA256;
+ break;
+ case HASH_ALG_SHA384:
+ *spu2_type = SPU2_HASH_TYPE_SHA384;
+ break;
+ case HASH_ALG_SHA512:
+ *spu2_type = SPU2_HASH_TYPE_SHA512;
+ break;
+ case HASH_ALG_AES:
+ switch (ciph_type) {
+ case CIPHER_TYPE_AES128:
+ *spu2_type = SPU2_HASH_TYPE_AES128;
+ break;
+ case CIPHER_TYPE_AES192:
+ *spu2_type = SPU2_HASH_TYPE_AES192;
+ break;
+ case CIPHER_TYPE_AES256:
+ *spu2_type = SPU2_HASH_TYPE_AES256;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ break;
+ case HASH_ALG_SHA3_224:
+ *spu2_type = SPU2_HASH_TYPE_SHA3_224;
+ break;
+ case HASH_ALG_SHA3_256:
+ *spu2_type = SPU2_HASH_TYPE_SHA3_256;
+ break;
+ case HASH_ALG_SHA3_384:
+ *spu2_type = SPU2_HASH_TYPE_SHA3_384;
+ break;
+ case HASH_ALG_SHA3_512:
+ *spu2_type = SPU2_HASH_TYPE_SHA3_512;
+ case HASH_ALG_LAST:
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err)
+ flow_log("Invalid hash alg %d or type %d\n",
+ hash_alg, hash_type);
+ return err;
+}
+
+/* Dump FMD ctrl0. The ctrl0 input is in host byte order */
+static void spu2_dump_fmd_ctrl0(u64 ctrl0)
+{
+ enum spu2_cipher_type ciph_type;
+ enum spu2_cipher_mode ciph_mode;
+ enum spu2_hash_type hash_type;
+ enum spu2_hash_mode hash_mode;
+ char *ciph_name;
+ char *ciph_mode_name;
+ char *hash_name;
+ char *hash_mode_name;
+ u8 cfb;
+ u8 proto;
+
+ packet_log(" FMD CTRL0 %#16llx\n", ctrl0);
+ if (ctrl0 & SPU2_CIPH_ENCRYPT_EN)
+ packet_log(" encrypt\n");
+ else
+ packet_log(" decrypt\n");
+
+ ciph_type = (ctrl0 & SPU2_CIPH_TYPE) >> SPU2_CIPH_TYPE_SHIFT;
+ ciph_name = spu2_ciph_type_name(ciph_type);
+ packet_log(" Cipher type: %s\n", ciph_name);
+
+ if (ciph_type != SPU2_CIPHER_TYPE_NONE) {
+ ciph_mode = (ctrl0 & SPU2_CIPH_MODE) >> SPU2_CIPH_MODE_SHIFT;
+ ciph_mode_name = spu2_ciph_mode_name(ciph_mode);
+ packet_log(" Cipher mode: %s\n", ciph_mode_name);
+ }
+
+ cfb = (ctrl0 & SPU2_CFB_MASK) >> SPU2_CFB_MASK_SHIFT;
+ packet_log(" CFB %#x\n", cfb);
+
+ proto = (ctrl0 & SPU2_PROTO_SEL) >> SPU2_PROTO_SEL_SHIFT;
+ packet_log(" protocol %#x\n", proto);
+
+ if (ctrl0 & SPU2_HASH_FIRST)
+ packet_log(" hash first\n");
+ else
+ packet_log(" cipher first\n");
+
+ if (ctrl0 & SPU2_CHK_TAG)
+ packet_log(" check tag\n");
+
+ hash_type = (ctrl0 & SPU2_HASH_TYPE) >> SPU2_HASH_TYPE_SHIFT;
+ hash_name = spu2_hash_type_name(hash_type);
+ packet_log(" Hash type: %s\n", hash_name);
+
+ if (hash_type != SPU2_HASH_TYPE_NONE) {
+ hash_mode = (ctrl0 & SPU2_HASH_MODE) >> SPU2_HASH_MODE_SHIFT;
+ hash_mode_name = spu2_hash_mode_name(hash_mode);
+ packet_log(" Hash mode: %s\n", hash_mode_name);
+ }
+
+ if (ctrl0 & SPU2_CIPH_PAD_EN) {
+ packet_log(" Cipher pad: %#2llx\n",
+ (ctrl0 & SPU2_CIPH_PAD) >> SPU2_CIPH_PAD_SHIFT);
+ }
+}
+
+/* Dump FMD ctrl1. The ctrl1 input is in host byte order */
+static void spu2_dump_fmd_ctrl1(u64 ctrl1)
+{
+ u8 hash_key_len;
+ u8 ciph_key_len;
+ u8 ret_iv_len;
+ u8 iv_offset;
+ u8 iv_len;
+ u8 hash_tag_len;
+ u8 ret_md;
+
+ packet_log(" FMD CTRL1 %#16llx\n", ctrl1);
+ if (ctrl1 & SPU2_TAG_LOC)
+ packet_log(" Tag after payload\n");
+
+ packet_log(" Msg includes ");
+ if (ctrl1 & SPU2_HAS_FR_DATA)
+ packet_log("FD ");
+ if (ctrl1 & SPU2_HAS_AAD1)
+ packet_log("AAD1 ");
+ if (ctrl1 & SPU2_HAS_NAAD)
+ packet_log("NAAD ");
+ if (ctrl1 & SPU2_HAS_AAD2)
+ packet_log("AAD2 ");
+ if (ctrl1 & SPU2_HAS_ESN)
+ packet_log("ESN ");
+ packet_log("\n");
+
+ hash_key_len = (ctrl1 & SPU2_HASH_KEY_LEN) >> SPU2_HASH_KEY_LEN_SHIFT;
+ packet_log(" Hash key len %u\n", hash_key_len);
+
+ ciph_key_len = (ctrl1 & SPU2_CIPH_KEY_LEN) >> SPU2_CIPH_KEY_LEN_SHIFT;
+ packet_log(" Cipher key len %u\n", ciph_key_len);
+
+ if (ctrl1 & SPU2_GENIV)
+ packet_log(" Generate IV\n");
+
+ if (ctrl1 & SPU2_HASH_IV)
+ packet_log(" IV included in hash\n");
+
+ if (ctrl1 & SPU2_RET_IV)
+ packet_log(" Return IV in output before payload\n");
+
+ ret_iv_len = (ctrl1 & SPU2_RET_IV_LEN) >> SPU2_RET_IV_LEN_SHIFT;
+ packet_log(" Length of returned IV %u bytes\n",
+ ret_iv_len ? ret_iv_len : 16);
+
+ iv_offset = (ctrl1 & SPU2_IV_OFFSET) >> SPU2_IV_OFFSET_SHIFT;
+ packet_log(" IV offset %u\n", iv_offset);
+
+ iv_len = (ctrl1 & SPU2_IV_LEN) >> SPU2_IV_LEN_SHIFT;
+ packet_log(" Input IV len %u bytes\n", iv_len);
+
+ hash_tag_len = (ctrl1 & SPU2_HASH_TAG_LEN) >> SPU2_HASH_TAG_LEN_SHIFT;
+ packet_log(" Hash tag length %u bytes\n", hash_tag_len);
+
+ packet_log(" Return ");
+ ret_md = (ctrl1 & SPU2_RETURN_MD) >> SPU2_RETURN_MD_SHIFT;
+ if (ret_md)
+ packet_log("FMD ");
+ if (ret_md == SPU2_RET_FMD_OMD)
+ packet_log("OMD ");
+ else if (ret_md == SPU2_RET_FMD_OMD_IV)
+ packet_log("OMD IV ");
+ if (ctrl1 & SPU2_RETURN_FD)
+ packet_log("FD ");
+ if (ctrl1 & SPU2_RETURN_AAD1)
+ packet_log("AAD1 ");
+ if (ctrl1 & SPU2_RETURN_NAAD)
+ packet_log("NAAD ");
+ if (ctrl1 & SPU2_RETURN_AAD2)
+ packet_log("AAD2 ");
+ if (ctrl1 & SPU2_RETURN_PAY)
+ packet_log("Payload");
+ packet_log("\n");
+}
+
+/* Dump FMD ctrl2. The ctrl2 input is in host byte order */
+static void spu2_dump_fmd_ctrl2(u64 ctrl2)
+{
+ packet_log(" FMD CTRL2 %#16llx\n", ctrl2);
+
+ packet_log(" AAD1 offset %llu length %llu bytes\n",
+ ctrl2 & SPU2_AAD1_OFFSET,
+ (ctrl2 & SPU2_AAD1_LEN) >> SPU2_AAD1_LEN_SHIFT);
+ packet_log(" AAD2 offset %llu\n",
+ (ctrl2 & SPU2_AAD2_OFFSET) >> SPU2_AAD2_OFFSET_SHIFT);
+ packet_log(" Payload offset %llu\n",
+ (ctrl2 & SPU2_PL_OFFSET) >> SPU2_PL_OFFSET_SHIFT);
+}
+
+/* Dump FMD ctrl3. The ctrl3 input is in host byte order */
+static void spu2_dump_fmd_ctrl3(u64 ctrl3)
+{
+ packet_log(" FMD CTRL3 %#16llx\n", ctrl3);
+
+ packet_log(" Payload length %llu bytes\n", ctrl3 & SPU2_PL_LEN);
+ packet_log(" TLS length %llu bytes\n",
+ (ctrl3 & SPU2_TLS_LEN) >> SPU2_TLS_LEN_SHIFT);
+}
+
+static void spu2_dump_fmd(struct SPU2_FMD *fmd)
+{
+ spu2_dump_fmd_ctrl0(le64_to_cpu(fmd->ctrl0));
+ spu2_dump_fmd_ctrl1(le64_to_cpu(fmd->ctrl1));
+ spu2_dump_fmd_ctrl2(le64_to_cpu(fmd->ctrl2));
+ spu2_dump_fmd_ctrl3(le64_to_cpu(fmd->ctrl3));
+}
+
+static void spu2_dump_omd(u8 *omd, u16 hash_key_len, u16 ciph_key_len,
+ u16 hash_iv_len, u16 ciph_iv_len)
+{
+ u8 *ptr = omd;
+
+ packet_log(" OMD:\n");
+
+ if (hash_key_len) {
+ packet_log(" Hash Key Length %u bytes\n", hash_key_len);
+ packet_dump(" KEY: ", ptr, hash_key_len);
+ ptr += hash_key_len;
+ }
+
+ if (ciph_key_len) {
+ packet_log(" Cipher Key Length %u bytes\n", ciph_key_len);
+ packet_dump(" KEY: ", ptr, ciph_key_len);
+ ptr += ciph_key_len;
+ }
+
+ if (hash_iv_len) {
+ packet_log(" Hash IV Length %u bytes\n", hash_iv_len);
+ packet_dump(" hash IV: ", ptr, hash_iv_len);
+ ptr += ciph_key_len;
+ }
+
+ if (ciph_iv_len) {
+ packet_log(" Cipher IV Length %u bytes\n", ciph_iv_len);
+ packet_dump(" cipher IV: ", ptr, ciph_iv_len);
+ }
+}
+
+/* Dump a SPU2 header for debug */
+void spu2_dump_msg_hdr(u8 *buf, unsigned int buf_len)
+{
+ struct SPU2_FMD *fmd = (struct SPU2_FMD *)buf;
+ u8 *omd;
+ u64 ctrl1;
+ u16 hash_key_len;
+ u16 ciph_key_len;
+ u16 hash_iv_len;
+ u16 ciph_iv_len;
+ u16 omd_len;
+
+ packet_log("\n");
+ packet_log("SPU2 message header %p len: %u\n", buf, buf_len);
+
+ spu2_dump_fmd(fmd);
+ omd = (u8 *)(fmd + 1);
+
+ ctrl1 = le64_to_cpu(fmd->ctrl1);
+ hash_key_len = (ctrl1 & SPU2_HASH_KEY_LEN) >> SPU2_HASH_KEY_LEN_SHIFT;
+ ciph_key_len = (ctrl1 & SPU2_CIPH_KEY_LEN) >> SPU2_CIPH_KEY_LEN_SHIFT;
+ hash_iv_len = 0;
+ ciph_iv_len = (ctrl1 & SPU2_IV_LEN) >> SPU2_IV_LEN_SHIFT;
+ spu2_dump_omd(omd, hash_key_len, ciph_key_len, hash_iv_len,
+ ciph_iv_len);
+
+ /* Double check sanity */
+ omd_len = hash_key_len + ciph_key_len + hash_iv_len + ciph_iv_len;
+ if (FMD_SIZE + omd_len != buf_len) {
+ packet_log
+ (" Packet parsed incorrectly. buf_len %u, sum of MD %zu\n",
+ buf_len, FMD_SIZE + omd_len);
+ }
+ packet_log("\n");
+}
+
+/**
+ * spu2_fmd_init() - At setkey time, initialize the fixed meta data for
+ * subsequent ablkcipher requests for this context.
+ * @spu2_cipher_type: Cipher algorithm
+ * @spu2_mode: Cipher mode
+ * @cipher_key_len: Length of cipher key, in bytes
+ * @cipher_iv_len: Length of cipher initialization vector, in bytes
+ *
+ * Return: 0 (success)
+ */
+static int spu2_fmd_init(struct SPU2_FMD *fmd,
+ enum spu2_cipher_type spu2_type,
+ enum spu2_cipher_mode spu2_mode,
+ u32 cipher_key_len, u32 cipher_iv_len)
+{
+ u64 ctrl0;
+ u64 ctrl1;
+ u64 ctrl2;
+ u64 ctrl3;
+ u32 aad1_offset;
+ u32 aad2_offset;
+ u16 aad1_len = 0;
+ u64 payload_offset;
+
+ ctrl0 = (spu2_type << SPU2_CIPH_TYPE_SHIFT) |
+ (spu2_mode << SPU2_CIPH_MODE_SHIFT);
+
+ ctrl1 = (cipher_key_len << SPU2_CIPH_KEY_LEN_SHIFT) |
+ ((u64)cipher_iv_len << SPU2_IV_LEN_SHIFT) |
+ ((u64)SPU2_RET_FMD_ONLY << SPU2_RETURN_MD_SHIFT) | SPU2_RETURN_PAY;
+
+ /*
+ * AAD1 offset is from start of FD. FD length is always 0 for this
+ * driver. So AAD1_offset is always 0.
+ */
+ aad1_offset = 0;
+ aad2_offset = aad1_offset;
+ payload_offset = 0;
+ ctrl2 = aad1_offset |
+ (aad1_len << SPU2_AAD1_LEN_SHIFT) |
+ (aad2_offset << SPU2_AAD2_OFFSET_SHIFT) |
+ (payload_offset << SPU2_PL_OFFSET_SHIFT);
+
+ ctrl3 = 0;
+
+ fmd->ctrl0 = cpu_to_le64(ctrl0);
+ fmd->ctrl1 = cpu_to_le64(ctrl1);
+ fmd->ctrl2 = cpu_to_le64(ctrl2);
+ fmd->ctrl3 = cpu_to_le64(ctrl3);
+
+ return 0;
+}
+
+/**
+ * spu2_fmd_ctrl0_write() - Write ctrl0 field in fixed metadata (FMD) field of
+ * SPU request packet.
+ * @fmd: Start of FMD field to be written
+ * @is_inbound: true if decrypting. false if encrypting.
+ * @authFirst: true if alg authenticates before encrypting
+ * @protocol: protocol selector
+ * @cipher_type: cipher algorithm
+ * @cipher_mode: cipher mode
+ * @auth_type: authentication type
+ * @auth_mode: authentication mode
+ */
+static void spu2_fmd_ctrl0_write(struct SPU2_FMD *fmd,
+ bool is_inbound, bool auth_first,
+ enum spu2_proto_sel protocol,
+ enum spu2_cipher_type cipher_type,
+ enum spu2_cipher_mode cipher_mode,
+ enum spu2_hash_type auth_type,
+ enum spu2_hash_mode auth_mode)
+{
+ u64 ctrl0 = 0;
+
+ if ((cipher_type != SPU2_CIPHER_TYPE_NONE) && !is_inbound)
+ ctrl0 |= SPU2_CIPH_ENCRYPT_EN;
+
+ ctrl0 |= ((u64)cipher_type << SPU2_CIPH_TYPE_SHIFT) |
+ ((u64)cipher_mode << SPU2_CIPH_MODE_SHIFT);
+
+ if (protocol)
+ ctrl0 |= (u64)protocol << SPU2_PROTO_SEL_SHIFT;
+
+ if (auth_first)
+ ctrl0 |= SPU2_HASH_FIRST;
+
+ if (is_inbound && (auth_type != SPU2_HASH_TYPE_NONE))
+ ctrl0 |= SPU2_CHK_TAG;
+
+ ctrl0 |= (((u64)auth_type << SPU2_HASH_TYPE_SHIFT) |
+ ((u64)auth_mode << SPU2_HASH_MODE_SHIFT));
+
+ fmd->ctrl0 = cpu_to_le64(ctrl0);
+}
+
+/**
+ * spu2_fmd_ctrl1_write() - Write ctrl1 field in fixed metadata (FMD) field of
+ * SPU request packet.
+ * @fmd: Start of FMD field to be written
+ * @assoc_size: Length of additional associated data, in bytes
+ * @auth_key_len: Length of authentication key, in bytes
+ * @cipher_key_len: Length of cipher key, in bytes
+ * @gen_iv: If true, hw generates IV and returns in response
+ * @hash_iv: IV participates in hash. Used for IPSEC and TLS.
+ * @return_iv: Return IV in output packet before payload
+ * @ret_iv_len: Length of IV returned from SPU, in bytes
+ * @ret_iv_offset: Offset into full IV of start of returned IV
+ * @cipher_iv_len: Length of input cipher IV, in bytes
+ * @digest_size: Length of digest (aka, hash tag or ICV), in bytes
+ * @return_payload: Return payload in SPU response
+ * @return_md : return metadata in SPU response
+ *
+ * Packet can have AAD2 w/o AAD1. For algorithms currently supported,
+ * associated data goes in AAD2.
+ */
+static void spu2_fmd_ctrl1_write(struct SPU2_FMD *fmd, bool is_inbound,
+ u64 assoc_size,
+ u64 auth_key_len, u64 cipher_key_len,
+ bool gen_iv, bool hash_iv, bool return_iv,
+ u64 ret_iv_len, u64 ret_iv_offset,
+ u64 cipher_iv_len, u64 digest_size,
+ bool return_payload, bool return_md)
+{
+ u64 ctrl1 = 0;
+
+ if (is_inbound && digest_size)
+ ctrl1 |= SPU2_TAG_LOC;
+
+ if (assoc_size) {
+ ctrl1 |= SPU2_HAS_AAD2;
+ ctrl1 |= SPU2_RETURN_AAD2; /* need aad2 for gcm aes esp */
+ }
+
+ if (auth_key_len)
+ ctrl1 |= ((auth_key_len << SPU2_HASH_KEY_LEN_SHIFT) &
+ SPU2_HASH_KEY_LEN);
+
+ if (cipher_key_len)
+ ctrl1 |= ((cipher_key_len << SPU2_CIPH_KEY_LEN_SHIFT) &
+ SPU2_CIPH_KEY_LEN);
+
+ if (gen_iv)
+ ctrl1 |= SPU2_GENIV;
+
+ if (hash_iv)
+ ctrl1 |= SPU2_HASH_IV;
+
+ if (return_iv) {
+ ctrl1 |= SPU2_RET_IV;
+ ctrl1 |= ret_iv_len << SPU2_RET_IV_LEN_SHIFT;
+ ctrl1 |= ret_iv_offset << SPU2_IV_OFFSET_SHIFT;
+ }
+
+ ctrl1 |= ((cipher_iv_len << SPU2_IV_LEN_SHIFT) & SPU2_IV_LEN);
+
+ if (digest_size)
+ ctrl1 |= ((digest_size << SPU2_HASH_TAG_LEN_SHIFT) &
+ SPU2_HASH_TAG_LEN);
+
+ /* Let's ask for the output pkt to include FMD, but don't need to
+ * get keys and IVs back in OMD.
+ */
+ if (return_md)
+ ctrl1 |= ((u64)SPU2_RET_FMD_ONLY << SPU2_RETURN_MD_SHIFT);
+ else
+ ctrl1 |= ((u64)SPU2_RET_NO_MD << SPU2_RETURN_MD_SHIFT);
+
+ /* Crypto API does not get assoc data back. So no need for AAD2. */
+
+ if (return_payload)
+ ctrl1 |= SPU2_RETURN_PAY;
+
+ fmd->ctrl1 = cpu_to_le64(ctrl1);
+}
+
+/**
+ * spu2_fmd_ctrl2_write() - Set the ctrl2 field in the fixed metadata field of
+ * SPU2 header.
+ * @fmd: Start of FMD field to be written
+ * @cipher_offset: Number of bytes from Start of Packet (end of FD field) where
+ * data to be encrypted or decrypted begins
+ * @auth_key_len: Length of authentication key, in bytes
+ * @auth_iv_len: Length of authentication initialization vector, in bytes
+ * @cipher_key_len: Length of cipher key, in bytes
+ * @cipher_iv_len: Length of cipher IV, in bytes
+ */
+static void spu2_fmd_ctrl2_write(struct SPU2_FMD *fmd, u64 cipher_offset,
+ u64 auth_key_len, u64 auth_iv_len,
+ u64 cipher_key_len, u64 cipher_iv_len)
+{
+ u64 ctrl2;
+ u64 aad1_offset;
+ u64 aad2_offset;
+ u16 aad1_len = 0;
+ u64 payload_offset;
+
+ /* AAD1 offset is from start of FD. FD length always 0. */
+ aad1_offset = 0;
+
+ aad2_offset = aad1_offset;
+ payload_offset = cipher_offset;
+ ctrl2 = aad1_offset |
+ (aad1_len << SPU2_AAD1_LEN_SHIFT) |
+ (aad2_offset << SPU2_AAD2_OFFSET_SHIFT) |
+ (payload_offset << SPU2_PL_OFFSET_SHIFT);
+
+ fmd->ctrl2 = cpu_to_le64(ctrl2);
+}
+
+/**
+ * spu2_fmd_ctrl3_write() - Set the ctrl3 field in FMD
+ * @fmd: Fixed meta data. First field in SPU2 msg header.
+ * @payload_len: Length of payload, in bytes
+ */
+static void spu2_fmd_ctrl3_write(struct SPU2_FMD *fmd, u64 payload_len)
+{
+ u64 ctrl3;
+
+ ctrl3 = payload_len & SPU2_PL_LEN;
+
+ fmd->ctrl3 = cpu_to_le64(ctrl3);
+}
+
+/**
+ * spu2_ctx_max_payload() - Determine the maximum length of the payload for a
+ * SPU message for a given cipher and hash alg context.
+ * @cipher_alg: The cipher algorithm
+ * @cipher_mode: The cipher mode
+ * @blocksize: The size of a block of data for this algo
+ *
+ * For SPU2, the hardware generally ignores the PayloadLen field in ctrl3 of
+ * FMD and just keeps computing until it receives a DMA descriptor with the EOF
+ * flag set. So we consider the max payload to be infinite. AES CCM is an
+ * exception.
+ *
+ * Return: Max payload length in bytes
+ */
+u32 spu2_ctx_max_payload(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ unsigned int blocksize)
+{
+ if ((cipher_alg == CIPHER_ALG_AES) &&
+ (cipher_mode == CIPHER_MODE_CCM)) {
+ u32 excess = SPU2_MAX_PAYLOAD % blocksize;
+
+ return SPU2_MAX_PAYLOAD - excess;
+ } else {
+ return SPU_MAX_PAYLOAD_INF;
+ }
+}
+
+/**
+ * spu_payload_length() - Given a SPU2 message header, extract the payload
+ * length.
+ * @spu_hdr: Start of SPU message header (FMD)
+ *
+ * Return: payload length, in bytes
+ */
+u32 spu2_payload_length(u8 *spu_hdr)
+{
+ struct SPU2_FMD *fmd = (struct SPU2_FMD *)spu_hdr;
+ u32 pl_len;
+ u64 ctrl3;
+
+ ctrl3 = le64_to_cpu(fmd->ctrl3);
+ pl_len = ctrl3 & SPU2_PL_LEN;
+
+ return pl_len;
+}
+
+/**
+ * spu_response_hdr_len() - Determine the expected length of a SPU response
+ * header.
+ * @auth_key_len: Length of authentication key, in bytes
+ * @enc_key_len: Length of encryption key, in bytes
+ *
+ * For SPU2, includes just FMD. OMD is never requested.
+ *
+ * Return: Length of FMD, in bytes
+ */
+u16 spu2_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash)
+{
+ return FMD_SIZE;
+}
+
+/**
+ * spu_hash_pad_len() - Calculate the length of hash padding required to extend
+ * data to a full block size.
+ * @hash_alg: hash algorithm
+ * @hash_mode: hash mode
+ * @chunksize: length of data, in bytes
+ * @hash_block_size: size of a hash block, in bytes
+ *
+ * SPU2 hardware does all hash padding
+ *
+ * Return: length of hash pad in bytes
+ */
+u16 spu2_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode,
+ u32 chunksize, u16 hash_block_size)
+{
+ return 0;
+}
+
+/**
+ * spu2_gcm_ccm_padlen() - Determine the length of GCM/CCM padding for either
+ * the AAD field or the data.
+ *
+ * Return: 0. Unlike SPU-M, SPU2 hardware does any GCM/CCM padding required.
+ */
+u32 spu2_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode,
+ unsigned int data_size)
+{
+ return 0;
+}
+
+/**
+ * spu_assoc_resp_len() - Determine the size of the AAD2 buffer needed to catch
+ * associated data in a SPU2 output packet.
+ * @cipher_mode: cipher mode
+ * @assoc_len: length of additional associated data, in bytes
+ * @iv_len: length of initialization vector, in bytes
+ * @is_encrypt: true if encrypting. false if decrypt.
+ *
+ * Return: Length of buffer to catch associated data in response
+ */
+u32 spu2_assoc_resp_len(enum spu_cipher_mode cipher_mode,
+ unsigned int assoc_len, unsigned int iv_len,
+ bool is_encrypt)
+{
+ u32 resp_len = assoc_len;
+
+ if (is_encrypt)
+ /* gcm aes esp has to write 8-byte IV in response */
+ resp_len += iv_len;
+ return resp_len;
+}
+
+/*
+ * spu_aead_ivlen() - Calculate the length of the AEAD IV to be included
+ * in a SPU request after the AAD and before the payload.
+ * @cipher_mode: cipher mode
+ * @iv_ctr_len: initialization vector length in bytes
+ *
+ * For SPU2, AEAD IV is included in OMD and does not need to be repeated
+ * prior to the payload.
+ *
+ * Return: Length of AEAD IV in bytes
+ */
+u8 spu2_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len)
+{
+ return 0;
+}
+
+/**
+ * spu2_hash_type() - Determine the type of hash operation.
+ * @src_sent: The number of bytes in the current request that have already
+ * been sent to the SPU to be hashed.
+ *
+ * SPU2 always does a FULL hash operation
+ */
+enum hash_type spu2_hash_type(u32 src_sent)
+{
+ return HASH_TYPE_FULL;
+}
+
+/**
+ * spu2_digest_size() - Determine the size of a hash digest to expect the SPU to
+ * return.
+ * alg_digest_size: Number of bytes in the final digest for the given algo
+ * alg: The hash algorithm
+ * htype: Type of hash operation (init, update, full, etc)
+ *
+ */
+u32 spu2_digest_size(u32 alg_digest_size, enum hash_alg alg,
+ enum hash_type htype)
+{
+ return alg_digest_size;
+}
+
+/**
+ * spu_create_request() - Build a SPU2 request message header, includint FMD and
+ * OMD.
+ * @spu_hdr: Start of buffer where SPU request header is to be written
+ * @req_opts: SPU request message options
+ * @cipher_parms: Parameters related to cipher algorithm
+ * @hash_parms: Parameters related to hash algorithm
+ * @aead_parms: Parameters related to AEAD operation
+ * @data_size: Length of data to be encrypted or authenticated. If AEAD, does
+ * not include length of AAD.
+ *
+ * Construct the message starting at spu_hdr. Caller should allocate this buffer
+ * in DMA-able memory at least SPU_HEADER_ALLOC_LEN bytes long.
+ *
+ * Return: the length of the SPU header in bytes. 0 if an error occurs.
+ */
+u32 spu2_create_request(u8 *spu_hdr,
+ struct spu_request_opts *req_opts,
+ struct spu_cipher_parms *cipher_parms,
+ struct spu_hash_parms *hash_parms,
+ struct spu_aead_parms *aead_parms,
+ unsigned int data_size)
+{
+ struct SPU2_FMD *fmd;
+ u8 *ptr;
+ unsigned int buf_len;
+ int err;
+ enum spu2_cipher_type spu2_ciph_type = SPU2_CIPHER_TYPE_NONE;
+ enum spu2_cipher_mode spu2_ciph_mode;
+ enum spu2_hash_type spu2_auth_type = SPU2_HASH_TYPE_NONE;
+ enum spu2_hash_mode spu2_auth_mode;
+ bool return_md = true;
+ enum spu2_proto_sel proto = SPU2_PROTO_RESV;
+
+ /* size of the payload */
+ unsigned int payload_len =
+ hash_parms->prebuf_len + data_size + hash_parms->pad_len -
+ ((req_opts->is_aead && req_opts->is_inbound) ?
+ hash_parms->digestsize : 0);
+
+ /* offset of prebuf or data from start of AAD2 */
+ unsigned int cipher_offset = aead_parms->assoc_size +
+ aead_parms->aad_pad_len + aead_parms->iv_len;
+
+#ifdef DEBUG
+ /* total size of the data following OMD (without STAT word padding) */
+ unsigned int real_db_size = spu_real_db_size(aead_parms->assoc_size,
+ aead_parms->iv_len,
+ hash_parms->prebuf_len,
+ data_size,
+ aead_parms->aad_pad_len,
+ aead_parms->data_pad_len,
+ hash_parms->pad_len);
+#endif
+ unsigned int assoc_size = aead_parms->assoc_size;
+
+ if (req_opts->is_aead &&
+ (cipher_parms->alg == CIPHER_ALG_AES) &&
+ (cipher_parms->mode == CIPHER_MODE_GCM))
+ /*
+ * On SPU 2, aes gcm cipher first on encrypt, auth first on
+ * decrypt
+ */
+ req_opts->auth_first = req_opts->is_inbound;
+
+ /* and do opposite for ccm (auth 1st on encrypt) */
+ if (req_opts->is_aead &&
+ (cipher_parms->alg == CIPHER_ALG_AES) &&
+ (cipher_parms->mode == CIPHER_MODE_CCM))
+ req_opts->auth_first = !req_opts->is_inbound;
+
+ flow_log("%s()\n", __func__);
+ flow_log(" in:%u authFirst:%u\n",
+ req_opts->is_inbound, req_opts->auth_first);
+ flow_log(" cipher alg:%u mode:%u type %u\n", cipher_parms->alg,
+ cipher_parms->mode, cipher_parms->type);
+ flow_log(" is_esp: %s\n", req_opts->is_esp ? "yes" : "no");
+ flow_log(" key: %d\n", cipher_parms->key_len);
+ flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len);
+ flow_log(" iv: %d\n", cipher_parms->iv_len);
+ flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len);
+ flow_log(" auth alg:%u mode:%u type %u\n",
+ hash_parms->alg, hash_parms->mode, hash_parms->type);
+ flow_log(" digestsize: %u\n", hash_parms->digestsize);
+ flow_log(" authkey: %d\n", hash_parms->key_len);
+ flow_dump(" authkey: ", hash_parms->key_buf, hash_parms->key_len);
+ flow_log(" assoc_size:%u\n", assoc_size);
+ flow_log(" prebuf_len:%u\n", hash_parms->prebuf_len);
+ flow_log(" data_size:%u\n", data_size);
+ flow_log(" hash_pad_len:%u\n", hash_parms->pad_len);
+ flow_log(" real_db_size:%u\n", real_db_size);
+ flow_log(" cipher_offset:%u payload_len:%u\n",
+ cipher_offset, payload_len);
+ flow_log(" hmac_offset:%u\n", hash_parms->hmac_offset);
+ flow_log(" aead_iv: %u\n", aead_parms->iv_len);
+
+ /* Convert to spu2 values for cipher alg, hash alg */
+ err = spu2_cipher_xlate(cipher_parms->alg, cipher_parms->mode,
+ cipher_parms->type,
+ &spu2_ciph_type, &spu2_ciph_mode);
+
+ /* If we are doing GCM hashing only - either via rfc4543 transform
+ * or because we happen to do GCM with AAD only and no payload - we
+ * need to configure hardware to use hash key rather than cipher key
+ * and put data into payload. This is because unlike SPU-M, running
+ * GCM cipher with 0 size payload is not permitted.
+ */
+ if ((req_opts->is_rfc4543) ||
+ ((spu2_ciph_mode == SPU2_CIPHER_MODE_GCM) &&
+ (payload_len == 0))) {
+ /* Use hashing (only) and set up hash key */
+ spu2_ciph_type = SPU2_CIPHER_TYPE_NONE;
+ hash_parms->key_len = cipher_parms->key_len;
+ memcpy(hash_parms->key_buf, cipher_parms->key_buf,
+ cipher_parms->key_len);
+ cipher_parms->key_len = 0;
+
+ if (req_opts->is_rfc4543)
+ payload_len += assoc_size;
+ else
+ payload_len = assoc_size;
+ cipher_offset = 0;
+ assoc_size = 0;
+ }
+
+ if (err)
+ return 0;
+
+ flow_log("spu2 cipher type %s, cipher mode %s\n",
+ spu2_ciph_type_name(spu2_ciph_type),
+ spu2_ciph_mode_name(spu2_ciph_mode));
+
+ err = spu2_hash_xlate(hash_parms->alg, hash_parms->mode,
+ hash_parms->type,
+ cipher_parms->type,
+ &spu2_auth_type, &spu2_auth_mode);
+ if (err)
+ return 0;
+
+ flow_log("spu2 hash type %s, hash mode %s\n",
+ spu2_hash_type_name(spu2_auth_type),
+ spu2_hash_mode_name(spu2_auth_mode));
+
+ fmd = (struct SPU2_FMD *)spu_hdr;
+
+ spu2_fmd_ctrl0_write(fmd, req_opts->is_inbound, req_opts->auth_first,
+ proto, spu2_ciph_type, spu2_ciph_mode,
+ spu2_auth_type, spu2_auth_mode);
+
+ spu2_fmd_ctrl1_write(fmd, req_opts->is_inbound, assoc_size,
+ hash_parms->key_len, cipher_parms->key_len,
+ false, false,
+ aead_parms->return_iv, aead_parms->ret_iv_len,
+ aead_parms->ret_iv_off,
+ cipher_parms->iv_len, hash_parms->digestsize,
+ !req_opts->bd_suppress, return_md);
+
+ spu2_fmd_ctrl2_write(fmd, cipher_offset, hash_parms->key_len, 0,
+ cipher_parms->key_len, cipher_parms->iv_len);
+
+ spu2_fmd_ctrl3_write(fmd, payload_len);
+
+ ptr = (u8 *)(fmd + 1);
+ buf_len = sizeof(struct SPU2_FMD);
+
+ /* Write OMD */
+ if (hash_parms->key_len) {
+ memcpy(ptr, hash_parms->key_buf, hash_parms->key_len);
+ ptr += hash_parms->key_len;
+ buf_len += hash_parms->key_len;
+ }
+ if (cipher_parms->key_len) {
+ memcpy(ptr, cipher_parms->key_buf, cipher_parms->key_len);
+ ptr += cipher_parms->key_len;
+ buf_len += cipher_parms->key_len;
+ }
+ if (cipher_parms->iv_len) {
+ memcpy(ptr, cipher_parms->iv_buf, cipher_parms->iv_len);
+ ptr += cipher_parms->iv_len;
+ buf_len += cipher_parms->iv_len;
+ }
+
+ packet_dump(" SPU request header: ", spu_hdr, buf_len);
+
+ return buf_len;
+}
+
+/**
+ * spu_cipher_req_init() - Build an ablkcipher SPU2 request message header,
+ * including FMD and OMD.
+ * @spu_hdr: Location of start of SPU request (FMD field)
+ * @cipher_parms: Parameters describing cipher request
+ *
+ * Called at setkey time to initialize a msg header that can be reused for all
+ * subsequent ablkcipher requests. Construct the message starting at spu_hdr.
+ * Caller should allocate this buffer in DMA-able memory at least
+ * SPU_HEADER_ALLOC_LEN bytes long.
+ *
+ * Return: the total length of the SPU header (FMD and OMD) in bytes. 0 if an
+ * error occurs.
+ */
+u16 spu2_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms)
+{
+ struct SPU2_FMD *fmd;
+ u8 *omd;
+ enum spu2_cipher_type spu2_type = SPU2_CIPHER_TYPE_NONE;
+ enum spu2_cipher_mode spu2_mode;
+ int err;
+
+ flow_log("%s()\n", __func__);
+ flow_log(" cipher alg:%u mode:%u type %u\n", cipher_parms->alg,
+ cipher_parms->mode, cipher_parms->type);
+ flow_log(" cipher_iv_len: %u\n", cipher_parms->iv_len);
+ flow_log(" key: %d\n", cipher_parms->key_len);
+ flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len);
+
+ /* Convert to spu2 values */
+ err = spu2_cipher_xlate(cipher_parms->alg, cipher_parms->mode,
+ cipher_parms->type, &spu2_type, &spu2_mode);
+ if (err)
+ return 0;
+
+ flow_log("spu2 cipher type %s, cipher mode %s\n",
+ spu2_ciph_type_name(spu2_type),
+ spu2_ciph_mode_name(spu2_mode));
+
+ /* Construct the FMD header */
+ fmd = (struct SPU2_FMD *)spu_hdr;
+ err = spu2_fmd_init(fmd, spu2_type, spu2_mode, cipher_parms->key_len,
+ cipher_parms->iv_len);
+ if (err)
+ return 0;
+
+ /* Write cipher key to OMD */
+ omd = (u8 *)(fmd + 1);
+ if (cipher_parms->key_buf && cipher_parms->key_len)
+ memcpy(omd, cipher_parms->key_buf, cipher_parms->key_len);
+
+ packet_dump(" SPU request header: ", spu_hdr,
+ FMD_SIZE + cipher_parms->key_len + cipher_parms->iv_len);
+
+ return FMD_SIZE + cipher_parms->key_len + cipher_parms->iv_len;
+}
+
+/**
+ * spu_cipher_req_finish() - Finish building a SPU request message header for a
+ * block cipher request.
+ * @spu_hdr: Start of the request message header (MH field)
+ * @spu_req_hdr_len: Length in bytes of the SPU request header
+ * @isInbound: 0 encrypt, 1 decrypt
+ * @cipher_parms: Parameters describing cipher operation to be performed
+ * @update_key: If true, rewrite the cipher key in SCTX
+ * @data_size: Length of the data in the BD field
+ *
+ * Assumes much of the header was already filled in at setkey() time in
+ * spu_cipher_req_init().
+ * spu_cipher_req_init() fills in the encryption key. For RC4, when submitting a
+ * request for a non-first chunk, we use the 260-byte SUPDT field from the
+ * previous response as the key. update_key is true for this case. Unused in all
+ * other cases.
+ */
+void spu2_cipher_req_finish(u8 *spu_hdr,
+ u16 spu_req_hdr_len,
+ unsigned int is_inbound,
+ struct spu_cipher_parms *cipher_parms,
+ bool update_key,
+ unsigned int data_size)
+{
+ struct SPU2_FMD *fmd;
+ u8 *omd; /* start of optional metadata */
+ u64 ctrl0;
+ u64 ctrl3;
+
+ flow_log("%s()\n", __func__);
+ flow_log(" in: %u\n", is_inbound);
+ flow_log(" cipher alg: %u, cipher_type: %u\n", cipher_parms->alg,
+ cipher_parms->type);
+ if (update_key) {
+ flow_log(" cipher key len: %u\n", cipher_parms->key_len);
+ flow_dump(" key: ", cipher_parms->key_buf,
+ cipher_parms->key_len);
+ }
+ flow_log(" iv len: %d\n", cipher_parms->iv_len);
+ flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len);
+ flow_log(" data_size: %u\n", data_size);
+
+ fmd = (struct SPU2_FMD *)spu_hdr;
+ omd = (u8 *)(fmd + 1);
+
+ /*
+ * FMD ctrl0 was initialized at setkey time. update it to indicate
+ * whether we are encrypting or decrypting.
+ */
+ ctrl0 = le64_to_cpu(fmd->ctrl0);
+ if (is_inbound)
+ ctrl0 &= ~SPU2_CIPH_ENCRYPT_EN; /* decrypt */
+ else
+ ctrl0 |= SPU2_CIPH_ENCRYPT_EN; /* encrypt */
+ fmd->ctrl0 = cpu_to_le64(ctrl0);
+
+ if (cipher_parms->alg && cipher_parms->iv_buf && cipher_parms->iv_len) {
+ /* cipher iv provided so put it in here */
+ memcpy(omd + cipher_parms->key_len, cipher_parms->iv_buf,
+ cipher_parms->iv_len);
+ }
+
+ ctrl3 = le64_to_cpu(fmd->ctrl3);
+ data_size &= SPU2_PL_LEN;
+ ctrl3 |= data_size;
+ fmd->ctrl3 = cpu_to_le64(ctrl3);
+
+ packet_dump(" SPU request header: ", spu_hdr, spu_req_hdr_len);
+}
+
+/**
+ * spu_request_pad() - Create pad bytes at the end of the data.
+ * @pad_start: Start of buffer where pad bytes are to be written
+ * @gcm_padding: Length of GCM padding, in bytes
+ * @hash_pad_len: Number of bytes of padding extend data to full block
+ * @auth_alg: Authentication algorithm
+ * @auth_mode: Authentication mode
+ * @total_sent: Length inserted at end of hash pad
+ * @status_padding: Number of bytes of padding to align STATUS word
+ *
+ * There may be three forms of pad:
+ * 1. GCM pad - for GCM mode ciphers, pad to 16-byte alignment
+ * 2. hash pad - pad to a block length, with 0x80 data terminator and
+ * size at the end
+ * 3. STAT pad - to ensure the STAT field is 4-byte aligned
+ */
+void spu2_request_pad(u8 *pad_start, u32 gcm_padding, u32 hash_pad_len,
+ enum hash_alg auth_alg, enum hash_mode auth_mode,
+ unsigned int total_sent, u32 status_padding)
+{
+ u8 *ptr = pad_start;
+
+ /* fix data alignent for GCM */
+ if (gcm_padding > 0) {
+ flow_log(" GCM: padding to 16 byte alignment: %u bytes\n",
+ gcm_padding);
+ memset(ptr, 0, gcm_padding);
+ ptr += gcm_padding;
+ }
+
+ if (hash_pad_len > 0) {
+ /* clear the padding section */
+ memset(ptr, 0, hash_pad_len);
+
+ /* terminate the data */
+ *ptr = 0x80;
+ ptr += (hash_pad_len - sizeof(u64));
+
+ /* add the size at the end as required per alg */
+ if (auth_alg == HASH_ALG_MD5)
+ *(u64 *)ptr = cpu_to_le64((u64)total_sent * 8);
+ else /* SHA1, SHA2-224, SHA2-256 */
+ *(u64 *)ptr = cpu_to_be64((u64)total_sent * 8);
+ ptr += sizeof(u64);
+ }
+
+ /* pad to a 4byte alignment for STAT */
+ if (status_padding > 0) {
+ flow_log(" STAT: padding to 4 byte alignment: %u bytes\n",
+ status_padding);
+
+ memset(ptr, 0, status_padding);
+ ptr += status_padding;
+ }
+}
+
+/**
+ * spu2_xts_tweak_in_payload() - Indicate that SPU2 does NOT place the XTS
+ * tweak field in the packet payload (it uses IV instead)
+ *
+ * Return: 0
+ */
+u8 spu2_xts_tweak_in_payload(void)
+{
+ return 0;
+}
+
+/**
+ * spu2_tx_status_len() - Return the length of the STATUS field in a SPU
+ * response message.
+ *
+ * Return: Length of STATUS field in bytes.
+ */
+u8 spu2_tx_status_len(void)
+{
+ return SPU2_TX_STATUS_LEN;
+}
+
+/**
+ * spu2_rx_status_len() - Return the length of the STATUS field in a SPU
+ * response message.
+ *
+ * Return: Length of STATUS field in bytes.
+ */
+u8 spu2_rx_status_len(void)
+{
+ return SPU2_RX_STATUS_LEN;
+}
+
+/**
+ * spu_status_process() - Process the status from a SPU response message.
+ * @statp: start of STATUS word
+ *
+ * Return: 0 - if status is good and response should be processed
+ * !0 - status indicates an error and response is invalid
+ */
+int spu2_status_process(u8 *statp)
+{
+ /* SPU2 status is 2 bytes by default - SPU_RX_STATUS_LEN */
+ u16 status = le16_to_cpu(*(__le16 *)statp);
+
+ if (status == 0)
+ return 0;
+
+ flow_log("rx status is %#x\n", status);
+ if (status == SPU2_INVALID_ICV)
+ return SPU_INVALID_ICV;
+
+ return -EBADMSG;
+}
+
+/**
+ * spu2_ccm_update_iv() - Update the IV as per the requirements for CCM mode.
+ *
+ * @digestsize: Digest size of this request
+ * @cipher_parms: (pointer to) cipher parmaeters, includes IV buf & IV len
+ * @assoclen: Length of AAD data
+ * @chunksize: length of input data to be sent in this req
+ * @is_encrypt: true if this is an output/encrypt operation
+ * @is_esp: true if this is an ESP / RFC4309 operation
+ *
+ */
+void spu2_ccm_update_iv(unsigned int digestsize,
+ struct spu_cipher_parms *cipher_parms,
+ unsigned int assoclen, unsigned int chunksize,
+ bool is_encrypt, bool is_esp)
+{
+ int L; /* size of length field, in bytes */
+
+ /*
+ * In RFC4309 mode, L is fixed at 4 bytes; otherwise, IV from
+ * testmgr contains (L-1) in bottom 3 bits of first byte,
+ * per RFC 3610.
+ */
+ if (is_esp)
+ L = CCM_ESP_L_VALUE;
+ else
+ L = ((cipher_parms->iv_buf[0] & CCM_B0_L_PRIME) >>
+ CCM_B0_L_PRIME_SHIFT) + 1;
+
+ /* SPU2 doesn't want these length bytes nor the first byte... */
+ cipher_parms->iv_len -= (1 + L);
+ memmove(cipher_parms->iv_buf, &cipher_parms->iv_buf[1],
+ cipher_parms->iv_len);
+}
+
+/**
+ * spu2_wordalign_padlen() - SPU2 does not require padding.
+ * @data_size: length of data field in bytes
+ *
+ * Return: length of status field padding, in bytes (always 0 on SPU2)
+ */
+u32 spu2_wordalign_padlen(u32 data_size)
+{
+ return 0;
+}
diff --git a/drivers/crypto/bcm/spu2.h b/drivers/crypto/bcm/spu2.h
new file mode 100644
index 0000000..ab1f599
--- /dev/null
+++ b/drivers/crypto/bcm/spu2.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+/*
+ * This file contains SPU message definitions specific to SPU2.
+ */
+
+#ifndef _SPU2_H
+#define _SPU2_H
+
+enum spu2_cipher_type {
+ SPU2_CIPHER_TYPE_NONE = 0x0,
+ SPU2_CIPHER_TYPE_AES128 = 0x1,
+ SPU2_CIPHER_TYPE_AES192 = 0x2,
+ SPU2_CIPHER_TYPE_AES256 = 0x3,
+ SPU2_CIPHER_TYPE_DES = 0x4,
+ SPU2_CIPHER_TYPE_3DES = 0x5,
+ SPU2_CIPHER_TYPE_LAST
+};
+
+enum spu2_cipher_mode {
+ SPU2_CIPHER_MODE_ECB = 0x0,
+ SPU2_CIPHER_MODE_CBC = 0x1,
+ SPU2_CIPHER_MODE_CTR = 0x2,
+ SPU2_CIPHER_MODE_CFB = 0x3,
+ SPU2_CIPHER_MODE_OFB = 0x4,
+ SPU2_CIPHER_MODE_XTS = 0x5,
+ SPU2_CIPHER_MODE_CCM = 0x6,
+ SPU2_CIPHER_MODE_GCM = 0x7,
+ SPU2_CIPHER_MODE_LAST
+};
+
+enum spu2_hash_type {
+ SPU2_HASH_TYPE_NONE = 0x0,
+ SPU2_HASH_TYPE_AES128 = 0x1,
+ SPU2_HASH_TYPE_AES192 = 0x2,
+ SPU2_HASH_TYPE_AES256 = 0x3,
+ SPU2_HASH_TYPE_MD5 = 0x6,
+ SPU2_HASH_TYPE_SHA1 = 0x7,
+ SPU2_HASH_TYPE_SHA224 = 0x8,
+ SPU2_HASH_TYPE_SHA256 = 0x9,
+ SPU2_HASH_TYPE_SHA384 = 0xa,
+ SPU2_HASH_TYPE_SHA512 = 0xb,
+ SPU2_HASH_TYPE_SHA512_224 = 0xc,
+ SPU2_HASH_TYPE_SHA512_256 = 0xd,
+ SPU2_HASH_TYPE_SHA3_224 = 0xe,
+ SPU2_HASH_TYPE_SHA3_256 = 0xf,
+ SPU2_HASH_TYPE_SHA3_384 = 0x10,
+ SPU2_HASH_TYPE_SHA3_512 = 0x11,
+ SPU2_HASH_TYPE_LAST
+};
+
+enum spu2_hash_mode {
+ SPU2_HASH_MODE_CMAC = 0x0,
+ SPU2_HASH_MODE_CBC_MAC = 0x1,
+ SPU2_HASH_MODE_XCBC_MAC = 0x2,
+ SPU2_HASH_MODE_HMAC = 0x3,
+ SPU2_HASH_MODE_RABIN = 0x4,
+ SPU2_HASH_MODE_CCM = 0x5,
+ SPU2_HASH_MODE_GCM = 0x6,
+ SPU2_HASH_MODE_RESERVED = 0x7,
+ SPU2_HASH_MODE_LAST
+};
+
+enum spu2_ret_md_opts {
+ SPU2_RET_NO_MD = 0, /* return no metadata */
+ SPU2_RET_FMD_OMD = 1, /* return both FMD and OMD */
+ SPU2_RET_FMD_ONLY = 2, /* return only FMD */
+ SPU2_RET_FMD_OMD_IV = 3, /* return FMD and OMD with just IVs */
+};
+
+/* Fixed Metadata format */
+struct SPU2_FMD {
+ u64 ctrl0;
+ u64 ctrl1;
+ u64 ctrl2;
+ u64 ctrl3;
+};
+
+#define FMD_SIZE sizeof(struct SPU2_FMD)
+
+/* Fixed part of request message header length in bytes. Just FMD. */
+#define SPU2_REQ_FIXED_LEN FMD_SIZE
+#define SPU2_HEADER_ALLOC_LEN (SPU_REQ_FIXED_LEN + \
+ 2 * MAX_KEY_SIZE + 2 * MAX_IV_SIZE)
+
+/* FMD ctrl0 field masks */
+#define SPU2_CIPH_ENCRYPT_EN 0x1 /* 0: decrypt, 1: encrypt */
+#define SPU2_CIPH_TYPE 0xF0 /* one of spu2_cipher_type */
+#define SPU2_CIPH_TYPE_SHIFT 4
+#define SPU2_CIPH_MODE 0xF00 /* one of spu2_cipher_mode */
+#define SPU2_CIPH_MODE_SHIFT 8
+#define SPU2_CFB_MASK 0x7000 /* cipher feedback mask */
+#define SPU2_CFB_MASK_SHIFT 12
+#define SPU2_PROTO_SEL 0xF00000 /* MACsec, IPsec, TLS... */
+#define SPU2_PROTO_SEL_SHIFT 20
+#define SPU2_HASH_FIRST 0x1000000 /* 1: hash input is input pkt
+ * data
+ */
+#define SPU2_CHK_TAG 0x2000000 /* 1: check digest provided */
+#define SPU2_HASH_TYPE 0x1F0000000 /* one of spu2_hash_type */
+#define SPU2_HASH_TYPE_SHIFT 28
+#define SPU2_HASH_MODE 0xF000000000 /* one of spu2_hash_mode */
+#define SPU2_HASH_MODE_SHIFT 36
+#define SPU2_CIPH_PAD_EN 0x100000000000 /* 1: Add pad to end of payload for
+ * enc
+ */
+#define SPU2_CIPH_PAD 0xFF000000000000 /* cipher pad value */
+#define SPU2_CIPH_PAD_SHIFT 48
+
+/* FMD ctrl1 field masks */
+#define SPU2_TAG_LOC 0x1 /* 1: end of payload, 0: undef */
+#define SPU2_HAS_FR_DATA 0x2 /* 1: msg has frame data */
+#define SPU2_HAS_AAD1 0x4 /* 1: msg has AAD1 field */
+#define SPU2_HAS_NAAD 0x8 /* 1: msg has NAAD field */
+#define SPU2_HAS_AAD2 0x10 /* 1: msg has AAD2 field */
+#define SPU2_HAS_ESN 0x20 /* 1: msg has ESN field */
+#define SPU2_HASH_KEY_LEN 0xFF00 /* len of hash key in bytes.
+ * HMAC only.
+ */
+#define SPU2_HASH_KEY_LEN_SHIFT 8
+#define SPU2_CIPH_KEY_LEN 0xFF00000 /* len of cipher key in bytes */
+#define SPU2_CIPH_KEY_LEN_SHIFT 20
+#define SPU2_GENIV 0x10000000 /* 1: hw generates IV */
+#define SPU2_HASH_IV 0x20000000 /* 1: IV incl in hash */
+#define SPU2_RET_IV 0x40000000 /* 1: return IV in output msg
+ * b4 payload
+ */
+#define SPU2_RET_IV_LEN 0xF00000000 /* length in bytes of IV returned.
+ * 0 = 16 bytes
+ */
+#define SPU2_RET_IV_LEN_SHIFT 32
+#define SPU2_IV_OFFSET 0xF000000000 /* gen IV offset */
+#define SPU2_IV_OFFSET_SHIFT 36
+#define SPU2_IV_LEN 0x1F0000000000 /* length of input IV in bytes */
+#define SPU2_IV_LEN_SHIFT 40
+#define SPU2_HASH_TAG_LEN 0x7F000000000000 /* hash tag length in bytes */
+#define SPU2_HASH_TAG_LEN_SHIFT 48
+#define SPU2_RETURN_MD 0x300000000000000 /* return metadata */
+#define SPU2_RETURN_MD_SHIFT 56
+#define SPU2_RETURN_FD 0x400000000000000
+#define SPU2_RETURN_AAD1 0x800000000000000
+#define SPU2_RETURN_NAAD 0x1000000000000000
+#define SPU2_RETURN_AAD2 0x2000000000000000
+#define SPU2_RETURN_PAY 0x4000000000000000 /* return payload */
+
+/* FMD ctrl2 field masks */
+#define SPU2_AAD1_OFFSET 0xFFF /* byte offset of AAD1 field */
+#define SPU2_AAD1_LEN 0xFF000 /* length of AAD1 in bytes */
+#define SPU2_AAD1_LEN_SHIFT 12
+#define SPU2_AAD2_OFFSET 0xFFF00000 /* byte offset of AAD2 field */
+#define SPU2_AAD2_OFFSET_SHIFT 20
+#define SPU2_PL_OFFSET 0xFFFFFFFF00000000 /* payload offset from AAD2 */
+#define SPU2_PL_OFFSET_SHIFT 32
+
+/* FMD ctrl3 field masks */
+#define SPU2_PL_LEN 0xFFFFFFFF /* payload length in bytes */
+#define SPU2_TLS_LEN 0xFFFF00000000 /* TLS encrypt: cipher len
+ * TLS decrypt: compressed len
+ */
+#define SPU2_TLS_LEN_SHIFT 32
+
+/*
+ * Max value that can be represented in the Payload Length field of the
+ * ctrl3 word of FMD.
+ */
+#define SPU2_MAX_PAYLOAD SPU2_PL_LEN
+
+/* Error values returned in STATUS field of response messages */
+#define SPU2_INVALID_ICV 1
+
+void spu2_dump_msg_hdr(u8 *buf, unsigned int buf_len);
+u32 spu2_ctx_max_payload(enum spu_cipher_alg cipher_alg,
+ enum spu_cipher_mode cipher_mode,
+ unsigned int blocksize);
+u32 spu2_payload_length(u8 *spu_hdr);
+u16 spu2_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash);
+u16 spu2_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode,
+ u32 chunksize, u16 hash_block_size);
+u32 spu2_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode,
+ unsigned int data_size);
+u32 spu2_assoc_resp_len(enum spu_cipher_mode cipher_mode,
+ unsigned int assoc_len, unsigned int iv_len,
+ bool is_encrypt);
+u8 spu2_aead_ivlen(enum spu_cipher_mode cipher_mode,
+ u16 iv_len);
+enum hash_type spu2_hash_type(u32 src_sent);
+u32 spu2_digest_size(u32 alg_digest_size, enum hash_alg alg,
+ enum hash_type htype);
+u32 spu2_create_request(u8 *spu_hdr,
+ struct spu_request_opts *req_opts,
+ struct spu_cipher_parms *cipher_parms,
+ struct spu_hash_parms *hash_parms,
+ struct spu_aead_parms *aead_parms,
+ unsigned int data_size);
+u16 spu2_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms);
+void spu2_cipher_req_finish(u8 *spu_hdr,
+ u16 spu_req_hdr_len,
+ unsigned int is_inbound,
+ struct spu_cipher_parms *cipher_parms,
+ bool update_key,
+ unsigned int data_size);
+void spu2_request_pad(u8 *pad_start, u32 gcm_padding, u32 hash_pad_len,
+ enum hash_alg auth_alg, enum hash_mode auth_mode,
+ unsigned int total_sent, u32 status_padding);
+u8 spu2_xts_tweak_in_payload(void);
+u8 spu2_tx_status_len(void);
+u8 spu2_rx_status_len(void);
+int spu2_status_process(u8 *statp);
+void spu2_ccm_update_iv(unsigned int digestsize,
+ struct spu_cipher_parms *cipher_parms,
+ unsigned int assoclen, unsigned int chunksize,
+ bool is_encrypt, bool is_esp);
+u32 spu2_wordalign_padlen(u32 data_size);
+#endif
diff --git a/drivers/crypto/bcm/spum.h b/drivers/crypto/bcm/spum.h
new file mode 100644
index 0000000..d0a5b58
--- /dev/null
+++ b/drivers/crypto/bcm/spum.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+/*
+ * This file contains SPU message definitions specific to SPU-M.
+ */
+
+#ifndef _SPUM_H_
+#define _SPUM_H_
+
+#define SPU_CRYPTO_OPERATION_GENERIC 0x1
+
+/* Length of STATUS field in tx and rx packets */
+#define SPU_TX_STATUS_LEN 4
+
+/* SPU-M error codes */
+#define SPU_STATUS_MASK 0x0000FF00
+#define SPU_STATUS_SUCCESS 0x00000000
+#define SPU_STATUS_INVALID_ICV 0x00000100
+
+#define SPU_STATUS_ERROR_FLAG 0x00020000
+
+/* Request message. MH + EMH + BDESC + BD header */
+#define SPU_REQ_FIXED_LEN 24
+
+/*
+ * Max length of a SPU message header. Used to allocate a buffer where
+ * the SPU message header is constructed. Can be used for either a SPU-M
+ * header or a SPU2 header.
+ * For SPU-M, sum of the following:
+ * MH - 4 bytes
+ * EMH - 4
+ * SCTX - 3 +
+ * max auth key len - 64
+ * max cipher key len - 264 (RC4)
+ * max IV len - 16
+ * BDESC - 12
+ * BD header - 4
+ * Total: 371
+ *
+ * For SPU2, FMD_SIZE (32) plus lengths of hash and cipher keys,
+ * hash and cipher IVs. If SPU2 does not support RC4, then
+ */
+#define SPU_HEADER_ALLOC_LEN (SPU_REQ_FIXED_LEN + MAX_KEY_SIZE + \
+ MAX_KEY_SIZE + MAX_IV_SIZE)
+
+/*
+ * Response message header length. Normally MH, EMH, BD header, but when
+ * BD_SUPPRESS is used for hash requests, there is no BD header.
+ */
+#define SPU_RESP_HDR_LEN 12
+#define SPU_HASH_RESP_HDR_LEN 8
+
+/*
+ * Max value that can be represented in the Payload Length field of the BD
+ * header. This is a 16-bit field.
+ */
+#define SPUM_NS2_MAX_PAYLOAD (BIT(16) - 1)
+
+/*
+ * NSP SPU is limited to ~9KB because of FA2 FIFO size limitations;
+ * Set MAX_PAYLOAD to 8k to allow for addition of header, digest, etc.
+ * and stay within limitation.
+ */
+
+#define SPUM_NSP_MAX_PAYLOAD 8192
+
+/* Buffer Descriptor Header [BDESC]. SPU in big-endian mode. */
+struct BDESC_HEADER {
+ u16 offset_mac; /* word 0 [31-16] */
+ u16 length_mac; /* word 0 [15-0] */
+ u16 offset_crypto; /* word 1 [31-16] */
+ u16 length_crypto; /* word 1 [15-0] */
+ u16 offset_icv; /* word 2 [31-16] */
+ u16 offset_iv; /* word 2 [15-0] */
+};
+
+/* Buffer Data Header [BD]. SPU in big-endian mode. */
+struct BD_HEADER {
+ u16 size;
+ u16 prev_length;
+};
+
+/* Command Context Header. SPU-M in big endian mode. */
+struct MHEADER {
+ u8 flags; /* [31:24] */
+ u8 op_code; /* [23:16] */
+ u16 reserved; /* [15:0] */
+};
+
+/* MH header flags bits */
+#define MH_SUPDT_PRES BIT(0)
+#define MH_HASH_PRES BIT(2)
+#define MH_BD_PRES BIT(3)
+#define MH_MFM_PRES BIT(4)
+#define MH_BDESC_PRES BIT(5)
+#define MH_SCTX_PRES BIT(7)
+
+/* SCTX word 0 bit offsets and fields masks */
+#define SCTX_SIZE 0x000000FF
+
+/* SCTX word 1 bit shifts and field masks */
+#define UPDT_OFST 0x000000FF /* offset of SCTX updateable fld */
+#define HASH_TYPE 0x00000300 /* hash alg operation type */
+#define HASH_TYPE_SHIFT 8
+#define HASH_MODE 0x00001C00 /* one of spu2_hash_mode */
+#define HASH_MODE_SHIFT 10
+#define HASH_ALG 0x0000E000 /* hash algorithm */
+#define HASH_ALG_SHIFT 13
+#define CIPHER_TYPE 0x00030000 /* encryption operation type */
+#define CIPHER_TYPE_SHIFT 16
+#define CIPHER_MODE 0x001C0000 /* encryption mode */
+#define CIPHER_MODE_SHIFT 18
+#define CIPHER_ALG 0x00E00000 /* encryption algo */
+#define CIPHER_ALG_SHIFT 21
+#define ICV_IS_512 BIT(27)
+#define ICV_IS_512_SHIFT 27
+#define CIPHER_ORDER BIT(30)
+#define CIPHER_ORDER_SHIFT 30
+#define CIPHER_INBOUND BIT(31)
+#define CIPHER_INBOUND_SHIFT 31
+
+/* SCTX word 2 bit shifts and field masks */
+#define EXP_IV_SIZE 0x7
+#define IV_OFFSET BIT(3)
+#define IV_OFFSET_SHIFT 3
+#define GEN_IV BIT(5)
+#define GEN_IV_SHIFT 5
+#define EXPLICIT_IV BIT(6)
+#define EXPLICIT_IV_SHIFT 6
+#define SCTX_IV BIT(7)
+#define SCTX_IV_SHIFT 7
+#define ICV_SIZE 0x0F00
+#define ICV_SIZE_SHIFT 8
+#define CHECK_ICV BIT(12)
+#define CHECK_ICV_SHIFT 12
+#define INSERT_ICV BIT(13)
+#define INSERT_ICV_SHIFT 13
+#define BD_SUPPRESS BIT(19)
+#define BD_SUPPRESS_SHIFT 19
+
+/* Generic Mode Security Context Structure [SCTX] */
+struct SCTX {
+/* word 0: protocol flags */
+ u32 proto_flags;
+
+/* word 1: cipher flags */
+ u32 cipher_flags;
+
+/* word 2: Extended cipher flags */
+ u32 ecf;
+
+};
+
+struct SPUHEADER {
+ struct MHEADER mh;
+ u32 emh;
+ struct SCTX sa;
+};
+
+#endif /* _SPUM_H_ */
diff --git a/drivers/crypto/bcm/util.c b/drivers/crypto/bcm/util.c
new file mode 100644
index 0000000..0502f46
--- /dev/null
+++ b/drivers/crypto/bcm/util.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#include <linux/debugfs.h>
+
+#include "cipher.h"
+#include "util.h"
+
+/* offset of SPU_OFIFO_CTRL register */
+#define SPU_OFIFO_CTRL 0x40
+#define SPU_FIFO_WATERMARK 0x1FF
+
+/**
+ * spu_sg_at_offset() - Find the scatterlist entry at a given distance from the
+ * start of a scatterlist.
+ * @sg: [in] Start of a scatterlist
+ * @skip: [in] Distance from the start of the scatterlist, in bytes
+ * @sge: [out] Scatterlist entry at skip bytes from start
+ * @sge_offset: [out] Number of bytes from start of sge buffer to get to
+ * requested distance.
+ *
+ * Return: 0 if entry found at requested distance
+ * < 0 otherwise
+ */
+int spu_sg_at_offset(struct scatterlist *sg, unsigned int skip,
+ struct scatterlist **sge, unsigned int *sge_offset)
+{
+ /* byte index from start of sg to the end of the previous entry */
+ unsigned int index = 0;
+ /* byte index from start of sg to the end of the current entry */
+ unsigned int next_index;
+
+ next_index = sg->length;
+ while (next_index <= skip) {
+ sg = sg_next(sg);
+ index = next_index;
+ if (!sg)
+ return -EINVAL;
+ next_index += sg->length;
+ }
+
+ *sge_offset = skip - index;
+ *sge = sg;
+ return 0;
+}
+
+/* Copy len bytes of sg data, starting at offset skip, to a dest buffer */
+void sg_copy_part_to_buf(struct scatterlist *src, u8 *dest,
+ unsigned int len, unsigned int skip)
+{
+ size_t copied;
+ unsigned int nents = sg_nents(src);
+
+ copied = sg_pcopy_to_buffer(src, nents, dest, len, skip);
+ if (copied != len) {
+ flow_log("%s copied %u bytes of %u requested. ",
+ __func__, (u32)copied, len);
+ flow_log("sg with %u entries and skip %u\n", nents, skip);
+ }
+}
+
+/*
+ * Copy data into a scatterlist starting at a specified offset in the
+ * scatterlist. Specifically, copy len bytes of data in the buffer src
+ * into the scatterlist dest, starting skip bytes into the scatterlist.
+ */
+void sg_copy_part_from_buf(struct scatterlist *dest, u8 *src,
+ unsigned int len, unsigned int skip)
+{
+ size_t copied;
+ unsigned int nents = sg_nents(dest);
+
+ copied = sg_pcopy_from_buffer(dest, nents, src, len, skip);
+ if (copied != len) {
+ flow_log("%s copied %u bytes of %u requested. ",
+ __func__, (u32)copied, len);
+ flow_log("sg with %u entries and skip %u\n", nents, skip);
+ }
+}
+
+/**
+ * spu_sg_count() - Determine number of elements in scatterlist to provide a
+ * specified number of bytes.
+ * @sg_list: scatterlist to examine
+ * @skip: index of starting point
+ * @nbytes: consider elements of scatterlist until reaching this number of
+ * bytes
+ *
+ * Return: the number of sg entries contributing to nbytes of data
+ */
+int spu_sg_count(struct scatterlist *sg_list, unsigned int skip, int nbytes)
+{
+ struct scatterlist *sg;
+ int sg_nents = 0;
+ unsigned int offset;
+
+ if (!sg_list)
+ return 0;
+
+ if (spu_sg_at_offset(sg_list, skip, &sg, &offset) < 0)
+ return 0;
+
+ while (sg && (nbytes > 0)) {
+ sg_nents++;
+ nbytes -= (sg->length - offset);
+ offset = 0;
+ sg = sg_next(sg);
+ }
+ return sg_nents;
+}
+
+/**
+ * spu_msg_sg_add() - Copy scatterlist entries from one sg to another, up to a
+ * given length.
+ * @to_sg: scatterlist to copy to
+ * @from_sg: scatterlist to copy from
+ * @from_skip: number of bytes to skip in from_sg. Non-zero when previous
+ * request included part of the buffer in entry in from_sg.
+ * Assumes from_skip < from_sg->length.
+ * @from_nents number of entries in from_sg
+ * @length number of bytes to copy. may reach this limit before exhausting
+ * from_sg.
+ *
+ * Copies the entries themselves, not the data in the entries. Assumes to_sg has
+ * enough entries. Does not limit the size of an individual buffer in to_sg.
+ *
+ * to_sg, from_sg, skip are all updated to end of copy
+ *
+ * Return: Number of bytes copied
+ */
+u32 spu_msg_sg_add(struct scatterlist **to_sg,
+ struct scatterlist **from_sg, u32 *from_skip,
+ u8 from_nents, u32 length)
+{
+ struct scatterlist *sg; /* an entry in from_sg */
+ struct scatterlist *to = *to_sg;
+ struct scatterlist *from = *from_sg;
+ u32 skip = *from_skip;
+ u32 offset;
+ int i;
+ u32 entry_len = 0;
+ u32 frag_len = 0; /* length of entry added to to_sg */
+ u32 copied = 0; /* number of bytes copied so far */
+
+ if (length == 0)
+ return 0;
+
+ for_each_sg(from, sg, from_nents, i) {
+ /* number of bytes in this from entry not yet used */
+ entry_len = sg->length - skip;
+ frag_len = min(entry_len, length - copied);
+ offset = sg->offset + skip;
+ if (frag_len)
+ sg_set_page(to++, sg_page(sg), frag_len, offset);
+ copied += frag_len;
+ if (copied == entry_len) {
+ /* used up all of from entry */
+ skip = 0; /* start at beginning of next entry */
+ }
+ if (copied == length)
+ break;
+ }
+ *to_sg = to;
+ *from_sg = sg;
+ if (frag_len < entry_len)
+ *from_skip = skip + frag_len;
+ else
+ *from_skip = 0;
+
+ return copied;
+}
+
+void add_to_ctr(u8 *ctr_pos, unsigned int increment)
+{
+ __be64 *high_be = (__be64 *)ctr_pos;
+ __be64 *low_be = high_be + 1;
+ u64 orig_low = __be64_to_cpu(*low_be);
+ u64 new_low = orig_low + (u64)increment;
+
+ *low_be = __cpu_to_be64(new_low);
+ if (new_low < orig_low)
+ /* there was a carry from the low 8 bytes */
+ *high_be = __cpu_to_be64(__be64_to_cpu(*high_be) + 1);
+}
+
+struct sdesc {
+ struct shash_desc shash;
+ char ctx[];
+};
+
+/* do a synchronous decrypt operation */
+int do_decrypt(char *alg_name,
+ void *key_ptr, unsigned int key_len,
+ void *iv_ptr, void *src_ptr, void *dst_ptr,
+ unsigned int block_len)
+{
+ struct scatterlist sg_in[1], sg_out[1];
+ struct crypto_blkcipher *tfm =
+ crypto_alloc_blkcipher(alg_name, 0, CRYPTO_ALG_ASYNC);
+ struct blkcipher_desc desc = {.tfm = tfm, .flags = 0 };
+ int ret = 0;
+ void *iv;
+ int ivsize;
+
+ flow_log("%s() name:%s block_len:%u\n", __func__, alg_name, block_len);
+
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ crypto_blkcipher_setkey((void *)tfm, key_ptr, key_len);
+
+ sg_init_table(sg_in, 1);
+ sg_set_buf(sg_in, src_ptr, block_len);
+
+ sg_init_table(sg_out, 1);
+ sg_set_buf(sg_out, dst_ptr, block_len);
+
+ iv = crypto_blkcipher_crt(tfm)->iv;
+ ivsize = crypto_blkcipher_ivsize(tfm);
+ memcpy(iv, iv_ptr, ivsize);
+
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, block_len);
+ crypto_free_blkcipher(tfm);
+
+ if (ret < 0)
+ pr_err("aes_decrypt failed %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * do_shash() - Do a synchronous hash operation in software
+ * @name: The name of the hash algorithm
+ * @result: Buffer where digest is to be written
+ * @data1: First part of data to hash. May be NULL.
+ * @data1_len: Length of data1, in bytes
+ * @data2: Second part of data to hash. May be NULL.
+ * @data2_len: Length of data2, in bytes
+ * @key: Key (if keyed hash)
+ * @key_len: Length of key, in bytes (or 0 if non-keyed hash)
+ *
+ * Note that the crypto API will not select this driver's own transform because
+ * this driver only registers asynchronous algos.
+ *
+ * Return: 0 if hash successfully stored in result
+ * < 0 otherwise
+ */
+int do_shash(unsigned char *name, unsigned char *result,
+ const u8 *data1, unsigned int data1_len,
+ const u8 *data2, unsigned int data2_len,
+ const u8 *key, unsigned int key_len)
+{
+ int rc;
+ unsigned int size;
+ struct crypto_shash *hash;
+ struct sdesc *sdesc;
+
+ hash = crypto_alloc_shash(name, 0, 0);
+ if (IS_ERR(hash)) {
+ rc = PTR_ERR(hash);
+ pr_err("%s: Crypto %s allocation error %d", __func__, name, rc);
+ return rc;
+ }
+
+ size = sizeof(struct shash_desc) + crypto_shash_descsize(hash);
+ sdesc = kmalloc(size, GFP_KERNEL);
+ if (!sdesc) {
+ rc = -ENOMEM;
+ pr_err("%s: Memory allocation failure", __func__);
+ goto do_shash_err;
+ }
+ sdesc->shash.tfm = hash;
+ sdesc->shash.flags = 0x0;
+
+ if (key_len > 0) {
+ rc = crypto_shash_setkey(hash, key, key_len);
+ if (rc) {
+ pr_err("%s: Could not setkey %s shash", __func__, name);
+ goto do_shash_err;
+ }
+ }
+
+ rc = crypto_shash_init(&sdesc->shash);
+ if (rc) {
+ pr_err("%s: Could not init %s shash", __func__, name);
+ goto do_shash_err;
+ }
+ rc = crypto_shash_update(&sdesc->shash, data1, data1_len);
+ if (rc) {
+ pr_err("%s: Could not update1", __func__);
+ goto do_shash_err;
+ }
+ if (data2 && data2_len) {
+ rc = crypto_shash_update(&sdesc->shash, data2, data2_len);
+ if (rc) {
+ pr_err("%s: Could not update2", __func__);
+ goto do_shash_err;
+ }
+ }
+ rc = crypto_shash_final(&sdesc->shash, result);
+ if (rc)
+ pr_err("%s: Could not genereate %s hash", __func__, name);
+
+do_shash_err:
+ crypto_free_shash(hash);
+ kfree(sdesc);
+
+ return rc;
+}
+
+/* Dump len bytes of a scatterlist starting at skip bytes into the sg */
+void __dump_sg(struct scatterlist *sg, unsigned int skip, unsigned int len)
+{
+ u8 dbuf[16];
+ unsigned int idx = skip;
+ unsigned int num_out = 0; /* number of bytes dumped so far */
+ unsigned int count;
+
+ if (packet_debug_logging) {
+ while (num_out < len) {
+ count = (len - num_out > 16) ? 16 : len - num_out;
+ sg_copy_part_to_buf(sg, dbuf, count, idx);
+ num_out += count;
+ print_hex_dump(KERN_ALERT, " sg: ", DUMP_PREFIX_NONE,
+ 4, 1, dbuf, count, false);
+ idx += 16;
+ }
+ }
+ if (debug_logging_sleep)
+ msleep(debug_logging_sleep);
+}
+
+/* Returns the name for a given cipher alg/mode */
+char *spu_alg_name(enum spu_cipher_alg alg, enum spu_cipher_mode mode)
+{
+ switch (alg) {
+ case CIPHER_ALG_RC4:
+ return "rc4";
+ case CIPHER_ALG_AES:
+ switch (mode) {
+ case CIPHER_MODE_CBC:
+ return "cbc(aes)";
+ case CIPHER_MODE_ECB:
+ return "ecb(aes)";
+ case CIPHER_MODE_OFB:
+ return "ofb(aes)";
+ case CIPHER_MODE_CFB:
+ return "cfb(aes)";
+ case CIPHER_MODE_CTR:
+ return "ctr(aes)";
+ case CIPHER_MODE_XTS:
+ return "xts(aes)";
+ case CIPHER_MODE_GCM:
+ return "gcm(aes)";
+ default:
+ return "aes";
+ }
+ break;
+ case CIPHER_ALG_DES:
+ switch (mode) {
+ case CIPHER_MODE_CBC:
+ return "cbc(des)";
+ case CIPHER_MODE_ECB:
+ return "ecb(des)";
+ case CIPHER_MODE_CTR:
+ return "ctr(des)";
+ default:
+ return "des";
+ }
+ break;
+ case CIPHER_ALG_3DES:
+ switch (mode) {
+ case CIPHER_MODE_CBC:
+ return "cbc(des3_ede)";
+ case CIPHER_MODE_ECB:
+ return "ecb(des3_ede)";
+ case CIPHER_MODE_CTR:
+ return "ctr(des3_ede)";
+ default:
+ return "3des";
+ }
+ break;
+ default:
+ return "other";
+ }
+}
+
+static ssize_t spu_debugfs_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *offp)
+{
+ struct device_private *ipriv;
+ char *buf;
+ ssize_t ret, out_offset, out_count;
+ int i;
+ u32 fifo_len;
+ u32 spu_ofifo_ctrl;
+ u32 alg;
+ u32 mode;
+ u32 op_cnt;
+
+ out_count = 2048;
+
+ buf = kmalloc(out_count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ipriv = filp->private_data;
+ out_offset = 0;
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Number of SPUs.........%u\n",
+ ipriv->spu.num_spu);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Current sessions.......%u\n",
+ atomic_read(&ipriv->session_count));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Session count..........%u\n",
+ atomic_read(&ipriv->stream_count));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Cipher setkey..........%u\n",
+ atomic_read(&ipriv->setkey_cnt[SPU_OP_CIPHER]));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Cipher Ops.............%u\n",
+ atomic_read(&ipriv->op_counts[SPU_OP_CIPHER]));
+ for (alg = 0; alg < CIPHER_ALG_LAST; alg++) {
+ for (mode = 0; mode < CIPHER_MODE_LAST; mode++) {
+ op_cnt = atomic_read(&ipriv->cipher_cnt[alg][mode]);
+ if (op_cnt) {
+ out_offset += snprintf(buf + out_offset,
+ out_count - out_offset,
+ " %-13s%11u\n",
+ spu_alg_name(alg, mode), op_cnt);
+ }
+ }
+ }
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Hash Ops...............%u\n",
+ atomic_read(&ipriv->op_counts[SPU_OP_HASH]));
+ for (alg = 0; alg < HASH_ALG_LAST; alg++) {
+ op_cnt = atomic_read(&ipriv->hash_cnt[alg]);
+ if (op_cnt) {
+ out_offset += snprintf(buf + out_offset,
+ out_count - out_offset,
+ " %-13s%11u\n",
+ hash_alg_name[alg], op_cnt);
+ }
+ }
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "HMAC setkey............%u\n",
+ atomic_read(&ipriv->setkey_cnt[SPU_OP_HMAC]));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "HMAC Ops...............%u\n",
+ atomic_read(&ipriv->op_counts[SPU_OP_HMAC]));
+ for (alg = 0; alg < HASH_ALG_LAST; alg++) {
+ op_cnt = atomic_read(&ipriv->hmac_cnt[alg]);
+ if (op_cnt) {
+ out_offset += snprintf(buf + out_offset,
+ out_count - out_offset,
+ " %-13s%11u\n",
+ hash_alg_name[alg], op_cnt);
+ }
+ }
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "AEAD setkey............%u\n",
+ atomic_read(&ipriv->setkey_cnt[SPU_OP_AEAD]));
+
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "AEAD Ops...............%u\n",
+ atomic_read(&ipriv->op_counts[SPU_OP_AEAD]));
+ for (alg = 0; alg < AEAD_TYPE_LAST; alg++) {
+ op_cnt = atomic_read(&ipriv->aead_cnt[alg]);
+ if (op_cnt) {
+ out_offset += snprintf(buf + out_offset,
+ out_count - out_offset,
+ " %-13s%11u\n",
+ aead_alg_name[alg], op_cnt);
+ }
+ }
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Bytes of req data......%llu\n",
+ (u64)atomic64_read(&ipriv->bytes_out));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Bytes of resp data.....%llu\n",
+ (u64)atomic64_read(&ipriv->bytes_in));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Mailbox full...........%u\n",
+ atomic_read(&ipriv->mb_no_spc));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Mailbox send failures..%u\n",
+ atomic_read(&ipriv->mb_send_fail));
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "Check ICV errors.......%u\n",
+ atomic_read(&ipriv->bad_icv));
+ if (ipriv->spu.spu_type == SPU_TYPE_SPUM)
+ for (i = 0; i < ipriv->spu.num_spu; i++) {
+ spu_ofifo_ctrl = ioread32(ipriv->spu.reg_vbase[i] +
+ SPU_OFIFO_CTRL);
+ fifo_len = spu_ofifo_ctrl & SPU_FIFO_WATERMARK;
+ out_offset += snprintf(buf + out_offset,
+ out_count - out_offset,
+ "SPU %d output FIFO high water.....%u\n",
+ i, fifo_len);
+ }
+
+ if (out_offset > out_count)
+ out_offset = out_count;
+
+ ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations spu_debugfs_stats = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = spu_debugfs_read,
+};
+
+/*
+ * Create the debug FS directories. If the top-level directory has not yet
+ * been created, create it now. Create a stats file in this directory for
+ * a SPU.
+ */
+void spu_setup_debugfs(void)
+{
+ if (!debugfs_initialized())
+ return;
+
+ if (!iproc_priv.debugfs_dir)
+ iproc_priv.debugfs_dir = debugfs_create_dir(KBUILD_MODNAME,
+ NULL);
+
+ if (!iproc_priv.debugfs_stats)
+ /* Create file with permissions S_IRUSR */
+ debugfs_create_file("stats", 0400, iproc_priv.debugfs_dir,
+ &iproc_priv, &spu_debugfs_stats);
+}
+
+void spu_free_debugfs(void)
+{
+ debugfs_remove_recursive(iproc_priv.debugfs_dir);
+ iproc_priv.debugfs_dir = NULL;
+}
+
+/**
+ * format_value_ccm() - Format a value into a buffer, using a specified number
+ * of bytes (i.e. maybe writing value X into a 4 byte
+ * buffer, or maybe into a 12 byte buffer), as per the
+ * SPU CCM spec.
+ *
+ * @val: value to write (up to max of unsigned int)
+ * @buf: (pointer to) buffer to write the value
+ * @len: number of bytes to use (0 to 255)
+ *
+ */
+void format_value_ccm(unsigned int val, u8 *buf, u8 len)
+{
+ int i;
+
+ /* First clear full output buffer */
+ memset(buf, 0, len);
+
+ /* Then, starting from right side, fill in with data */
+ for (i = 0; i < len; i++) {
+ buf[len - i - 1] = (val >> (8 * i)) & 0xff;
+ if (i >= 3)
+ break; /* Only handle up to 32 bits of 'val' */
+ }
+}
diff --git a/drivers/crypto/bcm/util.h b/drivers/crypto/bcm/util.h
new file mode 100644
index 0000000..712e029
--- /dev/null
+++ b/drivers/crypto/bcm/util.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation (the "GPL").
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include "spu.h"
+
+extern int flow_debug_logging;
+extern int packet_debug_logging;
+extern int debug_logging_sleep;
+
+#ifdef DEBUG
+#define flow_log(...) \
+ do { \
+ if (flow_debug_logging) { \
+ printk(__VA_ARGS__); \
+ if (debug_logging_sleep) \
+ msleep(debug_logging_sleep); \
+ } \
+ } while (0)
+#define flow_dump(msg, var, var_len) \
+ do { \
+ if (flow_debug_logging) { \
+ print_hex_dump(KERN_ALERT, msg, DUMP_PREFIX_NONE, \
+ 16, 1, var, var_len, false); \
+ if (debug_logging_sleep) \
+ msleep(debug_logging_sleep); \
+ } \
+ } while (0)
+
+#define packet_log(...) \
+ do { \
+ if (packet_debug_logging) { \
+ printk(__VA_ARGS__); \
+ if (debug_logging_sleep) \
+ msleep(debug_logging_sleep); \
+ } \
+ } while (0)
+#define packet_dump(msg, var, var_len) \
+ do { \
+ if (packet_debug_logging) { \
+ print_hex_dump(KERN_ALERT, msg, DUMP_PREFIX_NONE, \
+ 16, 1, var, var_len, false); \
+ if (debug_logging_sleep) \
+ msleep(debug_logging_sleep); \
+ } \
+ } while (0)
+
+void __dump_sg(struct scatterlist *sg, unsigned int skip, unsigned int len);
+
+#define dump_sg(sg, skip, len) __dump_sg(sg, skip, len)
+
+#else /* !DEBUG_ON */
+
+#define flow_log(...) do {} while (0)
+#define flow_dump(msg, var, var_len) do {} while (0)
+#define packet_log(...) do {} while (0)
+#define packet_dump(msg, var, var_len) do {} while (0)
+
+#define dump_sg(sg, skip, len) do {} while (0)
+
+#endif /* DEBUG_ON */
+
+int spu_sg_at_offset(struct scatterlist *sg, unsigned int skip,
+ struct scatterlist **sge, unsigned int *sge_offset);
+
+/* Copy sg data, from skip, length len, to dest */
+void sg_copy_part_to_buf(struct scatterlist *src, u8 *dest,
+ unsigned int len, unsigned int skip);
+/* Copy src into scatterlist from offset, length len */
+void sg_copy_part_from_buf(struct scatterlist *dest, u8 *src,
+ unsigned int len, unsigned int skip);
+
+int spu_sg_count(struct scatterlist *sg_list, unsigned int skip, int nbytes);
+u32 spu_msg_sg_add(struct scatterlist **to_sg,
+ struct scatterlist **from_sg, u32 *skip,
+ u8 from_nents, u32 tot_len);
+
+void add_to_ctr(u8 *ctr_pos, unsigned int increment);
+
+/* do a synchronous decrypt operation */
+int do_decrypt(char *alg_name,
+ void *key_ptr, unsigned int key_len,
+ void *iv_ptr, void *src_ptr, void *dst_ptr,
+ unsigned int block_len);
+
+/* produce a message digest from data of length n bytes */
+int do_shash(unsigned char *name, unsigned char *result,
+ const u8 *data1, unsigned int data1_len,
+ const u8 *data2, unsigned int data2_len,
+ const u8 *key, unsigned int key_len);
+
+char *spu_alg_name(enum spu_cipher_alg alg, enum spu_cipher_mode mode);
+
+void spu_setup_debugfs(void);
+void spu_free_debugfs(void);
+void format_value_ccm(unsigned int val, u8 *buf, u8 len);
+
+#endif
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 1/3] crypto: brcm: DT documentation for Broadcom SPU hardware
From: Rob Rice @ 2017-01-25 16:44 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Rob Herring, Mark Rutland,
linux-crypto, devicetree, linux-kernel, bcm-kernel-feedback-list,
Catalin Marinas, Will Deacon, linux-arm-kernel
Cc: Rob Rice, Steve Lin
In-Reply-To: <1485362689-11294-1-git-send-email-rob.rice@broadcom.com>
Device tree documentation for Broadcom Secure Processing Unit
(SPU) crypto hardware.
Signed-off-by: Steve Lin <steven.lin1@broadcom.com>
Signed-off-by: Rob Rice <rob.rice@broadcom.com>
---
.../devicetree/bindings/crypto/brcm,spu-crypto.txt | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 Documentation/devicetree/bindings/crypto/brcm,spu-crypto.txt
diff --git a/Documentation/devicetree/bindings/crypto/brcm,spu-crypto.txt b/Documentation/devicetree/bindings/crypto/brcm,spu-crypto.txt
new file mode 100644
index 0000000..29b6007
--- /dev/null
+++ b/Documentation/devicetree/bindings/crypto/brcm,spu-crypto.txt
@@ -0,0 +1,22 @@
+The Broadcom Secure Processing Unit (SPU) hardware supports symmetric
+cryptographic offload for Broadcom SoCs. A SoC may have multiple SPU hardware
+blocks.
+
+Required properties:
+- compatible: Should be one of the following:
+ brcm,spum-crypto - for devices with SPU-M hardware
+ brcm,spu2-crypto - for devices with SPU2 hardware
+ brcm,spu2-v2-crypto - for devices with enhanced SPU2 hardware features like SHA3
+ and Rabin Fingerprint support
+ brcm,spum-nsp-crypto - for the Northstar Plus variant of the SPU-M hardware
+
+- reg: Should contain SPU registers location and length.
+- mboxes: The mailbox channel to be used to communicate with the SPU.
+ Mailbox channels correspond to DMA rings on the device.
+
+Example:
+ crypto@612d0000 {
+ compatible = "brcm,spum-crypto";
+ reg = <0 0x612d0000 0 0x900>;
+ mboxes = <&pdc0 0>;
+ };
--
2.1.0
^ permalink raw reply related
* [PATCH v3 0/3] Add Broadcom SPU Crypto Driver
From: Rob Rice @ 2017-01-25 16:44 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Rob Herring, Mark Rutland,
linux-crypto-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, Catalin Marinas,
Will Deacon, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Rob Rice
Changes in v3:
- rebase to 4.10-rc3 in cryptodev-2.6 tree
- in bindings doc, list all valid compatibility strings
- rename DT nodes "crypto" rather than "spu-crypto"
- include a separate DT node for each SPU hardware block. Previously,
there was a single SPU node listing a register range for each
hw block.
- select hash algos in Kconfig. Driver HMAC implementation uses hash
sw algos for inner and outer hashes.
- Fix crash for AES CCM decrypt when AAD and data are both empty
Change in v2:
- select CRYPTO_DES in Kconfig
The Broadcom SPU crypto driver provides access to SPU hardware
for symmetric crypto offload. The driver supports ablkcipher,
ahash, and aead operations. The driver supports several
Broadcom SoCs with different revisions of the SPU hardware.
The driver supports SPU-M and SPU2 hardware revisions, and
a couple versions of each hw revision, each version with minor
differences.
Rob Rice (3):
crypto: brcm: DT documentation for Broadcom SPU hardware
crypto: brcm: Add Broadcom SPU driver
arm64: dts: ns2: Add Broadcom SPU driver DT entry.
.../devicetree/bindings/crypto/brcm,spu-crypto.txt | 22 +
arch/arm64/boot/dts/broadcom/ns2.dtsi | 24 +
drivers/crypto/Kconfig | 15 +
drivers/crypto/Makefile | 2 +
drivers/crypto/bcm/Makefile | 15 +
drivers/crypto/bcm/cipher.c | 4955 ++++++++++++++++++++
drivers/crypto/bcm/cipher.h | 475 ++
drivers/crypto/bcm/spu.c | 1252 +++++
drivers/crypto/bcm/spu.h | 288 ++
drivers/crypto/bcm/spu2.c | 1402 ++++++
drivers/crypto/bcm/spu2.h | 228 +
drivers/crypto/bcm/spum.h | 174 +
drivers/crypto/bcm/util.c | 581 +++
drivers/crypto/bcm/util.h | 116 +
14 files changed, 9549 insertions(+)
create mode 100644 Documentation/devicetree/bindings/crypto/brcm,spu-crypto.txt
create mode 100644 drivers/crypto/bcm/Makefile
create mode 100644 drivers/crypto/bcm/cipher.c
create mode 100644 drivers/crypto/bcm/cipher.h
create mode 100644 drivers/crypto/bcm/spu.c
create mode 100644 drivers/crypto/bcm/spu.h
create mode 100644 drivers/crypto/bcm/spu2.c
create mode 100644 drivers/crypto/bcm/spu2.h
create mode 100644 drivers/crypto/bcm/spum.h
create mode 100644 drivers/crypto/bcm/util.c
create mode 100644 drivers/crypto/bcm/util.h
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] crypto: doc - Fix hash export state information
From: Stephan Müller @ 2017-01-25 16:18 UTC (permalink / raw)
To: Rabin Vincent; +Cc: herbert, linux-crypto, Rabin Vincent
In-Reply-To: <1485359706-7184-1-git-send-email-rabin.vincent@axis.com>
Am Mittwoch, 25. Januar 2017, 16:55:06 CET schrieb Rabin Vincent:
Hi Rabin,
> From: Rabin Vincent <rabinv@axis.com>
>
> The documentation states that crypto_ahash_reqsize() provides the size
> of the state structure used by crypto_ahash_export(). But it's actually
> crypto_ahash_statesize() which provides this size.
ack for the statesize documentation addition.
In addition, can you please add crypto_ahash_statesize to Documentation/
crypto/api-digest.rst.
nack - The documentation is quite right for reqsize: it returns the *request
data structure* size as documented.
Ciao
Stephan
^ permalink raw reply
* [PATCH] crypto: doc - Fix hash export state information
From: Rabin Vincent @ 2017-01-25 15:55 UTC (permalink / raw)
To: herbert; +Cc: linux-crypto, smueller, Rabin Vincent
From: Rabin Vincent <rabinv@axis.com>
The documentation states that crypto_ahash_reqsize() provides the size
of the state structure used by crypto_ahash_export(). But it's actually
crypto_ahash_statesize() which provides this size.
Signed-off-by: Rabin Vincent <rabinv@axis.com>
---
include/crypto/hash.h | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
index 216a2b8..26ebbbc 100644
--- a/include/crypto/hash.h
+++ b/include/crypto/hash.h
@@ -329,6 +329,16 @@ static inline unsigned int crypto_ahash_digestsize(struct crypto_ahash *tfm)
return crypto_hash_alg_common(tfm)->digestsize;
}
+/**
+ * crypto_ahash_statesize() - obtain size of the ahash state
+ * @tfm: cipher handle
+ *
+ * Return the size of the ahash state. With the crypto_ahash_export
+ * function, the caller can export the state into a buffer whose size is
+ * defined with this function.
+ *
+ * Return: size of the ahash state
+ */
static inline unsigned int crypto_ahash_statesize(struct crypto_ahash *tfm)
{
return crypto_hash_alg_common(tfm)->statesize;
@@ -365,16 +375,6 @@ static inline struct crypto_ahash *crypto_ahash_reqtfm(
return __crypto_ahash_cast(req->base.tfm);
}
-/**
- * crypto_ahash_reqsize() - obtain size of the request data structure
- * @tfm: cipher handle
- *
- * Return the size of the ahash state size. With the crypto_ahash_export
- * function, the caller can export the state into a buffer whose size is
- * defined with this function.
- *
- * Return: size of the ahash state
- */
static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm)
{
return tfm->reqsize;
@@ -453,7 +453,7 @@ int crypto_ahash_digest(struct ahash_request *req);
*
* This function exports the hash state of the ahash_request handle into the
* caller-allocated output buffer out which must have sufficient size (e.g. by
- * calling crypto_ahash_reqsize).
+ * calling crypto_ahash_statesize).
*
* Return: 0 if the export was successful; < 0 if an error occurred
*/
--
2.1.4
^ permalink raw reply related
* Re: [PATCH v4 1/4] lib: Update LZ4 compressor module
From: Sven Schmidt @ 2017-01-24 16:48 UTC (permalink / raw)
To: Andrew Morton
Cc: bongkyu.kim, rsalvaterra, sergey.senozhatsky, gregkh,
linux-kernel, herbert, davem, linux-crypto, anton, ccross,
keescook, tony.luck, phillip
In-Reply-To: <20170123162357.69f8b6a8717af086a03b512e@linux-foundation.org>
On Mon, Jan 23, 2017 at 04:23:57PM -0800, Andrew Morton wrote:
> On Sun, 22 Jan 2017 20:35:14 +0100 Sven Schmidt <4sschmid@informatik.uni-hamburg.de> wrote:
>
> > This patch updates LZ4 kernel module to LZ4 v1.7.3 by Yann Collet.
> > The kernel module is inspired by the previous work by Chanho Min.
> > The updated LZ4 module will not break existing code since there were alias
> > methods added to ensure backwards compatibility.
> >
> > API changes:
> >
> > New method LZ4_compress_fast which differs from the variant available
> > in kernel by the new acceleration parameter,
> > allowing to trade compression ratio for more compression speed
> > and vice versa.
> >
> > LZ4_decompress_fast is the respective decompression method, featuring a very
> > fast decoder (multiple GB/s per core), able to reach RAM speed in multi-core
> > systems. The decompressor allows to decompress data compressed with
> > LZ4 fast as well as the LZ4 HC (high compression) algorithm.
> >
> > Also the useful functions LZ4_decompress_safe_partial
> > LZ4_compress_destsize were added. The latter reverses the logic by trying to
> > compress as much data as possible from source to dest while the former aims
> > to decompress partial blocks of data.
> >
> > A bunch of streaming functions were also added
> > which allow compressig/decompressing data in multiple steps
> > (so called "streaming mode").
> >
> > The methods lz4_compress and lz4_decompress_unknownoutputsize
> > are now known as LZ4_compress_default respectivley LZ4_decompress_safe.
> > The old methods are still available for providing backwards compatibility.
> >
> > ...
> >
> > +/*
> > + * For backward compatibility
> > + */
> > +static inline int lz4_compressbound(size_t isize)
> > {
>
> Do we actually need the back-compat wrappers? After your other three
> patches we have no callers, correct? If so, we should go ahead and
> eliminate such back-compatibility interfaces.
>
>
> > - return isize + (isize / 255) + 16;
> > + return LZ4_COMPRESSBOUND(isize);
> > }
>
Hey Andrew,
you're absolutely right. Since I changed the callers to use the new functions, we do not need these wrappers anymore.
I will add a patch removing them and send an update.
Thanks,
Sven
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox