xref: /haiku/src/system/kernel/arch/riscv64/arch_vm.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
1 /*
2  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
3  * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2019, Adrien Destugues, pulkomandy@pulkomandy.tk
5  * Distributed under the terms of the MIT License.
6  */
7 
8 #include <vm/vm.h>
9 #include <vm/VMAddressSpace.h>
10 #include <arch/vm.h>
11 #include <boot/kernel_args.h>
12 
13 #include "RISCV64VMTranslationMap.h"
14 
15 
16 #define TRACE_ARCH_VM
17 #ifdef TRACE_ARCH_VM
18 #	define TRACE(x) dprintf x
19 #else
20 #	define TRACE(x) ;
21 #endif
22 
23 
24 static uint64_t
25 SignExtendVirtAdr(uint64_t virtAdr)
26 {
27 	if (((uint64_t)1 << 38) & virtAdr)
28 		return virtAdr | 0xFFFFFF8000000000;
29 	return virtAdr;
30 }
31 
32 
33 static Pte*
34 LookupPte(phys_addr_t pageTable, addr_t virtAdr)
35 {
36 	Pte *pte = (Pte*)VirtFromPhys(pageTable);
37 	for (int level = 2; level > 0; level --) {
38 		pte += VirtAdrPte(virtAdr, level);
39 		if (!pte->isValid) {
40 			return NULL;
41 		}
42 		// TODO: Handle superpages (RWX=0 when not at lowest level)
43 		pte = (Pte*)VirtFromPhys(B_PAGE_SIZE * pte->ppn);
44 	}
45 	pte += VirtAdrPte(virtAdr, 0);
46 	return pte;
47 }
48 
49 
50 
51 static void
52 WritePteFlags(uint32 flags)
53 {
54 	bool first = true;
55 	dprintf("{");
56 	for (uint32 i = 0; i < 32; i++) {
57 		if ((1 << i) & flags) {
58 			if (first)
59 				first = false;
60 			else
61 				dprintf(", ");
62 
63 			switch (i) {
64 				case 0:
65 					dprintf("valid");
66 					break;
67 				case 1:
68 					dprintf("read");
69 					break;
70 				case 2:
71 					dprintf("write");
72 					break;
73 				case 3:
74 					dprintf("exec");
75 					break;
76 				case 4:
77 					dprintf("user");
78 					break;
79 				case 5:
80 					dprintf("global");
81 					break;
82 				case 6:
83 					dprintf("accessed");
84 					break;
85 				case 7:
86 					dprintf("dirty");
87 					break;
88 				default:
89 					dprintf("%" B_PRIu32, i);
90 			}
91 		}
92 	}
93 	dprintf("}");
94 }
95 
96 
97 class PageTableDumper
98 {
99 private:
100 	uint64 firstVirt;
101 	uint64 firstPhys;
102 	uint64 firstFlags;
103 	uint64 len;
104 
105 public:
106 	PageTableDumper()
107 		:
108 		firstVirt(0),
109 		firstPhys(0),
110 		firstFlags(0),
111 		len(0)
112 	{}
113 
114 	~PageTableDumper()
115 	{
116 		Write(0, 0, 0, 0);
117 	}
118 
119 	void Write(uint64_t virtAdr, uint64_t physAdr, size_t size, uint64 flags) {
120 		if (virtAdr == firstVirt + len && physAdr == firstPhys + len && flags == firstFlags) {
121 			len += size;
122 		} else {
123 			if (len != 0) {
124 				dprintf("  0x%08" B_PRIxADDR " - 0x%08" B_PRIxADDR,
125 				firstVirt, firstVirt + (len - 1));
126 				dprintf(": 0x%08" B_PRIxADDR " - 0x%08" B_PRIxADDR ", %#" B_PRIxADDR ", ",
127 					firstPhys, firstPhys + (len - 1), len);
128 				WritePteFlags(firstFlags); dprintf("\n");
129 			}
130 			firstVirt = virtAdr;
131 			firstPhys = physAdr;
132 			firstFlags = flags;
133 			len = size;
134 		}
135 	}
136 };
137 
138 
139 static void
140 DumpPageTableInt(Pte* pte, uint64_t virtAdr, uint32_t level, PageTableDumper& dumper)
141 {
142 	for (uint32 i = 0; i < pteCount; i++) {
143 		if (pte[i].isValid) {
144 			if (!pte[i].isRead && !pte[i].isWrite && !pte[i].isExec) {
145 
146 				if (level == 0)
147 					kprintf("  internal page table on level 0\n");
148 
149 				DumpPageTableInt((Pte*)VirtFromPhys(B_PAGE_SIZE*pte[i].ppn),
150 					virtAdr + ((uint64_t)i << (pageBits + pteIdxBits * level)),
151 					level - 1, dumper);
152 			} else {
153 				dumper.Write(SignExtendVirtAdr(virtAdr
154 						+ ((uint64_t)i << (pageBits + pteIdxBits*level))),
155 					pte[i].ppn * B_PAGE_SIZE, 1 << (pageBits + pteIdxBits * level),
156 					pte[i].val & 0xff);
157 			}
158 		}
159 	}
160 }
161 
162 
163 static int
164 DumpPageTable(int argc, char** argv)
165 {
166 	int curArg = 1;
167 	SatpReg satp;
168 	bool isArea = false;
169 	addr_t base = 0;
170 	size_t size = 0;
171 
172 	satp.val = Satp();
173 	while (curArg < argc && argv[curArg][0] == '-') {
174 		if (strcmp(argv[curArg], "-team") == 0) {
175 			curArg++;
176 			team_id id = strtoul(argv[curArg++], NULL, 0);
177 			VMAddressSpace* addrSpace = VMAddressSpace::DebugGet(id);
178 			if (addrSpace == NULL) {
179 				kprintf("could not find team %" B_PRId32 "\n", id);
180 				return 0;
181 			}
182 			satp.val = ((RISCV64VMTranslationMap*)
183 				addrSpace->TranslationMap())->Satp();
184 			isArea = false;
185 		} else if (strcmp(argv[curArg], "-area") == 0) {
186 			curArg++;
187 			uint64 areaId;
188 			if (!evaluate_debug_expression(argv[curArg++], &areaId, false))
189 				return 0;
190 			VMArea* area = VMAreas::Lookup((area_id)areaId);
191 			if (area == NULL) {
192 				kprintf("could not find area %" B_PRId32 "\n", (area_id)areaId);
193 				return 0;
194 			}
195 			satp.val = ((RISCV64VMTranslationMap*)
196 				area->address_space->TranslationMap())->Satp();
197 			base = area->Base();
198 			size = area->Size();
199 			kprintf("area %" B_PRId32 "(%s)\n", area->id, area->name);
200 				isArea = true;
201 		} else {
202 			kprintf("unknown flag \"%s\"\n", argv[curArg]);
203 			return 0;
204 		}
205 	}
206 
207 	kprintf("satp: %#" B_PRIx64 "\n", satp.val);
208 
209 	PageTableDumper dumper;
210 
211 	if (!isArea) {
212 		Pte* root = (Pte*)VirtFromPhys(satp.ppn * B_PAGE_SIZE);
213 		DumpPageTableInt(root, 0, 2, dumper);
214 	} else {
215 		for (; size > 0; base += B_PAGE_SIZE, size -= B_PAGE_SIZE) {
216 			Pte* pte = LookupPte(satp.ppn * B_PAGE_SIZE, base);
217 			if (pte == NULL || !pte->isValid)
218 				continue;
219 
220 			dumper.Write(base, pte->ppn * B_PAGE_SIZE, B_PAGE_SIZE, pte->val & 0xff);
221 		}
222 	}
223 
224 	return 0;
225 }
226 
227 
228 static int
229 DumpVirtPage(int argc, char** argv)
230 {
231 	int curArg = 1;
232 	SatpReg satp;
233 
234 	satp.val = Satp();
235 	while (curArg < argc && argv[curArg][0] == '-') {
236 		if (strcmp(argv[curArg], "-team") == 0) {
237 			curArg++;
238 			team_id id = strtoul(argv[curArg++], NULL, 0);
239 			VMAddressSpace* addrSpace = VMAddressSpace::DebugGet(id);
240 			if (addrSpace == NULL) {
241 				kprintf("could not find team %" B_PRId32 "\n", id);
242 				return 0;
243 			}
244 			satp.val = ((RISCV64VMTranslationMap*)
245 				addrSpace->TranslationMap())->Satp();
246 		} else {
247 			kprintf("unknown flag \"%s\"\n", argv[curArg]);
248 			return 0;
249 		}
250 	}
251 
252 	kprintf("satp: %#" B_PRIx64 "\n", satp.val);
253 
254 	uint64 virt = 0;
255 	if (!evaluate_debug_expression(argv[curArg++], &virt, false))
256 		return 0;
257 
258 	virt = ROUNDDOWN(virt, B_PAGE_SIZE);
259 
260 	Pte* pte = LookupPte(satp.ppn * B_PAGE_SIZE, virt);
261 	if (pte == NULL) {
262 		dprintf("not mapped\n");
263 		return 0;
264 	}
265 
266 	PageTableDumper dumper;
267 	dumper.Write(virt, pte->ppn * B_PAGE_SIZE, B_PAGE_SIZE, pte->val & 0xff);
268 
269 	return 0;
270 }
271 
272 
273 status_t
274 arch_vm_init(kernel_args *args)
275 {
276 	return B_OK;
277 }
278 
279 
280 status_t
281 arch_vm_init_post_area(kernel_args *args)
282 {
283 	void* address = (void*)args->arch_args.physMap.start;
284 	area_id area = vm_create_null_area(VMAddressSpace::KernelID(),
285 		"physical map area", &address, B_EXACT_ADDRESS,
286 		args->arch_args.physMap.size, 0);
287 	if (area < B_OK)
288 		return area;
289 
290 	add_debugger_command("dump_page_table", &DumpPageTable, "Dump page table");
291 	add_debugger_command("dump_virt_page", &DumpVirtPage, "Dump virtual page mapping");
292 
293 	return B_OK;
294 }
295 
296 
297 status_t
298 arch_vm_init_post_modules(kernel_args *args)
299 {
300 	return B_OK;
301 }
302 
303 
304 status_t
305 arch_vm_init_end(kernel_args *args)
306 {
307 	TRACE(("arch_vm_init_end(): %" B_PRIu32 " virtual ranges to keep:\n",
308 		args->arch_args.num_virtual_ranges_to_keep));
309 
310 	for (int i = 0; i < (int)args->arch_args.num_virtual_ranges_to_keep; i++) {
311 		addr_range &range = args->arch_args.virtual_ranges_to_keep[i];
312 
313 		TRACE(("  start: %p, size: %#" B_PRIxSIZE "\n", (void*)range.start, range.size));
314 
315 #if 1
316 		// skip ranges outside the kernel address space
317 		if (!IS_KERNEL_ADDRESS(range.start)) {
318 			TRACE(("    no kernel address, skipping...\n"));
319 			continue;
320 		}
321 
322 		phys_addr_t physicalAddress;
323 		void *address = (void*)range.start;
324 		if (vm_get_page_mapping(VMAddressSpace::KernelID(), range.start,
325 				&physicalAddress) != B_OK)
326 			panic("arch_vm_init_end(): No page mapping for %p\n", address);
327 		area_id area = vm_map_physical_memory(VMAddressSpace::KernelID(),
328 			"boot loader reserved area", &address,
329 			B_EXACT_ADDRESS, range.size,
330 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
331 			physicalAddress, true);
332 		if (area < 0) {
333 			panic("arch_vm_init_end(): Failed to create area for boot loader "
334 				"reserved area: %p - %p\n", (void*)range.start,
335 				(void*)(range.start + range.size));
336 		}
337 #endif
338 	}
339 
340 #if 0
341 	// Throw away any address space mappings we've inherited from the boot
342 	// loader and have not yet turned into an area.
343 	vm_free_unused_boot_loader_range(0, 0xffffffff - B_PAGE_SIZE + 1);
344 #endif
345 
346 	return B_OK;
347 }
348 
349 
350 void
351 arch_vm_aspace_swap(struct VMAddressSpace *from, struct VMAddressSpace *to)
352 {
353 	// This functions is only invoked when a userland thread is in the process
354 	// of dying. It switches to the kernel team and does whatever cleanup is
355 	// necessary (in case it is the team's main thread, it will delete the
356 	// team).
357 	// It is however not necessary to change the page directory. Userland team's
358 	// page directories include all kernel mappings as well. Furthermore our
359 	// arch specific translation map data objects are ref-counted, so they won't
360 	// go away as long as they are still used on any CPU.
361 
362 	SetSatp(((RISCV64VMTranslationMap*)to->TranslationMap())->Satp());
363 	FlushTlbAllAsid(0);
364 }
365 
366 
367 bool
368 arch_vm_supports_protection(uint32 protection)
369 {
370 	return true;
371 }
372 
373 
374 void
375 arch_vm_unset_memory_type(VMArea *area)
376 {
377 }
378 
379 
380 status_t
381 arch_vm_set_memory_type(VMArea *area, phys_addr_t physicalBase, uint32 type,
382 	uint32 *effectiveType)
383 {
384 	return B_OK;
385 }
386