/*
 * Start-up request IPI handler.
 *
 * This code is executed on an application processor in response to receiving
 * a Start-up IPI (SIPI) from another processor.
 * This must be placed on a 4KiB boundary
 * somewhere in the 1st MiB of conventional memory. However,
 * due to some shortcuts below it's restricted further to within the 1st 64KiB.
 * The AP starts in real-mode, with
 *   CS selector set to the startup memory address/16;
 *   CS base set to startup memory address;
 *   CS limit set to 64KiB;
 *   CPL and IP set to 0.
 */
#include "mem.h"
#include "amd64.h"
#ifndef __ASSEMBLER__
#define __ASSEMBLER__
#endif

.section .text
.code16

.align 4
apbootstrap:
	ljmp	$0x0, $_apbootstrap
	nop
	nop
	nop
_apvector:			/* address APBOOTSTRAP+0x08 */
	.quad	0
_appml4:			/* address APBOOTSTRAP+0x10 */
	.quad	0
_apapic:			/* address APBOOTSTRAP+0x18 */
	.quad	0
_apmach:			/* address APBOOTSTRAP+0x20 */
	.quad	0
_apbootstrap:
	mov	%cs, %ax
	mov	%ax, %ds
	lgdt	_gdtptr32p
	movl	%cr0, %eax
	or	$0x1, %ax
	mov	%eax, %cr0
	jmp	1f
1:
	ljmp	$0x18, $_ap32

.code32
_ap32:
	movl	$SELECTOR(2, SELGDT, 0), %eax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movw	%ax, %ss

	movl	_appml4, %eax		/* physical address of PML4 */
	movl	%eax, %cr3		/* load the mmu */
	jmp	2f
2:
	movl	%cr4, %eax
	andl	$0xffffffef, %eax	/* Page Size (~0x00000010 = 0xffffffef) */
	orl	$0xa0, %eax		/* Page Global, Phys. Address */
	movl	%eax, %cr4

	movl	$0xc0000080, %ecx	/* Extended Feature Enable */
	rdmsr
	orl	$0x00000100, %eax	/* Long Mode Enable */
	wrmsr

	movl	%cr0, %edx
	andl	$0x9ffffff5, %edx	/* (~0x6000000a = 0x9ffffff5) */
	orl	$0x80010000, %edx	/* Paging Enable, Write Protect */
	movl	%edx, %cr0

	ljmp	$0x8, $(_ap64)

.code64
_ap64:
	movq	$_gdtptr64v, %rax
	lgdt	(%rax)

	xorq	%rax, %rax
	movw	%ax, %ds		/* not used in long mode */
	movw	%ax, %es		/* not used in long mode */
	movw	%ax, %fs
	movw	%ax, %gs
	movw	%ax, %ss		/* not used in long mode */

	lldt	%ax

	movq	_apmach, %rsp

	movq	%rax, %r14		/* up = nil; */
	movq	%rsp, %r15		/* m = apmach */

	addq	$MACHSIZE, %rsp

	pushq	%rax			/* clear flags */
	popfq

	movq	_apvector, %rax
	movq	_apapic, %rdi
	pushq	%rbp

	call	*%rax

_halt:
	hlt
	jmp	_halt

.align 16
_gdt:
	/* null descriptor */
	.long	0
	.long	0

	/* (KESEG) 64 bit long mode exec segment */
	.long	0xFFFF
	.long	SEGL|SEGG|SEGP|(0xF<<16)|SEGPL(0)|SEGEXEC|SEGR

	/* 32 bit data segment descriptor for 4 gigabytes (PL 0) */
	.long	0xFFFF
	.long	SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW

	/* 32 bit exec segment descriptor for 4 gigabytes (PL 0) */
	.long	0xFFFF
	.long	SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR

.align 4
_gdtptr32p:
	.word	4*8-1
	.long	_gdt-KZERO

.align 4
_gdtptr64p:
	.word	4*8-1
	.quad	_gdt-KZERO

.align 4
_gdtptr64v:
	.word	4*8-1
	.quad	_gdt
