blob: ba443874cb4ea8e70eda8325fa7446e5fef226a2 [file] [log] [blame]
Wedson Almeida Filhofed69022018-07-11 15:39:12 +01001#ifndef _ARCH_MM_H
2#define _ARCH_MM_H
3
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7
Andrew Scull265ada92018-07-30 15:19:01 +01008/* Integer type large enough to hold a physical address. */
9typedef uintptr_t uintpaddr_t;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010010
Andrew Scull265ada92018-07-30 15:19:01 +010011/* Integer type large enough to hold a virtual address. */
12typedef uintptr_t uintvaddr_t;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010013
14/* A page table entry. */
Andrew Scull265ada92018-07-30 15:19:01 +010015typedef uint64_t pte_t;
16
17/* An opaque type for a physical address. */
18typedef struct {
19 uintpaddr_t pa;
20} paddr_t;
21
22/* An opaque type for a virtual address. */
23typedef struct {
24 uintvaddr_t va;
25} vaddr_t;
26
27/**
28 * Initializes a physical address.
29 */
30static inline paddr_t pa_init(uintpaddr_t p)
31{
32 return (paddr_t){.pa = p};
33}
34
35/**
36 * Extracts the absolute physical address.
37 */
38static inline uintpaddr_t pa_addr(paddr_t pa)
39{
40 return pa.pa;
41}
42
43/**
44 * Initializes a virtual address.
45 */
46static inline vaddr_t va_init(uintvaddr_t v)
47{
48 return (vaddr_t){.va = v};
49}
50
51/**
52 * Extracts the absolute virtual address.
53 */
54static inline uintvaddr_t va_addr(vaddr_t va)
55{
56 return va.va;
57}
58
59/**
60 * Advances a virtual address.
61 */
62static inline vaddr_t va_add(vaddr_t va, size_t n)
63{
64 return va_init(va_addr(va) + n);
65}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010066
67#define PAGE_LEVEL_BITS 9
68#define PAGE_BITS 12
69
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010070/**
71 * Converts a physical address to a table PTE.
72 *
73 * The spec says that 'Table descriptors for stage 2 translations do not
74 * include any attribute field', so we don't take any attributes as arguments.
75 */
76static inline pte_t arch_mm_pa_to_table_pte(paddr_t pa)
77{
Andrew Scull265ada92018-07-30 15:19:01 +010078 return pa_addr(pa) | 0x3;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010079}
80
81/**
82 * Converts a physical address to a block PTE.
83 */
84static inline pte_t arch_mm_pa_to_block_pte(paddr_t pa, uint64_t attrs)
85{
Andrew Scull265ada92018-07-30 15:19:01 +010086 return pa_addr(pa) | attrs;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010087}
88
89/**
90 * Converts a physical address to a page PTE.
91 */
92static inline pte_t arch_mm_pa_to_page_pte(paddr_t pa, uint64_t attrs)
93{
Andrew Scull265ada92018-07-30 15:19:01 +010094 return pa_addr(pa) | attrs | ((attrs & 1) << 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010095}
96
97/**
98 * Converts a block PTE to a page PTE.
99 */
100static inline pte_t arch_mm_block_to_page_pte(pte_t pte)
101{
102 return pte | 2;
103}
104
105/**
106 * Specifies whether block mappings are acceptable at the given level.
107 */
108static inline bool arch_mm_is_block_allowed(int level)
109{
110 return level == 1 || level == 2;
111}
112
113/**
114 * Returns the encoding of a page table entry that isn't present.
115 */
116static inline pte_t arch_mm_absent_pte(void)
117{
118 return 0;
119}
120
121/**
122 * Determines if the given pte is present, i.e., if it points to another table,
123 * to a page, or a block of pages.
124 */
125static inline bool arch_mm_pte_is_present(pte_t pte)
126{
127 return (pte & 1) != 0;
128}
129
130/**
131 * Determines if the given pte references another table.
132 */
133static inline bool arch_mm_pte_is_table(pte_t pte)
134{
135 return (pte & 3) == 3;
136}
137
138/**
139 * Determines if the given pte references a block of pages.
140 */
141static inline bool arch_mm_pte_is_block(pte_t pte)
142{
143 return (pte & 3) == 1;
144}
145
Andrew Scull265ada92018-07-30 15:19:01 +0100146#define CLEAR_PTE_ATTRS(v) \
147 ((v) & ~((1ull << PAGE_BITS) - 1) & ((1ull << 48) - 1))
148
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100149/**
150 * Clears the given virtual address, i.e., sets the ignored bits (from a page
151 * table perspective) to zero.
152 */
Andrew Scull265ada92018-07-30 15:19:01 +0100153static inline vaddr_t arch_mm_clear_va(vaddr_t va)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100154{
Andrew Scull265ada92018-07-30 15:19:01 +0100155 return va_init(CLEAR_PTE_ATTRS(va_addr(va)));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100156}
157
158/**
159 * Clears the given physical address, i.e., sets the ignored bits (from a page
160 * table perspective) to zero.
161 */
Andrew Scull265ada92018-07-30 15:19:01 +0100162static inline paddr_t arch_mm_clear_pa(paddr_t pa)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100163{
Andrew Scull265ada92018-07-30 15:19:01 +0100164 return pa_init(CLEAR_PTE_ATTRS(pa_addr(pa)));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100165}
166
167/**
168 * Extracts the physical address from a page table entry.
169 */
170static inline paddr_t arch_mm_pte_to_paddr(pte_t pte)
171{
Andrew Scull265ada92018-07-30 15:19:01 +0100172 return pa_init(CLEAR_PTE_ATTRS(pte));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100173}
174
175/**
176 * Extracts a page table pointer from the given page table entry.
177 */
178static inline pte_t *arch_mm_pte_to_table(pte_t pte)
179{
Andrew Scull265ada92018-07-30 15:19:01 +0100180 return (pte_t *)CLEAR_PTE_ATTRS(pte);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100181}
182
Andrew Scull265ada92018-07-30 15:19:01 +0100183#undef CLEAR_PTE_ATTRS
184
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100185/**
186 * Invalidates stage-1 TLB entries referring to the given virtual address range.
187 */
Andrew Scull265ada92018-07-30 15:19:01 +0100188static inline void arch_mm_invalidate_stage1_range(vaddr_t va_begin,
189 vaddr_t va_end)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100190{
Andrew Scull265ada92018-07-30 15:19:01 +0100191 uintvaddr_t begin = va_addr(va_begin);
192 uintvaddr_t end = va_addr(va_end);
193 uintvaddr_t it;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100194
195 begin >>= 12;
196 end >>= 12;
197
198 __asm__ volatile("dsb ishst");
199
Andrew Scull7364a8e2018-07-19 15:39:29 +0100200 for (it = begin; it < end; it += (1ull << (PAGE_BITS - 12))) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100201 __asm__("tlbi vae2is, %0" : : "r"(it));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100202 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100203
204 __asm__ volatile("dsb ish");
205}
206
207/**
208 * Invalidates stage-2 TLB entries referring to the given virtual address range.
209 */
Andrew Scull265ada92018-07-30 15:19:01 +0100210static inline void arch_mm_invalidate_stage2_range(vaddr_t va_begin,
211 vaddr_t va_end)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100212{
Andrew Scull265ada92018-07-30 15:19:01 +0100213 uintvaddr_t begin = va_addr(va_begin);
214 uintvaddr_t end = va_addr(va_end);
215 uintvaddr_t it;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100216
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100217 /* TODO: This only applies to the current VMID. */
218
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100219 begin >>= 12;
220 end >>= 12;
221
222 __asm__ volatile("dsb ishst");
223
Andrew Scull7364a8e2018-07-19 15:39:29 +0100224 for (it = begin; it < end; it += (1ull << (PAGE_BITS - 12))) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100225 __asm__("tlbi ipas2e1, %0" : : "r"(it));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100226 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100227
Andrew Scull4f170f52018-07-19 12:58:20 +0100228 __asm__ volatile(
229 "dsb ish\n"
230 "tlbi vmalle1is\n"
231 "dsb ish\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100232}
233
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100234static inline void arch_mm_set_vm(uint64_t vmid, paddr_t table)
235{
Andrew Scull265ada92018-07-30 15:19:01 +0100236 __asm__ volatile("msr vttbr_el2, %0"
237 :
238 : "r"(pa_addr(table) | (vmid << 48)));
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100239}
240
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100241uint64_t arch_mm_mode_to_attrs(int mode);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100242bool arch_mm_init(paddr_t table, bool first);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100243int arch_mm_max_level(int mode);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100244
Andrew Scull4f170f52018-07-19 12:58:20 +0100245#endif /* _ARCH_MM_H */