/*-
 * Copyright (c) 2016 The FreeBSD Foundation
 * All rights reserved.
 *
 * This software was developed by Konstantin Belousov under sponsorship
 * from the FreeBSD Foundation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: stable/11/stand/efi/loader/arch/amd64/exc.S 306738 2016-10-05 22:04:22Z kib $
 */

	.macro	EH	N, err=1
	.align	8
	.globl	EXC\N\()_handler
EXC\N\()_handler:
	.if	\err != 1
	pushq	$0
	.endif
	pushq	%rax
	pushq	%rdx
	pushq	%rcx
	movl	$\N,%ecx
	jmp	all_handlers
	.endm

	.text
	EH	0,0
	EH	1,0
	EH	2,0
	EH	3,0
	EH	4,0
	EH	5,0
	EH	6,0
	EH	7,0
	EH	8
	EH	9,0
	EH	10
	EH	11
	EH	12
	EH	13
	EH	14
	EH	16,0
	EH	17
	EH	18,0
	EH	19,0
	EH	20,0

	.globl	exc_rsp
all_handlers:
	cmpq	%rsp,exc_rsp(%rip)
	je	exception

	/*
	 * Interrupt, not exception.
	 * First, copy the hardware interrupt frame to the previous stack.
	 * Our handler always has private IST stack.
	 */
	movq	(6*8)(%rsp),%rax	/* saved %rsp value, AKA old stack */
	subq	(5*8),%rax
	movq	(3*8)(%rsp),%rdx	/* copy %rip to old stack */
	movq	%rdx,(%rax)
	movq	(4*8)(%rsp),%rdx	/* copy %cs */
	movq	%rdx,(1*8)(%rax)
	movq	(5*8)(%rsp),%rdx	/* copy %rflags */
	movq	%rdx,(2*8)(%rax)
	movq	(6*8)(%rsp),%rdx	/* copy %rsp */
	movq	%rdx,(3*8)(%rax)
	movq	(7*8)(%rsp),%rdx	/* copy %ss */
	movq	%rdx,(4*8)(%rax)

	/*
	 * Now simulate invocation of the original interrupt handler
	 * with retq.  We switch stacks and execute retq from the old
	 * stack since there is no free registers at the last moment.
	 */
	subq	$16,%rax
	leaq	fw_intr_handlers(%rip),%rdx
	movq	(%rdx,%rcx,8),%rdx /* push intr handler address on old stack */
	movq	%rdx,8(%rax)
	movq	(2*8)(%rsp),%rcx   /* saved %rax is put on top of old stack */
	movq	%rcx,(%rax)
	movq	(%rsp),%rcx
	movq	8(%rsp),%rdx

	movq	32(%rsp),%rsp	/* switch to old stack */
	popq	%rax
	retq

exception:
	/*
	 * Form the struct trapframe on our IST stack.
	 * Skip three words, which are currently busy with temporal
	 * saves.
	 */
	pushq	%r15
	pushq	%r14
	pushq	%r13
	pushq	%r12
	pushq	%r11
	pushq	%r10
	pushq	%rbp
	pushq	%rbx
	pushq	$0	/* %rax	*/
	pushq	%r9
	pushq	%r8
	pushq	$0	/* %rcx */
	pushq	$0	/* %rdx	*/
	pushq	%rsi
	pushq	%rdi

	/*
	 * Move %rax, %rdx, %rcx values into the final location,
	 * from the three words which were skipped above.
	 */
	movq	0x88(%rsp),%rax
	movq	%rax,0x30(%rsp)	/* tf_rax */
	movq	0x78(%rsp),%rax
	movq	%rax,0x18(%rsp)	/* tf_rcx */
	movq	0x80(%rsp),%rax
	movq	%rax,0x10(%rsp)	/* tf_rdx */

	/*
	 * And fill the three words themself.
	 */
	movq	%cr2,%rax
	movq	%rax,0x80(%rsp)	/* tf_addr */
	movl	%ecx,0x78(%rsp)	/* tf_trapno */
	movw	%ds,0x8e(%rsp)
	movw	%es,0x8c(%rsp)
	movw	%fs,0x7c(%rsp)
	movw	%gs,0x7e(%rsp)
	movw	$0,0x88(%rsp)	/* tf_flags */

	/*
	 * Call dump routine.
	 */
	movq	%rsp,%rdi
	callq	report_exc

	/*
	 * Hang after reporting. Interrupts are already disabled.
	 */
1:
	hlt
	jmp	1b
