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 ****************************************************************/
66static 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 ****************************************************************/
106static 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 ****************************************************************/
139static 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 ****************************************************************/
175static 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 ****************************************************************/
307static 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 ****************************************************************/
398static 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 ****************************************************************/
444static 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 ****************************************************************/
541static 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/*****************************************************************************/
596t_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/*****************************************************************************/
653void 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/*****************************************************************************/
702uint64_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/*****************************************************************************/
782uint64_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/*****************************************************************************/
839uint64_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/*****************************************************************************/
928uint64_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/*****************************************************************************/
980uint64_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/*****************************************************************************/
1011t_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/*****************************************************************************/
1071uint64_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/*****************************************************************************/
1090uint64_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/*****************************************************************************/
1102bool 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/*****************************************************************************/
1118uint64_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/*****************************************************************************/
1128void 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