1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_AddressSpaceManager.cpp
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 #include <nn/config.h>
17 #if NN_PLATFORM_HAS_MMU
18 
19 #include "os_AddressSpaceManager.h"
20 #include <nn/os/os_MemoryBlock.h>
21 
22 namespace nn {
23     namespace os {
24 
Initialize(uptr begin,size_t size)25 void AddressSpaceManager::Initialize(uptr begin, size_t size)
26 {
27     if ( m_SpaceBegin == 0 && m_SpaceEnd == 0 )
28     {
29         m_Lock.Initialize();
30 
31         m_SpaceBegin = begin;
32         m_SpaceEnd   = begin + size;
33         NN_MIN_TASSERT_(m_SpaceEnd, m_SpaceBegin);
34     }
35 }
36 
Allocate(MemoryBlockBase * pBlock,size_t size,size_t skipSize)37 uptr AddressSpaceManager::Allocate(MemoryBlockBase* pBlock, size_t size, size_t skipSize)
38 {
39     NN_NULL_TASSERT_(pBlock);
40     NN_ALIGN_TASSERT_(size, NN_OS_MEMORY_PAGE_SIZE);
41     NN_ALIGN_TASSERT_(skipSize, NN_OS_MEMORY_PAGE_SIZE);
42 
43     Lock::ScopedLock scopedLock(m_Lock);
44 
45     MemoryBlockBase* pPrev = FindSpace(size, skipSize);
46     uptr allocatedAddress;
47 
48     if( pPrev != NULL )
49     {
50         // There is space between pPrev and either GetNext(pPrev) or SpaceEnd
51 
52         allocatedAddress = pPrev->GetAddress() + pPrev->GetSize() + skipSize;
53         MemoryBlockBase* pNext = m_BlockList.GetNext(pPrev);
54 
55         if( pNext != NULL )
56         {
57             NN_MAX_TASSERT_(allocatedAddress + size + skipSize, pNext->GetAddress());
58 
59             m_BlockList.Insert(pNext, pBlock);
60         }
61         else
62         {
63             NN_MAX_TASSERT_(allocatedAddress + size + skipSize, m_SpaceEnd);
64 
65             m_BlockList.PushBack(pBlock);
66         }
67     }
68     else
69     {
70         // There is space between SpaceBegin and either GetFront() or SpaceEnd
71         // Or there is no space.
72 
73         allocatedAddress = m_SpaceBegin;
74         MemoryBlockBase* pNext = m_BlockList.GetFront();
75 
76         if( pNext != NULL )
77         {
78             const uptr allocatedEnd = allocatedAddress + size;
79             const uptr nextBegin    = pNext->GetAddress();
80 
81             if( nextBegin < allocatedEnd + skipSize )
82             {
83                 // A size-portion of open memory does not exist between the start of the address space and the next memory block.
84                 return NULL;
85             }
86 
87             m_BlockList.Insert(pNext, pBlock);
88         }
89         else
90         {
91             // When all space is open
92             const uptr allocatedEnd = allocatedAddress + size;
93 
94             if( m_SpaceEnd < allocatedEnd )
95             {
96                 // A size-portion of open memory does not exist in the region of managed address space.
97                 return NULL;
98             }
99 
100             m_BlockList.PushBack(pBlock);
101         }
102     }
103 
104     pBlock->SetAddressAndSize(allocatedAddress, size);
105 
106     return allocatedAddress;
107 }
108 
109 // Release memory block to the address space.
Free(MemoryBlockBase * pBlock)110 void AddressSpaceManager::Free(MemoryBlockBase* pBlock)
111 {
112     // TODO: Need to do pBlock NULL check.
113     Lock::ScopedLock scopedLock(m_Lock);
114 
115     m_BlockList.Erase(pBlock);
116     pBlock->SetAddressAndSize(NULL, 0);
117 }
118 
119 // Move memory block information from pFrom to pTo.
Switch(MemoryBlockBase * pTo,MemoryBlockBase * pFrom)120 void AddressSpaceManager::Switch(MemoryBlockBase* pTo, MemoryBlockBase* pFrom)
121 {
122     // TODO: Need to do pTo NULL check.
123     // TODO: Need to do pFrom NULL check.
124     Lock::ScopedLock scopedLock(m_Lock);
125 
126     pTo->SetAddressAndSize(pFrom->GetAddress(), pFrom->GetSize());
127     m_BlockList.Insert(pFrom, pTo);
128 
129     pFrom->SetAddressAndSize(NULL, 0);
130     m_BlockList.Erase(pFrom);
131 }
132 
133 // Searches for a region that has open memory equal to the specified size + page size portion.
FindSpace(size_t size,size_t skipSize)134 MemoryBlockBase* AddressSpaceManager::FindSpace(size_t size, size_t skipSize)
135 {
136     MemoryBlockBase* pItem = m_BlockList.GetBack();
137     uptr end = m_SpaceEnd;
138     NN_MIN_TASSERT_(end, m_SpaceBegin);
139 
140     while( pItem != NULL )
141     {
142         const uptr nextBegin = pItem->GetAddress();
143         const uptr nextEnd   = nextBegin + pItem->GetSize();
144         const size_t spaceSize = end - nextEnd;
145         NN_MAX_TASSERT_(nextEnd, end);
146 
147         if( spaceSize >= size + skipSize )
148         {
149             // The end of pItem is open
150             return pItem;
151         }
152 
153         end = nextBegin - skipSize;
154         pItem = m_BlockList.GetPrevious(pItem);
155     }
156 
157     // All space is open
158     // Or the start of space is open
159     // Or there is no opening
160     return NULL;
161 }
162 
163 // Debug output the memory block list existing in the address space.
Dump()164 void AddressSpaceManager::Dump()
165 {
166     // CHECK: Recommend locking so that list content from other threads is not changed during dump.
167 
168     MemoryBlockBase* pItem = m_BlockList.GetFront();
169 
170     NN_TLOG_("  --------  %08x %08x\n", m_SpaceBegin, m_SpaceEnd);
171     while( pItem != NULL )
172     {
173         NN_TLOG_("  %08x  %08x %08x\n", pItem, pItem->GetAddress(), pItem->GetAddress() + pItem->GetSize());
174         pItem = m_BlockList.GetNext(pItem);
175     }
176 
177     NN_TPANIC_("dump complete");
178 }
179 
180     }
181 }
182 
183 
184 
185 #include <new>
186 using namespace nn::os;
187 
188 extern "C" {
189 
nnosAddressSpaceManagerInitialize(nnosAddressSpaceManager * p,uptr begin,size_t size)190 void nnosAddressSpaceManagerInitialize(nnosAddressSpaceManager* p, uptr begin, size_t size)
191 {
192     AddressSpaceManager* pThis = new (p) AddressSpaceManager();
193     pThis->Initialize(begin, size);
194 }
195 
nnosAddressSpaceManagerAllocate(nnosAddressSpaceManager * p,nnosMemoryBlockBase * p2,size_t size,size_t skipSize)196 uptr nnosAddressSpaceManagerAllocate(nnosAddressSpaceManager* p, nnosMemoryBlockBase* p2, size_t size, size_t skipSize)
197 {
198     AddressSpaceManager* pThis = reinterpret_cast<AddressSpaceManager*>(p);
199     MemoryBlockBase* pBlock = reinterpret_cast<MemoryBlockBase*>(p2);
200     return pThis->Allocate(pBlock, size, skipSize);
201 }
202 
nnosAddressSpaceManagerFree(nnosAddressSpaceManager * p,nnosMemoryBlockBase * p2)203 void nnosAddressSpaceManagerFree(nnosAddressSpaceManager* p, nnosMemoryBlockBase* p2)
204 {
205     AddressSpaceManager* pThis = reinterpret_cast<AddressSpaceManager*>(p);
206     MemoryBlockBase* pBlock = reinterpret_cast<MemoryBlockBase*>(p2);
207     pThis->Free(pBlock);
208 }
209 
nnosAddressSpaceManagerSwitch(nnosAddressSpaceManager * p,nnosMemoryBlockBase * p2,nnosMemoryBlockBase * p3)210 void nnosAddressSpaceManagerSwitch(nnosAddressSpaceManager* p, nnosMemoryBlockBase* p2, nnosMemoryBlockBase* p3)
211 {
212     AddressSpaceManager* pThis = reinterpret_cast<AddressSpaceManager*>(p);
213     MemoryBlockBase* pTo = reinterpret_cast<MemoryBlockBase*>(p2);
214     MemoryBlockBase* pFrom = reinterpret_cast<MemoryBlockBase*>(p3);
215     pThis->Switch(pTo, pFrom);
216 }
217 
218 }
219 
220 
221 #endif  // if NN_PLATFORM_HAS_MMU
222