1

change the assembly to use push and pop

This commit is contained in:
2022-07-23 13:15:57 +02:00
parent 2d1c21584c
commit fe648cc49c

View File

@ -12,148 +12,245 @@
;* Autor: Olaf Spinczyk, TU Dortmund *
;*****************************************************************************
%include "kernel/threads/Thread.inc"
; EXPORTIERTE FUNKTIONEN
[GLOBAL Thread_switch]
[GLOBAL Thread_start]
; TODO: Add function to unlock the scheduler again so I can use the spinlock
; IMPLEMENTIERUNG DER FUNKTIONEN
[SECTION .text]
; COROUTINE_START : Startet die erste Coroutine ueberhaupt.
;
; C Prototyp: void Coroutine_start (struct CoroutineState* regs);
;; Coroutine_start is called with one arg: CoroutineState* regs, so the main stack looks like this:
;; NOTE: Since this assembly is not generated by the compiler, there is no ebp prelude.
;; To address parameters we use esp.
;; == High address ==
;; *REGS
;; ESP: RET ADDR
;; == Low address ==
Thread_start:
; *
; * Hier muss Code eingefuegt werden
; *
;; Set eax to the address where the CoroutineState struct is in memory:
mov eax, [esp + 0x4]
;; Now eax points to the beginning of CoroutineState:
;; struct CoroutineState {
;; == Low address ==
;; EAX: void *ebx;
;; void *esi;
;; void *edi;
;; void *ebp;
;; void *esp;
;; NOTE: New code with pusha/popa, restores all registers as I use this not only for first start
;; == High address ==
;; };
;; *ESP
;; SP --> RET ADDR
;; == Low address ==
;; Load the contents of CoroutineState to the registers
;; NOTE: I added all the registers saved in thread_state as I use it to switch to a new thread when the old thread
;; was killed/exited before (so thread_state not available)
mov esp, [esp + 0x4]
;; == High address ==
;; *OBJECT
;; 0x13115
;; *KICKOFF
;; EAX
;; ECX
;; EDX
;; EBX
;; ESP
;; EBP
;; ESI
;; EDI
;; SP --> EFLAGS
;; == Low address ==
mov ebx, [eax + efl_offset] ; restore eflags before restoring ebx
push ebx ; could be pushed directly from address but i didn't want to specify wordsize
popf
mov ebx, [eax + ebx_offset] ; restore other regs
mov esi, [eax + esi_offset]
mov edi, [eax + edi_offset]
mov ebp, [eax + ebp_offset]
mov esp, [eax + esp_offset]
mov ecx, [eax + ecx_offset]
mov edx, [eax + edx_offset]
mov eax, [eax + eax_offset] ; restore eax
;; The stackpointer now points to the coroutine stack
;; struct CoroutineState {
;; == High address ==
;; *OBJECT
;; 0x13115
;; SP: *KICKOFF
;; *OBJECT
;; 0x13115
;; *KICKOFF
;; EAX
;; ECX
;; EDX
;; EBX
;; ESP
;; EBP
;; ESI
;; SP --> EDI
;; == Low address ==
popa
;; == High address ==
;; *OBJECT
;; 0x13115
;; SP --> *KICKOFF
;; == Low address ==
;; };
;; Reenable interrupts
;; NOTE: I added this also to Thread_start as I use it to switch to a new thread when the old thread
;; was killed/exited before (so thread_state not available)
sti
;; Return to kickoff
ret
; COROUTINE_SWITCH : Coroutinenumschaltung. Der aktuelle Registersatz wird
; gesichert und der Registersatz der neuen Coroutine
; wird in den Prozessor eingelesen.
;
; C Prototyp: void Coroutine_switch (struct CoroutineState* regs_now, struct CoroutineState* reg_then);
;
; Achtung: Die Parameter werden von rechts nach links uebergeben.
;
;; == High address ==
;; *REGS_THEN
;; *REGS_NOW
;; SP --> RET ADDR
;; == Low address ==
Thread_switch:
; *
; * Hier muss Code eingefuegt werden
; *
;; Save current coroutine registers
;; NOTE: New code with pusha/popa
;; == High address ==
;; *REGS_THEN
;; *REGS_NOW
;; *ESP_NEXT
;; *ESP_PREV
;; SP --> RET ADDR
;; == Low address ==
push eax ; Backup eax
;; == High address == ; Scheduler stack
;; *ESP_NEXT
;; + 0x8 *ESP_PREV
;; RET ADDR
;; SP --> EAX
;; == Low address ==
push eax ; backup eax before using it as index
mov eax, [esp + 0x8] ; + 0x8 because we pushed eax
add esp, 0x4 ; store the original esp
mov [eax + esp_offset], esp
sub esp, 0x4
mov eax, [esp + 0x8]
;; == High address == ; Previous thread stack (thread that was running when the interrupt came)
;; OLD
;; THREAD
;; STACK
;; EAX -> RET ADDR
;; == Low address ==
mov [eax + ebx_offset], ebx ; store other regs
mov [eax + esi_offset], esi
mov [eax + edi_offset], edi
mov [eax + ebp_offset], ebp
mov [eax + ecx_offset], ecx
mov [eax + edx_offset], edx
sub eax, 0x28
;; == High address ==
;; OLD
;; THREAD
;; STACK
;; 0x0 RET ADDR
;; 0x4
;; 0x8
;; 0xc
;; 0x10
;; 0x14
;; 0x18
;; 0x1c
;; 0x20
;; 0x24
;; EAX ->
;; == Low address ==
pushf ; store eflags
pop ebx ; ebx has to be saved before
mov [eax + efl_offset], ebx
mov [eax], esp ; Current esp to old thread stack, 0x24 is the amount pusha, pushf change the esp
; We save it, push the current registers to the old threads stack, return and restore
;; == High address ==
;; OLD
;; THREAD
;; STACK
;; 0x0 RET ADDR
;; 0x4
;; 0x8
;; 0xc
;; 0x10
;; 0x14
;; 0x18
;; 0x1c
;; 0x20
;; 0x24
;; EAX -> ESP (Points not to RET ADDR but the EAX we pushed as backup!!!)
;; == Low address ==
pop ebx ; store eax
mov [eax + eax_offset], ebx
;; BUG: Not the correct value
pop eax ; ESP still points to the EAX we pushed before
;; == High address ==
;; *ESP_NEXT
;; *ESP_PREV
;; SP --> RET ADDR
;; == Low address ==
;; Load next coroutine registers ============================================================
mov eax, [esp + 0x8] ; + 0x8 again since we popped eax value again
mov esp, [esp + 0x4]
;; == High address ==
;; OLD
;; THREAD
;; STACK
;; SP --> RET ADDR
;; 0x4
;; 0x8
;; 0xc
;; 0x10
;; 0x14
;; 0x18
;; 0x1c
;; 0x20
;; 0x24
;; 0x28 ESP
;; == Low address ==
mov ebx, [eax + efl_offset] ; restore eflags before restoring ebx
push ebx ; could be pushed directly from address but i didn't want to specify wordsize
popf
pusha ; Save current registers to stack
;; == High address ==
;; OLD
;; THREAD
;; STACK
;; RET ADDR
;; EAX
;; ECX
;; EDX
;; EBX
;; ESP
;; EBP
;; ESI
;; EDI
;; SP -->
;; ESP
;; == Low address ==
mov ebx, [eax + ebx_offset] ; restore other regs
mov esi, [eax + esi_offset]
mov edi, [eax + edi_offset]
mov ebp, [eax + ebp_offset]
mov esp, [eax + esp_offset]
mov ecx, [eax + ecx_offset]
mov edx, [eax + edx_offset]
mov eax, [eax + eax_offset] ; restore eax
pushf
;; == High address ==
;; OLD
;; THREAD
;; STACK
;; RET ADDR
;; EAX
;; ECX
;; EDX
;; EBX
;; ESP
;; EBP
;; ESI
;; EDI
;; EFLAGS
;; SP --> ESP
;; == Low address ==
pop esp ; The POP ESP instruction increments the stack pointer (ESP)
; before data at the old top of stack is written into the destination.
;; == High address ==
;; *ESP_NEXT
;; *ESP_PREV
;; RET ADDR
;; SP -->
;; == Low address ==
mov esp, [esp + 0xc] ; Move to next coroutines stack
;; == High address ==
;; NEW
;; THREAD
;; STACK
;; RET ADDR
;; EAX
;; ECX
;; EDX
;; EBX
;; ESP
;; EBP
;; ESI
;; EDI
;; SP --> EFLAGS
;; == Low address ==
popf ; Load new registers from stack
;; == High address ==
;; NEW
;; THREAD
;; STACK
;; RET ADDR
;; EAX
;; ECX
;; EDX
;; EBX
;; ESP
;; EBP
;; ESI
;; SP --> EDI
;; == Low address ==
popa
;; == High address ==
;; NEW
;; THREAD
;; STACK
;; SP --> RET ADDR
;; == Low address ==
;; Enable interrupts again
sti
;; NOTE: The stackpointer points to kickoff if the next coroutine was just initialized.
;; Otherwise it just points somewhere in the next coroutines stack
ret