Context switch for kernel tasks

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
leosa99
Posts: 21
Joined: Tue Aug 30, 2016 9:34 am
Libera.chat IRC: leosa99

Context switch for kernel tasks

Post by leosa99 »

Hi,

I have multiple kernel tasks (or process, as you want) and I am wondering how to switch between them.

When we switch from different ring-level tasks we can use iret and put ss and esp on the stack then use iret.

But if there is no level change (like timer interrupt -> kernel task) these parameters are ignored.

I would like to do for instance :

push myNextCS
push myNextEIP
mov ss, nextSS
mov esp, nextESP
Retf

But as you might guess, editing esp and ss will make the last pushes innefective for my retf.

Also, as there is no ring level change editing the TSS will do nothing.

Am I missing something ?
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Context switch for kernel tasks

Post by rdos »

My code for loading a task differs between operation modes (V86, protected mode, long mode) and is also different based on which ring the thread should be resumed to. I save the register state (including ss:esp & cs:eip) in the thread block, and based on this I can select the proper method to resume the thread.

Something like this:

Code: Select all

    test dword ptr ds:p_rflags,20000h
    jnz load_vm
;
    test ds:p_cs,3
    jnz load_pm_app

load_kernel:
    mov ax,ds:p_ss
    mov ss,ax
    mov esp,dword ptr ds:p_rsp

...

load_pm_app:
    mov ax,ds:p_kernel_ss
    mov ss,ax
    mov esp,ds:p_kernel_esp

...

load_vm:
    mov ax,ds:p_kernel_ss
    mov ss,ax
    mov esp,ds:p_kernel_esp

...

Last edited by rdos on Fri Sep 10, 2021 6:32 am, edited 1 time in total.
leosa99
Posts: 21
Joined: Tue Aug 30, 2016 9:34 am
Libera.chat IRC: leosa99

Re: Context switch for kernel tasks

Post by leosa99 »

So in protected mode, how do you switch between your ring 0 tasks ? 0->0
leosa99
Posts: 21
Joined: Tue Aug 30, 2016 9:34 am
Libera.chat IRC: leosa99

Re: Context switch for kernel tasks

Post by leosa99 »

Thank you for this code snippet, I see how to update your stack but how do you actually jmp to the new code segment and eip ?
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Context switch for kernel tasks

Post by rdos »

Here is the full load code:

Code: Select all

    test dword ptr ds:p_rflags,20000h
    jnz load_vm
;
    test ds:p_cs,3
    jnz load_pm_app

load_kernel:
    mov ax,ds:p_ss
    mov ss,ax
    mov esp,dword ptr ds:p_rsp
;
    xor ax,ax
    push dword ptr ds:p_rflags
    push ax
    push ds:p_cs
    push dword ptr ds:p_rip
;       
    mov ecx,dword ptr ds:p_rcx
    mov edx,dword ptr ds:p_rdx
    mov ebx,dword ptr ds:p_rbx
    mov ebp,dword ptr ds:p_rbp
    mov esi,dword ptr ds:p_rsi
    mov edi,dword ptr ds:p_rdi
;
    mov ax,ds:p_es
    verr ax
    jz load_kernel_es
;
    xor ax,ax
    
load_kernel_es:
    mov es,ax
;       
    mov ax,ds:p_fs
    verr ax
    jz load_kernel_fs
;
    xor ax,ax
    
load_kernel_fs:
    mov fs,ax
;       
    mov ax,ds:p_gs
    verr ax
    jz load_kernel_gs
;
    xor ax,ax
    
load_kernel_gs:
    mov gs,ax
;       
    mov ax,ds:p_ds
    verr ax
    jz load_kernel_ds
;
    xor ax,ax
    
load_kernel_ds:
    push ax
    mov eax,dword ptr ds:p_rax
    pop ds
    iretd

load_pm_app:    
    mov ax,ds:p_kernel_ss
    mov ss,ax
    mov esp,ds:p_kernel_esp
;
    xor ax,ax
    push ax
    push ds:p_ss
    push dword ptr ds:p_rsp
    push dword ptr ds:p_rflags
    push ax
    push ds:p_cs
    push dword ptr ds:p_rip
;       
    mov ecx,dword ptr ds:p_rcx
    mov edx,dword ptr ds:p_rdx
    mov ebx,dword ptr ds:p_rbx
    mov ebp,dword ptr ds:p_rbp
    mov esi,dword ptr ds:p_rsi
    mov edi,dword ptr ds:p_rdi
;
    mov ax,ds:p_es
    verr ax
    jz load_pm_app_es
;
    xor ax,ax
    
load_pm_app_es:
    mov es,ax
;       
    mov ax,ds:p_fs
    verr ax
    jz load_pm_app_fs
;
    xor ax,ax
    
load_pm_app_fs:
    mov fs,ax
;       
    mov ax,ds:p_gs
    verr ax
    jz load_pm_app_gs
;
    xor ax,ax
    
load_pm_app_gs:
    mov gs,ax
;       
    mov ax,ds:p_ds
    verr ax
    jz load_pm_app_ds
;
    xor ax,ax
    
load_pm_app_ds:
    push ax
    mov eax,dword ptr ds:p_rax
    pop ds
    iretd

load_vm:
    mov ax,ds:p_kernel_ss
    mov ss,ax
    mov esp,ds:p_kernel_esp
;
    xor ax,ax
    push ax
    push ds:p_gs
    push ax
    push ds:p_fs
    push ax
    push ds:p_ds
    push ax
    push ds:p_es
    push ax
    push ds:p_ss
    push dword ptr ds:p_rsp
    push dword ptr ds:p_rflags
    push ax
    push ds:p_cs
    push dword ptr ds:p_rip
;
    mov eax,dword ptr ds:p_rax
    mov ecx,dword ptr ds:p_rcx
    mov edx,dword ptr ds:p_rdx
    mov ebx,dword ptr ds:p_rbx
    mov ebp,dword ptr ds:p_rbp
    mov esi,dword ptr ds:p_rsi
    mov edi,dword ptr ds:p_rdi
    iretd
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Context switch for kernel tasks

Post by rdos »

The save code looks like this:

Code: Select all


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;           NAME:           SaveCurrentThread
;
;           DESCRIPTION:    Save state of current thread
;
;       PARAMETERS:     Stack, return IP
;
;       RETURNS:    SS:SP       Core stack
;               FS          Core selector
;               ES, GS      Clear
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SaveCurrentThread       Proc near       
    push fs
    push ds
    push eax
    push edx
;    
    call LockCore    
    sti
;
    GetSystemTime
    mov ds,fs:cs_curr_thread
    sub eax,fs:cs_last_lsb
    add ds:p_lsb_tics,eax
    adc ds:p_msb_tics,0
    add fs:cs_lsb_tics,eax
    adc fs:cs_msb_tics,0
;    
    pushfd
    pop eax
    or ax,200h
    mov dword ptr ds:p_rflags,eax    
;
    pushf
    pop ax    
    and ax,NOT 100h
    push ax
    popf
;
    mov dword ptr ds:p_rcx,ecx
    mov dword ptr ds:p_rbx,ebx
    mov dword ptr ds:p_rbp,ebp
    mov dword ptr ds:p_rsi,esi
    mov dword ptr ds:p_rdi,edi
    mov ds:p_es,es
    mov ds:p_cs,cs
    mov ds:p_ss,ss
    mov ds:p_gs,gs
;
    pop dword ptr ds:p_rdx
    pop eax
    mov dword ptr ds:p_rax,eax
;
    pop ds:p_ds
    pop ds:p_fs
    pop bp
    pop dx
    movzx edx,dx
    mov dword ptr ds:p_rip,edx
    mov dword ptr ds:p_rsp,esp
;
    lss esp,fword ptr fs:cs_stack_offset
    call cs:fpu_save_proc
    mov edx,dword ptr ds:p_rdx    
    push bp
;
    xor bp,bp
    mov ds,bp
    mov es,bp
    mov gs,bp    
    ret
SaveCurrentThread   Endp
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: Context switch for kernel tasks

Post by Octocontrabass »

The typical OS design looks like this example on the wiki. Switching tasks is done by switching kernel stacks. If the task is ring 0, the new stack will contain a return value that will pick up the ring 0 thread where it left off. If the new task is ring 3, the new stack will contain a return value that will pick up in ring 0 right before the appropriate code to return to ring 3.

You may also want to look at this example on the wiki.
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Context switch for kernel tasks

Post by rdos »

Octocontrabass wrote:The typical OS design looks like this example on the wiki. Switching tasks is done by switching kernel stacks. If the task is ring 0, the new stack will contain a return value that will pick up the ring 0 thread where it left off. If the new task is ring 3, the new stack will contain a return value that will pick up in ring 0 right before the appropriate code to return to ring 3.

You may also want to look at this example on the wiki.
That's certainly possible. That's typically how my design works too when the SaveThread function is used, which is the normal case when a thread is blocked.

However, the kernel debugging interface will save the register state rather than just block. So, if you single step some code, the trap gate will save the register state in the thread block, and if the thread switches to the application, it will save application state rather than kernel state. That's when the load of application context is used. The kernel debugger uses this so it can trace threads in kernel or in the application by simply using the thread block. The user can also directly modify register context before doing a trace. This interface can also trace V86 code when accessing the BIOS or running DOS applications.

The application debugger works in a similar way, although it must use the application interrupts & exception structure the ABI defines. Still, the application debugger will read register state for a debugged thread from the thread control block and not from some other memory area or the stack. This design is also why the application debugger can seemlessly trace code into kernel space and show the correct register state & source.

Another advantage of this design is that application threads can be created directly using application register state and then start their execution in user space not in kernel space.

When a thread gets an exception it cannot handle, the register state is saved in the exception handler and the thread is moved to a special "debug" list. When a debugged thread is restarted it is moved to the ready queue.

This is how the code to save the register state looks like when called from an execption: (ebp will point to some registers stored by the exception handler, including eflags, ss:esp and cs:eip, ds, ebp, eax & ebx)

Code: Select all

    push fs
    mov al,[ebp].trap_exc_nr
    mov ds:p_fault_vector,al
    mov eax,[ebp].trap_err
    mov dword ptr ds:p_fault_code,eax
    mov dword ptr ds:p_fault_code+4,0
;
    mov eax,[ebp].trap_eax
    mov dword ptr ds:p_rax,eax
    mov eax,[ebp].trap_ebx
    mov dword ptr ds:p_rbx,eax
    mov dword ptr ds:p_rcx,ecx
    mov dword ptr ds:p_rdx,edx
    mov dword ptr ds:p_rsi,esi
    mov dword ptr ds:p_rdi,edi
    mov eax,[ebp].trap_ebp
    mov dword ptr ds:p_rbp,eax
;       
    mov eax,[ebp].trap_eflags
    mov dword ptr ds:p_rflags,eax
    mov ax,[ebp].trap_cs
    mov ds:p_cs,ax
    mov eax,[ebp].trap_eip
    mov dword ptr ds:p_rip,eax
;       
    pop si
    test dword ptr [ebp].trap_eflags,20000h
    jnz debug_vm

debug_pm:
    mov al,[ebp].trap_cs
    test al,3
    jz debug_kernel
;
    mov ax,[ebp].trap_ss
    mov ds:p_ss,ax
    mov eax,[ebp].trap_esp
    mov dword ptr ds:p_rsp,eax
    jmp debug_pm_common
    
debug_kernel:
    mov ax,ss
    mov ds:p_ss,ax
    mov eax,ebp
    add eax,trap_esp
    mov dword ptr ds:p_rsp,eax
    
debug_pm_common:
    mov ax,[ebp].trap_pds
    mov ds:p_ds,ax
    mov ax,es
    mov ds:p_es,ax
    mov ds:p_fs,si
    mov ax,gs
    mov ds:p_gs,ax
    jmp debug_save_ok

debug_vm:
    mov ax,[ebp].trap_gs
    mov ds:p_gs,ax
    mov ax,[ebp].trap_fs
    mov ds:p_fs,ax
    mov ax,[ebp].trap_ds
    mov ds:p_ds,ax
    mov ax,[ebp].trap_es
    mov ds:p_es,ax
    mov ax,[ebp].trap_ss
    mov ds:p_ss,ax
    mov eax,[ebp].trap_esp
    mov dword ptr ds:p_rsp,eax

debug_save_ok:
Post Reply