File: picoboot_connection_cxx.cpp

package info (click to toggle)
picotool 2.2.0-a4%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,084 kB
  • sloc: cpp: 61,059; ansic: 2,999; asm: 2,048; perl: 219; sh: 212; python: 97; makefile: 41; xml: 18
file content (146 lines) | stat: -rw-r--r-- 5,236 bytes parent folder | download
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); });
}