/*
 * Interrupt/exception handling.
 */
#include "amd64.h"
#include "mem.h"

.code64

/* Interrupts.
 *
 * Let's just talk about hardware interrupts.
 * What has to happen is that we save enough state to
 * return to where we were, and that's all we do. Hardware needs a stack,
 * so it pushes SS and %rsp. Hardware has to
 * clear If, which means we have to save the flags. We might be
 * in user mode, so we need to change CS, so we need to save
 * CS. Finally, we have to know where we were, so we need to save
 * the RIP. And that's all get saved.
 * Further, if you look at idthandlers below, you see a call to intrp.
 * So on entry to intrp, the stack looks like this:
 * EFLAGS 24(%rsp)
 * CS	16(%rsp)
 * EIP of interrupted code 8(%rsp)
 * EIP from the call from the idthandlers. (%rsp)
 * We, finally, need to push the error code and type.,
 * then the registers.
 * Why the call from IDThandlers? So we can get a way to point
 * to the type. We don't need to save 0(%rsp), we just need
 * it to get the type. We can pop it and throw it away when
 * needed.
 * What does the C level function need?
 * It needs a pointer to the Uregs, as defined in ureg.h
 * The other problem: all the regs have to be saved, so
 * Plan 9 code can see them all.
 * Finally, if it's a nested interrupt, we need to know that
 * so we don't swapgs at the wrong time.
 * Finally, this is utterly different from how Plan 9 does it, because
 * all args are on the stack in Plan 9. We need %rdi for the pointer.
 * And, the final x86 mess: for some things, the error code pointer
 * is on the stack. For others, it's not. To make the stacks
 * look identical for the common code and exit we save %rax and line things up.
 */

// When we enter:
// registers are NOT saved. We need to save them all.
// return PC is on the stat8(%rsp). It should be left there.
// @ (%rsp) is the PC from the vector table.
// So indirecting on that will get us the interrupt #.
// We need to get what the return PC is pointing to into %rdi.
// We no longer need to make the stack look the same as in Plan 9
// because the arg is in %rdi
.globl _intrp
_intrp:
	pushq	%rax // bogus error code. Makes stack look like intre.
	// Except in this case, bogus error code is at 0(%rsp)
	// vno pointer is at 8(%rsp)
	movq	8(%rsp), %rax
	// Now %rax points to the vector number.
	jmp	_intrcommon

// For intre, error is at top of stack on trap. But then we call here
// from the interrupt vectors so error is at 8(%rsp).
// I just realized I think intre means interrupt exception, e.g. page fault.
.globl _intre
_intre:
///jmp	_intre
	// The error code has been pushed. How I love the x86.
	// So the error code is ABOVE the pointer to the vector #.
	xchgq	%rax, (%rsp)
	// Now %rax points to the vector number.

// When we get here:
// %RAX points to our vector number, i.e. "return" pc from calls below.
// For intrp, (%rsp) is bogus code, 8(%rsp) is pointer to vno
// for intre, (%rsp) is pointer to vno, 8(%rsp) is error code.
// The rest of the stack is the same.
_intrcommon:
	// Get the vector number into %al
	MOVb	(%rax), %al
	andq	$0xff, %rax
	// Put that at TOS (this is plan 9 argument style)
	xchgq	%rax, 0(%rsp)
	// 0(%rsp) now has the vno
	subq	$16, %rsp				/* R1[45] */
	cmpw	$KESEL, 40(%rsp) 	/* old CS */
	je	_intrnested

	movq	%r14, 0(%rsp)
	movq	%r15, 8(%rsp)

	swapgs

	movq	%gs:0, %r15			/* m-> (movq GS:0x0, R15) */
	movq	16(%r15), %r14			/* m->proc (set up->) */

_intrnested:
	pushq	%r13
	pushq	%r12
	pushq	%r11
	pushq	%r10
	pushq	%r9
	pushq	%r8
	pushq	%rbp
	pushq	%rdi
	pushq	%rsi
	pushq	%rdx
	pushq	%rcx
	pushq	%rbx
	pushq	%rax

	movq	%rsp, %rdi // it's ok, we saved %rdi.
	xorq	%rax, %rax
	/* if we came from user, stack traces end here */
	cmpw	$SSEL(SiCS, SsTIGDT|SsRPL0), 144(%rsp)
	cmovneq	%rax, %rbp
	pushq	%rax
	popfq				/* clear all flags. is there something else we should clear too? */
	call	trap
.globl _intrr
_intrr:
	popq	%rax
	popq	%rbx
	popq	%rcx
	popq	%rdx
	popq	%rsi
	popq	%rdi
	popq	%rbp
	popq	%r8
	popq	%r9
	popq	%r10
	popq	%r11
	popq	%r12
	popq	%r13

	cmpw	$KESEL, 40(%rsp) 	/* old CS */
	je	_iretnested

	swapgs
	movq	8(%rsp), %r15
	movq	0(%rsp), %r14

_iretnested:
	// Throw away:
	// The %rax you pushed (error code)
	// EIP from the vector table.
	addq	$32, %rsp
	iretq

.globl idthandlers
idthandlers:
	call _intrp; .byte IdtDE		/* #DE Divide-by-Zero Error */
	call _intrp; .byte IdtDB		/* #DB Debug */
	call _intrp; .byte IdtNMI		/* #NMI Borked */
	call _intrp; .byte IdtBP		/* #BP Breakpoint */
	call _intrp; .byte IdtOF		/* #OF Overflow */
	call _intrp; .byte IdtBR		/* #BR Bound-Range */
	call _intrp; .byte IdtUD		/* #UD Invalid-Opcode */
	call _intrp; .byte IdtNM		/* #NM Device-Not-Available */
	call _intre; .byte IdtDF		/* #DF Double-Fault */
	call _intrp; .byte Idt09		/* reserved */
	call _intre; .byte IdtTS		/* #TS Invalid-TSS */
	call _intre; .byte IdtNP		/* #NP Segment-Not-Present */
	call _intre; .byte IdtSS		/* #SS Stack */
	call _intre; .byte IdtGP		/* #GP General-Protection */
	call _intre; .byte IdtPF		/* #PF Page-Fault */
	call _intrp; .byte Idt0F		/* reserved */
	call _intrp; .byte IdtMF		/* #MF x87 FPE-Pending */
	call _intre; .byte IdtAC		/* #AC Alignment-Check */
	call _intrp; .byte IdtMC		/* #MC Machine-Check */
	call _intrp; .byte IdtXF		/* #XF SIMD Floating-Point */
	call _intrp; .byte 0x14		/* reserved */
	call _intrp; .byte 0x15		/* reserved */
	call _intrp; .byte 0x16		/* reserved */
	call _intrp; .byte 0x17		/* reserved */
	call _intrp; .byte 0x18		/* reserved */
	call _intrp; .byte 0x19		/* reserved */
	call _intrp; .byte 0x1a		/* reserved */
	call _intrp; .byte 0x1b		/* reserved */
	call _intrp; .byte 0x1c		/* reserved */
	call _intrp; .byte 0x1d		/* reserved */
	call _intrp; .byte 0x1e		/* reserved */
	call _intrp; .byte 0x1f		/* reserved */
	call _intrp; .byte 0x20
	call _intrp; .byte 0x21
	call _intrp; .byte 0x22
	call _intrp; .byte 0x23
	call _intrp; .byte 0x24
	call _intrp; .byte 0x25
	call _intrp; .byte 0x26
	call _intrp; .byte 0x27
	call _intrp; .byte 0x28
	call _intrp; .byte 0x29
	call _intrp; .byte 0x2a
	call _intrp; .byte 0x2b
	call _intrp; .byte 0x2c
	call _intrp; .byte 0x2d
	call _intrp; .byte 0x2e
	call _intrp; .byte 0x2f
	call _intrp; .byte 0x30
	call _intrp; .byte 0x31
	call _intrp; .byte 0x32
	call _intrp; .byte 0x33
	call _intrp; .byte 0x34
	call _intrp; .byte 0x35
	call _intrp; .byte 0x36
	call _intrp; .byte 0x37
	call _intrp; .byte 0x38
	call _intrp; .byte 0x39
	call _intrp; .byte 0x3a
	call _intrp; .byte 0x3b
	call _intrp; .byte 0x3c
	call _intrp; .byte 0x3d
	call _intrp; .byte 0x3e
	call _intrp; .byte 0x3f
	call _intrp; .byte 0x40
	call _intrp; .byte 0x41
	call _intrp; .byte 0x42
	call _intrp; .byte 0x43
	call _intrp; .byte 0x44
	call _intrp; .byte 0x45
	call _intrp; .byte 0x46
	call _intrp; .byte 0x47
	call _intrp; .byte 0x48
	call _intrp; .byte 0x49
	call _intrp; .byte 0x4a
	call _intrp; .byte 0x4b
	call _intrp; .byte 0x4c
	call _intrp; .byte 0x4d
	call _intrp; .byte 0x4e
	call _intrp; .byte 0x4f
	call _intrp; .byte 0x50
	call _intrp; .byte 0x51
	call _intrp; .byte 0x52
	call _intrp; .byte 0x53
	call _intrp; .byte 0x54
	call _intrp; .byte 0x55
	call _intrp; .byte 0x56
	call _intrp; .byte 0x57
	call _intrp; .byte 0x58
	call _intrp; .byte 0x59
	call _intrp; .byte 0x5a
	call _intrp; .byte 0x5b
	call _intrp; .byte 0x5c
	call _intrp; .byte 0x5d
	call _intrp; .byte 0x5e
	call _intrp; .byte 0x5f
	call _intrp; .byte 0x60
	call _intrp; .byte 0x61
	call _intrp; .byte 0x62
	call _intrp; .byte 0x63
	call _intrp; .byte 0x64
	call _intrp; .byte 0x65
	call _intrp; .byte 0x66
	call _intrp; .byte 0x67
	call _intrp; .byte 0x68
	call _intrp; .byte 0x69
	call _intrp; .byte 0x6a
	call _intrp; .byte 0x6b
	call _intrp; .byte 0x6c
	call _intrp; .byte 0x6d
	call _intrp; .byte 0x6e
	call _intrp; .byte 0x6f
	call _intrp; .byte 0x70
	call _intrp; .byte 0x71
	call _intrp; .byte 0x72
	call _intrp; .byte 0x73
	call _intrp; .byte 0x74
	call _intrp; .byte 0x75
	call _intrp; .byte 0x76
	call _intrp; .byte 0x77
	call _intrp; .byte 0x78
	call _intrp; .byte 0x79
	call _intrp; .byte 0x7a
	call _intrp; .byte 0x7b
	call _intrp; .byte 0x7c
	call _intrp; .byte 0x7d
	call _intrp; .byte 0x7e
	call _intrp; .byte 0x7f
	call _intrp; .byte 0x80
	call _intrp; .byte 0x81
	call _intrp; .byte 0x82
	call _intrp; .byte 0x83
	call _intrp; .byte 0x84
	call _intrp; .byte 0x85
	call _intrp; .byte 0x86
	call _intrp; .byte 0x87
	call _intrp; .byte 0x88
	call _intrp; .byte 0x89
	call _intrp; .byte 0x8a
	call _intrp; .byte 0x8b
	call _intrp; .byte 0x8c
	call _intrp; .byte 0x8d
	call _intrp; .byte 0x8e
	call _intrp; .byte 0x8f
	call _intrp; .byte 0x90
	call _intrp; .byte 0x91
	call _intrp; .byte 0x92
	call _intrp; .byte 0x93
	call _intrp; .byte 0x94
	call _intrp; .byte 0x95
	call _intrp; .byte 0x96
	call _intrp; .byte 0x97
	call _intrp; .byte 0x98
	call _intrp; .byte 0x99
	call _intrp; .byte 0x9a
	call _intrp; .byte 0x9b
	call _intrp; .byte 0x9c
	call _intrp; .byte 0x9d
	call _intrp; .byte 0x9e
	call _intrp; .byte 0x9f
	call _intrp; .byte 0xa0
	call _intrp; .byte 0xa1
	call _intrp; .byte 0xa2
	call _intrp; .byte 0xa3
	call _intrp; .byte 0xa4
	call _intrp; .byte 0xa5
	call _intrp; .byte 0xa6
	call _intrp; .byte 0xa7
	call _intrp; .byte 0xa8
	call _intrp; .byte 0xa9
	call _intrp; .byte 0xaa
	call _intrp; .byte 0xab
	call _intrp; .byte 0xac
	call _intrp; .byte 0xad
	call _intrp; .byte 0xae
	call _intrp; .byte 0xaf
	call _intrp; .byte 0xb0
	call _intrp; .byte 0xb1
	call _intrp; .byte 0xb2
	call _intrp; .byte 0xb3
	call _intrp; .byte 0xb4
	call _intrp; .byte 0xb5
	call _intrp; .byte 0xb6
	call _intrp; .byte 0xb7
	call _intrp; .byte 0xb8
	call _intrp; .byte 0xb9
	call _intrp; .byte 0xba
	call _intrp; .byte 0xbb
	call _intrp; .byte 0xbc
	call _intrp; .byte 0xbd
	call _intrp; .byte 0xbe
	call _intrp; .byte 0xbf
	call _intrp; .byte 0xc0
	call _intrp; .byte 0xc1
	call _intrp; .byte 0xc2
	call _intrp; .byte 0xc3
	call _intrp; .byte 0xc4
	call _intrp; .byte 0xc5
	call _intrp; .byte 0xc6
	call _intrp; .byte 0xc7
	call _intrp; .byte 0xc8
	call _intrp; .byte 0xc9
	call _intrp; .byte 0xca
	call _intrp; .byte 0xcb
	call _intrp; .byte 0xcc
	call _intrp; .byte 0xce
	call _intrp; .byte 0xce
	call _intrp; .byte 0xcf
	call _intrp; .byte 0xd0
	call _intrp; .byte 0xd1
	call _intrp; .byte 0xd2
	call _intrp; .byte 0xd3
	call _intrp; .byte 0xd4
	call _intrp; .byte 0xd5
	call _intrp; .byte 0xd6
	call _intrp; .byte 0xd7
	call _intrp; .byte 0xd8
	call _intrp; .byte 0xd9
	call _intrp; .byte 0xda
	call _intrp; .byte 0xdb
	call _intrp; .byte 0xdc
	call _intrp; .byte 0xdd
	call _intrp; .byte 0xde
	call _intrp; .byte 0xdf
	call _intrp; .byte 0xe0
	call _intrp; .byte 0xe1
	call _intrp; .byte 0xe2
	call _intrp; .byte 0xe3
	call _intrp; .byte 0xe4
	call _intrp; .byte 0xe5
	call _intrp; .byte 0xe6
	call _intrp; .byte 0xe7
	call _intrp; .byte 0xe8
	call _intrp; .byte 0xe9
	call _intrp; .byte 0xea
	call _intrp; .byte 0xeb
	call _intrp; .byte 0xec
	call _intrp; .byte 0xed
	call _intrp; .byte 0xee
	call _intrp; .byte 0xef
	call _intrp; .byte 0xf0
	call _intrp; .byte 0xf1
	call _intrp; .byte 0xf2
	call _intrp; .byte 0xf3
	call _intrp; .byte 0xf4
	call _intrp; .byte 0xf5
	call _intrp; .byte 0xf6
	call _intrp; .byte 0xf7
	call _intrp; .byte 0xf8
	call _intrp; .byte 0xf9
	call _intrp; .byte 0xfa
	call _intrp; .byte 0xfb
	call _intrp; .byte 0xfc
	call _intrp; .byte 0xfd
	call _intrp; .byte 0xfe
	call _intrp; .byte 0xff
