Map buffers to GPU and execute command buffers.

Change-Id: Ifb61d1a575b8e908cedcba961e4e3d3bb8ebe6ee
diff --git a/media_driver/cmake/linux/media_compile_flags_linux.cmake b/media_driver/cmake/linux/media_compile_flags_linux.cmake
index c5c42ca..ae8481b 100755
--- a/media_driver/cmake/linux/media_compile_flags_linux.cmake
+++ b/media_driver/cmake/linux/media_compile_flags_linux.cmake
@@ -96,6 +96,8 @@
     set(MEDIA_COMPILER_FLAGS_COMMON
         ${MEDIA_COMPILER_FLAGS_COMMON}
         -DUSE_MAGMA=1
+        -Wthread-safety
+        -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1
     )
     include_directories(
         "$ENV{FUCHSIA_DIR}/src/graphics/lib/magma/include/magma_abi"
diff --git a/media_driver/linux/common/os/magma/address_space_allocator.h b/media_driver/linux/common/os/magma/address_space_allocator.h
new file mode 100644
index 0000000..29f4a84
--- /dev/null
+++ b/media_driver/linux/common/os/magma/address_space_allocator.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2021 The Fuchsia Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef ADDRESS_SPACE_ALLOCATOR_H
+#define ADDRESS_SPACE_ALLOCATOR_H
+
+#include <assert.h>
+#include <limits.h>
+
+class AddressSpaceAllocator {
+ public:
+  // Constructs an address space ranging from address base to address base + size.
+  AddressSpaceAllocator(uint64_t base, size_t size) : base_(base), size_(size) {
+    assert(size > 0);
+    assert(size <= UINT64_MAX - base);
+  }
+
+  virtual ~AddressSpaceAllocator() = default;
+
+  uint64_t base() const { return base_; }
+  size_t size() const { return size_; }
+
+  // Allocates an address for a region of the given size and alignment, where alignment
+  // is specified by 2 << align_pow2.
+  // If alignment is less than a page than page alignment will be used.
+  // On success returns true and addr_out is set; otherwise returns false.
+  virtual bool Alloc(size_t size, uint8_t align_pow2, uint64_t* addr_out) = 0;
+
+  // Frees an address that was previously allocated.
+  virtual bool Free(uint64_t addr) = 0;
+
+  // Returns true and the size of the region if mapped; otherwise returns false.
+  virtual bool GetSize(uint64_t addr, size_t* size_out) = 0;
+
+  static inline uint32_t GetPageSize() { return PAGE_SIZE; }
+
+  static inline bool IsPageAligned(uint64_t val) { return (val & (GetPageSize() - 1)) == 0; }
+
+  template <class T>
+  static bool IsPow2(T v, T* exponent = nullptr) {
+    uint32_t pow = 0;
+    if (v == 0) {
+      return false;
+    }
+
+    for (uint32_t count = 0; v != 0; count += 1) {
+      if (v & 1) {
+        if (pow != 0) {
+          return false;
+        }
+        pow = count;
+      }
+      v >>= 1;
+    }
+
+    if (exponent != nullptr) {
+      *exponent = pow;
+    }
+
+    return true;
+  }
+
+  static inline uint32_t PageShift() {
+    uint32_t exponent = 0;
+    assert(IsPow2(GetPageSize(), &exponent));
+
+    return exponent;
+  }
+
+  // Note, alignment must be a power of 2
+  template <class T>
+  static inline T RoundUp(T val, T alignment) {
+    assert(IsPow2(alignment));
+    return ((val - 1) | (alignment - 1)) + 1;
+  }
+
+ private:
+  uint64_t base_;
+  size_t size_;
+};
+
+#endif  // ADDRESS_SPACE_ALLOCATOR_H
diff --git a/media_driver/linux/common/os/magma/media_srcs.cmake b/media_driver/linux/common/os/magma/media_srcs.cmake
index 93ac76b..73f9e46 100644
--- a/media_driver/linux/common/os/magma/media_srcs.cmake
+++ b/media_driver/linux/common/os/magma/media_srcs.cmake
@@ -23,6 +23,7 @@
 set(TMP_SOURCES_
     ${CMAKE_CURRENT_LIST_DIR}/mos_bufmgr_magma.cpp
     ${CMAKE_CURRENT_LIST_DIR}/mos_bufmgr_stub.c
+    ${CMAKE_CURRENT_LIST_DIR}/simple_allocator.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../i915/mos_bufmgr_api.c
 )
 
diff --git a/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp b/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp
index eec5465..ecee046 100644
--- a/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp
+++ b/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp
@@ -27,10 +27,15 @@
 
 #include "mos_bufmgr.h"
 #include "mos_bufmgr_priv.h"
+#include "simple_allocator.h"
 #include <magma.h>
+#include <magma_intel_gen_defs.h>
 #include <magma_fd.h>
 #include <assert.h>
 #include <stdio.h>
+#include <unistd.h>
+#include <mutex>
+#include <vector>
 
 #define LOG_VERBOSE(msg, ...) do { \
     if (true) \
@@ -38,6 +43,7 @@
         fflush(stderr); \
 } while (0)
 
+static inline uint64_t page_size() { return sysconf(_SC_PAGESIZE); }
 
 class MagmaBo;
 
@@ -50,11 +56,23 @@
 
     uint32_t device_id() const { return device_id_; }
 
+    bool Alloc(size_t size, uint8_t align_pow2, uint64_t* addr_out) {
+        std::lock_guard<std::mutex> lock(allocator_mutex_);
+        return allocator_->Alloc(size, align_pow2, addr_out);
+    }
+
+    bool Free(uint64_t addr) {
+        std::lock_guard<std::mutex> lock(allocator_mutex_);
+        return allocator_->Free(addr);
+    }
+
     MagmaBo* CreateBo(const char *name, unsigned long size);
 
 private:
     magma_connection_t connection_ {};
     uint32_t device_id_;
+    std::mutex allocator_mutex_;
+    std::unique_ptr<SimpleAllocator> allocator_ __attribute__((__guarded_by__(allocator_mutex_)));
 };
 
 
@@ -80,15 +98,32 @@
 
     MagmaBufMgr* magma_bufmgr() const { return static_cast<MagmaBufMgr*>(bufmgr); }
 
+    uint64_t id() { return id_; }
+
     magma_buffer_t buffer() const { return buffer_; }
 
+    bool is_mapped_gpu() { return is_mapped_gpu_; }
+
+    std::vector<magma_exec_resource>& exec_resources() { return exec_resources_; }
+
+    bool MapGpu();
+
+    void AddExecResource(MagmaBo* bo);
+
+    void clear_exec_resources() { exec_resources_.clear(); }
+
 private:
     std::atomic_int ref_count_{};
     magma_buffer_t buffer_ {};
+    uint64_t id_ {};
+    bool is_mapped_gpu_= false;
+    std::vector<magma_exec_resource> exec_resources_;
 };
 
 
-MagmaBo::MagmaBo(magma_buffer_t buffer, uint64_t size, MagmaBufMgr* bufmgr) : buffer_(buffer) {
+MagmaBo::MagmaBo(magma_buffer_t buffer, uint64_t size, MagmaBufMgr* bufmgr) :
+    buffer_(buffer), id_(magma_get_buffer_id(buffer)) {
+
     mos_linux_bo::size = size;
     assert(mos_linux_bo::size == size);
     mos_linux_bo::align = 0;
@@ -104,6 +139,53 @@
 
 MagmaBo::~MagmaBo() {
     magma_release_buffer(magma_bufmgr()->connection(), buffer());
+
+    if (is_mapped_gpu_) {
+        magma_bufmgr()->Free(offset64);
+    }
+}
+
+void MagmaBo::AddExecResource(MagmaBo* bo) {
+    if (exec_resources_.empty()) {
+        // Add this as the first resource.
+        exec_resources_.push_back({
+            .buffer_id = id(),
+            .offset = 0,
+            .length = size,
+        });
+    }
+    exec_resources_.push_back({ .buffer_id = bo->id(),
+        .offset = 0,
+        .length = bo->size}
+    );
+}
+
+bool MagmaBo::MapGpu() {
+    if (is_mapped_gpu_)
+        return true;
+
+    if (!magma_bufmgr()->Alloc(size, __builtin_ctz(PAGE_SIZE_64K), &offset64)) {
+        LOG_VERBOSE("Alloc failed: size %lu", size);
+        return false;
+    }
+
+    uint64_t page_count = size / page_size();
+    constexpr uint64_t kFlags = MAGMA_GPU_MAP_FLAG_READ | MAGMA_GPU_MAP_FLAG_EXECUTE |
+        MAGMA_GPU_MAP_FLAG_WRITE;
+
+    LOG_VERBOSE("MapGpu buffer %lu addr 0x%lx", id(), offset64);
+
+    magma_status_t status = magma_map_buffer_gpu(magma_bufmgr()->connection(),
+        buffer(), 0 /*page_offset*/, page_count, offset64, kFlags);
+
+    if (status != MAGMA_STATUS_OK) {
+        LOG_VERBOSE("magma_map_buffer_gpu failed: %d", status);
+        return false;
+    }
+
+    is_mapped_gpu_ = true;
+
+    return true;
 }
 
 static void bufmgr_destroy(struct mos_bufmgr* mos_bufmgr)
@@ -132,7 +214,7 @@
     MagmaBo* bo = static_cast<MagmaBufMgr*>(mos_bufmgr)->CreateBo(name, size);
 
     if (bo)
-        LOG_VERBOSE("bo_alloc '%s' size %lu buffer %lu", name, size, magma_get_buffer_id(bo->buffer()));
+        LOG_VERBOSE("bo_alloc '%s' size %lu buffer %lu", name, size, bo->id());
 
     return bo;
 }
@@ -183,7 +265,7 @@
 
     if (bo)
         LOG_VERBOSE("bo_alloc_tiled '%s' x %d y %d cpp %d tiling %u size %lu buffer %lu",
-            name, x, y, cpp, tiling, size, magma_get_buffer_id(bo->buffer()));
+            name, x, y, cpp, tiling, size, bo->id());
 
     return bo;
 }
@@ -191,14 +273,14 @@
 static void bufmgr_bo_reference(struct mos_linux_bo *mos_linux_bo)
 {
     auto bo = static_cast<MagmaBo*>(mos_linux_bo);
-    LOG_VERBOSE("bo_reference %lu\n", magma_get_buffer_id(bo->buffer()));
+    LOG_VERBOSE("bo_reference %lu\n", bo->id());
     bo->AddRef();
 }
 
 static void bufmgr_bo_unreference(struct mos_linux_bo *mos_linux_bo)
 {
     auto bo = static_cast<MagmaBo*>(mos_linux_bo);
-    LOG_VERBOSE("bo_unreference %lu", magma_get_buffer_id(bo->buffer()));
+    LOG_VERBOSE("bo_unreference %lu", bo->id());
     bo->RemoveRef();
 }
 
@@ -231,7 +313,7 @@
 
     bo->virt = reinterpret_cast<void*>(zx_vaddr);
 
-    LOG_VERBOSE("mapped buffer %lu address %p", magma_get_buffer_id(bo->buffer()), bo->virt);
+    LOG_VERBOSE("mapped buffer %lu address %p", bo->id(), bo->virt);
     return 0;
 }
 
@@ -247,7 +329,7 @@
 
     bo->virt = nullptr;
 
-    LOG_VERBOSE("unmapped buffer %lu", magma_get_buffer_id(bo->buffer()));
+    LOG_VERBOSE("unmapped buffer %lu", bo->id());
     return 0;
 }
 
@@ -264,13 +346,33 @@
         return -1;
     }
 
-    LOG_VERBOSE("bo_subdata %lu offset %lu size %lu", magma_get_buffer_id(bo->buffer()), offset, size);
+    LOG_VERBOSE("bo_subdata %lu offset %lu size %lu", bo->id(), offset, size);
 
     memcpy(reinterpret_cast<uint8_t*>(bo->virt) + offset, data, size);
 
     return 0;
 }
 
+static int bufmgr_bo_set_softpin(struct mos_linux_bo *bo)
+{
+    // Never called because buffers mapped at creation.
+    assert(false);
+    return 0;
+}
+
+static int bufmgr_bo_add_softpin_target(struct mos_linux_bo *bo_base, struct mos_linux_bo *target_bo_base, bool /*write_flag*/)
+{
+    auto bo = static_cast<MagmaBo*>(bo_base);
+
+    auto target_bo = static_cast<MagmaBo*>(target_bo_base);
+
+    bo->AddExecResource(target_bo);
+
+    LOG_VERBOSE("bo_add_softpin_target bo %lu target_bo %lu", bo->id(), target_bo->id());
+
+    return 0;
+}
+
 // Stubs
 extern int bufmgr_bo_get_subdata(struct mos_linux_bo *bo, unsigned long offset,
                  unsigned long size, void *data);
@@ -340,11 +442,17 @@
     mos_bufmgr::bo_is_reusable = bufmgr_bo_is_reusable;
     mos_bufmgr::get_pipe_from_crtc_id = bufmgr_get_pipe_from_crtc_id;
     mos_bufmgr::bo_references = bufmgr_bo_references;
+    mos_bufmgr::bo_set_softpin = bufmgr_bo_set_softpin;
+    mos_bufmgr::bo_add_softpin_target = bufmgr_bo_add_softpin_target;
+
 #if DEBUG
     mos_bufmgr::debug = 1;
 #else
     mos_bufmgr::debug = 0;
 #endif
+
+    constexpr uint64_t kSize = MEMZONE_TOTAL - MEMZONE_SYS_START;
+    allocator_ = SimpleAllocator::Create(MEMZONE_SYS_START, kSize);
 }
 
 MagmaBufMgr::~MagmaBufMgr() {
@@ -365,7 +473,14 @@
 
     magma_buffer_set_name(connection(), buffer, name);
 
-    return new MagmaBo(buffer, size_actual, this);
+    auto bo = new MagmaBo(buffer, size_actual, this);
+
+    if (!bo->MapGpu()) {
+        bo->RemoveRef();
+        return nullptr;
+    }
+
+    return bo;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -441,3 +556,54 @@
 
     return bufmgr->device_id();
 }
+
+bool mos_gem_bo_is_softpin(struct mos_linux_bo *bo)
+{
+    assert(static_cast<MagmaBo*>(bo)->is_mapped_gpu());
+    return true;
+}
+
+int mos_gem_bo_context_exec2(struct mos_linux_bo *mos_linux_bo, int used, struct mos_linux_context *ctx,
+                           drm_clip_rect_t *cliprects, int num_cliprects, int DR4,
+                           unsigned int flags, int *fence) {
+    constexpr uint32_t kExpectedFlags = I915_EXEC_BSD|I915_EXEC_BSD_RING1;
+
+    auto bo = static_cast<MagmaBo*>(mos_linux_bo);
+
+    LOG_VERBOSE("mos_gem_bo_context_exec2 bo %lu used %d context_id %u num_cliprects %d DR4 %d flags 0x%x kExpectedFlags 0x%x",
+        bo->id(), used, ctx->ctx_id, num_cliprects, DR4, flags, kExpectedFlags);
+
+    assert((flags & ~kExpectedFlags) == 0); // only these flags
+
+    // TODO(fxbug.78281)
+    uint64_t* semaphore_ids = nullptr;
+
+    auto& resources = bo->exec_resources();
+
+    magma_command_buffer command_buffer = {
+        .resource_count = resources.size(),
+        .batch_buffer_resource_index = 0,
+        .batch_start_offset = 0,
+        .wait_semaphore_count = 0,
+        .signal_semaphore_count = 0,
+        .flags = kMagmaIntelGenCommandBufferForVideo,
+    };
+
+    magma_status_t status = magma_execute_command_buffer_with_resources2(bo->magma_bufmgr()->connection(),
+        ctx->ctx_id,
+        &command_buffer,
+        resources.data(),
+        semaphore_ids);
+
+    if (status != MAGMA_STATUS_OK) {
+        LOG_VERBOSE("magma_execute_command_buffer_with_resources failed: %d", status);
+        return -1;
+    }
+
+    return 0;
+}
+
+void mos_gem_bo_clear_relocs(struct mos_linux_bo *bo, int start)
+{
+    static_cast<MagmaBo*>(bo)->clear_exec_resources();
+}
diff --git a/media_driver/linux/common/os/magma/mos_bufmgr_stub.c b/media_driver/linux/common/os/magma/mos_bufmgr_stub.c
index 472b2dc..d9f18ec 100644
--- a/media_driver/linux/common/os/magma/mos_bufmgr_stub.c
+++ b/media_driver/linux/common/os/magma/mos_bufmgr_stub.c
@@ -39,11 +39,6 @@
     return 0;
 }
 
-void mos_gem_bo_clear_relocs(struct mos_linux_bo *bo, int start)
-{
-    LOG_VERBOSE("mos_gem_bo_clear_relocs unimplemented");
-}
-
 void mos_bufmgr_gem_enable_reuse(struct mos_bufmgr *bufmgr)
 {
     LOG_VERBOSE("mos_bufmgr_gem_enable_reuse unimplemented");
@@ -180,19 +175,6 @@
     return 0;
 }
 
-bool mos_gem_bo_is_softpin(struct mos_linux_bo *bo)
-{
-    LOG_VERBOSE("mos_gem_bo_is_softpin unimplemented");
-    return false;
-}
-
-int mos_gem_bo_context_exec2(struct mos_linux_bo *bo, int used, struct mos_linux_context *ctx,
-                           drm_clip_rect_t *cliprects, int num_cliprects, int DR4,
-                           unsigned int flags, int *fence) {
-    LOG_VERBOSE("mos_gem_bo_context_exec2 unimplemented");
-    return 0;
-}
-
 int mos_get_context_param_sseu(struct mos_linux_context *ctx,
                 struct drm_i915_gem_context_param_sseu *sseu) {
     LOG_VERBOSE("mos_get_context_param_sseu unimplemented");
diff --git a/media_driver/linux/common/os/magma/simple_allocator.cpp b/media_driver/linux/common/os/magma/simple_allocator.cpp
new file mode 100644
index 0000000..d115dce
--- /dev/null
+++ b/media_driver/linux/common/os/magma/simple_allocator.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2021 The Fuchsia Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "simple_allocator.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <memory>
+
+#define LOG_VERBOSE(msg, ...) do { \
+    if (true) \
+        fprintf(stderr, "%s:%d " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
+        fflush(stderr); \
+} while (0)
+
+bool SimpleAllocator::CheckGap(SimpleAllocator::Region* prev, SimpleAllocator::Region* next,
+                               uint64_t align, size_t size, uint64_t* addr_out,
+                               bool* continue_search_out) {
+  assert(addr_out);
+  assert(continue_search_out);
+
+  uint64_t gap_begin = prev ? (prev->base + prev->size) : base();
+  uint64_t gap_end;  // last byte of a gap
+
+  if (next) {
+    if (gap_begin == next->base) {
+      *continue_search_out = true;
+      return false;
+    }
+    gap_end = next->base - 1;
+  } else {
+    if (gap_begin == base() + this->size()) {
+      *continue_search_out = false;
+      return false;
+    }
+    gap_end = base() + this->size() - 1;
+  }
+
+  *addr_out = RoundUp(gap_begin, align);
+
+  if (*addr_out < gap_begin) {
+    *continue_search_out = false;
+    return false;
+  }
+
+  if (*addr_out < gap_end && ((gap_end - *addr_out + 1) >= size))
+    return true;
+
+  *continue_search_out = true;
+  return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SimpleAllocator::Region::Region(uint64_t base_in, size_t size_in) : base(base_in), size(size_in) {
+  assert(size > 0);
+  assert(base + size - 1 >= base);
+}
+
+std::unique_ptr<SimpleAllocator> SimpleAllocator::Create(uint64_t base, size_t size) {
+  return std::unique_ptr<SimpleAllocator>(new SimpleAllocator(base, size));
+}
+
+SimpleAllocator::SimpleAllocator(uint64_t base, size_t size) : AddressSpaceAllocator(base, size) {}
+
+bool SimpleAllocator::Alloc(size_t size, uint8_t align_pow2, uint64_t* addr_out) {
+  assert(addr_out);
+
+  size = RoundUp(size, size_t{GetPageSize()});
+  if (size == 0) {
+    LOG_VERBOSE("Can't allocate size zero");
+    return false;
+  }
+
+  assert(IsPageAligned(size));
+
+  uint32_t local_align_pow = align_pow2;
+  const uint32_t page_shift = PageShift();
+  if (align_pow2 < page_shift) {
+    local_align_pow = page_shift;
+  }
+
+  uint64_t align = 1UL << local_align_pow;
+  uint64_t addr;
+  bool continue_search;
+
+  // try to pick spot at the beginning of address space
+  if (CheckGap(nullptr, regions_.empty() ? nullptr : &regions_.front(), align, size, &addr,
+               &continue_search)) {
+    *addr_out = addr;
+    regions_.emplace_front(addr, size);
+    return true;
+  }
+
+  // search the middle of the list
+  for (auto iter = regions_.begin(); continue_search && iter != regions_.end();) {
+    auto prev = &(*iter);
+    auto next = (++iter == regions_.end()) ? nullptr : &(*iter);
+    if (CheckGap(prev, next, align, size, &addr, &continue_search)) {
+      *addr_out = addr;
+      regions_.insert(iter, Region(addr, size));
+      return true;
+    }
+  }
+
+  LOG_VERBOSE("Failed to allocate");
+  return false;
+}
+
+bool SimpleAllocator::Free(uint64_t addr) {
+  auto iter = FindRegion(addr);
+  if (iter == regions_.end()) {
+      LOG_VERBOSE("Couldn't find region to free");
+      return false;
+  }
+
+  regions_.erase(iter);
+
+  return true;
+}
+
+bool SimpleAllocator::GetSize(uint64_t addr, size_t* size_out) {
+  auto iter = FindRegion(addr);
+  if (iter == regions_.end()) {
+      LOG_VERBOSE("Couldn't find region");
+      return false;
+  }
+
+  *size_out = iter->size;
+  return true;
+}
+
+std::list<SimpleAllocator::Region>::iterator SimpleAllocator::FindRegion(uint64_t addr) {
+  for (auto iter = regions_.begin(); iter != regions_.end(); iter++) {
+    auto region = *iter;
+    if ((addr >= region.base) && (addr <= region.base + region.size - 1))
+      return iter;
+  }
+
+  return regions_.end();
+}
diff --git a/media_driver/linux/common/os/magma/simple_allocator.h b/media_driver/linux/common/os/magma/simple_allocator.h
new file mode 100644
index 0000000..5487be0
--- /dev/null
+++ b/media_driver/linux/common/os/magma/simple_allocator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2021 The Fuchsia Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef SIMPLE_ALLOCATOR_H
+#define SIMPLE_ALLOCATOR_H
+
+#include <list>
+
+#include "address_space_allocator.h"
+
+class SimpleAllocator final : public AddressSpaceAllocator {
+ public:
+  static std::unique_ptr<SimpleAllocator> Create(uint64_t base, size_t size);
+
+  bool Alloc(size_t size, uint8_t align_pow2, uint64_t* addr_out) override;
+  bool Free(uint64_t addr) override;
+  bool GetSize(uint64_t addr, size_t* size_out) override;
+
+ private:
+  SimpleAllocator(uint64_t base, size_t size);
+
+  struct Region {
+    Region(uint64_t base, size_t size);
+    uint64_t base;
+    size_t size;
+  };
+
+  bool CheckGap(SimpleAllocator::Region* prev, SimpleAllocator::Region* next, uint64_t align,
+                size_t size, uint64_t* addr_out, bool* continue_search_out);
+
+ private:
+  std::list<SimpleAllocator::Region>::iterator FindRegion(uint64_t addr);
+
+  std::list<SimpleAllocator::Region> regions_;
+};
+
+#endif  // SIMPLE_ALLOCATOR_H