mirror of https://gitlab.com/nakst/essence
				
				
				
			
		
			
				
	
	
		
			592 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
			
		
		
	
	
			592 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
| ; This file is part of the Essence operating system.
 | |
| ; It is released under the terms of the MIT license -- see LICENSE.md.
 | |
| ; Written by: nakst.
 | |
| 
 | |
| [bits 32]
 | |
| 
 | |
| [global ProcessorReset]
 | |
| [global ProcessorDebugOutputByte]
 | |
| [global ProcessorIn16]
 | |
| [global ProcessorIn32]
 | |
| [global ProcessorIn8]
 | |
| [global ProcessorOut16]
 | |
| [global ProcessorOut32]
 | |
| [global ProcessorOut8]
 | |
| [global ProcessorHalt]
 | |
| [global ProcessorDisableInterrupts]
 | |
| [global ProcessorEnableInterrupts]
 | |
| [global ProcessorAreInterruptsEnabled]
 | |
| [global ProcessorSetLocalStorage]
 | |
| [global ProcessorReadCR3]
 | |
| [global ProcessorInvalidatePage]
 | |
| [global ProcessorInvalidateAllPages]
 | |
| [global ProcessorReadTimeStamp]
 | |
| [global ProcessorSetThreadStorage]
 | |
| [global ProcessorFakeTimerInterrupt]
 | |
| [global ProcessorSetAddressSpace]
 | |
| [global ProcessorFlushCodeCache]
 | |
| [global ProcessorAPStartup]
 | |
| [global GetLocalStorage]
 | |
| [global GetCurrentThread]
 | |
| [global ArchSwitchContext]
 | |
| [global processorGDTR]
 | |
| [global gdt_data]
 | |
| [global timeStampCounterSynchronizationValue]
 | |
| 
 | |
| [extern ArchNextTimer]
 | |
| [extern InterruptHandler]
 | |
| [extern KThreadTerminate]
 | |
| [extern KernelInitialise]
 | |
| [extern PostContextSwitch]
 | |
| [extern SetupProcessor2]
 | |
| [extern Syscall]
 | |
| [extern installationID]
 | |
| [extern PCSetupCOM1]
 | |
| [extern PCDisablePIC]
 | |
| [extern PCProcessMemoryMap]
 | |
| [extern bootloaderID]
 | |
| [extern bootloaderInformationOffset]
 | |
| 
 | |
| [section .bss]
 | |
| 
 | |
| %define boot_stack_size 10000
 | |
| boot_stack: resb boot_stack_size
 | |
| 
 | |
| %define idt_size 2048
 | |
| idt_data: resb idt_size
 | |
| 
 | |
| %define cpu_local_storage_size (256 * 4 * 4)
 | |
| cpu_local_storage: resb cpu_local_storage_size
 | |
| cpu_local_index: resb 4
 | |
| 
 | |
| timeStampCounterSynchronizationValue: resb 8
 | |
| 
 | |
| [section .data]
 | |
| 
 | |
| processorGDTR: 
 | |
| 	times 8 db 0
 | |
| 
 | |
| idt:
 | |
| 	.limit: dw idt_size - 1
 | |
| 	.base:  dd idt_data
 | |
| 	align 4
 | |
| 
 | |
| gdt_data:
 | |
| 	.null_entry:	dq 0
 | |
| 	.code_entry:	dd 0xFFFF	; 0x0008
 | |
| 			db 0
 | |
| 			dw 0xCF9A
 | |
| 			db 0
 | |
| 	.data_entry:	dd 0xFFFF	; 0x0010
 | |
| 			db 0
 | |
| 			dw 0xCF92
 | |
| 			db 0
 | |
| 	.user_code:	dd 0xFFFF	; 0x001B
 | |
| 			db 0
 | |
| 			dw 0xCFFA
 | |
| 			db 0
 | |
| 	.user_data:	dd 0xFFFF	; 0x0023
 | |
| 			db 0
 | |
| 			dw 0xCFF2
 | |
| 			db 0
 | |
| 	.tss:		dd 0x68		; 0x0028
 | |
| 			db 0
 | |
| 			dw 0xE9
 | |
| 			db 0
 | |
| 			dq 0
 | |
| 	.local:		times 256 dq 0	; 0x0038 - 0x0838
 | |
| 	.gdt:		dw (gdt_data.gdt - gdt_data - 1)
 | |
| 	.gdt2:		dd gdt_data
 | |
| 
 | |
| [section .text]
 | |
| 
 | |
| [global _start]
 | |
| _start:
 | |
| 	; Save the bootloader ID and information offset.
 | |
| 	mov	[bootloaderID],esi
 | |
| 	mov	[bootloaderInformationOffset],edi
 | |
| 
 | |
| 	; The MBR bootloader does not know the address of the RSDP. 
 | |
| 	cmp	edi,0
 | |
| 	jne	.standard_acpi
 | |
| 	mov	[0x7FE8],edi
 | |
| 	.standard_acpi:
 | |
| 
 | |
| 	; Install the boot stack.
 | |
| 	mov	esp,boot_stack + boot_stack_size
 | |
| 
 | |
| 	; Load the installation ID.
 | |
| 	mov	eax,[edi + 0x7FF0]
 | |
| 	mov	[installationID + 0],eax
 | |
| 	mov	eax,[edi + 0x7FF4]
 | |
| 	mov	[installationID + 4],eax
 | |
| 	mov	eax,[edi + 0x7FF8]
 | |
| 	mov	[installationID + 8],eax
 | |
| 	mov	eax,[edi + 0x7FFC]
 | |
| 	mov	[installationID + 12],eax
 | |
| 
 | |
| 	; Load the new GDT, saving the location of the bootstrap GDT.
 | |
| 	lgdt	[gdt_data.gdt]
 | |
| 	sgdt	[processorGDTR]
 | |
| 
 | |
| 	; Move the identity paging the bootloader used to LOW_MEMORY_MAP_START.
 | |
| 	; Then, map the local APIC to LOCAL_APIC_BASE.
 | |
| 	mov	eax,[0xFFFFF000]
 | |
| 	mov	[0xFFFFFEC0],eax
 | |
| 	xor	eax,eax
 | |
| 	mov	[0xFFFFF000],eax
 | |
| 	mov	eax,0xFEE00103
 | |
| 	mov	[0xFFC00000 + (0xEC3FF << 2)],eax
 | |
| 	mov	eax,cr3
 | |
| 	mov	cr3,eax
 | |
| 
 | |
| 	; Install the interrupt handlers
 | |
| 	mov	ebx,idt_data
 | |
| %macro INSTALL_INTERRUPT_HANDLER 1
 | |
| 	mov	edx,InterruptHandler%1
 | |
| 	call	InstallInterruptHandler
 | |
| 	add	ebx,8
 | |
| %endmacro
 | |
| %assign i 0
 | |
| %rep 256
 | |
| 	INSTALL_INTERRUPT_HANDLER i
 | |
| %assign i i+1
 | |
| %endrep
 | |
| 
 | |
| 	; Setup the remaining things and call KernelInitialise
 | |
| 	call	SetupProcessor1 ; Need to get SSE up before calling into C code.
 | |
| 	call	PCSetupCOM1
 | |
| 	call	PCDisablePIC
 | |
| 	call	PCProcessMemoryMap
 | |
| 	call	KernelInitialise
 | |
| 
 | |
| 	; Fall-through.
 | |
| ProcessorReady:
 | |
| 	; Set the timer and become this CPU's idle thread.
 | |
| 	push	1
 | |
| 	call	ArchNextTimer
 | |
| 
 | |
| 	; Fall-through.
 | |
| ProcessorIdle:
 | |
| 	sti
 | |
| 	hlt
 | |
| 	jmp	ProcessorIdle
 | |
| 
 | |
| SetupProcessor1:
 | |
| 	; x87 FPU.
 | |
| 	fninit
 | |
| 	fldcw	[.cw]
 | |
| 	jmp	.cwa
 | |
| 	.cw:	dw 0x037A
 | |
| 	.cwa:
 | |
| 
 | |
| 	; Enable MMX, SSE and SSE2.
 | |
| 	; TODO Check these are actually present!
 | |
| 	mov	eax,cr0
 | |
| 	mov	ebx,cr4
 | |
| 	and	eax,~4
 | |
| 	or	eax,2
 | |
| 	or	ebx,512 + 1024
 | |
| 	mov	cr0,eax
 | |
| 	mov	cr4,ebx
 | |
| 
 | |
| 	; Setup the local storage.
 | |
| 	; This creates a new data segment in the GDT pointing to a unique 16-byte block of cpu_local_storage,
 | |
| 	; and updates FS to use the data segment.
 | |
| 	mov	eax,[cpu_local_index]
 | |
| 	mov	ebx,eax
 | |
| 	shl	ebx,4
 | |
| 	add	ebx,cpu_local_storage
 | |
| 	mov	edx,ebx
 | |
| 	shl	ebx,16
 | |
| 	or	ebx,0x0000FFFF
 | |
| 	mov	ecx,edx
 | |
| 	shr	ecx,16
 | |
| 	and	edx,0xFF000000
 | |
| 	or	edx,0x00CF9200
 | |
| 	or	dl,cl
 | |
| 	mov	dword [gdt_data.local + eax * 8 + 0],ebx
 | |
| 	mov	dword [gdt_data.local + eax * 8 + 4],edx
 | |
| 	lea	eax,[0x0038 + eax * 8]
 | |
| 	mov	fs,ax
 | |
| 	inc	dword [cpu_local_index]
 | |
| 
 | |
| 	; Enable global pages.
 | |
| 	mov	eax,cr4
 | |
| 	or	eax,1 << 7
 | |
| 	mov	cr4,eax
 | |
| 
 | |
| 	; Enable write protect, so copy-on-write works in the kernel, and MMArchSafeCopy will page fault in read-only regions.
 | |
| 	mov	eax,cr0
 | |
| 	or	eax,1 << 16
 | |
| 	mov	cr0,eax
 | |
| 
 | |
| 	; Load the IDTR.
 | |
| 	lidt	[idt]
 | |
| 	sti
 | |
| 
 | |
| 	; Enable the APIC.
 | |
| 	; TODO Check it is actually present!
 | |
| 	mov	ecx,0x1B
 | |
| 	rdmsr
 | |
| 	or	eax,0x800
 | |
| 	wrmsr
 | |
| 
 | |
| 	; Set the spurious interrupt vector to 0xFF
 | |
| 	mov	eax,0xEC3FF0F0
 | |
| 	mov	ebx,[eax]
 | |
| 	or	ebx,0x1FF
 | |
| 	mov	[eax],ebx
 | |
| 
 | |
| 	; Use the flat processor addressing model
 | |
| 	mov	eax,0xEC3FF0E0
 | |
| 	mov	dword [eax],0xFFFFFFFF
 | |
| 
 | |
| 	; Make sure that no external interrupts are masked
 | |
| 	xor	eax,eax
 | |
| 	mov	[0xEC3FF080],eax
 | |
| 
 | |
| 	; TODO More feature detection and initialisation!
 | |
| 
 | |
| 	ret
 | |
| 
 | |
| InstallInterruptHandler:
 | |
| 	mov	word [ebx + 0],dx
 | |
| 	mov	word [ebx + 2],0x0008
 | |
| 	mov	word [ebx + 4],0x8E00 
 | |
| 	shr	edx,16
 | |
| 	mov	word [ebx + 6],dx
 | |
| 	ret
 | |
| 
 | |
| %macro INTERRUPT_HANDLER 1
 | |
| InterruptHandler%1:
 | |
| 	push	dword 0 ; A fake error code
 | |
| 	push	dword %1 ; The interrupt number
 | |
| 	jmp	ASMInterruptHandler
 | |
| %endmacro
 | |
| 
 | |
| %macro INTERRUPT_HANDLER_EC 1
 | |
| InterruptHandler%1:
 | |
| 	; The CPU already pushed an error code
 | |
| 	push	dword %1 ; The interrupt number
 | |
| 	jmp	ASMInterruptHandler
 | |
| %endmacro
 | |
| 
 | |
| INTERRUPT_HANDLER 0
 | |
| INTERRUPT_HANDLER 1
 | |
| INTERRUPT_HANDLER 2
 | |
| INTERRUPT_HANDLER 3
 | |
| INTERRUPT_HANDLER 4
 | |
| INTERRUPT_HANDLER 5
 | |
| INTERRUPT_HANDLER 6
 | |
| INTERRUPT_HANDLER 7
 | |
| INTERRUPT_HANDLER_EC 8
 | |
| INTERRUPT_HANDLER 9
 | |
| INTERRUPT_HANDLER_EC 10
 | |
| INTERRUPT_HANDLER_EC 11
 | |
| INTERRUPT_HANDLER_EC 12
 | |
| INTERRUPT_HANDLER_EC 13
 | |
| INTERRUPT_HANDLER_EC 14
 | |
| INTERRUPT_HANDLER 15
 | |
| INTERRUPT_HANDLER 16
 | |
| INTERRUPT_HANDLER_EC 17
 | |
| INTERRUPT_HANDLER 18
 | |
| INTERRUPT_HANDLER 19
 | |
| INTERRUPT_HANDLER 20
 | |
| INTERRUPT_HANDLER 21
 | |
| INTERRUPT_HANDLER 22
 | |
| INTERRUPT_HANDLER 23
 | |
| INTERRUPT_HANDLER 24
 | |
| INTERRUPT_HANDLER 25
 | |
| INTERRUPT_HANDLER 26
 | |
| INTERRUPT_HANDLER 27
 | |
| INTERRUPT_HANDLER 28
 | |
| INTERRUPT_HANDLER 29
 | |
| INTERRUPT_HANDLER 30
 | |
| INTERRUPT_HANDLER 31
 | |
| 
 | |
| %assign i 32
 | |
| %rep 224
 | |
| INTERRUPT_HANDLER i
 | |
| %assign i i+1
 | |
| %endrep
 | |
| 
 | |
| ASMInterruptHandler:
 | |
| 	cld
 | |
| 
 | |
| 	test	byte [esp + 12],3
 | |
| 	jnz	.have_esp
 | |
| 	; When ring 0 is interrupted, ESP and SS aren't pushed.
 | |
| 	; We push them ourselves here; we'll fix the order later.
 | |
| 	push	eax
 | |
| 	push	esp
 | |
| 	mov	eax,ss
 | |
| 	xchg	[esp + 4],eax
 | |
| 	push	1
 | |
| 	jmp	.fixed
 | |
| 	.have_esp:
 | |
| 	push	0
 | |
| 	.fixed:
 | |
| 
 | |
| 	push	eax
 | |
| 	push	ebx
 | |
| 	push	ecx
 | |
| 	push	edx
 | |
| 	push	esi
 | |
| 	push	edi
 | |
| 	push	ebp
 | |
| 
 | |
| 	mov	ebx,esp
 | |
| 	and	esp,~0xF
 | |
| 	fxsave	[esp - 512]
 | |
| 	mov	esp,ebx
 | |
| 	sub	esp,512 + 16
 | |
| 	
 | |
| 	mov	eax,ds
 | |
| 	push	eax
 | |
| 	mov	eax,0x10
 | |
| 	mov	ds,ax
 | |
| 	mov	eax,cr2
 | |
| 	push	eax
 | |
| 
 | |
| 	mov	edx,[0xEC3FF080]
 | |
| 	push	edx
 | |
| 	mov	edx,0xF0
 | |
| 	mov	[0xEC3FF080],edx ; Mask all interrupts.
 | |
| 	sti                      ; ...so there's no need to have the interrupt flag clear.
 | |
| 
 | |
| 	push	esp
 | |
| 	call	InterruptHandler
 | |
| 	add	esp,4
 | |
| 	xor	eax,eax
 | |
| 
 | |
| 	.return:
 | |
| 
 | |
| 	cli ; Must be done before restoring CR8.
 | |
| 	pop	edx
 | |
| 	mov	[0xEC3FF080],edx
 | |
| 	add	esp,4
 | |
| 	pop	ebx
 | |
| 	mov	ds,bx
 | |
| 
 | |
| 	add	esp,512 + 16
 | |
| 	mov	ebx,esp
 | |
| 	and	ebx,~0xF
 | |
| 	fxrstor	[ebx - 512]
 | |
| 
 | |
| 	or	al,al
 | |
| 	jz	.old_thread
 | |
| 	fninit ; New thread - initialise FPU.
 | |
| 	.old_thread:
 | |
| 
 | |
| 	pop	ebp
 | |
| 	pop	edi
 | |
| 	pop	esi
 | |
| 	pop	edx
 | |
| 	pop	ecx
 | |
| 	pop	ebx
 | |
| 	pop	eax
 | |
| 
 | |
| 	test	byte [esp],1
 | |
| 	jz	.need_esp
 | |
| 	; When returning to ring 0, ESP and SS aren't popped.
 | |
| 	; So we do it manually here.
 | |
| 	add	esp,8
 | |
| 	.need_esp:
 | |
| 
 | |
| 	add	esp,12
 | |
| 	iret
 | |
| 
 | |
| ArchSwitchContext:
 | |
| 	cli
 | |
| 	mov	eax,[esp + 16]
 | |
| 	mov	[fs:8],eax
 | |
| 	mov	ebx,[esp + 12]
 | |
| 	mov	[fs:4],ebx
 | |
| 	mov	edi,[esp + 8]
 | |
| 	mov	ecx,[edi]
 | |
| 	mov	edx,cr3
 | |
| 	cmp	edx,ecx
 | |
| 	je	.cont
 | |
| 	mov	cr3,ecx
 | |
| 	.cont:
 | |
| 	mov	eax,[esp + 4]
 | |
| 	mov	ecx,[esp + 20]
 | |
| 	mov	esp,eax ; Put the stack just below the context.
 | |
| 	push	ecx
 | |
| 	push	eax
 | |
| 	call	PostContextSwitch
 | |
| 	add	esp,8
 | |
| 	jmp	ASMInterruptHandler.return
 | |
| 
 | |
| ProcessorDebugOutputByte:
 | |
| %ifdef COM_OUTPUT
 | |
| 	mov	dx,0x3F8 + 5
 | |
| 	.WaitRead:
 | |
| 	in	al,dx
 | |
| 	and	al,0x20
 | |
| 	cmp	al,0
 | |
| 	je	.WaitRead
 | |
| 	mov	dx,0x3F8 + 0
 | |
| 	mov	eax,[esp + 4]
 | |
| 	out	dx,al
 | |
| %endif
 | |
| 	ret
 | |
| 
 | |
| ProcessorOut8:
 | |
| 	mov	edx,[esp + 4]
 | |
| 	mov	eax,[esp + 8]
 | |
| 	out	dx,al
 | |
| 	ret
 | |
| 
 | |
| ProcessorIn8:
 | |
| 	mov	edx,[esp + 4]
 | |
| 	xor	eax,eax
 | |
| 	in	al,dx
 | |
| 	ret
 | |
| 
 | |
| ProcessorOut16:
 | |
| 	mov	edx,[esp + 4]
 | |
| 	mov	eax,[esp + 8]
 | |
| 	out	dx,ax
 | |
| 	ret
 | |
| 
 | |
| ProcessorIn16:
 | |
| 	mov	edx,[esp + 4]
 | |
| 	xor	eax,eax
 | |
| 	in	ax,dx
 | |
| 	ret
 | |
| 
 | |
| ProcessorOut32:
 | |
| 	mov	edx,[esp + 4]
 | |
| 	mov	eax,[esp + 8]
 | |
| 	out	dx,eax
 | |
| 	ret
 | |
| 
 | |
| ProcessorIn32:
 | |
| 	mov	edx,[esp + 4]
 | |
| 	xor	eax,eax
 | |
| 	in	eax,dx
 | |
| 	ret
 | |
| 
 | |
| ProcessorReset:
 | |
| 	in	al,0x64
 | |
| 	test	al,2
 | |
| 	jne	ProcessorReset
 | |
| 	mov	al,0xFE
 | |
| 	out	0x64,al
 | |
| 
 | |
| 	; Fall-through.
 | |
| ProcessorHalt:
 | |
| 	cli
 | |
| 	hlt
 | |
| 	jmp	ProcessorHalt
 | |
| 
 | |
| ProcessorDisableInterrupts:
 | |
| 	mov	eax,0xE0 ; Still allow important IPIs to go through.
 | |
| 	mov	[0xEC3FF080],eax
 | |
| 	ret
 | |
| 
 | |
| ProcessorEnableInterrupts:
 | |
| 	xor	eax,eax
 | |
| 	mov	[0xEC3FF080],eax
 | |
| 	ret
 | |
| 
 | |
| ProcessorAreInterruptsEnabled:
 | |
| 	xor	al,al
 | |
| 	mov	edx,[0xEC3FF080]
 | |
| 	or	edx,edx
 | |
| 	jnz	.done
 | |
| 	mov	al,1
 | |
| 	.done:
 | |
| 	ret
 | |
| 
 | |
| GetLocalStorage:
 | |
| 	mov	eax,[fs:0]
 | |
| 	ret
 | |
| 
 | |
| GetCurrentThread:
 | |
| 	mov	eax,[fs:8]
 | |
| 	ret
 | |
| 
 | |
| ProcessorSetLocalStorage:
 | |
| 	mov	eax,[esp + 4]
 | |
| 	mov	[fs:0],eax
 | |
| 	ret
 | |
| 
 | |
| ProcessorReadCR3:
 | |
| 	mov	eax,cr3
 | |
| 	ret
 | |
| 
 | |
| ProcessorInvalidatePage:
 | |
| 	mov	eax,[esp + 4]
 | |
| 	invlpg	[eax]
 | |
| 	ret
 | |
| 
 | |
| ProcessorInvalidateAllPages:
 | |
| 	; Toggle CR4.PGE to invalidate all TLB entries, including global entries.
 | |
| 	mov	eax,cr4
 | |
| 	and	eax,~(1 << 7)
 | |
| 	mov	cr4,eax
 | |
| 	or	eax,1 << 7
 | |
| 	mov	cr4,eax
 | |
| 	ret
 | |
| 
 | |
| ProcessorReadTimeStamp:
 | |
| 	rdtsc
 | |
| 	ret
 | |
| 
 | |
| ProcessorSetThreadStorage:
 | |
| 	; TODO.
 | |
| 	ret
 | |
| 
 | |
| ProcessorFakeTimerInterrupt:
 | |
| 	int	0x40
 | |
| 	ret
 | |
| 
 | |
| ProcessorSetAddressSpace:
 | |
| 	mov	eax,[esp + 4]
 | |
| 	mov	edx,[eax]
 | |
| 	mov	eax,cr3
 | |
| 	cmp	eax,edx
 | |
| 	je	.cont
 | |
| 	mov	cr3,edx
 | |
| 	.cont:
 | |
| 	ret
 | |
| 
 | |
| ProcessorFlushCodeCache:
 | |
| 	wbinvd
 | |
| 	ret
 | |
| 
 | |
| SynchronizeTimeStampCounter:
 | |
| 	; TODO
 | |
| 	ret
 | |
| 
 | |
| [bits 16]
 | |
| ProcessorAPStartup: ; This function must be less than 4KB in length (see drivers/acpi.cpp)
 | |
| 	mov	ax,0x1000
 | |
| 	mov	ds,ax
 | |
| 	mov	byte [0xFC0],1 ; Indicate we've started.
 | |
| 	mov	eax,[0xFF0]
 | |
| 	mov	cr3,eax
 | |
| 	lgdt	[0x1000 + gdt_data.gdt - gdt_data]
 | |
| 	mov	eax,cr0
 | |
| 	or	eax,1
 | |
| 	mov	cr0,eax
 | |
| 	jmp	0x8:dword (.pmode - ProcessorAPStartup + 0x10000)
 | |
| [bits 32]
 | |
| 	.pmode:
 | |
| 	mov	eax,0x10
 | |
| 	mov	ds,ax
 | |
| 	mov	es,ax
 | |
| 	mov	ss,ax
 | |
| 	lgdt	[gdt_data.gdt]
 | |
| 	mov	esp,[0x10FD0]
 | |
| 	call	SetupProcessor1
 | |
| 	call	SynchronizeTimeStampCounter
 | |
| 	mov	edi,[0x10FB0]
 | |
| 	push	edi
 | |
| 	call	SetupProcessor2
 | |
| 	mov	byte [0x10FC0],2 ; Indicate the BSP can start the next processor.
 | |
| 	jmp	ProcessorReady
 |