1/*
2 * Copyright 2019-2020, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * 		Adrien Destugues <pulkomandy@pulkomandy.tk>
7 */
8
9
10#include <arch/debug.h>
11#include <vm/VMAddressSpace.h>
12#include <vm/VMArea.h>
13#include <elf.h>
14#include <kimage.h>
15#include <arch/generic/user_memory.h>
16#include <AutoDeleterDrivers.h>
17
18#include "RISCV64VMTranslationMap.h"
19
20
21kernel_args *sKernelArgs;
22bool sInitCalled = false;
23
24
25extern "C" void SVecRet();
26extern "C" void SVecURet();
27
28void WriteRegisters(iframe* frame);
29
30
31static void
32WriteImage(preloaded_image* _image)
33{
34	preloaded_elf64_image* image = (preloaded_elf64_image*)_image;
35	dprintf("image %p\n", image);
36	dprintf("image \"%s\"\n", (char*)image->name);
37	dprintf("  text: 0x%" B_PRIxADDR " - 0x%" B_PRIxADDR ",%"
38		B_PRIdSSIZE "\n", image->text_region.start,
39		image->text_region.start + image->text_region.size,
40		image->text_region.delta);
41	dprintf("  data: 0x%" B_PRIxADDR " - 0x%" B_PRIxADDR ", %"
42		B_PRIdSSIZE "\n", image->data_region.start,
43		image->data_region.start + image->data_region.size,
44		image->data_region.delta);
45}
46
47
48static void
49WriteImages()
50{
51	WriteImage(sKernelArgs->kernel_image);
52
53	preloaded_image* image = sKernelArgs->preloaded_images;
54	while (image != NULL) {
55		WriteImage(image);
56		image = image->next;
57	}
58}
59
60
61static bool
62AddressInImage(preloaded_image* _image, addr_t adr)
63{
64	preloaded_elf64_image* image = (preloaded_elf64_image*)_image;
65	if (adr >= image->text_region.start
66			&& adr < image->text_region.start + image->text_region.size) {
67		return true;
68	}
69
70	if (adr >= image->data_region.start
71			&& adr < image->data_region.start + image->data_region.size) {
72		return true;
73	}
74
75	return false;
76}
77
78
79static bool
80SymbolAt(preloaded_image* _image, addr_t adr, const char **name, ssize_t *ofs)
81{
82	preloaded_elf64_image* image = (preloaded_elf64_image*)_image;
83	adr -= image->text_region.delta;
84	for (uint32 i = 0; i < image->num_debug_symbols; i++) {
85		Elf64_Sym& sym = image->debug_symbols[i];
86		if (sym.st_shndx != STN_UNDEF && adr >= sym.st_value
87			&& adr < sym.st_value + sym.st_size) {
88			if (name != NULL)
89				*name = &image->debug_string_table[sym.st_name];
90			if (ofs != NULL)
91				*ofs = adr - sym.st_value;
92			return true;
93		}
94	}
95	return false;
96}
97
98
99static preloaded_image*
100FindImage(addr_t adr)
101{
102	if (AddressInImage(sKernelArgs->kernel_image, adr))
103		return sKernelArgs->kernel_image;
104
105	preloaded_image* image = sKernelArgs->preloaded_images;
106	while (image != NULL) {
107		if (AddressInImage(image, adr))
108			return image;
109		image = image->next;
110	}
111
112	return NULL;
113}
114
115
116static VMArea*
117FindArea(addr_t adr)
118{
119	if (IS_KERNEL_ADDRESS(adr)) {
120		VMAddressSpacePutter addrSpace(VMAddressSpace::GetKernel());
121		return addrSpace->LookupArea(adr);
122	}
123	if (IS_USER_ADDRESS(adr)) {
124		VMAddressSpacePutter addrSpace(VMAddressSpace::GetCurrent());
125		return addrSpace->LookupArea(adr);
126	}
127	return NULL;
128}
129
130
131static VMArea*
132FindAreaEx(Thread* thread, addr_t adr)
133{
134	if (IS_KERNEL_ADDRESS(adr)) {
135		return VMAddressSpace::Kernel()->LookupArea(adr);
136	}
137	if (IS_USER_ADDRESS(adr)) {
138		return thread->team->address_space->LookupArea(adr);
139	}
140	return NULL;
141}
142
143
144static status_t
145lookup_symbol(Thread* thread, addr_t address, addr_t* _baseAddress,
146	const char** _symbolName, const char** _imageName, bool* _exactMatch)
147{
148	status_t status = B_ENTRY_NOT_FOUND;
149
150	if (IS_KERNEL_ADDRESS(address)) {
151		// a kernel symbol
152		status = elf_debug_lookup_symbol_address(address, _baseAddress,
153			_symbolName, _imageName, _exactMatch);
154	} else if (true && thread != NULL && thread->team != NULL) {
155		// try a lookup using the userland runtime loader structures
156		status = elf_debug_lookup_user_symbol_address(thread->team, address,
157			_baseAddress, _symbolName, _imageName, _exactMatch);
158
159		if (status != B_OK) {
160			// try to locate the image in the images loaded into user space
161			status = image_debug_lookup_user_symbol_address(thread->team,
162				address, _baseAddress, _symbolName, _imageName, _exactMatch);
163		}
164	}
165
166	return status;
167}
168
169
170void
171WritePCBoot(addr_t pc)
172{
173	preloaded_image* image = FindImage(pc);
174	if (image != NULL) {
175		const char *name;
176		ssize_t ofs;
177		if (SymbolAt(image, pc, &name, &ofs)) {
178			dprintf("<%s> %s + %" B_PRIdSSIZE, (char*)image->name, name, ofs);
179			return;
180		}
181		dprintf("<%s> 0x%" B_PRIxADDR, (char*)image->name,
182			pc - ((preloaded_elf64_image*)image)->text_region.delta);
183		return;
184	}
185/*
186	VMArea* area = FindArea(pc);
187	if (area != NULL) {
188		dprintf("<%s> 0x%" B_PRIxADDR, area->name, pc - area->Base());
189		return;
190	}
191*/
192	dprintf("0x%" B_PRIxADDR, pc);
193}
194
195
196static void
197WritePCEx(Thread* thread, addr_t pc)
198{
199	dprintf("0x%" B_PRIxADDR " ", pc);
200	if (!sInitCalled) {
201		WritePCBoot(pc); return;
202	}
203
204	addr_t baseAddress;
205	const char* symbolName;
206	const char* imageName;
207	bool exactMatch;
208	if (lookup_symbol(thread, pc, &baseAddress,
209		&symbolName, &imageName, &exactMatch) >= B_OK) {
210		if (symbolName != NULL) {
211			dprintf("<%s> %s + %" B_PRIdSSIZE, imageName, symbolName,
212				pc - baseAddress);
213			return;
214		}
215		dprintf("<%s> 0x%" B_PRIxADDR, imageName, pc - baseAddress);
216		return;
217	}
218
219	VMArea* area = FindAreaEx(thread, pc);
220	if (area != NULL) {
221		dprintf("<%s> 0x%" B_PRIxADDR, area->name, pc - area->Base());
222		return;
223	}
224
225	dprintf("0x%" B_PRIxADDR, pc);
226}
227
228
229void WritePC(addr_t pc)
230{
231	WritePCEx(thread_get_current_thread(), pc);
232}
233
234
235static status_t
236arch_debug_memcpy(void* dst, const void* src, size_t size)
237{
238	if (debug_debugger_running())
239		return debug_memcpy(B_CURRENT_TEAM, dst, src, size);
240
241	return user_memcpy(dst, src, size);
242}
243
244
245static void
246DumpMemory(uint64* adr, size_t len)
247{
248	while (len > 0) {
249		if ((addr_t)adr % 0x10 == 0)
250			dprintf("  %08" B_PRIxADDR " ", (addr_t)adr);
251		uint64 val;
252		if (arch_debug_memcpy(&val, adr++, sizeof(val)) < B_OK) {
253			dprintf(" ????????????????");
254		} else {
255			dprintf(" %016" B_PRIx64, val);
256		}
257		if ((addr_t)adr % 0x10 == 0)
258			dprintf("\n");
259		len -= 8;
260	}
261	if ((addr_t)adr % 0x10 != 0)
262		dprintf("\n");
263}
264
265
266static void
267DoStackTraceEx(Thread* thread, addr_t fp, addr_t pc)
268{
269	dprintf("Stack:\n");
270	dprintf("FP: 0x%" B_PRIxADDR, fp);
271	if (pc != 0) {
272		dprintf(", PC: "); WritePCEx(thread, pc);
273	}
274	dprintf("\n");
275	addr_t oldFp = fp;
276	int i = 0;
277	while (fp != 0 && i < 1000) {
278		if ((pc >= (addr_t)&strcpy && pc < (addr_t)&strcpy + 32)
279				|| (pc >= (addr_t)&memset && pc < (addr_t)&memset + 34)
280				|| (pc >= (addr_t)&memcpy && pc < (addr_t)&memcpy + 186)) {
281			if (arch_debug_memcpy(&fp, (uint64*)fp - 1, sizeof(pc)) < B_OK)
282				break;
283			pc = 0;
284		} else {
285			if (arch_debug_memcpy(&pc, (uint64*)fp - 1, sizeof(pc)) < B_OK)
286				break;
287			if (arch_debug_memcpy(&fp, (uint64*)fp - 2, sizeof(pc)) < B_OK)
288				break;
289		}
290		dprintf("FP: 0x%" B_PRIxADDR, fp);
291		dprintf(", PC: "); WritePCEx(thread, pc);
292		dprintf("\n");
293
294		if (pc == (addr_t)&SVecRet || pc == (addr_t)&SVecURet) {
295			WriteTrapInfo((iframe*)fp - 1);
296		}
297/*
298		if (IS_KERNEL_ADDRESS(oldFp) != IS_KERNEL_ADDRESS(fp))
299			oldFp = fp;
300		else if (fp != 0)
301			DumpMemory((uint64*)oldFp, (addr_t)fp - (addr_t)oldFp);
302*/
303		oldFp = fp;
304		i++;
305	}
306}
307
308
309void
310DoStackTrace(addr_t fp, addr_t pc)
311{
312	DoStackTraceEx(thread_get_current_thread(), fp, pc);
313}
314
315
316static int
317stack_trace(int argc, char **argv)
318{
319	if (argc >= 2) {
320		thread_id id = strtoul(argv[1], NULL, 0);
321		Thread* thread = Thread::GetDebug(id);
322		if (thread == NULL) {
323			kprintf("could not find thread %" B_PRId32 "\n", id);
324			return 0;
325		}
326
327		auto map = (RISCV64VMTranslationMap*)thread->team->address_space->TranslationMap();
328
329		uint64 oldSatp = Satp();
330		SetSatp(map->Satp());
331		FlushTlbAllAsid(0);
332
333		DebuggedThreadSetter threadSetter(thread);
334		DoStackTraceEx(thread, thread->arch_info.context.s[0], thread->arch_info.context.ra);
335
336		SetSatp(oldSatp);
337		FlushTlbAllAsid(0);
338
339		return 0;
340	}
341	DoStackTrace(Fp(), 0);
342	return 0;
343}
344
345
346void
347arch_debug_stack_trace(void)
348{
349	DoStackTrace(Fp(), 0);
350}
351
352
353bool
354arch_debug_contains_call(Thread *thread, const char *symbol,
355	addr_t start, addr_t end)
356{
357	return false;
358}
359
360
361void
362arch_debug_save_registers(struct arch_debug_registers* registers)
363{
364}
365
366
367static void __attribute__((naked))
368HandleFault()
369{
370	asm volatile("ld a0, 0(sp)");
371	asm volatile("li a1, 1");
372	asm volatile("call longjmp");
373}
374
375
376void
377arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer,
378	void (*function)(void*), void* parameter)
379{
380	cpu->fault_handler = (addr_t)&HandleFault;
381	cpu->fault_handler_stack_pointer = (addr_t)&jumpBuffer;
382	function(parameter);
383}
384
385
386bool
387arch_is_debug_variable_defined(const char* variableName)
388{
389	// TODO: Implement!
390	return false;
391}
392
393
394status_t
395arch_set_debug_variable(const char* variableName, uint64 value)
396{
397	// TODO: Implement!
398	return B_ENTRY_NOT_FOUND;
399}
400
401
402status_t
403arch_get_debug_variable(const char* variableName, uint64* value)
404{
405	// TODO: Implement!
406	return B_ENTRY_NOT_FOUND;
407}
408
409
410int32
411arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount,
412	int32 skipIframes, int32 skipFrames, uint32 flags)
413{
414	return 0;
415}
416
417
418ssize_t
419arch_debug_gdb_get_registers(char* buffer, size_t bufferSize)
420{
421	// TODO: Implement!
422	return B_NOT_SUPPORTED;
423}
424
425
426void*
427arch_debug_get_interrupt_pc(bool* _isSyscall)
428{
429	// TODO: Implement!
430	return NULL;
431}
432
433
434void
435arch_debug_unset_current_thread(void)
436{
437	// TODO: Implement!
438}
439
440
441status_t
442arch_debug_init_early(kernel_args *args)
443{
444	dprintf("arch_debug_init_early()\n");
445	sKernelArgs = args;
446	WriteImages();
447	return B_OK;
448}
449
450
451status_t
452arch_debug_init(kernel_args *args)
453{
454	sInitCalled = true;
455
456	add_debugger_command("where", &stack_trace, "Same as \"sc\"");
457	add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
458	add_debugger_command("sc", &stack_trace, "Stack crawl for current thread");
459
460	return B_OK;
461}
462