/*---------------------------------------------------------------------------* Project: Horizon File: os_AddressSpaceManager.cpp Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 17610 $ *---------------------------------------------------------------------------*/ #include #if NN_PLATFORM_HAS_MMU #include "os_AddressSpaceManager.h" #include namespace nn { namespace os { void AddressSpaceManager::Initialize(uptr begin, size_t size) { m_Lock.Initialize(); // CHECK: 初期化済みかどうかを確認することを推奨します。 m_SpaceBegin = begin; m_SpaceEnd = begin + size; NN_MIN_TASSERT_(m_SpaceEnd, m_SpaceBegin); } uptr AddressSpaceManager::Allocate(MemoryBlockBase* pBlock, size_t size, size_t skipSize) { NN_NULL_TASSERT_(pBlock); NN_ALIGN_TASSERT_(size, NN_OS_MEMORY_PAGE_SIZE); NN_ALIGN_TASSERT_(skipSize, NN_OS_MEMORY_PAGE_SIZE); Lock::ScopedLock scopedLock(m_Lock); MemoryBlockBase* pPrev = FindSpace(size, skipSize); uptr allocatedAddress; if( pPrev != NULL ) { // pPrev と GetNext(pPrev) または SpaceEnd の間の空間があいている allocatedAddress = pPrev->GetAddress() + pPrev->GetSize() + skipSize; MemoryBlockBase* pNext = m_BlockList.GetNext(pPrev); if( pNext != NULL ) { NN_MAX_TASSERT_(allocatedAddress + size + skipSize, pNext->GetAddress()); m_BlockList.Insert(pNext, pBlock); } else { NN_MAX_TASSERT_(allocatedAddress + size + skipSize, m_SpaceEnd); m_BlockList.PushBack(pBlock); } } else { // SpaceBegin と GetFront() または SpaceEnd の間の空間があいている // または、あいていない。 allocatedAddress = m_SpaceBegin; MemoryBlockBase* pNext = m_BlockList.GetFront(); if( pNext != NULL ) { const uptr allocatedEnd = allocatedAddress + size; const uptr nextBegin = pNext->GetAddress(); if( nextBegin < allocatedEnd + skipSize ) { // アドレス空間の先頭から次のメモリブロックまでの間に size 分の空きが存在しません。 return NULL; } m_BlockList.Insert(pNext, pBlock); } else { // 全空間が空いていた場合 const uptr allocatedEnd = allocatedAddress + size; if( m_SpaceEnd < allocatedEnd ) { // 管理しているアドレス空間の領域では size 分の空きが存在しません。 return NULL; } m_BlockList.PushBack(pBlock); } } pBlock->SetAddressAndSize(allocatedAddress, size); return allocatedAddress; } // メモリブロックをアドレス空間に解放します。 void AddressSpaceManager::Free(MemoryBlockBase* pBlock) { // TODO: pBlockのNULLチェックが必要です。 Lock::ScopedLock scopedLock(m_Lock); m_BlockList.Erase(pBlock); pBlock->SetAddressAndSize(NULL, 0); } // メモリブロックの情報を pFrom から pTo に移します。 void AddressSpaceManager::Switch(MemoryBlockBase* pTo, MemoryBlockBase* pFrom) { // TODO: pToのNULLチェックが必要です。 // TODO: pFromのNULLチェックが必要です。 Lock::ScopedLock scopedLock(m_Lock); pTo->SetAddressAndSize(pFrom->GetAddress(), pFrom->GetSize()); m_BlockList.Insert(pFrom, pTo); pFrom->SetAddressAndSize(NULL, 0); m_BlockList.Erase(pFrom); } // 指定したサイズ+ページサイズ分の空きがある領域を探索します。 MemoryBlockBase* AddressSpaceManager::FindSpace(size_t size, size_t skipSize) { MemoryBlockBase* pItem = m_BlockList.GetBack(); uptr end = m_SpaceEnd; NN_MIN_TASSERT_(end, m_SpaceBegin); while( pItem != NULL ) { const uptr nextBegin = pItem->GetAddress(); const uptr nextEnd = nextBegin + pItem->GetSize(); const size_t spaceSize = end - nextEnd; NN_MAX_TASSERT_(nextEnd, end); if( spaceSize >= size + skipSize ) { // pItem の後ろがあいている return pItem; } end = nextBegin - skipSize; pItem = m_BlockList.GetPrevious(pItem); } // 全空間があいている // または、先頭の空間があいている // または、あいていない return NULL; } // アドレス空間に存在するメモリブロックリストをデバッグ出力します。 void AddressSpaceManager::Dump() { // CHECK: ダンプ中に他のスレッドからリストの内容が変更されないようにロックすることを推奨します。 MemoryBlockBase* pItem = m_BlockList.GetFront(); NN_TLOG_(" -------- %08x %08x\n", m_SpaceBegin, m_SpaceEnd); while( pItem != NULL ) { NN_TLOG_(" %08x %08x %08x\n", pItem, pItem->GetAddress(), pItem->GetAddress() + pItem->GetSize()); pItem = m_BlockList.GetNext(pItem); } NN_TPANIC_("dump complete"); } } } #include using namespace nn::os; extern "C" { void nnosAddressSpaceManagerInitialize(nnosAddressSpaceManager* p, uptr begin, size_t size) { AddressSpaceManager* pThis = new (p) AddressSpaceManager(); pThis->Initialize(begin, size); } uptr nnosAddressSpaceManagerAllocate(nnosAddressSpaceManager* p, nnosMemoryBlockBase* p2, size_t size, size_t skipSize) { AddressSpaceManager* pThis = reinterpret_cast(p); MemoryBlockBase* pBlock = reinterpret_cast(p2); return pThis->Allocate(pBlock, size, skipSize); } void nnosAddressSpaceManagerFree(nnosAddressSpaceManager* p, nnosMemoryBlockBase* p2) { AddressSpaceManager* pThis = reinterpret_cast(p); MemoryBlockBase* pBlock = reinterpret_cast(p2); pThis->Free(pBlock); } void nnosAddressSpaceManagerSwitch(nnosAddressSpaceManager* p, nnosMemoryBlockBase* p2, nnosMemoryBlockBase* p3) { AddressSpaceManager* pThis = reinterpret_cast(p); MemoryBlockBase* pTo = reinterpret_cast(p2); MemoryBlockBase* pFrom = reinterpret_cast(p3); pThis->Switch(pTo, pFrom); } } #endif // if NN_PLATFORM_HAS_MMU