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