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

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

#define ARCH_HDR_VER			2

uint32_t z_arm_coredump_fault_sp;

struct arm_arch_block {
	struct {
		uint32_t	r0;
		uint32_t	r1;
		uint32_t	r2;
		uint32_t	r3;
		uint32_t	r12;
		uint32_t	lr;
		uint32_t	pc;
		uint32_t	xpsr;
		uint32_t	sp;

		/* callee registers - optionally collected in V2 */
		uint32_t	r4;
		uint32_t	r5;
		uint32_t	r6;
		uint32_t	r7;
		uint32_t	r8;
		uint32_t	r9;
		uint32_t	r10;
		uint32_t	r11;
	} r;
} __packed;

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

void arch_coredump_info_dump(const z_arch_esf_t *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));

	/*
	 * 17 registers expected by GDB.
	 * Not all are in ESF but the GDB stub
	 * will need to send all 17 as one packet.
	 * The stub will need to send undefined
	 * for registers not presented in coredump.
	 */
	arch_blk.r.r0 = esf->basic.r0;
	arch_blk.r.r1 = esf->basic.r1;
	arch_blk.r.r2 = esf->basic.r2;
	arch_blk.r.r3 = esf->basic.r3;
	arch_blk.r.r12 = esf->basic.ip;
	arch_blk.r.lr = esf->basic.lr;
	arch_blk.r.pc = esf->basic.pc;
	arch_blk.r.xpsr = esf->basic.xpsr;

	arch_blk.r.sp = z_arm_coredump_fault_sp;

#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
	if (esf->extra_info.callee) {
		arch_blk.r.r4  = esf->extra_info.callee->v1;
		arch_blk.r.r5  = esf->extra_info.callee->v2;
		arch_blk.r.r6  = esf->extra_info.callee->v3;
		arch_blk.r.r7  = esf->extra_info.callee->v4;
		arch_blk.r.r8  = esf->extra_info.callee->v5;
		arch_blk.r.r9  = esf->extra_info.callee->v6;
		arch_blk.r.r10 = esf->extra_info.callee->v7;
		arch_blk.r.r11 = esf->extra_info.callee->v8;
	}
#endif

	/* 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_ARM_CORTEX_M;
}
