Andrew Scull | 1883487 | 2018-10-12 11:48:09 +0100 | [diff] [blame] | 1 | /* |
| 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 Scull | fbc938a | 2018-08-20 14:09:28 +0100 | [diff] [blame] | 17 | #pragma once |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 18 | |
| 19 | #include <stdbool.h> |
| 20 | #include <stddef.h> |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 21 | |
Andrew Scull | 18c78fc | 2018-08-20 12:57:41 +0100 | [diff] [blame] | 22 | #include "hf/addr.h" |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 23 | |
Andrew Walbran | c3910f7 | 2018-11-27 14:24:36 +0000 | [diff] [blame] | 24 | /** A page table entry. */ |
Andrew Scull | 8087132 | 2018-08-06 12:04:09 +0100 | [diff] [blame] | 25 | typedef uint64_t pte_t; |
| 26 | |
| 27 | #define PAGE_LEVEL_BITS 9 |
| 28 | |
Andrew Walbran | 2513374 | 2018-09-28 16:28:02 +0100 | [diff] [blame] | 29 | /* |
| 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 Walbran | 65add58 | 2018-09-25 16:12:15 +0100 | [diff] [blame] | 34 | #define ARCH_AARCH64_MM_PTE_ATTR_MASK \ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 35 | (((UINT64_C(1) << PAGE_BITS) - 1) | ~((UINT64_C(1) << 48) - 1)) |
| 36 | |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 37 | /** |
| 38 | * Returns the encoding of a page table entry that isn't present. |
| 39 | */ |
| 40 | static inline pte_t arch_mm_absent_pte(int level) |
| 41 | { |
| 42 | return 0; |
| 43 | } |
| 44 | |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 45 | /** |
| 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 Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 51 | static inline pte_t arch_mm_table_pte(int level, paddr_t pa) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 52 | { |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 53 | /* This is the same for all levels on aarch64. */ |
| 54 | (void)level; |
Andrew Scull | 265ada9 | 2018-07-30 15:19:01 +0100 | [diff] [blame] | 55 | return pa_addr(pa) | 0x3; |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | /** |
| 59 | * Converts a physical address to a block PTE. |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 60 | * |
| 61 | * The level must allow block entries. |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 62 | */ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 63 | static inline pte_t arch_mm_block_pte(int level, paddr_t pa, uint64_t attrs) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 64 | { |
Andrew Walbran | 2513374 | 2018-09-28 16:28:02 +0100 | [diff] [blame] | 65 | pte_t pte = pa_addr(pa) | attrs | 0x1; |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 66 | if (level == 0) { |
Andrew Walbran | 72aaeca | 2018-09-26 14:01:09 +0100 | [diff] [blame] | 67 | /* A level 0 'block' is actually a page entry. */ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 68 | pte |= 0x2; |
| 69 | } |
| 70 | return pte; |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | /** |
| 74 | * Specifies whether block mappings are acceptable at the given level. |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 75 | * |
| 76 | * Level 0 must allow block entries. |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 77 | */ |
| 78 | static inline bool arch_mm_is_block_allowed(int level) |
| 79 | { |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 80 | return level <= 2; |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 81 | } |
| 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 Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 87 | static inline bool arch_mm_pte_is_present(pte_t pte, int level) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 88 | { |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 89 | return (pte & 0x1) != 0; |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | /** |
| 93 | * Determines if the given pte references another table. |
| 94 | */ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 95 | static inline bool arch_mm_pte_is_table(pte_t pte, int level) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 96 | { |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 97 | return level != 0 && (pte & 0x3) == 0x3; |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Determines if the given pte references a block of pages. |
| 102 | */ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 103 | static inline bool arch_mm_pte_is_block(pte_t pte, int level) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 104 | { |
Andrew Walbran | 72aaeca | 2018-09-26 14:01:09 +0100 | [diff] [blame] | 105 | /* We count pages at level 0 as blocks. */ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 106 | return arch_mm_is_block_allowed(level) && |
| 107 | (pte & 0x3) == (level == 0 ? 0x3 : 0x1); |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 108 | } |
| 109 | |
Andrew Walbran | 65add58 | 2018-09-25 16:12:15 +0100 | [diff] [blame] | 110 | static 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 Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 115 | /** |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 116 | * Clears the given physical address, i.e., sets the ignored bits (from a page |
| 117 | * table perspective) to zero. |
| 118 | */ |
Andrew Scull | 265ada9 | 2018-07-30 15:19:01 +0100 | [diff] [blame] | 119 | static inline paddr_t arch_mm_clear_pa(paddr_t pa) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 120 | { |
Andrew Walbran | 65add58 | 2018-09-25 16:12:15 +0100 | [diff] [blame] | 121 | return pa_init(arch_aarch64_mm_clear_pte_attrs(pa_addr(pa))); |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | /** |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 125 | * Extracts the physical address of the block referred to by the given page |
| 126 | * table entry. |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 127 | */ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 128 | static inline paddr_t arch_mm_block_from_pte(pte_t pte) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 129 | { |
Andrew Walbran | 65add58 | 2018-09-25 16:12:15 +0100 | [diff] [blame] | 130 | return pa_init(arch_aarch64_mm_clear_pte_attrs(pte)); |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | /** |
Andrew Scull | 8087132 | 2018-08-06 12:04:09 +0100 | [diff] [blame] | 134 | * Extracts the physical address of the page table referred to by the given page |
| 135 | * table entry. |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 136 | */ |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 137 | static inline paddr_t arch_mm_table_from_pte(pte_t pte) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 138 | { |
Andrew Walbran | 65add58 | 2018-09-25 16:12:15 +0100 | [diff] [blame] | 139 | return pa_init(arch_aarch64_mm_clear_pte_attrs(pte)); |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 140 | } |
| 141 | |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 142 | /** |
| 143 | * Extracts the architecture specific attributes applies to the given page table |
| 144 | * entry. |
| 145 | */ |
| 146 | static inline uint64_t arch_mm_pte_attrs(pte_t pte) |
| 147 | { |
Andrew Walbran | 65add58 | 2018-09-25 16:12:15 +0100 | [diff] [blame] | 148 | return pte & ARCH_AARCH64_MM_PTE_ATTR_MASK; |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 149 | } |
Andrew Scull | 265ada9 | 2018-07-30 15:19:01 +0100 | [diff] [blame] | 150 | |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 151 | /** |
| 152 | * Invalidates stage-1 TLB entries referring to the given virtual address range. |
| 153 | */ |
Andrew Scull | 265ada9 | 2018-07-30 15:19:01 +0100 | [diff] [blame] | 154 | static inline void arch_mm_invalidate_stage1_range(vaddr_t va_begin, |
| 155 | vaddr_t va_end) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 156 | { |
Andrew Scull | 265ada9 | 2018-07-30 15:19:01 +0100 | [diff] [blame] | 157 | uintvaddr_t begin = va_addr(va_begin); |
| 158 | uintvaddr_t end = va_addr(va_end); |
| 159 | uintvaddr_t it; |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 160 | |
| 161 | begin >>= 12; |
| 162 | end >>= 12; |
| 163 | |
| 164 | __asm__ volatile("dsb ishst"); |
| 165 | |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 166 | for (it = begin; it < end; it += (UINT64_C(1) << (PAGE_BITS - 12))) { |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 167 | __asm__("tlbi vae2is, %0" : : "r"(it)); |
Andrew Scull | 7364a8e | 2018-07-19 15:39:29 +0100 | [diff] [blame] | 168 | } |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 169 | |
| 170 | __asm__ volatile("dsb ish"); |
| 171 | } |
| 172 | |
| 173 | /** |
Andrew Scull | 8087132 | 2018-08-06 12:04:09 +0100 | [diff] [blame] | 174 | * Invalidates stage-2 TLB entries referring to the given intermediate physical |
| 175 | * address range. |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 176 | */ |
Andrew Scull | 8087132 | 2018-08-06 12:04:09 +0100 | [diff] [blame] | 177 | static inline void arch_mm_invalidate_stage2_range(ipaddr_t va_begin, |
| 178 | ipaddr_t va_end) |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 179 | { |
Andrew Scull | 8087132 | 2018-08-06 12:04:09 +0100 | [diff] [blame] | 180 | uintpaddr_t begin = ipa_addr(va_begin); |
| 181 | uintpaddr_t end = ipa_addr(va_end); |
| 182 | uintpaddr_t it; |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 183 | |
Wedson Almeida Filho | 84a30a0 | 2018-07-23 20:05:05 +0100 | [diff] [blame] | 184 | /* TODO: This only applies to the current VMID. */ |
| 185 | |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 186 | begin >>= 12; |
| 187 | end >>= 12; |
| 188 | |
| 189 | __asm__ volatile("dsb ishst"); |
| 190 | |
Andrew Scull | 78d6fd9 | 2018-09-06 15:08:36 +0100 | [diff] [blame] | 191 | for (it = begin; it < end; it += (UINT64_C(1) << (PAGE_BITS - 12))) { |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 192 | __asm__("tlbi ipas2e1, %0" : : "r"(it)); |
Andrew Scull | 7364a8e | 2018-07-19 15:39:29 +0100 | [diff] [blame] | 193 | } |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 194 | |
Andrew Scull | 4f170f5 | 2018-07-19 12:58:20 +0100 | [diff] [blame] | 195 | __asm__ volatile( |
| 196 | "dsb ish\n" |
| 197 | "tlbi vmalle1is\n" |
| 198 | "dsb ish\n"); |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 199 | } |
| 200 | |
Andrew Scull | d9225b3 | 2018-11-19 16:12:41 +0000 | [diff] [blame] | 201 | /** |
| 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 | */ |
| 205 | void arch_mm_write_back_dcache(void *base, size_t size); |
| 206 | |
Wedson Almeida Filho | fed6902 | 2018-07-11 15:39:12 +0100 | [diff] [blame] | 207 | uint64_t arch_mm_mode_to_attrs(int mode); |
Wedson Almeida Filho | 03e767a | 2018-07-30 15:32:03 +0100 | [diff] [blame] | 208 | bool arch_mm_init(paddr_t table, bool first); |
Andrew Scull | e982771 | 2018-10-19 14:54:20 +0100 | [diff] [blame] | 209 | uint8_t arch_mm_max_level(int mode); |
Andrew Scull | 1ba470e | 2018-10-31 15:14:31 +0000 | [diff] [blame] | 210 | uint8_t arch_mm_root_table_count(int mode); |
Andrew Walbran | 2400ed2 | 2018-09-27 14:45:58 +0100 | [diff] [blame] | 211 | |
| 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 | */ |
| 217 | uint64_t arch_mm_combine_table_entry_attrs(uint64_t table_attrs, |
| 218 | uint64_t block_attrs); |