blob: 4f8cd636bbdca8858947daf2c0cb04a0e13577c8 [file] [log] [blame]
// 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