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
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)
|