blob: 3c38ac999eb8c0a3a62eba2ec9201c3c541f53a4 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
#include <fbl/auto_lock.h>
#include <fbl/canary.h>
#include <fbl/mutex.h>
#include <hwreg/bitfields.h>
// Needed for ARCH_MMU_FLAG_*
#include <vm/arch_vm_aspace.h>
typedef uint64_t pt_entry_t;
#define PRIxPTE PRIx64
// Different page table levels in the page table mgmt hirerachy
enum PageTableLevel {
PT_L,
PD_L,
PDP_L,
PML4_L,
};
// Structure for tracking an upcoming TLB invalidation
struct PendingTlbInvalidation {
struct Item {
uint64_t raw;
DEF_SUBFIELD(raw, 2, 0, page_level);
DEF_SUBBIT(raw, 3, is_global);
DEF_SUBBIT(raw, 4, is_terminal);
DEF_SUBFIELD(raw, 63, 12, encoded_addr);
vaddr_t addr() const { return encoded_addr() << PAGE_SIZE_SHIFT; }
};
static_assert(sizeof(Item) == 8, "");
// If true, ignore |vaddr| and perform a full invalidation for this context.
bool full_shootdown = false;
// If true, at least one enqueued entry was for a global page.
bool contains_global = false;
// Number of valid elements in |item|
uint count = 0;
// List of addresses queued for invalidation
Item item[32];
// Add address |v|, translated at depth |level|, to the set of addresses to be invalidated.
// |is_terminal| should be true iff this invalidation is targeting the final step of the translation
// rather than a higher page table entry.
// |is_global_page| should be true iff this page was mapped with the global
// bit set.
void enqueue(vaddr_t v, PageTableLevel level, bool is_global_page, bool is_terminal);
// Clear the list of pending invalidations
void clear();
~PendingTlbInvalidation();
};
class X86PageTableBase {
public:
X86PageTableBase();
virtual ~X86PageTableBase();
// Type for flags used in the hardware page tables, for terminal entries.
// Note that some flags here may have meanings that depend on the level
// at which they occur (e.g. page size and PAT).
using PtFlags = uint64_t;
// Type for flags used in the hardware page tables, for non-terminal
// entries.
using IntermediatePtFlags = uint64_t;
paddr_t phys() const { return phys_; }
void* virt() const { return virt_; }
size_t pages() {
fbl::AutoLock al(&lock_);
return pages_;
}
void* ctx() const { return ctx_; }
zx_status_t MapPages(vaddr_t vaddr, paddr_t* phys, size_t count,
uint flags, size_t* mapped);
zx_status_t MapPagesContiguous(vaddr_t vaddr, paddr_t paddr, const size_t count,
uint flags, size_t* mapped);
zx_status_t UnmapPages(vaddr_t vaddr, const size_t count, size_t* unmapped);
zx_status_t ProtectPages(vaddr_t vaddr, size_t count, uint flags);
zx_status_t QueryVaddr(vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags);
protected:
// Initialize an empty page table, assigning this given context to it.
zx_status_t Init(void* ctx);
// Release the resources associated with this page table. |base| and |size|
// are only used for debug checks that the page tables have no more mappings.
void Destroy(vaddr_t base, size_t size);
// Returns the highest level of the page tables
virtual PageTableLevel top_level() = 0;
// Returns true if the given ARCH_MMU_FLAG_* flag combination is valid.
virtual bool allowed_flags(uint flags) = 0;
// Returns true if the given paddr is valid
virtual bool check_paddr(paddr_t paddr) = 0;
// Returns true if the given vaddr is valid
virtual bool check_vaddr(vaddr_t vaddr) = 0;
// Whether the processor supports the page size of this level
virtual bool supports_page_size(PageTableLevel level) = 0;
// Return the hardware flags to use on intermediate page tables entries
virtual IntermediatePtFlags intermediate_flags() = 0;
// Return the hardware flags to use on terminal page table entries
virtual PtFlags terminal_flags(PageTableLevel level, uint flags) = 0;
// Return the hardware flags to use on smaller pages after a splitting a
// large page with flags |flags|.
virtual PtFlags split_flags(PageTableLevel level, PtFlags flags) = 0;
// Execute the given pending invalidation
virtual void TlbInvalidate(PendingTlbInvalidation* pending) = 0;
// Convert PtFlags to ARCH_MMU_* flags.
virtual uint pt_flags_to_mmu_flags(PtFlags flags, PageTableLevel level) = 0;
// Returns true if a cache flush is necessary for pagetable changes to be
// visible.
virtual bool needs_cache_flushes() = 0;
// Pointer to the translation table.
paddr_t phys_ = 0;
pt_entry_t* virt_ = nullptr;
// Counter of pages allocated to back the translation table.
size_t pages_ TA_GUARDED(lock_) = 0;
// A context structure that may used by a PageTable type above as part of
// invalidation.
void* ctx_ = nullptr;
private:
DISALLOW_COPY_ASSIGN_AND_MOVE(X86PageTableBase);
class CacheLineFlusher;
class ConsistencyManager;
struct MappingCursor;
zx_status_t AddMapping(volatile pt_entry_t* table, uint mmu_flags,
PageTableLevel level, const MappingCursor& start_cursor,
MappingCursor* new_cursor, ConsistencyManager* cm) TA_REQ(lock_);
zx_status_t AddMappingL0(volatile pt_entry_t* table, uint mmu_flags,
const MappingCursor& start_cursor,
MappingCursor* new_cursor, ConsistencyManager* cm) TA_REQ(lock_);
bool RemoveMapping(volatile pt_entry_t* table,
PageTableLevel level, const MappingCursor& start_cursor,
MappingCursor* new_cursor, ConsistencyManager* cm) TA_REQ(lock_);
bool RemoveMappingL0(volatile pt_entry_t* table,
const MappingCursor& start_cursor,
MappingCursor* new_cursor, ConsistencyManager* cm) TA_REQ(lock_);
zx_status_t UpdateMapping(volatile pt_entry_t* table, uint mmu_flags,
PageTableLevel level, const MappingCursor& start_cursor,
MappingCursor* new_cursor, ConsistencyManager* cm) TA_REQ(lock_);
zx_status_t UpdateMappingL0(volatile pt_entry_t* table, uint mmu_flags,
const MappingCursor& start_cursor, MappingCursor* new_cursor,
ConsistencyManager* cm) TA_REQ(lock_);
zx_status_t GetMapping(volatile pt_entry_t* table, vaddr_t vaddr,
PageTableLevel level,
PageTableLevel* ret_level,
volatile pt_entry_t** mapping) TA_REQ(lock_);
zx_status_t GetMappingL0(volatile pt_entry_t* table, vaddr_t vaddr,
enum PageTableLevel* ret_level,
volatile pt_entry_t** mapping) TA_REQ(lock_);
zx_status_t SplitLargePage(PageTableLevel level, vaddr_t vaddr,
volatile pt_entry_t* pte, ConsistencyManager* cm) TA_REQ(lock_);
void UpdateEntry(ConsistencyManager* cm, PageTableLevel level, vaddr_t vaddr,
volatile pt_entry_t* pte, paddr_t paddr, PtFlags flags,
bool was_terminal) TA_REQ(lock_);
void UnmapEntry(ConsistencyManager* cm, PageTableLevel level, vaddr_t vaddr,
volatile pt_entry_t* pte, bool was_terminal) TA_REQ(lock_);
fbl::Canary<fbl::magic("X86P")> canary_;
// low lock to protect the mmu code
fbl::Mutex lock_;
};