dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
loadable.c
Go to the documentation of this file.
1 
9 /*
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18  * for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, see <http://www.gnu.org/licenses/>
22  *
23  * Additional note on redistribution: The copyright and license notices above
24  * must be maintained in each individual source file that is a derivative work
25  * of this source file; otherwise redistribution is prohibited.
26  */
27 
28 
29 #include "openpilot.h"
30 #include <stdbool.h>
31 #include "pios_thread.h"
32 
33 #include "loadable_extension.h"
34 
35 #include "modulesettings.h"
36 
37 #define MOD_RAM_BEGIN 0x10000000
38 #define MOD_RODATA_BEGIN 0x20000000
39 #define MOD_CALLS_BEGIN 0xdf000000
40 
41 #define CALL_DO_DUMB_TEST_DELAY 0xdf000001
42 #define CALL_PIOS_THREAD_CREATE 0xdf000003
43 #define CALL_PIOS_THREAD_SLEEP 0xdf000005
44 #define CALL_DO_DUMB_REGTASK 0xdf000007
45 #define CALL_ANNUNC_CUSTOM 0xdf000009
46 
47 static bool module_enabled;
48 
49 static int32_t loadable_initialize(void)
50 {
51 #ifdef MODULE_Loadable_BUILTIN
52  module_enabled = true;
53 #else
54  uint8_t module_state[MODULESETTINGS_ADMINSTATE_NUMELEM];
55  ModuleSettingsAdminStateGet(module_state);
56  if (module_state[MODULESETTINGS_ADMINSTATE_LOADABLE] == MODULESETTINGS_ADMINSTATE_ENABLED) {
57  module_enabled = true;
58  } else {
59  module_enabled = false;
60  }
61 #endif
62 
63  return 0;
64 }
65 
66 static uint32_t construct_trampoline(struct loadable_extension *ext,
67  void *data_seg, uint32_t got_addr)
68 {
69  /* FLASH relative offset */
70 
71  uint32_t tramp_len;
72  void *tramp_template;
73 
74  /* This wraps the trampoline code in a shim that gives us the
75  * length of the code and the address of it.
76  */
77  asm volatile(
78  ".align 2\n\t"
79  "ldr %0, tramplen\n\t"
80  "adr %1, trampbegin\n\t"
81  "b trampend\n\t"
82  "tramplen: .word trampend-trampbegin\n\t"
83  /* Next-- the actual trampoline code */
84  "trampbegin:\n\t"
85  "push { r9, lr }\n\t" // Preserve ret addr, and r9 just in case
86  "ldr ip, trampdest\n\t" // Put the trampoline dest into r12
87  "ldr r9, trampdata\n\t" // Set up data-seg-pointer
88  "blx ip\n\t" // Branch via IP, with linkage
89  "pop { r9, pc }\n\t" // Restore R9 and return via saved lr
90  ".align 2\n\t"
91  "trampdest: .word 123\n\t" /* Fill in tramp destination here */
92  "trampdata: .word 456\n\t" /* Fill in tramp data seg here */
93  "trampend:\n\t"
94  : "=r" (tramp_len), "=r" (tramp_template));
95 
96  uint32_t *tramp_alloc = PIOS_malloc(tramp_len);
97 
98  if (!tramp_alloc) {
99  return 0;
100  }
101 
102  memcpy(tramp_alloc, tramp_template, tramp_len);
103 
104  tramp_alloc[(tramp_len / 4) - 2] = ((uintptr_t) ext) + got_addr;
105  tramp_alloc[(tramp_len / 4) - 1] = (uintptr_t) data_seg;
106 
107  return ((uint32_t) tramp_alloc) + 1;
108 }
109 
110 static void *layout_extension_ram(struct loadable_extension *ext)
111 {
112  char *new_seg;
113 
114  /* Allocate a data segment, and copy the things that matter
115  * from it.
116  */
117  new_seg = PIOS_malloc(ext->ram_seg_len);
118  if (!new_seg) {
119  return NULL;
120  }
121 
122  memcpy(new_seg,
123  (void *) (((uintptr_t) ext) + ext->ram_seg_copyoff),
124  ext->ram_seg_copylen);
125 
126  /* Zero the rest of things */
127  memset(new_seg + ext->ram_seg_copylen, 0,
128  ext->ram_seg_len - ext->ram_seg_copylen);
129 
130  /* process relocations */
131  uint32_t *got = (uint32_t *) new_seg;
132 
133  for (int i = 0; i < (ext->ram_seg_gotlen / sizeof(uint32_t)); i++) {
134  if (got[i] >= MOD_CALLS_BEGIN) {
135  /* df... These shouldn't really show up in
136  * GOT, but ignore them if they do
137  */
138  } else if (got[i] >= MOD_RODATA_BEGIN) {
139  /* RODATA. Convert to a flash memory address */
140 
141  got[i] = ((uint32_t) ext) + got[i] - MOD_RODATA_BEGIN;
142  } else if (got[i] >= MOD_RAM_BEGIN) {
143  /* RAM-resident relative offset */
144  got[i] = (uint32_t) (new_seg + got[i] - MOD_RAM_BEGIN - ext->ram_seg_copyoff);
145  } else if (got[i]) {
146  /* Code address, presumably used in callback. It
147  * needs a trampoline that ensures that R9 is set.
148  */
149  got[i] = construct_trampoline(ext, new_seg, got[i]);
150 
151  if (!got[i]) {
152  return NULL;
153  }
154  }
155  }
156 
157  return new_seg;
158 }
159 
160 static void invoke_one_loadable(struct loadable_extension *ext)
161 {
162  /* XXX check payload CRC. */
163 
164  register void *data_seg asm("r9") = layout_extension_ram(ext);
165 
166  if (!data_seg) {
167  return;
168  }
169 
170  void (*entry)() = (void *) (((uintptr_t) ext) + ext->entry_offset);
171 
172  asm volatile(
173  "blx %0\n\t"
174  : /* No output operands */
175  : "r"(entry), "r"(data_seg) // Expects data seg in r9
176  : "memory", // callback may clobber memory,
177  "cc", // condition codes,
178  "r0", "r1", "r2", "r3", "r12",
179  // And call-clobbered floating point registers
180  "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
181  "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
182  "lr" // we clobber lr
183  );
184 }
185 
186 static void invoke_loadables()
187 {
188  uintptr_t part_id;
189 
191  &part_id)) {
192  /* No loadable partition */
193  return;
194  }
195 
196  uint32_t part_len;
197  uint8_t *part_beginning;
198 
199  part_beginning = PIOS_FLASH_get_address(part_id, &part_len);
200 
201  if (!part_beginning) {
202  return;
203  }
204 
205  uint32_t part_pos = 0;
206 
207  while ((part_pos + sizeof(struct loadable_extension)) < part_len) {
208  struct loadable_extension *ext =
209  (void *) (part_beginning + part_pos);
210 
211  /* Validate header before trying to go further */
212 
213  if (ext->magic != LOADABLE_EXTENSION_MAGIC) {
214  return;
215  }
216 
217  if (ext->length < sizeof(struct loadable_extension)) {
218  return;
219  }
220 
222  return;
223  }
224 
225  if (ext->entry_offset >= ext->length) {
226  return;
227  }
228 
229  if (ext->entry_offset < sizeof(struct loadable_extension)) {
230  return;
231  }
232 
233  /* XXX test the data seg related things for sanity */
234 
235  /* XXX alignment requirements */
236 
237  /* XXX check CRC */
238 
239  invoke_one_loadable(ext);
240 
241  part_pos += ext->length;
242  }
243 }
244 
245 static int32_t loadable_start(void)
246 {
247  if (!module_enabled) {
248  return 0;
249  }
250 
252 
253  return 0;
254 }
255 
256 
258 
259 static void do_dumb_regtask(struct pios_thread *h)
260 {
261  TaskMonitorAdd(TASKINFO_RUNNING_LOADABLE, h);
262 }
263 
264 static void do_dumb_test_delay(int cycles) {
265  for (volatile int i = 0; i < cycles; i++);
266 }
267 
268 #define EXCEPTION_STACK_PC_OFFSET 6
269 
270 /* This would be better as an enum, but C standard specifies enum type is
271  * signed... Also all of these should be odd, to specify thumbiness.
272  */
273 
274 void MemManageHandler_C(uint32_t *exception_stack) {
275  uintptr_t pc = exception_stack[EXCEPTION_STACK_PC_OFFSET];
276 
277  switch ((pc & 0xfffffffe) | 1) {
279  pc = (uintptr_t) do_dumb_test_delay;
280  break;
282  pc = (uintptr_t) PIOS_Thread_Create;
283  break;
285  pc = (uintptr_t) PIOS_Thread_Sleep;
286  break;
288  pc = (uintptr_t) do_dumb_regtask;
289  break;
290  case CALL_ANNUNC_CUSTOM:
291  pc = (uintptr_t) system_annunc_custom_string;
292  break;
293  default:
294  while (1);
295  }
296 
297  exception_stack[EXCEPTION_STACK_PC_OFFSET] = pc;
298 }
299 
300 void MemManageVector(void) __attribute__((naked));
301 
302 void MemManageVector(void)
303 {
304  /* Stores the exception stack pointer into an argument, and invokes
305  * the real handler (without linkage-- the BX LR at completion of
306  * MemManageHandler will in turn return from the exception.
307  */
308 
309  asm volatile(
310  /* Inspect the LSB of the link register, to determine whether
311  * we are using PSP or MSP.
312  */
313  "ldr r1, =stack_sel_mask\n\t"
314  "tst r1, lr\n\t"
315  "bne using_psp\n\t" /* if bit unset */
316  "mrs r0, MSP\n\t"
317  "b MemManageHandler_C\n\t"
318  "using_psp: mrs r0, PSP\n\t"
319  "b MemManageHandler_C\n\t"
320  "stack_sel_mask: .word 0x00000001\n\t"
321  : );
322 }
323 
int32_t PIOS_FLASH_find_partition_id(enum pios_flash_partition_labels label, uintptr_t *partition_id)
#define MOD_RODATA_BEGIN
Definition: loadable.c:38
#define MOD_RAM_BEGIN
Definition: loadable.c:37
static void invoke_loadables()
Definition: loadable.c:186
void * PIOS_FLASH_get_address(uintptr_t partition_id, uint32_t *partition_size)
static void do_dumb_test_delay(int cycles)
Definition: loadable.c:264
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
static int32_t loadable_initialize(void)
Definition: loadable.c:49
#define CALL_DO_DUMB_REGTASK
Definition: loadable.c:44
static uint32_t construct_trampoline(struct loadable_extension *ext, void *data_seg, uint32_t got_addr)
Definition: loadable.c:66
void MemManageHandler_C(uint32_t *exception_stack)
Definition: loadable.c:274
#define MODULE_INITCALL(ifn, sfn)
Definition: pios_initcall.h:67
#define CALL_ANNUNC_CUSTOM
Definition: loadable.c:45
static bool module_enabled
Definition: loadable.c:47
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
Definition: pios_thread.c:89
int32_t TaskMonitorAdd(TaskInfoRunningElem task, struct pios_thread *threadp)
Definition: taskmonitor.c:67
uint8_t i
Definition: msp_messages.h:97
#define CALL_DO_DUMB_TEST_DELAY
Definition: loadable.c:41
#define LOADABLE_EXTENSION_MAGIC
void PIOS_Thread_Sleep(uint32_t time_ms)
Definition: pios_thread.c:229
#define EXCEPTION_STACK_PC_OFFSET
Definition: loadable.c:268
#define CALL_PIOS_THREAD_SLEEP
Definition: loadable.c:43
#define MOD_CALLS_BEGIN
Definition: loadable.c:39
static void * layout_extension_ram(struct loadable_extension *ext)
Definition: loadable.c:110
Includes PiOS and core architecture components.
static int32_t loadable_start(void)
Definition: loadable.c:245
#define CALL_PIOS_THREAD_CREATE
Definition: loadable.c:42
void system_annunc_custom_string(const char *string)
Definition: systemmod.c:417
#define LOADABLE_REQUIRE_VERSION_INVALID
static void do_dumb_regtask(struct pios_thread *h)
Definition: loadable.c:259
void MemManageVector(void)
Definition: loadable.c:300
static void entry()
Definition: loadabletest.c:40
static void invoke_one_loadable(struct loadable_extension *ext)
Definition: loadable.c:160