| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sw/host/spiflash/updater.h" |
| |
| #include <algorithm> |
| #include <assert.h> |
| #include <unistd.h> |
| |
| namespace opentitan { |
| namespace spiflash { |
| namespace { |
| |
| /** |
| * Populate target frame `f`. |
| * |
| * Populates frame `f` with `frame_number`, `code_offset`, and frame data |
| * starting at `code_offset` from `code` buffer. Calculates SHA256 hash of |
| * frame payload and it stores it in the frame header. |
| * |
| * @return the number of bytes loaded into the frame. |
| */ |
| uint32_t Populate(uint32_t frame_number, uint32_t code_offset, |
| const std::string &code, Frame *f) { |
| assert(f); |
| assert(code_offset < code.size()); |
| |
| // Populate payload data. Initialize buffer to 0xff to minimize flash |
| // writes. |
| size_t copy_size = |
| std::min<size_t>(f->PayloadSize(), code.size() - code_offset); |
| memset(f->data, 0xff, f->PayloadSize()); |
| memcpy(f->data, code.data() + code_offset, copy_size); |
| |
| // Populate header number, offset and hash. |
| f->hdr.frame_num = frame_number; |
| f->hdr.offset = code_offset; |
| return copy_size; |
| } |
| |
| /** |
| * Calculate hash for frame `f` and store it in the frame header hash field. |
| */ |
| void HashFrame(Frame *f) { |
| SHA256_CTX sha256; |
| SHA256_Init(&sha256); |
| SHA256_Update(&sha256, &f->hdr.frame_num, sizeof(f->hdr.frame_num)); |
| SHA256_Update(&sha256, &f->hdr.offset, sizeof(f->hdr.offset)); |
| SHA256_Update(&sha256, f->data, f->PayloadSize()); |
| SHA256_Final(f->hdr.hash, &sha256); |
| } |
| |
| } // namespace |
| |
| bool Updater::Run() { |
| std::cout << "Running SPI flash update." << std::endl; |
| std::vector<Frame> frames; |
| if (!GenerateFrames(options_.code, &frames)) { |
| std::cerr << "Unable to process flash image." << std::endl; |
| return false; |
| } |
| std::cout << "Image divided into " << frames.size() << " frames." |
| << std::endl; |
| |
| 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; |
| |
| if (!spi_->TransmitFrame(reinterpret_cast<const uint8_t *>(&f), |
| sizeof(Frame))) { |
| std::cerr << "Failed to transmit frame no: 0x" << std::setfill('0') |
| << std::setw(8) << std::hex << f.hdr.frame_num << std::endl; |
| } |
| |
| // After receiving and validating the first frame, the device is erasing |
| // the Flash. |
| if (current_frame == 0) { |
| usleep(options_.flash_erase_delay_us); |
| } |
| |
| // When we send each frame we wait for the correct hash before continuing. |
| if (current_frame == frames.size() - 1 || |
| spi_->CheckHash(reinterpret_cast<const uint8_t *>(&f), sizeof(Frame))) { |
| current_frame++; |
| } |
| } |
| return true; |
| } |
| |
| bool Updater::GenerateFrames(const std::string &code, |
| std::vector<Frame> *frames) { |
| if (frames == nullptr) { |
| return false; |
| } |
| uint32_t frame_number = 0; |
| uint32_t code_offset = 0; |
| while (code_offset < code.size()) { |
| Frame frame; |
| uint32_t bytes_copied = Populate(frame_number, code_offset, code, &frame); |
| code_offset += bytes_copied; |
| frame_number++; |
| frames->emplace_back(frame); |
| } |
| // Update last frame to sentinel EOF value. |
| Frame &last_frame = frames->back(); |
| last_frame.hdr.frame_num = 0x80000000 | last_frame.hdr.frame_num; |
| |
| for (Frame &f : *frames) { |
| HashFrame(&f); |
| } |
| return true; |
| } |
| |
| } // namespace spiflash |
| } // namespace opentitan |