You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

60 lines
2.3 KiB
ArmAsm

/* Copyright (C) 2021,2022 fef <owo@fef.moe>. All rights reserved. */
#include <asm/common.h>
/*
* Alright, a lot of stuff that is not immediately obvious to someone who hasn't
* done this sort of thing before is going on here, and since i'm totally new to
* x86 myself, here is some excessive documentation of the entire process (which
* will hopefully also help other newcomers in understanding the actual switching
* mechanism). I think the main reason this particular function might seem a
* little confusing is that it returns to a different place than where it came
* from, which is kind of the whole point if you think about it.
*
* This routine is called from within kernel space, and will perform a switch to
* another task that also runs in kernel space. So, this has nothing to do with
* changing ring levels. When another task switches back to the original task,
* that original task just continues execution as if it had never called this
* function.
* As per the x86 SysV ABI, procedure calling is done by pushing all arguments
* to the stack in reverse order, and then use the `call` instruction which
* additionally pushes %eip to the stack and jumps to the subroutine's address.
* So, when we enter this method, the stack looks like this (remember, this is a
* full descending stack):
*
* | address | value |
* +---------+------------------+
* | 8(%esp) | old (argument 2) |
* | 4(%esp) | new (argument 1) |
* | (%esp) | caller's %eip |
*
* What we need to do now is store all caller saved registers (which critically
* include the stack pointer) into `old`, set their values to the ones from
* `new` (again, including the stack pointer) and then just return.
* The new stack pointer will point to the same stack layout shown above, but
* this time that of the new task we are going to switch to. Since the stack
* also includes the %eip from when the new task called arch_switch_to(), we
* automatically switch to that task when returning.
*/
.text
/* void arch_switch_to(tcb_t *new, tcb_t *old); */
ASM_ENTRY(arch_switch_to)
mov 8(%esp), %eax /* %eax = old */
mov %esp, (%eax)
mov %esi, 4(%eax)
mov %edi, 8(%eax)
mov %ebx, 12(%eax)
mov %ebp, 16(%eax)
mov 4(%esp), %eax /* %eax = new */
mov (%eax), %esp
mov 4(%eax), %esi
mov 8(%eax), %edi
mov 12(%eax), %ebx
mov 16(%eax), %ebp
ret
ASM_END(arch_switch_to)