[dmctl] Convert dmctl to a FIDL interface

Part of the removal of ioctl

ZX-2827 #done

Test: Booted a system through mexec, verified "dm" commands still
worked.

Change-Id: Icfe9e0c0336772455b87e5b59ce86fa24262ebec
diff --git a/system/core/devmgr/devmgr/coordinator.cpp b/system/core/devmgr/devmgr/coordinator.cpp
index 6924d471..1855caa 100644
--- a/system/core/devmgr/devmgr/coordinator.cpp
+++ b/system/core/devmgr/devmgr/coordinator.cpp
@@ -28,7 +28,6 @@
 #include <lib/zx/socket.h>
 #include <zircon/assert.h>
 #include <zircon/boot/bootdata.h>
-#include <zircon/device/dmctl.h>
 #include <zircon/processargs.h>
 #include <zircon/syscalls.h>
 #include <zircon/syscalls/policy.h>
diff --git a/system/core/devmgr/dmctl/dmctl.cpp b/system/core/devmgr/dmctl/dmctl.cpp
index e01cd68..ec14623 100644
--- a/system/core/devmgr/dmctl/dmctl.cpp
+++ b/system/core/devmgr/dmctl/dmctl.cpp
@@ -10,7 +10,8 @@
 #include <ddk/driver.h>
 #include <ddktl/device.h>
 #include <fuchsia/device/manager/c/fidl.h>
-#include <zircon/device/dmctl.h>
+#include <lib/zx/socket.h>
+#include <lib/zx/vmo.h>
 
 #include "../devhost/device-internal.h"
 
@@ -19,7 +20,7 @@
 namespace {
 
 class Dmctl;
-using DmctlBase = ddk::Device<Dmctl, ddk::Ioctlable, ddk::Writable>;
+using DmctlBase = ddk::Device<Dmctl, ddk::Messageable, ddk::Writable>;
 
 class Dmctl : public DmctlBase {
 public:
@@ -29,8 +30,7 @@
 
     void DdkRelease();
     zx_status_t DdkWrite(const void* buf, size_t count, zx_off_t off, size_t* actual);
-    zx_status_t DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf,
-                         size_t out_len, size_t* out_actual);
+    zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
 };
 
 Dmctl::Dmctl(zx_device_t* parent) : DmctlBase(parent) {
@@ -65,55 +65,48 @@
     return ZX_OK;
 }
 
-zx_status_t Dmctl::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len,
-                            void* out_buf, size_t out_len, size_t* out_actual) {
-    const zx::channel& rpc = *zxdev()->rpc;
+static zx_status_t fidl_ExecuteCommand(void* ctx, zx_handle_t raw_log_socket,
+                                       const char* command_data, size_t command_size, fidl_txn_t* txn) {
+    zx::socket log_socket(raw_log_socket);
+    auto zxdev = static_cast<zx_device_t*>(ctx);
+    const zx::channel& rpc = *zxdev->rpc;
+
     zx_status_t status, call_status;
-    switch (op) {
-    case IOCTL_DMCTL_COMMAND: {
-        if (in_len != sizeof(dmctl_cmd_t)) {
-            return ZX_ERR_INVALID_ARGS;
-        }
+    status = fuchsia_device_manager_CoordinatorDmCommand(
+            rpc.get(), log_socket.release(), command_data, command_size,
+            &call_status);
+    if (status == ZX_OK) {
+        status = call_status;
+    }
+    return fuchsia_device_manager_ExternalControllerExecuteCommand_reply(txn, status);
+}
 
-        dmctl_cmd_t cmd;
-        memcpy(&cmd, in_buf, sizeof(cmd));
-        cmd.name[sizeof(cmd.name) - 1] = 0;
+static zx_status_t fidl_OpenVirtcon(void* ctx, zx_handle_t raw_vc_receiver) {
+    zx::channel vc_receiver(raw_vc_receiver);
+    auto zxdev = static_cast<zx_device_t*>(ctx);
+    const zx::channel& rpc = *zxdev->rpc;
 
-        status = fuchsia_device_manager_CoordinatorDmCommand(
-                rpc.get(), cmd.h, static_cast<const char*>(cmd.name), strlen(cmd.name),
-                &call_status);
-        if (status != ZX_OK) {
-            return status;
-        } else if (call_status != ZX_OK) {
-            // NOT_SUPPORTED tells the dispatcher to close the handle for
-            // ioctls that accept a handle argument, so we have to avoid
-            // returning that in this case where the handle has been passed
-            // to another process (and effectively closed)
-            if (call_status == ZX_ERR_NOT_SUPPORTED) {
-                call_status = ZX_ERR_INTERNAL;
-            }
-            return call_status;
-        }
+    return fuchsia_device_manager_CoordinatorDmOpenVirtcon(rpc.get(), vc_receiver.release());
+}
 
-        *out_actual = 0;
-        return ZX_OK;
-    }
-    case IOCTL_DMCTL_OPEN_VIRTCON: {
-        if (in_len != sizeof(zx_handle_t)) {
-            return ZX_ERR_INVALID_ARGS;
-        }
-        return fuchsia_device_manager_CoordinatorDmOpenVirtcon(rpc.get(), *(zx_handle_t*)in_buf);
-    }
-    case IOCTL_DMCTL_MEXEC: {
-        if (in_len != sizeof(dmctl_mexec_args_t)) {
-            return ZX_ERR_INVALID_ARGS;
-        }
-        auto args = reinterpret_cast<const dmctl_mexec_args_t*>(in_buf);
-        return fuchsia_device_manager_CoordinatorDmMexec(rpc.get(), args->kernel, args->bootdata);
-    }
-    default:
-        return ZX_ERR_INVALID_ARGS;
-    }
+static zx_status_t fidl_PerformMexec(void* ctx, zx_handle_t raw_kernel, zx_handle_t raw_bootdata) {
+    zx::vmo kernel(raw_kernel);
+    zx::vmo bootdata(raw_bootdata);
+    auto zxdev = static_cast<zx_device_t*>(ctx);
+    const zx::channel& rpc = *zxdev->rpc;
+
+    return fuchsia_device_manager_CoordinatorDmMexec(rpc.get(), kernel.release(),
+                                                     bootdata.release());
+}
+
+static fuchsia_device_manager_ExternalController_ops_t fidl_ops = {
+    .ExecuteCommand = fidl_ExecuteCommand,
+    .OpenVirtcon = fidl_OpenVirtcon,
+    .PerformMexec = fidl_PerformMexec,
+};
+
+zx_status_t Dmctl::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
+    return fuchsia_device_manager_ExternalController_dispatch(zxdev(), txn, msg, &fidl_ops);
 }
 
 zx_driver_ops_t dmctl_driver_ops = []() {
diff --git a/system/core/netsvc/netboot.c b/system/core/netsvc/netboot.c
index eed77d2..37e729e 100644
--- a/system/core/netsvc/netboot.c
+++ b/system/core/netsvc/netboot.c
@@ -15,15 +15,14 @@
 #include <threads.h>
 #include <unistd.h>
 
+#include <fuchsia/device/manager/c/fidl.h>
 #include <inet6/inet6.h>
 #include <inet6/netifc.h>
-
-#include <zircon/process.h>
-#include <zircon/processargs.h>
-#include <zircon/syscalls.h>
-
+#include <lib/fdio/util.h>
 #include <zircon/boot/netboot.h>
-#include <zircon/device/dmctl.h>
+#include <zircon/processargs.h>
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
 
 static uint32_t last_cookie = 0;
 static uint32_t last_cmd = 0;
@@ -214,17 +213,17 @@
 }
 
 static zx_status_t do_dmctl_mexec(void) {
-    dmctl_mexec_args_t args;
+    zx_handle_t kernel, bootdata;
     zx_status_t status =
         netboot_prepare_zbi(nbkernel.data, nbbootdata.data,
-                            nbcmdline.file.data, nbcmdline.file.size, &args);
+                            nbcmdline.file.data, nbcmdline.file.size,
+                            &kernel, &bootdata);
     if (status != ZX_OK) {
         return status;
     }
 
     zx_handle_t wait_handle;
-    status = zx_handle_duplicate(
-        args.kernel, ZX_RIGHT_SAME_RIGHTS, &wait_handle);
+    status = zx_handle_duplicate(kernel, ZX_RIGHT_SAME_RIGHTS, &wait_handle);
     if (status != ZX_OK) {
         return status;
     }
@@ -234,10 +233,16 @@
         return ZX_ERR_INTERNAL;
     }
 
-    int r = ioctl_dmctl_mexec(fd, &args);
-    close(fd);
-    if (r < 0) {
-        return r;
+    zx_handle_t dmctl;
+    status = fdio_get_service_handle(fd, &dmctl);
+    if (status != ZX_OK) {
+        return status;
+    }
+    status = fuchsia_device_manager_ExternalControllerPerformMexec(dmctl, kernel,
+                                                                   bootdata);
+    zx_handle_close(dmctl);
+    if (status != ZX_OK) {
+        return status;
     }
 
     status = zx_object_wait_one(wait_handle, ZX_USER_SIGNAL_0,
diff --git a/system/core/netsvc/rules.mk b/system/core/netsvc/rules.mk
index e438c9c..4fb52d8 100644
--- a/system/core/netsvc/rules.mk
+++ b/system/core/netsvc/rules.mk
@@ -32,6 +32,8 @@
     system/ulib/fdio \
     system/ulib/zircon \
 
-MODULE_FIDL_LIBS := system/fidl/zircon-ethernet
+MODULE_FIDL_LIBS := \
+    system/fidl/fuchsia-device-manager \
+    system/fidl/zircon-ethernet \
 
 include make/module.mk
diff --git a/system/core/netsvc/zbi.cpp b/system/core/netsvc/zbi.cpp
index 8230f03..6645dde 100644
--- a/system/core/netsvc/zbi.cpp
+++ b/system/core/netsvc/zbi.cpp
@@ -11,7 +11,7 @@
 zx_status_t netboot_prepare_zbi(zx_handle_t nbkernel_vmo,
                                 zx_handle_t nbbootdata_vmo,
                                 const uint8_t* cmdline, uint32_t cmdline_size,
-                                dmctl_mexec_args_t* args) {
+                                zx_handle_t* kernel_vmo, zx_handle_t* bootdata_vmo) {
     zbi::ZbiVMO kernel, data;
 
     if (nbkernel_vmo == ZX_HANDLE_INVALID) {
@@ -64,7 +64,7 @@
     printf("netbootloader: kernel ZBI %#x bytes data ZBI %#x bytes\n",
            kernel.Length(), data.Length());
 
-    args->kernel = kernel.Release().release();
-    args->bootdata = data.Release().release();
+    *kernel_vmo = kernel.Release().release();
+    *bootdata_vmo = data.Release().release();
     return ZX_OK;
 }
diff --git a/system/core/netsvc/zbi.h b/system/core/netsvc/zbi.h
index e60a39c..8826ade 100644
--- a/system/core/netsvc/zbi.h
+++ b/system/core/netsvc/zbi.h
@@ -6,7 +6,6 @@
 
 #include <stdint.h>
 #include <zircon/compiler.h>
-#include <zircon/device/dmctl.h>
 #include <zircon/types.h>
 
 __BEGIN_CDECLS
@@ -14,6 +13,6 @@
 zx_status_t netboot_prepare_zbi(zx_handle_t nbkernel_vmo,
                                 zx_handle_t nbbootdata_vmo,
                                 const uint8_t* cmdline, uint32_t cmdline_size,
-                                dmctl_mexec_args_t* args);
+                                zx_handle_t* kernel, zx_handle_t* bootdata);
 
 __END_CDECLS
diff --git a/system/fidl/fuchsia-device-manager/coordinator.fidl b/system/fidl/fuchsia-device-manager/coordinator.fidl
index 21050a4..68187ee 100644
--- a/system/fidl/fuchsia-device-manager/coordinator.fidl
+++ b/system/fidl/fuchsia-device-manager/coordinator.fidl
@@ -4,6 +4,8 @@
 
 library fuchsia.device.manager;
 
+// TODO(teisenbe): Move these interfaces to be internal to the devmgr codebase
+
 using zx;
 
 // This definition must be the same size as zx_device_prop_t and is checked by
diff --git a/system/fidl/fuchsia-device-manager/dmctl.fidl b/system/fidl/fuchsia-device-manager/dmctl.fidl
new file mode 100644
index 0000000..0443cff
--- /dev/null
+++ b/system/fidl/fuchsia-device-manager/dmctl.fidl
@@ -0,0 +1,25 @@
+// Copyright 2018 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;
+
+using zx;
+
+/// Interface for requesting devmgr perform miscellaneous actions.
+/// These methods are all work-arounds that should go away eventually.
+[Layout = "Simple"]
+interface ExternalController {
+    /// Execute the given string command.  This is mostly used for accessing debug
+    /// interfaces, but also as a backdoor for plumbing that has not been fully
+    /// solved.
+    1: ExecuteCommand(handle<socket>? log_socket, string:COMMAND_MAX? command)
+                      -> (zx.status status);
+
+    /// Opens a new virtual console and transfers a handle to it over |vc_receiver|.
+    2: OpenVirtcon(handle<channel> vc_receiver);
+
+    /// Perform an mexec with the given kernel and bootdata.
+    /// See ZX-2069 for the thoughts on deprecating mexec.
+    3: PerformMexec(handle<vmo> kernel, handle<vmo> bootdata);
+};
diff --git a/system/fidl/fuchsia-device-manager/rules.mk b/system/fidl/fuchsia-device-manager/rules.mk
index a904bd5..a277ebf 100644
--- a/system/fidl/fuchsia-device-manager/rules.mk
+++ b/system/fidl/fuchsia-device-manager/rules.mk
@@ -12,6 +12,8 @@
 
 MODULE_FIDL_LIBRARY := fuchsia.device.manager
 
-MODULE_SRCS += $(LOCAL_DIR)/coordinator.fidl
+MODULE_SRCS += \
+    $(LOCAL_DIR)/coordinator.fidl \
+    $(LOCAL_DIR)/dmctl.fidl
 
 include make/module.mk
diff --git a/system/public/zircon/device/dmctl.h b/system/public/zircon/device/dmctl.h
deleted file mode 100644
index d2879fc..0000000
--- a/system/public/zircon/device/dmctl.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 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 <stdint.h>
-#include <zircon/device/ioctl.h>
-#include <zircon/device/ioctl-wrapper.h>
-#include <zircon/types.h>
-
-typedef struct {
-    zx_handle_t h;
-    char name[64];
-} dmctl_cmd_t;
-
-// Execute a dmctl command, returning output via provided
-// socket handle.
-#define IOCTL_DMCTL_COMMAND \
-    IOCTL(IOCTL_KIND_SET_HANDLE, IOCTL_FAMILY_DMCTL, 1)
-
-// Open a new virtual console
-// Pass a channel handle.
-// On success one or two handles will be written back (a remoteio device)
-// On failure the channel will be closed.
-#define IOCTL_DMCTL_OPEN_VIRTCON \
-    IOCTL(IOCTL_KIND_SET_HANDLE, IOCTL_FAMILY_DMCTL, 2)
-
-// Install a channel to receive updates on devices and drivers in the system
-// This is an experimental, non-stable interface.  Only one client is supported.
-// Any later calls will disconnect earlier clients.
-// One message will be sent on the provided channel per devmgr_event_t
-#define IOCTL_DMCTL_WATCH_DEVMGR \
-    IOCTL(IOCTL_KIND_SET_HANDLE, IOCTL_FAMILY_DMCTL, 3)
-
-typedef struct {
-    zx_handle_t kernel;
-    zx_handle_t bootdata;
-} dmctl_mexec_args_t;
-
-// Soft reboot the system with a new kernel and bootdata.
-// Passes a handle to the kernel vmo and a handle to the bootdata vmo.
-// The bootdata vmo should contain the cmdline.
-// If successful, this ioctl does not return.
-#define IOCTL_DMCTL_MEXEC \
-    IOCTL(IOCTL_KIND_SET_TWO_HANDLES, IOCTL_FAMILY_DMCTL, 4)
-
-// ssize_t ioctl_dmctl_command(int fd, dmctl_cmd_t* cmd);
-IOCTL_WRAPPER_IN(ioctl_dmctl_command, IOCTL_DMCTL_COMMAND, dmctl_cmd_t);
-
-// ssize_t ioctl_dmctl_open_virtcon(int fd, zx_handle_t* h);
-IOCTL_WRAPPER_IN(ioctl_dmctl_open_virtcon, IOCTL_DMCTL_OPEN_VIRTCON, zx_handle_t);
-
-// ssize_t ioctl_dmctl_mexec(int fd, dmctl_mexec_args_t* args);
-IOCTL_WRAPPER_IN(ioctl_dmctl_mexec, IOCTL_DMCTL_MEXEC, dmctl_mexec_args_t);
diff --git a/system/uapp/run-vc/main.c b/system/uapp/run-vc/main.c
index 2636b13..4e5b601 100644
--- a/system/uapp/run-vc/main.c
+++ b/system/uapp/run-vc/main.c
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <fcntl.h>
+#include <fuchsia/device/manager/c/fidl.h>
 #include <lib/fdio/io.h>
 #include <lib/fdio/spawn.h>
 #include <lib/fdio/util.h>
@@ -13,9 +14,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <zircon/device/dmctl.h>
-#include <zircon/process.h>
 #include <zircon/processargs.h>
+#include <zircon/process.h>
 #include <zircon/status.h>
 #include <zircon/syscalls.h>
 #include <zircon/types.h>
@@ -30,15 +30,23 @@
             return -1;
         }
     }
+    zx_handle_t dmctl;
+    zx_status_t status = fdio_get_service_handle(fd, &dmctl);
+    if (status != ZX_OK) {
+        fprintf(stderr, "error %s converting fd to handle\n", zx_status_get_string(status));
+        return -1;
+    }
 
     zx_handle_t h0, h1;
     if (zx_channel_create(0, &h0, &h1) < 0) {
         return -1;
     }
-    if (ioctl_dmctl_open_virtcon(fd, &h1) < 0) {
+
+    if (fuchsia_device_manager_ExternalControllerOpenVirtcon(dmctl, h1) < 0) {
         return -1;
     }
-    close(fd);
+    h1 = ZX_HANDLE_INVALID;
+    zx_handle_close(dmctl);
 
     zx_object_wait_one(h0, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
                        ZX_TIME_INFINITE, NULL);
@@ -81,8 +89,8 @@
     };
 
     char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
-    zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID, flags, argv[0], argv,
-                                  NULL, 1 + hcount, actions, NULL, err_msg);
+    status = fdio_spawn_etc(ZX_HANDLE_INVALID, flags, argv[0], argv,
+                            NULL, 1 + hcount, actions, NULL, err_msg);
     if (status != ZX_OK) {
         fprintf(stderr, "error %d (%s) launching: %s\n", status,
                 zx_status_get_string(status), err_msg);
diff --git a/system/uapp/run-vc/rules.mk b/system/uapp/run-vc/rules.mk
index d6e852c..95e4c96 100644
--- a/system/uapp/run-vc/rules.mk
+++ b/system/uapp/run-vc/rules.mk
@@ -17,4 +17,6 @@
     system/ulib/zircon \
     system/ulib/c
 
+MODULE_FIDL_LIBS := system/fidl/fuchsia-device-manager
+
 include make/module.mk
diff --git a/system/utest/kernel-unittests/kernel-unittests.cpp b/system/utest/kernel-unittests/kernel-unittests.cpp
index 19c62a70..c8924a9 100644
--- a/system/utest/kernel-unittests/kernel-unittests.cpp
+++ b/system/utest/kernel-unittests/kernel-unittests.cpp
@@ -5,10 +5,15 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include <fuchsia/device/manager/c/fidl.h>
+#include <lib/fdio/util.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/socket.h>
 #include <unittest/unittest.h>
-#include <zircon/device/dmctl.h>
 #include <zircon/syscalls.h>
 
+namespace {
+
 // Ask the kernel to run its unit tests.
 bool run_kernel_unittests() {
     BEGIN_TEST;
@@ -18,23 +23,27 @@
     // Send the command via devmgr.
     int dmctl_fd = open("/dev/misc/dmctl", O_WRONLY);
     ASSERT_GE(dmctl_fd, 0);
-    dmctl_cmd_t cmd;
-    ASSERT_LE(sizeof(command_string), sizeof(cmd.name));
-    strcpy(cmd.name, command_string);
-    // devmgr's ioctl() requires us to pass a socket, but we don't read
+
+    zx::channel dmctl;
+    ASSERT_EQ(fdio_get_service_handle(dmctl_fd, dmctl.reset_and_get_address()), ZX_OK);
+
+    // dmctl's ExecuteCommand() requires us to pass a socket, but we don't read
     // from the other endpoint.
-    zx_handle_t handle;
-    ASSERT_EQ(zx_socket_create(0, &cmd.h, &handle), ZX_OK);
-    ssize_t result = ioctl_dmctl_command(dmctl_fd, &cmd);
-    ASSERT_EQ(close(dmctl_fd), 0);
-    ASSERT_EQ(zx_handle_close(handle), ZX_OK);
+    zx::socket local, remote;
+    ASSERT_EQ(zx::socket::create(0, &local, &remote), ZX_OK);
+    zx_status_t call_status;
+    zx_status_t status = fuchsia_device_manager_ExternalControllerExecuteCommand(
+            dmctl.get(), remote.release(), command_string, strlen(command_string), &call_status);
 
     // Check result of kernel unit tests.
-    ASSERT_EQ(result, ZX_OK);
+    ASSERT_EQ(status, ZX_OK);
+    ASSERT_EQ(call_status, ZX_OK);
 
     END_TEST;
 }
 
+} // namespace
+
 BEGIN_TEST_CASE(kernel_unittests)
 RUN_TEST(run_kernel_unittests)
 END_TEST_CASE(kernel_unittests)
diff --git a/system/utest/kernel-unittests/rules.mk b/system/utest/kernel-unittests/rules.mk
index 090e22a..2ce7a8d 100644
--- a/system/utest/kernel-unittests/rules.mk
+++ b/system/utest/kernel-unittests/rules.mk
@@ -13,10 +13,16 @@
 
 MODULE_NAME := kernel-unittests
 
+MODULE_STATIC_LIBS := \
+    system/ulib/zx \
+
 MODULE_LIBS := \
     system/ulib/c \
     system/ulib/fdio \
     system/ulib/unittest \
     system/ulib/zircon \
 
+MODULE_FIDL_LIBS := \
+    system/fidl/fuchsia-device-manager \
+
 include make/module.mk
diff --git a/third_party/uapp/dash/rules.mk b/third_party/uapp/dash/rules.mk
index 5cdb0f2..7f3b589 100644
--- a/third_party/uapp/dash/rules.mk
+++ b/third_party/uapp/dash/rules.mk
@@ -47,8 +47,17 @@
 
 MODULE_NAME := sh
 
-MODULE_STATIC_LIBS := system/ulib/pretty third_party/ulib/linenoise
-MODULE_LIBS := system/ulib/fdio system/ulib/c system/ulib/zircon
+MODULE_STATIC_LIBS := \
+    system/ulib/pretty \
+    third_party/ulib/linenoise \
+
+MODULE_LIBS := \
+    system/ulib/c \
+    system/ulib/fdio \
+    system/ulib/zircon
+
+MODULE_FIDL_LIBS := \
+    system/fidl/fuchsia-device-manager \
 
 MODULE_CFLAGS := -D_GNU_SOURCE -DBSD -DIFS_BROKEN -DJOBS=0 -DSHELL \
                  -DUSE_LINENOISE
diff --git a/third_party/uapp/dash/src/bltin/zircon.c b/third_party/uapp/dash/src/bltin/zircon.c
index c9f30d2e..a7a6c457 100644
--- a/third_party/uapp/dash/src/bltin/zircon.c
+++ b/third_party/uapp/dash/src/bltin/zircon.c
@@ -15,10 +15,10 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include <zircon/syscalls.h>
+#include <fuchsia/device/manager/c/fidl.h>
+#include <lib/fdio/util.h>
 #include <pretty/hexdump.h>
-
-#include <zircon/device/dmctl.h>
+#include <zircon/syscalls.h>
 
 int zxc_dump(int argc, char** argv) {
     int fd;
@@ -466,44 +466,40 @@
         fprintf(stderr, "error: cannot open dmctl: %d\n", fd);
         return fd;
     }
-
-    // commands with ':' get passed through and don't use
-    // socket for results (since there are none)
-    const char* p;
-    for (p = command; p < (command + length); p++) {
-        if (*p == ':') {
-            write(fd, command, length);
-            return 0;
-        }
-    }
-
-    dmctl_cmd_t cmd;
-    if (length >= sizeof(cmd.name)) {
-        fprintf(stderr, "error: dmctl command longer than %zu bytes: '%.*s'\n",
-                sizeof(cmd.name), (int)length, command);
-        return -1;
-    }
-    snprintf(cmd.name, sizeof(cmd.name), command);
-
-    zx_handle_t h;
-    if (zx_socket_create(0, &cmd.h, &h) < 0) {
+    zx_handle_t dmctl;
+    zx_status_t status = fdio_get_service_handle(fd, &dmctl);
+    if (status != ZX_OK) {
         return -1;
     }
 
-    int r = ioctl_dmctl_command(fd, &cmd);
-    close(fd);
-    if (r < 0) {
-        zx_handle_close(h);
-        return r;
+    if (length > fuchsia_device_manager_COMMAND_MAX) {
+        fprintf(stderr, "error: dmctl command longer than %u bytes: '%.*s'\n",
+                fuchsia_device_manager_COMMAND_MAX, (int)length, command);
+        return -1;
+    }
+
+    zx_handle_t local, remote;
+    if (zx_socket_create(0, &remote, &local) != ZX_OK) {
+        zx_handle_close(dmctl);
+        return -1;
+    }
+
+    zx_status_t call_status;
+    status = fuchsia_device_manager_ExternalControllerExecuteCommand(dmctl, remote, command,
+                                                                     length, &call_status);
+    remote = ZX_HANDLE_INVALID;
+    zx_handle_close(dmctl);
+    if (status != ZX_OK || call_status != ZX_OK) {
+        zx_handle_close(local);
+        return -1;
     }
 
     for (;;) {
-        zx_status_t status;
         char buf[32768];
         size_t actual;
-        if ((status = zx_socket_read(h, 0, buf, sizeof(buf), &actual)) < 0) {
+        if ((status = zx_socket_read(local, 0, buf, sizeof(buf), &actual)) < 0) {
             if (status == ZX_ERR_SHOULD_WAIT) {
-                zx_object_wait_one(h, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
+                zx_object_wait_one(local, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
                                    ZX_TIME_INFINITE, NULL);
                 continue;
             }
@@ -519,7 +515,7 @@
             }
         }
     }
-    zx_handle_close(h);
+    zx_handle_close(local);
 
     return 0;
 }