I'm implementing my own kernel. I have a problem when I try to use syscall instruction. I already defined the setup for syscall, but when i call syscall command the 'INVALID OPCODE' (Interrupt 6 (#UD)) interruption is called.
I don't know what is happening to cause this exception. The code that invoke the syscall is the last to execute, if i comment the last line when i call 'syscall' no exception is called.
My syscall setup:
setup_syscall:
; Set (0xC0000081) bits 32-47 are kernel segment base, bits 48-63 are user segment base (ON GDT)
mov ecx, 0xC0000081
rdmsr
; setup with kernel segment inside and outside syscall
mov edx, 0x0808
wrmsr
; (0xC0000082) The kernel's RIP SYSCALL entry for 64 bit software
mov ecx, 0xC0000082
mov rdx, syscall_handler
mov eax,edx
shr rdx,32
wrmsr
; Set IA32_EFER.SCE (0xC0000080) and SCE (SYSCALL Enable) is its 0th bit
mov ecx,0xC0000080
xor rax,rax
rdmsr
bts rax,1
wrmsr
ret
My syscall invoke:
test_syscall:
; Enable and setup syscall
call setup_syscall
; test syscall
syscall
jmp $
My syscall:
section .text
[bits 64]
[global syscall_handler]
syscall_handler:
jmp $ ; infinite loop
sysret
My GDT:
ALIGN 4
GDT:
.Null:
; Null Descriptor - should be present.
dq 0
.Code: equ $ - GDT
; 64-bit code descriptor (exec/read).
dq (1<<43)|(1<<44)|(1<<47)|(1<<53)|(1<<41)
.Data:
; 64-bit code descriptor (exec/read).
dq (1<<44)|(1<<47)|(1<<41)
.Pointer:
dw $ - GDT - 1 ; 16-bit Size (Limit) of GDT.
dd GDT ; 32-bit Base Address of GDT. (CPU will zero extend to 64-bit)
Infos: Until now, my OS is in Long mode, has mapped the avaliable memory, but dont have any setup for ring 3, so all code is executed in ring 0, because of this, the base segments I use in the syscall setup is 0x8 for kernel and user.