/* Copyright (C) 2021,2022 fef . All rights reserved. */ #include /* * 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)