1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: osl_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: 15690 $ 11 *--------------------------------------------------------------------------- 12 13 14 */ 15 16 #ifndef NN_NET_OSL_BUDDYHEAP_H_ 17 #define NN_NET_OSL_BUDDYHEAP_H_ 18 19 #include <nn/types.h> 20 #include <nn/os/os_MemoryBlock.h> 21 #include <nn/fnd/fnd_HeapBase.h> 22 #include <nn/fnd/fnd_Allocator.h> 23 24 namespace nn { 25 namespace net { 26 namespace osl { 27 28 template<size_t PageSize, s32 MaxOrder> 29 class BuddyHeapBase : public nn::fnd::HeapBase 30 { 31 protected: 32 template <s32 align, typename T> DivUp(T x)33 static T DivUp(T x) 34 { 35 return static_cast<T>( (x + (align - 1)) / align ); 36 } 37 template<size_t alignment> RoundDown(uptr addr)38 static uptr RoundDown(uptr addr) 39 { 40 return (addr / alignment) * alignment; 41 } 42 43 template<size_t alignment> RoundUp(uptr addr)44 static uptr RoundUp(uptr addr) 45 { 46 return RoundDown<alignment>(addr + alignment - 1); 47 } 48 49 public: 50 static const size_t ALIGN = 8; 51 GetNumMaxPages(void)52 static size_t GetNumMaxPages(void) 53 { 54 return 1 << (MAX_ORDER - 1); 55 } 56 GetMaxSize(void)57 static size_t GetMaxSize(void) 58 { 59 return GetNumMaxPages() * PAGE_SIZE; 60 } 61 Initialize(uptr addr,size_t numPages)62 void Initialize(uptr addr, size_t numPages) 63 { 64 NN_POINTER_ASSERT(addr); 65 NN_ASSERT((addr % ALIGN) == 0); 66 67 m_HeapStart = addr; 68 m_HeapSize = PAGE_SIZE * numPages; 69 70 NN_LOG_DEBUG("start %p\n", m_HeapStart); 71 NN_LOG_DEBUG("size %p\n", m_HeapSize); 72 73 const size_t numMaxPages = GetNumMaxPages(); 74 NN_LOG_DEBUG("numMaxPages %d\n", numMaxPages); 75 const size_t maxSize = GetMaxSize(); 76 NN_LOG_DEBUG("maxSize %d\n", maxSize); 77 const size_t numMaxOrders = m_HeapSize / maxSize; 78 NN_LOG_DEBUG("numMaxOrders %d\n", numMaxOrders); 79 80 NN_ASSERT(numMaxOrders > 0); 81 82 // Provisional initialization of management area 83 m_pPages = reinterpret_cast<Page*>(m_HeapStart); 84 85 for(int i = 0; i < numPages; i++) 86 { 87 m_pPages[i].pNext = NULL; 88 } 89 90 for( int i = 0; i < MAX_ORDER; ++i ) 91 { 92 m_FreeArea[i].Initialize(); 93 } 94 for(int i = 0; i < numMaxOrders; i++) 95 { 96 m_FreeArea[MAX_ORDER - 1].PushBack(&(m_pPages[i * numMaxPages])); 97 } 98 99 // Relocate the management area (normally should be located in the same place) 100 const size_t manageAreaSize = sizeof(Page) * numPages; 101 102 if(m_pPages != reinterpret_cast<Page*>(AllocateByOrder(GetNearestHigherOrder(DivUp<PAGE_SIZE>(manageAreaSize))))) 103 { 104 NN_LOG_WARN("failed to allocate page managing area."); 105 } 106 } Finalize(void)107 void Finalize(void){} 108 AllocateByOrder(s32 order)109 void* AllocateByOrder(s32 order) 110 { 111 NN_ASSERT(order >= 0); 112 113 NN_LOG_DEBUG("demands order %d\n", order); 114 115 116 Page* pPage = GetFreePage(order); 117 NN_LOG_DEBUG("pPage = %08x\n", pPage); 118 119 if(pPage) 120 { 121 uptr addr = GetAddressFromPage(*pPage); 122 123 NN_LOG_DEBUG("returns order %d, address 0x%08X\n", order, addr); 124 125 return reinterpret_cast<void*>(addr); 126 } 127 else 128 { 129 return NULL; 130 } 131 } 132 133 // Cannot conform to HeapBase because Free argument is different Free(void * p,s32 order)134 void Free(void* p, s32 order) 135 { 136 if(!p) 137 { 138 return; 139 } 140 141 NN_ASSERT((reinterpret_cast<uptr>(p) - m_HeapStart) % PAGE_SIZE == 0); 142 143 Page* pPage = GetPageFromAddress(reinterpret_cast<uptr>(p)); 144 145 // Is the page index divisible by the order? 146 NN_ASSERT(GetIndexFromPage(*pPage) % (1 << order) == 0); 147 148 NN_LOG_DEBUG("addr = 0x%08x, order = %d, pPage = 0x%08x\n", p, order, pPage); 149 150 JointBuddies(pPage, order); 151 } 152 FreeV(void *)153 virtual void FreeV(void*) { NN_ASSERT(false); } 154 155 /* Please see man pages for details 156 157 */ GetStartAddress()158 virtual void* GetStartAddress() const { return reinterpret_cast<void*>(m_HeapStart); } 159 160 /* Please see man pages for details 161 162 */ GetTotalSize()163 virtual size_t GetTotalSize() const { return m_HeapSize; } 164 165 /* Please see man pages for details 166 167 */ Dump()168 virtual void Dump() const{}; HasAddress(const void * addr)169 virtual bool HasAddress(const void* addr) const 170 { 171 return m_HeapStart <= reinterpret_cast<uptr>(addr) 172 && reinterpret_cast<uptr>(addr) < m_HeapStart + m_HeapSize; 173 } 174 GetOrder(size_t size)175 static s32 GetOrder(size_t size) 176 { 177 return GetNearestHigherOrder(DivUp<PAGE_SIZE>(size)); 178 } 179 180 static const size_t PAGE_SIZE = PageSize; 181 static const s32 MAX_ORDER = MaxOrder; 182 183 private: 184 struct Page 185 { 186 Page* pNext; 187 }; 188 189 class PageList 190 { 191 private: 192 Page* m_pFirstPage; 193 Page* m_pLastPage; 194 195 public: Initialize()196 void Initialize() 197 { 198 m_pFirstPage = NULL; 199 m_pLastPage = NULL; 200 } 201 PopFront()202 Page* PopFront() 203 { 204 NN_ASSERT(m_pFirstPage); 205 206 Page* pPage = m_pFirstPage; 207 208 m_pFirstPage = pPage->pNext; 209 pPage->pNext = NULL; 210 211 if(pPage == m_pLastPage) 212 { 213 m_pLastPage = NULL; 214 } 215 216 return pPage; 217 } 218 PushBack(Page * pPage)219 void PushBack(Page* pPage) 220 { 221 NN_ASSERT(!pPage->pNext); 222 223 if(!m_pFirstPage) 224 { 225 m_pFirstPage = pPage; 226 } 227 else 228 { 229 m_pLastPage->pNext = pPage; 230 } 231 232 m_pLastPage = pPage; 233 } 234 Remove(Page * pPage)235 bool Remove(Page* pPage) 236 { 237 if(!m_pFirstPage) 238 { 239 return false; 240 } 241 242 Page* pPrevPage = NULL; 243 Page* page = m_pFirstPage; 244 245 for(;;) 246 { 247 if(page == pPage) 248 { 249 if(page == m_pFirstPage) 250 { 251 m_pFirstPage = page->pNext; 252 } 253 else if(page == m_pLastPage) 254 { 255 m_pLastPage = pPrevPage; 256 m_pLastPage->pNext = NULL; 257 } 258 else 259 { 260 pPrevPage->pNext = page->pNext; 261 } 262 263 page->pNext = NULL; 264 265 return true; 266 } 267 268 if(page->pNext == NULL) 269 { 270 return false; 271 } 272 273 pPrevPage = page; 274 page = page->pNext; 275 } 276 } 277 IsEmpty()278 bool IsEmpty() const 279 { 280 if(m_pFirstPage) 281 { 282 return false; 283 } 284 else 285 { 286 return true; 287 } 288 } 289 }; 290 291 PageList m_FreeArea[MAX_ORDER]; 292 293 uptr m_HeapStart; 294 size_t m_HeapSize; 295 GetFreePage(s32 order)296 Page* GetFreePage(s32 order) 297 { 298 for(int i = order; i < MAX_ORDER; i++) 299 { 300 if(!(m_FreeArea[i].IsEmpty())) 301 { 302 Page* pPage = m_FreeArea[i].PopFront(); 303 304 NN_ASSERT(pPage); 305 306 DivideBuddies(pPage, order, i); 307 308 return pPage; 309 } 310 } 311 312 NN_LOG_WARN("No free pages order %d\n", order); 313 return NULL; 314 } 315 DivideBuddies(Page * pPage,s32 demandedOrder,s32 freeOrder)316 void DivideBuddies(Page* pPage, s32 demandedOrder, s32 freeOrder) 317 { 318 for(int i = freeOrder; demandedOrder < i; i--) 319 { 320 Page* pDividedPage = &pPage[(1 << (i - 1))];; 321 322 NN_LOG_DEBUG("divides buddy address 0x%08X and 0x%08X, order%d\n", GetAddressFromPage(*pPage), GetAddressFromPage(*pDividedPage), i); 323 m_FreeArea[i - 1].PushBack(pDividedPage); 324 } 325 } 326 JointBuddies(Page * pPage,s32 order)327 void JointBuddies(Page* pPage, s32 order) 328 { 329 // Maximum "order" - 1 330 while(order < MAX_ORDER - 1) 331 { 332 Page* pBuddyPage = GetBuddy(pPage, order); 333 334 // Is there a buddy on the free list? 335 if(m_FreeArea[order].Remove(pBuddyPage)) 336 { 337 // If so, delete it (jointed) from the free list and attempt joint at (order + 1). 338 339 // If the page was not aligned to (order + 1), then set address to Buddy. 340 if(!IsAlignedToOrder(pPage, order + 1)) 341 { 342 pPage = pBuddyPage; 343 } 344 345 NN_LOG_DEBUG("joints buddy address 0x%08X and 0x%08X, order %d", GetAddressFromPage(*pPage), GetAddressFromPage(*GetBuddy(pPage, order)), order); 346 347 348 order++; 349 } 350 else 351 { 352 // If not, joint failed. Add page to the free list. 353 break; 354 } 355 } 356 357 // Buddy was being used or jointed up to max order 358 NN_LOG_DEBUG("adding a free page, address 0x%08X, order %d", GetAddressFromPage(*pPage), order ); 359 m_FreeArea[order].PushBack(pPage); 360 } 361 GetAddressFromPage(const Page & page)362 inline uptr GetAddressFromPage(const Page& page) const 363 { 364 return m_HeapStart + GetIndexFromPage(page) * PAGE_SIZE; 365 } 366 GetPageFromAddress(uptr addr)367 inline Page* GetPageFromAddress(uptr addr) 368 { 369 return &m_pPages[(addr - m_HeapStart) / PAGE_SIZE]; 370 } 371 GetIndexFromPage(const Page & page)372 inline s32 GetIndexFromPage(const Page& page) const 373 { 374 return &page - m_pPages; 375 } 376 GetBuddy(Page * pPage,s32 order)377 inline Page* GetBuddy(Page* pPage, s32 order) 378 { 379 // If the page index is divisible by the above order, the next right is Buddy 380 if(IsAlignedToOrder(pPage, order + 1)) 381 { 382 return pPage + GetNumPagesByOrder(order); 383 } 384 else // If not, the next left is Buddy 385 { 386 return pPage - GetNumPagesByOrder(order); 387 } 388 } 389 IsAlignedToOrder(Page * pPage,s32 order)390 inline bool IsAlignedToOrder(Page* pPage, s32 order) const 391 { 392 return GetIndexFromPage(*pPage) % GetNumPagesByOrder(order) == 0; 393 } 394 GetNumPagesByOrder(s32 order)395 inline int GetNumPagesByOrder(s32 order) const 396 { 397 return (1 << order); 398 } 399 GetNearestHigherOrder(size_t numPages)400 static s32 GetNearestHigherOrder(size_t numPages) 401 { 402 for(int order = 0; order <= MAX_ORDER; order++) 403 { 404 if( numPages <= (1 << order) ) { 405 return order; 406 } 407 } 408 409 NN_LOG_WARN("No nearest higher order: numPages=%d, MAX_ORDER=%d", numPages, MAX_ORDER); 410 return -1; 411 } 412 GetNearestLowerOrder(size_t numPages)413 static s32 GetNearestLowerOrder(size_t numPages) 414 { 415 for(int order = 0; order <= MAX_ORDER; order++) 416 { 417 if( numPages < (1 << order) ) { 418 return order - 1; 419 } 420 } 421 422 NN_LOG_WARN("No nearest lower order: numPages=%d, MAX_ORDER=%d", numPages, MAX_ORDER); 423 return -1; 424 } 425 426 Page* m_pPages; 427 }; 428 429 template <size_t PageSize, s32 MaxOrder, class LockPolicy> 430 class BuddyHeapTemplate : public BuddyHeapBase<PageSize, MaxOrder>, private LockPolicy::LockObject 431 { 432 private: 433 typedef BuddyHeapBase<PageSize, MaxOrder> Base; 434 typedef typename LockPolicy::LockObject LockObject; 435 typedef typename LockPolicy::ScopedLock ScopedLock; 436 437 public: BuddyHeapTemplate()438 BuddyHeapTemplate() {} 439 440 template <class MemoryBlockT> BuddyHeapTemplate(const MemoryBlockT & block)441 explicit BuddyHeapTemplate(const MemoryBlockT& block) 442 { 443 Initialize(block.GetAddress(), block.GetSize()); 444 } 445 Initialize(uptr addr,size_t size)446 void Initialize(uptr addr, size_t size) 447 { 448 uptr addrAligned = Base::RoundUp<Base::ALIGN>(addr); 449 size -= addrAligned - addr; 450 451 Base::Initialize(addrAligned, size / Base::PAGE_SIZE); 452 LockObject::Initialize(); 453 } Initialize(const nn::os::MemoryBlockBase & block)454 void Initialize(const nn::os::MemoryBlockBase& block) 455 { 456 Initialize(block.GetAddress(), block.GetSize()); 457 } 458 Finalize()459 void Finalize() 460 { 461 LockObject::Finalize(); 462 Base::Finalize(); 463 } 464 AllocateByOrder(s32 order)465 void* AllocateByOrder(s32 order) 466 { 467 ScopedLock lk(*this); 468 return Base::AllocateByOrder(order); 469 } 470 471 /* Please see man pages for details 472 473 474 475 476 477 */ Free(void * p,s32 order)478 void Free(void* p, s32 order) 479 { 480 ScopedLock lk(*this); 481 Base::Free(p, order); 482 } 483 484 class Allocator; 485 }; 486 487 template <size_t PageSize, s32 MaxOrder, class LockPolicy> 488 class BuddyHeapTemplate<PageSize, MaxOrder, LockPolicy>::Allocator : public nn::fnd::IAllocator 489 { 490 private: 491 typedef BuddyHeapTemplate<PageSize, MaxOrder, LockPolicy> BuddyHeap; 492 static const size_t ALIGN = BuddyHeap::ALIGN; 493 494 public: Allocator(BuddyHeap & heap)495 Allocator(BuddyHeap& heap) : m_Heap(0) { Initialize(heap); } 496 497 /* Please see man pages for details 498 499 */ Allocator()500 Allocator() : m_Heap(0) {} 501 Initialize(BuddyHeap & heap)502 void Initialize(BuddyHeap& heap) 503 { 504 NN_TASSERT_(!this->m_Heap); 505 this->m_Heap = &heap; 506 } 507 508 /* Please see man pages for details 509 510 511 */ GetHeap()512 BuddyHeap* GetHeap() { return m_Heap; } 513 514 /* Please see man pages for details 515 516 517 */ GetHeap()518 const BuddyHeap* GetHeap() const { return m_Heap; } 519 520 /* Please see man pages for details 521 522 523 524 525 526 527 */ 528 virtual void* Allocate(size_t size, s32 alignment = nn::fnd::HeapBase::DEFAULT_ALIGNMENT) 529 { 530 NN_ASSERT(alignment <= ALIGN); 531 if (alignment > ALIGN) 532 { 533 return NULL; 534 } 535 536 s32 order = m_Heap->GetOrder(size + ALIGN); 537 void* p = m_Heap->Allocate(order); 538 if (!p) 539 { 540 return NULL; 541 } 542 NN_LOG_DEBUG("Alloc size %d, order %d\n", size + ALIGN, order); 543 *reinterpret_cast<s32*>(p) = order; 544 return reinterpret_cast<void*>(reinterpret_cast<uptr>(p) + ALIGN); 545 } 546 547 /* Please see man pages for details 548 549 550 */ Free(void * p)551 virtual void Free(void* p) 552 { 553 p = reinterpret_cast<void*>(reinterpret_cast<uptr>(p) - ALIGN); 554 s32 order = *reinterpret_cast<s32*>(p); 555 m_Heap->Free(p, order); 556 } 557 558 private: 559 BuddyHeap* m_Heap; 560 }; 561 562 } 563 } 564 } 565 566 #endif /* NN_NET_OSL_BUDDYHEAP_H_ */ 567