| // 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 |