blob: cdc37c9da77c4b874a7c885ae3719c3233c152b4 [file] [log] [blame]
// Copyright 2016 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_intel_device.h"
#include "registers.h"
#include "gtest/gtest.h"
class TestEngineCommandStreamer {
public:
static bool ExecBatch(RenderEngineCommandStreamer* engine,
std::unique_ptr<MappedBatch> mapped_batch)
{
return engine->ExecBatch(std::move(mapped_batch));
}
static bool SubmitContext(RenderEngineCommandStreamer* engine, MsdIntelContext* context,
uint32_t tail)
{
return engine->SubmitContext(context, tail);
}
};
// These tests are unit testing the functionality of MsdIntelDevice.
// All of these tests instantiate the device in test mode, that is without the device thread active.
class TestMsdIntelDevice {
public:
void CreateAndDestroy()
{
for (uint32_t i = 0; i < 100; i++) {
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device =
MsdIntelDevice::Create(platform_device->GetDeviceHandle(), false);
EXPECT_NE(device, nullptr);
EXPECT_TRUE(device->WaitIdle());
// check that the render init batch succeeded.
EXPECT_EQ(device->global_context()
->hardware_status_page(RENDER_COMMAND_STREAMER)
->read_sequence_number(),
0x1001u);
// test register access
uint32_t expected = 0xabcd1234;
device->register_io()->Write32(0x4f100, expected);
uint32_t value = device->register_io()->Read32(0x4f100);
EXPECT_EQ(expected, value);
}
}
class FormattedString {
public:
FormattedString(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
printf(": ");
int size = std::vsnprintf(nullptr, 0, fmt, args);
buf_ = std::vector<char>(size + 1);
std::vsnprintf(buf_.data(), buf_.size(), fmt, args);
va_end(args);
}
char* data() { return buf_.data(); }
private:
std::vector<char> buf_;
};
void Dump()
{
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device =
MsdIntelDevice::Create(platform_device->GetDeviceHandle(), false);
EXPECT_NE(device, nullptr);
EXPECT_TRUE(device->WaitIdle());
MsdIntelDevice::DumpState dump_state;
device->Dump(&dump_state);
EXPECT_EQ(dump_state.render_cs.sequence_number,
device->global_context()
->hardware_status_page(RENDER_COMMAND_STREAMER)
->read_sequence_number());
EXPECT_EQ(dump_state.render_cs.active_head_pointer,
device->render_engine_cs()->GetActiveHeadPointer());
EXPECT_FALSE(dump_state.fault_present);
EXPECT_TRUE(dump_state.render_cs.inflight_batches.empty());
dump_state.fault_present = true;
dump_state.fault_engine = 0;
dump_state.fault_src = 3;
dump_state.fault_type = 10;
dump_state.fault_gpu_address = 0xaabbccdd11223344;
dump_state.global = 1;
std::string dump_string;
device->FormatDump(dump_state, dump_string);
EXPECT_NE(nullptr,
strstr(dump_string.c_str(), FormattedString("sequence_number 0x%x",
dump_state.render_cs.sequence_number)
.data()));
EXPECT_NE(nullptr, strstr(dump_string.c_str(),
FormattedString("active head pointer: 0x%llx",
dump_state.render_cs.active_head_pointer)
.data()));
EXPECT_NE(nullptr,
strstr(dump_string.c_str(),
FormattedString(
"engine 0x%x src 0x%x type 0x%x gpu_address 0x%lx global %d",
dump_state.fault_engine, dump_state.fault_src, dump_state.fault_type,
dump_state.fault_gpu_address, dump_state.global)
.data()));
}
void MockDump()
{
auto reg_io = std::make_unique<magma::RegisterIo>(MockMmio::Create(2 * 1024 * 1024));
reg_io->Write32(registers::FaultTlbReadData::kOffset0, 0xabcd1234);
reg_io->Write32(registers::FaultTlbReadData::kOffset1, 0x1f);
MsdIntelDevice::DumpState dump_state;
MsdIntelDevice::DumpFaultAddress(&dump_state, reg_io.get());
EXPECT_EQ(0xfabcd1234000ull, dump_state.fault_gpu_address);
EXPECT_TRUE(dump_state.global);
reg_io->Write32(registers::FaultTlbReadData::kOffset1, 0xf);
MsdIntelDevice::DumpFaultAddress(&dump_state, reg_io.get());
EXPECT_EQ(0xfabcd1234000ull, dump_state.fault_gpu_address);
EXPECT_FALSE(dump_state.global);
uint32_t engine = 0;
uint32_t src = 0xff;
uint32_t type = 0x3;
uint32_t valid = 0x1;
MsdIntelDevice::DumpFault(&dump_state, (engine << 12) | (src << 3) | (type << 1) | valid);
EXPECT_EQ(dump_state.fault_present, valid);
EXPECT_EQ(dump_state.fault_engine, engine);
EXPECT_EQ(dump_state.fault_src, src);
EXPECT_EQ(dump_state.fault_type, type);
EXPECT_TRUE(dump_state.render_cs.inflight_batches.empty());
}
void BatchBuffer(bool should_wrap_ringbuffer)
{
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device(
MsdIntelDevice::Create(platform_device->GetDeviceHandle(), false));
ASSERT_NE(device, nullptr);
EXPECT_TRUE(device->WaitIdle());
bool ringbuffer_wrapped = false;
// num_iterations updated after one iteration in case we're wrapping the ringbuffer
uint32_t num_iterations = 1;
for (uint32_t iteration = 0; iteration < num_iterations; iteration++) {
auto dst_mapping =
AddressSpace::MapBufferGpu(device->gtt(), MsdIntelBuffer::Create(PAGE_SIZE, "dst"));
ASSERT_NE(dst_mapping, nullptr);
void* dst_cpu_addr;
EXPECT_TRUE(dst_mapping->buffer()->platform_buffer()->MapCpu(&dst_cpu_addr));
auto batch_buffer =
std::shared_ptr<MsdIntelBuffer>(MsdIntelBuffer::Create(PAGE_SIZE, "batchbuffer"));
ASSERT_NE(batch_buffer, nullptr);
void* batch_cpu_addr;
ASSERT_TRUE(batch_buffer->platform_buffer()->MapCpu(&batch_cpu_addr));
uint32_t* batch_ptr = reinterpret_cast<uint32_t*>(batch_cpu_addr);
auto batch_mapping = AddressSpace::MapBufferGpu(device->gtt(), batch_buffer);
ASSERT_NE(batch_mapping, nullptr);
uint32_t expected_val = 0x8000000 + iteration;
uint64_t offset =
(iteration * sizeof(uint32_t)) % dst_mapping->buffer()->platform_buffer()->size();
static constexpr uint32_t kScratchRegOffset = 0x02600;
uint32_t i = 0;
batch_ptr[i++] = (0x22 << 23) | (3 - 2); // store to mmio
batch_ptr[i++] = kScratchRegOffset;
batch_ptr[i++] = expected_val;
static constexpr uint32_t kDwordCount = 4;
static constexpr uint32_t kAddressSpaceGtt = 1 << 22;
batch_ptr[i++] = (0x20 << 23) | (kDwordCount - 2) | kAddressSpaceGtt; // store dword
batch_ptr[i++] = magma::lower_32_bits(dst_mapping->gpu_addr() + offset);
batch_ptr[i++] = magma::upper_32_bits(dst_mapping->gpu_addr() + offset);
batch_ptr[i++] = expected_val;
batch_ptr[i++] = (0xA << 23); // batch end
auto ringbuffer =
device->global_context()->get_ringbuffer(device->render_engine_cs()->id());
uint32_t tail_start = ringbuffer->tail();
// Initialize the target
reinterpret_cast<uint32_t*>(dst_cpu_addr)[offset / sizeof(uint32_t)] = 0xdeadbeef;
device->register_io()->Write32(kScratchRegOffset, 0xdeadbeef);
EXPECT_TRUE(TestEngineCommandStreamer::ExecBatch(
device->render_engine_cs(),
std::unique_ptr<SimpleMappedBatch>(
new SimpleMappedBatch(device->global_context(), std::move(batch_mapping)))));
EXPECT_TRUE(device->WaitIdle());
EXPECT_EQ(ringbuffer->head(), ringbuffer->tail());
EXPECT_EQ(expected_val, device->register_io()->Read32(kScratchRegOffset));
uint32_t target_val =
reinterpret_cast<uint32_t*>(dst_cpu_addr)[offset / sizeof(uint32_t)];
EXPECT_EQ(target_val, expected_val);
if (ringbuffer->tail() < tail_start) {
DLOG("ringbuffer wrapped tail_start 0x%x tail 0x%x", tail_start,
ringbuffer->tail());
ringbuffer_wrapped = true;
}
if (should_wrap_ringbuffer && num_iterations == 1)
num_iterations =
(ringbuffer->size() - tail_start) / (ringbuffer->tail() - tail_start) + 10;
}
if (should_wrap_ringbuffer)
EXPECT_TRUE(ringbuffer_wrapped);
DLOG("Finished, num_iterations %u", num_iterations);
}
void RegisterWrite()
{
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device(new MsdIntelDevice());
ASSERT_NE(device, nullptr);
ASSERT_TRUE(device->BaseInit(platform_device->GetDeviceHandle()));
auto ringbuffer =
device->global_context()->get_ringbuffer(device->render_engine_cs()->id());
static constexpr uint32_t kScratchRegOffset = 0x02600;
device->register_io()->Write32(kScratchRegOffset, 0xdeadbeef);
static constexpr uint32_t expected_val = 0x8000000;
ringbuffer->write_tail((0x22 << 23) | (3 - 2)); // store to mmio
ringbuffer->write_tail(kScratchRegOffset);
ringbuffer->write_tail(expected_val);
ringbuffer->write_tail(0);
TestEngineCommandStreamer::SubmitContext(
device->render_engine_cs(), device->global_context().get(), ringbuffer->tail());
auto start = std::chrono::high_resolution_clock::now();
while (device->render_engine_cs()->GetActiveHeadPointer() != ringbuffer->tail() &&
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
.count() < 100) {
std::this_thread::yield();
}
EXPECT_EQ(ringbuffer->tail(), device->render_engine_cs()->GetActiveHeadPointer());
EXPECT_EQ(expected_val, device->register_io()->Read32(kScratchRegOffset));
}
void ProcessRequest()
{
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device(
MsdIntelDevice::Create(platform_device->GetDeviceHandle(), false));
ASSERT_NE(device, nullptr);
class TestRequest : public MsdIntelDevice::DeviceRequest {
public:
TestRequest(std::shared_ptr<bool> processing_complete)
: processing_complete_(processing_complete)
{
}
protected:
magma::Status Process(MsdIntelDevice* 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 MaxFreq()
{
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device =
MsdIntelDevice::Create(platform_device->GetDeviceHandle(), false);
EXPECT_NE(device, nullptr);
constexpr uint32_t max_freq = 1050;
uint32_t current_freq = device->GetCurrentFrequency();
DLOG("current_freq %u max_freq %u", current_freq, max_freq);
EXPECT_LE(current_freq, max_freq);
device->RequestMaxFreq();
uint32_t freq = device->GetCurrentFrequency();
EXPECT_LE(freq, max_freq);
EXPECT_GE(freq, current_freq);
}
void QuerySliceInfo()
{
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device =
MsdIntelDevice::Create(platform_device->GetDeviceHandle(), false);
EXPECT_NE(device, nullptr);
uint32_t subslice_total = 0, eu_total = 0;
device->QuerySliceInfo(&subslice_total, &eu_total);
EXPECT_EQ(3u, subslice_total);
// Expected values for eu_total are either 24 (for the Acer Switch
// Alpha 12) or 23 (for the Intel NUC).
if (eu_total != 24u)
EXPECT_EQ(23u, eu_total);
}
};
TEST(MsdIntelDevice, CreateAndDestroy)
{
TestMsdIntelDevice test;
test.CreateAndDestroy();
}
TEST(MsdIntelDevice, Dump)
{
TestMsdIntelDevice test;
test.Dump();
}
TEST(MsdIntelDevice, MockDump)
{
TestMsdIntelDevice test;
test.MockDump();
}
TEST(MsdIntelDevice, RegisterWrite)
{
TestMsdIntelDevice test;
test.RegisterWrite();
}
TEST(MsdIntelDevice, BatchBuffer)
{
TestMsdIntelDevice test;
test.BatchBuffer(false);
}
TEST(MsdIntelDevice, WrapRingbuffer)
{
TestMsdIntelDevice test;
test.BatchBuffer(true);
}
TEST(MsdIntelDevice, ProcessRequest)
{
TestMsdIntelDevice test;
test.ProcessRequest();
}
TEST(MsdIntelDevice, MaxFreq)
{
TestMsdIntelDevice test;
test.MaxFreq();
}
TEST(MsdIntelDevice, QuerySliceInfo)
{
TestMsdIntelDevice test;
test.QuerySliceInfo();
}