blob: ae5b8a9ac61158a42a318ab3439dd57ad0c7d2cc [file] [log] [blame]
// 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 <fidl/fuchsia.hardware.test/cpp/wire.h>
#include <fuchsia/hardware/test/c/fidl.h>
#include <lib/ddk/metadata.h>
#include <lib/ddk/platform-defs.h>
#include <lib/driver-integration-test/fixture.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/unsafe.h>
#include <lib/fidl/llcpp/coding.h>
#include <stdio.h>
#include <stdlib.h>
#include <zircon/fidl.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <vector>
#include <zxtest/zxtest.h>
#include "fidl/fuchsia.hardware.test/cpp/wire_messaging.h"
using driver_integration_test::IsolatedDevmgr;
namespace {
void CheckTransaction(const board_test::DeviceEntry& entry, const char* device_fs) {
IsolatedDevmgr devmgr;
zx_handle_t driver_channel;
// Set the driver arguments.
IsolatedDevmgr::Args args;
args.device_list.push_back(entry);
// Create the isolated Devmgr.
zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
ASSERT_OK(status);
// Wait for the driver to be created
fbl::unique_fd fd;
status = device_watcher::RecursiveWaitForFile(devmgr.devfs_root(), device_fs, &fd);
ASSERT_OK(status);
// Get a FIDL channel to the device
status = fdio_get_service_handle(fd.get(), &driver_channel);
ASSERT_OK(status);
// The method does not define a request payload, so the message should be a header only.
fidl_message_header_t hdr;
std::memset(&hdr, 0, sizeof(hdr));
zx_txid_t first_txid = 1;
fidl_init_txn_header(&hdr, first_txid, fuchsia_hardware_test_DeviceGetChannelOrdinal);
ASSERT_OK(zx_channel_write(driver_channel, 0, &hdr, sizeof(hdr), nullptr, 0));
ASSERT_OK(zx_object_wait_one(driver_channel, ZX_CHANNEL_READABLE, ZX_TIME_INFINITE, nullptr));
std::memset(&hdr, 0, sizeof(hdr));
zx_txid_t second_txid = 2;
fidl_init_txn_header(&hdr, second_txid, fuchsia_hardware_test_DeviceGetChannelOrdinal);
ASSERT_OK(zx_channel_write(driver_channel, 0, &hdr, sizeof(hdr), nullptr, 0));
// If the transaction incorrectly closes the sent handles, it will cause a policy violation.
// Waiting for the channel to be readable once isn't enough, there is still a very small amount
// of time before the transaction destructor runs. A second read ensures that the first
// succeeded. If a policy violation occurs, the second read below will fail as the driver
// channel will have been closed.
auto msg_bytes = std::make_unique<uint8_t[]>(ZX_CHANNEL_MAX_MSG_BYTES);
auto msg_handles = std::make_unique<zx_handle_t[]>(ZX_CHANNEL_MAX_MSG_HANDLES);
uint32_t actual_bytes = 0;
uint32_t actual_handles = 0;
status = zx_channel_read(driver_channel, 0, msg_bytes.get(), msg_handles.get(),
ZX_CHANNEL_MAX_MSG_BYTES, ZX_CHANNEL_MAX_MSG_HANDLES, &actual_bytes,
&actual_handles);
if (status == ZX_ERR_SHOULD_WAIT) {
ASSERT_OK(zx_object_wait_one(driver_channel, ZX_CHANNEL_READABLE, ZX_TIME_INFINITE, nullptr));
} else {
ASSERT_OK(status);
}
status = zx_channel_read(driver_channel, 0, msg_bytes.get(), msg_handles.get(),
ZX_CHANNEL_MAX_MSG_BYTES, ZX_CHANNEL_MAX_MSG_HANDLES, &actual_bytes,
&actual_handles);
if (status == ZX_ERR_SHOULD_WAIT) {
ASSERT_OK(zx_object_wait_one(driver_channel, ZX_CHANNEL_READABLE, ZX_TIME_INFINITE, nullptr));
} else {
ASSERT_OK(status);
}
}
// Test that the transaction does not incorrectly close handles during Reply.
TEST(FidlDDKDispatcherTest, SyncTransactionHandleTest) {
board_test::DeviceEntry entry = {};
strcpy(entry.name, "ddk-fidl");
entry.vid = PDEV_VID_TEST;
entry.pid = PDEV_PID_DDKFIDL_TEST;
entry.did = PDEV_DID_TEST_DDKFIDL;
CheckTransaction(entry, "sys/platform/11:09:d/ddk-fidl");
}
TEST(FidlDDKDispatcherTest, AsyncTransactionHandleTest) {
board_test::DeviceEntry entry = {};
strcpy(entry.name, "ddk-async-fidl");
entry.vid = PDEV_VID_TEST;
entry.pid = PDEV_PID_DDKFIDL_TEST;
entry.did = PDEV_DID_TEST_DDKASYNCFIDL;
CheckTransaction(entry, "sys/platform/11:09:15/ddk-async-fidl");
}
} // namespace