78 lines
2.7 KiB
NASM
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.
|