hwpatcher: add framework for CRC computation

Change-Id: Ib78f0fe58db5cec86f043d3e9e1ca14e69297ba0
Reviewed-on: http://gerrit.rockbox.org/911
Reviewed-by: Marcin Bukat <marcin.bukat@gmail.com>
diff --git a/utils/hwpatcher/hwpatcher.c b/utils/hwpatcher/hwpatcher.c
index 60e142b..7153a22 100644
--- a/utils/hwpatcher/hwpatcher.c
+++ b/utils/hwpatcher/hwpatcher.c
@@ -47,6 +47,8 @@
 #include "misc.h"
 #include "md5.h"
 
+#define ARRAYLEN(arr) (sizeof(arr) / sizeof(arr[0]))
+
 lua_State *g_lua;
 bool g_exit = false;
 
@@ -59,6 +61,18 @@
     FW_UNK, FW_ELF, FW_SB1, FW_SB2, FW_BIN, FW_EDOC
 };
 
+enum crc_type_t
+{
+    CRC_RKW
+};
+
+struct crc_type_desc_t
+{
+    enum crc_type_t type;
+    const char *lua_name;
+    unsigned (*fn)(uint8_t *buf, size_t len);
+};
+
 struct bin_file_t
 {
     size_t size;
@@ -849,6 +863,66 @@
     return 1;
 }
 
+unsigned crc_rkw(uint8_t *buf, size_t len)
+{
+    /* polynomial 0x04c10db7 */
+    static const uint32_t crc32_lookup[16] =
+    {   /* lookup table for 4 bits at a time is affordable */
+        0x00000000, 0x04C10DB7, 0x09821B6E, 0x0D4316D9,
+        0x130436DC, 0x17C53B6B, 0x1A862DB2, 0x1E472005,
+        0x26086DB8, 0x22C9600F, 0x2F8A76D6, 0x2B4B7B61,
+        0x350C5B64, 0x31CD56D3, 0x3C8E400A, 0x384F4DBD
+    };
+
+    uint32_t crc32 = 0;
+    unsigned char byte;
+    uint32_t t;
+
+    while (len--)
+    {
+        byte = *buf++; /* get one byte of data */
+
+        /* upper nibble of our data */
+        t = crc32 >> 28; /* extract the 4 most significant bits */
+        t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */
+        crc32 <<= 4; /* shift the CRC register left 4 bits */
+        crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
+
+        /* lower nibble of our data */
+        t = crc32 >> 28; /* extract the 4 most significant bits */
+        t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */
+        crc32 <<= 4; /* shift the CRC register left 4 bits */
+        crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
+    }
+
+    return crc32;
+}
+
+struct crc_type_desc_t crc_types[] =
+{
+    {CRC_RKW, "RKW", crc_rkw}
+};
+
+int my_lua_crc_buf(lua_State *state)
+{
+    int n = lua_gettop(state);
+    if(n != 2)
+        return luaL_error(state, "crc_buf takes two arguments: a crc type and a buffer");
+    unsigned type = lua_tounsigned(state, 1);
+    size_t len;
+    void *buf = my_lua_get_buffer(state, 2, &len);
+    for(int i = 0; i < ARRAYLEN(crc_types); i++)
+        if(crc_types[i].type == type)
+        {
+            lua_pushunsigned(state, crc_types[i].fn(buf, len));
+            free(buf);
+            return 1;
+        }
+    free(buf);
+    luaL_error(state, "crc_buf: unknown crc type");
+    return 0;
+}
+
 /* compute MD5 sum of a buffer */
 static bool compute_md5sum_buf(void *buf, size_t sz, uint8_t file_md5sum[16])
 {
@@ -945,6 +1019,17 @@
     lua_pushcfunction(g_lua, my_lua_md5sum);
     lua_setfield(g_lua, -2, "md5sum");
 
+    lua_newtable(g_lua);
+    for(int i = 0; i < ARRAYLEN(crc_types); i++)
+    {
+        lua_pushunsigned(g_lua, crc_types[i].type);
+        lua_setfield(g_lua, -2, crc_types[i].lua_name);
+    }
+    lua_setfield(g_lua, -2, "CRC");
+
+    lua_pushcfunction(g_lua, my_lua_crc_buf);
+    lua_setfield(g_lua, -2, "crc_buf");
+
     return true;
 }
 
diff --git a/utils/hwpatcher/lib.lua b/utils/hwpatcher/lib.lua
index 7a3e4a4..4c0d6ff 100644
--- a/utils/hwpatcher/lib.lua
+++ b/utils/hwpatcher/lib.lua
@@ -8,16 +8,21 @@
 - exit()    Same as quit()
 
 In the hwp table:
-- load_file(filename)       Load a firmware and guess type
-- load_elf_file(filename)   Load a firmware as ELF
-- load_sb_file(filename)    Load a firmware as SB
-- load_sb1_file(filename)   Load a firmware as SB1
-- load_bin_file(filename)   Load a firmware as binary
-- save_file(obj, filename)  Save a firmware to a file
-- read(obj, addr, len)      Read data from a firmware
-- write(obj, addr, data)    Write data to a firmware
-- section_info(obj, sec)    Return information about a section in a table (or nil)
-- md5sum(filename)          Compute the MD5 sum of a file
+- load_file(filename)           Load a firmware and guess type
+- load_elf_file(filename)       Load a firmware as ELF
+- load_sb_file(filename)        Load a firmware as SB
+- load_sb1_file(filename)       Load a firmware as SB1
+- load_bin_file(filename)       Load a firmware as binary
+- save_file(fw, filename)       Save a firmware to a file
+- read(fw, addr, len)           Read data from a firmware
+- write(fw, addr, data)         Write data to a firmware
+- section_info(fw, sec)         Return information about a section in a table (or nil)
+- md5sum(filename)              Compute the MD5 sum of a file
+- crc_buf(crc_type, buf)        Compute the CRC of a byte buffer and return a byte buffer
+- crc(crc_type, fw, addr, len)  Compute the CRC of a firmware part
+
+The list of CRCs available is available the hwp.CRC table:
+- RKW
 
 Data read/written from/to a firmware must must be an array of bytes.
 The address must be a table of the following fields:
@@ -27,7 +32,7 @@
 - address: first address if the section
 - size: size of the section
 We provide the following functions to help dealing with addresses:
-- make_addr
+- make_addr(addr, section)      Build a firmware address from a raw address and a section
 
 ]]--
 
@@ -105,3 +110,8 @@
     end
     return s
 end
+
+-- compute the CRC of a firmware part
+function hwp.crc(crc_type, fw, addr, len)
+    return hwp.crc_buf(crc_type, hwp.read(fw, addr, len))
+end