From d5b578b161bc7d63f6e9c1bd942b602d2dc59f87 Mon Sep 17 00:00:00 2001
From: Ivan Grokhotkov <igrokhotkov@gmail.com>
Date: Mon, 1 Jun 2015 02:08:48 +0300
Subject: [PATCH] reading eboot commands via RTC, flash erase/copy

---
 bootloaders/eboot/Makefile        |   3 +-
 bootloaders/eboot/eboot.c         |  94 +++++++++++++++++++++++++++++-
 bootloaders/eboot/eboot.elf       | Bin 5209 -> 11738 bytes
 bootloaders/eboot/eboot.h         |   3 +
 bootloaders/eboot/eboot_command.c |  47 +++++++++++++++
 bootloaders/eboot/eboot_command.h |  29 +++++++++
 6 files changed, 174 insertions(+), 2 deletions(-)
 create mode 100644 bootloaders/eboot/eboot_command.c
 create mode 100644 bootloaders/eboot/eboot_command.h

diff --git a/bootloaders/eboot/Makefile b/bootloaders/eboot/Makefile
index 32e325f34..8aaac9cab 100644
--- a/bootloaders/eboot/Makefile
+++ b/bootloaders/eboot/Makefile
@@ -5,6 +5,7 @@ TARGET_DIR := ./
 
 TARGET_OBJ_FILES := \
 	eboot.o \
+	eboot_command.o \
 
 TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES))
 
@@ -17,7 +18,7 @@ OBJDUMP := $(XTENSA_TOOCHAIN)xtensa-lx106-elf-objdump
 
 CFLAGS += -std=gnu99
 
-CFLAGS += -O0 -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mno-text-section-literals
+CFLAGS += -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mno-text-section-literals
 
 LDFLAGS	+= -nostdlib -Wl,--no-check-sections -umain
 
diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c
index e39375d2d..86a27eff0 100644
--- a/bootloaders/eboot/eboot.c
+++ b/bootloaders/eboot/eboot.c
@@ -10,6 +10,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "eboot.h"
+#include "eboot_command.h"
 extern void* flashchip;
 
 #define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0);
@@ -71,9 +72,100 @@ int load_app_from_flash_raw(const uint32_t flash_addr)
 }
 
 
+
+int erase(const uint32_t start, const uint32_t size)
+{
+    if (start & (FLASH_SECTOR_SIZE - 1) != 0) {
+        return 1;
+    }
+
+    const uint32_t sectors_per_block = FLASH_BLOCK_SIZE / FLASH_SECTOR_SIZE;
+    uint32_t current_sector = start / FLASH_SECTOR_SIZE;
+    uint32_t sector_count = (size + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE;
+    const uint32_t end = current_sector + sector_count;
+
+    for (; current_sector < end && (current_sector & (sectors_per_block-1)); 
+        ++current_sector, --sector_count) {
+        if (SPIEraseSector(current_sector)) {
+            return 2;
+        }
+    }
+
+    for (;current_sector + sectors_per_block <= end; 
+        current_sector += sectors_per_block, 
+        sector_count -= sectors_per_block) {
+        if (SPIEraseBlock(current_sector / sectors_per_block)) {
+            return 3;
+        }
+    }
+
+    for (; current_sector < end; 
+        ++current_sector, --sector_count) {
+        if (SPIEraseSector(current_sector)) {
+            return 4;
+        }
+    }
+
+    return 0;
+}
+
+int copy_raw(const uint32_t src_addr, 
+             const uint32_t dst_addr, 
+             const uint32_t size)
+{
+    // require regions to be aligned
+    if (src_addr & 0xfff != 0 ||
+        dst_addr & 0xfff != 0) {
+        return 1;
+    }
+
+    if (erase(dst_addr, size)) {
+        return 2;
+    }
+
+    const uint32_t buffer_size = 4096;
+    uint8_t buffer[buffer_size];
+
+    const uint32_t end = src_addr + size;
+    uint32_t saddr = src_addr;
+    uint32_t daddr = dst_addr;
+    uint32_t left = size;
+    while (saddr < end) {
+        uint32_t will_copy = (left < buffer_size) ? left : buffer_size;
+        if (SPIRead(saddr, buffer, will_copy)) {
+            return 3;
+        }
+        if (SPIWrite(daddr, buffer, will_copy)) {
+            return 4;
+        }
+        saddr += will_copy;
+        daddr += will_copy;
+        left  -= will_copy;
+    }
+
+    return 0;
+}
+
+
+
 void main()
 {
-    int res = load_app_from_flash_raw(0);
+    int res;
+    struct eboot_command cmd;
+
+    eboot_command_read(&cmd);
+
+    if (cmd.action == ACTION_COPY_RAW) {
+        res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]);
+        if (res == 0) {
+            cmd.action = ACTION_LOAD_APP;
+        }
+    }
+
+    if (cmd.action == ACTION_LOAD_APP) {
+        res = load_app_from_flash_raw(0);
+    }
+
     if (res) {
         ets_putc('\n');
         ets_putc('#');
diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf
index b2381a4098690e025f33c25290a14715934882fe..b7908dfd67b803008b425ac3b0d07ce5d7dd279b 100755
GIT binary patch
literal 11738
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

literal 5209
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

diff --git a/bootloaders/eboot/eboot.h b/bootloaders/eboot/eboot.h
index b77f8f45a..973c616a8 100644
--- a/bootloaders/eboot/eboot.h
+++ b/bootloaders/eboot/eboot.h
@@ -14,6 +14,9 @@ int SPIEraseSector(uint32_t sector);
 int SPIRead(uint32_t addr, void *dest, size_t size);
 int SPIWrite(uint32_t addr, void *src, size_t size);
 
+
+#define FLASH_SECTOR_SIZE 0x1000
+#define FLASH_BLOCK_SIZE 0x10000
 #define APP_START_OFFSET 0x1000
 
 typedef struct {
diff --git a/bootloaders/eboot/eboot_command.c b/bootloaders/eboot/eboot_command.c
new file mode 100644
index 000000000..1e9cbed2c
--- /dev/null
+++ b/bootloaders/eboot/eboot_command.c
@@ -0,0 +1,47 @@
+#include "eboot_command.h"
+
+uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length)
+{
+    uint32_t i;
+    bool bit;
+    uint8_t c;
+    
+    while (length--) {
+        c = *data++;
+        for (i = 0x80; i > 0; i >>= 1) {
+            bit = crc & 0x80000000;
+            if (c & i) {
+                bit = !bit;
+            }
+            crc <<= 1;
+            if (bit) {
+                crc ^= 0x04c11db7;
+            }
+        }
+    }
+    return crc;
+}
+
+uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd)
+{
+    return crc_update(0xffffffff, (const uint8_t*) cmd, 
+                      offsetof(struct eboot_command, crc32));
+}
+
+void eboot_command_read(struct eboot_command* cmd)
+{
+    const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t);
+    uint32_t* dst = (uint32_t *) cmd;
+    for (uint32_t i = 0; i < dw_count; ++i) {
+        dst[i] = RTC_MEM[i];
+    }
+
+    uint32_t crc32 = eboot_command_calculate_crc32(cmd);
+    if (cmd->magic & EBOOT_MAGIC_MASK != EBOOT_MAGIC || 
+        cmd->crc32 != crc32) {
+        
+        cmd->action = ACTION_LOAD_APP;
+        cmd->args[0] = 0;
+    }
+}
+
diff --git a/bootloaders/eboot/eboot_command.h b/bootloaders/eboot/eboot_command.h
new file mode 100644
index 000000000..aa0fc11bb
--- /dev/null
+++ b/bootloaders/eboot/eboot_command.h
@@ -0,0 +1,29 @@
+#ifndef EBOOT_COMMAND_H
+#define EBOOT_COMMAND_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#define RTC_MEM ((volatile uint32_t*)0x60001200)
+
+enum action_t {
+    ACTION_COPY_RAW = 0x00000001,
+    ACTION_LOAD_APP = 0xffffffff
+};
+
+#define EBOOT_MAGIC 	 0xeb001000
+#define EBOOT_MAGIC_MASK 0xfffff000
+
+struct eboot_command {
+    uint32_t magic;
+    enum action_t action;
+    uint32_t args[29];
+    uint32_t crc32;
+};
+
+
+void eboot_command_read(struct eboot_command* cmd);
+
+
+#endif //EBOOT_COMMAND_H