blob: 2b69c9037feac1a3fb01e08890fde862984619b9 [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.
#define MAGMA_DLOG_ENABLE 1
#include <fcntl.h>
#include <lib/fdio/directory.h>
#include <lib/zx/channel.h>
#include <poll.h>
#include <thread>
#include <gtest/gtest.h>
#include "helper/magma_map_cpu.h"
#include "helper/test_device_helper.h"
#include "magma.h"
#include "magma_arm_mali_types.h"
#include "magma_util/dlog.h"
#include "magma_util/macros.h"
#include "magma_vendor_queries.h"
namespace {
enum JobDescriptorType { kJobDescriptorTypeNop = 1 };
struct JobDescriptorHeader {
uint64_t reserved1;
uint64_t reserved2;
uint8_t job_descriptor_size : 1;
uint8_t job_type : 7;
uint8_t reserved3;
uint16_t reserved4;
uint16_t reserved5;
uint16_t reserved6;
uint64_t next_job;
};
class TestConnection : public magma::TestDeviceBase {
public:
TestConnection() : magma::TestDeviceBase(MAGMA_VENDOR_ID_MALI) {
magma_create_connection2(device(), &connection_);
DASSERT(connection_);
magma_create_context(connection_, &context_id_);
}
~TestConnection() {
magma_release_context(connection_, context_id_);
if (connection_)
magma_release_connection(connection_);
}
bool SupportsProtectedMode() {
uint64_t value_out;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_query2(device(), kMsdArmVendorQuerySupportsProtectedMode, &value_out));
return !!value_out;
}
enum How { NORMAL, NORMAL_ORDER, NORMAL_DATA, JOB_FAULT, MMU_FAULT };
void SubmitCommandBuffer(How how, uint8_t atom_number, uint8_t atom_dependency,
bool protected_mode) {
ASSERT_NE(connection_, nullptr);
uint64_t size;
magma_buffer_t job_buffer;
ASSERT_EQ(magma_create_buffer(connection_, PAGE_SIZE, &size, &job_buffer), 0);
uint64_t job_va;
InitJobBuffer(job_buffer, how, size, &job_va);
std::vector<uint8_t> vaddr(sizeof(magma_arm_mali_atom));
ASSERT_TRUE(InitBatchBuffer(vaddr.data(), vaddr.size(), job_va, atom_number, atom_dependency,
how, protected_mode));
magma_inline_command_buffer command_buffer;
command_buffer.data = vaddr.data();
command_buffer.size = vaddr.size();
command_buffer.semaphore_ids = nullptr;
command_buffer.semaphore_count = 0;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_execute_immediate_commands2(connection_, context_id_, 1, &command_buffer));
constexpr uint64_t kOneSecondPerNs = 1000000000;
magma_poll_item_t item = {.handle = magma_get_notification_channel_handle(connection_),
.type = MAGMA_POLL_TYPE_HANDLE,
.condition = MAGMA_POLL_CONDITION_READABLE};
EXPECT_EQ(MAGMA_STATUS_OK, magma_poll(&item, 1, kOneSecondPerNs));
magma_arm_mali_status status;
uint64_t status_size;
magma_bool_t more_data;
EXPECT_EQ(MAGMA_STATUS_OK, magma_read_notification_channel2(
connection_, &status, sizeof(status), &status_size, &more_data));
EXPECT_EQ(status_size, sizeof(status));
EXPECT_EQ(atom_number, status.atom_number);
switch (how) {
case NORMAL:
case NORMAL_ORDER:
EXPECT_EQ(kArmMaliResultSuccess, status.result_code);
break;
case JOB_FAULT:
case NORMAL_DATA:
EXPECT_NE(kArmMaliResultReadFault, status.result_code);
EXPECT_NE(kArmMaliResultSuccess, status.result_code);
break;
case MMU_FAULT:
if (protected_mode) {
EXPECT_EQ(kArmMaliResultUnknownFault, status.result_code);
} else {
EXPECT_EQ(kArmMaliResultReadFault, status.result_code);
}
break;
}
magma_release_buffer(connection_, job_buffer);
}
bool InitBatchBuffer(void* vaddr, uint64_t size, uint64_t job_va, uint8_t atom_number,
uint8_t atom_dependency, How how, bool protected_mode) {
memset(vaddr, 0, size);
magma_arm_mali_atom* atom = static_cast<magma_arm_mali_atom*>(vaddr);
atom->size = sizeof(*atom);
if (how == MMU_FAULT) {
atom->job_chain_addr = job_va - PAGE_SIZE;
if (atom->job_chain_addr == 0)
atom->job_chain_addr = PAGE_SIZE * 2;
} else {
atom->job_chain_addr = job_va;
}
atom->atom_number = atom_number;
atom->dependencies[0].atom_number = atom_dependency;
atom->dependencies[0].type =
how == NORMAL_DATA ? kArmMaliDependencyData : kArmMaliDependencyOrder;
if (protected_mode) {
atom->flags |= kAtomFlagProtected;
}
return true;
}
bool InitJobBuffer(magma_buffer_t buffer, How how, uint64_t size, uint64_t* job_va) {
void* vaddr;
if (!magma::MapCpuHelper(connection_, buffer, 0 /*offset*/, size, &vaddr) != 0)
return DRETF(false, "couldn't map job buffer");
*job_va = next_job_address_;
next_job_address_ += 0x5000;
magma_map_buffer_gpu(
connection_, buffer, 0, 1, *job_va,
MAGMA_GPU_MAP_FLAG_READ | MAGMA_GPU_MAP_FLAG_WRITE | kMagmaArmMaliGpuMapFlagInnerShareable);
magma_commit_buffer(connection_, buffer, 0, 1);
JobDescriptorHeader* header = static_cast<JobDescriptorHeader*>(vaddr);
memset(header, 0, sizeof(*header));
header->job_descriptor_size = 1; // Next job address is 64-bit.
if (how == JOB_FAULT) {
header->job_type = 127;
} else {
header->job_type = kJobDescriptorTypeNop;
}
header->next_job = 0;
magma_clean_cache(buffer, 0, PAGE_SIZE, MAGMA_CACHE_OPERATION_CLEAN);
magma::UnmapCpuHelper(vaddr, size);
return true;
}
private:
magma_connection_t connection_;
uint32_t context_id_;
uint64_t next_job_address_ = 0x1000000;
};
} // namespace
TEST(FaultRecovery, Test) {
std::unique_ptr<TestConnection> test;
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::JOB_FAULT, 1, 0, false);
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
}
TEST(FaultRecovery, TestOrderDependency) {
std::unique_ptr<TestConnection> test;
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
test->SubmitCommandBuffer(TestConnection::JOB_FAULT, 2, 1, false);
test->SubmitCommandBuffer(TestConnection::NORMAL_ORDER, 3, 2, false);
}
TEST(FaultRecovery, TestDataDependency) {
std::unique_ptr<TestConnection> test;
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
test->SubmitCommandBuffer(TestConnection::JOB_FAULT, 2, 1, false);
test->SubmitCommandBuffer(TestConnection::NORMAL_DATA, 3, 2, false);
}
TEST(FaultRecovery, TestMmu) {
std::unique_ptr<TestConnection> test;
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::MMU_FAULT, 1, 0, false);
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
}
TEST(FaultRecovery, TestProtected) {
std::unique_ptr<TestConnection> test;
test.reset(new TestConnection());
if (!test->SupportsProtectedMode()) {
printf("Protected mode not supported, skipping\n");
return;
}
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, true);
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::MMU_FAULT, 1, 0, true);
test.reset(new TestConnection());
test->SubmitCommandBuffer(TestConnection::NORMAL, 1, 0, false);
}