| // Copyright 2022 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 <libgen.h> |
| #include <limits.h> |
| |
| #include <list> |
| #include <unordered_set> |
| |
| #include <acpica/acpi.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/devices/board/lib/acpi/acpi-impl.h" |
| #include "src/devices/board/lib/acpi/device-args.h" |
| #include "src/devices/board/lib/acpi/device-for-host.h" |
| #include "src/devices/board/lib/acpi/manager-host.h" |
| #include "src/devices/board/tests/acpi-host-tests/table-manager.h" |
| #include "third_party/acpica/source/include/actypes.h" |
| |
| // Path to the compiled tables in out/, passed in argv[1]. |
| std::string test_data_dir; |
| namespace acpi_host_test { |
| |
| class AcpiHostTest : public testing::Test { |
| public: |
| void InitAcpiWithTables(const char* table_name) { |
| auto* tables = acpi::AcpiTableManager::LoadFromDir(test_data_dir + "/" + table_name); |
| ASSERT_NE(nullptr, tables); |
| tables->ApplyFixups(); |
| |
| ASSERT_EQ(AE_OK, AcpiInitializeSubsystem()); |
| AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_SYSTEM_MEMORY, |
| AcpiHostTest::MemoryHandlerThunk, nullptr, this); |
| |
| ASSERT_EQ(AE_OK, AcpiInitializeTables(NULL, 32, FALSE)); |
| ASSERT_EQ(AE_OK, AcpiLoadTables()); |
| |
| ASSERT_EQ(AE_OK, AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION)); |
| |
| ASSERT_EQ(AE_OK, AcpiInitializeObjects(ACPI_FULL_INITIALIZATION)); |
| } |
| |
| void MemoryHandler(uint32_t func, ACPI_PHYSICAL_ADDRESS addr, uint32_t width, uint64_t* value) { |
| if (func == ACPI_READ) { |
| uint8_t* value_bytes = reinterpret_cast<uint8_t*>(value); |
| for (size_t i = 0; i < width; i++) { |
| auto entry = fake_mmio_.find(addr + i); |
| if (entry == fake_mmio_.end()) { |
| continue; |
| } |
| |
| value_bytes[i] = entry->second; |
| } |
| } else { |
| uint8_t* value_bytes = reinterpret_cast<uint8_t*>(value); |
| for (size_t i = 0; i < width; i++) { |
| fake_mmio_.emplace(addr + i, value_bytes[i]); |
| } |
| } |
| } |
| |
| static ACPI_STATUS MemoryHandlerThunk(uint32_t func, ACPI_PHYSICAL_ADDRESS addr, uint32_t width, |
| uint64_t* value, void* ctx, void* region_ctx) { |
| static_cast<AcpiHostTest*>(ctx)->MemoryHandler(func, addr, width, value); |
| return AE_OK; |
| } |
| |
| void TearDown() override { |
| // Normally the DDK would free things, but we don't have the DDK, so we have to do it ourselves. |
| for (auto value : manager_.zx_devices_) { |
| auto dev = static_cast<acpi::Device*>(value.second); |
| delete dev; |
| } |
| } |
| |
| protected: |
| acpi::AcpiImpl acpi_; |
| acpi::Device root_device_{acpi::DeviceArgs(nullptr, &manager_, nullptr, ACPI_ROOT_OBJECT)}; |
| acpi::HostManager manager_{&acpi_, root_device_.zxdev()}; |
| std::unordered_map<ACPI_PHYSICAL_ADDRESS, uint8_t> fake_mmio_; |
| }; |
| |
| TEST_F(AcpiHostTest, DeviceIsChildOfScopeTest) { |
| InitAcpiWithTables("device-child-of-scope"); |
| |
| ASSERT_EQ(AE_OK, manager_.DiscoverDevices().status_value()); |
| ASSERT_EQ(AE_OK, manager_.ConfigureDiscoveredDevices().status_value()); |
| ASSERT_EQ(AE_OK, manager_.PublishDevices(nullptr, nullptr).status_value()); |
| |
| auto root_hnd = manager_.acpi()->GetHandle(nullptr, "\\"); |
| ASSERT_EQ(AE_OK, root_hnd.status_value()); |
| auto root = manager_.LookupDevice(*root_hnd); |
| |
| auto hnd = manager_.acpi()->GetHandle(ACPI_ROOT_OBJECT, "_GPE.DEV0"); |
| ASSERT_EQ(AE_OK, hnd.status_value()); |
| |
| auto child = manager_.LookupDevice(*hnd); |
| ASSERT_NE(nullptr, child); |
| ASSERT_TRUE(child->built()); |
| ASSERT_EQ(root, child->parent()) |
| << "Child of scope should end up as a child of the nearest ancestor device"; |
| } |
| } // namespace acpi_host_test |
| |
| zx_status_t pci_init(zx_device_t* parent, ACPI_HANDLE object, |
| acpi::UniquePtr<ACPI_DEVICE_INFO> info, acpi::Manager* acpi, |
| std::vector<pci_bdf_t> acpi_bdfs) { |
| return ZX_OK; |
| } |
| |
| int main(int argc, char** argv) { |
| if (argc != 2) { |
| printf("Usage: %s <path/to/tables>\n", argv[0]); |
| return -1; |
| } |
| test_data_dir = argv[1]; |
| |
| testing::InitGoogleTest(&argc, argv); |
| |
| return RUN_ALL_TESTS(); |
| } |