1 // Copyright 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "aemu/base/AndroidSubAllocator.h"
15
16 #include "aemu/base/address_space.h"
17 #include "aemu/base/files/Stream.h"
18
19 #include <iomanip>
20 #include <sstream>
21 #include <string>
22
23 #include <log/log.h>
24
25 namespace gfxstream {
26 namespace guest {
27
28 class SubAllocator::Impl {
29 public:
Impl(void * _buffer,uint64_t _totalSize,uint64_t _pageSize)30 Impl(
31 void* _buffer,
32 uint64_t _totalSize,
33 uint64_t _pageSize) :
34 buffer(_buffer),
35 totalSize(_totalSize),
36 pageSize(_pageSize),
37 startAddr((uintptr_t)buffer),
38 endAddr(startAddr + totalSize) {
39
40 address_space_allocator_init(
41 &addr_alloc,
42 totalSize,
43 32);
44 }
45
~Impl()46 ~Impl() {
47 address_space_allocator_destroy_nocleanup(&addr_alloc);
48 }
49
clear()50 void clear() {
51 address_space_allocator_destroy_nocleanup(&addr_alloc);
52 address_space_allocator_init(
53 &addr_alloc,
54 totalSize,
55 32);
56 }
57
save(Stream * stream)58 bool save(Stream* stream) {
59 address_space_allocator_iter_func_t allocatorSaver =
60 [](void* context, struct address_space_allocator* allocator) {
61 Stream* stream = reinterpret_cast<Stream*>(context);
62 stream->putBe32(allocator->size);
63 stream->putBe32(allocator->capacity);
64 stream->putBe64(allocator->total_bytes);
65 };
66 address_block_iter_func_t allocatorBlockSaver =
67 [](void* context, struct address_block* block) {
68 Stream* stream = reinterpret_cast<Stream*>(context);
69 stream->putBe64(block->offset);
70 stream->putBe64(block->size_available);
71 };
72 address_space_allocator_run(
73 &addr_alloc,
74 (void*)stream,
75 allocatorSaver,
76 allocatorBlockSaver);
77
78 stream->putBe64(pageSize);
79 stream->putBe64(totalSize);
80 stream->putBe32(allocCount);
81
82 return true;
83 }
84
load(Stream * stream)85 bool load(Stream* stream) {
86 clear();
87 address_space_allocator_iter_func_t allocatorLoader =
88 [](void* context, struct address_space_allocator* allocator) {
89 Stream* stream = reinterpret_cast<Stream*>(context);
90 allocator->size = stream->getBe32();
91 allocator->capacity = stream->getBe32();
92 allocator->total_bytes = stream->getBe64();
93 };
94 address_block_iter_func_t allocatorBlockLoader =
95 [](void* context, struct address_block* block) {
96 Stream* stream = reinterpret_cast<Stream*>(context);
97 block->offset = stream->getBe64();
98 block->size_available = stream->getBe64();
99 };
100 address_space_allocator_run(
101 &addr_alloc,
102 (void*)stream,
103 allocatorLoader,
104 allocatorBlockLoader);
105
106 pageSize = stream->getBe64();
107 totalSize = stream->getBe64();
108 allocCount = stream->getBe32();
109
110 return true;
111 }
112
postLoad(void * postLoadBuffer)113 bool postLoad(void* postLoadBuffer) {
114 buffer = postLoadBuffer;
115 startAddr =
116 (uint64_t)(uintptr_t)postLoadBuffer;
117 return true;
118 }
119
rangeCheck(const char * task,void * ptr)120 void rangeCheck(const char* task, void* ptr) {
121 uint64_t addr = (uintptr_t)ptr;
122 if (addr < startAddr ||
123 addr > endAddr) {
124 std::stringstream ss;
125 ss << "SubAllocator " << task << ": ";
126 ss << "Out of range: " << std::hex << addr << " ";
127 ss << "Range: " <<
128 std::hex << startAddr << " " <<
129 std::hex << endAddr;
130 std::string msg = ss.str();
131 ALOGE("Fatal: %s\n", msg.c_str());
132 }
133 }
134
getOffset(void * checkedPtr)135 uint64_t getOffset(void* checkedPtr) {
136 uint64_t addr = (uintptr_t)checkedPtr;
137 return addr - startAddr;
138 }
139
free(void * ptr)140 bool free(void* ptr) {
141 if (!ptr) return false;
142
143 rangeCheck("free", ptr);
144 if (EINVAL == address_space_allocator_deallocate(
145 &addr_alloc, getOffset(ptr))) {
146 return false;
147 }
148
149 --allocCount;
150 return true;
151 }
152
freeAll()153 void freeAll() {
154 address_space_allocator_reset(&addr_alloc);
155 allocCount = 0;
156 }
157
alloc(size_t wantedSize)158 void* alloc(size_t wantedSize) {
159 if (wantedSize == 0) return nullptr;
160
161 uint64_t wantedSize64 =
162 (uint64_t)wantedSize;
163
164 size_t toPageSize =
165 pageSize *
166 ((wantedSize + pageSize - 1) / pageSize);
167
168 uint64_t offset =
169 address_space_allocator_allocate(
170 &addr_alloc, toPageSize);
171
172 if (offset == ANDROID_EMU_ADDRESS_SPACE_BAD_OFFSET) {
173 return nullptr;
174 }
175
176 ++allocCount;
177 return (void*)(uintptr_t)(startAddr + offset);
178 }
179
empty() const180 bool empty() const {
181 return allocCount == 0;
182 }
183
184 void* buffer;
185 uint64_t totalSize;
186 uint64_t pageSize;
187 uint64_t startAddr;
188 uint64_t endAddr;
189 struct address_space_allocator addr_alloc;
190 uint32_t allocCount = 0;
191 };
192
SubAllocator(void * buffer,uint64_t totalSize,uint64_t pageSize)193 SubAllocator::SubAllocator(
194 void* buffer,
195 uint64_t totalSize,
196 uint64_t pageSize) :
197 mImpl(
198 new SubAllocator::Impl(buffer, totalSize, pageSize)) { }
199
~SubAllocator()200 SubAllocator::~SubAllocator() {
201 delete mImpl;
202 }
203
204 // Snapshotting
save(Stream * stream)205 bool SubAllocator::save(Stream* stream) {
206 return mImpl->save(stream);
207 }
208
load(Stream * stream)209 bool SubAllocator::load(Stream* stream) {
210 return mImpl->load(stream);
211 }
212
postLoad(void * postLoadBuffer)213 bool SubAllocator::postLoad(void* postLoadBuffer) {
214 return mImpl->postLoad(postLoadBuffer);
215 }
216
alloc(size_t wantedSize)217 void* SubAllocator::alloc(size_t wantedSize) {
218 return mImpl->alloc(wantedSize);
219 }
220
free(void * ptr)221 bool SubAllocator::free(void* ptr) {
222 return mImpl->free(ptr);
223 }
224
freeAll()225 void SubAllocator::freeAll() {
226 mImpl->freeAll();
227 }
228
getOffset(void * ptr)229 uint64_t SubAllocator::getOffset(void* ptr) {
230 return mImpl->getOffset(ptr);
231 }
232
empty() const233 bool SubAllocator::empty() const {
234 return mImpl->empty();
235 }
236
237 } // namespace guest
238 } // namespace gfxstream
239