[magma] Virtmagma support for magma_poll

Testability:no automation exists so manually tested

Change-Id: I16347bdc882dfe094775c6a8d6bb088743ff0b8e
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/377214
Commit-Queue: Craig Stout <cstout@google.com>
Reviewed-by: John Bauman <jbauman@google.com>
Testability-Review: John Bauman <jbauman@google.com>
diff --git a/src/graphics/lib/magma/src/libmagma_linux/magma.cc b/src/graphics/lib/magma/src/libmagma_linux/magma.cc
index 942879a..433aa55 100644
--- a/src/graphics/lib/magma/src/libmagma_linux/magma.cc
+++ b/src/graphics/lib/magma/src/libmagma_linux/magma.cc
@@ -104,7 +104,68 @@
 }
 
 magma_status_t magma_poll(magma_poll_item_t* items, uint32_t count, uint64_t timeout_ns) {
-  return MAGMA_STATUS_UNIMPLEMENTED;
+  if (count == 0)
+    return MAGMA_STATUS_OK;
+
+  std::vector<magma_poll_item_t> unwrapped_items(count);
+  int32_t file_descriptor = -1;
+
+  for (uint32_t i = 0; i < count; ++i) {
+    unwrapped_items[i] = items[i];
+
+    switch (items[i].type) {
+      case MAGMA_POLL_TYPE_SEMAPHORE: {
+        auto semaphore_wrapped = virtmagma_semaphore_t::Get(items[i].semaphore);
+        unwrapped_items[i].semaphore = semaphore_wrapped->Object();
+
+        if (i == 0) {
+          auto semaphore0_parent_wrapped = virtmagma_connection_t::Get(semaphore_wrapped->Parent());
+          file_descriptor = semaphore0_parent_wrapped->Parent().first;
+        }
+        break;
+      }
+
+      case MAGMA_POLL_TYPE_HANDLE: {
+        auto iter = GlobalHandleTable().find(items[i].handle);
+        if (iter == GlobalHandleTable().end()) {
+          return MAGMA_STATUS_INVALID_ARGS;  // Not found
+        }
+
+        virtmagma_handle_t* handle = iter->second;
+        unwrapped_items[i].handle = handle->Object();
+
+        if (i == 0) {
+          file_descriptor = handle->Parent();
+        }
+        break;
+      }
+    }
+  }
+
+  virtio_magma_poll_ctrl_t request{};
+  virtio_magma_poll_resp_t response{};
+  request.hdr.type = VIRTIO_MAGMA_CMD_POLL;
+  request.items = reinterpret_cast<uint64_t>(unwrapped_items.data());
+  // Send byte count so kernel knows how much memory to copy
+  request.count = count * sizeof(magma_poll_item_t);
+  request.timeout_ns = timeout_ns;
+
+  if (!virtmagma_send_command(file_descriptor, &request, sizeof(request), &response,
+                              sizeof(response)))
+    return MAGMA_STATUS_INTERNAL_ERROR;
+  if (response.hdr.type != VIRTIO_MAGMA_RESP_POLL)
+    return MAGMA_STATUS_INTERNAL_ERROR;
+
+  magma_status_t result_return = static_cast<decltype(result_return)>(response.result_return);
+  if (result_return != MAGMA_STATUS_OK)
+    return result_return;
+
+  // Update the results
+  for (uint32_t i = 0; i < count; i++) {
+    items[i].result = unwrapped_items[i].result;
+  }
+
+  return MAGMA_STATUS_OK;
 }
 
 void magma_execute_command_buffer_with_resources(magma_connection_t connection, uint32_t context_id,
diff --git a/src/graphics/lib/magma/src/libmagma_linux/magma_generic.cc.gen.py b/src/graphics/lib/magma/src/libmagma_linux/magma_generic.cc.gen.py
index 367a417..df985ea 100755
--- a/src/graphics/lib/magma/src/libmagma_linux/magma_generic.cc.gen.py
+++ b/src/graphics/lib/magma/src/libmagma_linux/magma_generic.cc.gen.py
@@ -181,6 +181,10 @@
     sub = 'handle_out'
     if name[-len(sub):] == sub:
       ret += '    GlobalHandleTable()[*' + name + '] = virtmagma_handle_t::Create(*' + name + ', file_descriptor);\n'
+
+  if export['type'] == 'magma_handle_t':
+    ret += '    GlobalHandleTable()[result_return] = virtmagma_handle_t::Create(result_return, file_descriptor);\n'
+
   return ret, needs_connection
 
 # Generate an implementation for an export
diff --git a/src/graphics/lib/magma/src/libmagma_linux/virtmagma_util.h b/src/graphics/lib/magma/src/libmagma_linux/virtmagma_util.h
index 63d44c3..8428eeb 100644
--- a/src/graphics/lib/magma/src/libmagma_linux/virtmagma_util.h
+++ b/src/graphics/lib/magma/src/libmagma_linux/virtmagma_util.h
@@ -118,7 +118,7 @@
     virtmagma_connection_t;
 typedef VirtmagmaObject<magma_buffer_t, magma_connection_t, 0x2222> virtmagma_buffer_t;
 typedef VirtmagmaObject<magma_semaphore_t, magma_connection_t, 0x3333> virtmagma_semaphore_t;
-typedef VirtmagmaObject<uint32_t, int32_t, 0x4444> virtmagma_handle_t;
+typedef VirtmagmaObject<magma_handle_t, int32_t, 0x4444> virtmagma_handle_t;
 typedef VirtmagmaObject<magma_device_t, OwnedFd, 0x5555> virtmagma_device_t;
 
 // TODO(MA-623): support an object that is a parent of magma_connection_t
diff --git a/src/virtualization/bin/vmm/device/virtio_magma.cc b/src/virtualization/bin/vmm/device/virtio_magma.cc
index 0961d0593..cca17cf 100644
--- a/src/virtualization/bin/vmm/device/virtio_magma.cc
+++ b/src/virtualization/bin/vmm/device/virtio_magma.cc
@@ -155,6 +155,16 @@
   return VirtioMagmaGeneric::Handle_wait_semaphores(&request_mod, response);
 }
 
+zx_status_t VirtioMagma::Handle_poll(const virtio_magma_poll_ctrl_t* request,
+                                     virtio_magma_poll_resp_t* response) {
+  auto request_mod = *request;
+  // The actual items immediately follow the request struct.
+  request_mod.items = reinterpret_cast<uint64_t>(&request[1]);
+  // Transform byte count back to item count
+  request_mod.count /= sizeof(magma_poll_item_t);
+  return VirtioMagmaGeneric::Handle_poll(&request_mod, response);
+}
+
 zx_status_t VirtioMagma::Handle_read_notification_channel(
     const virtio_magma_read_notification_channel_ctrl_t* request,
     virtio_magma_read_notification_channel_resp_t* response) {
diff --git a/src/virtualization/bin/vmm/device/virtio_magma.h b/src/virtualization/bin/vmm/device/virtio_magma.h
index 1b811b8..b8c060e 100644
--- a/src/virtualization/bin/vmm/device/virtio_magma.h
+++ b/src/virtualization/bin/vmm/device/virtio_magma.h
@@ -52,6 +52,8 @@
   virtual zx_status_t Handle_wait_semaphores(
       const virtio_magma_wait_semaphores_ctrl_t* request,
       virtio_magma_wait_semaphores_resp_t* response) override;
+  virtual zx_status_t Handle_poll(const virtio_magma_poll_ctrl_t* request,
+                                  virtio_magma_poll_resp_t* response) override;
   virtual zx_status_t Handle_read_notification_channel(
       const virtio_magma_read_notification_channel_ctrl_t* request,
       virtio_magma_read_notification_channel_resp_t* response) override;