blob: c26fa50618f5887d44ffe738588f632d9156d32e [file]
// 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.
#ifndef SRC_GRAPHICS_DRIVERS_MSD_ARM_MALI_SRC_ADDRESS_SPACE_H_
#define SRC_GRAPHICS_DRIVERS_MSD_ARM_MALI_SRC_ADDRESS_SPACE_H_
#include <lib/magma/platform/platform_buffer.h>
#include <lib/magma/platform/platform_bus_mapper.h>
#include <lib/magma/util/short_macros.h>
#include <limits.h>
#include <vector>
#include "drivers/msd-arm-mali/src/types.h"
// This is the standard page size for Mali when using LPAE page tables. The CPU page size must be a
// multiple of this size.
constexpr uint32_t kMaliPageShift = 12;
constexpr uint64_t kMaliPageSize = 1 << kMaliPageShift;
class AddressSpace;
class MsdArmConnection;
class AddressSlotMapping {
public:
AddressSlotMapping(uint32_t slot_number, std::shared_ptr<MsdArmConnection> connection)
: slot_number_(slot_number), connection_(connection) {}
uint32_t slot_number() const { return slot_number_; }
std::shared_ptr<MsdArmConnection> connection() const { return connection_; }
private:
uint32_t slot_number_;
std::shared_ptr<MsdArmConnection> connection_;
};
class AddressSpaceObserver {
public:
virtual void FlushAddressMappingRange(AddressSpace* address_space, uint64_t start,
uint64_t length, bool synchronous) = 0;
// Tells the GPU to retry any memory lookup using this address space. Also
// happens implicitly upon flush.
virtual void UnlockAddressSpace(AddressSpace* address_space) = 0;
virtual void ReleaseSpaceMappings(const AddressSpace* address_space) = 0;
};
// This should only be accessed on the connection thread (for now).
class AddressSpace {
public:
class Owner {
public:
virtual ~Owner() = 0;
virtual AddressSpaceObserver* GetAddressSpaceObserver() = 0;
virtual std::shared_ptr<Owner> GetSharedPtr() = 0;
virtual magma::PlatformBusMapper* GetBusMapper() = 0;
};
static constexpr uint32_t kVirtualAddressSize = 48;
static constexpr uint32_t kNormalMemoryAttributeSlot = 0;
static constexpr uint32_t kOuterCacheableAttributeSlot = 1;
// If cache_coherent is true, then updates to the page tables themselves
// should be cache coherent with the GPU.
static std::unique_ptr<AddressSpace> Create(Owner* owner, bool cache_coherent);
~AddressSpace();
static bool is_mali_page_aligned(uint64_t address) { return address % kMaliPageSize == 0; }
// offset_bytes and length_bytes must be multiples of both the CPU page size and the GPU page
// size.
bool Insert(gpu_addr_t addr, magma::PlatformBusMapper::BusMapping* bus_mapping,
uint64_t offset_bytes, uint64_t length_bytes, uint64_t flags);
bool Clear(gpu_addr_t start, uint64_t length);
void Unlock() { owner_->GetAddressSpaceObserver()->UnlockAddressSpace(this); }
void ReleaseSpaceMappings();
bool ReadPteForTesting(gpu_addr_t addr, mali_pte_t* entry);
uint64_t translation_table_entry() const;
std::shared_ptr<Owner> owner() const { return owner_->GetSharedPtr(); }
private:
static constexpr uint32_t kPageTableEntries = kMaliPageSize / sizeof(mali_pte_t);
static constexpr uint32_t kPageTableMask = kPageTableEntries - 1;
static constexpr uint32_t kPageOffsetBits = 9;
static_assert(kPageTableEntries == 1 << kPageOffsetBits, "incorrect page table entry count");
// There are 3 levels of page directories, then an address table.
static constexpr uint32_t kPageDirectoryLevels = 4;
static_assert(kPageOffsetBits * kPageDirectoryLevels + kMaliPageShift == kVirtualAddressSize,
"Incorrect virtual address size");
struct PageTableGpu {
mali_pte_t entry[kPageTableEntries];
};
class PageTable {
public:
static std::unique_ptr<PageTable> Create(Owner* owner, uint32_t level, bool cache_coherent);
PageTableGpu* gpu() { return gpu_; }
// Get the leaf page table for |page_number|. If |create| is false then
// returns null instead of creating one.
PageTable* GetPageTableLevel0(uint64_t page_number, bool create);
void WritePte(uint64_t page_index, mali_pte_t pte);
uint64_t page_bus_address() const { return bus_mapping_->Get()[0]; }
// Collect empty page tables that are in the path to page_number, and
// put them in |empty_tables|. |is_empty| is set if the page table is
// now empty.
void GarbageCollectChildren(uint64_t page_number, bool* is_empty,
std::vector<std::unique_ptr<PageTable>>* empty_tables);
private:
static mali_pte_t get_directory_entry(uint64_t physical_address);
PageTable(Owner* owner, uint32_t level, bool cache_coherent,
std::unique_ptr<magma::PlatformBuffer> buffer, PageTableGpu* gpu,
std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping);
// The root page table has level 3, and the leaves have level 0.
Owner* owner_;
const uint32_t level_;
const bool cache_coherent_;
std::unique_ptr<magma::PlatformBuffer> buffer_;
PageTableGpu* gpu_;
std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping_;
std::vector<std::unique_ptr<PageTable>> next_levels_;
friend class TestAddressSpace;
};
AddressSpace(Owner* owner, bool cache_coherent, std::unique_ptr<PageTable> root_page_directory);
Owner* owner_;
bool cache_coherent_;
std::unique_ptr<PageTable> root_page_directory_;
friend class TestAddressSpace;
AddressSpace(const AddressSpace&) = delete;
void operator=(const AddressSpace&) = delete;
};
#endif