[sw/boot_rom] Add frame retry logic

boot_rom sends an ack to spiflash with the hash of the entire previous
frame. spiflash retries the previous fram if unable to verify the ack
against previous frame hash.
diff --git a/sw/boot_rom/bootstrap.c b/sw/boot_rom/bootstrap.c
index fa41e0f..d884ba3 100644
--- a/sw/boot_rom/bootstrap.c
+++ b/sw/boot_rom/bootstrap.c
@@ -6,8 +6,8 @@
 
 #include "common.h"
 #include "flash_ctrl.h"
-#include "hw_sha256.h"
 #include "gpio.h"
+#include "hw_sha256.h"
 #include "spi_device.h"
 #include "uart.h"  // TODO: Wrap uart in DEBUG macros.
 
@@ -65,14 +65,15 @@
       uart_send_uint(expected_frame_no, 32);
       uart_send_str("\r\n");
 
-      if (check_frame_hash(&f)) {
-        uart_send_str("Error: detected hash mismatch on frame: ");
-        uart_send_uint(f.hdr.frame_num, 32);
-        uart_send_str("\r\n");
-        return E_BS_BAD_HASH_FRAME;
-      }
-
       if (FRAME_NO(f.hdr.frame_num) == expected_frame_no) {
+        if (check_frame_hash(&f)) {
+          uart_send_str("Error: detected hash mismatch on frame: ");
+          uart_send_uint(f.hdr.frame_num, 32);
+          uart_send_str("\r\n");
+          spid_send(ack, sizeof(ack));
+          continue;
+        }
+
         hw_SHA256_hash(&f, sizeof(f), ack);
         spid_send(ack, sizeof(ack));
 
diff --git a/sw/boot_rom/bootstrap_msgs.h b/sw/boot_rom/bootstrap_msgs.h
index 2ad9331..7ce2e5e 100644
--- a/sw/boot_rom/bootstrap_msgs.h
+++ b/sw/boot_rom/bootstrap_msgs.h
@@ -31,7 +31,6 @@
 } frame_t;
 
 /* Bootstrap error codes */
-#define E_BS_BAD_HASH_FRAME 9
 #define E_BS_ERASE 10
 #define E_BS_NOTEMPTY 11
 #define E_BS_WRITE 12
diff --git a/sw/boot_rom/srcs.mk b/sw/boot_rom/srcs.mk
index 456df0c..894f22f 100644
--- a/sw/boot_rom/srcs.mk
+++ b/sw/boot_rom/srcs.mk
@@ -3,7 +3,8 @@
 # SPDX-License-Identifier: Apache-2.0
 
 SW_NAME       ?= boot_rom
-SW_SRCS       += $(SW_DIR)/bootstrap.c $(SW_DIR)/boot_rom.c
+SW_SRCS       += $(SW_DIR)/bootstrap.c $(SW_DIR)/boot_rom.c $(LIB_DIR)/hw_sha256.c
+INCS          += -I$(SW_ROOT_DIR)/vendor
 
 # overrides
 CRT_SRCS      := $(SW_DIR)/crt0.S
diff --git a/sw/host/spiflash/updater.cc b/sw/host/spiflash/updater.cc
index 2025ca9..9f5cc2d 100644
--- a/sw/host/spiflash/updater.cc
+++ b/sw/host/spiflash/updater.cc
@@ -4,9 +4,10 @@
 
 #include "updater.h"
 
-#include <algorithm>
 #include <assert.h>
 
+#include <algorithm>
+
 namespace opentitan {
 namespace spiflash {
 namespace {
@@ -43,6 +44,15 @@
   SHA256_Final(f->hdr.hash, &sha256);
 }
 
+// Check hash portion of |ack| against |ack_expected|.
+bool CheckAckHash(const std::string &ack, const std::string &ack_expected) {
+  uint8_t result = 0;
+  for (int i = 0; i < 32; ++i) {
+    result |= ack[i] ^ ack_expected[i];
+  }
+  return (result == 0);
+}
+
 }  // namespace
 
 bool Updater::Run() {
@@ -55,16 +65,43 @@
   std::cout << "Image divided into " << frames.size() << " frames."
             << std::endl;
 
-  for (const Frame &f : frames) {
+  std::string ack_expected;
+  ack_expected.resize(sizeof(Frame), '\0');
+  std::string ack;
+  ack.resize(sizeof(Frame));
+  for (uint32_t current_frame = 0; current_frame < frames.size();) {
+    const Frame &f = frames[current_frame];
+
     std::cout << "frame: 0x" << std::setfill('0') << std::setw(8) << std::hex
               << f.hdr.frame_num << " to offset: 0x" << std::setfill('0')
               << std::setw(8) << std::hex << f.hdr.offset << std::endl;
-    uint8_t rx[sizeof(Frame)];
-    if (!spi_->TransmitFrame(reinterpret_cast<const uint8_t *>(&f), rx,
+
+    if (!spi_->TransmitFrame(reinterpret_cast<const uint8_t *>(&f),
+                             reinterpret_cast<uint8_t *>(&ack[0]),
                              sizeof(Frame))) {
       std::cerr << "Failed to transmit frame no: 0x" << std::setfill('0')
                 << std::setw(8) << std::hex << f.hdr.frame_num << std::endl;
     }
+
+    // When we send the next frame, we'll get the previous frame's hash as
+    // the ack, so with each increment of |current_frame| we also need to
+    // update the |ack_expected| value.
+    uint32_t ack_expected_index = 0;
+    if (current_frame == 0 || CheckAckHash(ack, ack_expected)) {
+      ack_expected_index = current_frame;
+      current_frame++;
+    } else {
+      // TODO: Improve protocol by encoding NEXT frame number, current error,
+      // ack marker and CRC.
+      // The current implementation will send the previous frame if the current
+      // ack doesn't match the expected response.
+      if (current_frame > 1) {
+        current_frame--;
+      }
+      ack_expected_index = (current_frame == 0) ? 0 : current_frame - 1;
+    }
+    SHA256(reinterpret_cast<const uint8_t *>(&frames[ack_expected_index]),
+           sizeof(f), reinterpret_cast<uint8_t *>(&ack_expected[0]));
   }
   return true;
 }
@@ -87,7 +124,7 @@
   Frame &last_frame = frames->back();
   last_frame.hdr.frame_num = 0x80000000 | last_frame.hdr.frame_num;
 
-  for (Frame& f : *frames) {
+  for (Frame &f : *frames) {
     HashFrame(&f);
   }
   return true;