/*
 * This file is part of the libpayload project.
 *
 * Copyright 2013 Google Inc.
 *
 * 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.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 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.
 */

	.align 4
	.global exception_stack_end
exception_stack_end:
	.long 0

/* Some temporary variables which are used while saving exception state. */
vector:
	.long 0
error_code:
	.long 0
old_esp:
	.long 0
old_eax:
	.long 0

	.align 8

/*
 * Each exception vector has a small stub associated with it which sets aside
 * the error code, if any, records which vector we entered from, and calls
 * the common exception entry point. Some exceptions have error codes and some
 * don't, so we have a macro for each type.
 */

	.macro stub num
exception_stub_\num:
	movl	$0, error_code
	movl	$\num, vector
	jmp	exception_common
	.endm

	.macro stub_err num
exception_stub_\num:
	popl	error_code
	movl	$\num, vector
	jmp	exception_common
	.endm

	stub 0
	stub 1
	stub 2
	stub 3
	stub 4
	stub 5
	stub 6
	stub 7
	stub_err 8
	stub 9
	stub_err 10
	stub_err 11
	stub_err 12
	stub_err 13
	stub_err 14
	stub 15
	stub 16
	stub_err 17
	stub 18
	stub 19
	stub 20
	stub 21
	stub 22
	stub 23
	stub 24
	stub 25
	stub 26
	stub 27
	stub 28
	stub 29
	stub_err 30
	stub 31

exception_common:
	/*
	 * Save off the stack pointer and old eax value and install the
	 * exception stack. eax points to the old stack which has the
	 * exception ip, cs, and flags.
	 */
	mov	%esp, old_esp
	addl	$12, old_esp
	mov	%eax, old_eax
	mov	%esp, %eax
	mov	exception_stack_end, %esp

	/*
	 * Push values onto the top of the exception stack to form an
	 * exception state structure.
	 */
	pushl	vector
	pushl	error_code
	pushl	%gs
	pushl	%fs
	pushl	%es
	pushl	%ds
	pushl	%ss
	pushl	4(%eax)
	pushl	8(%eax)
	pushl	(%eax)
	pushl	%edi
	pushl	%esi
	pushl	%ebp
	pushl	old_esp
	pushl	%ebx
	pushl	%edx
	pushl	%ecx
	pushl	old_eax

	/*
	 * Call the C exception handler. It will find the exception state on
	 * the exception stack. Not passing parameters means we don't have to
	 * worry about what ABI is being used.
	 */
	call	exception_handler

	/*
	 * Restore state from the exception state structure, including any
	 * changes that might have been made.
	 */
	popl	old_eax
	popl	%ecx
	popl	%edx
	popl	%ebx
	popl	old_esp

	mov	old_esp, %eax
	subl	$12, %eax

	popl	%ebp
	popl	%esi
	popl	%edi
	popl	(%eax)
	popl	8(%eax)
	popl	4(%eax)
	popl	%ss
	popl	%ds
	popl	%es
	popl	%fs
	popl	%gs

	mov	%eax, %esp
	mov	old_eax, %eax

	/* Return from the exception. */
	iretl

/*
 * We need segment selectors for the IDT, so we need to know where things are
 * in the GDT. We set one up here which is pretty standard and largely copied
 * from coreboot.
 */
	.align 8
gdt:
	/* selgdt 0, unused */
	.word 0x0000, 0x0000
	.byte 0x00, 0x00, 0x00, 0x00

	/* selgdt 8, unused */
	.word 0x0000, 0x0000
	.byte 0x00, 0x00, 0x00, 0x00

	/* selgdt 0x10, flat 4GB code segment */
        .word 0xffff, 0x0000
	.byte 0x00, 0x9b, 0xcf, 0x00

	/* selgdt 0x18, flat 4GB data segment */
	.word 0xffff, 0x0000
	.byte 0x00, 0x93, 0xcf, 0x00
gdt_end:

/* GDT pointer for use with lgdt */
gdt_ptr:
	.word	gdt_end - gdt - 1
	.long	gdt

	/*
	 * Record the target and construct the actual entry at init time. This
	 * is necessary because the linker doesn't want to construct the entry
	 * for us.
	 */
	.macro interrupt_gate target
	.long \target
	.long \target
	.endm

	.align 8
	.global	idt
idt:
	interrupt_gate exception_stub_0
	interrupt_gate exception_stub_1
	interrupt_gate exception_stub_2
	interrupt_gate exception_stub_3
	interrupt_gate exception_stub_4
	interrupt_gate exception_stub_5
	interrupt_gate exception_stub_6
	interrupt_gate exception_stub_7
	interrupt_gate exception_stub_8
	interrupt_gate exception_stub_9
	interrupt_gate exception_stub_10
	interrupt_gate exception_stub_11
	interrupt_gate exception_stub_12
	interrupt_gate exception_stub_13
	interrupt_gate exception_stub_14
	interrupt_gate exception_stub_15
	interrupt_gate exception_stub_16
	interrupt_gate exception_stub_17
	interrupt_gate exception_stub_18
	interrupt_gate exception_stub_19
	interrupt_gate exception_stub_20
	interrupt_gate exception_stub_21
	interrupt_gate exception_stub_22
	interrupt_gate exception_stub_23
	interrupt_gate exception_stub_24
	interrupt_gate exception_stub_25
	interrupt_gate exception_stub_26
	interrupt_gate exception_stub_27
	interrupt_gate exception_stub_28
	interrupt_gate exception_stub_29
	interrupt_gate exception_stub_30
	interrupt_gate exception_stub_31
idt_end:

/* IDT pointer for use with lidt */
idt_ptr:
	.word idt_end - idt - 1
	.long idt

	.global exception_init_asm
exception_init_asm:
	/* Save eax so we can use it as a temporary variable. */
	pushl	%eax

	/* Install the GDT. */
	lgdt	gdt_ptr
	/* Load the segment registers from it. */
	ljmp	$0x10, $1f
1:	movl	$0x18, %eax
	movl	%eax, %ds
	movl	%eax, %es
	movl	%eax, %ss
	movl	%eax, %fs
	movl	%eax, %gs

	/*
	 * Loop over the entries which start out as two copies of the target
	 * address. We can turn them into real interrupt gates by selectively
	 * replacing certain bit fields.
	 */
	movl	$idt, %eax
1:
	andl	$0x0000ffff, (%eax)
	orl	$0x00100000, (%eax)
	andl	$0xffff0000, 4(%eax)
	orl	$0x0000ee00, 4(%eax)
	addl	$8, %eax
	cmp	$idt_end, %eax
	jne	1b

	/* Install the IDT. */
	lidt	idt_ptr

	/* Restore eax and return to the caller. */
	popl	%eax
	ret
