1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fnd_BuddyHeap.h
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 46347 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NN_FND_FND_BUDDYHEAP_H_
17 #define NN_FND_FND_BUDDYHEAP_H_
18 
19 #include <nn/types.h>
20 #include <nn/fnd/fnd_HeapBase.h>
21 #include <nn/fnd/fnd_Allocator.h>
22 
23 namespace nn {
24 namespace fnd {
25 
26 template<size_t PageSize, s32 MaxOrder>
27 class BuddyHeapBase : public nn::fnd::HeapBase
28 {
29 protected:
30     template <s32 align, typename T>
DivUp(T x)31     static T DivUp(T x)
32     {
33         return static_cast<T>( (x + (align - 1)) / align );
34     }
35 
36     template<size_t alignment>
RoundDown(uptr addr)37     static uptr RoundDown(uptr addr)
38     {
39         return (addr / alignment) * alignment;
40     }
41 
42     template<size_t alignment>
RoundUp(uptr addr)43     static uptr RoundUp(uptr addr)
44     {
45         return RoundDown<alignment>(addr + alignment - 1);
46     }
47 
48 public:
49     static const size_t ALIGN = 8;
50 
51     /* Please see man pages for details
52 
53     */
GetNumMaxPages(void)54     static size_t GetNumMaxPages(void)
55     {
56         return 1 << (MAX_ORDER - 1);
57     }
58 
59     /* Please see man pages for details
60 
61     */
GetMaxSize(void)62     static size_t GetMaxSize(void)
63     {
64         return GetNumMaxPages() * PAGE_SIZE;
65     }
66 
67     /* Please see man pages for details
68 
69 
70 
71 
72 */
Initialize(uptr addr,size_t numPages)73     void Initialize(uptr addr, size_t numPages)
74     {
75         NN_ASSERT((addr % ALIGN) == 0);
76 
77         m_HeapStart = addr;
78         m_HeapSize  = PAGE_SIZE * numPages;
79 
80         NN_LOG_DEBUG("start %p\n", m_HeapStart);
81         NN_LOG_DEBUG("size  %p\n", m_HeapSize);
82 
83         const size_t numMaxPages    = GetNumMaxPages();
84         NN_LOG_DEBUG("numMaxPages %d\n", numMaxPages);
85         const size_t maxSize        = GetMaxSize();
86         NN_LOG_DEBUG("maxSize %d\n", maxSize);
87         const size_t numMaxOrders   = m_HeapSize / maxSize;
88         NN_LOG_DEBUG("numMaxOrders %d\n", numMaxOrders);
89 
90         NN_ASSERT(numMaxOrders > 0);
91 
92         // Provisional initialization of management area
93         // Allocates one management area for each page.
94         m_pPages = reinterpret_cast<Page*>(m_HeapStart);
95 
96         for (size_t i = 0; i < numPages; i++)
97         {
98             m_pPages[i].pNext  = NULL;
99         }
100 
101         for (size_t i = 0; i < MAX_ORDER; ++i )
102         {
103             m_FreeArea[i].Initialize();
104         }
105         for (size_t i = 0; i < numMaxOrders; i++)
106         {
107             // Registers the start address of each page to the free list.
108             m_FreeArea[MAX_ORDER - 1].PushBack(&(m_pPages[i * numMaxPages]));
109         }
110 
111         // Relocate the management area (normally should be located in the same place)
112         const size_t manageAreaSize = sizeof(Page) * numPages;
113 
114         if(m_pPages != reinterpret_cast<Page*>(AllocateByOrder(GetNearestHigherOrder(DivUp<PAGE_SIZE>(manageAreaSize)))))
115         {
116             NN_LOG_WARN("failed to allocate page managing area.");
117         }
118     }
119 
120     /* Please see man pages for details
121 
122     */
Finalize(void)123     void Finalize(void){}
124 
125     /* Please see man pages for details
126 
127 
128 
129 
130 
131 */
AllocateByOrder(s32 order)132     void* AllocateByOrder(s32 order)
133     {
134         NN_ASSERT(order >= 0);
135 
136         NN_LOG_DEBUG("demands order %d\n", order);
137 
138 
139         Page* pPage = GetFreePage(order);
140         NN_LOG_DEBUG("pPage = %08x\n", pPage);
141 
142         if(pPage)
143         {
144             uptr addr = GetAddressFromPage(*pPage);
145 
146             NN_LOG_DEBUG("returns order %d, address 0x%08X\n", order, addr);
147 
148             return reinterpret_cast<void*>(addr);
149         }
150         else
151         {
152             return NULL;
153         }
154     }
155 
156     /* Please see man pages for details
157 
158 
159 
160 
161 
162 
163 */
Free(void * p,s32 order)164     void Free(void* p, s32 order)
165     {
166         if(!p)
167         {
168             return;
169         }
170 
171         NN_ASSERT((reinterpret_cast<uptr>(p) - m_HeapStart) % PAGE_SIZE == 0);
172 
173         Page* pPage = GetPageFromAddress(reinterpret_cast<uptr>(p));
174 
175         // Is the page index divisible by the order?
176         NN_ASSERT(GetIndexFromPage(*pPage) % (1 << order) == 0);
177 
178         NN_LOG_DEBUG("addr = 0x%08x, order = %d, pPage = 0x%08x\n", p, order, pPage);
179 
180         JointBuddies(pPage, order);
181     }
182 
FreeV(void *)183     virtual void FreeV(void*) { NN_ASSERT(false); }
184 
185     /* Please see man pages for details
186 
187     */
GetStartAddress()188     virtual void* GetStartAddress() const { return reinterpret_cast<void*>(m_HeapStart); }
189 
190     /* Please see man pages for details
191 
192     */
GetTotalSize()193     virtual size_t GetTotalSize() const { return m_HeapSize; }
194 
195     /* Please see man pages for details
196 
197     */
Dump()198     virtual void Dump() const{};
199 
200     /* Please see man pages for details
201 
202     */
HasAddress(const void * addr)203     virtual bool HasAddress(const void* addr) const
204     {
205         return m_HeapStart <= reinterpret_cast<uptr>(addr)
206            && reinterpret_cast<uptr>(addr) < m_HeapStart + m_HeapSize;
207     }
208 
209     /* Please see man pages for details
210 
211 
212 
213 
214 
215 */
GetOrder(size_t size)216     static s32 GetOrder(size_t size)
217     {
218         return GetNearestHigherOrder(DivUp<PAGE_SIZE>(size));
219     }
220 
221     static const size_t PAGE_SIZE = PageSize;
222     static const s32    MAX_ORDER = MaxOrder;
223 
224 private:
225     // Page entry.
226     struct Page
227     {
228         Page* pNext;
229     };
230 
231     // Construct a linked list for each memory block of the same size.
232     class PageList
233     {
234     private:
235         Page* m_pFirstPage;    // Start element of the page list
236         Page* m_pLastPage;     // End element of the page list
237 
238     public:
239         // Initializes page list.
Initialize()240         void Initialize()
241         {
242             m_pFirstPage = NULL;
243             m_pLastPage = NULL;
244         }
245 
246         /*
247 
248 
249 
250 */
PopFront()251         Page* PopFront()
252         {
253             NN_ASSERT(m_pFirstPage);
254 
255             // Gets the first page's contents from the page list.
256             Page* pPage = m_pFirstPage;
257 
258             // Advances the page list by one.
259             m_pFirstPage = pPage->pNext;
260             pPage->pNext = NULL;
261 
262             // If the top and bottom of the page list are the same, you have reached the end of the page list.
263             //
264             // (Page list element is empty)
265             if(pPage == m_pLastPage)
266             {
267                 m_pLastPage = NULL;
268             }
269 
270             return pPage;
271         }
272 
273         /*
274 
275 
276 
277 */
PushBack(Page * pPage)278         void PushBack(Page* pPage)
279         {
280             NN_ASSERT(!pPage->pNext);
281 
282             // Is page list empty?
283             if(!m_pFirstPage)
284             {
285                 // Add as the first element.
286                 m_pFirstPage = pPage;
287             }
288             else
289             {
290                 // Add to the end of the list.
291                 m_pLastPage->pNext = pPage;
292             }
293 
294             m_pLastPage = pPage;
295         }
296 
297         /*
298 
299 
300 
301 
302 
303 */
Remove(Page * pPage)304         bool Remove(Page* pPage)
305         {
306             NN_NULL_TASSERT_(pPage);
307 
308             // Is page list empty?
309             if(m_pFirstPage == NULL)
310             {
311                 return false;
312             }
313 
314             Page* pPrevPage = NULL;
315             Page* page = m_pFirstPage;
316 
317             for(;;)
318             {
319                 if(page == pPage)
320                 {
321                     // If a page to be deleted was found
322                     if(page == m_pFirstPage)
323                     {
324                         // The page to be deleted was at the top of the page list, so advance down the list by one and delete the element.
325                         //
326                         m_pFirstPage = page->pNext;
327                     }
328                     else if(page == m_pLastPage)
329                     {
330                         // Page to delete was at the end of the page list, so set the last element back by one.
331                         //
332                         m_pLastPage = pPrevPage;
333                         m_pLastPage->pNext = NULL;
334                     }
335                     else
336                     {
337                         // Reconnect the linked list for the pages between the beginning and the end.
338                         // Reconnect the linked list.
339                         pPrevPage->pNext = page->pNext;
340                     }
341 
342                     page->pNext = NULL;
343 
344                     return true;
345                 }
346 
347                 if(page->pNext == NULL)
348                 {
349                     return false;
350                 }
351 
352                 pPrevPage = page;
353                 page = page->pNext;
354             }
355 
356             // not reached
357         }
358 
359         /*
360 
361 
362 
363 */
IsEmpty()364         bool IsEmpty() const
365         {
366             if(m_pFirstPage)
367             {
368                 return false;
369             }
370             else
371             {
372                 return true;
373             }
374         }
375     };
376 
377     PageList m_FreeArea[MAX_ORDER];
378 
379     uptr    m_HeapStart;
380     size_t  m_HeapSize;
381 
382     /*
383 
384 
385 
386 
387 
388 
389 
390 */
GetFreePage(s32 order)391     Page* GetFreePage(s32 order)
392     {
393         for(s32 i = order; i < MAX_ORDER; i++)
394         {
395             // Search the page list that corresponds to the "order" (power) of 2
396             // If no empty page was registered to that page, search for the page specified by the power of order+1, which is higher by one.
397             //
398             if(!(m_FreeArea[i].IsEmpty()))
399             {
400                 // Free memory found.
401                 Page* pPage = m_FreeArea[i].PopFront();
402 
403                 NN_NULL_TASSERT_(pPage);
404 
405                 // When allocating memory that is too large for the request, break up the unused portion, and re-register it to the empty page list.
406                 //
407                 DivideBuddies(pPage, order, i);
408 
409                 return pPage;
410             }
411         }
412 
413         NN_LOG_WARN("No free pages order %d\n", order);
414         return NULL;
415     }
416 
417     /*
418 
419 
420 
421 
422 
423 */
DivideBuddies(Page * pPage,s32 demandedOrder,s32 freeOrder)424     void DivideBuddies(Page* pPage, s32 demandedOrder, s32 freeOrder)
425     {
426         for(s32 i = freeOrder; demandedOrder < i; i--)
427         {
428             Page* pDividedPage = &pPage[(1 << (i - 1))];;
429 
430             // Break up into small portions and register to the empty page list.
431             // If a large, 16KB empty page is found for a 4KB request, the remaining 12KB (16KB - 4KB) that is not needed for allocation should be divided into two page lists (4KB and 8KB) and added to the free list.
432             //
433 
434             NN_LOG_DEBUG("divides buddy address 0x%08X and 0x%08X, order%d\n", GetAddressFromPage(*pPage), GetAddressFromPage(*pDividedPage), i);
435             m_FreeArea[i - 1].PushBack(pDividedPage);
436         }
437     }
438 
439     /*
440 
441 
442 
443 
444 
445 
446 */
JointBuddies(Page * pPage,s32 order)447     void JointBuddies(Page* pPage, s32 order)
448     {
449         // Maximum "order" - 1
450         while(order < MAX_ORDER - 1)
451         {
452             // Request pages that become buddies.
453             Page* pBuddyPage = GetBuddy(pPage, order);
454 
455             // Is there a buddy on the free list?
456             if(m_FreeArea[order].Remove(pBuddyPage))
457             {
458                 // If so, delete it (jointed) from the free list and attempt joint at (order + 1).
459 
460                 // If the page was not aligned to (order + 1), then set address to Buddy.
461                 if(!IsAlignedToOrder(pPage, order + 1))
462                 {
463                     pPage = pBuddyPage;
464                 }
465 
466                 NN_LOG_DEBUG("joints buddy address 0x%08X and 0x%08X, order %d", GetAddressFromPage(*pPage), GetAddressFromPage(*GetBuddy(pPage, order)), order);
467 
468 
469                 order++;
470             }
471             else
472             {
473                 // If not, joint failed. Add page to the free list.
474                 break;
475             }
476         }
477 
478         // Buddy was being used or jointed up to max order
479         NN_LOG_DEBUG("adding a free page, address 0x%08X, order %d", GetAddressFromPage(*pPage), order );
480         m_FreeArea[order].PushBack(pPage);
481     }
482 
483     /* Please see man pages for details
484 
485 
486 
487 
488 
489 */
GetAddressFromPage(const Page & page)490     inline uptr GetAddressFromPage(const Page& page) const
491     {
492         return m_HeapStart + GetIndexFromPage(page) * PAGE_SIZE;
493     }
494 
495     /* Please see man pages for details
496 
497 
498 
499 
500 
501 */
GetPageFromAddress(uptr addr)502     inline Page* GetPageFromAddress(uptr addr)
503     {
504         // TODO: Range check
505         return &m_pPages[(addr - m_HeapStart) / PAGE_SIZE];
506     }
507 
508     /*
509 
510 
511 
512 
513 
514 */
GetIndexFromPage(const Page & page)515     inline s32 GetIndexFromPage(const Page& page) const
516     {
517         return &page - m_pPages;
518     }
519 
GetBuddy(Page * pPage,s32 order)520     inline Page* GetBuddy(Page* pPage, s32 order)
521     {
522         // If the page index is divisible by the above order, the next right is Buddy
523         if(IsAlignedToOrder(pPage, order + 1))
524         {
525             return pPage + GetNumPagesByOrder(order);
526         }
527         else // If not, the next left is Buddy
528         {
529             return pPage - GetNumPagesByOrder(order);
530         }
531     }
532 
533     /*
534 
535 
536 
537 
538 
539 
540 */
IsAlignedToOrder(Page * pPage,s32 order)541     inline bool IsAlignedToOrder(Page* pPage, s32 order) const
542     {
543         return GetIndexFromPage(*pPage) % GetNumPagesByOrder(order) == 0;
544     }
545 
546     /*
547 
548 
549 
550 
551 
552 */
GetNumPagesByOrder(s32 order)553     inline int GetNumPagesByOrder(s32 order) const
554     {
555         return (1 << order);
556     }
557 
GetNearestHigherOrder(size_t numPages)558     static s32 GetNearestHigherOrder(size_t numPages)
559     {
560         for(int order = 0; order <= MAX_ORDER; order++)
561         {
562             if( numPages <= (1U << order) ) {
563                 return order;
564             }
565         }
566 
567         NN_LOG_WARN("No nearest higher order: numPages=%d, MAX_ORDER=%d", numPages, MAX_ORDER);
568         return -1;
569     }
570 
GetNearestLowerOrder(size_t numPages)571     static s32 GetNearestLowerOrder(size_t numPages)
572     {
573         for(int order = 0; order <= MAX_ORDER; order++)
574         {
575             if( numPages < (1 << order) ) {
576                 return order - 1;
577             }
578         }
579 
580         NN_LOG_WARN("No nearest lower order: numPages=%d, MAX_ORDER=%d", numPages, MAX_ORDER);
581         return -1;
582     }
583 
584     Page* m_pPages;
585 };
586 
587 template <size_t PageSize, s32 MaxOrder, class LockPolicy>
588 class BuddyHeapTemplate : public BuddyHeapBase<PageSize, MaxOrder>, private LockPolicy::LockObject
589 {
590 private:
591     typedef BuddyHeapBase<PageSize, MaxOrder> Base;
592     typedef typename LockPolicy::LockObject LockObject;
593     typedef typename LockPolicy::ScopedLock ScopedLock;
594 
595 public:
BuddyHeapTemplate()596     BuddyHeapTemplate() {}
597 
598     template <class MemoryBlockT>
BuddyHeapTemplate(const MemoryBlockT & block)599     explicit BuddyHeapTemplate(const MemoryBlockT& block)
600     {
601         Initialize(block.GetAddress(), block.GetSize());
602     }
603 
604     /*
605 
606 
607 
608 
609 */
Initialize(uptr addr,size_t size)610     void Initialize(uptr addr, size_t size)
611     {
612         uptr addrAligned = Base::RoundUp<Base::ALIGN>(addr);
613         size -= addrAligned - addr;
614 
615         Base::Initialize(addrAligned, size / Base::PAGE_SIZE);
616         LockObject::Initialize();
617     }
618 
619     /*
620 
621     */
Finalize()622     void Finalize()
623     {
624         LockObject::Finalize();
625         Base::Finalize();
626     }
627 
628     /* Please see man pages for details
629 
630 
631 
632 
633 
634 */
AllocateByOrder(s32 order)635     void* AllocateByOrder(s32 order)
636     {
637         ScopedLock lk(*this);
638         return Base::AllocateByOrder(order);
639     }
640 
641     /* Please see man pages for details
642 
643 
644 
645 
646 */
Free(void * p,s32 order)647     void Free(void* p, s32 order)
648     {
649         ScopedLock lk(*this);
650         Base::Free(p, order);
651     }
652 
653     class Allocator;
654 };
655 
656 template <size_t PageSize, s32 MaxOrder, class LockPolicy>
657 class BuddyHeapTemplate<PageSize, MaxOrder, LockPolicy>::Allocator : public nn::fnd::IAllocator
658 {
659 private:
660     typedef BuddyHeapTemplate<PageSize, MaxOrder, LockPolicy> BuddyHeap;
661     static const size_t ALIGN = BuddyHeap::ALIGN;
662 
663 public:
Allocator(BuddyHeap & heap)664     Allocator(BuddyHeap& heap) : m_Heap(0) { Initialize(heap); }
665 
666     /* Please see man pages for details
667 
668      */
Allocator()669     Allocator() : m_Heap(0) {}
670 
671     /* Please see man pages for details
672 
673 
674 
675 */
Initialize(BuddyHeap & heap)676     void Initialize(BuddyHeap& heap)
677     {
678         NN_TASSERT_(!this->m_Heap);
679         this->m_Heap = &heap;
680     }
681 
682     /* Please see man pages for details
683 
684 
685 
686 */
GetHeap()687     BuddyHeap* GetHeap() { return m_Heap; }
688 
689     /* Please see man pages for details
690 
691 
692 
693 */
GetHeap()694     const BuddyHeap* GetHeap() const { return m_Heap; }
695 
696     /* Please see man pages for details
697 
698 
699 
700 
701 
702 
703 */
704     virtual void* Allocate(size_t size, s32 alignment = nn::fnd::HeapBase::DEFAULT_ALIGNMENT)
705     {
706         NN_ASSERT(alignment <= ALIGN);
707         if (alignment > ALIGN)
708         {
709             return NULL;
710         }
711 
712         // Stores excess memory in order to store the order at the start of the allocated memory.
713         //
714         s32 order = m_Heap->GetOrder(size + ALIGN);
715         //NN_TLOG_("size: %d => order: %d\n", size, order);
716         void* p = m_Heap->AllocateByOrder(order);
717         if (!p)
718         {
719             return NULL;
720         }
721         NN_LOG_DEBUG("Alloc size %d, order %d\n", size + ALIGN, order);
722         *reinterpret_cast<s32*>(p) = order;
723         return reinterpret_cast<void*>(reinterpret_cast<uptr>(p) + ALIGN);
724     }
725 
726     /* Please see man pages for details
727 
728 
729 
730 */
Free(void * p)731     virtual void Free(void* p)
732     {
733         p = reinterpret_cast<void*>(reinterpret_cast<uptr>(p) - ALIGN);
734         s32 order = *reinterpret_cast<s32*>(p);
735         m_Heap->Free(p, order);
736     }
737 
738 private:
739     BuddyHeap* m_Heap;     //
740 };
741 
742 }}
743 
744 #endif /* NN_FND_FND_BUDDYHEAP_H_ */
745