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