zx0-bootloader/src/hf2.cpp
Michael Hope b1f84a16e5 zx0: ran addlicense
Update the source to include the full license block.
2021-07-04 14:50:28 +02:00

222 lines
5.4 KiB
C++

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "hf2.h"
#include <drivers/flash.h>
#include <drivers/gpio.h>
#include <string.h>
#include <sys/crc.h>
struct WriteFlashPage {
uint32_t target_addr;
uint8_t data[0];
};
struct ChksumPages {
uint32_t target_addr;
uint32_t num_pages;
};
struct ReadWords {
uint32_t target_addr;
uint32_t num_words;
};
struct Command {
enum class ID : uint32_t {
BININFO = 1,
INFO = 2,
RESET_INTO_APP = 3,
RESET_INTO_BOOTLOADER = 4,
START_FLASH = 5,
WRITE_FLASH_PAGE = 6,
CHKSUM_PAGES = 7,
READ_WORDS = 8,
WRITE_WORDS = 9,
};
ID command_id;
uint16_t tag;
uint8_t reserved0;
uint8_t reserved1;
union {
WriteFlashPage write_flash_page;
ChksumPages chksum_pages;
ReadWords read_words;
};
};
struct Header {
enum class Status : uint8_t {
OK,
Invalid,
Error,
};
uint16_t tag;
Status status;
uint8_t status_info;
};
struct BinInfo {
enum class Mode : uint32_t {
Bootloader = 1,
UserSpace = 2,
};
Mode mode;
uint32_t flash_page_size;
uint32_t flash_num_pages;
uint32_t max_message_size;
uint32_t family_id;
};
struct Response {
Header header;
union {
BinInfo bininfo;
uint16_t chksums[8];
uint32_t read_words[4];
};
};
// Must fit within a 32 byte I2C response.
static_assert(sizeof(Command) <= 30);
static_assert(sizeof(Response) <= 30);
void HF2::packet(const uint8_t *in, uint8_t *out) {
uint8_t header = in[0];
if ((header & StdinStderr) != 0) {
return;
}
auto size = header & SizeMask;
size_t end = at_ + size;
if (end >= sizeof(message_)) {
return;
}
memcpy(message_ + at_, in + 1, size);
at_ += size;
if ((header & Final) == 0) {
return;
}
at_ = 0;
Response resp;
auto wrote = message(*(Command *)message_, resp);
memcpy(out + 2, &resp, wrote);
out[0] = wrote + 1;
out[1] = wrote | Final;
}
size_t HF2::bininfo(BinInfo &resp) {
resp = {
.mode = BinInfo::Mode::Bootloader,
.flash_page_size = HAL::kPageSize,
.flash_num_pages = HAL::kNumPages,
.max_message_size = sizeof(message_),
.family_id = 1234,
};
return sizeof(BinInfo);
}
size_t HF2::chksum_pages(uint32_t addr, uint32_t pages, uint16_t *chksums) {
if (pages > ARRAY_SIZE(Response::chksums)) {
return -ENOMEM;
}
for (uint32_t i = 0; i < pages; i++) {
chksums[i] = crc16_itu_t(0, (uint8_t *)addr + i * HAL::kPageSize, HAL::kPageSize);
}
return pages * sizeof(*chksums);
}
size_t HF2::write_flash_page(uint32_t addr, const uint8_t *data) {
int err = flash_erase(flash_, addr, HAL::kPageSize);
if (err != 0) {
return err;
}
return flash_write(flash_, addr, data, HAL::kPageSize);
}
size_t HF2::reset_into_app() { return HAL::enter_app(); }
size_t HF2::read_words(uint32_t addr, uint32_t count, uint32_t *words) {
if (count > ARRAY_SIZE(Response::read_words)) {
return -ENOMEM;
}
for (uint32_t i = 0; i < count; i++) {
words[i] = ((uint32_t *)addr)[i];
}
return count * sizeof(*words);
}
size_t HF2::message(Command &command, Response &resp) {
gpio_pin_toggle(HAL::led1.port, HAL::led1.pin);
int err = -EINVAL;
switch (command.command_id) {
case Command::ID::BININFO:
err = bininfo(resp.bininfo);
break;
case Command::ID::INFO:
case Command::ID::WRITE_WORDS:
err = -ENOENT;
break;
case Command::ID::RESET_INTO_APP:
err = reset_into_app();
break;
case Command::ID::RESET_INTO_BOOTLOADER:
case Command::ID::START_FLASH:
err = 0;
break;
case Command::ID::WRITE_FLASH_PAGE:
err = write_flash_page(command.write_flash_page.target_addr,
command.write_flash_page.data);
break;
case Command::ID::CHKSUM_PAGES:
err = chksum_pages(command.chksum_pages.target_addr, command.chksum_pages.num_pages,
resp.chksums);
break;
case Command::ID::READ_WORDS:
err = read_words(command.read_words.target_addr, command.read_words.num_words,
resp.read_words);
break;
default:
break;
}
if (err < 0) {
resp.header = {
.tag = command.tag,
.status = Header::Status::Error,
.status_info = static_cast<uint8_t>(-err),
};
return sizeof(Header);
}
resp.header = {
.tag = command.tag,
.status = Header::Status::OK,
.status_info = 0,
};
return sizeof(Header) + err;
}