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