gen: Add progressive hash API

Add hash_init(), hash_update() and hash_finish() to the
hash_algo struct. Add hash_lookup_algo() to look up the
struct given an algorithm name.

Signed-off-by: Hung-ying Tyan <tyanh@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
diff --git a/common/hash.c b/common/hash.c
index 872cd85..7627b84 100644
--- a/common/hash.c
+++ b/common/hash.c
@@ -12,6 +12,7 @@
 
 #include <common.h>
 #include <command.h>
+#include <malloc.h>
 #include <hw_sha.h>
 #include <hash.h>
 #include <sha1.h>
@@ -19,6 +20,88 @@
 #include <asm/io.h>
 #include <asm/errno.h>
 
+#ifdef CONFIG_CMD_SHA1SUM
+static int hash_init_sha1(struct hash_algo *algo, void **ctxp)
+{
+	sha1_context *ctx = malloc(sizeof(sha1_context));
+	sha1_starts(ctx);
+	*ctxp = ctx;
+	return 0;
+}
+
+static int hash_update_sha1(struct hash_algo *algo, void *ctx, const void *buf,
+			    unsigned int size, int is_last)
+{
+	sha1_update((sha1_context *)ctx, buf, size);
+	return 0;
+}
+
+static int hash_finish_sha1(struct hash_algo *algo, void *ctx, void *dest_buf,
+			    int size)
+{
+	if (size < algo->digest_size)
+		return -1;
+
+	sha1_finish((sha1_context *)ctx, dest_buf);
+	free(ctx);
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_SHA256
+static int hash_init_sha256(struct hash_algo *algo, void **ctxp)
+{
+	sha256_context *ctx = malloc(sizeof(sha256_context));
+	sha256_starts(ctx);
+	*ctxp = ctx;
+	return 0;
+}
+
+static int hash_update_sha256(struct hash_algo *algo, void *ctx,
+			      const void *buf, unsigned int size, int is_last)
+{
+	sha256_update((sha256_context *)ctx, buf, size);
+	return 0;
+}
+
+static int hash_finish_sha256(struct hash_algo *algo, void *ctx, void
+			      *dest_buf, int size)
+{
+	if (size < algo->digest_size)
+		return -1;
+
+	sha256_finish((sha256_context *)ctx, dest_buf);
+	free(ctx);
+	return 0;
+}
+#endif
+
+static int hash_init_crc32(struct hash_algo *algo, void **ctxp)
+{
+	uint32_t *ctx = malloc(sizeof(uint32_t));
+	*ctx = 0;
+	*ctxp = ctx;
+	return 0;
+}
+
+static int hash_update_crc32(struct hash_algo *algo, void *ctx,
+			     const void *buf, unsigned int size, int is_last)
+{
+	*((uint32_t *)ctx) = crc32(*((uint32_t *)ctx), buf, size);
+	return 0;
+}
+
+static int hash_finish_crc32(struct hash_algo *algo, void *ctx, void *dest_buf,
+			     int size)
+{
+	if (size < algo->digest_size)
+		return -1;
+
+	*((uint32_t *)dest_buf) = *((uint32_t *)ctx);
+	free(ctx);
+	return 0;
+}
+
 /*
  * These are the hash algorithms we support. Chips which support accelerated
  * crypto could perhaps add named version of these algorithms here. Note that
@@ -53,6 +136,9 @@
 		SHA1_SUM_LEN,
 		sha1_csum_wd,
 		CHUNKSZ_SHA1,
+		hash_init_sha1,
+		hash_update_sha1,
+		hash_finish_sha1,
 	},
 #define MULTI_HASH
 #endif
@@ -62,6 +148,9 @@
 		SHA256_SUM_LEN,
 		sha256_csum_wd,
 		CHUNKSZ_SHA256,
+		hash_init_sha256,
+		hash_update_sha256,
+		hash_finish_sha256,
 	},
 #define MULTI_HASH
 #endif
@@ -70,6 +159,9 @@
 		4,
 		crc32_wd_buf,
 		CHUNKSZ_CRC32,
+		hash_init_crc32,
+		hash_update_crc32,
+		hash_finish_crc32,
 	},
 };
 
@@ -204,16 +296,19 @@
 	return 0;
 }
 
-static struct hash_algo *find_hash_algo(const char *name)
+int hash_lookup_algo(const char *algo_name, struct hash_algo **algop)
 {
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(hash_algo); i++) {
-		if (!strcmp(name, hash_algo[i].name))
-			return &hash_algo[i];
+		if (!strcmp(algo_name, hash_algo[i].name)) {
+			*algop = &hash_algo[i];
+			return 0;
+		}
 	}
 
-	return NULL;
+	debug("Unknown hash algorithm '%s'\n", algo_name);
+	return -EPROTONOSUPPORT;
 }
 
 static void show_hash(struct hash_algo *algo, ulong addr, ulong len,
@@ -230,12 +325,12 @@
 	       uint8_t *output, int *output_size)
 {
 	struct hash_algo *algo;
+	int ret;
 
-	algo = find_hash_algo(algo_name);
-	if (!algo) {
-		debug("Unknown hash algorithm '%s'\n", algo_name);
-		return -EPROTONOSUPPORT;
-	}
+	ret = hash_lookup_algo(algo_name, &algo);
+	if (ret)
+		return ret;
+
 	if (output_size && *output_size < algo->digest_size) {
 		debug("Output buffer size %d too small (need %d bytes)",
 		      *output_size, algo->digest_size);
@@ -265,8 +360,7 @@
 		u8 vsum[HASH_MAX_DIGEST_SIZE];
 		void *buf;
 
-		algo = find_hash_algo(algo_name);
-		if (!algo) {
+		if (hash_lookup_algo(algo_name, &algo)) {
 			printf("Unknown hash algorithm '%s'\n", algo_name);
 			return CMD_RET_USAGE;
 		}
diff --git a/include/hash.h b/include/hash.h
index e92d272..dc21678 100644
--- a/include/hash.h
+++ b/include/hash.h
@@ -27,6 +27,42 @@
 	void (*hash_func_ws)(const unsigned char *input, unsigned int ilen,
 		unsigned char *output, unsigned int chunk_sz);
 	int chunk_size;				/* Watchdog chunk size */
+	/*
+	 * hash_init: Create the context for progressive hashing
+	 *
+	 * @algo: Pointer to the hash_algo struct
+	 * @ctxp: Pointer to the pointer of the context for hashing
+	 * @return 0 if ok, -1 on error
+	 */
+	int (*hash_init)(struct hash_algo *algo, void **ctxp);
+	/*
+	 * hash_update: Perform hashing on the given buffer
+	 *
+	 * The context is freed by this function if an error occurs.
+	 *
+	 * @algo: Pointer to the hash_algo struct
+	 * @ctx: Pointer to the context for hashing
+	 * @buf: Pointer to the buffer being hashed
+	 * @size: Size of the buffer being hashed
+	 * @is_last: 1 if this is the last update; 0 otherwise
+	 * @return 0 if ok, -1 on error
+	 */
+	int (*hash_update)(struct hash_algo *algo, void *ctx, const void *buf,
+			   unsigned int size, int is_last);
+	/*
+	 * hash_finish: Write the hash result to the given buffer
+	 *
+	 * The context is freed by this function.
+	 *
+	 * @algo: Pointer to the hash_algo struct
+	 * @ctx: Pointer to the context for hashing
+	 * @dest_buf: Pointer to the buffer for the result
+	 * @size: Size of the buffer for the result
+	 * @return 0 if ok, -ENOSPC if size of the result buffer is too small
+	 *   or -1 on other errors
+	 */
+	int (*hash_finish)(struct hash_algo *algo, void *ctx, void *dest_buf,
+			   int size);
 };
 
 /*
@@ -77,4 +113,16 @@
 int hash_block(const char *algo_name, const void *data, unsigned int len,
 	       uint8_t *output, int *output_size);
 
+/**
+ * hash_lookup_algo() - Look up the hash_algo struct for an algorithm
+ *
+ * The function returns the pointer to the struct or -EPROTONOSUPPORT if the
+ * algorithm is not available.
+ *
+ * @algo_name: Hash algorithm to look up
+ * @algop: Pointer to the hash_algo struct if found
+ *
+ * @return 0 if ok, -EPROTONOSUPPORT for an unknown algorithm.
+ */
+int hash_lookup_algo(const char *algo_name, struct hash_algo **algop);
 #endif