blob: 1cad61ced2d981d916f8ba2e4f9cf545c1169236 [file] [log] [blame]
// Copyright 2017 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 "helper/platform_device_helper.h"
#include "mock/mock_mmio.h"
#include "msd_arm_device.h"
#include "registers.h"
#include "gtest/gtest.h"
// These tests are unit testing the functionality of MsdArmDevice.
// All of these tests instantiate the device in test mode, that is without the device thread active.
class TestMsdArmDevice {
public:
void CreateAndDestroy()
{
std::unique_ptr<MsdArmDevice> device = MsdArmDevice::Create(GetTestDeviceHandle(), false);
EXPECT_NE(device, nullptr);
}
void Dump()
{
std::unique_ptr<MsdArmDevice> device = MsdArmDevice::Create(GetTestDeviceHandle(), false);
EXPECT_NE(device, nullptr);
MsdArmDevice::DumpState dump_state;
device->Dump(&dump_state, true);
ASSERT_EQ(12u, dump_state.power_states.size());
EXPECT_EQ(std::string("L2 Cache"), dump_state.power_states[0].core_type);
EXPECT_EQ(std::string("Present"), dump_state.power_states[0].status_type);
EXPECT_EQ(1lu, dump_state.power_states[0].bitmask);
EXPECT_EQ(0u, dump_state.gpu_fault_status);
EXPECT_EQ(0u, dump_state.gpu_fault_address);
EXPECT_EQ(3u, dump_state.job_slot_status.size());
for (size_t i = 0; i < dump_state.job_slot_status.size(); i++)
EXPECT_EQ(0u, dump_state.job_slot_status[i].status);
EXPECT_EQ(8u, dump_state.address_space_status.size());
for (size_t i = 0; i < dump_state.address_space_status.size(); i++)
EXPECT_EQ(0u, dump_state.address_space_status[i].status);
std::string dump_string;
device->FormatDump(dump_state, dump_string);
EXPECT_NE(nullptr,
strstr(dump_string.c_str(), "Core type L2 Cache state Present bitmap: 0x1"));
EXPECT_NE(nullptr, strstr(dump_string.c_str(),
"Job slot 2 status 0x0 head 0x0 tail 0x0 config 0x0"));
EXPECT_NE(nullptr, strstr(dump_string.c_str(),
"AS 7 status 0x0 fault status 0x0 fault address 0x0"));
}
void MockDump()
{
auto reg_io = std::make_unique<magma::RegisterIo>(MockMmio::Create(1024 * 1024));
uint32_t offset = static_cast<uint32_t>(registers::CoreReadyState::CoreType::kShader) +
static_cast<uint32_t>(registers::CoreReadyState::StatusType::kReady);
reg_io->Write32(offset, 2);
reg_io->Write32(offset + 4, 5);
static constexpr uint64_t kFaultAddress = 0xffffffff88888888lu;
registers::GpuFaultAddress::Get().FromValue(kFaultAddress).WriteTo(reg_io.get());
registers::GpuFaultStatus::Get().FromValue(5).WriteTo(reg_io.get());
registers::AsRegisters(7).Status().FromValue(5).WriteTo(reg_io.get());
registers::AsRegisters(7).FaultStatus().FromValue(12).WriteTo(reg_io.get());
registers::AsRegisters(7).FaultAddress().FromValue(kFaultAddress).WriteTo(reg_io.get());
registers::JobSlotRegisters(2).Status().FromValue(10).WriteTo(reg_io.get());
registers::JobSlotRegisters(1).Head().FromValue(9).WriteTo(reg_io.get());
registers::JobSlotRegisters(0).Tail().FromValue(8).WriteTo(reg_io.get());
registers::JobSlotRegisters(0).Config().FromValue(7).WriteTo(reg_io.get());
MsdArmDevice::DumpState dump_state;
GpuFeatures features;
features.address_space_count = 9;
features.job_slot_count = 7;
MsdArmDevice::DumpRegisters(features, reg_io.get(), &dump_state);
bool found = false;
for (auto& pstate : dump_state.power_states) {
if (std::string("Shader") == pstate.core_type &&
std::string("Ready") == pstate.status_type) {
EXPECT_EQ(0x500000002ul, pstate.bitmask);
found = true;
}
}
EXPECT_EQ(5u, dump_state.gpu_fault_status);
EXPECT_EQ(kFaultAddress, dump_state.gpu_fault_address);
EXPECT_EQ(5u, dump_state.address_space_status[7].status);
EXPECT_EQ(12u, dump_state.address_space_status[7].fault_status);
EXPECT_EQ(kFaultAddress, dump_state.address_space_status[7].fault_address);
EXPECT_EQ(10u, dump_state.job_slot_status[2].status);
EXPECT_EQ(9u, dump_state.job_slot_status[1].head);
EXPECT_EQ(8u, dump_state.job_slot_status[0].tail);
EXPECT_EQ(7u, dump_state.job_slot_status[0].config);
EXPECT_TRUE(found);
}
void ProcessRequest()
{
std::unique_ptr<MsdArmDevice> device(MsdArmDevice::Create(GetTestDeviceHandle(), false));
ASSERT_NE(device, nullptr);
class TestRequest : public DeviceRequest {
public:
TestRequest(std::shared_ptr<bool> processing_complete)
: processing_complete_(processing_complete)
{
}
protected:
magma::Status Process(MsdArmDevice* device) override
{
*processing_complete_ = true;
return MAGMA_STATUS_OK;
}
private:
std::shared_ptr<bool> processing_complete_;
};
auto processing_complete = std::make_shared<bool>(false);
auto request = std::make_unique<TestRequest>(processing_complete);
request->ProcessAndReply(device.get());
EXPECT_TRUE(processing_complete);
}
void MockExecuteAtom()
{
auto register_io = std::make_unique<magma::RegisterIo>(MockMmio::Create(1024 * 1024));
magma::RegisterIo* reg_io = register_io.get();
std::unique_ptr<MsdArmDevice> device = MsdArmDevice::Create(GetTestDeviceHandle(), false);
EXPECT_NE(device, nullptr);
device->set_register_io(std::move(register_io));
auto connection = MsdArmConnection::Create(0, device.get());
auto null_atom =
std::make_unique<MsdArmAtom>(connection, 0, 0, 0, magma_arm_mali_user_data(), 0);
device->scheduler_->EnqueueAtom(std::move(null_atom));
device->scheduler_->TryToSchedule();
// Atom has 0 job chain address and should be thrown out.
EXPECT_EQ(0u, device->scheduler_->GetAtomListSize());
MsdArmAtom atom(connection, 5, 0, 0, magma_arm_mali_user_data(), 0);
atom.set_require_cycle_counter();
device->ExecuteAtomOnDevice(&atom, reg_io);
EXPECT_EQ(registers::GpuCommand::kCmdCycleCountStart,
reg_io->Read32(registers::GpuCommand::kOffset));
constexpr uint32_t kJobSlot = 1;
auto connection1 = MsdArmConnection::Create(0, device.get());
MsdArmAtom atom1(connection1, 100, kJobSlot, 0, magma_arm_mali_user_data(), 0);
device->ExecuteAtomOnDevice(&atom1, reg_io);
registers::JobSlotRegisters regs(kJobSlot);
EXPECT_EQ(0xffffffffffffffffu, regs.AffinityNext().ReadFrom(reg_io).reg_value());
EXPECT_EQ(100u, regs.HeadNext().ReadFrom(reg_io).reg_value());
constexpr uint32_t kCommandStart = registers::JobSlotCommand::kCommandStart;
EXPECT_EQ(kCommandStart, regs.CommandNext().ReadFrom(reg_io).reg_value());
auto config_next = regs.ConfigNext().ReadFrom(reg_io);
// connection should get address slot 0, and connection1 should get
// slot 1.
EXPECT_EQ(1u, config_next.address_space().get());
EXPECT_EQ(1u, config_next.start_flush_clean().get());
EXPECT_EQ(1u, config_next.start_flush_invalidate().get());
EXPECT_EQ(0u, config_next.job_chain_flag().get());
EXPECT_EQ(1u, config_next.end_flush_clean().get());
EXPECT_EQ(1u, config_next.end_flush_invalidate().get());
EXPECT_EQ(0u, config_next.enable_flush_reduction().get());
EXPECT_EQ(0u, config_next.disable_descriptor_write_back().get());
EXPECT_EQ(8u, config_next.thread_priority().get());
EXPECT_EQ(registers::GpuCommand::kCmdCycleCountStart,
reg_io->Read32(registers::GpuCommand::kOffset));
device->AtomCompleted(&atom, kArmMaliResultSuccess);
EXPECT_EQ(registers::GpuCommand::kCmdCycleCountStop,
reg_io->Read32(registers::GpuCommand::kOffset));
}
void TestIdle()
{
std::unique_ptr<MsdArmDevice> device = MsdArmDevice::Create(GetTestDeviceHandle(), false);
EXPECT_NE(device, nullptr);
MsdArmDevice::DumpState dump_state;
device->Dump(&dump_state, false);
// Ensure that the GPU is idle and not doing anything at this point. A
// failure in this may be caused by a previous test.
EXPECT_EQ(0u, dump_state.gpu_status);
}
void MockInitializeQuirks()
{
auto reg_io = std::make_unique<magma::RegisterIo>(MockMmio::Create(1024 * 1024));
GpuFeatures features;
features.gpu_id.set_reg_value(0x72120000);
MsdArmDevice::InitializeHardwareQuirks(&features, reg_io.get());
EXPECT_EQ(1u << 17, reg_io->Read32(0xf04));
features.gpu_id.set_reg_value(0x08201000); // T820 R1P0
MsdArmDevice::InitializeHardwareQuirks(&features, reg_io.get());
EXPECT_EQ(1u << 16, reg_io->Read32(0xf04));
features.gpu_id.set_reg_value(0x9990000);
MsdArmDevice::InitializeHardwareQuirks(&features, reg_io.get());
EXPECT_EQ(0u, reg_io->Read32(0xf04));
}
void ProtectedMode()
{
// Use device thread so the test can wait for a reset interrupt.
std::unique_ptr<MsdArmDevice> device = MsdArmDevice::Create(GetTestDeviceHandle(), true);
ASSERT_NE(nullptr, device);
if (!device->IsProtectedModeSupported()) {
magma::log(magma::LOG_WARNING, "Protected mode not supported, skipping test");
return;
}
EXPECT_FALSE(device->IsInProtectedMode());
EXPECT_EQ(1u, device->power_manager_->l2_ready_status());
EXPECT_TRUE(device->perf_counters_->Enable());
device->EnterProtectedMode();
EXPECT_EQ(1u, device->power_manager_->l2_ready_status());
EXPECT_TRUE(device->IsInProtectedMode());
EXPECT_TRUE(device->perf_counters_->running());
EXPECT_TRUE(device->ExitProtectedMode());
EXPECT_EQ(1u, device->power_manager_->l2_ready_status());
EXPECT_FALSE(device->IsInProtectedMode());
// Exiting protected mode should disable performance counters.
EXPECT_FALSE(device->perf_counters_->running());
}
void PowerDownL2()
{
// Use device thread so the test can wait for a power down interrupt.
std::unique_ptr<MsdArmDevice> device = MsdArmDevice::Create(GetTestDeviceHandle(), true);
ASSERT_NE(nullptr, device);
// In theory this could work without protected mode, but it's not needed. On the amlogic
// T820 in the VIM2, powering down the L2 seems to cause GPU faults when the shader cores
// are later powered back up again.
if (!device->IsProtectedModeSupported()) {
magma::log(magma::LOG_WARNING, "Protected mode not supported, skipping test");
return;
}
EXPECT_TRUE(device->PowerDownL2());
EXPECT_EQ(0u, device->power_manager_->l2_ready_status());
}
};
TEST(MsdArmDevice, CreateAndDestroy)
{
TestMsdArmDevice test;
test.CreateAndDestroy();
}
TEST(MsdArmDevice, Dump)
{
TestMsdArmDevice test;
test.Dump();
}
TEST(MsdArmDevice, MockDump)
{
TestMsdArmDevice test;
test.MockDump();
}
TEST(MsdArmDevice, ProcessRequest)
{
TestMsdArmDevice test;
test.ProcessRequest();
}
TEST(MsdArmDevice, MockExecuteAtom)
{
TestMsdArmDevice test;
test.MockExecuteAtom();
}
TEST(MsdArmDevice, Idle)
{
TestMsdArmDevice test;
test.TestIdle();
}
TEST(MsdArmDevice, MockInitializeQuirks)
{
TestMsdArmDevice test;
test.MockInitializeQuirks();
}
TEST(MsdArmDevice, ProtectMode)
{
TestMsdArmDevice test;
test.ProtectedMode();
}
TEST(MsdArmDevice, PowerDownL2)
{
TestMsdArmDevice test;
test.PowerDownL2();
}