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