1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** ThreadX Component */
16 /** */
17 /** Module Manager */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define TX_SOURCE_CODE
23
24 #include "tx_api.h"
25 #include "tx_initialize.h"
26 #include "tx_mutex.h"
27 #include "tx_thread.h"
28 #include "tx_byte_pool.h"
29 #include "txm_module.h"
30 #include "txm_module_manager_util.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _txm_module_manager_internal_load PORTABLE C */
38 /* 6.1 */
39 /* AUTHOR */
40 /* */
41 /* Scott Larson, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function allocates data memory for module and prepares the */
46 /* module for execution from the supplied code location. */
47 /* */
48 /* INPUT */
49 /* */
50 /* module_instance Module instance pointer */
51 /* module_name Module name pointer */
52 /* module_location Module code location */
53 /* code_size Module code size */
54 /* code_allocation_ptr Allocated code location */
55 /* code_allocation_size Allocated code size */
56 /* */
57 /* OUTPUT */
58 /* */
59 /* status Completion status */
60 /* */
61 /* CALLS */
62 /* */
63 /* _tx_byte_allocate Allocate data area */
64 /* _tx_mutex_get Get protection mutex */
65 /* _tx_mutex_put Release protection mutex */
66 /* */
67 /* CALLED BY */
68 /* */
69 /* Application code */
70 /* */
71 /* RELEASE HISTORY */
72 /* */
73 /* DATE NAME DESCRIPTION */
74 /* */
75 /* 09-30-2020 Scott Larson Initial Version 6.1 */
76 /* */
77 /**************************************************************************/
_txm_module_manager_internal_load(TXM_MODULE_INSTANCE * module_instance,CHAR * module_name,VOID * module_location,ULONG code_size,VOID * code_allocation_ptr,ULONG code_allocation_size)78 UINT _txm_module_manager_internal_load(TXM_MODULE_INSTANCE *module_instance, CHAR *module_name, VOID *module_location,
79 ULONG code_size, VOID *code_allocation_ptr, ULONG code_allocation_size)
80 {
81
82 TX_INTERRUPT_SAVE_AREA
83
84 TXM_MODULE_PREAMBLE *module_preamble;
85 TXM_MODULE_INSTANCE *next_module, *previous_module;
86 ULONG shell_function_adjust;
87 ULONG start_function_adjust;
88 ULONG stop_function_adjust;
89 ULONG callback_function_adjust;
90 ULONG start_stop_stack_size;
91 ULONG callback_stack_size;
92 ULONG code_size_ignored;
93 ULONG code_alignment_ignored;
94 ALIGN_TYPE data_start;
95 ULONG data_size;
96 ULONG data_alignment;
97 ULONG data_allocation_size;
98 ULONG module_properties;
99 CHAR *memory_ptr;
100 UINT status;
101
102
103 /* Check for interrupt call. */
104 if (TX_THREAD_GET_SYSTEM_STATE() != 0)
105 {
106
107 /* Now, make sure the call is from an interrupt and not initialization. */
108 if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS)
109 {
110
111 /* Invalid caller of this function, return appropriate error code. */
112 return(TX_CALLER_ERROR);
113 }
114 }
115
116 /* Determine if the module manager has not been initialized yet. */
117 if (_txm_module_manager_ready != TX_TRUE)
118 {
119
120 /* Module manager has not been initialized. */
121 return(TX_NOT_AVAILABLE);
122 }
123
124 /* Determine if the module is valid. */
125 if (module_instance == TX_NULL)
126 {
127
128 /* Invalid module pointer. */
129 return(TX_PTR_ERROR);
130 }
131
132 /* Get module manager protection mutex. */
133 _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER);
134
135 /* Determine if the module is already valid. */
136 if (module_instance -> txm_module_instance_id == TXM_MODULE_ID)
137 {
138
139 /* Release the protection mutex. */
140 _tx_mutex_put(&_txm_module_manager_mutex);
141
142 /* Module already loaded. */
143 return(TXM_MODULE_ALREADY_LOADED);
144 }
145
146 /* Pickup the module's information. */
147 module_preamble = (TXM_MODULE_PREAMBLE *) module_location;
148
149 /* Check to make sure there is a valid module to load. */
150 if (module_preamble -> txm_module_preamble_id != TXM_MODULE_ID)
151 {
152
153 /* Release the protection mutex. */
154 _tx_mutex_put(&_txm_module_manager_mutex);
155
156 /* Invalid module preamble. */
157 return(TXM_MODULE_INVALID);
158 }
159
160 /* Check the properties of this module. */
161 module_properties = module_preamble -> txm_module_preamble_property_flags & TXM_MODULE_OPTIONS_MASK;
162 if (/* Ensure the requested properties are supported. */
163 ((module_properties & _txm_module_manager_properties_supported) != module_properties) ||
164 /* Ensure the required properties are there. */
165 ((_txm_module_manager_properties_required & module_properties) != _txm_module_manager_properties_required) ||
166 /* If memory protection is enabled, then so must user mode. */
167 ((module_properties & TXM_MODULE_MEMORY_PROTECTION) && !(module_properties & TXM_MODULE_USER_MODE))
168 )
169 {
170
171 /* Release the protection mutex. */
172 _tx_mutex_put(&_txm_module_manager_mutex);
173
174 /* Invalid properties. Return error. */
175 return(TXM_MODULE_INVALID_PROPERTIES);
176 }
177
178 /* Check for valid module entry offsets. */
179 if ((module_preamble -> txm_module_preamble_shell_entry_function == 0) ||
180 (module_preamble -> txm_module_preamble_start_function == 0))
181 {
182
183 /* Release the protection mutex. */
184 _tx_mutex_put(&_txm_module_manager_mutex);
185
186 /* Invalid module preamble. */
187 return(TXM_MODULE_INVALID);
188 }
189
190 /* Check for valid sizes. */
191 if ((module_preamble -> txm_module_preamble_code_size == 0) ||
192 (module_preamble -> txm_module_preamble_data_size == 0) ||
193 (module_preamble -> txm_module_preamble_start_stop_stack_size == 0) ||
194 (module_preamble -> txm_module_preamble_callback_stack_size == 0))
195 {
196
197 /* Release the protection mutex. */
198 _tx_mutex_put(&_txm_module_manager_mutex);
199
200 /* Invalid module preamble. */
201 return(TXM_MODULE_INVALID);
202 }
203
204 /* Initialize module control block to all zeros. */
205 TX_MEMSET(module_instance, 0, sizeof(TXM_MODULE_INSTANCE));
206
207 /* Pickup the basic module sizes. */
208 data_size = module_preamble -> txm_module_preamble_data_size;
209 start_stop_stack_size = module_preamble -> txm_module_preamble_start_stop_stack_size;
210 callback_stack_size = module_preamble -> txm_module_preamble_callback_stack_size;
211
212 /* Adjust the size of the module elements to be aligned to the default alignment. We do this
213 so that when we partition the allocated memory, we can simply place these regions right beside
214 each other without having to align their pointers. Note this only works when they all have
215 the same alignment. */
216
217 TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(data_size, TXM_MODULE_DATA_ALIGNMENT, data_size);
218 data_size = ((data_size - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT;
219
220 TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(start_stop_stack_size, TXM_MODULE_DATA_ALIGNMENT, start_stop_stack_size);
221 start_stop_stack_size = ((start_stop_stack_size - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT;
222
223 TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(callback_stack_size, TXM_MODULE_DATA_ALIGNMENT, callback_stack_size);
224 callback_stack_size = ((callback_stack_size - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT;
225
226 /* Update the data size to account for the default thread stacks. */
227 TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(data_size, start_stop_stack_size, data_size);
228 TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(data_size, callback_stack_size, data_size);
229
230 /* Setup the default code and data alignments. */
231 data_alignment = (ULONG) TXM_MODULE_DATA_ALIGNMENT;
232
233 /* Get the port-specific alignment for the data size. Note we only want data
234 so we pass values of 1 for code (to avoid any possible div by 0 errors). */
235 code_size_ignored = 1;
236 code_alignment_ignored = 1;
237 TXM_MODULE_MANAGER_ALIGNMENT_ADJUST(module_preamble, code_size_ignored, code_alignment_ignored, data_size, data_alignment)
238
239 /* Calculate the module's total RAM memory requirement. This entire area is allocated from the module
240 manager's byte pool. The general layout is defined as follows:
241
242 Lowest Address: Start of start/stop thread stack
243 ... [note: thread entry info is embedded near end of stack areas]
244 End of start/stop thread stack
245
246 Start of callback thread stack
247 ... [note: thread entry info is embedded near end of stack areas]
248 End of callback thread stack
249
250 Module's Data Area
251 ...
252 End of Module's Data Area
253 Highest Address: */
254
255 /* Add an extra alignment increment so we can align the pointer after allocation. */
256 TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(data_size, data_alignment, data_allocation_size);
257
258 /* Allocate memory for the module. */
259 status = _tx_byte_allocate(&_txm_module_manager_byte_pool, (VOID **) &memory_ptr, data_allocation_size, TX_NO_WAIT);
260
261 /* Determine if the module memory allocation was successful. */
262 if (status)
263 {
264
265 /* Release the protection mutex. */
266 _tx_mutex_put(&_txm_module_manager_mutex);
267
268 /* No memory, return an error. */
269 return(TX_NO_MEMORY);
270 }
271
272 /* Clear the allocated memory. */
273 TX_MEMSET(memory_ptr, ((UCHAR) 0), data_allocation_size);
274
275 /* Disable interrupts. */
276 TX_DISABLE
277
278 /* Setup the module instance structure. */
279 module_instance -> txm_module_instance_id = TXM_MODULE_ID;
280
281 /* Save the module name. */
282 module_instance -> txm_module_instance_name = module_name;
283
284 /* Save the module properties. */
285 module_instance -> txm_module_instance_property_flags = module_preamble -> txm_module_preamble_property_flags;
286
287 /* Set the module data memory allocation. This is the address released
288 when the module is unloaded. */
289 module_instance -> txm_module_instance_data_allocation_ptr = (VOID *) memory_ptr;
290
291 /* Save the data allocation size. */
292 module_instance -> txm_module_instance_data_allocation_size = data_allocation_size;
293
294 /* Calculate the actual start of the data area. This needs to be adjusted based on the alignment. */
295 data_start = (ALIGN_TYPE) memory_ptr;
296 data_start = (data_start + (((ALIGN_TYPE)data_alignment) - 1)) & ~(((ALIGN_TYPE)data_alignment) - 1);
297 memory_ptr = (CHAR *) data_start;
298 module_instance -> txm_module_instance_data_start = (VOID *) memory_ptr;
299
300 /* Compute the end of the data memory allocation. */
301 module_instance -> txm_module_instance_data_end = (VOID *) (memory_ptr + (data_size - 1));
302
303 /* Save the size of the data area. */
304 module_instance -> txm_module_instance_data_size = data_size;
305
306 /* Set the module code memory allocation. This is the address released
307 when the module is unloaded. */
308 module_instance -> txm_module_instance_code_allocation_ptr = (VOID *) code_allocation_ptr;
309
310 /* Save the code allocation size. */
311 module_instance -> txm_module_instance_code_allocation_size = code_allocation_size;
312
313 /* Setup the code pointers. Since the code was loaded in-place, this is effectively just the values supplied in the API call. */
314 module_instance -> txm_module_instance_code_start = (VOID *) module_location;
315 module_instance -> txm_module_instance_code_end = (VOID *) (((CHAR *) module_location) + (code_size - 1));
316
317 /* Setup the code size. */
318 module_instance -> txm_module_instance_code_size = code_size;
319
320 /* Save the module's total memory usage. */
321 module_instance -> txm_module_instance_total_ram_usage = data_allocation_size + code_allocation_size;
322
323 /* Set the module state to started. */
324 module_instance -> txm_module_instance_state = TXM_MODULE_LOADED;
325
326 /* Save the preamble pointer. */
327 module_instance -> txm_module_instance_preamble_ptr = module_preamble;
328
329 /* Save the module application ID in the module instance. */
330 module_instance -> txm_module_instance_application_module_id = module_preamble -> txm_module_preamble_application_module_id;
331
332 /* Setup the module's start/stop thread stack area. */
333 module_instance -> txm_module_instance_start_stop_stack_start_address = (VOID *) (memory_ptr);
334 module_instance -> txm_module_instance_start_stop_stack_size = start_stop_stack_size;
335 module_instance -> txm_module_instance_start_stop_stack_end_address = (VOID *) (memory_ptr + (start_stop_stack_size - 1));
336
337 /* Move the memory pointer forward. */
338 memory_ptr = memory_ptr + start_stop_stack_size;
339
340 /* Save the start/stop thread priority. */
341 module_instance -> txm_module_instance_start_stop_priority = module_preamble -> txm_module_preamble_start_stop_priority;
342
343 /* Setup the module's callback thread stack area. */
344 module_instance -> txm_module_instance_callback_stack_start_address = (VOID *) (memory_ptr);
345 module_instance -> txm_module_instance_callback_stack_size = callback_stack_size;
346 module_instance -> txm_module_instance_callback_stack_end_address = (VOID *) (memory_ptr + (callback_stack_size - 1));
347
348 /* Move the memory pointer forward. */
349 memory_ptr = memory_ptr + callback_stack_size;
350
351 /* Save the callback thread priority. */
352 module_instance -> txm_module_instance_callback_priority = module_preamble -> txm_module_preamble_callback_priority;
353
354 /* Setup the start of the module data section. */
355 module_instance -> txm_module_instance_module_data_base_address = (VOID *) (memory_ptr);
356
357 /* Calculate the function adjustments based on the specific implementation of the module manager/module. */
358 TXM_MODULE_MANAGER_CALCULATE_ADJUSTMENTS(module_preamble -> txm_module_preamble_property_flags, shell_function_adjust, start_function_adjust, stop_function_adjust, callback_function_adjust)
359
360 /* Build actual addresses based on load... Setup all the function pointers. Any adjustments needed to shell entry, start function, and callback function are defined in the
361 module preamble. */
362 module_instance -> txm_module_instance_shell_entry_function = (VOID (*)(TX_THREAD *, TXM_MODULE_INSTANCE *)) (((CHAR *) module_instance -> txm_module_instance_code_start) +
363 (module_preamble -> txm_module_preamble_shell_entry_function) +
364 (shell_function_adjust));
365 module_instance -> txm_module_instance_start_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) +
366 (module_preamble -> txm_module_preamble_start_function) +
367 (start_function_adjust));
368 module_instance -> txm_module_instance_callback_request_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) +
369 (module_preamble -> txm_module_preamble_callback_function) +
370 (callback_function_adjust));
371 /* Determine if there is a stop function for this module. */
372 if (module_preamble -> txm_module_preamble_stop_function)
373 {
374
375 /* Yes, there is a stop function, build the address. */
376 module_instance -> txm_module_instance_stop_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) +
377 (module_preamble -> txm_module_preamble_stop_function) +
378 (stop_function_adjust));
379 }
380 else
381 {
382
383 /* No, there is no stop function. Just set the pointer to NULL. */
384 module_instance -> txm_module_instance_stop_thread_entry = TX_NULL;
385 }
386
387 /* Load the module control block with port-specific information. */
388 TXM_MODULE_MANAGER_MODULE_SETUP(module_instance);
389
390 /* Now add the module to the linked list of created modules. */
391 if (_txm_module_manger_loaded_count++ == 0)
392 {
393
394 /* The loaded module list is empty. Add module to empty list. */
395 _txm_module_manager_loaded_list_ptr = module_instance;
396 module_instance -> txm_module_instance_loaded_next = module_instance;
397 module_instance -> txm_module_instance_loaded_previous = module_instance;
398 }
399 else
400 {
401
402 /* This list is not NULL, add to the end of the list. */
403 next_module = _txm_module_manager_loaded_list_ptr;
404 previous_module = next_module -> txm_module_instance_loaded_previous;
405
406 /* Place the new module in the list. */
407 next_module -> txm_module_instance_loaded_previous = module_instance;
408 previous_module -> txm_module_instance_loaded_next = module_instance;
409
410 /* Setup this module's created links. */
411 module_instance -> txm_module_instance_loaded_previous = previous_module;
412 module_instance -> txm_module_instance_loaded_next = next_module;
413 }
414
415 /* Restore interrupts. */
416 TX_RESTORE
417
418 /* Release the protection mutex. */
419 _tx_mutex_put(&_txm_module_manager_mutex);
420
421 /* Return success. */
422 return(TX_SUCCESS);
423 }
424