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