os/kernel/cpu/i386/tasking_helpers.asm

69 lines
2.1 KiB
NASM

section .text
global switch_to_task
extern currentTask
extern tss
;WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns
switch_to_task:
;Save previous task'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 task 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,[currentTask] ;edi = address of the previous task's data structure
mov [edi],esp ;Save ESP for the task's kernel stack in the task's data structure
;Load next task's state
mov esi,[esp+(4+1)*4] ;esi = address of the next task's data structure
mov [currentTask],esi ;Current task's task data is the next task thread data
mov esp,[esi] ;Load ESP for next task's kernel stack from the task's data structure
mov eax,[esi+8] ;eax = address of page directory for next task
mov ebx,[esi+4] ;ebx = address for the top of the next task'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 task'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
mov cr3,eax ; yes, load the next task's virtual address space
.doneVAS:
pop ebp
pop edi
pop esi
pop ebx
ret ;Load next task's EIP from its kernel stack
global task_init
task_init:
pop ecx
pop ebx
cli
mov ax, 0x23
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, ecx
push 0x23
push eax
pushf
pop eax
or eax, 0x200
push eax
push 0x1B
push ebx
iret