[zircon][dev][goldfish] Add goldfish control driver
DX-939 #comment
This implements a goldfish control device driver. The
purpose of this device is to manage HW side resources
associated sysmem allocated VMOs. It provides a FIDL
interface that can be used by the vulkan ICD to create
HW resources (color buffers) for a VMO and query the
existing resource associated with a VMO.
In order to support this VMO to color buffer mapping,
the concept of a 'heap' is introduced in sysmem. Sysmem
participants can require that memory is allocated on
a specific heap. Memory allocated on a specific heap
can be device local and doesn't need to support CPU
access.
The FIDL interface makes it possible to implement
import/export of memory dedicated to images in the
goldfish vulkan ICD.
The query interface is also exposed as a banjo protocol.
This allows child drivers (e.g. goldfish display driver)
to acquire HW resource associated with VMOs.
This change is part of a series of changes that allow
Fuchsia to run in AEMU, which makes it possible to run a
large set of our existing UI unit/integration tests
without Fuchsia hardware.
Test: /boot/test/sys/sysmem-test
Test: /boot/test/sys/golfish-test
Change-Id: Ib3517bcbf4f5b8561f51f6a7cb19805d984f7923
diff --git a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc
index d3116ab..e119154 100644
--- a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc
+++ b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc
@@ -317,7 +317,6 @@
// These are all false because SW decode.
result.buffer_memory_constraints.physically_contiguous_required = false;
result.buffer_memory_constraints.secure_required = false;
- result.buffer_memory_constraints.secure_permitted = false;
if (port == kOutputPort) {
ZX_ASSERT(decoded_output_info_.has_value());
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc b/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc
index f3acf2d..bbe8be6 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc
@@ -600,9 +600,6 @@
// amlogic requires physically contiguous on both input and output
result.buffer_memory_constraints.physically_contiguous_required = true;
result.buffer_memory_constraints.secure_required = false;
- // This isn't expected to fully work at first, but allow getting as far as we
- // can.
- result.buffer_memory_constraints.secure_permitted = true;
if (port == kOutputPort) {
result.image_format_constraints_count = 1;
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc b/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc
index 2ecbc81..1d08a38 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc
@@ -210,9 +210,6 @@
// amlogic requires physically contiguous on both input and output
result.buffer_memory_constraints.physically_contiguous_required = true;
result.buffer_memory_constraints.secure_required = false;
- // This isn't expected to fully work at first, but allow getting as far as we
- // can.
- result.buffer_memory_constraints.secure_permitted = true;
if (port == kOutputPort) {
result.image_format_constraints_count = 1;
diff --git a/garnet/lib/magma/src/magma_util/platform/zircon/zircon_platform_sysmem_connection.cc b/garnet/lib/magma/src/magma_util/platform/zircon/zircon_platform_sysmem_connection.cc
index e5085d7b..b7a09de 100644
--- a/garnet/lib/magma/src/magma_util/platform/zircon/zircon_platform_sysmem_connection.cc
+++ b/garnet/lib/magma/src/magma_util/platform/zircon/zircon_platform_sysmem_connection.cc
@@ -40,8 +40,7 @@
// No buffer constraints, except those passed directly through from the client. These two
// are for whether this memory should be protected (e.g. usable for DRM content, the precise
// definition depending on the system).
- constraints_.buffer_memory_constraints.secure_required = constraints->secure_permitted;
- constraints_.buffer_memory_constraints.secure_permitted = constraints->secure_required;
+ constraints_.buffer_memory_constraints.secure_required = constraints->secure_required;
constraints_.buffer_memory_constraints.ram_domain_supported =
constraints->ram_domain_supported;
constraints_.buffer_memory_constraints.cpu_domain_supported =
@@ -253,7 +252,6 @@
constraints.buffer_memory_constraints.min_size_bytes = size;
if (flags & MAGMA_SYSMEM_FLAG_PROTECTED) {
constraints.buffer_memory_constraints.secure_required = true;
- constraints.buffer_memory_constraints.secure_permitted = true;
}
constraints.image_format_constraints_count = 0;
diff --git a/garnet/lib/media/codec_impl/codec_impl.cc b/garnet/lib/media/codec_impl/codec_impl.cc
index e44e180..e28d845 100644
--- a/garnet/lib/media/codec_impl/codec_impl.cc
+++ b/garnet/lib/media/codec_impl/codec_impl.cc
@@ -2864,7 +2864,7 @@
ZX_DEBUG_ASSERT(!usage.display);
if (IsCoreCodecHwBased()) {
// Let's see if we can deprecate videoUsageHwProtected, since it's redundant
- // with secure_required and secure_permitted.
+ // with secure_required.
if (usage.video & fuchsia::sysmem::videoUsageHwProtected) {
Fail("Core codec set deprecated videoUsageHwProtected - disallow");
return false;
diff --git a/garnet/lib/media/test/codec_client.cc b/garnet/lib/media/test/codec_client.cc
index a1ab748..58c8ed7 100644
--- a/garnet/lib/media/test/codec_client.cc
+++ b/garnet/lib/media/test/codec_client.cc
@@ -745,9 +745,6 @@
std::numeric_limits<uint32_t>::max();
constraints.buffer_memory_constraints.physically_contiguous_required = false;
constraints.buffer_memory_constraints.secure_required = false;
- // This test client code has no way to produce or consume output data in
- // protected memory.
- constraints.buffer_memory_constraints.secure_permitted = false;
// Despite being a consumer of output uncompressed video frames (when decoding
// video and is_output), for now we intentionally don't constrain to the
diff --git a/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api b/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api
index 48c76c5..0af834b 100644
--- a/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api
+++ b/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api
@@ -2,10 +2,11 @@
"fidl/fuchsia.sysmem/allocator.fidl": "d75f7dfb7275520d89113d9b72b4a493",
"fidl/fuchsia.sysmem/collection.fidl": "8f9c46318344c3b589266a97dc2786a4",
"fidl/fuchsia.sysmem/collections_deprecated.fidl": "4953fa82beb051ff8e12be5df4e3c1a7",
- "fidl/fuchsia.sysmem/constraints.fidl": "91f6e92108fbe62a4d74f577755b4a27",
+ "fidl/fuchsia.sysmem/constraints.fidl": "74cbdd576b7c8300802c64b0239c7fc5",
"fidl/fuchsia.sysmem/driver_connector.fidl": "d2818e5d7c2a0eae18fb006c8d802bf4",
"fidl/fuchsia.sysmem/format_modifier.fidl": "72bd4509f27b9581544a9b3915240aac",
"fidl/fuchsia.sysmem/formats_deprecated.fidl": "8c47d0b4664069b45b4af650e1089dde",
+ "fidl/fuchsia.sysmem/heap.fidl": "6f983cbe501760262b68f1f1ac1c8f37",
"fidl/fuchsia.sysmem/image_formats.fidl": "e3ada1fe345acead3768ccac584a9e10",
"fidl/fuchsia.sysmem/image_formats_deprecated.fidl": "0660023fbaed2b5abca9e5038d809df8",
"fidl/fuchsia.sysmem/usages.fidl": "64442fca34901fc3c7e45b5b640be209"
diff --git a/src/media/codecs/stress/src/buffer_collection_constraints.rs b/src/media/codecs/stress/src/buffer_collection_constraints.rs
index e5b2f4f..36a98d2 100644
--- a/src/media/codecs/stress/src/buffer_collection_constraints.rs
+++ b/src/media/codecs/stress/src/buffer_collection_constraints.rs
@@ -48,9 +48,11 @@
max_size_bytes: std::u32::MAX,
physically_contiguous_required: false,
secure_required: false,
- secure_permitted: false,
ram_domain_supported: false,
cpu_domain_supported: true,
+ inaccessible_domain_supported: false,
+ heap_permitted_count: 0,
+ heap_permitted: [HeapType::SystemRam; 32],
},
image_format_constraints_count: 0,
image_format_constraints: [IMAGE_FORMAT_CONSTRAINTS_DEFAULT; 32],
diff --git a/zircon/system/banjo/ddk.protocol.acpi/acpi.banjo b/zircon/system/banjo/ddk.protocol.acpi/acpi.banjo
index 4e46138..b4db2d9 100644
--- a/zircon/system/banjo/ddk.protocol.acpi/acpi.banjo
+++ b/zircon/system/banjo/ddk.protocol.acpi/acpi.banjo
@@ -19,4 +19,6 @@
GetMmio(uint32 index) -> (zx.status s, AcpiMmio mmio);
MapInterrupt(int64 index) -> (zx.status s, handle<interrupt> @handle);
GetBti(uint32 bdf, uint32 index) -> (zx.status s, handle<bti> bti);
+ ConnectSysmem(handle<channel> connection) -> (zx.status s);
+ RegisterSysmemHeap(uint64 heap, handle<channel> connection) -> (zx.status s);
};
diff --git a/zircon/system/banjo/ddk.protocol.goldfish.control/BUILD.gn b/zircon/system/banjo/ddk.protocol.goldfish.control/BUILD.gn
new file mode 100644
index 0000000..7e1abf2
--- /dev/null
+++ b/zircon/system/banjo/ddk.protocol.goldfish.control/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("$zx/public/gn/banjo.gni")
+
+banjo_library("ddk.protocol.goldfish.control") {
+ sources = [
+ "goldfish-control.banjo",
+ ]
+}
diff --git a/zircon/system/banjo/ddk.protocol.goldfish.control/goldfish-control.banjo b/zircon/system/banjo/ddk.protocol.goldfish.control/goldfish-control.banjo
new file mode 100644
index 0000000..1c4a53d
--- /dev/null
+++ b/zircon/system/banjo/ddk.protocol.goldfish.control/goldfish-control.banjo
@@ -0,0 +1,14 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library ddk.protocol.goldfish.control;
+
+using zx;
+
+[Layout = "ddk-protocol"]
+protocol GoldfishControl {
+ /// Get color buffer for VMO. Fails if VMO is not associated with a color
+ /// buffer.
+ GetColorBuffer(handle<vmo> vmo) -> (zx.status ret, uint32 cb);
+};
diff --git a/zircon/system/banjo/ddk.protocol.goldfish.pipe/goldfish-pipe.banjo b/zircon/system/banjo/ddk.protocol.goldfish.pipe/goldfish-pipe.banjo
index eaec961..89b67f4 100644
--- a/zircon/system/banjo/ddk.protocol.goldfish.pipe/goldfish-pipe.banjo
+++ b/zircon/system/banjo/ddk.protocol.goldfish.pipe/goldfish-pipe.banjo
@@ -89,4 +89,10 @@
/// Get BTI that can be used create IO buffers for read/write commands.
GetBti() -> (zx.status s, handle<bti> bti);
+
+ /// Create a sysmem connection - used to implement ddk.protocol.sysmem.
+ ConnectSysmem(handle<channel> connection) -> (zx.status s);
+
+ /// Register a sysmem heap.
+ RegisterSysmemHeap(uint64 heap, handle<channel> connection) -> (zx.status s);
};
diff --git a/zircon/system/banjo/ddk.protocol.sysmem/sysmem.banjo b/zircon/system/banjo/ddk.protocol.sysmem/sysmem.banjo
index b2f441d..b236003 100644
--- a/zircon/system/banjo/ddk.protocol.sysmem/sysmem.banjo
+++ b/zircon/system/banjo/ddk.protocol.sysmem/sysmem.banjo
@@ -12,4 +12,8 @@
/// fuchsia.sysmem.Allocator. If the connection fails, the channel will
/// close.
Connect(handle<channel> allocator_request) -> (zx.status s);
+
+ /// Takes the client end of a FIDL connection that'll serve
+ /// fuchsia.sysmem.Heap.
+ RegisterHeap(uint64 heap, handle<channel> heap_connection) -> (zx.status s);
};
diff --git a/zircon/system/core/devmgr/component/component-proxy.cpp b/zircon/system/core/devmgr/component/component-proxy.cpp
index 5dee5808..e9a5d45d 100644
--- a/zircon/system/core/devmgr/component/component-proxy.cpp
+++ b/zircon/system/core/devmgr/component/component-proxy.cpp
@@ -580,6 +580,17 @@
return Rpc(&req.header, sizeof(req), &resp, sizeof(resp), &handle, 1, nullptr, 0, nullptr);
}
+zx_status_t ComponentProxy::SysmemRegisterHeap(uint64_t heap, zx::channel heap_connection) {
+ SysmemProxyRequest req = {};
+ ProxyResponse resp = {};
+ req.header.proto_id = ZX_PROTOCOL_SYSMEM;
+ req.op = SysmemOp::REGISTER_HEAP;
+ req.heap = heap;
+ zx_handle_t handle = heap_connection.release();
+
+ return Rpc(&req.header, sizeof(req), &resp, sizeof(resp), &handle, 1, nullptr, 0, nullptr);
+}
+
zx_status_t ComponentProxy::UsbModeSwitchSetMode(usb_mode_t mode) {
UsbModeSwitchProxyRequest req = {};
ProxyResponse resp = {};
diff --git a/zircon/system/core/devmgr/component/component-proxy.h b/zircon/system/core/devmgr/component/component-proxy.h
index bcb4b2a..95412b8 100644
--- a/zircon/system/core/devmgr/component/component-proxy.h
+++ b/zircon/system/core/devmgr/component/component-proxy.h
@@ -94,6 +94,7 @@
zx_status_t PowerWritePmicCtrlReg(uint32_t reg_addr, uint32_t value);
zx_status_t PowerReadPmicCtrlReg(uint32_t reg_addr, uint32_t* out_value);
zx_status_t SysmemConnect(zx::channel allocator2_request);
+ zx_status_t SysmemRegisterHeap(uint64_t heap, zx::channel heap_connection);
zx_status_t MipiCsiInit(const mipi_info_t* mipi_info,
const mipi_adap_info_t* adap_info);
zx_status_t MipiCsiDeInit();
diff --git a/zircon/system/core/devmgr/component/component.cpp b/zircon/system/core/devmgr/component/component.cpp
index ab19710..b7cde1e 100644
--- a/zircon/system/core/devmgr/component/component.cpp
+++ b/zircon/system/core/devmgr/component/component.cpp
@@ -357,10 +357,10 @@
*out_resp_size = sizeof(ProxyResponse);
switch (req->op) {
- case SysmemOp::CONNECT: {
-
+ case SysmemOp::CONNECT:
return sysmem_.Connect(zx::channel(req_handles[0]));
- }
+ case SysmemOp::REGISTER_HEAP:
+ return sysmem_.RegisterHeap(req->heap, zx::channel(req_handles[0]));
default:
zxlogf(ERROR, "%s: unknown sysmem op %u\n", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
diff --git a/zircon/system/core/devmgr/component/proxy-protocol.h b/zircon/system/core/devmgr/component/proxy-protocol.h
index d0c65d2..ff6e62a 100644
--- a/zircon/system/core/devmgr/component/proxy-protocol.h
+++ b/zircon/system/core/devmgr/component/proxy-protocol.h
@@ -137,11 +137,13 @@
// ZX_PROTOCOL_SYSMEM proxy support.
enum class SysmemOp {
CONNECT,
+ REGISTER_HEAP,
};
struct SysmemProxyRequest {
ProxyRequest header;
SysmemOp op;
+ uint64_t heap;
};
// ZX_PROTOCOL_AMLOGIC_CANVAS proxy support.
diff --git a/zircon/system/dev/board/x86/acpi-nswalk.c b/zircon/system/dev/board/x86/acpi-nswalk.c
index 7790ddc..d3bbd10 100644
--- a/zircon/system/dev/board/x86/acpi-nswalk.c
+++ b/zircon/system/dev/board/x86/acpi-nswalk.c
@@ -5,6 +5,7 @@
#include <ddk/debug.h>
#include <ddk/protocol/acpi.h>
#include <ddk/protocol/pciroot.h>
+#include <ddk/protocol/sysmem.h>
#include <inttypes.h>
#include <limits.h>
@@ -353,11 +354,46 @@
return zx_bti_create(iommu_handle, 0, bdf, bti);
}
+static zx_status_t acpi_op_connect_sysmem(void* ctx, zx_handle_t handle) {
+ acpi_device_t* dev = (acpi_device_t*)ctx;
+ mtx_lock(&dev->lock);
+
+ sysmem_protocol_t sysmem;
+ zx_status_t st = device_get_protocol(dev->platform_bus, ZX_PROTOCOL_SYSMEM, &sysmem);
+ if (st != ZX_OK) {
+ zx_handle_close(handle);
+ goto unlock;
+ }
+ st = sysmem_connect(&sysmem, handle);
+unlock:
+ mtx_unlock(&dev->lock);
+ return st;
+}
+
+static zx_status_t acpi_op_register_sysmem_heap(void* ctx, uint64_t heap, zx_handle_t handle) {
+ acpi_device_t* dev = (acpi_device_t*)ctx;
+ mtx_lock(&dev->lock);
+
+ sysmem_protocol_t sysmem;
+ zx_status_t st = device_get_protocol(dev->platform_bus, ZX_PROTOCOL_SYSMEM, &sysmem);
+ if (st != ZX_OK) {
+ zx_handle_close(handle);
+ goto unlock;
+ }
+
+ st = sysmem_register_heap(&sysmem, heap, handle);
+unlock:
+ mtx_unlock(&dev->lock);
+ return st;
+}
+
// TODO marking unused until we publish some devices
static __attribute__ ((unused)) acpi_protocol_ops_t acpi_proto = {
.get_mmio = acpi_op_get_mmio,
.map_interrupt = acpi_op_map_interrupt,
.get_bti = acpi_op_get_bti,
+ .connect_sysmem = acpi_op_connect_sysmem,
+ .register_sysmem_heap = acpi_op_register_sysmem_heap,
};
static const char* hid_from_acpi_devinfo(ACPI_DEVICE_INFO* info) {
diff --git a/zircon/system/dev/bus/platform/platform-proxy.cpp b/zircon/system/dev/bus/platform/platform-proxy.cpp
index 17af4c3..1ae66b81 100644
--- a/zircon/system/dev/bus/platform/platform-proxy.cpp
+++ b/zircon/system/dev/bus/platform/platform-proxy.cpp
@@ -134,14 +134,27 @@
}
zx_status_t ProxySysmem::SysmemConnect(zx::channel allocator_request) {
- platform_proxy_req_t req = {};
+ rpc_sysmem_req_t req = {};
platform_proxy_rsp_t resp = {};
- req.proto_id = ZX_PROTOCOL_SYSMEM;
- req.op = SYSMEM_CONNECT;
+ req.header.proto_id = ZX_PROTOCOL_SYSMEM;
+ req.header.op = SYSMEM_CONNECT;
zx_handle_t handle = allocator_request.release();
- return proxy_->Rpc(&req, sizeof(req), &resp, sizeof(resp), &handle, 1, nullptr, 0,
- nullptr);
+ return proxy_->Rpc(&req.header, sizeof(req), &resp, sizeof(resp), &handle,
+ 1, nullptr, 0, nullptr);
+}
+
+zx_status_t ProxySysmem::SysmemRegisterHeap(uint64_t heap,
+ zx::channel heap_connection) {
+ rpc_sysmem_req_t req = {};
+ platform_proxy_rsp_t resp = {};
+ req.header.proto_id = ZX_PROTOCOL_SYSMEM;
+ req.header.op = SYSMEM_REGISTER_HEAP;
+ req.heap = heap;
+ zx_handle_t handle = heap_connection.release();
+
+ return proxy_->Rpc(&req.header, sizeof(req), &resp, sizeof(resp), &handle,
+ 1, nullptr, 0, nullptr);
}
zx_status_t ProxyAmlogicCanvas::AmlogicCanvasConfig(zx::vmo vmo, size_t offset,
diff --git a/zircon/system/dev/bus/platform/platform-proxy.h b/zircon/system/dev/bus/platform/platform-proxy.h
index 0478018..732a011 100644
--- a/zircon/system/dev/bus/platform/platform-proxy.h
+++ b/zircon/system/dev/bus/platform/platform-proxy.h
@@ -72,6 +72,7 @@
// Sysmem protocol implementation.
zx_status_t SysmemConnect(zx::channel allocator2_request);
+ zx_status_t SysmemRegisterHeap(uint64_t heap, zx::channel heap_connection);
void GetProtocol(sysmem_protocol_t* proto) {
proto->ops = &sysmem_protocol_ops_;
diff --git a/zircon/system/dev/bus/platform/proxy-protocol.h b/zircon/system/dev/bus/platform/proxy-protocol.h
index 71418d1..98341ee 100644
--- a/zircon/system/dev/bus/platform/proxy-protocol.h
+++ b/zircon/system/dev/bus/platform/proxy-protocol.h
@@ -106,6 +106,12 @@
// ZX_PROTOCOL_SYSMEM proxy support.
enum {
SYSMEM_CONNECT,
+ SYSMEM_REGISTER_HEAP,
+};
+
+struct rpc_sysmem_req_t {
+ platform_proxy_req_t header;
+ uint64_t heap;
};
// ZX_PROTOCOL_AMLOGIC_CANVAS proxy support.
diff --git a/zircon/system/dev/display/astro-display/astro-display.cpp b/zircon/system/dev/display/astro-display/astro-display.cpp
index 3df1589..322c0e2 100644
--- a/zircon/system/dev/display/astro-display/astro-display.cpp
+++ b/zircon/system/dev/display/astro-display/astro-display.cpp
@@ -570,9 +570,11 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = true;
buffer_constraints.secure_required = false;
- buffer_constraints.secure_permitted = true;
buffer_constraints.ram_domain_supported = true;
buffer_constraints.cpu_domain_supported = true;
+ buffer_constraints.heap_permitted_count = 2;
+ buffer_constraints.heap_permitted[0] = fuchsia_sysmem_HeapType_SYSTEM_RAM;
+ buffer_constraints.heap_permitted[1] = fuchsia_sysmem_HeapType_AMLOGIC_SECURE;
constraints.image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints =
constraints.image_format_constraints[0];
diff --git a/zircon/system/dev/display/display/client.cpp b/zircon/system/dev/display/display/client.cpp
index d681f63..0a1e7f3 100644
--- a/zircon/system/dev/display/display/client.cpp
+++ b/zircon/system/dev/display/display/client.cpp
@@ -504,7 +504,7 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = true;
buffer_constraints.secure_required = false;
- buffer_constraints.secure_permitted = false;
+ buffer_constraints.ram_domain_supported = true;
constraints.image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints =
constraints.image_format_constraints[0];
diff --git a/zircon/system/dev/display/dummy/dummy-display.cpp b/zircon/system/dev/display/dummy/dummy-display.cpp
index f4684f1..38039cf 100644
--- a/zircon/system/dev/display/dummy/dummy-display.cpp
+++ b/zircon/system/dev/display/dummy/dummy-display.cpp
@@ -175,7 +175,6 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = false;
buffer_constraints.secure_required = false;
- buffer_constraints.secure_permitted = false;
buffer_constraints.ram_domain_supported = true;
buffer_constraints.cpu_domain_supported = true;
constraints.image_format_constraints_count = 1;
diff --git a/zircon/system/dev/display/hikey-display/ddk-interface.cpp b/zircon/system/dev/display/hikey-display/ddk-interface.cpp
index 43c9888..90d5963 100644
--- a/zircon/system/dev/display/hikey-display/ddk-interface.cpp
+++ b/zircon/system/dev/display/hikey-display/ddk-interface.cpp
@@ -154,9 +154,10 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = true;
buffer_constraints.secure_required = false;
- buffer_constraints.secure_permitted = false;
buffer_constraints.ram_domain_supported = true;
buffer_constraints.cpu_domain_supported = true;
+ buffer_constraints.heap_permitted_count = 1;
+ buffer_constraints.heap_permitted[0] = fuchsia_sysmem_HeapType_SYSTEM_RAM;
constraints.image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints =
constraints.image_format_constraints[0];
diff --git a/zircon/system/dev/display/intel-i915/intel-i915.cpp b/zircon/system/dev/display/intel-i915/intel-i915.cpp
index 9bf5f19..041ed77 100644
--- a/zircon/system/dev/display/intel-i915/intel-i915.cpp
+++ b/zircon/system/dev/display/intel-i915/intel-i915.cpp
@@ -1772,9 +1772,10 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = false;
buffer_constraints.secure_required = false;
- buffer_constraints.secure_permitted = false;
buffer_constraints.ram_domain_supported = true;
buffer_constraints.cpu_domain_supported = true;
+ buffer_constraints.heap_permitted_count = 1;
+ buffer_constraints.heap_permitted[0] = fuchsia_sysmem_HeapType_SYSTEM_RAM;
constraints.image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints =
constraints.image_format_constraints[0];
diff --git a/zircon/system/dev/display/mt8167s-display/mt8167s-display.cpp b/zircon/system/dev/display/mt8167s-display/mt8167s-display.cpp
index 0629b52..6c83700 100644
--- a/zircon/system/dev/display/mt8167s-display/mt8167s-display.cpp
+++ b/zircon/system/dev/display/mt8167s-display/mt8167s-display.cpp
@@ -355,9 +355,10 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = true;
buffer_constraints.secure_required = false;
- buffer_constraints.secure_permitted = false;
buffer_constraints.ram_domain_supported = true;
buffer_constraints.cpu_domain_supported = true;
+ buffer_constraints.heap_permitted_count = 1;
+ buffer_constraints.heap_permitted[0] = fuchsia_sysmem_HeapType_SYSTEM_RAM;
constraints.image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints =
constraints.image_format_constraints[0];
diff --git a/zircon/system/dev/display/vim-display/vim-display.cpp b/zircon/system/dev/display/vim-display/vim-display.cpp
index 66b9eb9..96f45dd 100644
--- a/zircon/system/dev/display/vim-display/vim-display.cpp
+++ b/zircon/system/dev/display/vim-display/vim-display.cpp
@@ -502,9 +502,10 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = true;
buffer_constraints.secure_required = false;
- buffer_constraints.secure_permitted = false;
buffer_constraints.ram_domain_supported = true;
buffer_constraints.cpu_domain_supported = true;
+ buffer_constraints.heap_permitted_count = 1;
+ buffer_constraints.heap_permitted[0] = fuchsia_sysmem_HeapType_SYSTEM_RAM;
constraints.image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints =
constraints.image_format_constraints[0];
diff --git a/zircon/system/dev/misc/BUILD.gn b/zircon/system/dev/misc/BUILD.gn
index 12acdf0..c170d036 100644
--- a/zircon/system/dev/misc/BUILD.gn
+++ b/zircon/system/dev/misc/BUILD.gn
@@ -9,6 +9,7 @@
"cpu-trace",
"goldfish",
"goldfish-address-space",
+ "goldfish-control",
"ktrace",
"pty",
"sysinfo",
diff --git a/zircon/system/dev/misc/goldfish-control/BUILD.gn b/zircon/system/dev/misc/goldfish-control/BUILD.gn
new file mode 100644
index 0000000..6714e0a
--- /dev/null
+++ b/zircon/system/dev/misc/goldfish-control/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+driver("goldfish-control") {
+ sources = [
+ "control-device.cpp",
+ ]
+ deps = [
+ "$zx/system/banjo/ddk.protocol.goldfish.control",
+ "$zx/system/banjo/ddk.protocol.goldfish.pipe",
+ "$zx/system/fidl/fuchsia-hardware-goldfish-control:c",
+ "$zx/system/fidl/fuchsia-sysmem:c",
+ "$zx/system/ulib/async-loop:async-loop-cpp",
+ "$zx/system/ulib/ddk",
+ "$zx/system/ulib/ddktl",
+ "$zx/system/ulib/fbl",
+ "$zx/system/ulib/fidl",
+ "$zx/system/ulib/fidl-async-2",
+ "$zx/system/ulib/fidl-utils",
+ "$zx/system/ulib/fit",
+ "$zx/system/ulib/trace:headers",
+ "$zx/system/ulib/trace:trace-driver",
+ "$zx/system/ulib/zx",
+ ]
+}
diff --git a/zircon/system/dev/misc/goldfish-control/control-device.cpp b/zircon/system/dev/misc/goldfish-control/control-device.cpp
new file mode 100644
index 0000000..2fb7743
--- /dev/null
+++ b/zircon/system/dev/misc/goldfish-control/control-device.cpp
@@ -0,0 +1,573 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "control-device.h"
+
+#include <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/trace/event.h>
+#include <fbl/auto_call.h>
+#include <fbl/auto_lock.h>
+#include <fbl/unique_ptr.h>
+#include <fuchsia/sysmem/c/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/fidl-async-2/fidl_server.h>
+#include <lib/fidl-async-2/simple_binding.h>
+#include <lib/fidl-utils/bind.h>
+#include <lib/zx/event.h>
+#include <zircon/syscalls.h>
+
+#include <memory>
+
+namespace goldfish {
+namespace {
+
+const char* kTag = "goldfish-control";
+
+const char* kPipeName = "pipe:opengles";
+
+constexpr uint32_t kClientFlags = 0;
+
+constexpr uint32_t VULKAN_ONLY = 1;
+
+struct CreateColorBufferCmd {
+ uint32_t op;
+ uint32_t size;
+ uint32_t width;
+ uint32_t height;
+ uint32_t internalformat;
+};
+constexpr uint32_t kOP_rcCreateColorBuffer = 10012;
+constexpr uint32_t kSize_rcCreateColorBuffer = 20;
+
+struct CloseColorBufferCmd {
+ uint32_t op;
+ uint32_t size;
+ uint32_t id;
+};
+constexpr uint32_t kOP_rcCloseColorBuffer = 10014;
+constexpr uint32_t kSize_rcCloseColorBuffer = 12;
+
+struct SetColorBufferVulkanModeCmd {
+ uint32_t op;
+ uint32_t size;
+ uint32_t id;
+ uint32_t mode;
+};
+constexpr uint32_t kOP_rcSetColorBufferVulkanMode = 10045;
+constexpr uint32_t kSize_rcSetColorBufferVulkanMode = 16;
+
+zx_koid_t GetKoidForVmo(const zx::vmo& vmo) {
+ zx_info_handle_basic_t info;
+ zx_status_t status = zx_object_get_info(
+ vmo.get(), ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: zx_object_get_info() failed - status: %d\n", kTag,
+ status);
+ return ZX_KOID_INVALID;
+ }
+ return info.koid;
+}
+
+void vLog(bool is_error, const char* prefix1, const char* prefix2,
+ const char* format, va_list args) {
+ va_list args2;
+ va_copy(args2, args);
+
+ size_t buffer_bytes = vsnprintf(nullptr, 0, format, args) + 1;
+
+ std::unique_ptr<char[]> buffer(new char[buffer_bytes]);
+
+ size_t buffer_bytes_2 =
+ vsnprintf(buffer.get(), buffer_bytes, format, args2) + 1;
+ (void)buffer_bytes_2;
+ // sanity check; should match so go ahead and assert that it does.
+ ZX_DEBUG_ASSERT(buffer_bytes == buffer_bytes_2);
+ va_end(args2);
+
+ if (is_error) {
+ zxlogf(ERROR, "[%s %s] %s\n", prefix1, prefix2, buffer.get());
+ } else {
+ zxlogf(TRACE, "[%s %s] %s\n", prefix1, prefix2, buffer.get());
+ }
+}
+
+constexpr uint32_t kConcurrencyCap = 64;
+
+// An instance of this class serves a Heap connection.
+class Heap : public FidlServer<Heap,
+ SimpleBinding<Heap, fuchsia_sysmem_Heap_ops_t,
+ fuchsia_sysmem_Heap_dispatch>,
+ vLog> {
+public:
+ // Public for std::unique_ptr<Heap>:
+ ~Heap() = default;
+
+private:
+ friend class FidlServer;
+
+ Heap(Control* control)
+ : FidlServer("GoldfishHeap", kConcurrencyCap), control_(control) {}
+
+ zx_status_t AllocateVmo(uint64_t size, fidl_txn* txn) {
+ BindingType::Txn::RecognizeTxn(txn);
+
+ zx::vmo vmo;
+ zx_status_t status = zx::vmo::create(size, 0, &vmo);
+ if (status != ZX_OK) {
+ zxlogf(ERROR,
+ "%s: zx::vmo::create() failed - size: %lu status: %d\n",
+ kTag, size, status);
+ return fuchsia_sysmem_HeapAllocateVmo_reply(txn, status,
+ ZX_HANDLE_INVALID);
+ }
+
+ return fuchsia_sysmem_HeapAllocateVmo_reply(txn, ZX_OK, vmo.release());
+ }
+
+ zx_status_t CreateResource(zx_handle_t vmo_handle, fidl_txn* txn) {
+ BindingType::Txn::RecognizeTxn(txn);
+
+ zx::vmo vmo(vmo_handle);
+
+ zx_koid_t id = GetKoidForVmo(vmo);
+ if (id == ZX_KOID_INVALID) {
+ return fuchsia_sysmem_HeapCreateResource_reply(
+ txn, ZX_ERR_INVALID_ARGS, 0);
+ }
+
+ control_->RegisterColorBuffer(id);
+ return fuchsia_sysmem_HeapCreateResource_reply(txn, ZX_OK, id);
+ }
+
+ zx_status_t DestroyResource(uint64_t id, fidl_txn* txn) {
+ BindingType::Txn::RecognizeTxn(txn);
+
+ control_->FreeColorBuffer(id);
+ return fuchsia_sysmem_HeapDestroyResource_reply(txn);
+ }
+
+ static constexpr fuchsia_sysmem_Heap_ops_t kOps = {
+ fidl::Binder<Heap>::BindMember<&Heap::AllocateVmo>,
+ fidl::Binder<Heap>::BindMember<&Heap::CreateResource>,
+ fidl::Binder<Heap>::BindMember<&Heap::DestroyResource>,
+ };
+
+ Control* const control_;
+};
+
+} // namespace
+
+// static
+zx_status_t Control::Create(void* ctx, zx_device_t* device) {
+ auto control = std::make_unique<Control>(device);
+
+ zx_status_t status = control->Bind();
+ if (status == ZX_OK) {
+ // devmgr now owns device.
+ __UNUSED auto* dev = control.release();
+ }
+ return status;
+}
+
+Control::Control(zx_device_t* parent)
+ : ControlType(parent), pipe_(parent),
+ heap_loop_(&kAsyncLoopConfigNoAttachToThread) {
+ goldfish_control_protocol_t self{&goldfish_control_protocol_ops_, this};
+ control_ = ddk::GoldfishControlProtocolClient(&self);
+}
+
+Control::~Control() {
+ heap_loop_.Shutdown();
+ if (id_) {
+ fbl::AutoLock lock(&lock_);
+ if (cmd_buffer_.is_valid()) {
+ for (auto& buffer : color_buffers_) {
+ CloseColorBufferLocked(buffer.second);
+ }
+ auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
+ buffer->id = id_;
+ buffer->cmd = PIPE_CMD_CODE_CLOSE;
+ buffer->status = PIPE_ERROR_INVAL;
+
+ pipe_.Exec(id_);
+ ZX_DEBUG_ASSERT(!buffer->status);
+ }
+ pipe_.Destroy(id_);
+ }
+}
+
+zx_status_t Control::Bind() {
+ fbl::AutoLock lock(&lock_);
+
+ if (!pipe_.is_valid()) {
+ zxlogf(ERROR, "%s: no pipe protocol\n", kTag);
+ return ZX_ERR_NOT_SUPPORTED;
+ }
+
+ zx_status_t status = pipe_.GetBti(&bti_);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: GetBti failed: %d\n", kTag, status);
+ return status;
+ }
+
+ status =
+ io_buffer_.Init(bti_.get(), PAGE_SIZE, IO_BUFFER_RW | IO_BUFFER_CONTIG);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: io_buffer_init failed: %d\n", kTag, status);
+ return status;
+ }
+
+ zx::vmo vmo;
+ goldfish_pipe_signal_value_t signal_cb = {Control::OnSignal, this};
+ status = pipe_.Create(&signal_cb, &id_, &vmo);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: Create failed: %d\n", kTag, status);
+ return status;
+ }
+
+ status = cmd_buffer_.InitVmo(bti_.get(), vmo.get(), 0, IO_BUFFER_RW);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: io_buffer_init_vmo failed: %d\n", kTag, status);
+ return status;
+ }
+
+ auto release_buffer =
+ fbl::MakeAutoCall([this]() TA_NO_THREAD_SAFETY_ANALYSIS
+ { cmd_buffer_.release(); });
+
+ auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
+ buffer->id = id_;
+ buffer->cmd = PIPE_CMD_CODE_OPEN;
+ buffer->status = PIPE_ERROR_INVAL;
+
+ pipe_.Open(id_);
+ if (buffer->status) {
+ zxlogf(ERROR, "%s: Open failed: %d\n", kTag, buffer->status);
+ return ZX_ERR_INTERNAL;
+ }
+
+ // Keep buffer after successful execution of OPEN command. This way
+ // we'll send CLOSE later.
+ release_buffer.cancel();
+
+ size_t length = strlen(kPipeName) + 1;
+ memcpy(io_buffer_.virt(), kPipeName, length);
+ WriteLocked(static_cast<uint32_t>(length));
+
+ memcpy(io_buffer_.virt(), &kClientFlags, sizeof(kClientFlags));
+ WriteLocked(sizeof(kClientFlags));
+
+ // We are now ready to serve goldfish heap allocations. Create a channel
+ // and register client-end with sysmem.
+ zx::channel heap_request, heap_connection;
+ status = zx::channel::create(0, &heap_request, &heap_connection);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: zx::channel:create() failed: %d\n", kTag, status);
+ return status;
+ }
+ status =
+ pipe_.RegisterSysmemHeap(fuchsia_sysmem_HeapType_GOLDFISH_DEVICE_LOCAL,
+ std::move(heap_connection));
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: failed to register heap: %d\n", kTag, status);
+ return status;
+ }
+
+ // Start server thread. Heap server must be running on a seperate
+ // thread as sysmem might be making synchronous allocation requests
+ // from the main thread.
+ heap_loop_.StartThread("goldfish_control_heap_thread");
+ async::PostTask(heap_loop_.dispatcher(),
+ [this, request = std::move(heap_request)]() mutable {
+ // The Heap is channel-owned / self-owned.
+ Heap::CreateChannelOwned(std::move(request), this);
+ });
+
+ return DdkAdd("goldfish-control", 0, nullptr, 0,
+ ZX_PROTOCOL_GOLDFISH_CONTROL);
+}
+
+void Control::RegisterColorBuffer(zx_koid_t koid) {
+ fbl::AutoLock lock(&lock_);
+ color_buffers_[koid] = 0;
+}
+
+void Control::FreeColorBuffer(zx_koid_t koid) {
+ fbl::AutoLock lock(&lock_);
+
+ auto it = color_buffers_.find(koid);
+ if (it == color_buffers_.end()) {
+ zxlogf(ERROR, "%s: invalid key\n", kTag);
+ return;
+ }
+
+ if (it->second) {
+ CloseColorBufferLocked(it->second);
+ }
+ color_buffers_.erase(it);
+}
+
+zx_status_t Control::FidlCreateColorBuffer(zx_handle_t vmo_handle,
+ uint32_t width, uint32_t height,
+ uint32_t format, fidl_txn_t* txn) {
+ TRACE_DURATION("gfx", "Control::FidlCreateColorBuffer", "width", width,
+ "height", height);
+
+ zx::vmo vmo(vmo_handle);
+ zx_koid_t koid = GetKoidForVmo(vmo);
+ if (koid == ZX_KOID_INVALID) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ fbl::AutoLock lock(&lock_);
+
+ auto it = color_buffers_.find(koid);
+ if (it == color_buffers_.end()) {
+ zxlogf(ERROR, "%s: invalid VMO\n", kTag);
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ if (it->second) {
+ zxlogf(ERROR, "%s: color buffer already exists\n", kTag);
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ uint32_t id;
+ zx_status_t status = CreateColorBufferLocked(width, height, format, &id);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: failed to create color buffer: %d\n", kTag, status);
+ return status;
+ }
+
+ auto close_color_buffer =
+ fbl::MakeAutoCall([this, id]() TA_NO_THREAD_SAFETY_ANALYSIS {
+ CloseColorBufferLocked(id);
+ });
+
+ uint32_t result = 0;
+ status = SetColorBufferVulkanModeLocked(id, VULKAN_ONLY, &result);
+ if (status != ZX_OK || result) {
+ zxlogf(ERROR, "%s: failed to set vulkan mode: %d %d\n", kTag, status,
+ result);
+ return status;
+ }
+
+ close_color_buffer.cancel();
+ it->second = id;
+ return fuchsia_hardware_goldfish_control_DeviceCreateColorBuffer_reply(
+ txn, ZX_OK);
+}
+
+zx_status_t Control::FidlGetColorBuffer(zx_handle_t vmo_handle,
+ fidl_txn_t* txn) {
+ TRACE_DURATION("gfx", "Control::FidlGetColorBuffer");
+
+ zx::vmo vmo(vmo_handle);
+ zx_koid_t koid = GetKoidForVmo(vmo);
+ if (koid == ZX_KOID_INVALID) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ fbl::AutoLock lock(&lock_);
+
+ auto it = color_buffers_.find(koid);
+ if (it == color_buffers_.end()) {
+ return fuchsia_hardware_goldfish_control_DeviceGetColorBuffer_reply(
+ txn, ZX_ERR_INVALID_ARGS, 0);
+ }
+
+ return fuchsia_hardware_goldfish_control_DeviceGetColorBuffer_reply(
+ txn, ZX_OK, it->second);
+}
+
+void Control::DdkUnbind() {
+ DdkRemove();
+}
+
+void Control::DdkRelease() {
+ delete this;
+}
+
+zx_status_t Control::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
+ using Binder = fidl::Binder<Control>;
+
+ static const fuchsia_hardware_goldfish_control_Device_ops_t kOps = {
+ .CreateColorBuffer =
+ Binder::BindMember<&Control::FidlCreateColorBuffer>,
+ .GetColorBuffer = Binder::BindMember<&Control::FidlGetColorBuffer>,
+ };
+
+ return fuchsia_hardware_goldfish_control_Device_dispatch(this, txn, msg,
+ &kOps);
+}
+
+zx_status_t Control::DdkGetProtocol(uint32_t proto_id, void* out_protocol) {
+ fbl::AutoLock lock(&lock_);
+
+ switch (proto_id) {
+ case ZX_PROTOCOL_GOLDFISH_PIPE: {
+ pipe_.GetProto(static_cast<goldfish_pipe_protocol_t*>(out_protocol));
+ return ZX_OK;
+ }
+ case ZX_PROTOCOL_GOLDFISH_CONTROL: {
+ control_.GetProto(
+ static_cast<goldfish_control_protocol_t*>(out_protocol));
+ return ZX_OK;
+ }
+ default:
+ return ZX_ERR_NOT_SUPPORTED;
+ }
+}
+
+zx_status_t Control::GoldfishControlGetColorBuffer(zx::vmo vmo,
+ uint32_t* out_id) {
+ zx_koid_t koid = GetKoidForVmo(vmo);
+ if (koid == ZX_KOID_INVALID) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ fbl::AutoLock lock(&lock_);
+
+ auto it = color_buffers_.find(koid);
+ if (it == color_buffers_.end()) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ *out_id = it->second;
+ return ZX_OK;
+}
+
+void Control::OnSignal(void* ctx, int32_t flags) {
+ TRACE_DURATION("gfx", "Control::OnSignal", "flags", flags);
+
+ if (flags & (PIPE_WAKE_FLAG_READ | PIPE_WAKE_FLAG_CLOSED)) {
+ static_cast<Control*>(ctx)->OnReadable();
+ }
+}
+
+void Control::OnReadable() {
+ TRACE_DURATION("gfx", "Control::OnReadable");
+
+ fbl::AutoLock lock(&lock_);
+ readable_cvar_.Signal();
+}
+
+void Control::WriteLocked(uint32_t cmd_size) {
+ TRACE_DURATION("gfx", "Control::Write", "cmd_size", cmd_size);
+
+ auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
+ buffer->id = id_;
+ buffer->cmd = PIPE_CMD_CODE_WRITE;
+ buffer->status = PIPE_ERROR_INVAL;
+ buffer->rw_params.ptrs[0] = io_buffer_.phys();
+ buffer->rw_params.sizes[0] = cmd_size;
+ buffer->rw_params.buffers_count = 1;
+ buffer->rw_params.consumed_size = 0;
+ pipe_.Exec(id_);
+ ZX_DEBUG_ASSERT(buffer->rw_params.consumed_size ==
+ static_cast<int32_t>(cmd_size));
+}
+
+zx_status_t Control::ReadResultLocked(uint32_t* result) {
+ TRACE_DURATION("gfx", "Control::ReadResult");
+
+ while (true) {
+ auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
+ buffer->id = id_;
+ buffer->cmd = PIPE_CMD_CODE_READ;
+ buffer->status = PIPE_ERROR_INVAL;
+ buffer->rw_params.ptrs[0] = io_buffer_.phys();
+ buffer->rw_params.sizes[0] = sizeof(*result);
+ buffer->rw_params.buffers_count = 1;
+ buffer->rw_params.consumed_size = 0;
+ pipe_.Exec(id_);
+
+ // Positive consumed size always indicate a successful transfer.
+ if (buffer->rw_params.consumed_size) {
+ ZX_DEBUG_ASSERT(buffer->rw_params.consumed_size == sizeof(*result));
+ *result = *static_cast<uint32_t*>(io_buffer_.virt());
+ return ZX_OK;
+ }
+
+ // Early out if error is not because of back-pressure.
+ if (buffer->status != PIPE_ERROR_AGAIN) {
+ zxlogf(ERROR, "%s: reading result failed: %d\n", kTag,
+ buffer->status);
+ return ZX_ERR_INTERNAL;
+ }
+
+ buffer->id = id_;
+ buffer->cmd = PIPE_CMD_CODE_WAKE_ON_READ;
+ buffer->status = PIPE_ERROR_INVAL;
+ pipe_.Exec(id_);
+ ZX_DEBUG_ASSERT(!buffer->status);
+
+ // Wait for pipe to become readable.
+ readable_cvar_.Wait(&lock_);
+ }
+}
+
+zx_status_t Control::ExecuteCommandLocked(uint32_t cmd_size, uint32_t* result) {
+ TRACE_DURATION("gfx", "Control::ExecuteCommand", "cnd_size", cmd_size);
+
+ WriteLocked(cmd_size);
+ return ReadResultLocked(result);
+}
+
+zx_status_t Control::CreateColorBufferLocked(uint32_t width, uint32_t height,
+ uint32_t format, uint32_t* id) {
+ TRACE_DURATION("gfx", "Control::CreateColorBuffer", "width", width,
+ "height", height);
+
+ auto cmd = static_cast<CreateColorBufferCmd*>(io_buffer_.virt());
+ cmd->op = kOP_rcCreateColorBuffer;
+ cmd->size = kSize_rcCreateColorBuffer;
+ cmd->width = width;
+ cmd->height = height;
+ cmd->internalformat = format;
+
+ return ExecuteCommandLocked(kSize_rcCreateColorBuffer, id);
+}
+
+void Control::CloseColorBufferLocked(uint32_t id) {
+ TRACE_DURATION("gfx", "Control::CloseColorBuffer", "id", id);
+
+ auto cmd = static_cast<CloseColorBufferCmd*>(io_buffer_.virt());
+ cmd->op = kOP_rcCloseColorBuffer;
+ cmd->size = kSize_rcCloseColorBuffer;
+ cmd->id = id;
+
+ WriteLocked(kSize_rcCloseColorBuffer);
+}
+
+zx_status_t Control::SetColorBufferVulkanModeLocked(uint32_t id, uint32_t mode,
+ uint32_t* result) {
+ TRACE_DURATION("gfx", "Control::SetColorBufferVulkanMode", "id", id, "mode",
+ mode);
+
+ auto cmd = static_cast<SetColorBufferVulkanModeCmd*>(io_buffer_.virt());
+ cmd->op = kOP_rcSetColorBufferVulkanMode;
+ cmd->size = kSize_rcSetColorBufferVulkanMode;
+ cmd->id = id;
+ cmd->mode = mode;
+
+ return ExecuteCommandLocked(kSize_rcSetColorBufferVulkanMode, result);
+}
+
+} // namespace goldfish
+
+static zx_driver_ops_t goldfish_control_driver_ops = []() -> zx_driver_ops_t {
+ zx_driver_ops_t ops;
+ ops.version = DRIVER_OPS_VERSION;
+ ops.bind = goldfish::Control::Create;
+ return ops;
+}();
+
+// clang-format off
+ZIRCON_DRIVER_BEGIN(goldfish_control, goldfish_control_driver_ops, "zircon",
+ "0.1", 1)
+ BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_GOLDFISH_PIPE),
+ZIRCON_DRIVER_END(goldfish_control)
+// clang-format on
diff --git a/zircon/system/dev/misc/goldfish-control/control-device.h b/zircon/system/dev/misc/goldfish-control/control-device.h
new file mode 100644
index 0000000..883f8f6
--- /dev/null
+++ b/zircon/system/dev/misc/goldfish-control/control-device.h
@@ -0,0 +1,87 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ZIRCON_SYSTEM_DEV_MISC_GOLDFISH_CONTROL_CONTROL_DEVICE_H_
+#define ZIRCON_SYSTEM_DEV_MISC_GOLDFISH_CONTROL_CONTROL_DEVICE_H_
+
+#include <ddk/device.h>
+#include <ddk/io-buffer.h>
+#include <ddktl/device.h>
+#include <ddktl/protocol/goldfish/control.h>
+#include <ddktl/protocol/goldfish/pipe.h>
+#include <fbl/condition_variable.h>
+#include <fbl/mutex.h>
+#include <fuchsia/hardware/goldfish/control/c/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/async/cpp/wait.h>
+#include <zircon/thread_annotations.h>
+#include <zircon/types.h>
+
+#include <map>
+
+namespace goldfish {
+
+class Control;
+using ControlType = ddk::Device<Control, ddk::Unbindable, ddk::Messageable,
+ ddk::GetProtocolable>;
+
+class Control
+ : public ControlType,
+ public ddk::GoldfishControlProtocol<Control, ddk::base_protocol> {
+public:
+ static zx_status_t Create(void* ctx, zx_device_t* parent);
+
+ explicit Control(zx_device_t* parent);
+ ~Control();
+
+ zx_status_t Bind();
+
+ void RegisterColorBuffer(zx_koid_t koid);
+ void FreeColorBuffer(zx_koid_t koid);
+
+ zx_status_t FidlCreateColorBuffer(zx_handle_t vmo_handle, uint32_t width,
+ uint32_t height, uint32_t format,
+ fidl_txn_t* txn);
+ zx_status_t FidlGetColorBuffer(zx_handle_t vmo_handle, fidl_txn_t* txn);
+
+ // Device protocol implementation.
+ void DdkUnbind();
+ void DdkRelease();
+ zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
+ zx_status_t DdkGetProtocol(uint32_t proto_id, void* out_protocol);
+ zx_status_t GoldfishControlGetColorBuffer(zx::vmo vmo, uint32_t* out_id);
+
+private:
+ static void OnSignal(void* ctx, int32_t flags);
+ void OnReadable();
+
+ void WriteLocked(uint32_t cmd_size) TA_REQ(lock_);
+ zx_status_t ReadResultLocked(uint32_t* result) TA_REQ(lock_);
+ zx_status_t ExecuteCommandLocked(uint32_t cmd_size, uint32_t* result)
+ TA_REQ(lock_);
+ zx_status_t CreateColorBufferLocked(uint32_t width, uint32_t height,
+ uint32_t format, uint32_t* id)
+ TA_REQ(lock_);
+ void CloseColorBufferLocked(uint32_t id) TA_REQ(lock_);
+ zx_status_t SetColorBufferVulkanModeLocked(uint32_t id, uint32_t mode,
+ uint32_t* result) TA_REQ(lock_);
+
+ fbl::Mutex lock_;
+ fbl::ConditionVariable readable_cvar_;
+ ddk::GoldfishPipeProtocolClient pipe_;
+ ddk::GoldfishControlProtocolClient control_;
+ int32_t id_ = 0;
+ zx::bti bti_ TA_GUARDED(lock_);
+ ddk::IoBuffer cmd_buffer_ TA_GUARDED(lock_);
+ ddk::IoBuffer io_buffer_ TA_GUARDED(lock_);
+ // TODO(TC-383): This should be std::unordered_map.
+ std::map<zx_koid_t, uint32_t> color_buffers_ TA_GUARDED(lock_);
+ async::Loop heap_loop_;
+
+ DISALLOW_COPY_ASSIGN_AND_MOVE(Control);
+};
+
+} // namespace goldfish
+
+#endif // ZIRCON_SYSTEM_DEV_MISC_GOLDFISH_CONTROL_CONTROL_DEVICE_H_
diff --git a/zircon/system/dev/misc/goldfish/pipe-device.cpp b/zircon/system/dev/misc/goldfish/pipe-device.cpp
index 8337a60..0a1298a 100644
--- a/zircon/system/dev/misc/goldfish/pipe-device.cpp
+++ b/zircon/system/dev/misc/goldfish/pipe-device.cpp
@@ -253,6 +253,19 @@
return bti_.duplicate(ZX_RIGHT_SAME_RIGHTS, out_bti);
}
+zx_status_t PipeDevice::GoldfishPipeConnectSysmem(zx::channel connection) {
+ TRACE_DURATION("gfx", "PipeDevice::GoldfishPipeConnectSysmem");
+
+ return acpi_.ConnectSysmem(std::move(connection));
+}
+
+zx_status_t PipeDevice::GoldfishPipeRegisterSysmemHeap(uint64_t heap,
+ zx::channel connection) {
+ TRACE_DURATION("gfx", "PipeDevice::GoldfishPipeRegisterSysmemHeap");
+
+ return acpi_.RegisterSysmemHeap(heap, std::move(connection));
+}
+
int PipeDevice::IrqHandler() {
while (1) {
zx_status_t status = irq_.wait(nullptr);
diff --git a/zircon/system/dev/misc/goldfish/pipe-device.h b/zircon/system/dev/misc/goldfish/pipe-device.h
index 6fd0d66..ddcc639 100644
--- a/zircon/system/dev/misc/goldfish/pipe-device.h
+++ b/zircon/system/dev/misc/goldfish/pipe-device.h
@@ -49,6 +49,9 @@
void GoldfishPipeOpen(int32_t id);
void GoldfishPipeExec(int32_t id);
zx_status_t GoldfishPipeGetBti(zx::bti* out_bti);
+ zx_status_t GoldfishPipeConnectSysmem(zx::channel connection);
+ zx_status_t GoldfishPipeRegisterSysmemHeap(uint64_t heap,
+ zx::channel connection);
int IrqHandler();
diff --git a/zircon/system/dev/sysmem/sysmem/BUILD.gn b/zircon/system/dev/sysmem/sysmem/BUILD.gn
index c79ebce..05ef78c 100644
--- a/zircon/system/dev/sysmem/sysmem/BUILD.gn
+++ b/zircon/system/dev/sysmem/sysmem/BUILD.gn
@@ -14,6 +14,7 @@
"koid_util.cpp",
"logging.cpp",
"logical_buffer_collection.cpp",
+ "memory_allocator.cpp",
"usage_pixel_format_cost.cpp",
]
deps = [
diff --git a/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.cpp b/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.cpp
index 28927d7..610419c 100644
--- a/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.cpp
+++ b/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.cpp
@@ -80,6 +80,10 @@
return ZX_OK;
}
+bool AmlogicMemoryAllocator::CoherencyDomainIsInaccessible() {
+ return false;
+}
+
zx_status_t AmlogicMemoryAllocator::GetProtectedMemoryInfo(uint64_t* base, uint64_t* size) {
*base = start_;
*size = size_;
diff --git a/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.h b/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.h
index 91b38cc..d673441 100644
--- a/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.h
+++ b/zircon/system/dev/sysmem/sysmem/amlogic_memory_allocator.h
@@ -17,6 +17,7 @@
zx_status_t Init(uint64_t size);
zx_status_t Allocate(uint64_t size, zx::vmo* vmo) override;
+ bool CoherencyDomainIsInaccessible() override;
zx_status_t GetProtectedMemoryInfo(uint64_t* base, uint64_t* size) override;
private:
diff --git a/zircon/system/dev/sysmem/sysmem/device.cpp b/zircon/system/dev/sysmem/sysmem/device.cpp
index 3dc4154..336a982 100644
--- a/zircon/system/dev/sysmem/sysmem/device.cpp
+++ b/zircon/system/dev/sysmem/sysmem/device.cpp
@@ -20,6 +20,149 @@
namespace {
+class SystemRamMemoryAllocator : public MemoryAllocator {
+public:
+ zx_status_t Allocate(uint64_t size, zx::vmo* vmo) override {
+ return zx::vmo::create(size, 0, vmo);
+ }
+ bool CoherencyDomainIsInaccessible() override { return false; }
+};
+
+class ContiguousSystemRamMemoryAllocator : public MemoryAllocator {
+public:
+ explicit ContiguousSystemRamMemoryAllocator(Device* parent_device)
+ : parent_device_(parent_device) {}
+
+ zx_status_t Allocate(uint64_t size, zx::vmo* vmo) override {
+ // TODO(dustingreen): Optionally (per board) pre-allocate a
+ // physically-contiguous VMO with board-specific size, have a page heap,
+ // dole out portions of that VMO or non-copy-on-write child VMOs of that
+ // VMO. For now, we just attempt to allocate contiguous when buffers
+ // are allocated, which is unlikely to work after the system has been
+ // running for a while and physical memory is more fragmented than early
+ // during boot.
+ zx_status_t status =
+ zx::vmo::create_contiguous(parent_device_->bti(), size, 0, vmo);
+ if (status != ZX_OK) {
+ DRIVER_ERROR(
+ "zx::vmo::create_contiguous() failed - size_bytes: %lu "
+ "status: %d",
+ size, status);
+ // sanitize to ZX_ERR_NO_MEMORY regardless of why.
+ status = ZX_ERR_NO_MEMORY;
+ return status;
+ }
+ return ZX_OK;
+ }
+ bool CoherencyDomainIsInaccessible() override { return false; }
+
+private:
+ Device* const parent_device_;
+};
+
+class ExternalMemoryAllocator : public MemoryAllocator {
+public:
+ ExternalMemoryAllocator(zx::channel connection,
+ fbl::unique_ptr<async::Wait> wait_for_close)
+ : connection_(std::move(connection)),
+ wait_for_close_(std::move(wait_for_close)) {}
+
+ zx_status_t Allocate(uint64_t size, zx::vmo* vmo) override {
+ zx::vmo parent_vmo;
+ zx_status_t status2 = ZX_OK;
+ zx_status_t status =
+ fuchsia_sysmem_HeapAllocateVmo(connection_.get(), size, &status2,
+ parent_vmo.reset_and_get_address());
+ if (status != ZX_OK || status2 != ZX_OK) {
+ DRIVER_ERROR("HeapAllocate() failed - status: %d status2: %d",
+ status, status2);
+ // sanitize to ZX_ERR_NO_MEMORY regardless of why.
+ status = ZX_ERR_NO_MEMORY;
+ return status;
+ }
+
+ // Create child VMO. This makes it possible to detect when all
+ // references to the VMO are gone by waiting for VMO_ZERO_CHILDREN
+ // signal on parent VMO.
+ //
+ // Note: Always a 1:1 relationship between parent and child VMOs.
+ //
+ // TODO(reveman): Don't assume that copy-on-write VMO is OK.
+ zx::vmo child_vmo;
+ status =
+ parent_vmo.create_child(ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &child_vmo);
+ if (status != ZX_OK) {
+ DRIVER_ERROR("zx::vmo::create_child() failed - status: %d\n",
+ status);
+ // sanitize to ZX_ERR_NO_MEMORY regardless of why.
+ status = ZX_ERR_NO_MEMORY;
+ return status;
+ }
+
+ zx::vmo vmo_copy;
+ status = child_vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy);
+ if (status != ZX_OK) {
+ DRIVER_ERROR("duplicate() failed - status: %d", status);
+ // sanitize to ZX_ERR_NO_MEMORY regardless of why.
+ status = ZX_ERR_NO_MEMORY;
+ return status;
+ }
+
+ uint64_t id;
+ status = fuchsia_sysmem_HeapCreateResource(
+ connection_.get(), vmo_copy.release(), &status2, &id);
+ if (status != ZX_OK || status2 != ZX_OK) {
+ DRIVER_ERROR("HeapCreateResource() failed - status: %d status2: %d",
+ status, status2);
+ // sanitize to ZX_ERR_NO_MEMORY regardless of why.
+ status = ZX_ERR_NO_MEMORY;
+ return status;
+ }
+
+ auto vmo_handle = parent_vmo.get();
+
+ // Free resource when parent VMO has zero references.
+ auto wait = std::make_unique<async::Wait>(
+ vmo_handle, ZX_VMO_ZERO_CHILDREN,
+ async::Wait::Handler([this, id, vmo = std::move(parent_vmo)](
+ async_dispatcher_t* dispatcher,
+ async::Wait* wait, zx_status_t status,
+ const zx_packet_signal_t* signal) mutable {
+ auto it = allocations_.find(vmo.get());
+ if (it == allocations_.end()) {
+ DRIVER_ERROR("Invalid allocation - vmo_handle: %d",
+ vmo.get());
+ return;
+ }
+ status =
+ fuchsia_sysmem_HeapDestroyResource(connection_.get(), id);
+ if (status != ZX_OK) {
+ DRIVER_ERROR("HeapDestroyResource() failed - status: %d",
+ status);
+ // fall-through - this can only fail because resource has
+ // already been destroyed.
+ }
+ allocations_.erase(it);
+ }));
+ // It is safe to call Begin() here before adding entry to the map as
+ // handler will run on current thread.
+ wait->Begin(async_get_default_dispatcher());
+
+ allocations_[vmo_handle] = std::move(wait);
+ *vmo = std::move(child_vmo);
+ return ZX_OK;
+ }
+ bool CoherencyDomainIsInaccessible() override {
+ // TODO(reveman): Add support for CPU/RAM domains to external heaps.
+ return true;
+ }
+
+private:
+ zx::channel connection_;
+ fbl::unique_ptr<async::Wait> wait_for_close_;
+ std::map<zx_handle_t, std::unique_ptr<async::Wait>> allocations_;
+};
+
fuchsia_sysmem_DriverConnector_ops_t driver_connector_ops = {
.Connect = fidl::Binder<Device>::BindMember<&Device::Connect>,
.GetProtectedMemoryInfo = fidl::Binder<Device>::BindMember<&Device::GetProtectedMemoryInfo>,
@@ -44,10 +187,17 @@
return self->Connect(allocator_request_param);
}
+zx_status_t in_proc_sysmem_RegisterHeap(void* ctx, uint64_t heap,
+ zx_handle_t heap_connection_param) {
+ Device* self = static_cast<Device*>(ctx);
+ return self->RegisterHeap(heap, heap_connection_param);
+}
+
// In-proc sysmem interface. Essentially an in-proc version of
// fuchsia.sysmem.DriverConnector.
sysmem_protocol_ops_t in_proc_sysmem_protocol_ops = {
.connect = in_proc_sysmem_Connect,
+ .register_heap = in_proc_sysmem_RegisterHeap,
};
} // namespace
@@ -83,6 +233,11 @@
protected_memory_size = metadata.protected_memory_size;
}
+ allocators_[fuchsia_sysmem_HeapType_SYSTEM_RAM] =
+ std::make_unique<SystemRamMemoryAllocator>();
+ contiguous_system_ram_allocator_ =
+ std::make_unique<ContiguousSystemRamMemoryAllocator>(this);
+
status = pdev_get_bti(&pdev_, 0, bti_.reset_and_get_address());
if (status != ZX_OK) {
DRIVER_ERROR("Failed pdev_get_bti() - status: %d", status);
@@ -104,7 +259,8 @@
DRIVER_ERROR("Failed to init allocator for amlogic protected memory: %d", status);
return status;
}
- protected_allocator_ = std::move(amlogic_allocator);
+ protected_allocator_ = amlogic_allocator.get();
+ allocators_[fuchsia_sysmem_HeapType_AMLOGIC_SECURE] = std::move(amlogic_allocator);
}
pbus_protocol_t pbus;
@@ -168,6 +324,35 @@
return ZX_OK;
}
+zx_status_t Device::RegisterHeap(uint64_t heap, zx_handle_t heap_connection) {
+ zx::channel local_heap_connection(heap_connection);
+
+ // External heaps should not have bit 63 set but bit 60 must be set.
+ if ((heap & 0x8000000000000000) || !(heap & 0x1000000000000000)) {
+ DRIVER_ERROR("Invalid external heap");
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ // Clean up heap allocator after peer closed channel.
+ auto wait_for_close = std::make_unique<async::Wait>(
+ local_heap_connection.get(), ZX_CHANNEL_PEER_CLOSED,
+ async::Wait::Handler([this, heap](async_dispatcher_t* dispatcher,
+ async::Wait* wait, zx_status_t status,
+ const zx_packet_signal_t* signal) {
+ allocators_.erase(heap);
+ }));
+ // It is safe to call Begin() here before adding entry to the map as
+ // handler will run on current thread.
+ wait_for_close->Begin(async_get_default_dispatcher());
+
+ // This replaces any previously registered allocator for heap. This
+ // behavior is preferred as it avoids a potential race-condition during
+ // heap restart.
+ allocators_[heap] = std::make_unique<ExternalMemoryAllocator>(
+ std::move(local_heap_connection), std::move(wait_for_close));
+ return ZX_OK;
+}
+
zx_status_t Device::GetProtectedMemoryInfo(fidl_txn* txn) {
if (!protected_allocator_) {
return fuchsia_sysmem_DriverConnectorGetProtectedMemoryInfo_reply(txn, ZX_ERR_NOT_SUPPORTED, 0u, 0u);
@@ -222,3 +407,17 @@
}
return iter->second;
}
+
+MemoryAllocator* Device::GetAllocator(
+ const fuchsia_sysmem_BufferMemorySettings* settings) {
+ if (settings->heap == fuchsia_sysmem_HeapType_SYSTEM_RAM &&
+ settings->is_physically_contiguous) {
+ return contiguous_system_ram_allocator_.get();
+ }
+
+ auto iter = allocators_.find(settings->heap);
+ if (iter == allocators_.end()) {
+ return nullptr;
+ }
+ return iter->second.get();
+}
diff --git a/zircon/system/dev/sysmem/sysmem/device.h b/zircon/system/dev/sysmem/sysmem/device.h
index 13c5b8c..f177c9d 100644
--- a/zircon/system/dev/sysmem/sysmem/device.h
+++ b/zircon/system/dev/sysmem/sysmem/device.h
@@ -14,7 +14,9 @@
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <fuchsia/sysmem/c/fidl.h>
+#include <lib/async/cpp/wait.h>
#include <lib/zx/bti.h>
+#include <lib/zx/channel.h>
#include <region-alloc/region-alloc.h>
#include "protected_memory_allocator.h"
@@ -35,6 +37,7 @@
//
zx_status_t Connect(zx_handle_t allocator_request);
+ zx_status_t RegisterHeap(uint64_t heap, zx_handle_t heap_connection);
zx_status_t GetProtectedMemoryInfo(fidl_txn* txn);
@@ -58,7 +61,12 @@
BufferCollectionToken* FindTokenByServerChannelKoid(
zx_koid_t token_server_koid);
- ProtectedMemoryAllocator* protected_allocator() { return protected_allocator_.get(); }
+ // Get allocator for |settings|. Returns NULL if allocator is not
+ // registered for settings.
+ MemoryAllocator* GetAllocator(
+ const fuchsia_sysmem_BufferMemorySettings* settings);
+
+ ProtectedMemoryAllocator* protected_allocator() { return protected_allocator_; }
private:
zx_device_t* parent_device_ = nullptr;
@@ -81,7 +89,12 @@
// the server end of a BufferCollectionToken channel.
std::map<zx_koid_t, BufferCollectionToken*> tokens_by_koid_;
- fbl::unique_ptr<ProtectedMemoryAllocator> protected_allocator_;
+ // This map contains all registered memory allocators.
+ std::map<fuchsia_sysmem_HeapType, fbl::unique_ptr<MemoryAllocator>> allocators_;
+
+ fbl::unique_ptr<MemoryAllocator> contiguous_system_ram_allocator_;
+
+ ProtectedMemoryAllocator* protected_allocator_ = nullptr;
};
#endif // ZIRCON_SYSTEM_DEV_SYSMEM_SYSMEM_DEVICE_H_
diff --git a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp
index 7613e5d..9178847 100644
--- a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp
+++ b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp
@@ -253,6 +253,11 @@
// empty.
ZX_DEBUG_ASSERT(token_views_.empty());
ZX_DEBUG_ASSERT(collection_views_.empty());
+
+ if (memory_allocator_) {
+ memory_allocator_->RemoveDestroyCallback(
+ reinterpret_cast<intptr_t>(this));
+ }
}
void LogicalBufferCollection::Fail(const char* format, ...) {
@@ -551,6 +556,11 @@
return false;
}
if (constraints->has_buffer_memory_constraints) {
+ if (IsCpuUsage(constraints->usage) &&
+ constraints->buffer_memory_constraints.inaccessible_domain_supported) {
+ LogError("IsCpuUsage && inaccessible_domain_supported doesn't make sense.");
+ return false;
+ }
if (!CheckSanitizeBufferMemoryConstraints(
&constraints->buffer_memory_constraints)) {
return false;
@@ -565,19 +575,28 @@
return true;
}
+static bool
+IsHeapPermitted(const fuchsia_sysmem_BufferMemoryConstraints* constraints,
+ fuchsia_sysmem_HeapType heap) {
+ if (constraints->heap_permitted_count) {
+ auto begin = constraints->heap_permitted;
+ auto end =
+ constraints->heap_permitted + constraints->heap_permitted_count;
+ return std::find(begin, end, heap) != end;
+ }
+ return true;
+}
+
bool LogicalBufferCollection::CheckSanitizeBufferMemoryConstraints(
fuchsia_sysmem_BufferMemoryConstraints* constraints) {
- if (!constraints->ram_domain_supported &&
- !constraints->cpu_domain_supported) {
- LogError("Neither RAM nor CPU coherency domains supported");
- return false;
- }
if (constraints->min_size_bytes > constraints->max_size_bytes) {
LogError("min_size_bytes > max_size_bytes");
return false;
}
- if (constraints->secure_required && !constraints->secure_permitted) {
- LogError("secure_required && !secure_permitted");
+ bool secure_permitted =
+ IsHeapPermitted(constraints, fuchsia_sysmem_HeapType_AMLOGIC_SECURE);
+ if (constraints->secure_required && !secure_permitted) {
+ LogError("secure memory required but not permitted");
return false;
}
return true;
@@ -834,6 +853,42 @@
return true;
}
+bool LogicalBufferCollection::AccumulateConstraintHeapPermitted(
+ uint32_t* acc_count, fuchsia_sysmem_HeapType acc[],
+ uint32_t c_count, const fuchsia_sysmem_HeapType c[]) {
+ // Remove any heap in acc that's not in c. If zero heaps
+ // remain in acc, return false.
+ ZX_DEBUG_ASSERT(*acc_count > 0);
+
+ for (uint32_t ai = 0; ai < *acc_count; ++ai) {
+ uint32_t ci;
+ for (ci = 0; ci < c_count; ++ci) {
+ if (acc[ai] == c[ci]) {
+ // We found heap in c. Break so we can move on to
+ // the next heap.
+ break;
+ }
+ }
+ if (ci == c_count) {
+ // remove from acc because not found in c
+ --(*acc_count);
+ // copy of formerly last item on top of the item being
+ // removed
+ acc[ai] = acc[*acc_count];
+ // adjust ai to force current index to be processed again as it's
+ // now a different item
+ --ai;
+ }
+ }
+
+ if (!*acc_count) {
+ LogError("Zero heap permitted overlap");
+ return false;
+ }
+
+ return true;
+}
+
bool LogicalBufferCollection::AccumulateConstraintBufferMemory(
fuchsia_sysmem_BufferMemoryConstraints* acc,
const fuchsia_sysmem_BufferMemoryConstraints* c) {
@@ -852,11 +907,26 @@
c->physically_contiguous_required;
acc->secure_required = acc->secure_required || c->secure_required;
- acc->secure_permitted = acc->secure_permitted && c->secure_permitted;
acc->ram_domain_supported = acc->ram_domain_supported && c->ram_domain_supported;
acc->cpu_domain_supported = acc->cpu_domain_supported && c->cpu_domain_supported;
+ acc->inaccessible_domain_supported =
+ acc->inaccessible_domain_supported && c->inaccessible_domain_supported;
+ if (!acc->heap_permitted_count) {
+ std::copy(c->heap_permitted,
+ c->heap_permitted + c->heap_permitted_count,
+ acc->heap_permitted);
+ acc->heap_permitted_count = c->heap_permitted_count;
+ } else {
+ if (c->heap_permitted_count) {
+ if (!AccumulateConstraintHeapPermitted(
+ &acc->heap_permitted_count, acc->heap_permitted,
+ c->heap_permitted_count, c->heap_permitted)) {
+ return false;
+ }
+ }
+ }
return true;
}
@@ -1049,20 +1119,77 @@
return a.type == b.type;
}
-static fuchsia_sysmem_CoherencyDomain
-GetCoherencyDomain(const fuchsia_sysmem_BufferCollectionConstraints* constraints) {
- if (!constraints->has_buffer_memory_constraints)
- return fuchsia_sysmem_CoherencyDomain_Cpu;
+static uint64_t
+GetHeap(const fuchsia_sysmem_BufferMemoryConstraints* constraints) {
+ if (constraints->secure_required) {
+ // checked previously
+ ZX_DEBUG_ASSERT(
+ !(constraints->secure_required &&
+ !IsHeapPermitted(constraints,
+ fuchsia_sysmem_HeapType_AMLOGIC_SECURE)));
+ return fuchsia_sysmem_HeapType_AMLOGIC_SECURE;
+ }
+ if (IsHeapPermitted(constraints, fuchsia_sysmem_HeapType_SYSTEM_RAM)) {
+ return fuchsia_sysmem_HeapType_SYSTEM_RAM;
+ }
+ ZX_DEBUG_ASSERT(constraints->heap_permitted_count);
+ return constraints->heap_permitted[0];
+}
- if (!constraints->buffer_memory_constraints.ram_domain_supported)
- return fuchsia_sysmem_CoherencyDomain_Cpu;
- if (!constraints->buffer_memory_constraints.cpu_domain_supported)
- return fuchsia_sysmem_CoherencyDomain_Ram;
+static bool GetCoherencyDomain(
+ const fuchsia_sysmem_BufferCollectionConstraints* constraints,
+ MemoryAllocator* memory_allocator,
+ fuchsia_sysmem_CoherencyDomain* domain_out) {
+ if (!constraints->has_buffer_memory_constraints) {
+ // CPU domain by default.
+ *domain_out = fuchsia_sysmem_CoherencyDomain_Cpu;
+ return true;
+ }
- // Display controllers generally aren't cache coherent.
- // TODO - base on the system in use.
- return constraints->usage.display != 0 ? fuchsia_sysmem_CoherencyDomain_Ram
- : fuchsia_sysmem_CoherencyDomain_Cpu;
+ // The heap not being accessible from the CPU can force Inaccessible as the only
+ // potential option.
+ if (memory_allocator->CoherencyDomainIsInaccessible()) {
+ if (!constraints->buffer_memory_constraints.inaccessible_domain_supported) {
+ return false;
+ }
+ *domain_out = fuchsia_sysmem_CoherencyDomain_Inaccessible;
+ return true;
+ }
+
+ // Display prefers RAM coherency domain for now.
+ if (constraints->usage.display != 0) {
+ if (constraints->buffer_memory_constraints.ram_domain_supported) {
+ // Display controllers generally aren't cache coherent, so prefer
+ // RAM coherency domain.
+ //
+ // TODO - base on the system in use.
+ *domain_out = fuchsia_sysmem_CoherencyDomain_Ram;
+ return true;
+ }
+ }
+
+ // If none of the above cases apply, then prefer CPU, RAM, Inaccessible
+ // in that order.
+
+ if (constraints->buffer_memory_constraints.cpu_domain_supported) {
+ *domain_out = fuchsia_sysmem_CoherencyDomain_Cpu;
+ return true;
+ }
+
+ if (constraints->buffer_memory_constraints.ram_domain_supported) {
+ *domain_out = fuchsia_sysmem_CoherencyDomain_Ram;
+ return true;
+ }
+
+ if (constraints->buffer_memory_constraints.inaccessible_domain_supported) {
+ // Intentionally permit treating as Inaccessible if we reach here, even
+ // if the heap permits CPU access. Only domain in common among
+ // participants is Inaccessible.
+ *domain_out = fuchsia_sysmem_CoherencyDomain_Inaccessible;
+ return true;
+ }
+
+ return false;
}
BufferCollection::BufferCollectionInfo
@@ -1123,17 +1250,35 @@
buffer_constraints->physically_contiguous_required;
// checked previously
ZX_DEBUG_ASSERT(!(buffer_constraints->secure_required &&
- !buffer_constraints->secure_permitted));
- // checked previously
- ZX_DEBUG_ASSERT(!(buffer_constraints->secure_required &&
IsCpuUsage(constraints_->usage)));
buffer_settings->is_secure = buffer_constraints->secure_required;
+ buffer_settings->heap = GetHeap(buffer_constraints);
// We can't fill out buffer_settings yet because that also depends on
// ImageFormatConstraints. We do need the min and max from here though.
min_size_bytes = buffer_constraints->min_size_bytes;
max_size_bytes = buffer_constraints->max_size_bytes;
}
- buffer_settings->coherency_domain = GetCoherencyDomain(constraints_.get());
+
+ // Get memory allocator for settings.
+ MemoryAllocator* allocator = parent_device_->GetAllocator(buffer_settings);
+ if (!allocator) {
+ LogError("No memory allocator for buffer settings");
+ *allocation_result = ZX_ERR_NO_MEMORY;
+ return BufferCollection::BufferCollectionInfo(
+ BufferCollection::BufferCollectionInfo::Null);
+ }
+
+ if (!GetCoherencyDomain(constraints_.get(), allocator,
+ &buffer_settings->coherency_domain)) {
+ LogError("No coherency domain found for buffer constraints");
+ *allocation_result = ZX_ERR_NOT_SUPPORTED;
+ return BufferCollection::BufferCollectionInfo(
+ BufferCollection::BufferCollectionInfo::Null);
+ }
+
+ ZX_DEBUG_ASSERT(
+ constraints_->usage.cpu == 0 || buffer_settings->coherency_domain !=
+ fuchsia_sysmem_CoherencyDomain_Inaccessible);
// It's allowed for zero participants to have any ImageFormatConstraint(s),
// in which case the combined constraints_ will have zero (and that's fine,
@@ -1280,7 +1425,7 @@
// Assign directly into result to benefit from FidlStruct<> management
// of handle lifetime.
zx::vmo vmo;
- zx_status_t allocate_result = AllocateVmo(settings, &vmo);
+ zx_status_t allocate_result = AllocateVmo(allocator, settings, &vmo);
if (allocate_result != ZX_OK) {
ZX_DEBUG_ASSERT(allocate_result == ZX_ERR_NO_MEMORY);
LogError("AllocateVmo() failed - status: %d", allocate_result);
@@ -1294,79 +1439,37 @@
result->buffers[i].vmo = vmo.release();
}
+ // Register failure handler with memory allocator.
+ allocator->AddDestroyCallback(reinterpret_cast<intptr_t>(this), [this](){
+ Fail("LogicalBufferCollection memory allocator gone - now auto-failing self.");
+ });
+ memory_allocator_ = allocator;
+
ZX_DEBUG_ASSERT(*allocation_result == ZX_OK);
return result;
}
zx_status_t LogicalBufferCollection::AllocateVmo(
- const fuchsia_sysmem_SingleBufferSettings* settings, zx::vmo* vmo) {
- if (settings->buffer_settings.is_secure) {
- ProtectedMemoryAllocator* allocator = parent_device_->protected_allocator();
- if (!allocator) {
- LogError("No protected memory allocator");
- return ZX_ERR_NOT_SUPPORTED;
- }
-
- zx::vmo raw_vmo;
- zx_status_t status = allocator->Allocate(
- settings->buffer_settings.size_bytes, &raw_vmo);
- if (status != ZX_OK) {
- LogError("Protected allocate failed - size_bytes: %u "
- "status: %d",
- settings->buffer_settings.size_bytes, status);
- // sanitize to ZX_ERR_NO_MEMORY regardless of why.
- status = ZX_ERR_NO_MEMORY;
- return status;
- }
- status = raw_vmo.duplicate(kSysmemVmoRights, vmo);
- if (status != ZX_OK) {
- LogError("zx::object::duplicate() failed - status: %d", status);
- return status;
- }
- } else if (settings->buffer_settings.is_physically_contiguous) {
- // TODO(dustingreen): Optionally (per board) pre-allocate a
- // physically-contiguous VMO with board-specific size, have a page heap,
- // dole out portions of that VMO or non-copy-on-write child VMOs of that
- // VMO. For now, we just attempt to allocate contiguous when buffers
- // are allocated, which is unlikely to work after the system has been
- // running for a while and physical memory is more fragmented than early
- // during boot.
- zx::vmo raw_vmo;
- zx_status_t status = zx::vmo::create_contiguous(
- parent_device_->bti(), settings->buffer_settings.size_bytes, 0,
- &raw_vmo);
- if (status != ZX_OK) {
- LogError("zx::vmo::create_contiguous() failed - size_bytes: %u "
- "status: %d",
- settings->buffer_settings.size_bytes, status);
- // sanitize to ZX_ERR_NO_MEMORY regardless of why.
- status = ZX_ERR_NO_MEMORY;
- return status;
- }
- status = raw_vmo.duplicate(kSysmemVmoRights, vmo);
- if (status != ZX_OK) {
- LogError("zx::object::duplicate() failed - status: %d", status);
- return status;
- }
- // ~raw_vmo - *vmo is a duplicate with slightly-reduced rights.
- } else {
- zx::vmo raw_vmo;
- zx_status_t status =
- zx::vmo::create(settings->buffer_settings.size_bytes, 0, &raw_vmo);
- if (status != ZX_OK) {
- LogError("zx::vmo::create() failed - size_bytes: %u status: %d",
- settings->buffer_settings.size_bytes, status);
- // sanitize to ZX_ERR_NO_MEMORY regardless of why.
- status = ZX_ERR_NO_MEMORY;
- return status;
- }
- status = raw_vmo.duplicate(kSysmemVmoRights, vmo);
- if (status != ZX_OK) {
- LogError("zx::object::duplicate() failed - status: %d", status);
- return status;
- }
- // ~raw_vmo - *vmo is a duplicate with slightly-reduced rights.
+ MemoryAllocator* allocator,
+ const fuchsia_sysmem_SingleBufferSettings* settings,
+ zx::vmo* vmo) {
+ zx::vmo raw_vmo;
+ zx_status_t status =
+ allocator->Allocate(settings->buffer_settings.size_bytes, &raw_vmo);
+ if (status != ZX_OK) {
+ LogError("Allocate failed - size_bytes: %u "
+ "status: %d",
+ settings->buffer_settings.size_bytes, status);
+ // sanitize to ZX_ERR_NO_MEMORY regardless of why.
+ status = ZX_ERR_NO_MEMORY;
+ return status;
}
+ status = raw_vmo.duplicate(kSysmemVmoRights, vmo);
+ if (status != ZX_OK) {
+ LogError("zx::object::duplicate() failed - status: %d", status);
+ return status;
+ }
+ // ~raw_vmo - *vmo is a duplicate with slightly-reduced rights.
return ZX_OK;
}
diff --git a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h
index 6636ef1..c3afa89 100644
--- a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h
+++ b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h
@@ -23,6 +23,7 @@
class BufferCollectionToken;
class BufferCollection;
+class MemoryAllocator;
class LogicalBufferCollection
: public fbl::RefCounted<LogicalBufferCollection> {
public:
@@ -120,6 +121,11 @@
fuchsia_sysmem_BufferCollectionConstraints* acc,
const fuchsia_sysmem_BufferCollectionConstraints* c);
+ bool AccumulateConstraintHeapPermitted(uint32_t* acc_count,
+ fuchsia_sysmem_HeapType acc[],
+ uint32_t c_count,
+ const fuchsia_sysmem_HeapType c[]);
+
bool AccumulateConstraintBufferMemory(
fuchsia_sysmem_BufferMemoryConstraints* acc,
const fuchsia_sysmem_BufferMemoryConstraints* c);
@@ -142,7 +148,8 @@
BufferCollectionInfo Allocate(zx_status_t* allocation_result);
- zx_status_t AllocateVmo(const fuchsia_sysmem_SingleBufferSettings* settings,
+ zx_status_t AllocateVmo(MemoryAllocator* allocator,
+ const fuchsia_sysmem_SingleBufferSettings* settings,
zx::vmo* vmo);
int32_t CompareImageFormatConstraintsTieBreaker(
@@ -175,6 +182,8 @@
bool has_allocation_result_ = false;
zx_status_t allocation_result_status_ = ZX_OK;
BufferCollectionInfo allocation_result_info_{BufferCollectionInfo::Null};
+
+ MemoryAllocator* memory_allocator_ = nullptr;
};
#endif // ZIRCON_SYSTEM_DEV_SYSMEM_SYSMEM_LOGICAL_BUFFER_COLLECTION_H_
diff --git a/zircon/system/dev/sysmem/sysmem/memory_allocator.cpp b/zircon/system/dev/sysmem/sysmem/memory_allocator.cpp
new file mode 100644
index 0000000..9cb6737
--- /dev/null
+++ b/zircon/system/dev/sysmem/sysmem/memory_allocator.cpp
@@ -0,0 +1,25 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "memory_allocator.h"
+
+#include <zircon/assert.h>
+
+MemoryAllocator::~MemoryAllocator() {
+ for (auto& it : destroy_callbacks_) {
+ it.second();
+ }
+}
+
+void MemoryAllocator::AddDestroyCallback(intptr_t key,
+ fit::callback<void()> callback) {
+ ZX_DEBUG_ASSERT(destroy_callbacks_.find(key) == destroy_callbacks_.end());
+ destroy_callbacks_[key] = std::move(callback);
+}
+
+void MemoryAllocator::RemoveDestroyCallback(intptr_t key) {
+ // The key isn't required to be in the map in case of failures during
+ // create. Erase if present.
+ destroy_callbacks_.erase(key);
+}
diff --git a/zircon/system/dev/sysmem/sysmem/memory_allocator.h b/zircon/system/dev/sysmem/sysmem/memory_allocator.h
new file mode 100644
index 0000000..98d41eb
--- /dev/null
+++ b/zircon/system/dev/sysmem/sysmem/memory_allocator.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <lib/fit/function.h>
+#include <lib/zx/vmo.h>
+
+#include <map>
+
+class MemoryAllocator {
+public:
+ virtual ~MemoryAllocator();
+
+ virtual zx_status_t Allocate(uint64_t size, zx::vmo* vmo) = 0;
+ virtual bool CoherencyDomainIsInaccessible() = 0;
+
+ void AddDestroyCallback(intptr_t key, fit::callback<void()> callback);
+ void RemoveDestroyCallback(intptr_t key);
+
+public:
+ std::map<intptr_t, fit::callback<void()>> destroy_callbacks_;
+};
diff --git a/zircon/system/dev/sysmem/sysmem/protected_memory_allocator.h b/zircon/system/dev/sysmem/sysmem/protected_memory_allocator.h
index e7cded2..4cec46f 100644
--- a/zircon/system/dev/sysmem/sysmem/protected_memory_allocator.h
+++ b/zircon/system/dev/sysmem/sysmem/protected_memory_allocator.h
@@ -6,10 +6,11 @@
#include <lib/zx/vmo.h>
-class ProtectedMemoryAllocator {
+#include "memory_allocator.h"
+
+class ProtectedMemoryAllocator : public MemoryAllocator {
public:
virtual ~ProtectedMemoryAllocator() {}
- virtual zx_status_t Allocate(uint64_t size, zx::vmo* vmo) = 0;
virtual zx_status_t GetProtectedMemoryInfo(uint64_t* base, uint64_t* size) = 0;
};
diff --git a/zircon/system/fidl/BUILD.gn b/zircon/system/fidl/BUILD.gn
index 69da451..0b55ac8 100644
--- a/zircon/system/fidl/BUILD.gn
+++ b/zircon/system/fidl/BUILD.gn
@@ -31,6 +31,7 @@
"fuchsia-hardware-ethernet",
"fuchsia-hardware-ethertap",
"fuchsia-hardware-goldfish-address-space",
+ "fuchsia-hardware-goldfish-control",
"fuchsia-hardware-goldfish-pipe",
"fuchsia-hardware-gpu-clock",
"fuchsia-hardware-hidctl",
diff --git a/zircon/system/fidl/fuchsia-hardware-goldfish-control/BUILD.gn b/zircon/system/fidl/fuchsia-hardware-goldfish-control/BUILD.gn
new file mode 100644
index 0000000..e43c421
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-hardware-goldfish-control/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("$zx/public/gn/fidl.gni")
+
+fidl_library("fuchsia-hardware-goldfish-control") {
+ sources = [
+ "goldfish-control.fidl",
+ ]
+}
diff --git a/zircon/system/fidl/fuchsia-hardware-goldfish-control/goldfish-control.fidl b/zircon/system/fidl/fuchsia-hardware-goldfish-control/goldfish-control.fidl
new file mode 100644
index 0000000..4ff2d09
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-hardware-goldfish-control/goldfish-control.fidl
@@ -0,0 +1,29 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.hardware.goldfish.control;
+
+using zx;
+
+/// Color buffer formats.
+enum FormatType : uint32 {
+ RGBA = 0x1908;
+ BGRA = 0x80E1;
+};
+
+[Layout = "Simple"]
+protocol Device {
+ /// Create shared color buffer. Color buffer is automatically freed when
+ /// all references to |vmo| have been closed. Fails if VMO is not
+ /// associated with goldfish heap memory.
+ CreateColorBuffer(handle<vmo> vmo,
+ uint32 width,
+ uint32 height,
+ FormatType format)
+ -> (zx.status res);
+
+ /// Get color buffer for VMO. Fails if VMO is not associated with a color
+ /// buffer.
+ GetColorBuffer(handle<vmo> vmo) -> (zx.status res, uint32 id);
+};
diff --git a/zircon/system/fidl/fuchsia-sysmem/BUILD.gn b/zircon/system/fidl/fuchsia-sysmem/BUILD.gn
index 7cc35ef..0d7a779 100644
--- a/zircon/system/fidl/fuchsia-sysmem/BUILD.gn
+++ b/zircon/system/fidl/fuchsia-sysmem/BUILD.gn
@@ -14,6 +14,7 @@
"driver_connector.fidl",
"format_modifier.fidl",
"formats_deprecated.fidl",
+ "heap.fidl",
"image_formats.fidl",
"image_formats_deprecated.fidl",
"usages.fidl",
diff --git a/zircon/system/fidl/fuchsia-sysmem/constraints.fidl b/zircon/system/fidl/fuchsia-sysmem/constraints.fidl
index 4690595..2289691 100644
--- a/zircon/system/fidl/fuchsia-sysmem/constraints.fidl
+++ b/zircon/system/fidl/fuchsia-sysmem/constraints.fidl
@@ -172,6 +172,19 @@
ImageFormatConstraints image_format_constraints;
};
+/// Known heap types.
+/// Device specific types should have bit 60 set. Top order bit is reserved
+/// and should not be set.
+enum HeapType : uint64 {
+ SYSTEM_RAM = 0x0000000000000000;
+
+ /// Heap used for amlogic protected memory.
+ AMLOGIC_SECURE = 0x1000000000010000;
+
+ /// Heap used by goldfish vulkan for device-local memory.
+ GOLDFISH_DEVICE_LOCAL = 0x1000000000020000;
+};
+
struct BufferMemoryConstraints {
uint32 min_size_bytes = 0;
uint32 max_size_bytes = 0xFFFFFFFF;
@@ -183,12 +196,6 @@
/// When aggregating BufferCollectionConstraints, these values boolean-OR.
bool secure_required = false;
- /// If false, at least one participant can't handle secure memory. All
- /// participants that can handle secure memory must specify true here.
- ///
- /// When aggregating BufferCollectionConstraints, these values boolean-AND.
- bool secure_permitted = false;
-
/// By default, participants must ensure the CPU can read or write data to
/// the buffer without cache operations. If they support using the RAM
/// domain, data must be available in RAM (with CPU cache state such that
@@ -198,11 +205,26 @@
/// zero stale "clean" cache lines)
bool ram_domain_supported = false;
bool cpu_domain_supported = true;
+ bool inaccessible_domain_supported = false;
+
+ /// Optional heap constraints. Participants that don't care which heap
+ /// memory is allocated on should leave this field 0.
+ uint32 heap_permitted_count;
+ array<HeapType>:32 heap_permitted;
};
+/// Inaccessible is only for cases where there is no CPU-based access to the
+/// buffers. A secure_required buffer can still have CoherencyDomain Cpu or
+/// Ram even if the secure_required buffer can only be accessed by the CPU when
+/// the CPU is running in secure mode (or similar). In contrast, device-local
+/// memory that isn't reachable from the CPU is CoherencyDomain Inaccessible,
+/// even if it's possible to cause a device (physical or virtual) to copy the
+/// data from the Inaccessible buffers to buffers that are visible to the CPU.
+/// TODO: Use upper snake case for these enum members.
enum CoherencyDomain {
Cpu = 0;
Ram = 1;
+ Inaccessible = 2;
};
struct BufferMemorySettings {
@@ -210,6 +232,9 @@
bool is_physically_contiguous;
bool is_secure;
CoherencyDomain coherency_domain;
+ /// The specific heap from which buffers are allocated.
+ /// See above in this file for heap identifier values.
+ HeapType heap;
};
/// Describes constraints on layout of image data in buffers.
diff --git a/zircon/system/fidl/fuchsia-sysmem/heap.fidl b/zircon/system/fidl/fuchsia-sysmem/heap.fidl
new file mode 100644
index 0000000..60213e6
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-sysmem/heap.fidl
@@ -0,0 +1,54 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.sysmem;
+
+using zx;
+
+/// Manages resources on a specific sysmem heap.
+///
+/// Needs Layout = "Simple" because used with "FIDL Simple C Bindings".
+[Layout = "Simple"]
+protocol Heap {
+ /// Request a new memory allocation of |size| on heap.
+ /// For heaps which don't permit CPU access to the buffer data, this
+ /// will create a VMO with an official size, but which never has any
+ /// physical pages. For such heaps, the VMO is effectively used as
+ /// an opaque buffer identifier.
+ ///
+ /// Heaps should defer allocation of any associated resources until
+ /// CreateResource(), because the caller of AllocateVmo() may simply
+ /// delete the returned VMO with no further notification to the heap.
+ /// In contrast, after CreateResource(), the caller guarantees that
+ /// DestroyResource() or heap channel closure will occur.
+ ///
+ /// The caller guarantees that CreateResource() will be called prior
+ /// to the returned VMO or any associated child VMO being used.
+ AllocateVmo(uint64 size) -> (zx.status s, handle<vmo>? vmo);
+
+ /// Create resources and associate heap-specific resources with the
+ /// passed-in VMO. Resources can be hardware specific and their
+ /// lifetime don't have to be tied to |vmo|. |vmo| must be a VMO
+ /// (or a direct or indirect child of a VMO) acquired through a call
+ /// to AllocateVmo method above. If the passed-in vmo is a child VMO,
+ /// it's size must match the size of the parent VMO created by
+ /// AllocateVmo(). For heaps that permit CPU access, the passed-in
+ /// VMO must not have a copy-on-write relationship with the parent
+ /// VMO, but rather a pass-through relationship. Successful return
+ /// status indicate that Heap has established a mapping between
+ /// VMO and hardware specific resources.
+ ///
+ /// The returned id must be passed to DestroyResource() later when
+ /// resources associated with VMO are no longer needed, unless the
+ /// heap channel closes first.
+ ///
+ /// The heap must not own/keep a handle to VMO, or any derived child
+ /// VMO, or any VMAR mapping to VMO, as any of those would keep VMO
+ /// alive beyond all sysmem participant usages of the vmo; instead
+ /// the heap can get the vmo's koid for the heap's mapping.
+ CreateResource(handle<vmo> vmo) -> (zx.status s, uint64 id);
+
+ /// Destroy previously created resources.
+ DestroyResource(uint64 id) -> ();
+};
diff --git a/zircon/system/ulib/ddk/include/ddk/protodefs.h b/zircon/system/ulib/ddk/include/ddk/protodefs.h
index 4588ad6..4e31f6d 100644
--- a/zircon/system/ulib/ddk/include/ddk/protodefs.h
+++ b/zircon/system/ulib/ddk/include/ddk/protodefs.h
@@ -24,6 +24,7 @@
DDK_PROTOCOL_DEF(ETHMAC, 'pEMA', "ethmac", 0)
DDK_PROTOCOL_DEF(FRAMEBUFFER, 'pFRB', "framebuffer", 0)
DDK_PROTOCOL_DEF(GOLDFISH_ADDRESS_SPACE, 'pGFA', "goldfish-address-space", 0)
+DDK_PROTOCOL_DEF(GOLDFISH_CONTROL, 'pGFC', "goldfish-control", 0)
DDK_PROTOCOL_DEF(GOLDFISH_PIPE, 'pGFP', "goldfish-pipe", 0)
DDK_PROTOCOL_DEF(GPIO, 'pGPO', "gpio", PF_NOPUB)
DDK_PROTOCOL_DEF(GPIO_IMPL, 'pGPI', "gpio-impl", PF_NOPUB)
diff --git a/zircon/system/utest/goldfish/BUILD.gn b/zircon/system/utest/goldfish/BUILD.gn
index 2a52ec6..03c7ce6 100644
--- a/zircon/system/utest/goldfish/BUILD.gn
+++ b/zircon/system/utest/goldfish/BUILD.gn
@@ -8,9 +8,12 @@
]
deps = [
"$zx/system/fidl/fuchsia-hardware-goldfish-address-space:c",
+ "$zx/system/fidl/fuchsia-hardware-goldfish-control:c",
"$zx/system/fidl/fuchsia-hardware-goldfish-pipe:c",
+ "$zx/system/fidl/fuchsia-sysmem:c",
"$zx/system/ulib/fdio",
"$zx/system/ulib/fidl",
+ "$zx/system/ulib/fidl-async-2",
"$zx/system/ulib/unittest",
"$zx/system/ulib/zircon",
"$zx/system/ulib/zx",
diff --git a/zircon/system/utest/goldfish/goldfish-test.cpp b/zircon/system/utest/goldfish/goldfish-test.cpp
index 0b59dbe..4a6746816 100644
--- a/zircon/system/utest/goldfish/goldfish-test.cpp
+++ b/zircon/system/utest/goldfish/goldfish-test.cpp
@@ -4,8 +4,12 @@
#include <fcntl.h>
#include <fuchsia/hardware/goldfish/address/space/c/fidl.h>
+#include <fuchsia/hardware/goldfish/control/c/fidl.h>
#include <fuchsia/hardware/goldfish/pipe/c/fidl.h>
+#include <fuchsia/sysmem/c/fidl.h>
+#include <lib/fdio/directory.h>
#include <lib/fdio/fdio.h>
+#include <lib/fidl-async-2/fidl_struct.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <unistd.h>
@@ -96,6 +100,113 @@
RUN_TEST(GoldfishPipeTest)
END_TEST_CASE(GoldfishPipeTests)
+extern const fidl_type_t fuchsia_sysmem_BufferCollectionConstraintsTable;
+using BufferCollectionConstraints =
+ FidlStruct<fuchsia_sysmem_BufferCollectionConstraints,
+ &fuchsia_sysmem_BufferCollectionConstraintsTable>;
+extern const fidl_type_t fuchsia_sysmem_BufferCollectionInfo_2Table;
+using BufferCollectionInfo =
+ FidlStruct<fuchsia_sysmem_BufferCollectionInfo_2,
+ &fuchsia_sysmem_BufferCollectionInfo_2Table>;
+
+static bool GoldfishControlTest() {
+ BEGIN_TEST;
+
+ int fd = open("/dev/class/goldfish-control/000", O_RDWR);
+ EXPECT_GE(fd, 0);
+
+ zx::channel channel;
+ EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()),
+ ZX_OK);
+
+ zx::channel allocator_client;
+ zx::channel allocator_server;
+ EXPECT_EQ(zx::channel::create(0, &allocator_client, &allocator_server),
+ ZX_OK);
+ EXPECT_EQ(fdio_service_connect("/svc/fuchsia.sysmem.Allocator",
+ allocator_server.release()),
+ ZX_OK);
+
+ zx::channel token_client;
+ zx::channel token_server;
+ EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK);
+ EXPECT_EQ(fuchsia_sysmem_AllocatorAllocateSharedCollection(
+ allocator_client.get(), token_server.release()),
+ ZX_OK);
+
+ zx::channel collection_client;
+ zx::channel collection_server;
+ EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server),
+ ZX_OK);
+ EXPECT_EQ(fuchsia_sysmem_AllocatorBindSharedCollection(
+ allocator_client.get(), token_client.release(),
+ collection_server.release()),
+ ZX_OK);
+
+ BufferCollectionConstraints constraints(
+ BufferCollectionConstraints::Default);
+ constraints->usage.vulkan = fuchsia_sysmem_vulkanUsageTransferDst;
+ constraints->min_buffer_count_for_camping = 1;
+ constraints->has_buffer_memory_constraints = true;
+ constraints->buffer_memory_constraints =
+ fuchsia_sysmem_BufferMemoryConstraints{
+ .min_size_bytes = 4 * 1024,
+ .max_size_bytes = 4 * 1024,
+ .physically_contiguous_required = false,
+ .secure_required = false,
+ .ram_domain_supported = false,
+ .cpu_domain_supported = false,
+ .inaccessible_domain_supported = true,
+ .heap_permitted_count = 1,
+ .heap_permitted = {fuchsia_sysmem_HeapType_GOLDFISH_DEVICE_LOCAL}};
+ EXPECT_EQ(fuchsia_sysmem_BufferCollectionSetConstraints(
+ collection_client.get(), true, constraints.release()),
+ ZX_OK);
+
+ zx_status_t status2 = ZX_OK;
+ BufferCollectionInfo info(BufferCollectionInfo::Default);
+ EXPECT_EQ(fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated(
+ collection_client.get(), &status2, info.get()),
+ ZX_OK);
+ EXPECT_EQ(status2, ZX_OK);
+ EXPECT_EQ(info->buffer_count, 1);
+ EXPECT_NE(info->buffers[0].vmo, ZX_HANDLE_INVALID);
+
+ zx::vmo vmo(info->buffers[0].vmo);
+ info->buffers[0].vmo = ZX_HANDLE_INVALID;
+ EXPECT_TRUE(vmo.is_valid());
+
+ EXPECT_EQ(fuchsia_sysmem_BufferCollectionClose(collection_client.get()),
+ ZX_OK);
+
+ zx::vmo vmo_copy;
+ EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
+
+ status2 = ZX_OK;
+ EXPECT_EQ(fuchsia_hardware_goldfish_control_DeviceCreateColorBuffer(
+ channel.get(), vmo_copy.release(), 64, 64,
+ fuchsia_hardware_goldfish_control_FormatType_BGRA, &status2),
+ ZX_OK);
+ EXPECT_EQ(status2, ZX_OK);
+
+ zx::vmo vmo_copy2;
+ EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK);
+
+ status2 = ZX_OK;
+ uint32_t id = 0;
+ EXPECT_EQ(fuchsia_hardware_goldfish_control_DeviceGetColorBuffer(
+ channel.get(), vmo_copy2.release(), &status2, &id),
+ ZX_OK);
+ EXPECT_EQ(status2, ZX_OK);
+ EXPECT_NE(id, 0);
+
+ END_TEST;
+}
+
+BEGIN_TEST_CASE(GoldfishControlTests)
+RUN_TEST(GoldfishControlTest)
+END_TEST_CASE(GoldfishControlTests)
+
static bool GoldfishAddressSpaceTest() {
BEGIN_TEST;
diff --git a/zircon/system/utest/sysmem/sysmem_tests.cpp b/zircon/system/utest/sysmem/sysmem_tests.cpp
index 4837bb0..6e35055 100644
--- a/zircon/system/utest/sysmem/sysmem_tests.cpp
+++ b/zircon/system/utest/sysmem/sysmem_tests.cpp
@@ -160,9 +160,11 @@
.max_size_bytes = 128 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
- .secure_permitted = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
+ .inaccessible_domain_supported = false,
+ .heap_permitted_count = 0,
+ .heap_permitted = {},
};
ZX_DEBUG_ASSERT(constraints->image_format_constraints_count == 0);
status = fuchsia_sysmem_BufferCollectionSetConstraints(collection_client.get(), true,
@@ -240,9 +242,11 @@
.max_size_bytes = 128 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
- .secure_permitted = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
+ .inaccessible_domain_supported = false,
+ .heap_permitted_count = 0,
+ .heap_permitted = {},
};
constraints->image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints =
@@ -352,9 +356,11 @@
.max_size_bytes = 128 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
- .secure_permitted = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
+ .inaccessible_domain_supported = false,
+ .heap_permitted_count = 0,
+ .heap_permitted = {},
};
ZX_DEBUG_ASSERT(constraints->image_format_constraints_count == 0);
status = fuchsia_sysmem_BufferCollectionSetConstraints(collection_client.get(), true,
@@ -403,9 +409,11 @@
.max_size_bytes = 128 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
- .secure_permitted = false,
.ram_domain_supported = true,
.cpu_domain_supported = true,
+ .inaccessible_domain_supported = false,
+ .heap_permitted_count = 0,
+ .heap_permitted = {},
};
ZX_DEBUG_ASSERT(constraints->image_format_constraints_count == 0);
status = fuchsia_sysmem_BufferCollectionSetConstraints(collection_client.get(), true,
@@ -511,9 +519,11 @@
.max_size_bytes = (512 * 512) * 3 / 2,
.physically_contiguous_required = false,
.secure_required = false,
- .secure_permitted = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
+ .inaccessible_domain_supported = false,
+ .heap_permitted_count = 0,
+ .heap_permitted = {},
};
constraints_1->image_format_constraints_count = 1;
fuchsia_sysmem_ImageFormatConstraints& image_constraints_1 =
@@ -797,9 +807,11 @@
.max_size_bytes = 64 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
- .secure_permitted = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
+ .inaccessible_domain_supported = false,
+ .heap_permitted_count = 0,
+ .heap_permitted = {},
};
// constraints_2 is just a copy of constraints_1 - since both participants
@@ -885,6 +897,139 @@
END_TEST;
}
+extern "C" bool test_sysmem_heap_constraints(void) {
+ BEGIN_TEST;
+
+ zx_status_t status;
+ zx::channel allocator_client;
+ status = connect_to_sysmem_driver(&allocator_client);
+ ASSERT_EQ(status, ZX_OK, "");
+
+ zx::channel token_client;
+ zx::channel token_server;
+ status = zx::channel::create(0, &token_client, &token_server);
+ ASSERT_EQ(status, ZX_OK, "");
+
+ status = fuchsia_sysmem_AllocatorAllocateSharedCollection(
+ allocator_client.get(), token_server.release());
+ ASSERT_EQ(status, ZX_OK, "");
+
+ zx::channel collection_client;
+ zx::channel collection_server;
+ status = zx::channel::create(0, &collection_client, &collection_server);
+ ASSERT_EQ(status, ZX_OK, "");
+
+ ASSERT_NE(token_client.get(), ZX_HANDLE_INVALID, "");
+ status = fuchsia_sysmem_AllocatorBindSharedCollection(
+ allocator_client.get(), token_client.release(),
+ collection_server.release());
+ ASSERT_EQ(status, ZX_OK, "");
+
+ BufferCollectionConstraints constraints(
+ BufferCollectionConstraints::Default);
+ constraints->usage.vulkan = fuchsia_sysmem_vulkanUsageTransferDst;
+ constraints->min_buffer_count_for_camping = 1;
+ constraints->has_buffer_memory_constraints = true;
+ constraints->buffer_memory_constraints =
+ fuchsia_sysmem_BufferMemoryConstraints{
+ .min_size_bytes = 4 * 1024,
+ .max_size_bytes = 4 * 1024,
+ .physically_contiguous_required = true,
+ .secure_required = false,
+ .ram_domain_supported = false,
+ .cpu_domain_supported = false,
+ .inaccessible_domain_supported = true,
+ .heap_permitted_count = 1,
+ .heap_permitted = {fuchsia_sysmem_HeapType_SYSTEM_RAM}};
+
+ status = fuchsia_sysmem_BufferCollectionSetConstraints(
+ collection_client.get(), true, constraints.release());
+ ASSERT_EQ(status, ZX_OK, "");
+
+ zx_status_t allocation_status;
+ BufferCollectionInfo buffer_collection_info(BufferCollectionInfo::Default);
+ status = fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated(
+ collection_client.get(), &allocation_status,
+ buffer_collection_info.get());
+ // This is the first round-trip to/from sysmem. A failure here can be due
+ // to any step above failing async.
+ ASSERT_EQ(status, ZX_OK, "");
+ ASSERT_EQ(allocation_status, ZX_OK, "");
+ ASSERT_EQ(buffer_collection_info->buffer_count, 1, "");
+ ASSERT_EQ(buffer_collection_info->settings.buffer_settings.coherency_domain,
+ fuchsia_sysmem_CoherencyDomain_Inaccessible, "");
+ ASSERT_EQ(buffer_collection_info->settings.buffer_settings.heap,
+ fuchsia_sysmem_HeapType_SYSTEM_RAM, "");
+ ASSERT_EQ(buffer_collection_info->settings.buffer_settings
+ .is_physically_contiguous,
+ true, "");
+
+ END_TEST;
+}
+
+extern "C" bool test_sysmem_cpu_usage_and_inaccessible_domain_fails(void) {
+ BEGIN_TEST;
+
+ zx_status_t status;
+ zx::channel allocator_client;
+ status = connect_to_sysmem_driver(&allocator_client);
+ ASSERT_EQ(status, ZX_OK, "");
+
+ zx::channel token_client;
+ zx::channel token_server;
+ status = zx::channel::create(0, &token_client, &token_server);
+ ASSERT_EQ(status, ZX_OK, "");
+
+ status = fuchsia_sysmem_AllocatorAllocateSharedCollection(
+ allocator_client.get(), token_server.release());
+ ASSERT_EQ(status, ZX_OK, "");
+
+ zx::channel collection_client;
+ zx::channel collection_server;
+ status = zx::channel::create(0, &collection_client, &collection_server);
+ ASSERT_EQ(status, ZX_OK, "");
+
+ ASSERT_NE(token_client.get(), ZX_HANDLE_INVALID, "");
+ status = fuchsia_sysmem_AllocatorBindSharedCollection(
+ allocator_client.get(), token_client.release(),
+ collection_server.release());
+ ASSERT_EQ(status, ZX_OK, "");
+
+ BufferCollectionConstraints constraints(
+ BufferCollectionConstraints::Default);
+ constraints->usage.cpu = fuchsia_sysmem_cpuUsageReadOften | fuchsia_sysmem_cpuUsageWriteOften;
+ constraints->min_buffer_count_for_camping = 1;
+ constraints->has_buffer_memory_constraints = true;
+ constraints->buffer_memory_constraints =
+ fuchsia_sysmem_BufferMemoryConstraints{
+ .min_size_bytes = 4 * 1024,
+ .max_size_bytes = 4 * 1024,
+ .physically_contiguous_required = true,
+ .secure_required = false,
+ .ram_domain_supported = false,
+ .cpu_domain_supported = false,
+ .inaccessible_domain_supported = true,
+ .heap_permitted_count = 1,
+ .heap_permitted = {fuchsia_sysmem_HeapType_SYSTEM_RAM}};
+
+ status = fuchsia_sysmem_BufferCollectionSetConstraints(
+ collection_client.get(), true, constraints.release());
+ ASSERT_EQ(status, ZX_OK, "");
+
+ zx_status_t allocation_status;
+ BufferCollectionInfo buffer_collection_info(BufferCollectionInfo::Default);
+ status = fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated(
+ collection_client.get(), &allocation_status,
+ buffer_collection_info.get());
+ // usage.cpu != 0 && inaccessible_domain_supported is expected to result in failure to
+ // allocate.
+ ASSERT_NE(status, ZX_OK, "");
+
+ END_TEST;
+}
+
+// TODO(dustingreen): Add tests to cover more failure cases.
+
// clang-format off
BEGIN_TEST_CASE(sysmem_tests)
RUN_TEST(test_sysmem_driver_connection)
@@ -895,5 +1040,7 @@
RUN_TEST(test_sysmem_no_token)
RUN_TEST(test_sysmem_multiple_participants)
RUN_TEST(test_sysmem_constraints_retained_beyond_clean_close)
+ RUN_TEST(test_sysmem_heap_constraints)
+ RUN_TEST(test_sysmem_cpu_usage_and_inaccessible_domain_fails)
END_TEST_CASE(sysmem_tests)
// clang-format on