/* arch-eclair.c COPYRIGHT FUJITSU LIMITED 2016-2018 */
#include <stdio.h>
#include <eclair.h>
#include <arch-eclair.h>
#include <sys/ioctl.h>
#include <ihk/ihk_host_user.h>

uint64_t PHYS_OFFSET;

int print_kregs(char *rbp, size_t rbp_size, const struct arch_kregs *kregs)
{
	int i, ret, total = 0;
	uint32_t pstate;
	const unsigned long *regs[] = {&kregs->x19, &kregs->x20, &kregs->x21,
				       &kregs->x22, &kregs->x23, &kregs->x24,
				       &kregs->x25, &kregs->x26, &kregs->x27,
				       &kregs->x28};

	for (i = 0; i <= 18; i++)	/* x0-x18 */{
		ret = snprintf(rbp, rbp_size, "xxxxxxxxxxxxxxxx");
		if (ret < 0) {
			return ret;
		}
		rbp += ret;
		total += ret;
		rbp_size -= ret;
	}

	for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) {	/* x19-x28 */
		ret = print_bin(rbp, rbp_size, (void *)regs[i], sizeof(*regs[0]));
		if (ret < 0) {
			return ret;
		}
		rbp += ret;
		total += ret;
		rbp_size -= ret;
	}

	// X29 FP
	ret = print_bin(rbp, rbp_size, (void *)&kregs->fp, sizeof(kregs->fp));
	if (ret < 0) {
		return ret;
	}
	total += ret;
	dprintf("%s: FP: %s, kregs->fp: 0x%lx\n", __func__, rbp, kregs->fp);
	rbp += ret;
	rbp_size -= ret;

	// x30 LR
	for (i = 0; i < 1; i++)	{
		ret = snprintf(rbp, rbp_size, "xxxxxxxxxxxxxxxx");
		if (ret < 0) {
			return ret;
		}
		rbp += ret;
		total += ret;
		rbp_size -= ret;
	}

	// X31 SP
	ret = print_bin(rbp, rbp_size, (void *)&kregs->sp, sizeof(kregs->sp));
	if (ret < 0) {
		return ret;
	}
	total += ret;
	dprintf("%s: SP: %s, kregs->sp: 0x%lx\n", __func__, rbp, kregs->sp);
	rbp += ret;
	rbp_size -= ret;

	// X32 PC
	ret = print_bin(rbp, rbp_size, (void *)&kregs->pc, sizeof(kregs->pc));
	if (ret < 0) {
		return ret;
	}
	total += ret;
	dprintf("%s: PC: %s, kregs->pc: 0x%lx\n", __func__, rbp, kregs->pc);
	rbp += ret;
	rbp_size -= ret;

	// PSTATE
#define PSR_MODE_EL1h   0x00000005
	pstate = PSR_MODE_EL1h;
	ret = print_bin(rbp, rbp_size, (void *)&pstate, sizeof(pstate));
	if (ret < 0) {
		return ret;
	}
	total += ret;
	rbp += ret;
	rbp_size -= ret;

	dprintf("%s: total: %d\n", __func__, total);
	return total;
}

uintptr_t virt_to_phys(uintptr_t va)
{
	extern uintptr_t kernel_base;

	if (va >= MAP_ST) {
		return (va - MAP_ST + PHYS_OFFSET);
	}

	if (va >= MAP_KERNEL_START) {
		return (va - MAP_KERNEL_START + kernel_base);
	}

	return NOPHYS;
} /* virt_to_phys() */

int arch_setup_constants(int mcos_fd)
{
	int error;
	dumpargs_t args;

	MAP_KERNEL_START = lookup_symbol("_head");
	if (MAP_KERNEL_START == NOSYMBOL) {
		fprintf(stderr, "error: obtaining MAP_KERNEL_START\n");
		return 1;
	}
	printf("arm64 MAP_KERNEL_START 0x%lx\n", MAP_KERNEL_START);

	args.cmd = DUMP_QUERY_PHYS_START;
	args.buf = &PHYS_OFFSET;

	error = ioctl(mcos_fd, IHK_OS_DUMP, &args);
	return error;
}

/*
 * NOTE: in ARM64, ctx is a pointer to thread_info, in which
 * cpu_context is the real member we are looking for thus an extra
 * indirection is needed.
 */
int arch_read_kregs(unsigned long ctx, struct arch_kregs *kregs)
{
	int error;
	error = read_mem(ctx, &ctx, sizeof(ctx));
	if (error) {
		return error;
	}

	/*
	 * TODO: the 16 below is offsetof(struct thread_info, cpu_context)
	 * add this to debug_constants or move the whole thing over DWARF
	 * based inspection...
	 */
	return read_mem(ctx + 16, kregs, sizeof(*kregs));
}
