xref: /freebsd/sys/contrib/ncsw/etc/mm.c (revision d4eeb02986980bf33dd56c41ceb9fc5f180c0d47)
1 /*
2  * Copyright 2008-2012 Freescale Semiconductor Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *     * Redistributions of source code must retain the above copyright
7  *       notice, this list of conditions and the following disclaimer.
8  *     * Redistributions in binary form must reproduce the above copyright
9  *       notice, this list of conditions and the following disclaimer in the
10  *       documentation and/or other materials provided with the distribution.
11  *     * Neither the name of Freescale Semiconductor nor the
12  *       names of its contributors may be used to endorse or promote products
13  *       derived from this software without specific prior written permission.
14  *
15  *
16  * ALTERNATIVELY, this software may be distributed under the terms of the
17  * GNU General Public License ("GPL") as published by the Free Software
18  * Foundation, either version 2 of that License or (at your option) any
19  * later version.
20  *
21  * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 
34 #include "string_ext.h"
35 #include "error_ext.h"
36 #include "std_ext.h"
37 #include "part_ext.h"
38 #include "xx_ext.h"
39 
40 #include "mm.h"
41 
42 
43 
44 
45 /**********************************************************************
46  *                     MM internal routines set                       *
47  **********************************************************************/
48 
49 /****************************************************************
50  *  Routine:   CreateBusyBlock
51  *
52  *  Description:
53  *      Initializes a new busy block of "size" bytes and started
54  *      rom "base" address. Each busy block has a name that
55  *      specified the purpose of the memory allocation.
56  *
57  *  Arguments:
58  *      base      - base address of the busy block
59  *      size      - size of the busy block
60  *      name      - name that specified the busy block
61  *
62  *  Return value:
63  *      A pointer to new created structure returned on success;
64  *      Otherwise, NULL.
65  ****************************************************************/
66 static t_BusyBlock * CreateBusyBlock(uint64_t base, uint64_t size, char *name)
67 {
68     t_BusyBlock *p_BusyBlock;
69     uint32_t    n;
70 
71     p_BusyBlock = (t_BusyBlock *)XX_Malloc(sizeof(t_BusyBlock));
72     if ( !p_BusyBlock )
73     {
74         REPORT_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
75         return NULL;
76     }
77 
78     p_BusyBlock->base = base;
79     p_BusyBlock->end = base + size;
80 
81     n = strlen(name);
82     if (n >= MM_MAX_NAME_LEN)
83         n = MM_MAX_NAME_LEN - 1;
84     strncpy(p_BusyBlock->name, name, MM_MAX_NAME_LEN-1);
85     p_BusyBlock->name[n] = '\0';
86     p_BusyBlock->p_Next = 0;
87 
88     return p_BusyBlock;
89 }
90 
91 /****************************************************************
92  *  Routine:   CreateNewBlock
93  *
94  *  Description:
95  *      Initializes a new memory block of "size" bytes and started
96  *      from "base" address.
97  *
98  *  Arguments:
99  *      base    - base address of the memory block
100  *      size    - size of the memory block
101  *
102  *  Return value:
103  *      A pointer to new created structure returned on success;
104  *      Otherwise, NULL.
105  ****************************************************************/
106 static t_MemBlock * CreateNewBlock(uint64_t base, uint64_t size)
107 {
108     t_MemBlock *p_MemBlock;
109 
110     p_MemBlock = (t_MemBlock *)XX_Malloc(sizeof(t_MemBlock));
111     if ( !p_MemBlock )
112     {
113         REPORT_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
114         return NULL;
115     }
116 
117     p_MemBlock->base = base;
118     p_MemBlock->end = base+size;
119     p_MemBlock->p_Next = 0;
120 
121     return p_MemBlock;
122 }
123 
124 /****************************************************************
125  *  Routine:   CreateFreeBlock
126  *
127  *  Description:
128  *      Initializes a new free block of of "size" bytes and
129  *      started from "base" address.
130  *
131  *  Arguments:
132  *      base      - base address of the free block
133  *      size      - size of the free block
134  *
135  *  Return value:
136  *      A pointer to new created structure returned on success;
137  *      Otherwise, NULL.
138  ****************************************************************/
139 static t_FreeBlock * CreateFreeBlock(uint64_t base, uint64_t size)
140 {
141     t_FreeBlock *p_FreeBlock;
142 
143     p_FreeBlock = (t_FreeBlock *)XX_Malloc(sizeof(t_FreeBlock));
144     if ( !p_FreeBlock )
145     {
146         REPORT_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
147         return NULL;
148     }
149 
150     p_FreeBlock->base = base;
151     p_FreeBlock->end = base + size;
152     p_FreeBlock->p_Next = 0;
153 
154     return p_FreeBlock;
155 }
156 
157 /****************************************************************
158  *  Routine:    AddFree
159  *
160  *  Description:
161  *      Adds a new free block to the free lists. It updates each
162  *      free list to include a new free block.
163  *      Note, that all free block in each free list are ordered
164  *      by their base address.
165  *
166  *  Arguments:
167  *      p_MM  - pointer to the MM object
168  *      base  - base address of a given free block
169  *      end   - end address of a given free block
170  *
171  *  Return value:
172  *
173  *
174  ****************************************************************/
175 static t_Error AddFree(t_MM *p_MM, uint64_t base, uint64_t end)
176 {
177     t_FreeBlock *p_PrevB, *p_CurrB, *p_NewB;
178     uint64_t    alignment;
179     uint64_t    alignBase;
180     int         i;
181 
182     /* Updates free lists to include  a just released block */
183     for (i=0; i <= MM_MAX_ALIGNMENT; i++)
184     {
185         p_PrevB = p_NewB = 0;
186         p_CurrB = p_MM->freeBlocks[i];
187 
188         alignment = (uint64_t)(0x1 << i);
189         alignBase = MAKE_ALIGNED(base, alignment);
190 
191         /* Goes to the next free list if there is no block to free */
192         if (alignBase >= end)
193             continue;
194 
195         /* Looks for a free block that should be updated */
196         while ( p_CurrB )
197         {
198             if ( alignBase <= p_CurrB->end )
199             {
200                 if ( end > p_CurrB->end )
201                 {
202                     t_FreeBlock *p_NextB;
203                     while ( p_CurrB->p_Next && end > p_CurrB->p_Next->end )
204                     {
205                         p_NextB = p_CurrB->p_Next;
206                         p_CurrB->p_Next = p_CurrB->p_Next->p_Next;
207                         XX_Free(p_NextB);
208                     }
209 
210                     p_NextB = p_CurrB->p_Next;
211                     if ( !p_NextB || (p_NextB && end < p_NextB->base) )
212                     {
213                         p_CurrB->end = end;
214                     }
215                     else
216                     {
217                         p_CurrB->end = p_NextB->end;
218                         p_CurrB->p_Next = p_NextB->p_Next;
219                         XX_Free(p_NextB);
220                     }
221                 }
222                 else if ( (end < p_CurrB->base) && ((end-alignBase) >= alignment) )
223                 {
224                     if ((p_NewB = CreateFreeBlock(alignBase, end-alignBase)) == NULL)
225                         RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
226 
227                     p_NewB->p_Next = p_CurrB;
228                     if (p_PrevB)
229                         p_PrevB->p_Next = p_NewB;
230                     else
231                         p_MM->freeBlocks[i] = p_NewB;
232                     break;
233                 }
234 
235                 if ((alignBase < p_CurrB->base) && (end >= p_CurrB->base))
236                 {
237                     p_CurrB->base = alignBase;
238                 }
239 
240                 /* if size of the free block is less then alignment
241                  * deletes that free block from the free list. */
242                 if ( (p_CurrB->end - p_CurrB->base) < alignment)
243                 {
244                     if ( p_PrevB )
245                         p_PrevB->p_Next = p_CurrB->p_Next;
246                     else
247                         p_MM->freeBlocks[i] = p_CurrB->p_Next;
248                     XX_Free(p_CurrB);
249                     p_CurrB = NULL;
250                 }
251                 break;
252             }
253             else
254             {
255                 p_PrevB = p_CurrB;
256                 p_CurrB = p_CurrB->p_Next;
257             }
258         }
259 
260         /* If no free block found to be updated, insert a new free block
261          * to the end of the free list.
262          */
263         if ( !p_CurrB && ((((uint64_t)(end-base)) & ((uint64_t)(alignment-1))) == 0) )
264         {
265             if ((p_NewB = CreateFreeBlock(alignBase, end-base)) == NULL)
266                 RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
267 
268             if (p_PrevB)
269                 p_PrevB->p_Next = p_NewB;
270             else
271                 p_MM->freeBlocks[i] = p_NewB;
272         }
273 
274         /* Update boundaries of the new free block */
275         if ((alignment == 1) && !p_NewB)
276         {
277             if ( p_CurrB && base > p_CurrB->base )
278                 base = p_CurrB->base;
279             if ( p_CurrB && end < p_CurrB->end )
280                 end = p_CurrB->end;
281         }
282     }
283 
284     return (E_OK);
285 }
286 
287 /****************************************************************
288  *  Routine:      CutFree
289  *
290  *  Description:
291  *      Cuts a free block from holdBase to holdEnd from the free lists.
292  *      That is, it updates all free lists of the MM object do
293  *      not include a block of memory from holdBase to holdEnd.
294  *      For each free lists it seek for a free block that holds
295  *      either holdBase or holdEnd. If such block is found it updates it.
296  *
297  *  Arguments:
298  *      p_MM            - pointer to the MM object
299  *      holdBase        - base address of the allocated block
300  *      holdEnd         - end address of the allocated block
301  *
302  *  Return value:
303  *      E_OK is returned on success,
304  *      otherwise returns an error code.
305  *
306  ****************************************************************/
307 static t_Error CutFree(t_MM *p_MM, uint64_t holdBase, uint64_t holdEnd)
308 {
309     t_FreeBlock *p_PrevB, *p_CurrB, *p_NewB;
310     uint64_t    alignBase, base, end;
311     uint64_t    alignment;
312     int         i;
313 
314     for (i=0; i <= MM_MAX_ALIGNMENT; i++)
315     {
316         p_PrevB = p_NewB = 0;
317         p_CurrB = p_MM->freeBlocks[i];
318 
319         alignment = (uint64_t)(0x1 << i);
320         alignBase = MAKE_ALIGNED(holdEnd, alignment);
321 
322         while ( p_CurrB )
323         {
324             base = p_CurrB->base;
325             end = p_CurrB->end;
326 
327             if ( (holdBase <= base) && (holdEnd <= end) && (holdEnd > base) )
328             {
329                 if ( alignBase >= end ||
330                      (alignBase < end && ((end-alignBase) < alignment)) )
331                 {
332                     if (p_PrevB)
333                         p_PrevB->p_Next = p_CurrB->p_Next;
334                     else
335                         p_MM->freeBlocks[i] = p_CurrB->p_Next;
336                     XX_Free(p_CurrB);
337                 }
338                 else
339                 {
340                     p_CurrB->base = alignBase;
341                 }
342                 break;
343             }
344             else if ( (holdBase > base) && (holdEnd <= end) )
345             {
346                 if ( (holdBase-base) >= alignment )
347                 {
348                     if ( (alignBase < end) && ((end-alignBase) >= alignment) )
349                     {
350                         if ((p_NewB = CreateFreeBlock(alignBase, end-alignBase)) == NULL)
351                             RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
352                         p_NewB->p_Next = p_CurrB->p_Next;
353                         p_CurrB->p_Next = p_NewB;
354                     }
355                     p_CurrB->end = holdBase;
356                 }
357                 else if ( (alignBase < end) && ((end-alignBase) >= alignment) )
358                 {
359                     p_CurrB->base = alignBase;
360                 }
361                 else
362                 {
363                     if (p_PrevB)
364                         p_PrevB->p_Next = p_CurrB->p_Next;
365                     else
366                         p_MM->freeBlocks[i] = p_CurrB->p_Next;
367                     XX_Free(p_CurrB);
368                 }
369                 break;
370             }
371             else
372             {
373                 p_PrevB = p_CurrB;
374                 p_CurrB = p_CurrB->p_Next;
375             }
376         }
377     }
378 
379     return (E_OK);
380 }
381 
382 /****************************************************************
383  *  Routine:     AddBusy
384  *
385  *  Description:
386  *      Adds a new busy block to the list of busy blocks. Note,
387  *      that all busy blocks are ordered by their base address in
388  *      the busy list.
389  *
390  *  Arguments:
391  *      MM              - handler to the MM object
392  *      p_NewBusyB      - pointer to the a busy block
393  *
394  *  Return value:
395  *      None.
396  *
397  ****************************************************************/
398 static void AddBusy(t_MM *p_MM, t_BusyBlock *p_NewBusyB)
399 {
400     t_BusyBlock *p_CurrBusyB, *p_PrevBusyB;
401 
402     /* finds a place of a new busy block in the list of busy blocks */
403     p_PrevBusyB = 0;
404     p_CurrBusyB = p_MM->busyBlocks;
405 
406     while ( p_CurrBusyB && p_NewBusyB->base > p_CurrBusyB->base )
407     {
408         p_PrevBusyB = p_CurrBusyB;
409         p_CurrBusyB = p_CurrBusyB->p_Next;
410     }
411 
412     /* insert the new busy block into the list of busy blocks */
413     if ( p_CurrBusyB )
414         p_NewBusyB->p_Next = p_CurrBusyB;
415     if ( p_PrevBusyB )
416         p_PrevBusyB->p_Next = p_NewBusyB;
417     else
418         p_MM->busyBlocks = p_NewBusyB;
419 }
420 
421 /****************************************************************
422  *  Routine:    CutBusy
423  *
424  *  Description:
425  *      Cuts a block from base to end from the list of busy blocks.
426  *      This is done by updating the list of busy blocks do not
427  *      include a given block, that block is going to be free. If a
428  *      given block is a part of some other busy block, so that
429  *      busy block is updated. If there are number of busy blocks
430  *      included in the given block, so all that blocks are removed
431  *      from the busy list and the end blocks are updated.
432  *      If the given block devides some block into two parts, a new
433  *      busy block is added to the busy list.
434  *
435  *  Arguments:
436  *      p_MM  - pointer to the MM object
437  *      base  - base address of a given busy block
438  *      end   - end address of a given busy block
439  *
440  *  Return value:
441  *      E_OK on success, E_NOMEMORY otherwise.
442  *
443  ****************************************************************/
444 static t_Error CutBusy(t_MM *p_MM, uint64_t base, uint64_t end)
445 {
446     t_BusyBlock  *p_CurrB, *p_PrevB, *p_NewB;
447 
448     p_CurrB = p_MM->busyBlocks;
449     p_PrevB = p_NewB = 0;
450 
451     while ( p_CurrB )
452     {
453         if ( base < p_CurrB->end )
454         {
455             if ( end > p_CurrB->end )
456             {
457                 t_BusyBlock *p_NextB;
458                 while ( p_CurrB->p_Next && end >= p_CurrB->p_Next->end )
459                 {
460                     p_NextB = p_CurrB->p_Next;
461                     p_CurrB->p_Next = p_CurrB->p_Next->p_Next;
462                     XX_Free(p_NextB);
463                 }
464 
465                 p_NextB = p_CurrB->p_Next;
466                 if ( p_NextB && end > p_NextB->base )
467                 {
468                     p_NextB->base = end;
469                 }
470             }
471 
472             if ( base <= p_CurrB->base )
473             {
474                 if ( end < p_CurrB->end && end > p_CurrB->base )
475                 {
476                     p_CurrB->base = end;
477                 }
478                 else if ( end >= p_CurrB->end )
479                 {
480                     if ( p_PrevB )
481                         p_PrevB->p_Next = p_CurrB->p_Next;
482                     else
483                         p_MM->busyBlocks = p_CurrB->p_Next;
484                     XX_Free(p_CurrB);
485                 }
486             }
487             else
488             {
489                 if ( end < p_CurrB->end && end > p_CurrB->base )
490                 {
491                     if ((p_NewB = CreateBusyBlock(end,
492                                                   p_CurrB->end-end,
493                                                   p_CurrB->name)) == NULL)
494                         RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
495                     p_NewB->p_Next = p_CurrB->p_Next;
496                     p_CurrB->p_Next = p_NewB;
497                 }
498                 p_CurrB->end = base;
499             }
500             break;
501         }
502         else
503         {
504             p_PrevB = p_CurrB;
505             p_CurrB = p_CurrB->p_Next;
506         }
507     }
508 
509     return (E_OK);
510 }
511 
512 /****************************************************************
513  *  Routine:     MmGetGreaterAlignment
514  *
515  *  Description:
516  *      Allocates a block of memory according to the given size
517  *      and the alignment. That routine is called from the MM_Get
518  *      routine if the required alignment is greater then MM_MAX_ALIGNMENT.
519  *      In that case, it goes over free blocks of 64 byte align list
520  *      and checks if it has the required size of bytes of the required
521  *      alignment. If no blocks found returns ILLEGAL_BASE.
522  *      After the block is found and data is allocated, it calls
523  *      the internal CutFree routine to update all free lists
524  *      do not include a just allocated block. Of course, each
525  *      free list contains a free blocks with the same alignment.
526  *      It is also creates a busy block that holds
527  *      information about an allocated block.
528  *
529  *  Arguments:
530  *      MM              - handle to the MM object
531  *      size            - size of the MM
532  *      alignment       - index as a power of two defines
533  *                        a required alignment that is greater then 64.
534  *      name            - the name that specifies an allocated block.
535  *
536  *  Return value:
537  *      base address of an allocated block.
538  *      ILLEGAL_BASE if can't allocate a block
539  *
540  ****************************************************************/
541 static uint64_t MmGetGreaterAlignment(t_MM *p_MM, uint64_t size, uint64_t alignment, char* name)
542 {
543     t_FreeBlock *p_FreeB;
544     t_BusyBlock *p_NewBusyB;
545     uint64_t    holdBase, holdEnd, alignBase = 0;
546 
547     /* goes over free blocks of the 64 byte alignment list
548        and look for a block of the suitable size and
549        base address according to the alignment. */
550     p_FreeB = p_MM->freeBlocks[MM_MAX_ALIGNMENT];
551 
552     while ( p_FreeB )
553     {
554         alignBase = MAKE_ALIGNED(p_FreeB->base, alignment);
555 
556         /* the block is found if the aligned base inside the block
557          * and has the anough size. */
558         if ( alignBase >= p_FreeB->base &&
559              alignBase < p_FreeB->end &&
560              size <= (p_FreeB->end - alignBase) )
561             break;
562         else
563             p_FreeB = p_FreeB->p_Next;
564     }
565 
566     /* If such block isn't found */
567     if ( !p_FreeB )
568         return (uint64_t)(ILLEGAL_BASE);
569 
570     holdBase = alignBase;
571     holdEnd = alignBase + size;
572 
573     /* init a new busy block */
574     if ((p_NewBusyB = CreateBusyBlock(holdBase, size, name)) == NULL)
575         return (uint64_t)(ILLEGAL_BASE);
576 
577     /* calls Update routine to update a lists of free blocks */
578     if ( CutFree ( p_MM, holdBase, holdEnd ) != E_OK )
579     {
580         XX_Free(p_NewBusyB);
581         return (uint64_t)(ILLEGAL_BASE);
582     }
583 
584     /* insert the new busy block into the list of busy blocks */
585     AddBusy ( p_MM, p_NewBusyB );
586 
587     return (holdBase);
588 }
589 
590 
591 /**********************************************************************
592  *                     MM API routines set                            *
593  **********************************************************************/
594 
595 /*****************************************************************************/
596 t_Error MM_Init(t_Handle *h_MM, uint64_t base, uint64_t size)
597 {
598     t_MM        *p_MM;
599     uint64_t    newBase, newSize;
600     int         i;
601 
602     if (!size)
603     {
604         RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Size (should be positive)"));
605     }
606 
607     /* Initializes a new MM object */
608     p_MM = (t_MM *)XX_Malloc(sizeof(t_MM));
609     if (!p_MM)
610     {
611         RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
612     }
613 
614     p_MM->h_Spinlock = XX_InitSpinlock();
615     if (!p_MM->h_Spinlock)
616     {
617         XX_Free(p_MM);
618         RETURN_ERROR(MAJOR, E_NO_MEMORY, ("MM spinlock!"));
619     }
620 
621     /* Initializes counter of free memory to total size */
622     p_MM->freeMemSize = size;
623 
624     /* A busy list is empty */
625     p_MM->busyBlocks = 0;
626 
627     /* Initializes a new memory block */
628     if ((p_MM->memBlocks = CreateNewBlock(base, size)) == NULL)
629     {
630         MM_Free(p_MM);
631         RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
632     }
633 
634     /* Initializes a new free block for each free list*/
635     for (i=0; i <= MM_MAX_ALIGNMENT; i++)
636     {
637         newBase = MAKE_ALIGNED( base, (0x1 << i) );
638         newSize = size - (newBase - base);
639 
640         if ((p_MM->freeBlocks[i] = CreateFreeBlock(newBase, newSize)) == NULL)
641         {
642             MM_Free(p_MM);
643             RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
644         }
645     }
646 
647     *h_MM = p_MM;
648 
649     return (E_OK);
650 }
651 
652 /*****************************************************************************/
653 void MM_Free(t_Handle h_MM)
654 {
655     t_MM        *p_MM = (t_MM *)h_MM;
656     t_MemBlock  *p_MemBlock;
657     t_BusyBlock *p_BusyBlock;
658     t_FreeBlock *p_FreeBlock;
659     void        *p_Block;
660     int         i;
661 
662     ASSERT_COND(p_MM);
663 
664     /* release memory allocated for busy blocks */
665     p_BusyBlock = p_MM->busyBlocks;
666     while ( p_BusyBlock )
667     {
668         p_Block = p_BusyBlock;
669         p_BusyBlock = p_BusyBlock->p_Next;
670         XX_Free(p_Block);
671     }
672 
673     /* release memory allocated for free blocks */
674     for (i=0; i <= MM_MAX_ALIGNMENT; i++)
675     {
676         p_FreeBlock = p_MM->freeBlocks[i];
677         while ( p_FreeBlock )
678         {
679             p_Block = p_FreeBlock;
680             p_FreeBlock = p_FreeBlock->p_Next;
681             XX_Free(p_Block);
682         }
683     }
684 
685     /* release memory allocated for memory blocks */
686     p_MemBlock = p_MM->memBlocks;
687     while ( p_MemBlock )
688     {
689         p_Block = p_MemBlock;
690         p_MemBlock = p_MemBlock->p_Next;
691         XX_Free(p_Block);
692     }
693 
694     if (p_MM->h_Spinlock)
695         XX_FreeSpinlock(p_MM->h_Spinlock);
696 
697     /* release memory allocated for MM object itself */
698     XX_Free(p_MM);
699 }
700 
701 /*****************************************************************************/
702 uint64_t MM_Get(t_Handle h_MM, uint64_t size, uint64_t alignment, char* name)
703 {
704     t_MM        *p_MM = (t_MM *)h_MM;
705     t_FreeBlock *p_FreeB;
706     t_BusyBlock *p_NewBusyB;
707     uint64_t    holdBase, holdEnd, j, i = 0;
708     uint32_t    intFlags;
709 
710     SANITY_CHECK_RETURN_VALUE(p_MM, E_INVALID_HANDLE, (uint64_t)ILLEGAL_BASE);
711 
712     /* checks that alignment value is greater then zero */
713     if (alignment == 0)
714     {
715         alignment = 1;
716     }
717 
718     j = alignment;
719 
720     /* checks if alignment is a power of two, if it correct and if the
721        required size is multiple of the given alignment. */
722     while ((j & 0x1) == 0)
723     {
724         i++;
725         j = j >> 1;
726     }
727 
728     /* if the given alignment isn't power of two, returns an error */
729     if (j != 1)
730     {
731         REPORT_ERROR(MAJOR, E_INVALID_VALUE, ("alignment (should be power of 2)"));
732         return (uint64_t)ILLEGAL_BASE;
733     }
734 
735     if (i > MM_MAX_ALIGNMENT)
736     {
737         return (MmGetGreaterAlignment(p_MM, size, alignment, name));
738     }
739 
740     intFlags = XX_LockIntrSpinlock(p_MM->h_Spinlock);
741     /* look for a block of the size greater or equal to the required size. */
742     p_FreeB = p_MM->freeBlocks[i];
743     while ( p_FreeB && (p_FreeB->end - p_FreeB->base) < size )
744         p_FreeB = p_FreeB->p_Next;
745 
746     /* If such block is found */
747     if ( !p_FreeB )
748     {
749         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
750         return (uint64_t)(ILLEGAL_BASE);
751     }
752 
753     holdBase = p_FreeB->base;
754     holdEnd = holdBase + size;
755 
756     /* init a new busy block */
757     if ((p_NewBusyB = CreateBusyBlock(holdBase, size, name)) == NULL)
758     {
759         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
760         return (uint64_t)(ILLEGAL_BASE);
761     }
762 
763     /* calls Update routine to update a lists of free blocks */
764     if ( CutFree ( p_MM, holdBase, holdEnd ) != E_OK )
765     {
766         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
767         XX_Free(p_NewBusyB);
768         return (uint64_t)(ILLEGAL_BASE);
769     }
770 
771     /* Decreasing the allocated memory size from free memory size */
772     p_MM->freeMemSize -= size;
773 
774     /* insert the new busy block into the list of busy blocks */
775     AddBusy ( p_MM, p_NewBusyB );
776     XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
777 
778     return (holdBase);
779 }
780 
781 /*****************************************************************************/
782 uint64_t MM_GetForce(t_Handle h_MM, uint64_t base, uint64_t size, char* name)
783 {
784     t_MM        *p_MM = (t_MM *)h_MM;
785     t_FreeBlock *p_FreeB;
786     t_BusyBlock *p_NewBusyB;
787     uint32_t    intFlags;
788     bool        blockIsFree = FALSE;
789 
790     ASSERT_COND(p_MM);
791 
792     intFlags = XX_LockIntrSpinlock(p_MM->h_Spinlock);
793     p_FreeB = p_MM->freeBlocks[0]; /* The biggest free blocks are in the
794                                       free list with alignment 1 */
795 
796     while ( p_FreeB )
797     {
798         if ( base >= p_FreeB->base && (base+size) <= p_FreeB->end )
799         {
800             blockIsFree = TRUE;
801             break;
802         }
803         else
804             p_FreeB = p_FreeB->p_Next;
805     }
806 
807     if ( !blockIsFree )
808     {
809         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
810         return (uint64_t)(ILLEGAL_BASE);
811     }
812 
813     /* init a new busy block */
814     if ((p_NewBusyB = CreateBusyBlock(base, size, name)) == NULL)
815     {
816         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
817         return (uint64_t)(ILLEGAL_BASE);
818     }
819 
820     /* calls Update routine to update a lists of free blocks */
821     if ( CutFree ( p_MM, base, base+size ) != E_OK )
822     {
823         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
824         XX_Free(p_NewBusyB);
825         return (uint64_t)(ILLEGAL_BASE);
826     }
827 
828     /* Decreasing the allocated memory size from free memory size */
829     p_MM->freeMemSize -= size;
830 
831     /* insert the new busy block into the list of busy blocks */
832     AddBusy ( p_MM, p_NewBusyB );
833     XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
834 
835     return (base);
836 }
837 
838 /*****************************************************************************/
839 uint64_t MM_GetForceMin(t_Handle h_MM, uint64_t size, uint64_t alignment, uint64_t min, char* name)
840 {
841     t_MM        *p_MM = (t_MM *)h_MM;
842     t_FreeBlock *p_FreeB;
843     t_BusyBlock *p_NewBusyB;
844     uint64_t    holdBase, holdEnd, j = alignment, i=0;
845     uint32_t    intFlags;
846 
847     ASSERT_COND(p_MM);
848 
849     /* checks if alignment is a power of two, if it correct and if the
850        required size is multiple of the given alignment. */
851     while ((j & 0x1) == 0)
852     {
853         i++;
854         j = j >> 1;
855     }
856 
857     if ( (j != 1) || (i > MM_MAX_ALIGNMENT) )
858     {
859         return (uint64_t)(ILLEGAL_BASE);
860     }
861 
862     intFlags = XX_LockIntrSpinlock(p_MM->h_Spinlock);
863     p_FreeB = p_MM->freeBlocks[i];
864 
865     /* look for the first block that contains the minimum
866        base address. If the whole required size may be fit
867        into it, use that block, otherwise look for the next
868        block of size greater or equal to the required size. */
869     while ( p_FreeB && (min >= p_FreeB->end))
870             p_FreeB = p_FreeB->p_Next;
871 
872     /* If such block is found */
873     if ( !p_FreeB )
874     {
875         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
876         return (uint64_t)(ILLEGAL_BASE);
877     }
878 
879     /* if this block is large enough, use this block */
880     holdBase = ( min <= p_FreeB->base ) ? p_FreeB->base : min;
881     if ((holdBase + size) <= p_FreeB->end )
882     {
883         holdEnd = holdBase + size;
884     }
885     else
886     {
887         p_FreeB = p_FreeB->p_Next;
888         while ( p_FreeB && ((p_FreeB->end - p_FreeB->base) < size) )
889             p_FreeB = p_FreeB->p_Next;
890 
891         /* If such block is found */
892         if ( !p_FreeB )
893         {
894             XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
895             return (uint64_t)(ILLEGAL_BASE);
896         }
897 
898         holdBase = p_FreeB->base;
899         holdEnd = holdBase + size;
900     }
901 
902     /* init a new busy block */
903     if ((p_NewBusyB = CreateBusyBlock(holdBase, size, name)) == NULL)
904     {
905         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
906         return (uint64_t)(ILLEGAL_BASE);
907     }
908 
909     /* calls Update routine to update a lists of free blocks */
910     if ( CutFree( p_MM, holdBase, holdEnd ) != E_OK )
911     {
912         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
913         XX_Free(p_NewBusyB);
914         return (uint64_t)(ILLEGAL_BASE);
915     }
916 
917     /* Decreasing the allocated memory size from free memory size */
918     p_MM->freeMemSize -= size;
919 
920     /* insert the new busy block into the list of busy blocks */
921     AddBusy( p_MM, p_NewBusyB );
922     XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
923 
924     return (holdBase);
925 }
926 
927 /*****************************************************************************/
928 uint64_t MM_Put(t_Handle h_MM, uint64_t base)
929 {
930     t_MM        *p_MM = (t_MM *)h_MM;
931     t_BusyBlock *p_BusyB, *p_PrevBusyB;
932     uint64_t    size;
933     uint32_t    intFlags;
934 
935     ASSERT_COND(p_MM);
936 
937     /* Look for a busy block that have the given base value.
938      * That block will be returned back to the memory.
939      */
940     p_PrevBusyB = 0;
941 
942     intFlags = XX_LockIntrSpinlock(p_MM->h_Spinlock);
943     p_BusyB = p_MM->busyBlocks;
944     while ( p_BusyB && base != p_BusyB->base )
945     {
946         p_PrevBusyB = p_BusyB;
947         p_BusyB = p_BusyB->p_Next;
948     }
949 
950     if ( !p_BusyB )
951     {
952         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
953         return (uint64_t)(0);
954     }
955 
956     if ( AddFree( p_MM, p_BusyB->base, p_BusyB->end ) != E_OK )
957     {
958         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
959         return (uint64_t)(0);
960     }
961 
962     /* removes a busy block form the list of busy blocks */
963     if ( p_PrevBusyB )
964         p_PrevBusyB->p_Next = p_BusyB->p_Next;
965     else
966         p_MM->busyBlocks = p_BusyB->p_Next;
967 
968     size = p_BusyB->end - p_BusyB->base;
969 
970     /* Adding the deallocated memory size to free memory size */
971     p_MM->freeMemSize += size;
972 
973     XX_Free(p_BusyB);
974     XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
975 
976     return (size);
977 }
978 
979 /*****************************************************************************/
980 uint64_t MM_PutForce(t_Handle h_MM, uint64_t base, uint64_t size)
981 {
982     t_MM        *p_MM = (t_MM *)h_MM;
983     uint64_t    end = base + size;
984     uint32_t    intFlags;
985 
986     ASSERT_COND(p_MM);
987 
988     intFlags = XX_LockIntrSpinlock(p_MM->h_Spinlock);
989 
990     if ( CutBusy( p_MM, base, end ) != E_OK )
991     {
992         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
993         return (uint64_t)(0);
994     }
995 
996     if ( AddFree ( p_MM, base, end ) != E_OK )
997     {
998         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
999         return (uint64_t)(0);
1000     }
1001 
1002     /* Adding the deallocated memory size to free memory size */
1003     p_MM->freeMemSize += size;
1004 
1005     XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
1006 
1007     return (size);
1008 }
1009 
1010 /*****************************************************************************/
1011 t_Error MM_Add(t_Handle h_MM, uint64_t base, uint64_t size)
1012 {
1013     t_MM        *p_MM = (t_MM *)h_MM;
1014     t_MemBlock  *p_MemB, *p_NewMemB;
1015     t_Error     errCode;
1016     uint32_t    intFlags;
1017 
1018     ASSERT_COND(p_MM);
1019 
1020     /* find a last block in the list of memory blocks to insert a new
1021      * memory block
1022      */
1023     intFlags = XX_LockIntrSpinlock(p_MM->h_Spinlock);
1024 
1025     p_MemB = p_MM->memBlocks;
1026     while ( p_MemB->p_Next )
1027     {
1028         if ( base >= p_MemB->base && base < p_MemB->end )
1029         {
1030         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
1031             RETURN_ERROR(MAJOR, E_ALREADY_EXISTS, NO_MSG);
1032         }
1033         p_MemB = p_MemB->p_Next;
1034     }
1035     /* check for a last memory block */
1036     if ( base >= p_MemB->base && base < p_MemB->end )
1037     {
1038         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
1039         RETURN_ERROR(MAJOR, E_ALREADY_EXISTS, NO_MSG);
1040     }
1041 
1042     /* create a new memory block */
1043     if ((p_NewMemB = CreateNewBlock(base, size)) == NULL)
1044     {
1045         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
1046         RETURN_ERROR(MAJOR, E_NO_MEMORY, NO_MSG);
1047     }
1048 
1049     /* append a new memory block to the end of the list of memory blocks */
1050     p_MemB->p_Next = p_NewMemB;
1051 
1052     /* add a new free block to the free lists */
1053     errCode = AddFree(p_MM, base, base+size);
1054     if (errCode)
1055     {
1056         XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
1057         p_MemB->p_Next = 0;
1058         XX_Free(p_NewMemB);
1059         return ((t_Error)errCode);
1060     }
1061 
1062     /* Adding the new block size to free memory size */
1063     p_MM->freeMemSize += size;
1064 
1065     XX_UnlockIntrSpinlock(p_MM->h_Spinlock, intFlags);
1066 
1067     return (E_OK);
1068 }
1069 
1070 /*****************************************************************************/
1071 uint64_t MM_GetMemBlock(t_Handle h_MM, int index)
1072 {
1073     t_MM       *p_MM = (t_MM*)h_MM;
1074     t_MemBlock *p_MemBlock;
1075     int         i;
1076 
1077     ASSERT_COND(p_MM);
1078 
1079     p_MemBlock = p_MM->memBlocks;
1080     for (i=0; i < index; i++)
1081         p_MemBlock = p_MemBlock->p_Next;
1082 
1083     if ( p_MemBlock )
1084         return (p_MemBlock->base);
1085     else
1086         return (uint64_t)ILLEGAL_BASE;
1087 }
1088 
1089 /*****************************************************************************/
1090 uint64_t MM_GetBase(t_Handle h_MM)
1091 {
1092     t_MM       *p_MM = (t_MM*)h_MM;
1093     t_MemBlock *p_MemBlock;
1094 
1095     ASSERT_COND(p_MM);
1096 
1097     p_MemBlock = p_MM->memBlocks;
1098     return  p_MemBlock->base;
1099 }
1100 
1101 /*****************************************************************************/
1102 bool MM_InRange(t_Handle h_MM, uint64_t addr)
1103 {
1104     t_MM       *p_MM = (t_MM*)h_MM;
1105     t_MemBlock *p_MemBlock;
1106 
1107     ASSERT_COND(p_MM);
1108 
1109     p_MemBlock = p_MM->memBlocks;
1110 
1111     if ((addr >= p_MemBlock->base) && (addr < p_MemBlock->end))
1112         return TRUE;
1113     else
1114         return FALSE;
1115 }
1116 
1117 /*****************************************************************************/
1118 uint64_t MM_GetFreeMemSize(t_Handle h_MM)
1119 {
1120     t_MM       *p_MM = (t_MM*)h_MM;
1121 
1122     ASSERT_COND(p_MM);
1123 
1124     return p_MM->freeMemSize;
1125 }
1126 
1127 /*****************************************************************************/
1128 void MM_Dump(t_Handle h_MM)
1129 {
1130     t_MM        *p_MM = (t_MM *)h_MM;
1131     t_FreeBlock *p_FreeB;
1132     t_BusyBlock *p_BusyB;
1133     int          i;
1134 
1135     p_BusyB = p_MM->busyBlocks;
1136     XX_Print("List of busy blocks:\n");
1137     while (p_BusyB)
1138     {
1139         XX_Print("\t0x%p: (%s: b=0x%llx, e=0x%llx)\n", p_BusyB, p_BusyB->name, p_BusyB->base, p_BusyB->end );
1140         p_BusyB = p_BusyB->p_Next;
1141     }
1142 
1143     XX_Print("\nLists of free blocks according to alignment:\n");
1144     for (i=0; i <= MM_MAX_ALIGNMENT; i++)
1145     {
1146         XX_Print("%d alignment:\n", (0x1 << i));
1147         p_FreeB = p_MM->freeBlocks[i];
1148         while (p_FreeB)
1149         {
1150             XX_Print("\t0x%p: (b=0x%llx, e=0x%llx)\n", p_FreeB, p_FreeB->base, p_FreeB->end);
1151             p_FreeB = p_FreeB->p_Next;
1152         }
1153         XX_Print("\n");
1154     }
1155 }
1156