[test] Send metadata to integration test drivers

ZX-4126 #comment

It is often helpful to give test drivers data
so they can configure themselves before binding.
This patch adds the ability to send a driver
an arbitrary amount of binary data through the
DEVICE_METADATA_TEST metadata.

Metadata is pushed onto the IsolatedDevmgr
args the same way devices are pushed.

TEST: runtests -t metadata-driver-test

Change-Id: Ide481709b7c3e1eb9289d42580ae62e433d8bbf8
diff --git a/zircon/system/dev/BUILD.gn b/zircon/system/dev/BUILD.gn
index dc6de00..6bc6721 100644
--- a/zircon/system/dev/BUILD.gn
+++ b/zircon/system/dev/BUILD.gn
@@ -36,6 +36,7 @@
     "serial",
     "sysmem",
     "tee",
+    "test/isolateddevmgr:isolateddevmgr-test",
     "test/mock-device",
     "test/sysdev",
     "thermal",
diff --git a/zircon/system/dev/board/integration-test/test.cpp b/zircon/system/dev/board/integration-test/test.cpp
index e5bb396..dc04975 100644
--- a/zircon/system/dev/board/integration-test/test.cpp
+++ b/zircon/system/dev/board/integration-test/test.cpp
@@ -56,6 +56,7 @@
 
     fbl::Array<uint8_t> metadata_;
 
+    fbl::Vector<pbus_metadata_t> devices_metadata_;
     fbl::Vector<pbus_dev_t> devices_;
 
     ddk::PBusProtocolClient pbus_;
@@ -67,6 +68,8 @@
     delete this;
 }
 
+// This function must be kept updated with the function that serializes the date.
+// This function is driver_integration_test::GetBootItem.
 zx_status_t TestBoard::FetchAndDeserialize() {
     size_t metadata_size;
     zx_status_t status = DdkGetMetadataSize(DEVICE_METADATA_BOARD_PRIVATE, &metadata_size);
@@ -103,14 +106,29 @@
         return ZX_ERR_NO_MEMORY;
     }
 
+    size_t metadata_offset = (device_list->count * sizeof(DeviceEntry)) + sizeof(DeviceList);
     for (size_t i = 0; i < device_list->count; i++) {
         const auto& entry = device_list->list[i];
+        // Create the device.
         pbus_dev_t device = {};
         device.name = entry.name;
         device.vid = entry.vid;
         device.pid = entry.pid;
         device.did = entry.did;
 
+        //Create the metadata.
+        pbus_metadata_t metadata = {};
+        metadata.type = DEVICE_METADATA_TEST;
+        metadata.data_size = entry.metadata_size;
+        metadata.data_buffer = metadata_.get() + metadata_offset;
+        metadata_offset += metadata.data_size;
+
+        // Store the metadata and link the device to it.
+        devices_metadata_.push_back(std::move(metadata));
+        device.metadata_count = 1;
+        device.metadata_list = &devices_metadata_[devices_metadata_.size() - 1];
+
+
         devices_.push_back(device);
     }
 
diff --git a/zircon/system/dev/test/BUILD.gn b/zircon/system/dev/test/BUILD.gn
index 11a2914..3e0be90 100644
--- a/zircon/system/dev/test/BUILD.gn
+++ b/zircon/system/dev/test/BUILD.gn
@@ -6,6 +6,7 @@
   testonly = true
   deps = [
     "ddk-test",
+    "isolateddevmgr",
     "mock-device",
     "operation",
     "sysdev",
diff --git a/zircon/system/dev/test/isolateddevmgr/BUILD.gn b/zircon/system/dev/test/isolateddevmgr/BUILD.gn
new file mode 100644
index 0000000..a5de605
--- /dev/null
+++ b/zircon/system/dev/test/isolateddevmgr/BUILD.gn
@@ -0,0 +1,34 @@
+# 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("isolateddevmgr-test") {
+  sources = [
+    "test-driver.cpp",
+  ]
+  deps = [
+    "$zx/system/fidl/fuchsia-device-manager-test:c",
+    "$zx/system/ulib/ddk",
+    "$zx/system/ulib/ddktl",
+    "$zx/system/ulib/fdio",
+    "$zx/system/ulib/zircon",
+  ]
+}
+
+test("isolateddevmgr") {
+  sources = [
+    "test.cpp",
+  ]
+  deps = [
+    "$zx/system/fidl/fuchsia-device-manager-test:c",
+    "$zx/system/ulib/ddk",
+    "$zx/system/ulib/devmgr-integration-test",
+    "$zx/system/ulib/devmgr-launcher",
+    "$zx/system/ulib/driver-integration-test",
+    "$zx/system/ulib/fbl",
+    "$zx/system/ulib/fdio",
+    "$zx/system/ulib/zircon",
+    "$zx/system/ulib/zx",
+    "$zx/system/ulib/zxtest",
+  ]
+}
diff --git a/zircon/system/dev/test/isolateddevmgr/test-driver.cpp b/zircon/system/dev/test/isolateddevmgr/test-driver.cpp
new file mode 100644
index 0000000..3963a6a
--- /dev/null
+++ b/zircon/system/dev/test/isolateddevmgr/test-driver.cpp
@@ -0,0 +1,97 @@
+// 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 <vector>
+
+#include <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/driver.h>
+#include <ddk/platform-defs.h>
+#include <ddktl/device.h>
+#include <fbl/alloc_checker.h>
+#include <fuchsia/device/manager/test/c/fidl.h>
+
+class IsolatedDevMgrTestDriver;
+using DeviceType = ddk::Device<IsolatedDevMgrTestDriver, ddk::Unbindable, ddk::Messageable>;
+class IsolatedDevMgrTestDriver : public DeviceType {
+public:
+    IsolatedDevMgrTestDriver(zx_device_t* parent)
+        : DeviceType(parent) {}
+    zx_status_t Bind();
+    void DdkUnbind() {
+        DdkRemove();
+    }
+    void DdkRelease() {
+        delete this;
+    }
+    zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
+
+private:
+    static zx_status_t FidlGetMetadata(void* ctx, uint32_t type, fidl_txn_t* txn);
+};
+
+zx_status_t IsolatedDevMgrTestDriver::FidlGetMetadata(void* ctx, uint32_t type, fidl_txn_t* txn) {
+    IsolatedDevMgrTestDriver* driver = reinterpret_cast<IsolatedDevMgrTestDriver*>(ctx);
+    size_t size;
+    zx_status_t status = driver->DdkGetMetadataSize(type, &size);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    std::vector<uint8_t> metadata;
+    metadata.resize(size);
+
+    status = driver->DdkGetMetadata(type, metadata.data(), metadata.size(), &size);
+    if (status != ZX_OK) {
+        return status;
+    }
+    if (size != metadata.size()) {
+        return ZX_ERR_INTERNAL;
+    }
+
+    return fuchsia_device_manager_test_MetadataGetMetadata_reply(txn, metadata.data(),
+                                                                 metadata.size());
+}
+
+zx_status_t IsolatedDevMgrTestDriver::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
+    static const fuchsia_device_manager_test_Metadata_ops_t kOps = {
+        .GetMetadata = IsolatedDevMgrTestDriver::FidlGetMetadata,
+    };
+    return fuchsia_device_manager_test_Metadata_dispatch(this, txn, msg, &kOps);
+}
+
+zx_status_t IsolatedDevMgrTestDriver::Bind() {
+    return DdkAdd("metadata-test");
+}
+
+zx_status_t isolateddevmgr_test_bind(void* ctx, zx_device_t* device) {
+    fbl::AllocChecker ac;
+    auto dev = fbl::make_unique_checked<IsolatedDevMgrTestDriver>(&ac, device);
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
+    }
+    auto status = dev->Bind();
+    if (status == ZX_OK) {
+        // devmgr is now in charge of the memory for dev
+        __UNUSED auto ptr = dev.release();
+    }
+    return status;
+}
+
+static zx_driver_ops_t isolateddevmgr_test_driver_ops = []() -> zx_driver_ops_t {
+    zx_driver_ops_t ops;
+    ops.version = DRIVER_OPS_VERSION;
+    ops.bind = isolateddevmgr_test_bind;
+    return ops;
+}();
+
+// clang-format off
+ZIRCON_DRIVER_BEGIN(metadataTest, isolateddevmgr_test_driver_ops, "zircon", "0.1", 4)
+    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_TEST),
+    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_METADATA_TEST),
+    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_TEST_CHILD_1),
+    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_TEST_CHILD_2),
+ZIRCON_DRIVER_END(metadataTest)
+    // clang-format on
diff --git a/zircon/system/dev/test/isolateddevmgr/test.cpp b/zircon/system/dev/test/isolateddevmgr/test.cpp
new file mode 100644
index 0000000..24bc13ac
--- /dev/null
+++ b/zircon/system/dev/test/isolateddevmgr/test.cpp
@@ -0,0 +1,154 @@
+// 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+
+#include <ddk/metadata.h>
+#include <ddk/platform-defs.h>
+#include <fbl/unique_ptr.h>
+#include <fuchsia/device/manager/test/c/fidl.h>
+#include <lib/driver-integration-test/fixture.h>
+#include <lib/fdio/fd.h>
+#include <lib/fdio/fdio.h>
+#include <zircon/syscalls.h>
+#include <zxtest/zxtest.h>
+
+using driver_integration_test::IsolatedDevmgr;
+
+namespace {
+
+class IsolatedDevMgrTest : public zxtest::Test {
+};
+
+const uint8_t metadata1[] = {1, 2, 3, 4, 5};
+const board_test::DeviceEntry kDeviceEntry1 = []() {
+    board_test::DeviceEntry entry = {};
+    strcpy(entry.name, "metadata-test");
+    entry.vid = PDEV_VID_TEST;
+    entry.pid = PDEV_PID_METADATA_TEST;
+    entry.did = PDEV_DID_TEST_CHILD_1;
+    entry.metadata_size = sizeof(metadata1);
+    entry.metadata = metadata1;
+    return entry;
+}();
+
+const uint8_t metadata2[] = {7, 6, 5, 4, 3, 2, 1};
+const board_test::DeviceEntry kDeviceEntry2 = []() {
+    board_test::DeviceEntry entry = {};
+    strcpy(entry.name, "metadata-test");
+    entry.vid = PDEV_VID_TEST;
+    entry.pid = PDEV_PID_METADATA_TEST;
+    entry.did = PDEV_DID_TEST_CHILD_2;
+    entry.metadata_size = sizeof(metadata2);
+    entry.metadata = metadata2;
+    return entry;
+}();
+
+TEST_F(IsolatedDevMgrTest, MetadataOneDriverTest) {
+    IsolatedDevmgr devmgr;
+    zx_handle_t metadata_driver_channel;
+
+    // Set the driver arguments.
+    IsolatedDevmgr::Args args;
+    args.driver_search_paths.push_back("/boot/driver");
+    args.device_list.push_back(kDeviceEntry1);
+
+    // Create the isolated Devmgr.
+    zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
+    ASSERT_EQ(ZX_OK, status);
+
+    // Wait for Metadata-test driver to be created
+    fbl::unique_fd fd;
+    status = devmgr_integration_test::RecursiveWaitForFile(devmgr.devfs_root(),
+                                                           "sys/platform/11:07:2/metadata-test",
+                                                           zx::deadline_after(zx::sec(5)),
+                                                           &fd);
+    ASSERT_EQ(ZX_OK, status);
+
+    // Get a FIDL channel to the Metadata device
+    status = fdio_get_service_handle(fd.get(), &metadata_driver_channel);
+    ASSERT_EQ(ZX_OK, status);
+
+    // Read the metadata it received.
+    size_t out_size;
+    std::vector<uint8_t> received_metadata(sizeof(metadata1));
+
+    status = fuchsia_device_manager_test_MetadataGetMetadata(
+        metadata_driver_channel, DEVICE_METADATA_TEST,
+        received_metadata.data(), received_metadata.size(), &out_size);
+
+    ASSERT_EQ(status, ZX_OK);
+    ASSERT_EQ(out_size, sizeof(metadata1));
+
+    for (size_t i = 0; i < received_metadata.size(); i++) {
+        EXPECT_EQ(received_metadata[i], metadata1[i]);
+    }
+}
+
+TEST_F(IsolatedDevMgrTest, MetadataTwoDriverTest) {
+    IsolatedDevmgr devmgr;
+
+    // Set the driver arguments.
+    IsolatedDevmgr::Args args;
+    args.driver_search_paths.push_back("/boot/driver");
+    args.device_list.push_back(kDeviceEntry1);
+    args.device_list.push_back(kDeviceEntry2);
+
+    // Create the isolated Devmgr.
+    zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
+    ASSERT_EQ(ZX_OK, status);
+
+    // Wait for Metadata-test driver to be created
+    fbl::unique_fd fd1;
+    status = devmgr_integration_test::RecursiveWaitForFile(devmgr.devfs_root(),
+                                                           "sys/platform/11:07:2/metadata-test",
+                                                           zx::deadline_after(zx::sec(5)),
+                                                           &fd1);
+    ASSERT_EQ(ZX_OK, status);
+    fbl::unique_fd fd2;
+    status = devmgr_integration_test::RecursiveWaitForFile(devmgr.devfs_root(),
+                                                           "sys/platform/11:07:3/metadata-test",
+                                                           zx::deadline_after(zx::sec(5)),
+                                                           &fd2);
+    ASSERT_EQ(ZX_OK, status);
+
+    // Get a FIDL channel to the Metadata device
+    zx_handle_t metadata_driver_channel1;
+    zx_handle_t metadata_driver_channel2;
+    status = fdio_get_service_handle(fd1.get(), &metadata_driver_channel1);
+    ASSERT_EQ(ZX_OK, status);
+    status = fdio_get_service_handle(fd2.get(), &metadata_driver_channel2);
+    ASSERT_EQ(ZX_OK, status);
+
+    // Read the metadata it received.
+    size_t out_size;
+    std::vector<uint8_t> received_metadata(sizeof(metadata1));
+
+    status = fuchsia_device_manager_test_MetadataGetMetadata(
+        metadata_driver_channel1, DEVICE_METADATA_TEST,
+        received_metadata.data(), received_metadata.size(), &out_size);
+
+    ASSERT_EQ(status, ZX_OK);
+    ASSERT_EQ(out_size, sizeof(metadata1));
+
+    for (size_t i = 0; i < received_metadata.size(); i++) {
+        EXPECT_EQ(received_metadata[i], metadata1[i]);
+    }
+
+    received_metadata.resize(sizeof(metadata2));
+    status = fuchsia_device_manager_test_MetadataGetMetadata(
+        metadata_driver_channel2, DEVICE_METADATA_TEST,
+        received_metadata.data(), received_metadata.size(), &out_size);
+    ASSERT_EQ(status, ZX_OK);
+    ASSERT_EQ(out_size, sizeof(metadata2));
+
+    for (size_t i = 0; i < received_metadata.size(); i++) {
+        EXPECT_EQ(received_metadata[i], metadata2[i]);
+    }
+}
+
+} // namespace
diff --git a/zircon/system/fidl/BUILD.gn b/zircon/system/fidl/BUILD.gn
index 5800afe..69da451 100644
--- a/zircon/system/fidl/BUILD.gn
+++ b/zircon/system/fidl/BUILD.gn
@@ -15,6 +15,7 @@
     "fuchsia-debugdata",
     "fuchsia-device",
     "fuchsia-device-manager",
+    "fuchsia-device-manager-test",
     "fuchsia-device-test",
     "fuchsia-device-vsock",
     "fuchsia-fshost",
diff --git a/zircon/system/fidl/fuchsia-device-manager-test/BUILD.gn b/zircon/system/fidl/fuchsia-device-manager-test/BUILD.gn
new file mode 100644
index 0000000..8b17f9a
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-device-manager-test/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-device-manager-test") {
+  sources = [
+    "metadata.fidl",
+  ]
+}
diff --git a/zircon/system/fidl/fuchsia-device-manager-test/metadata.fidl b/zircon/system/fidl/fuchsia-device-manager-test/metadata.fidl
new file mode 100644
index 0000000..28f0f66
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-device-manager-test/metadata.fidl
@@ -0,0 +1,16 @@
+// 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.device.manager.test;
+
+// Max size of returned metadata
+const uint32 METADATA_MAX_SIZE = 0x1000;
+
+/// Protocol to query a driver's metadata. This is only used to test that the
+/// isolateddevmgr sends test metadata correctly to drivers.
+[Layout = "Simple"]
+protocol Metadata {
+    /// Recieves the metadata that the driver has been given.
+    GetMetadata(uint32 type) -> (vector<uint8>:METADATA_MAX_SIZE data);
+};
diff --git a/zircon/system/ulib/ddk/include/ddk/metadata.h b/zircon/system/ulib/ddk/include/ddk/metadata.h
index 3491e5b..bf3e61a 100644
--- a/zircon/system/ulib/ddk/include/ddk/metadata.h
+++ b/zircon/system/ulib/ddk/include/ddk/metadata.h
@@ -45,6 +45,10 @@
 #define DEVICE_METADATA_BOARD_PRIVATE             0x524F426D // mBOR
 static_assert(DEVICE_METADATA_BOARD_PRIVATE == ZBI_TYPE_DRV_BOARD_PRIVATE, "");
 
+// Information that is sent through the isolated dev manager by a test.
+// type: ??? - test defined
+#define DEVICE_METADATA_TEST                      0x54534554 // TEST
+
 // Interrupt controller type (for sysinfo driver)
 // type: uint8_t
 #define DEVICE_METADATA_INTERRUPT_CONTROLLER_TYPE 0x43544E49 // INTC
diff --git a/zircon/system/ulib/ddk/include/ddk/metadata/test.h b/zircon/system/ulib/ddk/include/ddk/metadata/test.h
index 50b385e..aa4add2 100644
--- a/zircon/system/ulib/ddk/include/ddk/metadata/test.h
+++ b/zircon/system/ulib/ddk/include/ddk/metadata/test.h
@@ -21,6 +21,10 @@
     uint32_t pid;
     // BIND_PLATFORM_DEV_DID`
     uint32_t did;
+
+    // Below metadata is passed on to the device in DEVICE_METADATA_TEST.
+    size_t metadata_size;
+    const uint8_t* metadata;
 };
 
 struct DeviceList {
@@ -30,4 +34,4 @@
 
 } // namespace board_test
 
-#endif  // DDK_METADATA_TEST_H_
+#endif // DDK_METADATA_TEST_H_
diff --git a/zircon/system/ulib/ddk/include/ddk/platform-defs.h b/zircon/system/ulib/ddk/include/ddk/platform-defs.h
index d03dadf..56a5b22 100644
--- a/zircon/system/ulib/ddk/include/ddk/platform-defs.h
+++ b/zircon/system/ulib/ddk/include/ddk/platform-defs.h
@@ -187,6 +187,7 @@
 #define PDEV_PID_HIDCTL_TEST        4
 #define PDEV_PID_VCAMERA_TEST       5
 #define PDEV_PID_LIBDRIVER_TEST     6
+#define PDEV_PID_METADATA_TEST      7
 
 #define PDEV_DID_TEST_PARENT        1
 #define PDEV_DID_TEST_CHILD_1       2
diff --git a/zircon/system/ulib/driver-integration-test/launcher.cpp b/zircon/system/ulib/driver-integration-test/launcher.cpp
index ed66313..133d2f4 100644
--- a/zircon/system/ulib/driver-integration-test/launcher.cpp
+++ b/zircon/system/ulib/driver-integration-test/launcher.cpp
@@ -30,8 +30,11 @@
     return plat_id;
 }();
 
-zx_status_t GetBootItem(const fbl::Vector<board_test::DeviceEntry>& entries, uint32_t type,
-                        uint32_t extra, zx::vmo* out, uint32_t* length) {
+// This function is responsible for serializing driver data. It must be kept
+// updated with the function that deserialized the data. This function
+// is TestBoard::FetchAndDeserialize.
+zx_status_t GetBootItem(const fbl::Vector<board_test::DeviceEntry>& entries,
+                        uint32_t type, uint32_t extra, zx::vmo* out, uint32_t* length) {
     zx::vmo vmo;
     switch (type) {
     case ZBI_TYPE_PLATFORM_ID: {
@@ -49,20 +52,41 @@
     case ZBI_TYPE_DRV_BOARD_PRIVATE: {
         size_t list_size = sizeof(board_test::DeviceList);
         size_t entry_size = entries.size() * sizeof(board_test::DeviceEntry);
-        zx_status_t status = zx::vmo::create(list_size + entry_size, 0, &vmo);
+
+        size_t metadata_size = 0;
+        for (board_test::DeviceEntry& entry : entries) {
+            metadata_size += entry.metadata_size;
+        }
+
+        zx_status_t status = zx::vmo::create(list_size + entry_size + metadata_size, 0, &vmo);
         if (status != ZX_OK) {
             return status;
         }
+
+        // Write DeviceList to vmo.
         board_test::DeviceList list{.count = entries.size()};
         status = vmo.write(&list, 0, sizeof(list));
         if (status != ZX_OK) {
             return status;
         }
+
+        // Write DeviceEntries to vmo.
         status = vmo.write(entries.get(), list_size, entry_size);
         if (status != ZX_OK) {
             return status;
         }
-        *length = static_cast<uint32_t>(list_size + entry_size);
+
+        // Write Metadata to vmo.
+        size_t write_offset = list_size + entry_size;
+        for (board_test::DeviceEntry& entry : entries) {
+            status = vmo.write(entry.metadata, write_offset, entry.metadata_size);
+            if (status != ZX_OK) {
+                return status;
+            }
+            write_offset += entry.metadata_size;
+        }
+
+        *length = static_cast<uint32_t>(list_size + entry_size + metadata_size);
         break;
     }
     default: