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

#ifndef ZEPHYR_INCLUDE_ARCH_X86_INTEL64_THREAD_H_
#define ZEPHYR_INCLUDE_ARCH_X86_INTEL64_THREAD_H_

#define X86_THREAD_FLAG_ALL 0x01 /* _thread_arch.flags: entire state saved */

/*
 * GDT selectors - these must agree with the GDT layout in locore.S.
 */

#define X86_KERNEL_CS_32	0x08	/* 32-bit kernel code */
#define X86_KERNEL_DS_32	0x10	/* 32-bit kernel data */
#define X86_KERNEL_CS		0x18	/* 64-bit kernel code */
#define X86_KERNEL_DS		0x20	/* 64-bit kernel data */
#define X86_USER_CS_32		0x28	/* 32-bit user data (unused) */
#define X86_USER_DS		0x30	/* 64-bit user mode data */
#define X86_USER_CS		0x38	/* 64-bit user mode code */

/* Value programmed into bits 63:32 of STAR MSR with proper segment
 * descriptors for implementing user mode with syscall/sysret
 */
#define X86_STAR_UPPER		((X86_USER_CS_32 << 16) | X86_KERNEL_CS)

#define X86_KERNEL_CPU0_TR	0x40	/* 64-bit task state segment */
#define X86_KERNEL_CPU1_TR	0x50	/* 64-bit task state segment */
#define X86_KERNEL_CPU2_TR	0x60	/* 64-bit task state segment */
#define X86_KERNEL_CPU3_TR	0x70	/* 64-bit task state segment */

/*
 * Some SSE definitions. Ideally these will ultimately be shared with 32-bit.
 */

#define X86_FXSAVE_SIZE		512	/* size and alignment of buffer ... */
#define X86_FXSAVE_ALIGN	16	/* ... for FXSAVE/FXRSTOR ops */

/* MXCSR Control and Status Register for SIMD floating-point operations.
 * Set default value 1F80H according to the Intel(R) 64 and IA-32 Manual.
 * Disable denormals-are-zeros mode.
 */
#define X86_MXCSR_SANE		0x1f80

#ifndef _ASMLANGUAGE

#include <zephyr/types.h>
#include <arch/x86/mmustructs.h>

/*
 * 64-bit Task State Segment. One defined per CPU.
 */

struct x86_tss64 {
	/*
	 * Architecturally-defined portion. It is somewhat tedious to
	 * enumerate each member specifically (rather than using arrays)
	 * but we need to get (some of) their offsets from assembly.
	 */

	uint8_t reserved0[4];

	uint64_t rsp0;		/* privileged stacks */
	uint64_t rsp1;
	uint64_t rsp2;

	uint8_t reserved[8];

	uint64_t ist1;		/* interrupt stacks */
	uint64_t ist2;
	uint64_t ist3;
	uint64_t ist4;
	uint64_t ist5;
	uint64_t ist6;
	uint64_t ist7;

	uint8_t reserved1[10];

	uint16_t iomapb;		/* offset to I/O base */

	/*
	 * Zephyr specific portion. Stash per-CPU data here for convenience.
	 */

	struct _cpu *cpu;
#ifdef CONFIG_USERSPACE
	/* Privilege mode stack pointer value when doing a system call */
	char *psp;

	/* Storage area for user mode stack pointer when doing a syscall */
	char *usp;
#endif /* CONFIG_USERSPACE */
} __packed __aligned(8);

typedef struct x86_tss64 x86_tss64_t;

/*
 * The _callee_saved registers are unconditionally saved/restored across
 * context switches; the _thread_arch registers are only preserved when
 * the thread is interrupted. _arch_thread.flags tells __resume when to
 * cheat and only restore the first set. For more details see locore.S.
 */

struct _callee_saved {
	uint64_t rsp;
	uint64_t rbx;
	uint64_t rbp;
	uint64_t r12;
	uint64_t r13;
	uint64_t r14;
	uint64_t r15;
	uint64_t rip;
	uint64_t rflags;
};

typedef struct _callee_saved _callee_saved_t;

struct _thread_arch {
	uint8_t flags;

#ifdef CONFIG_USERSPACE
#ifndef CONFIG_X86_COMMON_PAGE_TABLE
	/* Physical address of the page tables used by this thread */
	uintptr_t ptables;
#endif /* CONFIG_X86_COMMON_PAGE_TABLE */

	/* Initial privilege mode stack pointer when doing a system call.
	 * Un-set for supervisor threads.
	 */
	char *psp;

	/* SS and CS selectors for this thread when restoring context */
	uint64_t ss;
	uint64_t cs;
#endif

	uint64_t rax;
	uint64_t rcx;
	uint64_t rdx;
	uint64_t rsi;
	uint64_t rdi;
	uint64_t r8;
	uint64_t r9;
	uint64_t r10;
	uint64_t r11;
	char __aligned(X86_FXSAVE_ALIGN) sse[X86_FXSAVE_SIZE];
};

typedef struct _thread_arch _thread_arch_t;

#endif /* _ASMLANGUAGE */

#endif /* ZEPHYR_INCLUDE_ARCH_X86_INTEL64_THREAD_H_ */
