/*
 * Copyright (c) 2020 Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/debug/coredump.h>

#define ARCH_HDR_VER			1

struct x86_arch_block {
	uint32_t	vector;
	uint32_t	code;

	struct {
		uint32_t	eax;
		uint32_t	ecx;
		uint32_t	edx;
		uint32_t	ebx;
		uint32_t	esp;
		uint32_t	ebp;
		uint32_t	esi;
		uint32_t	edi;
		uint32_t	eip;
		uint32_t	eflags;
		uint32_t	cs;
	} r;
} __packed;

/*
 * This might be too large for stack space if defined
 * inside function. So do it here.
 */
static struct x86_arch_block arch_blk;

void arch_coredump_info_dump(const struct arch_esf *esf)
{
	struct coredump_arch_hdr_t hdr = {
		.id = COREDUMP_ARCH_HDR_ID,
		.hdr_version = ARCH_HDR_VER,
		.num_bytes = sizeof(arch_blk),
	};

	/* Nothing to process */
	if (esf == NULL) {
		return;
	}

	(void)memset(&arch_blk, 0, sizeof(arch_blk));

	arch_blk.vector = z_x86_exception_vector;
	arch_blk.code = esf->errorCode;

	/*
	 * 16 registers expected by GDB.
	 * Not all are in ESF but the GDB stub
	 * will need to send all 16 as one packet.
	 * The stub will need to send undefined
	 * for registers not presented in coredump.
	 */
	arch_blk.r.eax = esf->eax;
	arch_blk.r.ebx = esf->ebx;
	arch_blk.r.ecx = esf->ecx;
	arch_blk.r.edx = esf->edx;
	arch_blk.r.esp = esf->esp;
	arch_blk.r.ebp = esf->ebp;
	arch_blk.r.esi = esf->esi;
	arch_blk.r.edi = esf->edi;
	arch_blk.r.eip = esf->eip;
	arch_blk.r.eflags = esf->eflags;
	arch_blk.r.cs = esf->cs & 0xFFFFU;

	/* Send for output */
	coredump_buffer_output((uint8_t *)&hdr, sizeof(hdr));
	coredump_buffer_output((uint8_t *)&arch_blk, sizeof(arch_blk));
}

uint16_t arch_coredump_tgt_code_get(void)
{
	return COREDUMP_TGT_X86;
}

#if defined(CONFIG_DEBUG_COREDUMP_DUMP_THREAD_PRIV_STACK)
void arch_coredump_priv_stack_dump(struct k_thread *thread)
{
	struct z_x86_thread_stack_header *hdr_stack_obj;
	uintptr_t start_addr, end_addr;

#if defined(CONFIG_THREAD_STACK_MEM_MAPPED)
	hdr_stack_obj = (struct z_x86_thread_stack_header *)thread->stack_info.mapped.addr;
#else
	hdr_stack_obj = (struct z_x86_thread_stack_header *)thread->stack_obj;
#endif /* CONFIG_THREAD_STACK_MEM_MAPPED) */

	start_addr = (uintptr_t)&hdr_stack_obj->privilege_stack[0];
	end_addr = start_addr + sizeof(hdr_stack_obj->privilege_stack);

	coredump_memory_dump(start_addr, end_addr);
}
#endif /* CONFIG_DEBUG_COREDUMP_DUMP_THREAD_PRIV_STACK */
