blob: 6872f64efeba6041e6cb23a0508127947ad3662e [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scullfbc938a2018-08-20 14:09:28 +010017#pragma once
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010018
19#include <stdbool.h>
20#include <stddef.h>
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010021
Andrew Scull18c78fc2018-08-20 12:57:41 +010022#include "hf/addr.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010023
Andrew Walbranc3910f72018-11-27 14:24:36 +000024/** A page table entry. */
Andrew Scull80871322018-08-06 12:04:09 +010025typedef uint64_t pte_t;
26
27#define PAGE_LEVEL_BITS 9
28
Andrew Walbran25133742018-09-28 16:28:02 +010029/*
30 * This mask actually includes everything other than the address bits: not just
31 * the attributes but also some ignored bits, reserved bits, and the entry type
32 * bits which distinguish between absent, table, block or page entries.
33 */
Andrew Walbran65add582018-09-25 16:12:15 +010034#define ARCH_AARCH64_MM_PTE_ATTR_MASK \
Andrew Scull78d6fd92018-09-06 15:08:36 +010035 (((UINT64_C(1) << PAGE_BITS) - 1) | ~((UINT64_C(1) << 48) - 1))
36
Andrew Scull78d6fd92018-09-06 15:08:36 +010037/**
38 * Returns the encoding of a page table entry that isn't present.
39 */
40static inline pte_t arch_mm_absent_pte(int level)
41{
42 return 0;
43}
44
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010045/**
46 * Converts a physical address to a table PTE.
47 *
48 * The spec says that 'Table descriptors for stage 2 translations do not
49 * include any attribute field', so we don't take any attributes as arguments.
50 */
Andrew Scull78d6fd92018-09-06 15:08:36 +010051static inline pte_t arch_mm_table_pte(int level, paddr_t pa)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010052{
Andrew Scull78d6fd92018-09-06 15:08:36 +010053 /* This is the same for all levels on aarch64. */
54 (void)level;
Andrew Scull265ada92018-07-30 15:19:01 +010055 return pa_addr(pa) | 0x3;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010056}
57
58/**
59 * Converts a physical address to a block PTE.
Andrew Scull78d6fd92018-09-06 15:08:36 +010060 *
61 * The level must allow block entries.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010062 */
Andrew Scull78d6fd92018-09-06 15:08:36 +010063static inline pte_t arch_mm_block_pte(int level, paddr_t pa, uint64_t attrs)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010064{
Andrew Walbran25133742018-09-28 16:28:02 +010065 pte_t pte = pa_addr(pa) | attrs | 0x1;
Andrew Scull78d6fd92018-09-06 15:08:36 +010066 if (level == 0) {
Andrew Walbran72aaeca2018-09-26 14:01:09 +010067 /* A level 0 'block' is actually a page entry. */
Andrew Scull78d6fd92018-09-06 15:08:36 +010068 pte |= 0x2;
69 }
70 return pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010071}
72
73/**
74 * Specifies whether block mappings are acceptable at the given level.
Andrew Scull78d6fd92018-09-06 15:08:36 +010075 *
76 * Level 0 must allow block entries.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010077 */
78static inline bool arch_mm_is_block_allowed(int level)
79{
Andrew Scull78d6fd92018-09-06 15:08:36 +010080 return level <= 2;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010081}
82
83/**
84 * Determines if the given pte is present, i.e., if it points to another table,
85 * to a page, or a block of pages.
86 */
Andrew Scull78d6fd92018-09-06 15:08:36 +010087static inline bool arch_mm_pte_is_present(pte_t pte, int level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010088{
Andrew Scull78d6fd92018-09-06 15:08:36 +010089 return (pte & 0x1) != 0;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010090}
91
92/**
93 * Determines if the given pte references another table.
94 */
Andrew Scull78d6fd92018-09-06 15:08:36 +010095static inline bool arch_mm_pte_is_table(pte_t pte, int level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010096{
Andrew Scull78d6fd92018-09-06 15:08:36 +010097 return level != 0 && (pte & 0x3) == 0x3;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010098}
99
100/**
101 * Determines if the given pte references a block of pages.
102 */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100103static inline bool arch_mm_pte_is_block(pte_t pte, int level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100104{
Andrew Walbran72aaeca2018-09-26 14:01:09 +0100105 /* We count pages at level 0 as blocks. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100106 return arch_mm_is_block_allowed(level) &&
107 (pte & 0x3) == (level == 0 ? 0x3 : 0x1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100108}
109
Andrew Walbran65add582018-09-25 16:12:15 +0100110static inline uint64_t arch_aarch64_mm_clear_pte_attrs(pte_t pte)
111{
112 return pte & ~ARCH_AARCH64_MM_PTE_ATTR_MASK;
113}
114
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100115/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100116 * Clears the given physical address, i.e., sets the ignored bits (from a page
117 * table perspective) to zero.
118 */
Andrew Scull265ada92018-07-30 15:19:01 +0100119static inline paddr_t arch_mm_clear_pa(paddr_t pa)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100120{
Andrew Walbran65add582018-09-25 16:12:15 +0100121 return pa_init(arch_aarch64_mm_clear_pte_attrs(pa_addr(pa)));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100122}
123
124/**
Andrew Scull78d6fd92018-09-06 15:08:36 +0100125 * Extracts the physical address of the block referred to by the given page
126 * table entry.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100127 */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100128static inline paddr_t arch_mm_block_from_pte(pte_t pte)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100129{
Andrew Walbran65add582018-09-25 16:12:15 +0100130 return pa_init(arch_aarch64_mm_clear_pte_attrs(pte));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100131}
132
133/**
Andrew Scull80871322018-08-06 12:04:09 +0100134 * Extracts the physical address of the page table referred to by the given page
135 * table entry.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100136 */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100137static inline paddr_t arch_mm_table_from_pte(pte_t pte)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100138{
Andrew Walbran65add582018-09-25 16:12:15 +0100139 return pa_init(arch_aarch64_mm_clear_pte_attrs(pte));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100140}
141
Andrew Scull78d6fd92018-09-06 15:08:36 +0100142/**
143 * Extracts the architecture specific attributes applies to the given page table
144 * entry.
145 */
146static inline uint64_t arch_mm_pte_attrs(pte_t pte)
147{
Andrew Walbran65add582018-09-25 16:12:15 +0100148 return pte & ARCH_AARCH64_MM_PTE_ATTR_MASK;
Andrew Scull78d6fd92018-09-06 15:08:36 +0100149}
Andrew Scull265ada92018-07-30 15:19:01 +0100150
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100151/**
152 * Invalidates stage-1 TLB entries referring to the given virtual address range.
153 */
Andrew Scull265ada92018-07-30 15:19:01 +0100154static inline void arch_mm_invalidate_stage1_range(vaddr_t va_begin,
155 vaddr_t va_end)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100156{
Andrew Scull265ada92018-07-30 15:19:01 +0100157 uintvaddr_t begin = va_addr(va_begin);
158 uintvaddr_t end = va_addr(va_end);
159 uintvaddr_t it;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100160
161 begin >>= 12;
162 end >>= 12;
163
164 __asm__ volatile("dsb ishst");
165
Andrew Scull78d6fd92018-09-06 15:08:36 +0100166 for (it = begin; it < end; it += (UINT64_C(1) << (PAGE_BITS - 12))) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100167 __asm__("tlbi vae2is, %0" : : "r"(it));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100168 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100169
170 __asm__ volatile("dsb ish");
171}
172
173/**
Andrew Scull80871322018-08-06 12:04:09 +0100174 * Invalidates stage-2 TLB entries referring to the given intermediate physical
175 * address range.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100176 */
Andrew Scull80871322018-08-06 12:04:09 +0100177static inline void arch_mm_invalidate_stage2_range(ipaddr_t va_begin,
178 ipaddr_t va_end)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100179{
Andrew Scull80871322018-08-06 12:04:09 +0100180 uintpaddr_t begin = ipa_addr(va_begin);
181 uintpaddr_t end = ipa_addr(va_end);
182 uintpaddr_t it;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100183
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100184 /* TODO: This only applies to the current VMID. */
185
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100186 begin >>= 12;
187 end >>= 12;
188
189 __asm__ volatile("dsb ishst");
190
Andrew Scull78d6fd92018-09-06 15:08:36 +0100191 for (it = begin; it < end; it += (UINT64_C(1) << (PAGE_BITS - 12))) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100192 __asm__("tlbi ipas2e1, %0" : : "r"(it));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100193 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100194
Andrew Scull4f170f52018-07-19 12:58:20 +0100195 __asm__ volatile(
196 "dsb ish\n"
197 "tlbi vmalle1is\n"
198 "dsb ish\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100199}
200
Andrew Sculld9225b32018-11-19 16:12:41 +0000201/**
202 * Ensures that the range of data in the cache is written back so that it is
203 * visible to all cores in the system.
204 */
205void arch_mm_write_back_dcache(void *base, size_t size);
206
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100207uint64_t arch_mm_mode_to_attrs(int mode);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100208bool arch_mm_init(paddr_t table, bool first);
Andrew Sculle9827712018-10-19 14:54:20 +0100209uint8_t arch_mm_max_level(int mode);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000210uint8_t arch_mm_root_table_count(int mode);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100211
212/**
213 * Given the attrs from a table at some level and the attrs from all the blocks
214 * in that table, return equivalent attrs to use for a block which will replace
215 * the entire table.
216 */
217uint64_t arch_mm_combine_table_entry_attrs(uint64_t table_attrs,
218 uint64_t block_attrs);