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