Here's my example of redirecting kbd IRQ through lapic instead of PIC. Requires
long mode and IDT set up properly. Also PIC needs to be shut down beforehand.
Code: Select all
use64
;Constants.
;----------
TASK_PRIORITY_REGISTER =0080H
DESTINATION_FORMAT_REGISTER =00E0H
SPURIOUS_INT_VEC_REG =0xF0
LVT_TIMER_REGISTER =0320H
LVT_PERFORMANCE_MONITORING_COUNTERS =0340H
LVT_LINT0_REGISTER =0350H
LVT_LINT1_REGISTER =0360H
LVT_ERROR_REGISTER =0370H
SPURIOUS_INT_VECTOR =0x4f ;use vector 0x4f for the spurious int.
;.........
;Setup long mode and IDT......
;------------------------------
;Handle LAPIC and IOAPIC setup.
;------------------------------
mov eax,0xfee00000
mov dword[Local_Apic_address],eax ;Presume lapic base address and IOapic base address.
;Note: Correct way of finding the base
;address's is through apic /mp spec table parsing.
mov eax,0xfec00000
mov dword[ACPI_IOAPIC_Base_Address_Table],eax ;Presumme lapic base address and IOapic base address.
call LM_Enable_Local_Apic ;Try to enable acpi.
call LM_Disable_All_Lapic_IRQs ;Make sure that all IOapic ints are off.
call LM_Initialize_Local_APIC_Irqs ;Setup Local apic to accept interruptions.
;Let's enable keyboard interrupt (irq1) and direct it to BSP
;-------------------------------------------------------------
mov r9b,0 ;r11b-->Requested IOAPIC redirection table entry to be enabled.
mov r10b,0x21 ;r10b-->Requested interrupt vector
mov r11b,1 ;r9b -->APIC ID number where the interrupt will be redirected.
call LM_Enable_IOAPIC_IRQ ;Enable APIC KBD int
;Continue with your code here.....
;.....
;Variables
;----------
align 8
ACPI_IOAPIC_Temp_redir_entry dq 0
Local_Apic_address dq 0
ACPI_IOAPIC_Base_Address_Table dq 0,0,0,0
;****************************************************************************************************
;LM_Enable_Local_Apic The procedure tries to enable the local apic.
;
;
;
; Input: --
;
; Output: --
;
;***********************************************************************************************
align 8
LM_Enable_Local_Apic:
push rax
push rdi
cmp qword[Local_Apic_address],0 ;Prevent illegal access.
jz .no_apic_adress
;Read current data, change needed bit(s)
;and write the data back.
;-----------------------------------------
mov rdi,SPURIOUS_INT_VEC_REG ;Read Spurious Interrupt Vector Register
call LM_Read_Apic_Register_Dword
or eax, 0000000100000000b ;Set bit8
mov rdi,SPURIOUS_INT_VEC_REG ;Write Spurious Interrupt Vector Register
call LM_Write_Apic_Register_Dword
.no_apic_adress:
pop rdi
pop rax
ret ;The end of procedure
;*************************************************************************************************************
;LM_Disable_All_Lapic_IRQs The procedure disables all lapic interrupts.
;
; Input: --
;
; Output: --
;
;*************************************************************************************************************
align 8
LM_Disable_All_Lapic_IRQs:
push rax rbx rcx rdx rdi rsi r8 r9 r10 r11
mov r10,24 ;Handle 24 table entries.
mov r8,0x10 ;Start from 1st IO redirection table register.
.read_next_entry:
mov qword[ACPI_IOAPIC_Temp_redir_entry],0 ;Clear the variable.
;1) Read next IO redirection table entry from IOapic.
;-----------------------------------------------------
mov rbx,r8 ;rbx--> Read the lower dword of IO redirection table register
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Read_IOapic_Register
mov dword[ACPI_IOAPIC_Temp_redir_entry],eax
mov rbx,r8 ;rbx--> Read the higher dword of IO redirection table register
add rbx,1 ;Read upper dword.
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Read_IOapic_Register
mov dword[ACPI_IOAPIC_Temp_redir_entry+4],eax
;2) Check whether the interrupt mask is allready on.
;----------------------------------------------------
bt qword[ACPI_IOAPIC_Temp_redir_entry],16 ;Test bit16. Interrupt mask.
jc .prepare_to_read_next_entry ;If bit16 is set to 1, the mask is allreaady on and there's no need
;to tweak it.
bts qword[ACPI_IOAPIC_Temp_redir_entry],16 ;Set Interrupt mask.
;3) The interrupt mask wasn't set, so we need to
;write the tweaked table entry back to IOAPIc.
;---------------------------------------------------
mov eax,dword[ACPI_IOAPIC_Temp_redir_entry]
mov rbx,r8 ;rbx--> Write the lower dword of IO redirection table register
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Write_IOapic_Register
mov eax,dword[ACPI_IOAPIC_Temp_redir_entry+4]
mov rbx,r8 ;rbx--> Write the higher dword of IO redirection table register
add rbx,1 ;Read upper dword.
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Write_IOapic_Register
.prepare_to_read_next_entry:
add r8,2 ;Read next entry from it's low dword.
dec r10
jnz .read_next_entry
.done:
pop r11 r10 r9 r8 rsi rdi rdx rcx rbx rax
ret ;The end of procedure
;*************************************************************************************************************
;LM_Initialize_Local_APIC_Irqs The procedure prepares the local apic controller to accept interrputs.
;
;
;
; Input: --
;
; Output: --
;
;*************************************************************************************************************
align 8
LM_Initialize_Local_APIC_Irqs:
push rax rbx rcx rdx rdi rsi r8 r9 r10 r11
cmp qword[Local_Apic_address],0 ;Prevent illegal access.
jz .done
;1) Set task priority (baseaddress +00080h)
;--------------------------------------------
mov rdi,TASK_PRIORITY_REGISTER ;Read current data.
call LM_Read_Apic_Register_Dword
mov al,0 ;Clear Task Priority (bits 7:4) and Task Priority Sub-Class (bits 3:0)
mov rdi,TASK_PRIORITY_REGISTER ;Perform the write.
call LM_Write_Apic_Register_Dword
;2) Set timer interrupt vector (baseaddress +00320h)
;-----------------------------------------------------
mov rdi,LVT_TIMER_REGISTER ;Read current data.
call LM_Read_Apic_Register_Dword
bts eax,16 ;bit16:Mask interrupts (0==Unmasked, 1== Masked)
mov rdi,LVT_TIMER_REGISTER ;Perform the write.
call LM_Write_Apic_Register_Dword
;3) Set performance counter vector (baseaddress +00340h)
;Performance may or may not exist.Intel 3a-3b states that
;offset 00340h may vary as well.
;--------------------------------------------------------
; mov rdi,LVT_PERFORMANCE_MONITORING_COUNTERS ;Read current data.
; call LM_Read_Apic_Register_Dword
;
; bts eax,16 ;bit16:Mask interrupts (0==Unmasked, 1== Masked)
;; mov eax, 0x10000 ;0x10000 (10000000000000000b) to disable performance counter interrupts
; mov rdi,LVT_PERFORMANCE_MONITORING_COUNTERS ;Perform the write.
; call LM_Write_Apic_Register_Dword
;4) Set local interrupt 0 (baseaddress +00350h)
;--------------------------------------------------------
mov rdi,LVT_LINT0_REGISTER ;Read current data.
call LM_Read_Apic_Register_Dword
mov al, 0 ;Set interrupt vector (bits 7:0)
bts eax,8 ;Delivery Mode (111b==ExtlNT] (bits 10:8)
bts eax,9
bts eax,10
bts eax,15 ;bit15:Set trigger mode to Level (0== Edge, 1== Level)
;This flag (Trigger mode) is only used when the delivery mode is
;Fixed. When the delivery mode is NMI, SMI, or INIT, the trigger
;mode is always edge sensitive.When the delivery mode is ExtINT,
;the trigger mode is always level sensitive. The timer and error
;interrupts are always treated as edge sensitive.
btr eax,16 ;bit16:unmask interrupts (0==Unmasked, 1== Masked)
mov rdi,LVT_LINT0_REGISTER ;Perform the write.
call LM_Write_Apic_Register_Dword
;5) Set local interrupt 1 (baseaddress +00360h)
;--------------------------------------------------------
mov rdi,LVT_LINT1_REGISTER ;Read current data.
call LM_Read_Apic_Register_Dword
mov al, 0 ;Set interrupt vector (bits 7:0)
btr eax,8 ;Delivery Mode (000b==Fixed] (bits 10:8)
btr eax,9
btr eax,10
btr eax,15 ;bit15:Set trigger mode to Edge (0== Edge, 1== Level)
;This flag (Trigger mode) is only used when the delivery mode is
;Fixed. When the delivery mode is NMI, SMI, or INIT, the trigger
;mode is always edge sensitive.When the delivery mode is ExtINT,
;the trigger mode is always level sensitive. The timer and error
;interrupts are always treated as edge sensitive.
btr eax,16 ;bit16:unmask interrupts (0==Unmasked, 1== Masked)
mov rdi,LVT_LINT1_REGISTER ;Perform the write.
call LM_Write_Apic_Register_Dword
;6) Set error interrupt [baseaddress +00370h)
;--------------------------------------------------------
mov rdi,LVT_ERROR_REGISTER ;Read current data.
call LM_Read_Apic_Register_Dword
mov al, 0 ;LVT_LINT_ERR_VECTOR ;Clear interrupt vector (bits 7:0)
bts eax,16 ;bit16:Mask interrupts (0==Unmasked, 1== Masked)
mov rdi,LVT_ERROR_REGISTER ;Perform the write.
call LM_Write_Apic_Register_Dword
;7) Setup the "destination format register"
;--------------------------------------------------------
mov rdi,DESTINATION_FORMAT_REGISTER ;Read current data.
call LM_Read_Apic_Register_Dword
bts eax,28 ;Flat Model is selected by programming DFR bits 28 through 31
bts eax,29 ;to 1111.
bts eax,30
bts eax,31
mov rdi,DESTINATION_FORMAT_REGISTER ;Perform the write.
call LM_Write_Apic_Register_Dword
;8) Set spurious interrupt [baseaddress +00F0h)
;--------------------------------------------------------
mov rdi,SPURIOUS_INT_VEC_REG ;Read current data.
call LM_Read_Apic_Register_Dword
mov al,SPURIOUS_INT_VECTOR ;Set interrupt vector (bits 7:0)
bts eax,8 ;bit8: APIC Software Enable/Disable, (0==APIC Disabled,1==APIC Enabled)
;Intel 3a-3b manual says that bit12 (EOI-Broadcast Suppression)
;and bit9 (Focus Processor Checking ) operations are not
;supported by all configurations. Let's disable them.
;--------------------------------------------------------------
bts eax,12 ;bit12: EOI-Broadcast Suppression (0==Enabled, 1== Disabled)
bts eax,9 ;bit9: Focus Processor Checking (0==Enabled 1==Disabled)
mov rdi,SPURIOUS_INT_VEC_REG ;Perform the write.
call LM_Write_Apic_Register_Dword
.done:
pop r11 r10 r9 r8 rsi rdi rdx rcx rbx rax
ret ;The end of procedure
;*************************************************************************************************************
;LM_Enable_IOAPIC_IRQ The procedure enables the requested IOAPIC IRQ from the IOAPIC redirection
; table entry[x].
;
;
;
; Input: r11b-->Requested IOAPIC redirection table entry to be enabled..
; r10b-->Requested interrupt vector.
; r9b -->APIC ID number where the interrupt will be redirected.
;
; Output: --
;
;*************************************************************************************************************
align 8
LM_Enable_IOAPIC_IRQ:
push rax rbx rcx rdx rdi rsi r8 r9 r10 r11
pushfq
and r9,0xff ;Clear unwanted bits.
and r10,0xff ;Clear unwanted bits.
and r11,0xff ;Clear unwanted bits.
shl r11,1 ;Entry number*2
mov r8,0x10 ;Get first table entry.
add r8,r11 ;Point to requested table entry.
.read_next_entry:
mov qword[ACPI_IOAPIC_Temp_redir_entry],0 ;Reset the variable
;1) Read next IO redirection table entry from IOapic.
;-----------------------------------------------------
mov rbx,r8 ;rbx--> Read the lower dword of IO redirection table register
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Read_IOapic_Register
mov dword[ACPI_IOAPIC_Temp_redir_entry],eax
mov rbx,r8 ;rbx--> Read the higher dword of IO redirection table register
add rbx,1 ;Read upper dword.
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Read_IOapic_Register
mov dword[ACPI_IOAPIC_Temp_redir_entry+4],eax
;2) Adjust the bits for low dword
;----------------------------------------------------
mov byte[ACPI_IOAPIC_Temp_redir_entry],r10b ;Set the interrupt vector.(bits7:0)
btr qword[ACPI_IOAPIC_Temp_redir_entry],8 ;Set delivery mode (bits10:8) to (000b==fixed)
btr qword[ACPI_IOAPIC_Temp_redir_entry],9
btr qword[ACPI_IOAPIC_Temp_redir_entry],10
btr qword[ACPI_IOAPIC_Temp_redir_entry],11 ;Set the Destination mode to physical (=0).(bit11)
btr qword[ACPI_IOAPIC_Temp_redir_entry],13 ;Set int PIN polarity. (Active high (=0) for ISA int's)(bit13)
btr qword[ACPI_IOAPIC_Temp_redir_entry],15 ;Set the trigger mode (Edge trigger (=0) for ISA int's.(bit15)
btr qword[ACPI_IOAPIC_Temp_redir_entry],16 ;Clear the interrupt mask.(bit16)
;3) Adjust the bits for high dword
;----------------------------------------------------
mov byte[ACPI_IOAPIC_Temp_redir_entry+7],r9b ;Set the requested APIC ID.
;4) Write the tweaked table entry back to IOAPIc.
;---------------------------------------------------
mov eax,dword[ACPI_IOAPIC_Temp_redir_entry]
mov rbx,r8 ;rbx--> Write the lower dword of IO redirection table register
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Write_IOapic_Register
mov eax,dword[ACPI_IOAPIC_Temp_redir_entry+4]
mov rbx,r8 ;rbx--> Write the higher dword of IO redirection table register
add rbx,1 ;Read upper dword.
mov rcx,0 ;rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
call LM_Write_IOapic_Register
.done:
popfq
pop r11 r10 r9 r8 rsi rdi rdx rcx rbx rax
ret ;The end of procedure
;****************************************************************************************************
;LM_Read_Apic_Register_Dword The procedure reads a dword from the requested Local APIC
; register.
;
; Input: rdi--> Register offset
;
; Output: eax -->Read register contents.
;
;***********************************************************************************************
align 8
LM_Read_Apic_Register_Dword:
push rsi
cmp qword[Local_Apic_address],0 ;Prevent illegal access.
jz .no_apic_adress
mov rsi,qword[Local_Apic_address] ;Get local apic address.
add rsi, rdi ;Add the offset
mov eax, dword[rsi] ;Read the dword.
.no_apic_adress:
pop rsi
ret ;The end of procedure
;****************************************************************************************************
;LM_Write_Apic_Register_Dword The procedure writes the contents o feax into the requested
; Local APIC register.
;
; Input: rdi--> Register offset
; eax--> dword to be written.
;
; Output: --
;
;****************************************************************************************************
align 8
LM_Write_Apic_Register_Dword:
push rsi
cmp qword[Local_Apic_address],0 ;Prevent illegal access.
jz .no_apic_adress
mov rsi,qword[Local_Apic_address] ;Get local apic address.
add rsi, rdi ;Add the offset
mov dword[rsi],eax ;Write the dword.
.no_apic_adress:
pop rsi
ret ;The end of procedure
;************************************************************************************************************
;LM_Select_IOAPIC_Register_Address The procedure selects the requested IOapic register.
;
;
;
; Input: rbx--> IOapic register offset to be selected.(IOREGSEL)
;
; Output: --
;
;************************************************************************************************************
align 8
LM_Select_IOAPIC_Register_Address:
push rax rbx rcx rdx rdi rsi r8 r9 r10
;Select requested IOWIN register through IOREGSEL.
;--------------------------------------------------
and rbx,0xff
mov rsi,qword[ACPI_IOAPIC_Base_Address_Table]
mov r10,rsi ;Duplicate the address.
add rsi,0x0
lodsd ;Read IOREGSEL register
mov al,bl ;Modify bit7:0 (APIC register Address)
mov eax, eax ;Write offset
mov rdi,r10 ;Get the base address.
add rdi,0x0
stosd ;Write IOREGSEL register
.done:
pop r10 r9 r8 rsi rdi rdx rcx rbx rax
ret ;The end of procedure
;****************************************************************************************************
;LM_Write_IOapic_Register The procedure writes to requested IOapic register.
;
;
;
; Input:
; eax--> Value to be written. (IOWIN)
; rbx--> IOapic register offset to be written.(IOREGSEL)
; rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
;
; Output: --
;
;***********************************************************************************************
align 8
LM_Write_IOapic_Register:
push rax rbx rcx rdx rdi rsi r8 r9 r10
;rbx--> IOapic register offset to be selected.(IOREGSEL)
call LM_Select_IOAPIC_Register_Address
mov eax, eax ;Write offset
mov rdi,qword[ACPI_IOAPIC_Base_Address_Table] ;Get the base address.
add rdi,0x10
stosd ;Write IOWIN register
.done:
pop r10 r9 r8 rsi rdi rdx rcx rbx rax
ret ;The end of procedure
;****************************************************************************************************
;LM_Read_IOapic_Register The procedure reads requested IOapic register.
;
; Input:
; rbx--> IOapic register offset to be read.(IOREGSEL)
; rcx--> IOapic controller number to be accessed, (0==1st, 1==2nd and so on...)
;
; Output: eax--> Read value from IOWIN register.
;
;***********************************************************************************************
align 8
LM_Read_IOapic_Register:
push rbx rcx rdx rdi rsi
;rbx--> IOapic register offset to be selected.(IOREGSEL)
call LM_Select_IOAPIC_Register_Address
mov rsi,qword[ACPI_IOAPIC_Base_Address_Table]
add rsi,0x10
lodsd ;Read IOWIN register
.done:
pop rsi rdi rdx rcx rbx
ret ;The end of procedure