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