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
3#ifdef ARDUINO
4
5#include <cstring> // memcpy
6
7#include "imxrt.h" // framework-arduinoteensy/cores/teensy4
8#include "teensy_startup.h"
9
10#include "utils/etl_base64.h"
11
12#include "plugin/plugin.h"
13#include "utils/align.h"
14#include "utils/hashflash.h"
15#include "utils/logging.h"
16
17using namespace loader;
18using namespace utils;
19
20#ifdef ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER
21constexpr static int the_memsize = 1024; //< number of bytes permanently hold
23#endif
24
31 // Turn off MPU completely
32 // Effect: Can execute code in stack and global storage.
33 SCB_MPU_CTRL = 0;
34
35 /*
36 SCB_MPU_RBAR = 0x00000000 | REGION(1); // ITCM
37 SCB_MPU_RASR = MEM_NOCACHE | READWRITE | SIZE_512K;
38
39 SCB_MPU_RBAR = 0x20000000 | REGION(4); // DTCM
40 SCB_MPU_RASR = MEM_NOCACHE | READWRITE | NOEXEC | SIZE_512K;
41
42 SCB_MPU_RBAR = 0x20200000 | REGION(6); // RAM (AXI bus)
43 SCB_MPU_RASR = MEM_CACHE_WBWA | READWRITE | NOEXEC | SIZE_1M;
44 */
45
46 // Enabling back the MPU
47 // SCB_MPU_CTRL = SCB_MPU_CTRL_ENABLE;
48
49 // recommended to do before and after changing MPU registers
50 asm("dsb");
51 asm("isb");
52}
53
54FLASHMEM
56#ifdef ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER
60 LOGMEV("Preparing MPU and %d bytes memory", memsize);
61#else
62 LOGMEV("Skipping due to missing ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER", "dummy");
63#endif
64}
65
66FLASHMEM
67void loader::convertFromJson(JsonVariantConst src, Function &f) {
68 if (src.is<int>()) {
69 f.addr = src;
70 } else if (src.is<JsonObjectConst>()) {
71 auto o = src.as<JsonObjectConst>();
72
73 f.addr = o["point"];
74 if (o["returns"]) {
75#define M(name) \
76 if (o["returns"] == #name) \
77 f.ret_type = Function::Returns::name
78 M(None);
79 M(Bool);
80 M(Int);
81 M(String);
82 M(JsonObject);
83 }
84 }
85}
86
87FLASHMEM
88std::string shortened_hexdump(uint8_t *mem, size_t size) {
89 constexpr size_t inspect_bytes = 3;
90 char ret[2 * inspect_bytes + 3]; // AABBCC...DDEEFF
91 for (size_t i = 0; i < inspect_bytes; i++)
92 sprintf(ret + 2 * i, "%x", mem[i]);
93 for (size_t i = 0; i < inspect_bytes; i++)
94 sprintf(ret + sizeof(ret) - 2 * (i + 1), "%x", mem[i]);
95 return std::string(ret);
96}
97
98FLASHMEM
99void loader::convertToJson(const GlobalPluginLoader &src, JsonVariant dst) {
100 dst["type"] = "GlobalPluginLoader";
101#ifndef ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER
102 dst["hint"] = "Set ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER debug feature flag to enable this loader";
103#else
104 dst["load_addr"] = (uint32_t)src.load_addr;
105 dst["memsize"] = src.memsize;
106
107 dst["can_load"] = src.can_load();
108 dst["is_loaded"] = src.plugin.has_value();
109
110 if (src.plugin) {
111 auto plugin = dst.createNestedObject("plugin");
112 plugin["size"] = src.plugin->size;
113 utils::sha256 plugin_checksum(src.plugin->load_addr, src.plugin->size);
114 plugin["sha256sum"] = plugin_checksum.to_string();
115
116 // can probably skip that
117 plugin["hexdump"] = shortened_hexdump(src.plugin->load_addr, src.plugin->size);
118 }
119#endif
120}
121
122FLASHMEM
123void dispatch(uint8_t *callee, Function::Returns ret_type, JsonVariant ret) {
124 switch (ret_type) {
126 auto entry = (Function::None_f *)callee;
127 entry();
128 break;
129 }
130#define CALL(type, sig) \
131 case Function::Returns::type: { \
132 auto entry = (Function::sig *)callee; \
133 ret.set(entry()); \
134 break; \
135 }
136 CALL(Bool, Bool_f);
137 CALL(Int, Int_f);
138 CALL(String, String_f);
139 CALL(JsonObject, JsonObject_f)
140 }
141}
142
143#define return_err(code, msg) \
144 { \
145 msg_out["error"] = msg; \
146 return code; \
147 }
148
149FLASHMEM
150int loader::SinglePluginLoader::load_and_execute(JsonObjectConst msg_in, JsonObject &msg_out) {
151 if (plugin)
152 return_err(1, "Already have plugin loaded, can only load one plugin at a time. Call unload before.");
153 if (!can_load())
154 return_err(2, "PluginLoader cannot load code. This is currently most likely due to the missing compile "
155 "time flag ANABRID_ENABLE_GLOBAL_PLUGIN_LOADER");
156 if (!msg_in.containsKey("entry"))
157 return_err(3, "Requiring entry point \"entry\"=0x1234 even if it is just 0.");
158
159 // Load address verification, to ensure correct linking
160 if (msg_in["load_addr"] != load_addr)
162 4,
163 "Require matching load address for verification. This SinglePluginLoader can only load from: [todo]");
164 auto firmeware_hash = loader::flashimage::sha256sum().to_string();
165 if (utils::sha256_test_short(msg_in["firmware_sha256"], firmeware_hash))
166 return_err(5, "ABI mismatch: You built against [your firmware sha256] but we run [our sha256]");
167
168 // Function pointer registration
169 Plugin new_plugin;
170 new_plugin.load_addr = load_addr;
171 new_plugin.entry = msg_in["entry"];
172 if (msg_in.containsKey("exit")) {
173 new_plugin.exit = msg_in["exit"].as<Function>();
174 }
175
176 // Actual code submission
177 // For the time being, expect base64 encoded data.
178 // TODO: For slightly larger payloads (mind the typical 1024byte JsonObject maximum size in the code),
179 // a multi-step transmission has to be introduced.
180 auto base64_payload = msg_in["payload"].as<std::string>();
181 new_plugin.size = base64_payload.size() / 4 * 3;
182 if (!load(new_plugin))
183 return_err(6, "Payload too large or some code is already loaded.");
184 auto length_decoded = etl::base64::decode(base64_payload.c_str(), base64_payload.length(),
185 new_plugin.load_addr, new_plugin.size);
186 if (length_decoded != new_plugin.size) {
187 unload();
188 return_err(7, "Could not decode base64-encoded payload.");
189 }
190
191 // Code execution
192 uint8_t *entry_point = assert_callable(plugin->load_addr + plugin->entry.addr);
193 if (!entry_point) {
194 unload();
195 return_err(8, "Entry point address not properly aligned");
196 }
197 dispatch(entry_point, plugin->entry.ret_type, msg_out["returns"].as<JsonVariant>());
198
199 // Plugins which are actually RPC.
200 if (msg_in.containsKey("immediately_unload"))
201 unload();
202
203 return 0;
204}
205
206FLASHMEM
207int loader::SinglePluginLoader::unload(JsonObjectConst msg_in, JsonObject &msg_out) {
208 if (!plugin) {
209 return_err(9, "Cannot unload, nothing loaded");
210 }
211 if (plugin->exit) {
212 // Code execution
213 uint8_t *exit_point = assert_callable(plugin->load_addr + plugin->exit->addr);
214 if (!exit_point) {
215 unload();
216 return_err(10, "Exit point address not properly aligned");
217 }
218 dispatch(exit_point, plugin->exit->ret_type, msg_out["returns"].as<JsonVariant>());
219 }
220 unload();
221 return 0;
222}
223FLASHMEM
225 if (plugin)
226 return 1; // call unload() first.
227 if (new_plugin.size > memsize)
228 return 2;
229 plugin = new_plugin; // copy
230 return true;
231}
232
233// instantiate global singleton here.
235
236#endif /* ARDUINO */
static ETL_CONSTEXPR14 etl::enable_if< etl::is_integral< T >::value &&(etl::integral_limits< T >::bits==8U), size_t >::type decode(const char *input, size_t input_length, T *output, size_t output_length)
Decode from Base64 from and to pointer/length.
Definition etl_base64.h:293
uint32_t
Definition flasher.cpp:195
uint32_t src
Definition flasher.cpp:63
#define return_err(code, msg)
Definition flasher.cpp:276
uint32_t uint32_t size
Definition flasher.cpp:63
virtual int load_and_execute(JsonObjectConst msg_in, JsonObject &msg_out)
load from protocol message, gives out reply msg, returns 0 on success
Definition plugin.cpp:150
#define LOGMEV(message,...)
Definition logging.h:93
void convertToJson(const GlobalPluginLoader &src, JsonVariant dst)
Definition plugin.cpp:99
GlobalPluginLoader PluginLoader
Definition plugin.cpp:234
void convertFromJson(JsonVariantConst src, Function &f)
Definition plugin.cpp:67
uintptr_t align(uintptr_t base, uint8_t exp)
Definition align.h:10
uintptr_t disalignment(uint8_t *base, uint8_t exp)
Definition align.h:14
uint8_t * assert_callable(uint8_t *addr)
ARM32 requires callable addresses to be memory aligned.
Definition align.h:23
bool sha256_test_short(std::string a, std::string b)
Definition dcp.h:81
#define M(name)
#define CALL(type, sig)
void prepare_mpu()
At the time being, we completely disable the Memory Protection Unit in order to enable our plugin sys...
Definition plugin.cpp:30
static constexpr int the_memsize
Definition plugin.cpp:21
uint8_t GlobalPluginLoader_Storage[the_memsize]
Definition plugin.cpp:22
FLASHMEM std::string shortened_hexdump(uint8_t *mem, size_t size)
Definition plugin.cpp:88
FLASHMEM void dispatch(uint8_t *callee, Function::Returns ret_type, JsonVariant ret)
Definition plugin.cpp:123
A jumpable function, ie something with a signature "ret_type foo();", located at relative or absolute...
Definition plugin.h:18
void None_f()
Definition plugin.h:20
uint32_t addr
Definition plugin.h:23
Reserves storage in the data segment (address space that constains static variables,...
Definition plugin.h:96
A plugin (a synonym could also be "extension" or "module") is a small piece of user-defined code whic...
Definition plugin.h:47
uint32_t size
Actual size of plugin memory image.
Definition plugin.h:49
Function entry
entry points ("constructor function"). entry.addr is relative to load_addr.
Definition plugin.h:50
uint8_t * load_addr
Absolute address in memory where the plugin is loaded to.
Definition plugin.h:48
optional< Function > exit
destructor function. entry.addr is relative to load_addr.
Definition plugin.h:51
uint8_t * load_addr
Absolute address of the memory segment. It is always (plugin->load_addr = load_addr).
Definition plugin.h:65
void unload()
frees memory, does not call unloader.
Definition plugin.h:72
uint32_t memsize
The maximum memory size managed/accessible/available by this loader. It is always (plugin->size <= me...
Definition plugin.h:66
int load(const Plugin &new_plugin)
returns 0 on success
Definition plugin.cpp:224
static utils::sha256 sha256sum()
Computes a SHA256 hash of the program image stored on Flash.
Definition hashflash.h:40
std::string to_string() const
Definition dcp.h:67
Memory Processing Unit macros from framework-arduinoteensy/cores/teensy4/startup.c.