1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdexcept>
#include <system_error>
#include <map>
#include <algorithm>
#include "picoboot_connection_cxx.h"
#ifdef _WIN32
#undef min
#undef max
#define _CRT_SECURE_NO_WARNINGS
#endif
using picoboot::connection;
using picoboot::connection_error;
using picoboot::command_failure;
std::map<enum picoboot_status, const char *> status_code_strings = {
{picoboot_status::PICOBOOT_OK, "ok"},
{picoboot_status::PICOBOOT_BAD_ALIGNMENT, "bad address alignment"},
{picoboot_status::PICOBOOT_INTERLEAVED_WRITE, "interleaved write"},
{picoboot_status::PICOBOOT_INVALID_ADDRESS, "invalid address"},
{picoboot_status::PICOBOOT_INVALID_CMD_LENGTH, "invalid cmd length"},
{picoboot_status::PICOBOOT_INVALID_TRANSFER_LENGTH, "invalid transfer length"},
{picoboot_status::PICOBOOT_REBOOTING, "rebooting"},
{picoboot_status::PICOBOOT_UNKNOWN_CMD, "unknown cmd"},
{picoboot_status::PICOBOOT_UNKNOWN_ERROR, "unknown error"},
{picoboot_status::PICOBOOT_INVALID_STATE, "invalid state"},
{picoboot_status::PICOBOOT_INVALID_ADDRESS, "invalid argument"},
{picoboot_status::PICOBOOT_NOT_PERMITTED, "permission failure"},
{picoboot_status::PICOBOOT_INVALID_ARG, "invalid arg"},
{picoboot_status::PICOBOOT_BUFFER_TOO_SMALL, "buffer too small"},
{picoboot_status::PICOBOOT_PRECONDITION_NOT_MET, "precondition not met (pt not loaded)"},
{picoboot_status::PICOBOOT_MODIFIED_DATA, "modified data (pt modified since load)"},
{picoboot_status::PICOBOOT_INVALID_DATA, "data is invalid"},
{picoboot_status::PICOBOOT_NOT_FOUND, "not found"},
{picoboot_status::PICOBOOT_UNSUPPORTED_MODIFICATION, "unsupported modification (attempt to clear otp bits)"},
};
const char *command_failure::what() const noexcept {
auto f = status_code_strings.find((enum picoboot_status) code);
if (f != status_code_strings.end()) {
return f->second;
} else {
return "<unknown>";
}
}
template <typename F> void connection::wrap_call(F&& func) {
int rc = func();
#if 0
// we should always get a failure if there is an error, hence this is NDEBUG
if (!rc) {
struct picoboot_cmd_status status;
rc = picoboot_cmd_status(device, &status);
if (!rc) {
assert(!status.dStatusCode);
}
}
#endif
if (rc) {
struct picoboot_cmd_status status;
status.dStatusCode = 0;
rc = picoboot_cmd_status(device, &status);
if (!rc) {
reset(); // so we can continue
throw command_failure(status.dStatusCode ? (int)status.dStatusCode : PICOBOOT_UNKNOWN_ERROR);
}
throw connection_error(rc);
}
}
void connection::reset() {
wrap_call([&] { return picoboot_reset(device); });
}
void connection::exclusive_access(uint8_t exclusive) {
wrap_call([&] { return picoboot_exclusive_access(device, exclusive); });
}
void connection::enter_cmd_xip() {
wrap_call([&] { return picoboot_enter_cmd_xip(device); });
}
void connection::exit_xip() {
wrap_call([&] { return picoboot_exit_xip(device); });
}
void connection::reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms) {
wrap_call([&] { return picoboot_reboot(device, pc, sp, delay_ms); });
}
void connection::reboot2(struct picoboot_reboot2_cmd *cmd) {
wrap_call([&] { return picoboot_reboot2(device, cmd); });
}
void connection::get_info(struct picoboot_get_info_cmd *get_info_cmd, uint8_t *buffer, uint32_t len) {
wrap_call([&] { return picoboot_get_info(device, get_info_cmd, buffer, len); });
}
void connection::exec(uint32_t addr) {
wrap_call([&] { return picoboot_exec(device, addr); });
}
// void connection::exec2(struct picoboot_exec2_cmd *cmd) {
// wrap_call([&] { return picoboot_exec2(device, cmd); });
// } // currently unused
void connection::flash_erase(uint32_t addr, uint32_t len) {
wrap_call([&] { return picoboot_flash_erase(device, addr, len); });
}
void connection::vector(uint32_t addr) {
wrap_call([&] { return picoboot_vector(device, addr); });
}
void connection::write(uint32_t addr, uint8_t *buffer, uint32_t len) {
wrap_call([&] { return picoboot_write(device, addr, buffer, len); });
}
void connection::read(uint32_t addr, uint8_t *buffer, uint32_t len) {
// Workaround due to picoboot interface not supporting reads over 4MiB
uint32_t max_chunk_size = 0x00400000;
for (uint32_t i=0; i < len; i += max_chunk_size) {
uint32_t read_size = std::min(len - i, max_chunk_size);
wrap_call([&] { return picoboot_read(device, addr + i, buffer + i, read_size); });
}
}
void connection::otp_write(struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
wrap_call([&] { return picoboot_otp_write(device, otp_cmd, buffer, len); });
}
void connection::otp_read(struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
wrap_call([&] { return picoboot_otp_read(device, otp_cmd, buffer, len); });
}
void connection::flash_id(uint64_t &data) {
wrap_call([&] { return picoboot_flash_id(device, &data); });
}
|