REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
plugin.cpp
Go to the documentation of this file.
1#include <proto/main.pb.h>
2
3#include <Arduino.h>
4#include <cstring> // memcpy
5
6#include "utils/etl_base64.h"
7
8#include "plugin/plugin.h"
9#include "utils/align.h"
10#include "utils/hashflash.h"
11#include "utils/logging.h"
12
13using namespace loader;
14using namespace utils;
15
16#ifdef ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER
17constexpr static int the_memsize = 1024; //< number of bytes permanently hold
19#endif
20
21
22GlobalPluginLoader::GlobalPluginLoader() {
23#ifdef ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER
25 load_addr = align(GlobalPluginLoader_Storage, 4);
26 memsize = the_memsize - disalignment(GlobalPluginLoader_Storage, 4);
27 LOGMEV("Preparing MPU and %d bytes memory", memsize);
28#else
29 LOGMEV("Skipping due to missing ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER", "dummy");
30#endif
31}
32
33
34void loader::convertFromJson(JsonVariantConst src, Function &f) {
35 if (src.is<int>()) {
36 f.addr = src;
37 } else if (src.is<JsonObjectConst>()) {
38 auto o = src.as<JsonObjectConst>();
39
40 f.addr = o["point"];
41 if (o["returns"]) {
42#define M(name) \
43 if (o["returns"] == #name) \
44 f.ret_type = Function::Returns::name
45 M(None);
46 M(Bool);
47 M(Int);
48 M(String);
49 M(JsonObject);
50 }
51 }
52}
53
54
55std::string shortened_hexdump(uint8_t *mem, size_t size) {
56 constexpr size_t inspect_bytes = 3;
57 char ret[2 * inspect_bytes + 3]; // AABBCC...DDEEFF
58 for (size_t i = 0; i < inspect_bytes; i++)
59 sprintf(ret + 2 * i, "%x", mem[i]);
60 for (size_t i = 0; i < inspect_bytes; i++)
61 sprintf(ret + sizeof(ret) - 2 * (i + 1), "%x", mem[i]);
62 return std::string(ret);
63}
64
65
66void loader::convertToJson(const GlobalPluginLoader &src, JsonVariant dst) {
67 dst["type"] = "GlobalPluginLoader";
68#ifndef ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER
69 dst["hint"] = "Set ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER debug feature flag to enable this loader";
70#else
71 dst["load_addr"] = (uint32_t)src.load_addr;
72 dst["memsize"] = src.memsize;
73
74 dst["can_load"] = src.can_load();
75 dst["is_loaded"] = src.plugin.has_value();
76
77 if (src.plugin) {
78 auto plugin = dst.createNestedObject("plugin");
79 plugin["size"] = src.plugin->size;
80 utils::sha256 plugin_checksum(src.plugin->load_addr, src.plugin->size);
81 plugin["sha256sum"] = plugin_checksum.to_string();
82
83 // can probably skip that
84 plugin["hexdump"] = shortened_hexdump(src.plugin->load_addr, src.plugin->size);
85 }
86#endif
87}
88
89
90void dispatch(uint8_t *callee, Function::Returns ret_type, JsonVariant ret) {
91 switch (ret_type) {
92 case Function::Returns::None: {
93 auto entry = (Function::None_f *)callee;
94 entry();
95 break;
96 }
97#define CALL(type, sig) \
98 case Function::Returns::type: { \
99 auto entry = (Function::sig *)callee; \
100 ret.set(entry()); \
101 break; \
102 }
103 CALL(Bool, Bool_f);
104 CALL(Int, Int_f);
105 CALL(String, String_f);
106 }
107}
108
109#define return_err(code, msg) \
110 { \
111 msg_out["error"] = msg; \
112 return code; \
113 }
114
115
116int loader::SinglePluginLoader::load_and_execute(JsonObjectConst msg_in, JsonObject &msg_out) {
117 if (plugin)
118 return_err(1, "Already have plugin loaded, can only load one plugin at a time. Call unload before.");
119 if (!can_load())
120 return_err(2, "PluginLoader cannot load code. This is currently most likely due to the missing compile "
121 "time flag ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER");
122 if (!msg_in.containsKey("entry"))
123 return_err(3, "Requiring entry point \"entry\"=0x1234 even if it is just 0.");
124
125 // Load address verification, to ensure correct linking
126 if (msg_in["load_addr"] != load_addr)
128 4,
129 "Require matching load address for verification. This SinglePluginLoader can only load from: [todo]");
130 auto firmeware_hash = loader::flashimage::sha256sum().to_string();
131 if (utils::sha256_test_short(msg_in["firmware_sha256"], firmeware_hash))
132 return_err(5, "ABI mismatch: You built against [your firmware sha256] but we run [our sha256]");
133
134 // Function pointer registration
135 Plugin new_plugin;
136 new_plugin.load_addr = load_addr;
137 new_plugin.entry = msg_in["entry"];
138 if (msg_in.containsKey("exit")) {
139 new_plugin.exit = msg_in["exit"].as<Function>();
140 }
141
142 // Actual code submission
143 // For the time being, expect base64 encoded data.
144 // TODO: For slightly larger payloads (mind the typical 1024byte JsonObject maximum size in the code),
145 // a multi-step transmission has to be introduced.
146 auto base64_payload = msg_in["payload"].as<std::string>();
147 new_plugin.size = base64_payload.size() / 4 * 3;
148 if (!load(new_plugin))
149 return_err(6, "Payload too large or some code is already loaded.");
150 auto length_decoded = etl::base64::decode(base64_payload.c_str(), base64_payload.length(),
151 new_plugin.load_addr, new_plugin.size);
152 if (length_decoded != new_plugin.size) {
153 unload();
154 return_err(7, "Could not decode base64-encoded payload.");
155 }
156
157 // Code execution
158 uint8_t *entry_point = assert_callable(plugin->load_addr + plugin->entry.addr);
159 if (!entry_point) {
160 unload();
161 return_err(8, "Entry point address not properly aligned");
162 }
163 dispatch(entry_point, plugin->entry.ret_type, msg_out["returns"].as<JsonVariant>());
164
165 // Plugins which are actually RPC.
166 if (msg_in.containsKey("immediately_unload"))
167 unload();
168
169 return 0;
170}
171
172
173int loader::SinglePluginLoader::unload(JsonObjectConst msg_in, JsonObject &msg_out) {
174 if (!plugin) {
175 return_err(9, "Cannot unload, nothing loaded");
176 }
177 if (plugin->exit) {
178 // Code execution
179 uint8_t *exit_point = assert_callable(plugin->load_addr + plugin->exit->addr);
180 if (!exit_point) {
181 unload();
182 return_err(10, "Exit point address not properly aligned");
183 }
184 dispatch(exit_point, plugin->exit->ret_type, msg_out["returns"].as<JsonVariant>());
185 }
186 unload();
187 return 0;
188}
189
190
191int loader::SinglePluginLoader::load(const Plugin &new_plugin) {
192 if (plugin)
193 return 1; // call unload() first.
194 if (new_plugin.size > memsize)
195 return 2;
196 plugin = new_plugin; // copy
197 return true;
198}
199
200// instantiate global singleton here.
201loader::GlobalPluginLoader loader::PluginLoader;
#define M(name)
#define CALL(type, sig)
#define return_err(code, msg)
Definition plugin.cpp:109
static constexpr int the_memsize
Definition plugin.cpp:17
void dispatch(uint8_t *callee, Function::Returns ret_type, JsonVariant ret)
Definition plugin.cpp:90
std::string shortened_hexdump(uint8_t *mem, size_t size)
Definition plugin.cpp:55
uint8_t GlobalPluginLoader_Storage[the_memsize]
Definition plugin.cpp:18
uint32_t
Definition flasher.cpp:195
uint32_t src
Definition flasher.cpp:63
uint32_t uint32_t size
Definition flasher.cpp:63
void prepare_mpu()
At the time being, we completely disable the Memory Protection Unit in order to enable our plugin sys...
Definition plugin.cpp:15