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 /** USBX Component                                                        */
16 /**                                                                       */
17 /**   Utility                                                             */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 
23 /* Include necessary system files.  */
24 
25 #define UX_SOURCE_CODE
26 
27 #include "ux_api.h"
28 
29 /**************************************************************************/
30 /*                                                                        */
31 /*  FUNCTION                                               RELEASE        */
32 /*                                                                        */
33 /*    _ux_utility_memory_allocate                         PORTABLE C      */
34 /*                                                           6.3.0        */
35 /*  AUTHOR                                                                */
36 /*                                                                        */
37 /*    Chaoqiong Xiao, Microsoft Corporation                               */
38 /*                                                                        */
39 /*  DESCRIPTION                                                           */
40 /*                                                                        */
41 /*    This function allocates a block of memory for the specified size    */
42 /*    and alignment.                                                      */
43 /*                                                                        */
44 /*  INPUT                                                                 */
45 /*                                                                        */
46 /*    memory_alignment                      Memory alignment required     */
47 /*    memory_cache_flag                     Memory pool source            */
48 /*    memory_size_requested                 Number of bytes required      */
49 /*                                                                        */
50 /*  OUTPUT                                                                */
51 /*                                                                        */
52 /*    Pointer to block of memory                                          */
53 /*                                                                        */
54 /*  CALLS                                                                 */
55 /*                                                                        */
56 /*    _ux_utility_memory_free_block_best_get Get best fit block of memory */
57 /*    _ux_utility_memory_set                 Set block of memory          */
58 /*                                                                        */
59 /*  CALLED BY                                                             */
60 /*                                                                        */
61 /*    USBX Components                                                     */
62 /*                                                                        */
63 /*  RELEASE HISTORY                                                       */
64 /*                                                                        */
65 /*    DATE              NAME                      DESCRIPTION             */
66 /*                                                                        */
67 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
68 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
69 /*                                            verified memset and memcpy  */
70 /*                                            cases,                      */
71 /*                                            resulting in version 6.1    */
72 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
73 /*                                            added standalone support,   */
74 /*                                            resulting in version 6.1.10 */
75 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
76 /*                                            internal clean up,          */
77 /*                                            resulting in version 6.1.11 */
78 /*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
79 /*                                            refined memory management,  */
80 /*                                            fixed issue in 64-bit env,  */
81 /*                                            resulting in version 6.3.0  */
82 /*                                                                        */
83 /**************************************************************************/
_ux_utility_memory_allocate(ULONG memory_alignment,ULONG memory_cache_flag,ULONG memory_size_requested)84 VOID  *_ux_utility_memory_allocate(ULONG memory_alignment, ULONG memory_cache_flag,
85                                    ULONG memory_size_requested)
86 {
87 UX_MEMORY_BYTE_POOL *pool_ptr;
88 UCHAR               *current_ptr;
89 UCHAR               *work_ptr;
90 UCHAR               *next_ptr;
91 ALIGN_TYPE          *free_ptr;
92 UCHAR               **this_block_link_ptr;
93 UCHAR               **next_block_link_ptr;
94 ULONG               available_bytes;
95 
96 ALIGN_TYPE          int_memory_buffer;
97 #ifdef UX_ENABLE_MEMORY_STATISTICS
98 UINT                index;
99 #endif
100 
101     /* Get the pool ptr */
102     if (memory_cache_flag == UX_REGULAR_MEMORY)
103     {
104         pool_ptr = _ux_system -> ux_system_memory_byte_pool[UX_MEMORY_BYTE_POOL_REGULAR];
105     }
106     else if (memory_cache_flag == UX_CACHE_SAFE_MEMORY)
107     {
108         pool_ptr = _ux_system -> ux_system_memory_byte_pool[UX_MEMORY_BYTE_POOL_CACHE_SAFE];
109     }
110     else
111     {
112         return(UX_NULL);
113     }
114 
115     /* Check if pool_ptr is NX_NULL */
116     if (pool_ptr == UX_NULL)
117     {
118         return(UX_NULL);
119     }
120 
121     /* Check if the memory size requested is 0.  */
122     if (memory_size_requested == 0)
123     {
124         return(UX_NULL);
125     }
126 
127     /* Get the mutex as this is a critical section.  */
128     _ux_system_mutex_on(&_ux_system -> ux_system_mutex);
129 
130 #ifdef UX_ENFORCE_SAFE_ALIGNMENT
131 
132     /* Check if safe alignment requested, in this case switch to UX_NO_ALIGN.  */
133     if (memory_alignment == UX_SAFE_ALIGN)
134     {
135 
136         /* We will use the memory_size_requested for the alignment.
137            But we check to see if we have a minimum or maximum alignment.  */
138         if (memory_size_requested < UX_ALIGN_MIN)
139 
140             /* No need to bother about alignment for small packets sizes.  */
141             memory_alignment = UX_NO_ALIGN;
142 
143         /* Check if we are over the maximum.  */
144         else if (memory_size_requested > UX_MAX_SCATTER_GATHER_ALIGNMENT)
145 
146             /* We are over the max alignment required. Use the maximum instead.  */
147             memory_alignment = UX_MAX_SCATTER_GATHER_ALIGNMENT - 1;
148 
149         /* We are not over the maximum, so approximate the alignment according to the size of the memory.
150             Check range for alignment on 4096 bytes.  */
151         else if (memory_size_requested >= UX_ALIGN_2048 + 1)
152             memory_alignment = UX_ALIGN_4096;
153 
154         /* Check range for alignment on 2048 bytes.  */
155         else if (memory_size_requested >= UX_ALIGN_1024 + 1)
156             memory_alignment = UX_ALIGN_2048;
157 
158         /* Check range for alignment on 1024 bytes.  */
159         else if (memory_size_requested >= UX_ALIGN_512 + 1)
160             memory_alignment = UX_ALIGN_1024;
161 
162         /* Check range for alignment on 512 bytes.  */
163         else if (memory_size_requested >= UX_ALIGN_256 + 1)
164             memory_alignment = UX_ALIGN_512;
165 
166         /* Check range for alignment on 256 bytes.  */
167         else if (memory_size_requested >= UX_ALIGN_128 + 1)
168             memory_alignment = UX_ALIGN_256;
169 
170         /* Check range for alignment on 128 bytes.  */
171         else if (memory_size_requested >= UX_ALIGN_64 + 1)
172             memory_alignment = UX_ALIGN_128;
173 
174         /* Check range for alignment on 64 bytes.  */
175         else if (memory_size_requested >= UX_ALIGN_32 + 1)
176             memory_alignment = UX_ALIGN_64;
177 
178         /* Check range for alignment on 32 bytes.  */
179         else if (memory_size_requested >= UX_ALIGN_16 + 1)
180             memory_alignment = UX_ALIGN_32;
181 
182         /* Check range for alignment on 16 bytes.  */
183         else if (memory_size_requested >= UX_ALIGN_8 + 1)
184             memory_alignment = UX_ALIGN_16;
185 
186         else
187             memory_alignment = UX_ALIGN_MIN;
188     }
189 
190 #else
191 
192     /* Check if safe alignment requested, in this case switch to UX_NO_ALIGN.  */
193     if (memory_alignment == UX_SAFE_ALIGN)
194         memory_alignment = UX_NO_ALIGN;
195 
196 #endif
197 
198     /* Ensure the alignment meats the minimum.  */
199     if (memory_alignment < UX_ALIGN_MIN)
200         memory_alignment =  UX_ALIGN_MIN;
201 
202     /* We need to make sure that the next memory block buffer is 8-byte aligned too. We
203        do this by first adjusting the requested memory to be 8-byte aligned. One problem
204        now is that the memory block might not be a size that is a multiple of 8, so we need
205        to add the amount of memory required such that the memory buffer after the block has
206        the correct alignment. For example, if the memory block has a size of 12, then we need
207        to make sure it is placed on an 8-byte alignment that is after a 8-byte alignment so
208        that the memory right after the memory block is 8-byte aligned (16).  */
209     memory_size_requested =  (memory_size_requested + UX_ALIGN_MIN) & (~(ULONG)UX_ALIGN_MIN);
210     memory_size_requested += (((ULONG)(UX_MEMORY_BLOCK_HEADER_SIZE + UX_ALIGN_MIN) & (~(ULONG)UX_ALIGN_MIN)) - (ULONG)UX_MEMORY_BLOCK_HEADER_SIZE);
211 
212     if (memory_alignment <= UX_ALIGN_MIN)
213         current_ptr = _ux_utility_memory_byte_pool_search(pool_ptr, memory_size_requested);
214     else
215         current_ptr = _ux_utility_memory_byte_pool_search(pool_ptr, memory_size_requested + memory_alignment);
216 
217     /* Check if we found a memory block.  */
218     if (current_ptr == UX_NULL)
219     {
220 
221         /* We could not find a memory block.  */
222         _ux_system_mutex_off(&_ux_system -> ux_system_mutex);
223 
224         UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, memory_size_requested, 0, 0, UX_TRACE_ERRORS, 0, 0)
225 
226         /* Error trap. */
227         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, UX_MEMORY_INSUFFICIENT);
228 
229         return(UX_NULL);
230     }
231 
232     /* Pickup the next block's pointer.  */
233     this_block_link_ptr =  UX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
234     next_ptr =             *this_block_link_ptr;
235 
236     /* Calculate the number of bytes available in this block.  */
237     available_bytes =   UX_UCHAR_POINTER_DIF(next_ptr, current_ptr);
238     available_bytes =   available_bytes - UX_MEMORY_BLOCK_HEADER_SIZE;
239 
240     /* Get the memory buffer for this block.  */
241     int_memory_buffer = (ALIGN_TYPE) (UX_UCHAR_POINTER_ADD(current_ptr, UX_MEMORY_BLOCK_HEADER_SIZE));
242 
243     /* In case we are not aligned  */
244     if ((int_memory_buffer & memory_alignment) != 0)
245     {
246 
247         /* No, we need to align the memory buffer.  */
248         int_memory_buffer += (ALIGN_TYPE)UX_MEMORY_BLOCK_HEADER_SIZE;
249         int_memory_buffer += memory_alignment;
250         int_memory_buffer &=  ~((ALIGN_TYPE) memory_alignment);
251         int_memory_buffer -= (ALIGN_TYPE)UX_MEMORY_BLOCK_HEADER_SIZE;
252 
253         /* Setup the new free block.  */
254         next_ptr = (UCHAR *)int_memory_buffer;
255 
256         /* Setup the new free block.  */
257         next_block_link_ptr =   UX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
258         *next_block_link_ptr =  *this_block_link_ptr;
259         work_ptr =              UX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
260         free_ptr =              UX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
261         *free_ptr =             UX_BYTE_BLOCK_FREE;
262 
263         /* Increase the total fragment counter.  */
264         pool_ptr -> ux_byte_pool_fragments++;
265 
266         /* Update the current pointer to point at the newly created block.  */
267         *this_block_link_ptr =  next_ptr;
268 
269         /* Calculate the available bytes.  */
270         available_bytes -=  UX_UCHAR_POINTER_DIF(next_ptr, current_ptr);
271 
272         /* Set Current pointer to the aligned memory buffer.  */
273         current_ptr = next_ptr;
274     }
275 
276     /* Now we are aligned, determine if we need to split this block.  */
277     if ((available_bytes - memory_size_requested) >= ((ULONG) UX_BYTE_BLOCK_MIN))
278     {
279 
280         /* Split the block.  */
281         next_ptr =  UX_UCHAR_POINTER_ADD(current_ptr, (memory_size_requested + UX_MEMORY_BLOCK_HEADER_SIZE));
282 
283         /* Setup the new free block.  */
284         next_block_link_ptr =   UX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
285         this_block_link_ptr =   UX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
286         *next_block_link_ptr =  *this_block_link_ptr;
287         work_ptr =              UX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
288         free_ptr =              UX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
289         *free_ptr =             UX_BYTE_BLOCK_FREE;
290 
291         /* Increase the total fragment counter.  */
292         pool_ptr -> ux_byte_pool_fragments++;
293 
294         /* Update the current pointer to point at the newly created block.  */
295         *this_block_link_ptr =  next_ptr;
296 
297         /* Set available equal to memory size for subsequent calculation.  */
298         available_bytes =  memory_size_requested;
299     }
300 
301     /* In any case, mark the current block as allocated.  */
302     work_ptr =              UX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
303     this_block_link_ptr =   UX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
304     *this_block_link_ptr =  UX_BYTE_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr);
305 
306     /* Reduce the number of available bytes in the pool.  */
307     pool_ptr -> ux_byte_pool_available =  pool_ptr -> ux_byte_pool_available - (available_bytes + UX_MEMORY_BLOCK_HEADER_SIZE);
308 
309     /* Determine if the search pointer needs to be updated. This is only done
310         if the search pointer matches the block to be returned.  */
311     if (current_ptr == pool_ptr -> ux_byte_pool_search)
312     {
313 
314         /* Yes, update the search pointer to the next block.  */
315         this_block_link_ptr =   UX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
316         pool_ptr -> ux_byte_pool_search =  *this_block_link_ptr;
317     }
318 
319     /* Adjust the pointer for the application.  */
320     work_ptr =  UX_UCHAR_POINTER_ADD(current_ptr, UX_MEMORY_BLOCK_HEADER_SIZE);
321 
322     /* Clear the memory block.  */
323     _ux_utility_memory_set(work_ptr, 0, available_bytes); /* Use case of memset is verified. */
324 
325 #ifdef UX_ENABLE_MEMORY_STATISTICS
326 
327     /* Update allocate count, total size.  */
328     if (memory_cache_flag == UX_REGULAR_MEMORY)
329         index = UX_MEMORY_BYTE_POOL_REGULAR;
330     else
331         index = UX_MEMORY_BYTE_POOL_CACHE_SAFE;
332 
333     /* Update allocate count, total size.  */
334     _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_count ++;
335     _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_total += (available_bytes + UX_MEMORY_BLOCK_HEADER_SIZE);
336 
337     if (_ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_max_count < _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_count)
338         _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_max_count = _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_count;
339 
340     if (_ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_max_total < _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_total)
341         _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_max_total = _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_alloc_total;
342 
343     /* Log max usage of memory pool.  */
344     if (_ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_min_free > _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_available)
345         _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_min_free = _ux_system -> ux_system_memory_byte_pool[index] -> ux_byte_pool_available;
346 #endif
347 
348     /* Release the protection.  */
349     _ux_system_mutex_off(&_ux_system -> ux_system_mutex);
350 
351     return(work_ptr);
352 }
353