[dev][platform-bus] Add metadata support for children of platform devices

ZX-2500 #done

TEST: manual testing on VIM2 via another CL that will be out for review son.

Change-Id: Ibe873700a02bfd9baaadee6c25149b2c9f4990a6
diff --git a/system/dev/bus/platform/platform-device.cpp b/system/dev/bus/platform/platform-device.cpp
index 464cfaa..43680b3 100644
--- a/system/dev/bus/platform/platform-device.cpp
+++ b/system/dev/bus/platform/platform-device.cpp
@@ -248,6 +248,36 @@
     return ZX_OK;
 }
 
+zx_status_t PlatformDevice::RpcGetMetadata(const DeviceResources* dr, uint32_t index,
+                                           uint8_t* out_metadata, uint32_t* out_metadata_type,
+                                           uint32_t* out_metadata_length) {
+    if (index >= dr->metadata_count()) {
+        return ZX_ERR_OUT_OF_RANGE;
+    }
+    auto& metadata = dr->metadata(index);
+    if (metadata.data && metadata.len) {
+        if (metadata.len > PROXY_MAX_METADATA_SIZE) {
+            return ZX_ERR_BUFFER_TOO_SMALL;
+        }
+        memcpy(out_metadata, metadata.data, metadata.len);
+        *out_metadata_type = metadata.type;
+        *out_metadata_length = metadata.len;
+        return ZX_OK;
+    } else {
+        const void* data;
+        uint32_t length;
+        auto status = GetZbiMetadata(metadata.type, metadata.extra, &data, &length);
+        if (status == ZX_OK) {
+            if (length > PROXY_MAX_METADATA_SIZE) {
+                return ZX_ERR_BUFFER_TOO_SMALL;
+            }
+            memcpy(out_metadata, data, length);
+            *out_metadata_length = length;
+        }
+        return status;
+    }
+}
+
 zx_status_t PlatformDevice::RpcUmsSetMode(const DeviceResources* dr, usb_mode_t mode) {
     if (bus_->ums() == nullptr) {
         return ZX_ERR_NOT_SUPPORTED;
@@ -507,6 +537,13 @@
         case PDEV_DEVICE_ADD:
             status = RpcDeviceAdd(dr, req->index, &resp->device_id);
             break;
+        case PDEV_GET_METADATA: {
+            auto metadata = reinterpret_cast<uint8_t*>(&resp[1]);
+            status = RpcGetMetadata(dr, req->index, metadata, &resp->metadata_type,
+                                    &resp->metadata_length);
+            resp_len += static_cast<uint32_t>(resp->metadata_length * sizeof(*metadata));
+            break;
+        }
         default:
             zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op);
             return ZX_ERR_INTERNAL;
@@ -702,9 +739,8 @@
     delete this;
 }
 
-zx_status_t PlatformDevice::AddMetaData(const pbus_metadata_t& pbm) {
-    const uint32_t type = pbm.type;
-    const uint32_t extra = pbm.extra;
+zx_status_t PlatformDevice::GetZbiMetadata(uint32_t type, uint32_t extra, const void** out_metadata,
+                                           uint32_t* out_size) {
     const uint8_t* metadata = bus_->metadata();
     const size_t metadata_size = bus_->metadata_size();
     size_t offset = 0;
@@ -714,7 +750,9 @@
         size_t length = ZBI_ALIGN(sizeof(zbi_header_t) + header->length);
 
         if (header->type == type && header->extra == extra) {
-            return DdkAddMetadata(type, header + 1, length - sizeof(zbi_header_t));
+            *out_metadata = header + 1;
+            *out_size = static_cast<uint32_t>(length - sizeof(zbi_header_t));
+            return ZX_OK;
         }
         metadata += length;
         offset += length;
@@ -762,7 +800,12 @@
             if (pbm.data && pbm.len) {
                 status = DdkAddMetadata(pbm.type, pbm.data, pbm.len);
             } else {
-                status = AddMetaData(pbm);
+                const void* data;
+                uint32_t length;
+                status = GetZbiMetadata(pbm.type, pbm.extra, &data, &length);
+                if (status == ZX_OK) {
+                    status = DdkAddMetadata(pbm.type, data, length);
+                }
             }
             if (status != ZX_OK) {
                 DdkRemove();
diff --git a/system/dev/bus/platform/platform-device.h b/system/dev/bus/platform/platform-device.h
index 1731c2f..4817079 100644
--- a/system/dev/bus/platform/platform-device.h
+++ b/system/dev/bus/platform/platform-device.h
@@ -86,6 +86,8 @@
     zx_status_t RpcGetBti(const DeviceResources* dr, uint32_t index, zx_handle_t* out_handle,
                           uint32_t* out_handle_count);
     zx_status_t RpcDeviceAdd(const DeviceResources* dr, uint32_t index, uint32_t* out_device_id);
+    zx_status_t RpcGetMetadata(const DeviceResources* dr, uint32_t index, uint8_t* out_metadata,
+                               uint32_t* out_metadata_type, uint32_t* out_metadata_length);
     zx_status_t RpcUmsSetMode(const DeviceResources* dr, usb_mode_t mode);
     zx_status_t RpcGpioConfig(const DeviceResources* dr, uint32_t index, uint32_t flags);
     zx_status_t RpcGpioSetAltFunction(const DeviceResources* dr, uint32_t index, uint64_t function);
@@ -112,7 +114,8 @@
     zx_status_t RpcClkEnable(const DeviceResources* dr, uint32_t index);
     zx_status_t RpcClkDisable(const DeviceResources* dr, uint32_t index);
 
-    zx_status_t AddMetaData(const pbus_metadata_t& pbm);
+    zx_status_t GetZbiMetadata(uint32_t type, uint32_t extra, const void** out_metadata,
+                               uint32_t* out_size);
 
     PlatformBus* bus_;
     char name_[ZX_DEVICE_NAME_MAX + 1];
diff --git a/system/dev/bus/platform/platform-proxy-device.cpp b/system/dev/bus/platform/platform-proxy-device.cpp
index 33480eb..cd5aed0 100644
--- a/system/dev/bus/platform/platform-proxy-device.cpp
+++ b/system/dev/bus/platform/platform-proxy-device.cpp
@@ -470,62 +470,58 @@
 
     fbl::AllocChecker ac;
 
-    if (info.mmio_count) {
-        for (uint32_t i = 0; i < info.mmio_count; i++) {
-            rpc_pdev_req_t req = {};
-            rpc_pdev_rsp_t resp = {};
-            zx_handle_t rsrc_handle;
+    for (uint32_t i = 0; i < info.mmio_count; i++) {
+        rpc_pdev_req_t req = {};
+        rpc_pdev_rsp_t resp = {};
+        zx_handle_t rsrc_handle;
 
-            req.header.protocol = ZX_PROTOCOL_PLATFORM_DEV;
-            req.header.op = PDEV_GET_MMIO;
-            req.index = i;
-            status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
-                                 NULL, 0, &rsrc_handle, 1, NULL);
-            if (status != ZX_OK) {
-                return status;
-            }
-
-            Mmio mmio;
-            mmio.base = resp.paddr;
-            mmio.length = resp.length;
-            mmio.resource.reset(rsrc_handle);
-            mmios_.push_back(fbl::move(mmio), &ac);
-            if (!ac.check()) {
-                return ZX_ERR_NO_MEMORY;
-            }
-
-            zxlogf(SPEW, "%s: received MMIO %u (base %#lx length %#lx handle %#x)\n", name_, i,
-                   mmio.base, mmio.length, mmio.resource.get());
+        req.header.protocol = ZX_PROTOCOL_PLATFORM_DEV;
+        req.header.op = PDEV_GET_MMIO;
+        req.index = i;
+        status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
+                             NULL, 0, &rsrc_handle, 1, NULL);
+        if (status != ZX_OK) {
+            return status;
         }
+
+        Mmio mmio;
+        mmio.base = resp.paddr;
+        mmio.length = resp.length;
+        mmio.resource.reset(rsrc_handle);
+        mmios_.push_back(fbl::move(mmio), &ac);
+        if (!ac.check()) {
+            return ZX_ERR_NO_MEMORY;
+        }
+
+        zxlogf(SPEW, "%s: received MMIO %u (base %#lx length %#lx handle %#x)\n", name_, i,
+               mmio.base, mmio.length, mmio.resource.get());
     }
 
-    if (info.irq_count) {
-        for (uint32_t i = 0; i < info.irq_count; i++) {
-            rpc_pdev_req_t req = {};
-            rpc_pdev_rsp_t resp = {};
-            zx_handle_t rsrc_handle;
+    for (uint32_t i = 0; i < info.irq_count; i++) {
+        rpc_pdev_req_t req = {};
+        rpc_pdev_rsp_t resp = {};
+        zx_handle_t rsrc_handle;
 
-            req.header.protocol = ZX_PROTOCOL_PLATFORM_DEV;
-            req.header.op = PDEV_GET_INTERRUPT;
-            req.index = i;
-            status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
-                                 NULL, 0, &rsrc_handle, 1, NULL);
-            if (status != ZX_OK) {
-                return status;
-            }
-
-            Irq irq;
-            irq.irq = resp.irq;
-            irq.mode = resp.mode;
-            irq.resource.reset(rsrc_handle);
-            irqs_.push_back(fbl::move(irq), &ac);
-            if (!ac.check()) {
-                return ZX_ERR_NO_MEMORY;
-            }
-
-            zxlogf(SPEW, "%s: received IRQ %u (irq %#x handle %#x)\n", name_, i, irq.irq,
-                   irq.resource.get());
+        req.header.protocol = ZX_PROTOCOL_PLATFORM_DEV;
+        req.header.op = PDEV_GET_INTERRUPT;
+        req.index = i;
+        status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
+                             NULL, 0, &rsrc_handle, 1, NULL);
+        if (status != ZX_OK) {
+            return status;
         }
+
+        Irq irq;
+        irq.irq = resp.irq;
+        irq.mode = resp.mode;
+        irq.resource.reset(rsrc_handle);
+        irqs_.push_back(fbl::move(irq), &ac);
+        if (!ac.check()) {
+            return ZX_ERR_NO_MEMORY;
+        }
+
+        zxlogf(SPEW, "%s: received IRQ %u (irq %#x handle %#x)\n", name_, i, irq.irq,
+               irq.resource.get());
     }
 
     if (args) {
@@ -534,7 +530,42 @@
         proto_id_ = args->proto_id;
         proto_ops_ = args->proto_ops;
 
-        return DdkAdd(args->name, args->flags, args->props, args->prop_count);
+        if (info.metadata_count == 0) {
+            return DdkAdd(args->name, args->flags, args->props, args->prop_count);
+        }
+
+        auto status = DdkAdd(args->name, args->flags | DEVICE_ADD_INVISIBLE, args->props,
+                             args->prop_count);
+        if (status != ZX_OK) {
+            return status;
+        }
+
+        for (uint32_t i = 0; i < info.metadata_count; i++) {
+            rpc_pdev_req_t req = {};
+            struct {
+                rpc_pdev_rsp_t pdev;
+                uint8_t metadata[PROXY_MAX_METADATA_SIZE];
+            } resp = {};
+            req.header.protocol = ZX_PROTOCOL_PLATFORM_DEV;
+            req.header.op = PDEV_GET_METADATA;
+            req.index = i;
+        
+            status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.pdev.header,
+                                 sizeof(resp));
+            if (status != ZX_OK) {
+                DdkRemove();
+                return status;
+            }
+            status = DdkAddMetadata(resp.pdev.metadata_type, resp.metadata,
+                                    resp.pdev.metadata_length);
+            if (status != ZX_OK) {
+                DdkRemove();
+                return status;
+            }
+        }
+
+        DdkMakeVisible(); 
+        return ZX_OK;
     } else {
         return DdkAdd(name_);
     }
diff --git a/system/dev/bus/platform/proxy-protocol.h b/system/dev/bus/platform/proxy-protocol.h
index 195a23c..d04b141 100644
--- a/system/dev/bus/platform/proxy-protocol.h
+++ b/system/dev/bus/platform/proxy-protocol.h
@@ -41,6 +41,7 @@
     PDEV_GET_DEVICE_INFO,
     PDEV_GET_BOARD_INFO,
     PDEV_DEVICE_ADD,
+    PDEV_GET_METADATA,
 };
 
 typedef struct {
@@ -58,6 +59,8 @@
     pdev_device_info_t device_info;
     pdev_board_info_t board_info;
     uint32_t device_id;
+    uint32_t metadata_type;
+    uint32_t metadata_length;
 } rpc_pdev_rsp_t;
 
 // Maximum I2C transfer is I2C_MAX_TRANSFER_SIZE minus size of largest header.
@@ -65,6 +68,9 @@
             (sizeof(rpc_pdev_req_t) > sizeof(rpc_pdev_rsp_t) ?
              sizeof(rpc_pdev_req_t) : sizeof(rpc_pdev_rsp_t)));
 
+// Maximum metadata size that can be returned via PDEV_DEVICE_GET_METADATA.
+static constexpr size_t PROXY_MAX_METADATA_SIZE = (PROXY_MAX_TRANSFER_SIZE - sizeof(rpc_pdev_rsp_t));
+
 // ZX_PROTOCOL_USB_MODE_SWITCH  proxy support.
 enum {
     UMS_SET_MODE,
diff --git a/system/ulib/ddk/include/ddk/protocol/platform-bus.h b/system/ulib/ddk/include/ddk/protocol/platform-bus.h
index 55f2137..17f291d 100644
--- a/system/ulib/ddk/include/ddk/protocol/platform-bus.h
+++ b/system/ulib/ddk/include/ddk/protocol/platform-bus.h
@@ -46,7 +46,7 @@
     uint32_t    type;   // metadata type (matches zbi_header_t.type for bootloader metadata)
     uint32_t    extra;  // matches zbi_header_t.extra for bootloader metadata
     const void* data;   // pointer to metadata (set to NULL for bootloader metadata)
-    size_t      len;    // metadata length in bytes (set to zero for bootloader metadata)
+    uint32_t    len;    // metadata length in bytes (set to zero for bootloader metadata)
 } pbus_metadata_t;
 
 typedef struct pbus_dev pbus_dev_t;