; =============================================================================
; Pure64 -- a 64-bit OS loader written in Assembly for x86-64 systems
; Copyright (C) 2008-2011 Return Infinity -- see LICENSE.TXT
;
; INIT SMP
; =============================================================================

;MP_debugmsg: db 'MP_CODE!', 0


smp_setup:
	sti				; Enable the timer

; Step 1: Get APIC Information via ACPI
smp_check_for_acpi:			; Look for the Root System Description Pointer Structure
	mov rsi, 0x00000000000E0000	; We want to start looking here
	mov rbx, 'RSD PTR '		; This in the Signature for the ACPI Structure Table (0x2052545020445352)
searchingforACPI:
	lodsq				; Load a quad word from RSI and store in RAX, then increment RSI by 8
	cmp rax, rbx
	je foundACPI
	cmp rsi, 0x00000000000FFFFF	; Keep looking until we get here
	jge noMP			; We can't find ACPI either.. bail out and default to single cpu mode
	jmp searchingforACPI 

foundACPI:
	jmp init_smp_acpi 

makempgonow:

; Step 2: Enable Local APIC on BSP
	mov rsi, [os_LocalAPICAddress]
	cmp rsi, 0x00000000
	je noMP				; Skip MP init if we didn't get a valid LAPIC address
	add rsi, 0xf0			; Offset to Spurious Interrupt Register
	mov rdi, rsi
	lodsd
	or eax, 0000000100000000b
	stosd

; Check if we want the AP's to be enabled.. if not then skip to end
;	cmp byte [cfg_smpinit], 1	; Check if SMP should be enabled
;	jne noMP			; If not then skip SMP init

; Step 3: Start the AP's one by one
	xor eax, eax
	xor ecx, ecx
	xor edx, edx
	mov rsi, [os_LocalAPICAddress]
	add rsi, 0x20		; Add the offset for the APIC ID location
	lodsd			; APIC ID is stored in bits 31:24
	shr rax, 24		; AL now holds the BSP CPU's APIC ID
	mov dl, al		; Store BSP APIC ID in DL
	mov rsi, 0x000000000000F800
	xor eax, eax

nextcore:
	cmp rsi, 0x000000000000F900
	je done
	lodsb
	cmp al, 1		; Is it enabled?
	jne skipcore

	push rax
	mov al, cl
	add al, 48
	call os_print_char
	pop rax

	cmp cl, dl		; Is it the BSP?
	je skipcore

; Broadcast 'INIT' IPI to APIC ID in AL
	mov al, cl
	shl eax, 24
	mov rdi, [os_LocalAPICAddress]
	add rdi, 0x310
	stosd
	mov eax, 0x00004500
	mov rdi, [os_LocalAPICAddress]
	add rdi, 0x300
	stosd
	push rsi
verifyinit:
	mov rsi, [os_LocalAPICAddress]
	add rsi, 0x300
	lodsd
	bt eax, 12			; Verify that the command completed
	jc verifyinit
	pop rsi

	mov rax, [os_Counter]
	add rax, 10
wait1:
	mov rbx, [os_Counter]
	cmp rax, rbx
	jg wait1

; Broadcast 'Startup' IPI to destination using vector 0x0A to specify entry-point is at the memory-address 0x0000A000
	mov al, cl
	shl eax, 24
	mov rdi, [os_LocalAPICAddress]
	add rdi, 0x310
	stosd
	mov eax, 0x0000460A		; Vector 0x0A
	mov rdi, [os_LocalAPICAddress]
	add rdi, 0x300
	stosd
	push rsi
verifystartup1:
	mov rsi, [os_LocalAPICAddress]
	add rsi, 0x300
	lodsd
	bt eax, 12			; Verify that the command completed
	jc verifystartup1
	pop rsi

	mov rax, [os_Counter]
	add rax, 2
wait2:
	mov rbx, [os_Counter]
	cmp rax, rbx
	jg wait2

skipcore:
	inc cl
	jmp nextcore

done:

; Let things settle (Give the AP's some time to finish)
	mov rax, [os_Counter]
	add rax, 10
wait3:
	mov rbx, [os_Counter]
	cmp rax, rbx
	jg wait3

; Step 4: Prepare the IOAPIC
; To be coded...

; Step 5: Finish up

noMP:
	lock
	inc word [cpu_activated]	; BSP adds one here

	xor eax, eax
	mov rsi, [os_LocalAPICAddress]
	add rsi, 0x20			; Add the offset for the APIC ID location
	lodsd				; APIC ID is stored in bits 31:24
	shr rax, 24			; AL now holds the CPU's APIC ID (0 - 255)
	mov rdi, 0x0000F700		; The location where the cpu values are stored
	add rdi, rax			; RDI points to infomap CPU area + APIC ID. ex F701 would be APIC ID 1
	mov al, 3			; This is the BSP so bits 0 and 1 are set
	stosb

	cli				; Disable the timer

ret


%include "init_smp_acpi.asm"


; =============================================================================
; EOF
