| // 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 <fuchsia/hardware/cpu/ctrl/llcpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/fake_ddk/fidl-helper.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| |
| #include <ddktl/device.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "performance-domain.h" |
| |
| namespace { |
| |
| constexpr cpuctrl::wire::CpuPerformanceStateInfo kTestPstates[] = { |
| {.frequency_hz = 1000, .voltage_uv = 100}, {.frequency_hz = 800, .voltage_uv = 90}, |
| {.frequency_hz = 600, .voltage_uv = 80}, {.frequency_hz = 400, .voltage_uv = 70}, |
| {.frequency_hz = 200, .voltage_uv = 60}, |
| }; |
| |
| constexpr uint32_t kInitialPstate = 0; |
| |
| constexpr uint32_t kNumLogicalCores = 4; |
| |
| constexpr uint64_t kLogicalCoreIds[kNumLogicalCores] = {1, 2, 3, 4}; |
| |
| class FakeCpuDevice; |
| using TestDeviceType = ddk::Device<FakeCpuDevice, ddk::Messageable, ddk::PerformanceTunable>; |
| |
| class FakeCpuDevice : TestDeviceType, |
| fidl::WireInterface<cpuctrl::Device>, |
| fidl::WireInterface<fuchsia_device::Controller> { |
| public: |
| FakeCpuDevice() : TestDeviceType(nullptr) {} |
| ~FakeCpuDevice() {} |
| |
| unsigned int PstateSetCount() const { return pstate_set_count_; } |
| |
| // Manage the fake FIDL Loop |
| zx_status_t Init(); |
| static zx_status_t MessageOp(void* ctx, fidl_incoming_msg_t* msg, fidl_txn_t* txn); |
| zx::channel& GetMessengerChannel() { return messenger_.local(); } |
| |
| zx_status_t DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn); |
| void DdkRelease() {} |
| |
| zx_status_t DdkSetPerformanceState(uint32_t requested_state, uint32_t* out_state) { |
| *out_state = requested_state; |
| return ZX_OK; |
| } |
| |
| // fidl::WireInterface<fuchsia_device::Controller> methods |
| // We only implement the following methods for now |
| void SetPerformanceState(uint32_t requested_state, |
| SetPerformanceStateCompleter::Sync& _completer) override; |
| void GetDevicePerformanceStates(GetDevicePerformanceStatesCompleter::Sync& completer) override; |
| void GetCurrentPerformanceState(GetCurrentPerformanceStateCompleter::Sync& completer) override; |
| |
| // The following methods are left unimplemented and it's an error to call them. |
| void Bind(::fidl::StringView driver, BindCompleter::Sync& _completer) override {} |
| void Rebind(::fidl::StringView driver, RebindCompleter::Sync& _completer) override {} |
| void UnbindChildren(UnbindChildrenCompleter::Sync& completer) override {} |
| void ScheduleUnbind(ScheduleUnbindCompleter::Sync& _completer) override {} |
| void GetDriverName(GetDriverNameCompleter::Sync& _completer) override {} |
| void GetDeviceName(GetDeviceNameCompleter::Sync& _completer) override {} |
| void GetTopologicalPath(GetTopologicalPathCompleter::Sync& _completer) override {} |
| void GetEventHandle(GetEventHandleCompleter::Sync& _completer) override {} |
| void GetDriverLogFlags(GetDriverLogFlagsCompleter::Sync& _completer) override {} |
| void SetDriverLogFlags(uint32_t clear_flags, uint32_t set_flags, |
| SetDriverLogFlagsCompleter::Sync& _completer) override {} |
| void RunCompatibilityTests(int64_t hook_wait_time, |
| RunCompatibilityTestsCompleter::Sync& _completer) override {} |
| void GetDevicePowerCaps(GetDevicePowerCapsCompleter::Sync& _completer) override {} |
| void ConfigureAutoSuspend(bool enable, fuchsia_device::wire::DevicePowerState requested_state, |
| ConfigureAutoSuspendCompleter::Sync& _completer) override {} |
| void UpdatePowerStateMapping(::fidl::Array<fuchsia_device::wire::SystemPowerStateInfo, 7> mapping, |
| UpdatePowerStateMappingCompleter::Sync& _completer) override {} |
| void GetPowerStateMapping(GetPowerStateMappingCompleter::Sync& _completer) override {} |
| void Suspend(fuchsia_device::wire::DevicePowerState requested_state, |
| SuspendCompleter::Sync& _completer) override {} |
| void Resume(ResumeCompleter::Sync& _complete) override {} |
| |
| private: |
| virtual void GetPerformanceStateInfo(uint32_t state, |
| GetPerformanceStateInfoCompleter::Sync& completer) override; |
| virtual void GetNumLogicalCores(GetNumLogicalCoresCompleter::Sync& completer) override; |
| virtual void GetLogicalCoreId(uint64_t index, |
| GetLogicalCoreIdCompleter::Sync& completer) override; |
| |
| fake_ddk::FidlMessenger messenger_; |
| |
| uint32_t current_pstate_ = kInitialPstate; |
| unsigned int pstate_set_count_ = 0; |
| }; |
| |
| zx_status_t FakeCpuDevice::Init() { |
| return messenger_.SetMessageOp(this, FakeCpuDevice::MessageOp); |
| } |
| |
| zx_status_t FakeCpuDevice::MessageOp(void* ctx, fidl_incoming_msg_t* msg, fidl_txn_t* txn) { |
| return static_cast<FakeCpuDevice*>(ctx)->DdkMessage(msg, txn); |
| } |
| |
| zx_status_t FakeCpuDevice::DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn) { |
| DdkTransaction transaction(txn); |
| if (fidl::WireTryDispatch<cpuctrl::Device>(this, msg, &transaction) == |
| ::fidl::DispatchResult::kFound) { |
| return transaction.Status(); |
| } |
| fidl::WireDispatch<fuchsia_device::Controller>(this, msg, &transaction); |
| return transaction.Status(); |
| } |
| |
| void FakeCpuDevice::GetPerformanceStateInfo(uint32_t state, |
| GetPerformanceStateInfoCompleter::Sync& completer) { |
| if (state >= countof(kTestPstates)) { |
| completer.ReplyError(ZX_ERR_OUT_OF_RANGE); |
| } else { |
| completer.ReplySuccess(kTestPstates[state]); |
| } |
| } |
| |
| void FakeCpuDevice::GetNumLogicalCores(GetNumLogicalCoresCompleter::Sync& completer) { |
| completer.Reply(kNumLogicalCores); |
| } |
| |
| void FakeCpuDevice::GetLogicalCoreId(uint64_t index, GetLogicalCoreIdCompleter::Sync& completer) { |
| if (index >= countof(kLogicalCoreIds)) { |
| completer.Reply(UINT64_MAX); |
| } |
| completer.Reply(kLogicalCoreIds[index]); |
| } |
| |
| void FakeCpuDevice::SetPerformanceState(uint32_t requested_state, |
| SetPerformanceStateCompleter::Sync& completer) { |
| if (requested_state > countof(kTestPstates)) { |
| completer.Reply(ZX_ERR_NOT_SUPPORTED, requested_state); |
| return; |
| } |
| |
| pstate_set_count_++; |
| current_pstate_ = requested_state; |
| completer.Reply(ZX_OK, requested_state); |
| } |
| |
| void FakeCpuDevice::GetDevicePerformanceStates( |
| GetDevicePerformanceStatesCompleter::Sync& completer) { |
| ::fidl::Array<fuchsia_device::wire::DevicePerformanceStateInfo, |
| fuchsia_device::wire::kMaxDevicePerformanceStates> |
| states{}; |
| |
| for (size_t i = 0; i < fuchsia_device::wire::kMaxDevicePerformanceStates; i++) { |
| states[i].is_supported = (i < countof(kTestPstates)); |
| states[i].state_id = static_cast<uint32_t>(i); |
| } |
| completer.Reply(states, ZX_OK); |
| } |
| |
| void FakeCpuDevice::GetCurrentPerformanceState( |
| GetCurrentPerformanceStateCompleter::Sync& completer) { |
| completer.Reply(current_pstate_); |
| } |
| |
| class TestCpuPerformanceDomain : public CpuPerformanceDomain { |
| public: |
| // Permit Explicit Construction |
| TestCpuPerformanceDomain(fidl::WireSyncClient<cpuctrl::Device> cpu_client, |
| fidl::WireSyncClient<fuchsia_device::Controller> device_client) |
| : CpuPerformanceDomain(std::move(cpu_client), std::move(device_client)) {} |
| }; |
| |
| class PerformanceDomainTest : public zxtest::Test { |
| public: |
| void SetUp() override; |
| |
| protected: |
| FakeCpuDevice cpu_; |
| |
| std::unique_ptr<TestCpuPerformanceDomain> pd_; |
| }; |
| |
| void PerformanceDomainTest::SetUp() { |
| ASSERT_OK(cpu_.Init()); |
| |
| zx::channel cpu_client_channel(cpu_.GetMessengerChannel().get()); |
| zx::channel device_client_channel(cpu_.GetMessengerChannel().get()); |
| |
| fidl::WireSyncClient<cpuctrl::Device> cpu_client(std::move(cpu_client_channel)); |
| fidl::WireSyncClient<fuchsia_device::Controller> device_client(std::move(device_client_channel)); |
| |
| pd_ = std::make_unique<TestCpuPerformanceDomain>(std::move(cpu_client), std::move(device_client)); |
| } |
| |
| // Trivial Tests. |
| TEST_F(PerformanceDomainTest, TestNumLogicalCores) { |
| const auto [core_count_status, core_count] = pd_->GetNumLogicalCores(); |
| |
| EXPECT_OK(core_count_status); |
| EXPECT_EQ(core_count, kNumLogicalCores); |
| } |
| |
| TEST_F(PerformanceDomainTest, TestGetCurrentPerformanceState) { |
| const auto [st, pstate, pstate_info] = pd_->GetCurrentPerformanceState(); |
| EXPECT_OK(st); |
| EXPECT_EQ(pstate, kInitialPstate); |
| EXPECT_EQ(pstate_info.frequency_hz, kTestPstates[kInitialPstate].frequency_hz); |
| EXPECT_EQ(pstate_info.voltage_uv, kTestPstates[kInitialPstate].voltage_uv); |
| } |
| |
| TEST_F(PerformanceDomainTest, TestGetPerformanceStates) { |
| const auto pstates = pd_->GetPerformanceStates(); |
| |
| ASSERT_EQ(pstates.size(), countof(kTestPstates)); |
| |
| for (size_t i = 0; i < pstates.size(); i++) { |
| EXPECT_EQ(pstates[i].voltage_uv, kTestPstates[i].voltage_uv); |
| EXPECT_EQ(pstates[i].frequency_hz, kTestPstates[i].frequency_hz); |
| } |
| } |
| |
| TEST_F(PerformanceDomainTest, TestSetPerformanceState) { |
| // Just move to the next sequential pstate with wraparound. |
| const uint32_t test_pstate = (kInitialPstate + 1) % countof(kTestPstates); |
| const uint32_t invalid_pstate = countof(kTestPstates) + 1; |
| zx_status_t st = pd_->SetPerformanceState(test_pstate); |
| |
| EXPECT_OK(st); |
| |
| { |
| const auto [res, new_pstate, info] = pd_->GetCurrentPerformanceState(); |
| EXPECT_OK(res); |
| EXPECT_EQ(new_pstate, test_pstate); |
| } |
| |
| st = pd_->SetPerformanceState(invalid_pstate); |
| EXPECT_NOT_OK(st); |
| |
| { |
| // Make sure the pstate hasn't changed. |
| const auto [res, new_pstate, info] = pd_->GetCurrentPerformanceState(); |
| EXPECT_OK(res); |
| EXPECT_EQ(new_pstate, test_pstate); |
| } |
| |
| // Make sure there was exactly one successful call to SetPerformanceState. |
| EXPECT_EQ(cpu_.PstateSetCount(), 1); |
| } |
| |
| } // namespace |