1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fnd_BuddyHeap.h
4   Copyright (C)2009 Nintendo Co., Ltd.  All rights reserved.
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law. They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10   $Rev: 23169 $
11  *---------------------------------------------------------------------------
12 
13 
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