That's interesting... I came from a different direction where I implemented interrupt handlers first, multitasking second and system calls last.nullplan wrote:I wrote the syscall handler first, and then the multitasking code afterwards. The syscall handler was basically an outgrowth of the interrupt handler code I needed first.
The end result is that I use a fake interrupt frame to spawn kernel thread and to jump to user space.
For example:
Code: Select all
.global JumpToUserMode
JumpToUserMode:
# rdi = entry point in user space
# rsi = user args
# rdx = user space stack
movq $0x1b, %rax # Selector 0x18 (User Data) + RPL 3
movw %ax, %ds
movw %ax, %es
# Build a fake iret frame
pushq %rax # Selector 0x18 (User Data) + RPL 3
pushq %rdx # User space stack
pushq $0x202 # rflags = interrupt enable + reserved bit
pushq $0x23 # Selector 0x20 (User Code) + RPL 3
pushq %rdi # Entry point in user space
# Setup "user args" as parameter to user function
movq %rsi, %rdi # First parameter = user args
# Interrupts need to be disabled to ensure we don't
# get interrupted between swapgs and iret.
cli
swapgs
iretq