| // Copyright 2020 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 <fuchsia/hardware/acpi/llcpp/fidl.h> |
| #include <lib/fdio/cpp/caller.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fidl/cpp/message.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/vmo.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <fbl/array.h> |
| #include <fbl/span.h> |
| #include <fbl/unique_fd.h> |
| #include <zxtest/zxtest.h> |
| |
| // These are integration tests of the x86 board drive which check that exported services |
| // are correctly functioning. |
| |
| namespace { |
| |
| constexpr uint32_t kGiB = 1024 * 1024 * 1024; |
| |
| using llcpp::fuchsia::hardware::acpi::Acpi; |
| using llcpp::fuchsia::hardware::acpi::TableInfo; |
| |
| const char kAcpiDevicePath[] = "/dev/sys/platform/acpi"; |
| |
| // Open up channel to ACPI device. |
| fdio_cpp::FdioCaller OpenChannel() { |
| fbl::unique_fd fd{open(kAcpiDevicePath, O_RDWR)}; |
| ZX_ASSERT(fd.is_valid()); |
| return fdio_cpp::FdioCaller{std::move(fd)}; |
| } |
| |
| // Convert a fidl::Array<uint8_t, n> type to a std::string. |
| template <uint64_t N> |
| std::string SignatureToString(const fidl::Array<uint8_t, N>& array) { |
| return std::string(reinterpret_cast<const char*>(array.data()), array.size()); |
| } |
| |
| // Convert a string type to a fidl::Array<uint8_t, 4>. |
| fidl::Array<uint8_t, 4> StringToSignature(std::string_view str) { |
| fidl::Array<uint8_t, 4> result; |
| ZX_ASSERT(str.size() >= 4); |
| memcpy(result.data(), str.data(), 4); |
| return result; |
| } |
| |
| // Create a pair of VMOs for transferring data to and from the x86 board driver. |
| // |
| // |size| specifies how much memory to use for the VMO. By default, we allocate |
| // 1 GiB to ensure that we have more than enough space: the kernel won't actually |
| // allocate this memory until needed, though, so in practice we will only use |
| // a tiny fraction of this (A typical size for the DSDT table is ~100kiB.) |
| std::tuple<zx::vmo, zx::vmo> CreateVmoPair(size_t size = 1 * kGiB) { |
| zx::vmo a, b; |
| ZX_ASSERT(zx::vmo::create(size, /*options=*/0, &a) == ZX_OK); |
| ZX_ASSERT(a.duplicate(ZX_RIGHT_SAME_RIGHTS, &b) == ZX_OK); |
| return std::make_tuple(std::move(a), std::move(b)); |
| } |
| |
| TEST(X86Board, Connect) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| EXPECT_TRUE(dev.channel()->is_valid()); |
| } |
| |
| TEST(X86Board, ListTableEntries) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| |
| Acpi::ResultOf::ListTableEntries result = Acpi::Call::ListTableEntries(dev.channel()); |
| ASSERT_TRUE(result.ok()); |
| ASSERT_TRUE(result.value().result.is_response()); |
| const auto& response = result.value().result.response(); |
| |
| // We expect to find at least a DSDT entry. |
| EXPECT_GE(response.entries.count(), 1); |
| bool found_dsdt = false; |
| for (const TableInfo& info : response.entries) { |
| if (SignatureToString(info.name) != "DSDT") { |
| continue; |
| } |
| EXPECT_GE(info.size, 1); |
| found_dsdt = true; |
| } |
| EXPECT_TRUE(found_dsdt); |
| } |
| |
| TEST(X86Board, ReadNamedTable) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| |
| // Read the system's DSDT entry. Every system should have one of these. |
| auto [vmo, vmo_copy] = CreateVmoPair(); |
| Acpi::ResultOf::ReadNamedTable result = |
| Acpi::Call::ReadNamedTable(dev.channel(), StringToSignature("DSDT"), 0, std::move(vmo_copy)); |
| ASSERT_TRUE(result.ok()); |
| ASSERT_TRUE(result.value().result.is_response()); |
| const auto& response = result.value().result.response(); |
| |
| // Ensure the size looks sensible. |
| ASSERT_GE(response.size, 4); |
| |
| // Ensure the first four bytes match "DSDT". |
| char buff[4]; |
| ASSERT_OK(vmo.read(buff, /*offset=*/0, /*length=*/4)); |
| EXPECT_EQ("DSDT", std::string_view(buff, 4)); |
| } |
| |
| TEST(X86Board, InvalidTableName) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| |
| // Read an invalid entry. |
| auto [vmo, vmo_copy] = CreateVmoPair(); |
| Acpi::ResultOf::ReadNamedTable result = |
| Acpi::Call::ReadNamedTable(dev.channel(), StringToSignature("???\n"), 0, std::move(vmo_copy)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_TRUE(result.value().result.is_err()); |
| EXPECT_EQ(result.value().result.err(), ZX_ERR_NOT_FOUND); |
| } |
| |
| TEST(X86Board, InvalidIndexNumber) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| |
| // Read a large index of the DSDT table. We should have a DSDT table, but really only |
| // have 1 of them. |
| auto [vmo, vmo_copy] = CreateVmoPair(); |
| Acpi::ResultOf::ReadNamedTable result = Acpi::Call::ReadNamedTable( |
| dev.channel(), StringToSignature("DSDT"), 1234, std::move(vmo_copy)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_TRUE(result.value().result.is_err()); |
| EXPECT_EQ(result.value().result.err(), ZX_ERR_NOT_FOUND); |
| } |
| |
| TEST(X86Board, VmoTooSmall) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| |
| // Only allocate a VMO with 3 bytes backing it. |
| auto [vmo, vmo_copy] = CreateVmoPair(/*size=*/3); |
| Acpi::ResultOf::ReadNamedTable result = |
| Acpi::Call::ReadNamedTable(dev.channel(), StringToSignature("DSDT"), 0, std::move(vmo_copy)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_TRUE(result.value().result.is_err()); |
| EXPECT_EQ(result.value().result.err(), ZX_ERR_OUT_OF_RANGE); |
| } |
| |
| TEST(X86Board, ReadOnlyVmoSent) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| |
| // Send a read-only VMO. |
| auto [vmo, vmo_copy] = CreateVmoPair(); |
| zx::vmo read_only_vmo; |
| ZX_ASSERT(vmo_copy.replace(ZX_RIGHT_NONE, &read_only_vmo) == ZX_OK); |
| Acpi::ResultOf::ReadNamedTable result = Acpi::Call::ReadNamedTable( |
| dev.channel(), StringToSignature("DSDT"), 0, std::move(read_only_vmo)); |
| EXPECT_EQ(result.status(), ZX_ERR_ACCESS_DENIED); |
| } |
| |
| TEST(X86Board, InvalidObject) { |
| fdio_cpp::FdioCaller dev = OpenChannel(); |
| |
| // Send something that is not a VMO. |
| zx::channel a, b; |
| zx::channel::create(/*flags=*/0, &a, &b); |
| Acpi::ResultOf::ReadNamedTable result = |
| Acpi::Call::ReadNamedTable(dev.channel(), StringToSignature("DSDT"), 0, zx::vmo(a.release())); |
| // FIDL detects that a channel is being sent as a VMO handle. |
| ASSERT_FALSE(result.ok()); |
| } |
| |
| } // namespace |