blob: 8627a2d4ce37baba989cd6e7367ef858f724d761 [file] [log] [blame]
// Copyright 2018 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_VSI_VIP_SRC_ADDRESS_SPACE_H_
#define SRC_GRAPHICS_DRIVERS_MSD_VSI_VIP_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 <lib/magma_service/util/address_space.h>
#include <limits.h>
#include <vector>
#include "gpu_mapping.h"
#include "macros.h"
#ifndef PAGE_SHIFT
#define PAGE_SHIFT 12
#endif
static_assert(PAGE_SHIFT == 12, "Need 4k page");
class AddressSpace : public magma::AddressSpace<GpuMapping> {
public:
class Owner : public magma::AddressSpaceOwner {
public:
virtual void AddressSpaceReleased(AddressSpace* address_space) = 0;
};
static std::unique_ptr<AddressSpace> Create(Owner* owner, uint32_t page_table_array_slot);
AddressSpace(Owner* owner, uint32_t page_table_array_slot)
: magma::AddressSpace<GpuMapping>(owner),
owner_(owner),
page_table_array_slot_(page_table_array_slot) {}
virtual ~AddressSpace() { owner_->AddressSpaceReleased(this); }
// Though this address space does not support allocations, this needs to be implemented
// to avoid errors from when a gpu mapping is released and attempts to call |FreeLocked|.
bool FreeLocked(uint64_t addr) override { return true; }
bool InsertLocked(uint64_t addr, magma::PlatformBusMapper::BusMapping* bus_mapping,
uint32_t guard_page_count) override;
bool ClearLocked(uint64_t addr, magma::PlatformBusMapper::BusMapping* bus_mapping) override;
uint64_t Size() const override { return 1ull << 40; }
uint64_t bus_addr() { return root_->bus_addr(); }
uint32_t page_table_array_slot() { return page_table_array_slot_; }
void SetRingbufferGpuMapping(std::shared_ptr<GpuMapping> gpu_mapping) {
DASSERT(!ringbuffer_gpu_mapping_);
ringbuffer_gpu_mapping_ = gpu_mapping;
}
bool GetRingbufferGpuAddress(uint64_t* gpu_addr_out) {
if (ringbuffer_gpu_mapping_) {
*gpu_addr_out = ringbuffer_gpu_mapping_->gpu_addr();
return true;
}
return false;
}
private:
// Maximum bus address is 40 bits.
// Only a 32bit pte is needed because bits [11..0] of a page aligned address
// are always zero. Address bits [39..32] are stored in pte bits [11..4].
using pte_t = uint32_t;
using pde_t = pte_t;
static const AddressSpace::pte_t kInvalidPte;
static const AddressSpace::pte_t kInvalidPde;
static inline pte_t pte_encode_unsafe(uint64_t bus_addr, bool valid, bool writeable,
bool exception) {
// Must be a 4k page address.
DASSERT(magma::is_page_aligned(bus_addr));
DASSERT(fits_in_40_bits(bus_addr));
pte_t pte =
magma::to_uint32(bus_addr & 0xFFFFFFFF) | ((magma::to_uint32(bus_addr >> 32) & 0xFF) << 4);
if (valid)
pte |= 1;
if (exception)
pte |= (1 << 1);
if (writeable)
pte |= (1 << 2);
return pte;
}
static inline bool pte_encode(uint64_t bus_addr, bool valid, bool writeable, bool exception,
pte_t* pte_out) {
if (!fits_in_40_bits(bus_addr)) {
MAGMA_LOG(ERROR, "bus address doesn't fit in 40 bits: 0x%lx", bus_addr);
return false;
}
*pte_out = pte_encode_unsafe(bus_addr, valid, writeable, exception);
return true;
}
static inline bool pde_encode(uint64_t bus_addr, bool valid, pde_t* pde_out) {
return pte_encode(bus_addr, valid, false, true, pde_out);
}
static constexpr uint32_t kVirtualAddressBits = 32;
static constexpr uint64_t kPageDirectoryShift = 10;
static constexpr uint64_t kPageDirectoryEntries = 1 << kPageDirectoryShift;
static constexpr uint64_t kPageDirectoryMask = kPageDirectoryEntries - 1;
static constexpr uint64_t kPageTableShift = 10;
static constexpr uint64_t kPageTableEntries = 1 << kPageTableShift;
static constexpr uint64_t kPageTableMask = kPageTableEntries - 1;
class Page {
public:
Page(Owner* owner) : owner_(owner) {}
bool Init(bool cached);
void* mapping() { return mapping_; }
uint64_t bus_addr() { return bus_mapping_->Get()[0]; }
Owner* owner() { return owner_; }
void Flush() { buffer_->CleanCache(0, buffer_->size(), false); }
private:
Owner* owner_;
std::unique_ptr<magma::PlatformBuffer> buffer_;
void* mapping_ = nullptr;
std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping_;
};
class PageTable : public Page {
public:
pte_t* entry(uint32_t page_index) {
DASSERT(page_index < kPageTableEntries);
struct PageTableGpu {
pte_t entry[kPageTableEntries];
};
return &reinterpret_cast<PageTableGpu*>(mapping())->entry[page_index];
}
static std::unique_ptr<PageTable> Create(Owner* owner);
PageTable(Owner* owner) : Page(owner) {}
};
class PageDirectory : public Page {
public:
pde_t* entry(uint32_t index) {
DASSERT(index < kPageDirectoryEntries);
struct PageDirectoryTableGpu {
pde_t entry[kPageDirectoryEntries];
};
return &reinterpret_cast<PageDirectoryTableGpu*>(mapping())->entry[index];
}
uint32_t valid_count(uint32_t index) { return valid_counts_[index]; }
// If |alloc| a page table will be created if one doesn't exist.
PageTable* GetPageTable(uint32_t index, bool alloc);
// Gets a page table, allocating one if necessary. Returns the valid count.
pte_t* GetPageTableEntry(uint32_t page_directory_index, uint32_t page_table_index,
uint32_t* valid_count_out);
// Should be called after a page table has been modified; if |valid_count| is
// zero, the page table will be removed.
void PageTableUpdated(uint32_t page_directory_index, uint32_t valid_count);
static std::unique_ptr<PageDirectory> Create(Owner* owner);
PageDirectory(Owner* owner)
: Page(owner), page_tables_(kPageDirectoryEntries), valid_counts_(kPageDirectoryEntries) {}
private:
std::vector<std::unique_ptr<PageTable>> page_tables_;
std::vector<uint32_t> valid_counts_;
};
bool Init();
Owner* owner_;
std::unique_ptr<PageDirectory> root_;
uint32_t page_table_array_slot_;
std::shared_ptr<GpuMapping> ringbuffer_gpu_mapping_;
friend class TestAddressSpace;
friend class TestAddressSpace_GarbageCollect_Test;
AddressSpace(const AddressSpace&) = delete;
void operator=(const AddressSpace&) = delete;
};
#endif