blob: d0daa559cc0916fe81ce14aff8aa7b56ab6692e8 [file] [log] [blame]
/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include "hf/addr.h"
/*
* Our fake architecture has page tables rather similar to aarch64, but not
* quite.
* - The highest level table is always 2, lowest level is 0.
* - Blocks are allowed at all levels.
* There are four types of entries:
* - Absent: 0
* - Page, at level 0: <page-aligned address> | <attrs> | 0x3
* - Block, at level 2 or 1: <block-aligned address> | <attrs> | 0x1
* - Subtable, at level 2 or 1: <subtable address> | 0x3
* <attrs> are always 0 for now.
*/
/** A page table entry. */
typedef uint64_t pte_t;
#define PAGE_LEVEL_BITS 9
/**
* Returns the encoding of a page table entry that isn't present.
*/
static inline pte_t arch_mm_absent_pte(int level)
{
return 0;
}
/**
* Converts a physical address to a table PTE.
*
* The spec says that 'Table descriptors for stage 2 translations do not
* include any attribute field', so we don't take any attributes as arguments.
*/
static inline pte_t arch_mm_table_pte(int level, paddr_t pa)
{
/* This is the same for all levels on aarch64. */
(void)level;
return pa_addr(pa) | 0x3;
}
/**
* Converts a physical address to a block PTE.
*
* The level must allow block entries.
*/
static inline pte_t arch_mm_block_pte(int level, paddr_t pa, uint64_t attrs)
{
pte_t pte = pa_addr(pa) | attrs | 0x1;
if (level == 0) {
pte |= 0x2;
}
return pte;
}
/**
* Specifies whether block mappings are acceptable at the given level.
*
* Level 0 must allow block entries.
*/
static inline bool arch_mm_is_block_allowed(int level)
{
return level <= 2;
}
/**
* Determines if the given pte is present, i.e., if it points to another table,
* to a page, or a block of pages.
*/
static inline bool arch_mm_pte_is_present(pte_t pte, int level)
{
return (pte & 0x1) != 0;
}
/**
* Determines if the given pte references another table.
*/
static inline bool arch_mm_pte_is_table(pte_t pte, int level)
{
return level != 0 && (pte & 0x3) == 0x3;
}
/**
* Determines if the given pte references a block of pages.
*/
static inline bool arch_mm_pte_is_block(pte_t pte, int level)
{
return arch_mm_is_block_allowed(level) &&
(pte & 0x3) == (level == 0 ? 0x3 : 0x1);
}
static inline uint64_t hf_arch_fake_mm_clear_pte_attrs(pte_t pte)
{
return pte & ~0x3;
}
/**
* Clears the given physical address, i.e., sets the ignored bits (from a page
* table perspective) to zero.
*/
static inline paddr_t arch_mm_clear_pa(paddr_t pa)
{
/* This is assumed to round down to the page boundary. */
return pa_init(hf_arch_fake_mm_clear_pte_attrs(pa_addr(pa)) &
~((1 << PAGE_BITS) - 1));
}
/**
* Extracts the physical address of the block referred to by the given page
* table entry.
*/
static inline paddr_t arch_mm_block_from_pte(pte_t pte)
{
return pa_init(hf_arch_fake_mm_clear_pte_attrs(pte));
}
/**
* Extracts the physical address of the page table referred to by the given page
* table entry.
*/
static inline paddr_t arch_mm_table_from_pte(pte_t pte)
{
return pa_init(hf_arch_fake_mm_clear_pte_attrs(pte));
}
/**
* Extracts the architecture specific attributes applies to the given page table
* entry.
*/
static inline uint64_t arch_mm_pte_attrs(pte_t pte)
{
return 0;
}
/**
* Given the attrs from a table at some level and the attrs from all the blocks
* in that table, return equivalent attrs to use for a block which will replace
* the entire table.
*/
static inline uint64_t arch_mm_combine_table_entry_attrs(uint64_t table_attrs,
uint64_t block_attrs)
{
return table_attrs | block_attrs;
}
/**
* Invalidates stage-1 TLB entries referring to the given virtual address range.
*/
static inline void arch_mm_invalidate_stage1_range(vaddr_t va_begin,
vaddr_t va_end)
{
}
/**
* Invalidates stage-2 TLB entries referring to the given intermediate physical
* address range.
*/
static inline void arch_mm_invalidate_stage2_range(ipaddr_t va_begin,
ipaddr_t va_end)
{
}
/**
* Determines the maximum level supported by the given mode.
*/
static inline uint8_t arch_mm_max_level(int mode)
{
(void)mode;
return 2;
}
static inline uint64_t arch_mm_mode_to_attrs(int mode)
{
(void)mode;
return 0;
}
static inline bool arch_mm_init(paddr_t table, bool first)
{
(void)table;
(void)first;
return true;
}