blob: 0b17a8a4855911d6534b194e600760cf4c86c768 [file] [log] [blame]
// Copyright 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "android/base/AndroidSubAllocator.h"
#include "android/base/address_space.h"
#include "android/base/files/Stream.h"
#include <iomanip>
#include <sstream>
#include <string>
#include <log/log.h>
namespace android {
namespace base {
namespace guest {
class SubAllocator::Impl {
public:
Impl(
void* _buffer,
uint64_t _totalSize,
uint64_t _pageSize) :
buffer(_buffer),
totalSize(_totalSize),
pageSize(_pageSize),
startAddr((uintptr_t)buffer),
endAddr(startAddr + totalSize) {
address_space_allocator_init(
&addr_alloc,
totalSize,
32);
}
~Impl() {
address_space_allocator_destroy_nocleanup(&addr_alloc);
}
void clear() {
address_space_allocator_destroy_nocleanup(&addr_alloc);
address_space_allocator_init(
&addr_alloc,
totalSize,
32);
}
bool save(Stream* stream) {
address_space_allocator_iter_func_t allocatorSaver =
[](void* context, struct address_space_allocator* allocator) {
Stream* stream = reinterpret_cast<Stream*>(context);
stream->putBe32(allocator->size);
stream->putBe32(allocator->capacity);
stream->putBe64(allocator->total_bytes);
};
address_block_iter_func_t allocatorBlockSaver =
[](void* context, struct address_block* block) {
Stream* stream = reinterpret_cast<Stream*>(context);
stream->putBe64(block->offset);
stream->putBe64(block->size_available);
};
address_space_allocator_run(
&addr_alloc,
(void*)stream,
allocatorSaver,
allocatorBlockSaver);
stream->putBe64(pageSize);
stream->putBe64(totalSize);
stream->putBe32(allocCount);
return true;
}
bool load(Stream* stream) {
clear();
address_space_allocator_iter_func_t allocatorLoader =
[](void* context, struct address_space_allocator* allocator) {
Stream* stream = reinterpret_cast<Stream*>(context);
allocator->size = stream->getBe32();
allocator->capacity = stream->getBe32();
allocator->total_bytes = stream->getBe64();
};
address_block_iter_func_t allocatorBlockLoader =
[](void* context, struct address_block* block) {
Stream* stream = reinterpret_cast<Stream*>(context);
block->offset = stream->getBe64();
block->size_available = stream->getBe64();
};
address_space_allocator_run(
&addr_alloc,
(void*)stream,
allocatorLoader,
allocatorBlockLoader);
pageSize = stream->getBe64();
totalSize = stream->getBe64();
allocCount = stream->getBe32();
return true;
}
bool postLoad(void* postLoadBuffer) {
buffer = postLoadBuffer;
startAddr =
(uint64_t)(uintptr_t)postLoadBuffer;
return true;
}
void rangeCheck(const char* task, void* ptr) {
uint64_t addr = (uintptr_t)ptr;
if (addr < startAddr ||
addr > endAddr) {
std::stringstream ss;
ss << "SubAllocator " << task << ": ";
ss << "Out of range: " << std::hex << addr << " ";
ss << "Range: " <<
std::hex << startAddr << " " <<
std::hex << endAddr;
std::string msg = ss.str();
ALOGE("Fatal: %s\n", msg.c_str());
}
}
uint64_t getOffset(void* checkedPtr) {
uint64_t addr = (uintptr_t)checkedPtr;
return addr - startAddr;
}
bool free(void* ptr) {
if (!ptr) return false;
rangeCheck("free", ptr);
if (EINVAL == address_space_allocator_deallocate(
&addr_alloc, getOffset(ptr))) {
return false;
}
--allocCount;
return true;
}
void freeAll() {
address_space_allocator_reset(&addr_alloc);
allocCount = 0;
}
void* alloc(size_t wantedSize) {
if (wantedSize == 0) return nullptr;
uint64_t wantedSize64 =
(uint64_t)wantedSize;
size_t toPageSize =
pageSize *
((wantedSize + pageSize - 1) / pageSize);
uint64_t offset =
address_space_allocator_allocate(
&addr_alloc, toPageSize);
if (offset == ANDROID_EMU_ADDRESS_SPACE_BAD_OFFSET) {
return nullptr;
}
++allocCount;
return (void*)(uintptr_t)(startAddr + offset);
}
bool empty() const {
return allocCount == 0;
}
void* buffer;
uint64_t totalSize;
uint64_t pageSize;
uint64_t startAddr;
uint64_t endAddr;
struct address_space_allocator addr_alloc;
uint32_t allocCount = 0;
};
SubAllocator::SubAllocator(
void* buffer,
uint64_t totalSize,
uint64_t pageSize) :
mImpl(
new SubAllocator::Impl(buffer, totalSize, pageSize)) { }
SubAllocator::~SubAllocator() {
delete mImpl;
}
// Snapshotting
bool SubAllocator::save(Stream* stream) {
return mImpl->save(stream);
}
bool SubAllocator::load(Stream* stream) {
return mImpl->load(stream);
}
bool SubAllocator::postLoad(void* postLoadBuffer) {
return mImpl->postLoad(postLoadBuffer);
}
void* SubAllocator::alloc(size_t wantedSize) {
return mImpl->alloc(wantedSize);
}
bool SubAllocator::free(void* ptr) {
return mImpl->free(ptr);
}
void SubAllocator::freeAll() {
mImpl->freeAll();
}
uint64_t SubAllocator::getOffset(void* ptr) {
return mImpl->getOffset(ptr);
}
bool SubAllocator::empty() const {
return mImpl->empty();
}
} // namespace guest
} // namespace base
} // namespace android