os/kernel/cpu/i386/tasking_helpers.asm

78 lines
2.7 KiB
NASM

section .text
global switch_to_thread_asm
extern load_address_space
extern current_thread
extern tss
;WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns
switch_to_thread_asm:
;Save previous thread's state
;Notes:
; For cdecl; EAX, ECX, and EDX are already saved by the caller and don't need to be saved again
; EIP is already saved on the stack by the caller's "CALL" instruction
; The thread isn't able to change CR3 so it doesn't need to be saved
; Segment registers are constants (while running kernel code) so they don't need to be saved
push ebx
push esi
push edi
push ebp
mov edi,[current_thread] ;edi = address of the previous thread's data structure
mov [edi],esp ;Save ESP for the thread's kernel stack in the thread's data structure
;Load next thread's state
mov esi,[esp+(4+1)*4] ;esi = address of the next thread's data structure
mov [current_thread],esi ;Set the current thread to the thread we are switching to
mov esp,[esi] ;Load ESP for next thread's kernel stack from the thread's data structure
mov eax,[esi+8] ;eax = address of page directory for next thread
mov ebx,[esi+4] ;ebx = address for the top of the next thread's kernel stack
mov [tss+4],ebx ;Adjust the ESP0 field in the TSS (used by CPU for for CPL=3 -> CPL=0 privilege level changes)
mov ecx,cr3 ;ecx = previous thread's virtual address space
cmp eax,ecx ;Does the virtual address space need to being changed?
je .doneVAS ; no, virtual address space is the same, so don't reload it and cause TLB flushes
; yes, load the next thread's virtual address space
mov cr3, eax
.doneVAS:
pop ebp
pop edi
pop esi
pop ebx
ret ;Load next thread's EIP from its kernel stack
global task_init
; Switch to usermode, given a usermode stack and EIP to switch to
task_init:
pop ecx ; ecx = user ESP
pop ebx ; ebx = user EIP
mov ax, 0x23 ; Load data segment selectors with the usermode data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push 0x23 ; Push SS
push ecx ; push user ESP
pushf ; push flags
pop eax ; pop flags into eax
or eax, 0x200 ; enable interrupts when iret runs
push eax ; push modified flags
push 0x1B ; push CS
push ebx ; push user EIP
iret
global wait_for_unblocked_thread_asm
wait_for_unblocked_thread_asm:
sti ;As interrupts are stopped in tasking code, re-enable them
hlt ;Wait for an interrupt handler to run and return.
cli ;Clear interrupts, as tasking code must not be run with interrupts on.