xref: /haiku/src/system/boot/platform/efi/arch/arm64/arch_mmu.h (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
1 /*
2  * Copyright 2021-2022 Haiku, Inc. All rights reserved.
3  * Released under the terms of the MIT License.
4  */
5 
6 #ifndef _ARM64_ARCH_MMU_H
7 #define _ARM64_ARCH_MMU_H
8 
9 
10 /*
11  * Quotes taken from:
12  * Arm(C) Architecture Reference Manual
13  * Armv8, for Armv8-A architecture profile
14  * Chapter: D5.3 VMSAv8-64 translation table format descriptors
15  */
16 class ARMv8TranslationTableDescriptor {
17 
18 	/* Descriptor bit[0] identifies whether the descriptor is valid,
19 	 * and is 1 for a valid descriptor. If a lookup returns an invalid
20 	 * descriptor, the associated input address is unmapped, and any
21 	 * attempt to access it generates a Translation fault.
22 	 *
23 	 * Descriptor bit[1] identifies the descriptor type, and is encoded as:
24 	 * 0, Block The descriptor gives the base address of a block of memory,
25 	 * and the attributes for that memory region.
26 	 * 1, Table The descriptor gives the address of the next level of
27 	 * translation table, and for a stage 1 translation, some attributes for
28 	 * that translation.
29 	 */
30 
31 	static constexpr uint64_t kTypeMask = 0x3u;
32 
33 	static constexpr uint64_t kTypeInvalid = 0x0u;
34 	static constexpr uint64_t kTypeBlock = 0x1u;
35 	static constexpr uint64_t kTypeTable = 0x3u;
36 	static constexpr uint64_t kTypePage = 0x3u;
37 
38 	// TODO: Place TABLE PAGE BLOCK prefixes accordingly
39 	struct UpperAttributes {
40 		static constexpr uint64_t TABLE_PXN	= (1UL << 59);
41 		static constexpr uint64_t TABLE_XN	= (1UL << 60);
42 		static constexpr uint64_t TABLE_AP	= (1UL << 61);
43 		static constexpr uint64_t TABLE_NS	= (1UL << 63);
44 		static constexpr uint64_t BLOCK_PXN	= (1UL << 53);
45 		static constexpr uint64_t BLOCK_UXN	= (1UL << 54);
46 	};
47 
48 	struct LowerAttributes {
49 		static constexpr uint64_t BLOCK_NS			= (1 << 5);
50 		static constexpr uint64_t BLOCK_NON_SHARE	= (0 << 8);
51 		static constexpr uint64_t BLOCK_OUTER_SHARE	= (2 << 8);
52 		static constexpr uint64_t BLOCK_INNER_SHARE	= (3 << 8);
53 		static constexpr uint64_t BLOCK_AF			= (1UL << 10);
54 		static constexpr uint64_t BLOCK_NG			= (1UL << 11);
55 	};
56 
57 public:
58 
59 	static constexpr uint64 DefaultPeripheralAttribute = LowerAttributes::BLOCK_AF
60 		| LowerAttributes::BLOCK_NON_SHARE
61 		| UpperAttributes::BLOCK_PXN
62 		| UpperAttributes::BLOCK_UXN;
63 
64 	static constexpr uint64 DefaultCodeAttribute = LowerAttributes::BLOCK_AF
65 		| LowerAttributes::BLOCK_OUTER_SHARE;
66 
67 	ARMv8TranslationTableDescriptor(uint64_t* descriptor)
68 		: fDescriptor(descriptor)
69 	{}
70 
71 	ARMv8TranslationTableDescriptor(uint64_t descriptor)
72 		: fDescriptor(reinterpret_cast<uint64_t*>(descriptor))
73 	{}
74 
75 	bool IsInvalid() {
76 		return (*fDescriptor & kTypeMask) == kTypeInvalid;
77 	}
78 
79 	bool IsBlock() {
80 		return (*fDescriptor & kTypeMask) == kTypeBlock;
81 	}
82 
83 	bool IsPage() {
84 		return (*fDescriptor & kTypeMask) == kTypePage;
85 	}
86 
87 	bool IsTable() {
88 		return (*fDescriptor & kTypeMask) == kTypeTable;
89 	}
90 
91 
92 	uint64_t* Dereference() {
93 		if (IsTable())
94 			// TODO: Use ATTR_MASK
95 			return reinterpret_cast<uint64_t*>((*fDescriptor) & 0x0000fffffffff000ULL);
96 		else
97 			return NULL;
98 	}
99 
100 	void SetToTable(uint64* descriptor, uint64_t attributes) {
101 		*fDescriptor = reinterpret_cast<uint64_t>(descriptor) | kTypeTable;
102 	}
103 
104 	void SetAsPage(uint64_t* physical, uint64_t attributes) {
105 		*fDescriptor = CleanAttributes(reinterpret_cast<uint64_t>(physical)) | attributes | kTypePage;
106 	}
107 
108 	void SetAsBlock(uint64_t* physical, uint64_t attributes) {
109 		*fDescriptor = CleanAttributes(reinterpret_cast<uint64_t>(physical)) | attributes | kTypeBlock;
110 	}
111 
112 	void Next() {
113 		fDescriptor++;
114 	}
115 
116 	void JumpTo(uint16 slot) {
117 		fDescriptor += slot;
118 	}
119 
120 	uint64 Value() {
121 		return *fDescriptor;
122 	}
123 
124 	uint64 Location() {
125 		return reinterpret_cast<uint64_t>(fDescriptor);
126 	}
127 
128 private:
129 
130 	static uint64 CleanAttributes(uint64 address) {
131 		return address & ~ATTR_MASK;
132 	}
133 
134 	uint64_t* fDescriptor;
135 };
136 
137 
138 class MemoryAttributeIndirection {
139 
140 public:
141 	MemoryAttributeIndirection(uint8 el = kInvalidExceptionLevel)
142 	{
143 		if (el == kInvalidExceptionLevel) {
144 			el = arch_exception_level();
145 		}
146 
147 		switch(el)
148 		{
149 			case 1:
150 				fMair = READ_SPECIALREG(MAIR_EL1);
151 				break;
152 			case 2:
153 				fMair = READ_SPECIALREG(MAIR_EL2);
154 				break;
155 			case 3:
156 				fMair = READ_SPECIALREG(MAIR_EL3);
157 				break;
158 			default:
159 				fMair = 0x00u;
160 				break;
161 		}
162 	}
163 
164 	uint8 IndexOf(uint8 requirement) {
165 
166 		uint64 processedMair = fMair;
167 		uint8 index = 0;
168 
169 		while (((processedMair & 0xFF) != requirement) && (index < 8)) {
170 			index++;
171 			processedMair = (processedMair >> 8);
172 		}
173 
174 		return (index < 8)?index:0xff;
175 	}
176 
177 
178 	uint64 MaskOf(uint8 requirement) {
179 		return IndexOf(requirement) << 2;
180 	}
181 
182 private:
183 	uint64 fMair;
184 };
185 
186 
187 class ARMv8TranslationRegime {
188 
189 	static const uint8 skTranslationLevels = 4;
190 public:
191 
192 	struct TranslationLevel {
193 		uint8 shift;
194 		uint64 mask;
195 		bool blocks;
196 		bool tables;
197 		bool pages;
198 	};
199 
200 	typedef struct TranslationLevel TranslationDescriptor[skTranslationLevels];
201 
202 	ARMv8TranslationRegime(TranslationDescriptor& regime)
203 		: fRegime(regime)
204 	{}
205 
206 	uint16 DescriptorIndex(addr_t virt_addr, uint8 level) {
207 		return (virt_addr >> fRegime[level].shift) & fRegime[level].mask;
208 	}
209 
210 	bool BlocksAllowed(uint8 level) {
211 		return fRegime[level].blocks;
212 	}
213 
214 	bool TablesAllowed(uint8 level) {
215 		return fRegime[level].tables;
216 	}
217 
218 	bool PagesAllowed(uint8 level) {
219 		return fRegime[level].pages;
220 	}
221 
222 	uint64 Mask(uint8 level) {
223 		return EntrySize(level) - 1;
224 	}
225 
226 	bool Aligned(addr_t address, uint8 level) {
227 		return (address & Mask(level)) == 0;
228 	}
229 
230 	uint64 EntrySize(uint8 level) {
231 		return 1ul << fRegime[level].shift;
232 	}
233 
234 	uint64 TableSize(uint8 level) {
235 		return EntrySize(level) * arch_mmu_entries_per_granularity(Granularity());
236 	}
237 
238 	uint64* AllocatePage(void) {
239 		uint64 size = Granularity();
240 		uint64* page = NULL;
241 #if 0
242 		// BUG: allocation here overlaps assigned memory ...
243 		if (platform_allocate_region((void **)&page, size, 0, false) == B_OK) {
244 #else
245 		// TODO: luckly size == B_PAGE_SIZE == 4KB ...
246 		page = reinterpret_cast<uint64*>(mmu_allocate_page());
247 		if (page != NULL) {
248 #endif
249 			memset(page, 0, size);
250 			if ((reinterpret_cast<uint64>(page) & (size - 1)) != 0) {
251 				panic("Memory requested not %lx aligned\n", size - 1);
252 			}
253 			return page;
254 		} else {
255 			panic("Unavalable memory for descriptors\n");
256 			return NULL;
257 		}
258 	}
259 
260 	uint8 MaxLevels() {
261 		return skTranslationLevels;
262 	}
263 
264 	uint64 Granularity() {
265 		// Size of the last level ...
266 		return EntrySize(skTranslationLevels - 1);
267 	}
268 
269 private:
270 	TranslationDescriptor& fRegime;
271 };
272 
273 #endif /* _ARM64_ARCH_MMU_H */
274