From bd4bfd035d43882d1df45ed531cad9acc65c552e Mon Sep 17 00:00:00 2001 From: andrew sutherland Date: Sat, 7 Jan 2012 22:40:24 -0600 Subject: [PATCH] gralloc: add BOARD_USES_LEGACY_QCOM Change-Id: I7fae5cf67ed4a1f00012c64c384bb621ab8bb183 --- Android.mk | 4 + libgralloc-legacy/Android.mk | 49 ++ libgralloc-legacy/MODULE_LICENSE_APACHE2 | 0 libgralloc-legacy/NOTICE | 190 +++++++ libgralloc-legacy/allocator.cpp | 165 ++++++ libgralloc-legacy/allocator.h | 130 +++++ libgralloc-legacy/framebuffer.cpp | 457 ++++++++++++++++ libgralloc-legacy/gpu.cpp | 341 ++++++++++++ libgralloc-legacy/gpu.h | 76 +++ libgralloc-legacy/gr.h | 63 +++ libgralloc-legacy/gralloc.cpp | 196 +++++++ libgralloc-legacy/gralloc_priv.h | 165 ++++++ libgralloc-legacy/mapper.cpp | 325 +++++++++++ libgralloc-legacy/pmemalloc.cpp | 324 +++++++++++ libgralloc-legacy/pmemalloc.h | 161 ++++++ libgralloc-legacy/tests/Android.mk | 55 ++ libgralloc-legacy/tests/pmemalloc_test.cpp | 601 +++++++++++++++++++++ 17 files changed, 3302 insertions(+) create mode 100644 libgralloc-legacy/Android.mk create mode 100644 libgralloc-legacy/MODULE_LICENSE_APACHE2 create mode 100644 libgralloc-legacy/NOTICE create mode 100644 libgralloc-legacy/allocator.cpp create mode 100644 libgralloc-legacy/allocator.h create mode 100644 libgralloc-legacy/framebuffer.cpp create mode 100644 libgralloc-legacy/gpu.cpp create mode 100644 libgralloc-legacy/gpu.h create mode 100644 libgralloc-legacy/gr.h create mode 100644 libgralloc-legacy/gralloc.cpp create mode 100644 libgralloc-legacy/gralloc_priv.h create mode 100644 libgralloc-legacy/mapper.cpp create mode 100644 libgralloc-legacy/pmemalloc.cpp create mode 100644 libgralloc-legacy/pmemalloc.h create mode 100644 libgralloc-legacy/tests/Android.mk create mode 100644 libgralloc-legacy/tests/pmemalloc_test.cpp diff --git a/Android.mk b/Android.mk index 52d3bbd..cd7d977 100644 --- a/Android.mk +++ b/Android.mk @@ -1,7 +1,11 @@ #Enables the listed display HAL modules ifeq ($(BOARD_USES_QCOM_HARDWARE),true) +ifeq ($(BOARD_USES_LEGACY_QCOM),true) + display-hals := libgralloc-legacy libcopybit +else display-hals := libhwcomposer liboverlay libgralloc libcopybit libgenlock libtilerenderer display-hals += libqcomui +endif include $(call all-named-subdir-makefiles,$(display-hals)) endif diff --git a/libgralloc-legacy/Android.mk b/libgralloc-legacy/Android.mk new file mode 100644 index 0000000..ed33cdc --- /dev/null +++ b/libgralloc-legacy/Android.mk @@ -0,0 +1,49 @@ +# Copyright (C) 2008 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. + +LOCAL_PATH := $(call my-dir) + +# HAL module implemenation stored in +# hw/..so +include $(CLEAR_VARS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_SHARED_LIBRARIES := liblog libcutils libGLESv1_CM + +LOCAL_SRC_FILES := \ + allocator.cpp \ + framebuffer.cpp \ + gpu.cpp \ + gralloc.cpp \ + mapper.cpp \ + pmemalloc.cpp + +LOCAL_MODULE := gralloc.$(TARGET_BOARD_PLATFORM) +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS:= -DLOG_TAG=\"$(TARGET_BOARD_PLATFORM).gralloc\" +include $(BUILD_SHARED_LIBRARY) + +# Build a host library for testing +ifeq ($(HOST_OS),linux) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + gpu.cpp \ + pmemalloc.cpp + +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := libgralloc_qsd8k_host +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS:= -DLOG_TAG=\"gralloc-qsd8k\" +include $(BUILD_HOST_STATIC_LIBRARY) +endif diff --git a/libgralloc-legacy/MODULE_LICENSE_APACHE2 b/libgralloc-legacy/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/libgralloc-legacy/NOTICE b/libgralloc-legacy/NOTICE new file mode 100644 index 0000000..3237da6 --- /dev/null +++ b/libgralloc-legacy/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2008-2009, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libgralloc-legacy/allocator.cpp b/libgralloc-legacy/allocator.cpp new file mode 100644 index 0000000..e7645b1 --- /dev/null +++ b/libgralloc-legacy/allocator.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009 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 + +#include "allocator.h" + + +// align all the memory blocks on a cache-line boundary +const int SimpleBestFitAllocator::kMemoryAlign = 32; + +SimpleBestFitAllocator::SimpleBestFitAllocator() + : mHeapSize(0) +{ +} + +SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) + : mHeapSize(0) +{ + setSize(size); +} + +SimpleBestFitAllocator::~SimpleBestFitAllocator() +{ + while(!mList.isEmpty()) { + delete mList.remove(mList.head()); + } +} + +ssize_t SimpleBestFitAllocator::setSize(size_t size) +{ + Locker::Autolock _l(mLock); + if (mHeapSize != 0) return -EINVAL; + size_t pagesize = getpagesize(); + mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); + chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); + mList.insertHead(node); + return size; +} + + +size_t SimpleBestFitAllocator::size() const +{ + return mHeapSize; +} + +ssize_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) +{ + Locker::Autolock _l(mLock); + if (mHeapSize == 0) return -EINVAL; + ssize_t offset = alloc(size, flags); + return offset; +} + +ssize_t SimpleBestFitAllocator::deallocate(size_t offset) +{ + Locker::Autolock _l(mLock); + if (mHeapSize == 0) return -EINVAL; + chunk_t const * const freed = dealloc(offset); + if (freed) { + return 0; + } + return -ENOENT; +} + +ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) +{ + if (size == 0) { + return 0; + } + size = (size + kMemoryAlign-1) / kMemoryAlign; + chunk_t* free_chunk = 0; + chunk_t* cur = mList.head(); + + size_t pagesize = getpagesize(); + while (cur) { + int extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; + + // best fit + if (cur->free && (cur->size >= (size+extra))) { + if ((!free_chunk) || (cur->size < free_chunk->size)) { + free_chunk = cur; + } + if (cur->size == size) { + break; + } + } + cur = cur->next; + } + + if (free_chunk) { + const size_t free_size = free_chunk->size; + free_chunk->free = 0; + free_chunk->size = size; + if (free_size > size) { + int extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; + if (extra) { + chunk_t* split = new chunk_t(free_chunk->start, extra); + free_chunk->start += extra; + mList.insertBefore(free_chunk, split); + } + + LOGE_IF(((free_chunk->start*kMemoryAlign)&(pagesize-1)), + "page is not aligned!!!"); + + const ssize_t tail_free = free_size - (size+extra); + if (tail_free > 0) { + chunk_t* split = new chunk_t( + free_chunk->start + free_chunk->size, tail_free); + mList.insertAfter(free_chunk, split); + } + } + return (free_chunk->start)*kMemoryAlign; + } + return -ENOMEM; +} + +SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) +{ + start = start / kMemoryAlign; + chunk_t* cur = mList.head(); + while (cur) { + if (cur->start == start) { + LOG_FATAL_IF(cur->free, + "block at offset 0x%08lX of size 0x%08lX already freed", + cur->start*kMemoryAlign, cur->size*kMemoryAlign); + + // merge freed blocks together + chunk_t* freed = cur; + cur->free = 1; + do { + chunk_t* const p = cur->prev; + chunk_t* const n = cur->next; + if (p && (p->free || !cur->size)) { + freed = p; + p->size += cur->size; + mList.remove(cur); + delete cur; + } + cur = n; + } while (cur && cur->free); + + LOG_FATAL_IF(!freed->free, + "freed block at offset 0x%08lX of size 0x%08lX is not free!", + freed->start * kMemoryAlign, freed->size * kMemoryAlign); + + return freed; + } + cur = cur->next; + } + return 0; +} diff --git a/libgralloc-legacy/allocator.h b/libgralloc-legacy/allocator.h new file mode 100644 index 0000000..dc81f51 --- /dev/null +++ b/libgralloc-legacy/allocator.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009 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. + */ + + +#ifndef GRALLOC_ALLOCATOR_H_ +#define GRALLOC_ALLOCATOR_H_ + +#include +#include + +#include "gr.h" +#include "pmemalloc.h" + +// ---------------------------------------------------------------------------- + +/* + * A simple templatized doubly linked-list implementation + */ + +template +class LinkedList +{ + NODE* mFirst; + NODE* mLast; + +public: + LinkedList() : mFirst(0), mLast(0) { } + bool isEmpty() const { return mFirst == 0; } + NODE const* head() const { return mFirst; } + NODE* head() { return mFirst; } + NODE const* tail() const { return mLast; } + NODE* tail() { return mLast; } + + void insertAfter(NODE* node, NODE* newNode) { + newNode->prev = node; + newNode->next = node->next; + if (node->next == 0) mLast = newNode; + else node->next->prev = newNode; + node->next = newNode; + } + + void insertBefore(NODE* node, NODE* newNode) { + newNode->prev = node->prev; + newNode->next = node; + if (node->prev == 0) mFirst = newNode; + else node->prev->next = newNode; + node->prev = newNode; + } + + void insertHead(NODE* newNode) { + if (mFirst == 0) { + mFirst = mLast = newNode; + newNode->prev = newNode->next = 0; + } else { + newNode->prev = 0; + newNode->next = mFirst; + mFirst->prev = newNode; + mFirst = newNode; + } + } + + void insertTail(NODE* newNode) { + if (mLast == 0) { + insertHead(newNode); + } else { + newNode->prev = mLast; + newNode->next = 0; + mLast->next = newNode; + mLast = newNode; + } + } + + NODE* remove(NODE* node) { + if (node->prev == 0) mFirst = node->next; + else node->prev->next = node->next; + if (node->next == 0) mLast = node->prev; + else node->next->prev = node->prev; + return node; + } +}; + +class SimpleBestFitAllocator : public PmemUserspaceAllocator::Deps::Allocator +{ +public: + + SimpleBestFitAllocator(); + SimpleBestFitAllocator(size_t size); + virtual ~SimpleBestFitAllocator(); + + virtual ssize_t setSize(size_t size); + + virtual ssize_t allocate(size_t size, uint32_t flags = 0); + virtual ssize_t deallocate(size_t offset); + virtual size_t size() const; + +private: + struct chunk_t { + chunk_t(size_t start, size_t size) + : start(start), size(size), free(1), prev(0), next(0) { + } + size_t start; + size_t size : 28; + int free : 4; + mutable chunk_t* prev; + mutable chunk_t* next; + }; + + ssize_t alloc(size_t size, uint32_t flags); + chunk_t* dealloc(size_t start); + + static const int kMemoryAlign; + mutable Locker mLock; + LinkedList mList; + size_t mHeapSize; +}; + +#endif /* GRALLOC_ALLOCATOR_H_ */ diff --git a/libgralloc-legacy/framebuffer.cpp b/libgralloc-legacy/framebuffer.cpp new file mode 100644 index 0000000..f106e76 --- /dev/null +++ b/libgralloc-legacy/framebuffer.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2008 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 + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "gralloc_priv.h" +#include "gr.h" + +/*****************************************************************************/ + +// numbers of buffers for page flipping +#define NUM_BUFFERS 2 + + +enum { + PAGE_FLIP = 0x00000001, + LOCKED = 0x00000002 +}; + +struct fb_context_t { + framebuffer_device_t device; +}; + +/*****************************************************************************/ + +static void +msm_copy_buffer(buffer_handle_t handle, int fd, + int width, int height, int format, + int x, int y, int w, int h); + +static int fb_setSwapInterval(struct framebuffer_device_t* dev, + int interval) +{ + fb_context_t* ctx = (fb_context_t*)dev; + if (interval < dev->minSwapInterval || interval > dev->maxSwapInterval) + return -EINVAL; + // FIXME: implement fb_setSwapInterval + return 0; +} + +static int fb_setUpdateRect(struct framebuffer_device_t* dev, + int l, int t, int w, int h) +{ + if (((w|h) <= 0) || ((l|t)<0)) + return -EINVAL; + + fb_context_t* ctx = (fb_context_t*)dev; + private_module_t* m = reinterpret_cast( + dev->common.module); + m->info.reserved[0] = 0x54445055; // "UPDT"; + m->info.reserved[1] = (uint16_t)l | ((uint32_t)t << 16); + m->info.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16); + return 0; +} + +static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer) +{ + if (private_handle_t::validate(buffer) < 0) + return -EINVAL; + + fb_context_t* ctx = (fb_context_t*)dev; + + private_handle_t const* hnd = reinterpret_cast(buffer); + private_module_t* m = reinterpret_cast( + dev->common.module); + + if (m->currentBuffer) { + m->base.unlock(&m->base, m->currentBuffer); + m->currentBuffer = 0; + } + + if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { + + m->base.lock(&m->base, buffer, + private_module_t::PRIV_USAGE_LOCKED_FOR_POST, + 0, 0, m->info.xres, m->info.yres, NULL); + + const size_t offset = hnd->base - m->framebuffer->base; + m->info.activate = FB_ACTIVATE_VBL; + m->info.yoffset = offset / m->finfo.line_length; + if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) { + LOGE("FBIOPUT_VSCREENINFO failed"); + m->base.unlock(&m->base, buffer); + return -errno; + } + m->currentBuffer = buffer; + + } else { + void* fb_vaddr; + void* buffer_vaddr; + + m->base.lock(&m->base, m->framebuffer, + GRALLOC_USAGE_SW_WRITE_RARELY, + 0, 0, m->info.xres, m->info.yres, + &fb_vaddr); + + m->base.lock(&m->base, buffer, + GRALLOC_USAGE_SW_READ_RARELY, + 0, 0, m->info.xres, m->info.yres, + &buffer_vaddr); + + //memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres); + + msm_copy_buffer( + m->framebuffer, m->framebuffer->fd, + m->info.xres, m->info.yres, m->fbFormat, + m->info.xoffset, m->info.yoffset, + m->info.width, m->info.height); + + m->base.unlock(&m->base, buffer); + m->base.unlock(&m->base, m->framebuffer); + } + + return 0; +} + +static int fb_compositionComplete(struct framebuffer_device_t* dev) +{ + // TODO: Properly implement composition complete callback + glFinish(); + + return 0; +} + +/*****************************************************************************/ + +int mapFrameBufferLocked(struct private_module_t* module) +{ + // already initialized... + if (module->framebuffer) { + return 0; + } + + char const * const device_template[] = { + "/dev/graphics/fb%u", + "/dev/fb%u", + 0 }; + + int fd = -1; + int i=0; + char name[64]; + + while ((fd==-1) && device_template[i]) { + snprintf(name, 64, device_template[i], 0); + fd = open(name, O_RDWR, 0); + i++; + } + if (fd < 0) + return -errno; + + struct fb_fix_screeninfo finfo; + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + struct fb_var_screeninfo info; + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + info.reserved[0] = 0; + info.reserved[1] = 0; + info.reserved[2] = 0; + info.xoffset = 0; + info.yoffset = 0; + info.activate = FB_ACTIVATE_NOW; + + /* Interpretation of offset for color fields: All offsets are from the right, + * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you + * can use the offset as right argument to <<). A pixel afterwards is a bit + * stream and is written to video memory as that unmodified. This implies + * big-endian byte order if bits_per_pixel is greater than 8. + */ + + /* + * Explicitly request RGBA_8888 + */ + info.bits_per_pixel = 32; + info.red.offset = 24; + info.red.length = 8; + info.green.offset = 16; + info.green.length = 8; + info.blue.offset = 8; + info.blue.length = 8; + info.transp.offset = 0; + info.transp.length = 0; + + /* Note: the GL driver does not have a r=8 g=8 b=8 a=0 config, so if we do + * not use the MDP for composition (i.e. hw composition == 0), ask for + * RGBA instead of RGBX. */ + char property[PROPERTY_VALUE_MAX]; + if (property_get("debug.sf.hw", property, NULL) > 0 && atoi(property) == 0) + module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888; + else + module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888; + + /* + * Request NUM_BUFFERS screens (at lest 2 for page flipping) + */ + info.yres_virtual = info.yres * NUM_BUFFERS; + + + uint32_t flags = PAGE_FLIP; + if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); + } + + if (info.yres_virtual < info.yres * 2) { + // we need at least 2 for page-flipping + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", + info.yres_virtual, info.yres*2); + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + uint64_t refreshQuotient = + ( + uint64_t( info.upper_margin + info.lower_margin + info.yres ) + * ( info.left_margin + info.right_margin + info.xres ) + * info.pixclock + ); + + /* Beware, info.pixclock might be 0 under emulation, so avoid a + * division-by-0 here (SIGFPE on ARM) */ + int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0; + + if (refreshRate == 0) { + // bleagh, bad info from the driver + refreshRate = 60*1000; // 60 Hz + } + + if (int(info.width) <= 0 || int(info.height) <= 0) { + // the driver doesn't return that information + // default to 160 dpi + info.width = ((info.xres * 25.4f)/160.0f + 0.5f); + info.height = ((info.yres * 25.4f)/160.0f + 0.5f); + } + + float xdpi = (info.xres * 25.4f) / info.width; + float ydpi = (info.yres * 25.4f) / info.height; + float fps = refreshRate / 1000.0f; + + LOGI( "using (fd=%d)\n" + "id = %s\n" + "xres = %d px\n" + "yres = %d px\n" + "xres_virtual = %d px\n" + "yres_virtual = %d px\n" + "bpp = %d\n" + "r = %2u:%u\n" + "g = %2u:%u\n" + "b = %2u:%u\n", + fd, + finfo.id, + info.xres, + info.yres, + info.xres_virtual, + info.yres_virtual, + info.bits_per_pixel, + info.red.offset, info.red.length, + info.green.offset, info.green.length, + info.blue.offset, info.blue.length + ); + + LOGI( "width = %d mm (%f dpi)\n" + "height = %d mm (%f dpi)\n" + "refresh rate = %.2f Hz\n", + info.width, xdpi, + info.height, ydpi, + fps + ); + + + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + if (finfo.smem_len <= 0) + return -errno; + + + module->flags = flags; + module->info = info; + module->finfo = finfo; + module->xdpi = xdpi; + module->ydpi = ydpi; + module->fps = fps; + + /* + * map the framebuffer + */ + + int err; + size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual); + module->framebuffer = new private_handle_t(dup(fd), fbSize, + private_handle_t::PRIV_FLAGS_USES_PMEM); + + module->numBuffers = info.yres_virtual / info.yres; + module->bufferMask = 0; + + void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (vaddr == MAP_FAILED) { + LOGE("Error mapping the framebuffer (%s)", strerror(errno)); + return -errno; + } + module->framebuffer->base = intptr_t(vaddr); + memset(vaddr, 0, fbSize); + return 0; +} + +static int mapFrameBuffer(struct private_module_t* module) +{ + pthread_mutex_lock(&module->lock); + int err = mapFrameBufferLocked(module); + pthread_mutex_unlock(&module->lock); + return err; +} + +/*****************************************************************************/ + +static int fb_close(struct hw_device_t *dev) +{ + fb_context_t* ctx = (fb_context_t*)dev; + if (ctx) { + free(ctx); + } + return 0; +} + +int fb_device_open(hw_module_t const* module, const char* name, + hw_device_t** device) +{ + int status = -EINVAL; + if (!strcmp(name, GRALLOC_HARDWARE_FB0)) { + alloc_device_t* gralloc_device; + status = gralloc_open(module, &gralloc_device); + if (status < 0) + return status; + + /* initialize our state here */ + fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + + /* initialize the procs */ + dev->device.common.tag = HARDWARE_DEVICE_TAG; + dev->device.common.version = 0; + dev->device.common.module = const_cast(module); + dev->device.common.close = fb_close; + dev->device.setSwapInterval = fb_setSwapInterval; + dev->device.post = fb_post; + dev->device.setUpdateRect = 0; + dev->device.compositionComplete = fb_compositionComplete; + + private_module_t* m = (private_module_t*)module; + status = mapFrameBuffer(m); + if (status >= 0) { + int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3); + const_cast(dev->device.flags) = 0; + const_cast(dev->device.width) = m->info.xres; + const_cast(dev->device.height) = m->info.yres; + const_cast(dev->device.stride) = stride; + const_cast(dev->device.format) = m->fbFormat; + const_cast(dev->device.xdpi) = m->xdpi; + const_cast(dev->device.ydpi) = m->ydpi; + const_cast(dev->device.fps) = m->fps; + const_cast(dev->device.minSwapInterval) = 1; + const_cast(dev->device.maxSwapInterval) = 1; + + if (m->finfo.reserved[0] == 0x5444 && + m->finfo.reserved[1] == 0x5055) { + dev->device.setUpdateRect = fb_setUpdateRect; + LOGD("UPDATE_ON_DEMAND supported"); + } + + *device = &dev->device.common; + } + } + return status; +} + +/* Copy a pmem buffer to the framebuffer */ + +static void +msm_copy_buffer(buffer_handle_t handle, int fd, + int width, int height, int format, + int x, int y, int w, int h) +{ + struct { + unsigned int count; + mdp_blit_req req; + } blit; + private_handle_t *priv = (private_handle_t*) handle; + + memset(&blit, 0, sizeof(blit)); + blit.count = 1; + + blit.req.flags = 0; + blit.req.alpha = 0xff; + blit.req.transp_mask = 0xffffffff; + + blit.req.src.width = width; + blit.req.src.height = height; + blit.req.src.offset = 0; + blit.req.src.memory_id = priv->fd; + + blit.req.dst.width = width; + blit.req.dst.height = height; + blit.req.dst.offset = 0; + blit.req.dst.memory_id = fd; + blit.req.dst.format = format; + + blit.req.src_rect.x = blit.req.dst_rect.x = x; + blit.req.src_rect.y = blit.req.dst_rect.y = y; + blit.req.src_rect.w = blit.req.dst_rect.w = w; + blit.req.src_rect.h = blit.req.dst_rect.h = h; + + if (ioctl(fd, MSMFB_BLIT, &blit)) + LOGE("MSMFB_BLIT failed = %d", -errno); +} diff --git a/libgralloc-legacy/gpu.cpp b/libgralloc-legacy/gpu.cpp new file mode 100644 index 0000000..90ff445 --- /dev/null +++ b/libgralloc-legacy/gpu.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2010 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 +#include +#include + +#include + +#include "gr.h" +#include "gpu.h" + +gpu_context_t::gpu_context_t(Deps& deps, PmemAllocator& pmemAllocator, + PmemAllocator& pmemAdspAllocator, const private_module_t* module) : + deps(deps), + pmemAllocator(pmemAllocator), + pmemAdspAllocator(pmemAdspAllocator) +{ + // Zero out the alloc_device_t + memset(static_cast(this), 0, sizeof(alloc_device_t)); + + // Initialize the procs + common.tag = HARDWARE_DEVICE_TAG; + common.version = 0; + common.module = const_cast(&module->base.common); + common.close = gralloc_close; + alloc = gralloc_alloc; + free = gralloc_free; +} + +int gpu_context_t::gralloc_alloc_framebuffer_locked(size_t size, int usage, + buffer_handle_t* pHandle) +{ + private_module_t* m = reinterpret_cast(common.module); + + // we don't support allocations with both the FB and PMEM_ADSP flags + if (usage & GRALLOC_USAGE_PRIVATE_PMEM_ADSP) { + return -EINVAL; + } + + // allocate the framebuffer + if (m->framebuffer == NULL) { + // initialize the framebuffer, the framebuffer is mapped once + // and forever. + int err = deps.mapFrameBufferLocked(m); + if (err < 0) { + return err; + } + } + + const uint32_t bufferMask = m->bufferMask; + const uint32_t numBuffers = m->numBuffers; + const size_t bufferSize = m->finfo.line_length * m->info.yres; + if (numBuffers == 1) { + // If we have only one buffer, we never use page-flipping. Instead, + // we return a regular buffer which will be memcpy'ed to the main + // screen when post is called. + int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; + return gralloc_alloc_buffer(bufferSize, newUsage, pHandle); + } + + if (bufferMask >= ((1LU<framebuffer->base); + private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size, + private_handle_t::PRIV_FLAGS_USES_PMEM | + private_handle_t::PRIV_FLAGS_FRAMEBUFFER); + + // find a free slot + for (uint32_t i=0 ; ibufferMask |= (1LU<base = vaddr; + hnd->offset = vaddr - intptr_t(m->framebuffer->base); + *pHandle = hnd; + + return 0; +} + + +int gpu_context_t::gralloc_alloc_framebuffer(size_t size, int usage, + buffer_handle_t* pHandle) +{ + private_module_t* m = reinterpret_cast(common.module); + pthread_mutex_lock(&m->lock); + int err = gralloc_alloc_framebuffer_locked(size, usage, pHandle); + pthread_mutex_unlock(&m->lock); + return err; +} + + +int gpu_context_t::gralloc_alloc_buffer(size_t size, int usage, buffer_handle_t* pHandle) +{ + int err = 0; + int flags = 0; + + int fd = -1; + void* base = 0; // XXX JMG: This should change to just get an address from + // the PmemAllocator rather than getting the base & offset separately + int offset = 0; + int lockState = 0; + + size = roundUpToPageSize(size); + + if (usage & GRALLOC_USAGE_HW_TEXTURE) { + // enable pmem in that case, so our software GL can fallback to + // the copybit module. + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + if (usage & GRALLOC_USAGE_HW_2D) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + if (usage & GRALLOC_USAGE_PRIVATE_PMEM_ADSP) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP; + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + private_module_t* m = reinterpret_cast(common.module); + + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) != 0 || + (flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0) { + + PmemAllocator* pma = 0; + + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) != 0) { + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0) { + LOGE("attempting to allocate a gralloc buffer with both the " + "USES_PMEM and USES_PMEM_ADSP flags. Unsetting the " + "USES_PMEM_ADSP flag."); + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP; + } + pma = &pmemAllocator; + } else { // (flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0 + pma = &pmemAdspAllocator; + } + + // PMEM buffers are always mmapped + lockState |= private_handle_t::LOCK_STATE_MAPPED; + + // Allocate the buffer from pmem + err = pma->alloc_pmem_buffer(size, usage, &base, &offset, &fd); + if (err < 0) { + if (((usage & GRALLOC_USAGE_HW_MASK) == 0) && + ((usage & GRALLOC_USAGE_PRIVATE_PMEM_ADSP) == 0)) { + // the caller didn't request PMEM, so we can try something else + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; + err = 0; + goto try_ashmem; + } else { + LOGE("couldn't open pmem (%s)", strerror(errno)); + } + } + } else { +try_ashmem: + fd = deps.ashmem_create_region("gralloc-buffer", size); + if (fd < 0) { + LOGE("couldn't create ashmem (%s)", strerror(errno)); + err = -errno; + } + } + + if (err == 0) { + private_handle_t* hnd = new private_handle_t(fd, size, flags); + hnd->offset = offset; + hnd->base = int(base)+offset; + hnd->lockState = lockState; + *pHandle = hnd; + } + + LOGE_IF(err, "gralloc failed err=%s", strerror(-err)); + + return err; +} + +static inline size_t ALIGN(size_t x, size_t align) { + return (x + align-1) & ~(align-1); +} + +int gpu_context_t::alloc_impl(int w, int h, int format, int usage, + buffer_handle_t* pHandle, int* pStride) { + if (!pHandle || !pStride) + return -EINVAL; + + size_t size, alignedw, alignedh; + + alignedw = ALIGN(w, 32); + alignedh = ALIGN(h, 32); + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + size = alignedw * alignedh * 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + size = alignedw * alignedh * 3; + break; + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + size = alignedw * alignedh * 2; + break; + + // adreno formats + case HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO: // NV21 + size = ALIGN(alignedw*alignedh, 4096); + size += ALIGN(2 * ALIGN(w/2, 32) * ALIGN(h/2, 32), 4096); + break; + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: // NV12 + // The chroma plane is subsampled, + // but the pitch in bytes is unchanged + // The GPU needs 4K alignment, but the video decoder needs 8K + alignedw = ALIGN(w, 128); + size = ALIGN( alignedw * alignedh, 8192); + size += ALIGN( alignedw * ALIGN(h/2, 32), 4096); + break; + + case HAL_PIXEL_FORMAT_YV12: + if ((w&1) || (h&1)) { + LOGE("w or h is odd for HAL_PIXEL_FORMAT_YV12"); + return -EINVAL; + } + alignedw = ALIGN(w, 16); + alignedh = h; + size = alignedw*alignedh + + (ALIGN(alignedw/2, 16) * (alignedh/2))*2; + break; + + default: + LOGE("unrecognized pixel format: %d", format); + return -EINVAL; + } + + if ((ssize_t)size <= 0) + return -EINVAL; + + int err; + if (usage & GRALLOC_USAGE_HW_FB) { + err = gralloc_alloc_framebuffer(size, usage, pHandle); + } else { + err = gralloc_alloc_buffer(size, usage, pHandle); + } + + if (err < 0) { + return err; + } + + *pStride = alignedw; + return 0; +} + +int gpu_context_t::free_impl(private_handle_t const* hnd) { + private_module_t* m = reinterpret_cast(common.module); + if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { + // free this buffer + const size_t bufferSize = m->finfo.line_length * m->info.yres; + int index = (hnd->base - m->framebuffer->base) / bufferSize; + m->bufferMask &= ~(1<flags & private_handle_t::PRIV_FLAGS_USES_PMEM) { + pmem_allocator = &pmemAllocator; + } else if (hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) { + pmem_allocator = &pmemAdspAllocator; + } + if (pmem_allocator) { + pmem_allocator->free_pmem_buffer(hnd->size, (void*)hnd->base, + hnd->offset, hnd->fd); + } + deps.terminateBuffer(&m->base, const_cast(hnd)); + } + + deps.close(hnd->fd); + delete hnd; // XXX JMG: move this to the deps + return 0; +} + +/****************************************************************************** + * Static functions + *****************************************************************************/ + +int gpu_context_t::gralloc_alloc(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride) +{ + if (!dev) { + return -EINVAL; + } + gpu_context_t* gpu = reinterpret_cast(dev); + return gpu->alloc_impl(w, h, format, usage, pHandle, pStride); +} + +int gpu_context_t::gralloc_free(alloc_device_t* dev, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + private_handle_t const* hnd = reinterpret_cast(handle); + gpu_context_t* gpu = reinterpret_cast(dev); + return gpu->free_impl(hnd); +} + +/*****************************************************************************/ + +int gpu_context_t::gralloc_close(struct hw_device_t *dev) +{ + gpu_context_t* ctx = reinterpret_cast(dev); + if (ctx) { + /* TODO: keep a list of all buffer_handle_t created, and free them + * all here. + */ + delete ctx; + } + return 0; +} + + +gpu_context_t::Deps::~Deps() {} diff --git a/libgralloc-legacy/gpu.h b/libgralloc-legacy/gpu.h new file mode 100644 index 0000000..5da7b6a --- /dev/null +++ b/libgralloc-legacy/gpu.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef GRALLOC_QSD8K_GPU_H_ +#define GRALLOC_QSD8K_GPU_H_ + +#include +#include +#include +#include + +#include +#include + +#include "gralloc_priv.h" +#include "pmemalloc.h" + + +class gpu_context_t : public alloc_device_t { + public: + + class Deps { + public: + + virtual ~Deps(); + + // ashmem + virtual int ashmem_create_region(const char *name, size_t size) = 0; + + // POSIX + virtual int close(int fd) = 0; + + // Framebuffer (locally defined) + virtual int mapFrameBufferLocked(struct private_module_t* module) = 0; + virtual int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) = 0; + }; + + gpu_context_t(Deps& deps, PmemAllocator& pmemAllocator, + PmemAllocator& pmemAdspAllocator, const private_module_t* module); + + int gralloc_alloc_framebuffer_locked(size_t size, int usage, + buffer_handle_t* pHandle); + int gralloc_alloc_framebuffer(size_t size, int usage, + buffer_handle_t* pHandle); + int gralloc_alloc_buffer(size_t size, int usage, buffer_handle_t* pHandle); + int free_impl(private_handle_t const* hnd); + int alloc_impl(int w, int h, int format, int usage, + buffer_handle_t* pHandle, int* pStride); + + static int gralloc_alloc(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride); + static int gralloc_free(alloc_device_t* dev, buffer_handle_t handle); + static int gralloc_close(struct hw_device_t *dev); + + private: + + Deps& deps; + PmemAllocator& pmemAllocator; + PmemAllocator& pmemAdspAllocator; +}; + +#endif // GRALLOC_QSD8K_GPU_H diff --git a/libgralloc-legacy/gr.h b/libgralloc-legacy/gr.h new file mode 100644 index 0000000..1775bfa --- /dev/null +++ b/libgralloc-legacy/gr.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef GR_H_ +#define GR_H_ + +#include +#ifdef HAVE_ANDROID_OS // just want PAGE_SIZE define +# include +#else +# include +#endif +#include +#include +#include +#include +#include + +#include + +/*****************************************************************************/ + +struct private_module_t; +struct private_handle_t; + +inline size_t roundUpToPageSize(size_t x) { + return (x + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); +} + +int mapFrameBufferLocked(struct private_module_t* module); +int terminateBuffer(gralloc_module_t const* module, private_handle_t* hnd); + +/*****************************************************************************/ + +class Locker { + pthread_mutex_t mutex; +public: + class Autolock { + Locker& locker; + public: + inline Autolock(Locker& locker) : locker(locker) { locker.lock(); } + inline ~Autolock() { locker.unlock(); } + }; + inline Locker() { pthread_mutex_init(&mutex, 0); } + inline ~Locker() { pthread_mutex_destroy(&mutex); } + inline void lock() { pthread_mutex_lock(&mutex); } + inline void unlock() { pthread_mutex_unlock(&mutex); } +}; + +#endif /* GR_H_ */ diff --git a/libgralloc-legacy/gralloc.cpp b/libgralloc-legacy/gralloc.cpp new file mode 100644 index 0000000..bf60324 --- /dev/null +++ b/libgralloc-legacy/gralloc.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008 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 +#include + +#include +#include +#include +#include + +#include + +#include "allocator.h" +#include "gr.h" +#include "gpu.h" + +/*****************************************************************************/ + +static int gralloc_alloc_buffer(alloc_device_t* dev, + size_t size, int usage, buffer_handle_t* pHandle); + +/*****************************************************************************/ + +int fb_device_open(const hw_module_t* module, const char* name, + hw_device_t** device); + +static int gralloc_device_open(const hw_module_t* module, const char* name, + hw_device_t** device); + +extern int gralloc_lock(gralloc_module_t const* module, + buffer_handle_t handle, int usage, + int l, int t, int w, int h, + void** vaddr); + +extern int gralloc_unlock(gralloc_module_t const* module, + buffer_handle_t handle); + +extern int gralloc_register_buffer(gralloc_module_t const* module, + buffer_handle_t handle); + +extern int gralloc_unregister_buffer(gralloc_module_t const* module, + buffer_handle_t handle); + +extern int gralloc_perform(struct gralloc_module_t const* module, + int operation, ... ); + +/*****************************************************************************/ + +/* On-device dependency implementation */ +class PmemAllocatorDepsDeviceImpl : public PmemUserspaceAllocator::Deps, + public PmemKernelAllocator::Deps { + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + pmem_region region; + int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®ion); + if (err == 0) { + *size = region.len; + } + return err; + } + + virtual int connectPmem(int fd, int master_fd) { + return ioctl(fd, PMEM_CONNECT, master_fd); + } + + virtual int mapPmem(int fd, int offset, size_t size) { + struct pmem_region sub = { offset, size }; + return ioctl(fd, PMEM_MAP, &sub); + } + + virtual int unmapPmem(int fd, int offset, size_t size) { + struct pmem_region sub = { offset, size }; + return ioctl(fd, PMEM_UNMAP, &sub); + } + + virtual int getErrno() { + return errno; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return ::mmap(start, length, prot, flags, fd, offset); + } + + virtual int munmap(void* start, size_t length) { + return ::munmap(start, length); + } + + virtual int open(const char* pathname, int flags, int mode) { + return ::open(pathname, flags, mode); + } + + virtual int close(int fd) { + return ::close(fd); + } +}; + +class GpuContextDepsDeviceImpl : public gpu_context_t::Deps { + + public: + + virtual int ashmem_create_region(const char *name, size_t size) { + return ::ashmem_create_region(name, size); + } + + virtual int mapFrameBufferLocked(struct private_module_t* module) { + return ::mapFrameBufferLocked(module); + } + + virtual int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) { + return ::terminateBuffer(module, hnd); + } + + virtual int close(int fd) { + return ::close(fd); + } +}; + +static PmemAllocatorDepsDeviceImpl pmemAllocatorDeviceDepsImpl; +static GpuContextDepsDeviceImpl gpuContextDeviceDepsImpl; + +/*****************************************************************************/ + +static SimpleBestFitAllocator pmemAllocMgr; +static PmemUserspaceAllocator pmemAllocator(pmemAllocatorDeviceDepsImpl, pmemAllocMgr, + "/dev/pmem"); + +static PmemKernelAllocator pmemAdspAllocator(pmemAllocatorDeviceDepsImpl, + "/dev/pmem_adsp"); + +/*****************************************************************************/ + +static struct hw_module_methods_t gralloc_module_methods = { + open: gralloc_device_open +}; + +struct private_module_t HAL_MODULE_INFO_SYM = { + base: { + common: { + tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 0, + id: GRALLOC_HARDWARE_MODULE_ID, + name: "Graphics Memory Allocator Module", + author: "The Android Open Source Project", + methods: &gralloc_module_methods + }, + registerBuffer: gralloc_register_buffer, + unregisterBuffer: gralloc_unregister_buffer, + lock: gralloc_lock, + unlock: gralloc_unlock, + perform: gralloc_perform, + }, + framebuffer: 0, + fbFormat: 0, + flags: 0, + numBuffers: 0, + bufferMask: 0, + lock: PTHREAD_MUTEX_INITIALIZER, + currentBuffer: 0, +}; + +/*****************************************************************************/ + +int gralloc_device_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + int status = -EINVAL; + if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { + const private_module_t* m = reinterpret_cast( + module); + gpu_context_t *dev; + dev = new gpu_context_t(gpuContextDeviceDepsImpl, pmemAllocator, + pmemAdspAllocator, m); + *device = &dev->common; + status = 0; + } else { + status = fb_device_open(module, name, device); + } + return status; +} diff --git a/libgralloc-legacy/gralloc_priv.h b/libgralloc-legacy/gralloc_priv.h new file mode 100644 index 0000000..c573941 --- /dev/null +++ b/libgralloc-legacy/gralloc_priv.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef GRALLOC_PRIV_H_ +#define GRALLOC_PRIV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +enum { + /* gralloc usage bit indicating a pmem_adsp allocation should be used */ + GRALLOC_USAGE_PRIVATE_PMEM_ADSP = GRALLOC_USAGE_PRIVATE_0, +}; + +/*****************************************************************************/ + +enum { + /* OEM specific HAL formats */ + //HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x100, // defined in hardware.h + //HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x101, // defined in hardware.h + HAL_PIXEL_FORMAT_YCbCr_422_P = 0x102, + HAL_PIXEL_FORMAT_YCbCr_420_P = 0x103, + //HAL_PIXEL_FORMAT_YCbCr_422_I = 0x104, // defined in hardware.h + HAL_PIXEL_FORMAT_YCbCr_420_I = 0x105, + HAL_PIXEL_FORMAT_CbYCrY_422_I = 0x106, + HAL_PIXEL_FORMAT_CbYCrY_420_I = 0x107, + HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED = 0x108, + HAL_PIXEL_FORMAT_YCbCr_420_SP = 0x109, + HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO = 0x10A, + HAL_PIXEL_FORMAT_YCrCb_422_SP = 0x10B, + HAL_PIXEL_FORMAT_YCrCb_420_SP_INTERLACE = 0x10C, +}; + +/*****************************************************************************/ + +struct private_module_t; +struct private_handle_t; +struct PmemAllocator; + +struct private_module_t { + gralloc_module_t base; + + struct private_handle_t* framebuffer; + uint32_t fbFormat; + uint32_t flags; + uint32_t numBuffers; + uint32_t bufferMask; + pthread_mutex_t lock; + buffer_handle_t currentBuffer; + + struct fb_var_screeninfo info; + struct fb_fix_screeninfo finfo; + float xdpi; + float ydpi; + float fps; + + enum { + // flag to indicate we'll post this buffer + PRIV_USAGE_LOCKED_FOR_POST = 0x80000000 + }; +}; + +/*****************************************************************************/ + +#ifdef __cplusplus +struct private_handle_t : public native_handle { +#else +struct private_handle_t { + native_handle_t nativeHandle; +#endif + + enum { + PRIV_FLAGS_FRAMEBUFFER = 0x00000001, + PRIV_FLAGS_USES_PMEM = 0x00000002, + PRIV_FLAGS_USES_PMEM_ADSP = 0x00000004, + PRIV_FLAGS_NEEDS_FLUSH = 0x00000008, + }; + + enum { + LOCK_STATE_WRITE = 1<<31, + LOCK_STATE_MAPPED = 1<<30, + LOCK_STATE_READ_MASK = 0x3FFFFFFF + }; + + // file-descriptors + int fd; + // ints + int magic; + int flags; + int size; + int offset; + int gpu_fd; // stored as an int, b/c we don't want it marshalled + + // FIXME: the attributes below should be out-of-line + int base; + int lockState; + int writeOwner; + int gpuaddr; // The gpu address mapped into the mmu. If using ashmem, set to 0 They don't care + int pid; + +#ifdef __cplusplus + static const int sNumInts = 10; + static const int sNumFds = 1; + static const int sMagic = 'gmsm'; + + private_handle_t(int fd, int size, int flags) : + fd(fd), magic(sMagic), flags(flags), size(size), offset(0), gpu_fd(-1), + base(0), lockState(0), writeOwner(0), gpuaddr(0), pid(getpid()) + { + version = sizeof(native_handle); + numInts = sNumInts; + numFds = sNumFds; + } + ~private_handle_t() { + magic = 0; + } + + bool usesPhysicallyContiguousMemory() { + return (flags & PRIV_FLAGS_USES_PMEM) != 0; + } + + static int validate(const native_handle* h) { + const private_handle_t* hnd = (const private_handle_t*)h; + if (!h || h->version != sizeof(native_handle) || + h->numInts != sNumInts || h->numFds != sNumFds || + hnd->magic != sMagic) + { + LOGE("invalid gralloc handle (at %p)", h); + return -EINVAL; + } + return 0; + } + + static private_handle_t* dynamicCast(const native_handle* in) { + if (validate(in) == 0) { + return (private_handle_t*) in; + } + return NULL; + } +#endif +}; + +#endif /* GRALLOC_PRIV_H_ */ diff --git a/libgralloc-legacy/mapper.cpp b/libgralloc-legacy/mapper.cpp new file mode 100644 index 0000000..6de7346 --- /dev/null +++ b/libgralloc-legacy/mapper.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "gralloc_priv.h" + + +// we need this for now because pmem cannot mmap at an offset +#define PMEM_HACK 1 + +/* desktop Linux needs a little help with gettid() */ +#if defined(ARCH_X86) && !defined(HAVE_ANDROID_OS) +#define __KERNEL__ +# include +pid_t gettid() { return syscall(__NR_gettid);} +#undef __KERNEL__ +#endif + +/*****************************************************************************/ + +static int gralloc_map(gralloc_module_t const* module, + buffer_handle_t handle, + void** vaddr) +{ + private_handle_t* hnd = (private_handle_t*)handle; + if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { + size_t size = hnd->size; +#if PMEM_HACK + size += hnd->offset; +#endif + void* mappedAddress = mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0); + if (mappedAddress == MAP_FAILED) { + LOGE("Could not mmap handle %p, fd=%d (%s)", + handle, hnd->fd, strerror(errno)); + hnd->base = 0; + return -errno; + } + hnd->base = intptr_t(mappedAddress) + hnd->offset; + //LOGD("gralloc_map() succeeded fd=%d, off=%d, size=%d, vaddr=%p", + // hnd->fd, hnd->offset, hnd->size, mappedAddress); + } + *vaddr = (void*)hnd->base; + return 0; +} + +static int gralloc_unmap(gralloc_module_t const* module, + buffer_handle_t handle) +{ + private_handle_t* hnd = (private_handle_t*)handle; + if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { + void* base = (void*)hnd->base; + size_t size = hnd->size; +#if PMEM_HACK + base = (void*)(intptr_t(base) - hnd->offset); + size += hnd->offset; +#endif + //LOGD("unmapping from %p, size=%d", base, size); + if (munmap(base, size) < 0) { + LOGE("Could not unmap %s", strerror(errno)); + } + } + hnd->base = 0; + return 0; +} + +/*****************************************************************************/ + +static pthread_mutex_t sMapLock = PTHREAD_MUTEX_INITIALIZER; + +/*****************************************************************************/ + +int gralloc_register_buffer(gralloc_module_t const* module, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + // In this implementation, we don't need to do anything here + + /* NOTE: we need to initialize the buffer as not mapped/not locked + * because it shouldn't when this function is called the first time + * in a new process. Ideally these flags shouldn't be part of the + * handle, but instead maintained in the kernel or at least + * out-of-line + */ + + // if this handle was created in this process, then we keep it as is. + private_handle_t* hnd = (private_handle_t*)handle; + if (hnd->pid != getpid()) { + hnd->base = 0; + hnd->lockState = 0; + hnd->writeOwner = 0; + } + return 0; +} + +int gralloc_unregister_buffer(gralloc_module_t const* module, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + /* + * If the buffer has been mapped during a lock operation, it's time + * to un-map it. It's an error to be here with a locked buffer. + * NOTE: the framebuffer is handled differently and is never unmapped. + */ + + private_handle_t* hnd = (private_handle_t*)handle; + + LOGE_IF(hnd->lockState & private_handle_t::LOCK_STATE_READ_MASK, + "[unregister] handle %p still locked (state=%08x)", + hnd, hnd->lockState); + + // never unmap buffers that were created in this process + if (hnd->pid != getpid()) { + if (hnd->lockState & private_handle_t::LOCK_STATE_MAPPED) { + gralloc_unmap(module, handle); + } + hnd->base = 0; + hnd->lockState = 0; + hnd->writeOwner = 0; + } + return 0; +} + +int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) +{ + /* + * If the buffer has been mapped during a lock operation, it's time + * to un-map it. It's an error to be here with a locked buffer. + */ + + LOGE_IF(hnd->lockState & private_handle_t::LOCK_STATE_READ_MASK, + "[terminate] handle %p still locked (state=%08x)", + hnd, hnd->lockState); + + if (hnd->lockState & private_handle_t::LOCK_STATE_MAPPED) { + // this buffer was mapped, unmap it now + if ((hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM) || + (hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP)) { + if (hnd->pid != getpid()) { + // ... unless it's a "master" pmem buffer, that is a buffer + // mapped in the process it's been allocated. + // (see gralloc_alloc_buffer()) + gralloc_unmap(module, hnd); + } + } else { + gralloc_unmap(module, hnd); + } + } + + return 0; +} + +int gralloc_lock(gralloc_module_t const* module, + buffer_handle_t handle, int usage, + int l, int t, int w, int h, + void** vaddr) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + int err = 0; + private_handle_t* hnd = (private_handle_t*)handle; + int32_t current_value, new_value; + int retry; + + do { + current_value = hnd->lockState; + new_value = current_value; + + if (current_value & private_handle_t::LOCK_STATE_WRITE) { + // already locked for write + LOGE("handle %p already locked for write", handle); + return -EBUSY; + } else if (current_value & private_handle_t::LOCK_STATE_READ_MASK) { + // already locked for read + if (usage & (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_HW_RENDER)) { + LOGE("handle %p already locked for read", handle); + return -EBUSY; + } else { + // this is not an error + //LOGD("%p already locked for read... count = %d", + // handle, (current_value & ~(1<<31))); + } + } + + // not currently locked + if (usage & (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_HW_RENDER)) { + // locking for write + new_value |= private_handle_t::LOCK_STATE_WRITE; + } + new_value++; + + retry = android_atomic_cmpxchg(current_value, new_value, + (volatile int32_t*)&hnd->lockState); + } while (retry); + + if (new_value & private_handle_t::LOCK_STATE_WRITE) { + // locking for write, store the tid + hnd->writeOwner = gettid(); + } + + // if requesting sw write for non-framebuffer handles, flag for + // flushing at unlock + + const uint32_t pmemMask = + private_handle_t::PRIV_FLAGS_USES_PMEM | + private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP; + + if ((usage & GRALLOC_USAGE_SW_WRITE_MASK) && + (hnd->flags & pmemMask) && + !(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { + hnd->flags |= private_handle_t::PRIV_FLAGS_NEEDS_FLUSH; + } + + if (usage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) { + if (!(current_value & private_handle_t::LOCK_STATE_MAPPED)) { + // we need to map for real + pthread_mutex_t* const lock = &sMapLock; + pthread_mutex_lock(lock); + if (!(hnd->lockState & private_handle_t::LOCK_STATE_MAPPED)) { + err = gralloc_map(module, handle, vaddr); + if (err == 0) { + android_atomic_or(private_handle_t::LOCK_STATE_MAPPED, + (volatile int32_t*)&(hnd->lockState)); + } + } + pthread_mutex_unlock(lock); + } + *vaddr = (void*)hnd->base; + } + + return err; +} + +int gralloc_unlock(gralloc_module_t const* module, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + private_handle_t* hnd = (private_handle_t*)handle; + int32_t current_value, new_value; + + if (hnd->flags & private_handle_t::PRIV_FLAGS_NEEDS_FLUSH) { + struct pmem_region region; + int err; + + region.offset = hnd->offset; + region.len = hnd->size; + err = ioctl(hnd->fd, PMEM_CACHE_FLUSH, ®ion); + LOGE_IF(err < 0, "cannot flush handle %p (offs=%x len=%x)\n", + hnd, hnd->offset, hnd->size); + hnd->flags &= ~private_handle_t::PRIV_FLAGS_NEEDS_FLUSH; + } + + do { + current_value = hnd->lockState; + new_value = current_value; + + if (current_value & private_handle_t::LOCK_STATE_WRITE) { + // locked for write + if (hnd->writeOwner == gettid()) { + hnd->writeOwner = 0; + new_value &= ~private_handle_t::LOCK_STATE_WRITE; + } + } + + if ((new_value & private_handle_t::LOCK_STATE_READ_MASK) == 0) { + LOGE("handle %p not locked", handle); + return -EINVAL; + } + + new_value--; + + } while (android_atomic_cmpxchg(current_value, new_value, + (volatile int32_t*)&hnd->lockState)); + + return 0; +} + +/*****************************************************************************/ + +int gralloc_perform(struct gralloc_module_t const* module, + int operation, ... ) +{ + int res = -EINVAL; + return res; +} diff --git a/libgralloc-legacy/pmemalloc.cpp b/libgralloc-legacy/pmemalloc.cpp new file mode 100644 index 0000000..e42e898 --- /dev/null +++ b/libgralloc-legacy/pmemalloc.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2010 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. + */ + +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "gralloc_priv.h" +#include "pmemalloc.h" + + +#define BEGIN_FUNC LOGV("%s begin", __PRETTY_FUNCTION__) +#define END_FUNC LOGV("%s end", __PRETTY_FUNCTION__) + + +static int get_open_flags(int usage) { + int openFlags = O_RDWR | O_SYNC; + uint32_t uread = usage & GRALLOC_USAGE_SW_READ_MASK; + uint32_t uwrite = usage & GRALLOC_USAGE_SW_WRITE_MASK; + if (uread == GRALLOC_USAGE_SW_READ_OFTEN || + uwrite == GRALLOC_USAGE_SW_WRITE_OFTEN) { + openFlags &= ~O_SYNC; + } + return openFlags; +} + +PmemAllocator::~PmemAllocator() +{ + BEGIN_FUNC; + END_FUNC; +} + + +PmemUserspaceAllocator::PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev): + deps(deps), + allocator(allocator), + pmemdev(pmemdev), + master_fd(MASTER_FD_INIT) +{ + BEGIN_FUNC; + pthread_mutex_init(&lock, NULL); + END_FUNC; +} + + +PmemUserspaceAllocator::~PmemUserspaceAllocator() +{ + BEGIN_FUNC; + END_FUNC; +} + + +void* PmemUserspaceAllocator::get_base_address() { + BEGIN_FUNC; + END_FUNC; + return master_base; +} + + +int PmemUserspaceAllocator::init_pmem_area_locked() +{ + BEGIN_FUNC; + int err = 0; + int fd = deps.open(pmemdev, O_RDWR, 0); + if (fd >= 0) { + size_t size = 0; + err = deps.getPmemTotalSize(fd, &size); + if (err < 0) { + LOGE("%s: PMEM_GET_TOTAL_SIZE failed (%d), limp mode", pmemdev, + err); + size = 8<<20; // 8 MiB + } + allocator.setSize(size); + + void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, + 0); + if (base == MAP_FAILED) { + LOGE("%s: failed to map pmem master fd: %s", pmemdev, + strerror(deps.getErrno())); + err = -deps.getErrno(); + base = 0; + deps.close(fd); + fd = -1; + } else { + master_fd = fd; + master_base = base; + } + } else { + LOGE("%s: failed to open pmem device: %s", pmemdev, + strerror(deps.getErrno())); + err = -deps.getErrno(); + } + END_FUNC; + return err; +} + + +int PmemUserspaceAllocator::init_pmem_area() +{ + BEGIN_FUNC; + pthread_mutex_lock(&lock); + int err = master_fd; + if (err == MASTER_FD_INIT) { + // first time, try to initialize pmem + err = init_pmem_area_locked(); + if (err) { + LOGE("%s: failed to initialize pmem area", pmemdev); + master_fd = err; + } + } else if (err < 0) { + // pmem couldn't be initialized, never use it + } else { + // pmem OK + err = 0; + } + pthread_mutex_unlock(&lock); + END_FUNC; + return err; +} + + +int PmemUserspaceAllocator::alloc_pmem_buffer(size_t size, int usage, + void** pBase, int* pOffset, int* pFd) +{ + BEGIN_FUNC; + int err = init_pmem_area(); + if (err == 0) { + void* base = master_base; + int offset = allocator.allocate(size); + if (offset < 0) { + // no more pmem memory + LOGE("%s: no more pmem available", pmemdev); + err = -ENOMEM; + } else { + int openFlags = get_open_flags(usage); + + //LOGD("%s: allocating pmem at offset 0x%p", pmemdev, offset); + + // now create the "sub-heap" + int fd = deps.open(pmemdev, openFlags, 0); + err = fd < 0 ? fd : 0; + + // and connect to it + if (err == 0) + err = deps.connectPmem(fd, master_fd); + + // and make it available to the client process + if (err == 0) + err = deps.mapPmem(fd, offset, size); + + if (err < 0) { + LOGE("%s: failed to initialize pmem sub-heap: %d", pmemdev, + err); + err = -deps.getErrno(); + deps.close(fd); + allocator.deallocate(offset); + fd = -1; + } else { + LOGV("%s: mapped fd %d at offset %d, size %d", pmemdev, fd, offset, size); + memset((char*)base + offset, 0, size); + *pBase = base; + *pOffset = offset; + *pFd = fd; + } + //LOGD_IF(!err, "%s: allocating pmem size=%d, offset=%d", pmemdev, size, offset); + } + } + END_FUNC; + return err; +} + + +int PmemUserspaceAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd) +{ + BEGIN_FUNC; + int err = 0; + if (fd >= 0) { + int err = deps.unmapPmem(fd, offset, size); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), fd=%d, sub.offset=%u, " + "sub.size=%u", strerror(deps.getErrno()), fd, offset, size); + if (err == 0) { + // we can't deallocate the memory in case of UNMAP failure + // because it would give that process access to someone else's + // surfaces, which would be a security breach. + allocator.deallocate(offset); + } + } + END_FUNC; + return err; +} + +PmemUserspaceAllocator::Deps::Allocator::~Allocator() +{ + BEGIN_FUNC; + END_FUNC; +} + +PmemUserspaceAllocator::Deps::~Deps() +{ + BEGIN_FUNC; + END_FUNC; +} + +PmemKernelAllocator::PmemKernelAllocator(Deps& deps, const char* pmemdev): + deps(deps), + pmemdev(pmemdev) +{ + BEGIN_FUNC; + END_FUNC; +} + + +PmemKernelAllocator::~PmemKernelAllocator() +{ + BEGIN_FUNC; + END_FUNC; +} + + +void* PmemKernelAllocator::get_base_address() { + BEGIN_FUNC; + END_FUNC; + return 0; +} + + +static unsigned clp2(unsigned x) { + x = x - 1; + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >>16); + return x + 1; +} + + +int PmemKernelAllocator::alloc_pmem_buffer(size_t size, int usage, + void** pBase,int* pOffset, int* pFd) +{ + BEGIN_FUNC; + + *pBase = 0; + *pOffset = 0; + *pFd = -1; + + int err; + int openFlags = get_open_flags(usage); + int fd = deps.open(pmemdev, openFlags, 0); + if (fd < 0) { + err = -deps.getErrno(); + END_FUNC; + return err; + } + + // The size should already be page aligned, now round it up to a power of 2. + size = clp2(size); + + void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("%s: failed to map pmem fd: %s", pmemdev, + strerror(deps.getErrno())); + err = -deps.getErrno(); + deps.close(fd); + END_FUNC; + return err; + } + + memset(base, 0, size); + + *pBase = base; + *pOffset = 0; + *pFd = fd; + + END_FUNC; + return 0; +} + + +int PmemKernelAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd) +{ + BEGIN_FUNC; + // The size should already be page aligned, now round it up to a power of 2 + // like we did when allocating. + size = clp2(size); + + int err = deps.munmap(base, size); + if (err < 0) { + err = deps.getErrno(); + LOGW("%s: error unmapping pmem fd: %s", pmemdev, strerror(err)); + return -err; + } + END_FUNC; + return 0; +} + +PmemKernelAllocator::Deps::~Deps() +{ + BEGIN_FUNC; + END_FUNC; +} diff --git a/libgralloc-legacy/pmemalloc.h b/libgralloc-legacy/pmemalloc.h new file mode 100644 index 0000000..b0f45c7 --- /dev/null +++ b/libgralloc-legacy/pmemalloc.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef GRALLOC_QSD8K_PMEMALLOC_H +#define GRALLOC_QSD8K_PMEMALLOC_H + +#include +#include +#include +#include + + +/** + * An interface to the PMEM allocators. + */ +class PmemAllocator { + + public: + + virtual ~PmemAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address() = 0; + + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd) = 0; + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd) = 0; +}; + + +/** + * A PMEM allocator that allocates the entire pmem memory from the kernel and + * then uses a user-space allocator to suballocate from that. This requires + * that the PMEM device driver have kernel allocation disabled. + */ +class PmemUserspaceAllocator: public PmemAllocator { + + public: + + class Deps { + public: + + class Allocator { + public: + virtual ~Allocator(); + virtual ssize_t setSize(size_t size) = 0; + virtual size_t size() const = 0; + virtual ssize_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual ssize_t deallocate(size_t offset) = 0; + }; + + virtual ~Deps(); + + // pmem + virtual size_t getPmemTotalSize(int fd, size_t* size) = 0; + virtual int connectPmem(int fd, int master_fd) = 0; + virtual int mapPmem(int fd, int offset, size_t size) = 0; + virtual int unmapPmem(int fd, int offset, size_t size) = 0; + + // C99 + virtual int getErrno() = 0; + + // POSIX + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) = 0; + virtual int open(const char* pathname, int flags, int mode) = 0; + virtual int close(int fd) = 0; + }; + + PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev); + virtual ~PmemUserspaceAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address(); + + virtual int init_pmem_area_locked(); + virtual int init_pmem_area(); + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd); + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd); + +#ifndef ANDROID_OS + // DO NOT USE: For testing purposes only. + void set_master_values(int fd, void* base) { + master_fd = fd; + master_base = base; + } +#endif // ANDROID_OS + + private: + + enum { + MASTER_FD_INIT = -1, + }; + + Deps& deps; + Deps::Allocator& allocator; + + pthread_mutex_t lock; + const char* pmemdev; + int master_fd; + void* master_base; +}; + + +/** + * A PMEM allocator that allocates each individual allocation from the kernel + * (using the kernel's allocator). This requires the kernel driver for the + * particular PMEM device being allocated from to support kernel allocation. + */ +class PmemKernelAllocator: public PmemAllocator { + + public: + + class Deps { + public: + + virtual ~Deps(); + + // C99 + virtual int getErrno() = 0; + + // POSIX + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) = 0; + virtual int munmap(void* start, size_t length) = 0; + virtual int open(const char* pathname, int flags, int mode) = 0; + virtual int close(int fd) = 0; + }; + + PmemKernelAllocator(Deps& deps, const char* pmemdev); + virtual ~PmemKernelAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address(); + + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd); + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd); + + private: + + Deps& deps; + + const char* pmemdev; +}; + +#endif // GRALLOC_QSD8K_PMEMALLOC_H diff --git a/libgralloc-legacy/tests/Android.mk b/libgralloc-legacy/tests/Android.mk new file mode 100644 index 0000000..b9a7459 --- /dev/null +++ b/libgralloc-legacy/tests/Android.mk @@ -0,0 +1,55 @@ +# Copyright (C) 2008 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. + +LOCAL_PATH := $(call my-dir) + +# you can use EXTRA_CFLAGS to indicate additional CFLAGS to use +# in the build. The variables will be cleaned on exit +# +# + +libgralloc_test_includes:= \ + bionic/libstdc++/include \ + external/astl/include \ + external/gtest/include \ + $(LOCAL_PATH)/.. + +libgralloc_test_static_libs := \ + libgralloc_qsd8k_host \ + libgtest_main_host \ + libgtest_host \ + libastl_host \ + liblog + +define host-test + $(foreach file,$(1), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_CPP_EXTENSION := .cpp) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_C_INCLUDES := $(libgralloc_test_includes)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_CFLAGS += $(EXTRA_CFLAGS)) \ + $(eval LOCAL_LDLIBS += $(EXTRA_LDLIBS)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(libgralloc_test_static_libs)) \ + $(eval LOCAL_MODULE_TAGS := eng tests) \ + $(eval include $(BUILD_HOST_EXECUTABLE)) \ + ) \ + $(eval EXTRA_CFLAGS :=) \ + $(eval EXTRA_LDLIBS :=) +endef + +TEST_SRC_FILES := \ + pmemalloc_test.cpp + +$(call host-test, $(TEST_SRC_FILES)) diff --git a/libgralloc-legacy/tests/pmemalloc_test.cpp b/libgralloc-legacy/tests/pmemalloc_test.cpp new file mode 100644 index 0000000..94e86bf --- /dev/null +++ b/libgralloc-legacy/tests/pmemalloc_test.cpp @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2010 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 + +#include +#include +#include +#include + +#include "pmemalloc.h" + +class DepsStub : public PmemUserspaceAllocator::Deps, public PmemKernelAllocator::Deps { + + public: + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + return 0; + } + + virtual int connectPmem(int fd, int master_fd) { + return 0; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + return 0; + } + + virtual int unmapPmem(int fd, int offset, size_t size) { + return 0; + } + + virtual int getErrno() { + return 0; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return 0; + } + + virtual int munmap(void* start, size_t length) { + return 0; + } + + virtual int open(const char* pathname, int flags, int mode) { + return 0; + } + + virtual int close(int fd) { + return 0; + } +}; + +/******************************************************************************/ + +class AllocatorStub : public PmemUserspaceAllocator::Deps::Allocator { + virtual ssize_t setSize(size_t size) { + return 0; + } + + virtual size_t size() const { + return 0; + } + + virtual ssize_t allocate(size_t size, uint32_t flags = 0) { + return 0; + } + + virtual ssize_t deallocate(size_t offset) { + return 0; + } +}; + +/******************************************************************************/ + +static const char* fakePmemDev = "/foo/bar"; + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithSuccessfulCompletion : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + *size = 16 << 20; + return 0; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + EXPECT_EQ(1234, fd); + return (void*)0x87654321; + } + +}; + +struct Allocator_InitPmemAreaLockedWithSuccessfulCompletion : public AllocatorStub { + + virtual ssize_t setSize(size_t size) { + EXPECT_EQ(size_t(16 << 20), size); + return 0; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWithSuccessfulCompletion) { + Deps_InitPmemAreaLockedWithSuccessfulCompletion depsMock; + Allocator_InitPmemAreaLockedWithSuccessfulCompletion allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(0, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnMmap : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + *size = 16 << 20; + return 0; + } + + virtual int getErrno() { + return ENOMEM; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return (void*)MAP_FAILED; + } + +}; + +struct Allocator_InitPmemAreaLockedWithEnomemOnMmap : public AllocatorStub { + + virtual ssize_t setSize(size_t size) { + EXPECT_EQ(size_t(16 << 20), size); + return 0; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWthEnomemOnMmap) { + Deps_InitPmemAreaLockedWithEnomemOnMmap depsMock; + Allocator_InitPmemAreaLockedWithEnomemOnMmap allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEaccesOnGetPmemTotalSize : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + return -EACCES; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWthEaccesOnGetPmemTotalSize) { + Deps_InitPmemAreaLockedWithEaccesOnGetPmemTotalSize depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEaccesOnOpen : public DepsStub { + + virtual int getErrno() { + return EACCES; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return -1; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWithEaccesOnOpenMaster) { + Deps_InitPmemAreaLockedWithEaccesOnOpen depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithSuccessfulCompletion Deps_InitPmemAreaWithSuccessfulInitialCompletion; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaWithSuccessfulInitialCompletion) { + Deps_InitPmemAreaWithSuccessfulInitialCompletion depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area(); + ASSERT_EQ(0, result); +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithEaccesOnOpen Deps_InitPmemAreaWithEaccesOnInitLocked; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaWithEaccesOnInitLocked) { + Deps_InitPmemAreaWithEaccesOnInitLocked depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +TEST(test_pmem_userspace_allocator, testInitPmemAreaAfterSuccessfulInitialCompletion) { + DepsStub depsStub; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsStub, allocStub, fakePmemDev); + + pma.set_master_values(1234, 0); // Indicate that the pma has been successfully init'd + + int result = pma.init_pmem_area(); + ASSERT_EQ(0, result); + //XXX JMG: Add this back in maybe? ASSERT_EQ(1234, pmi.master); // Make sure the master fd wasn't changed +} + +/******************************************************************************/ + +TEST(test_pmem_userspace_allocator, testInitPmemAreaAfterFailedInit) { + DepsStub depsStub; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsStub, allocStub, fakePmemDev); + + pma.set_master_values(-EACCES, 0); // Indicate that the pma has failed init + + int result = pma.init_pmem_area(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual int connectPmem(int fd, int master_fd) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(1234, master_fd); + return 0; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(0x300, offset); + EXPECT_EQ(size_t(0x100), size); + return 0; + } +}; + + +struct Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags : public AllocatorStub { + + virtual ssize_t allocate(size_t size, uint32_t flags = 0) { + EXPECT_EQ(size_t(0x100), size); + EXPECT_EQ(uint32_t(0x0), flags); + return 0x300; + } +}; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithNoFlags) { + Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags depsMock; + Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = 0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(0x300, offset); + ASSERT_EQ(5678, fd); + for (int i = 0x300; i < 0x400; ++i) { + ASSERT_EQ(uint8_t(0), buf[i]); + } +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags Deps_InitPmemAreaLockedWithSuccessfulCompletionWithAllFlags; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithSuccessfulCompletionWithAllFlags; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithAllFlags) { + Deps_InitPmemAreaLockedWithSuccessfulCompletionWithAllFlags depsMock; + Allocator_AllocPmemBufferWithSuccessfulCompletionWithAllFlags allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(0x300, offset); + ASSERT_EQ(5678, fd); + for (int i = 0x300; i < 0x400; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnodevOnOpen : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENODEV; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnodevOnOpen; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithEnodevOnOpen) { + Deps_InitPmemAreaLockedWithEnodevOnOpen depsMock; + Allocator_AllocPmemBufferWithEnodevOnOpen allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENODEV, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnConnectPmem : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENOMEM; + } + + virtual int connectPmem(int fd, int master_fd) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(1234, master_fd); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnomemOnConnectPmem; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithEnomemOnConnectPmem) { + Deps_InitPmemAreaLockedWithEnomemOnConnectPmem depsMock; + Allocator_AllocPmemBufferWithEnomemOnConnectPmem allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnMapPmem : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENOMEM; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(0x300, offset); + EXPECT_EQ(size_t(0x100), size); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnomemOnMapPmem; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithEnomemOnMapPmem) { + Deps_InitPmemAreaLockedWithEnomemOnMapPmem depsMock; + Allocator_AllocPmemBufferWithEnomemOnMapPmem allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags : public DepsStub { + + void* mmapResult; + + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags(void* mmapResult) : + mmapResult(mmapResult) {} + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + EXPECT_EQ(5678, fd); + return mmapResult; + } +}; + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithSuccessfulCompletionWithNoFlags) { + uint8_t buf[0x100]; // Create a buffer to get memzero'd + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags depsMock(buf); + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = 0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(buf, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(5678, fd); + for (int i = 0; i < 0x100; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +typedef Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithAllFlags; + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithSuccessfulCompletionWithAllFlags) { + uint8_t buf[0x100]; // Create a buffer to get memzero'd + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithAllFlags depsMock(buf); + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(buf, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(5678, fd); + for (int i = 0; i < 0x100; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithEpermOnOpen : public DepsStub { + + virtual int getErrno() { + return EPERM; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return -1; + } +}; + + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithEpermOnOpen) { + Deps_KernelAllocPmemBufferWithEpermOnOpen depsMock; + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-EPERM, result); + ASSERT_EQ(0, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(-1, fd); +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithEnomemOnMmap : DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return (void*)MAP_FAILED; + } + + virtual int getErrno() { + return ENOMEM; + } +}; + + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithEnomemOnMmap) { + Deps_KernelAllocPmemBufferWithEnomemOnMmap depsMock; + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); + ASSERT_EQ(0, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(-1, fd); +} + +/******************************************************************************/