Is this Context Switch correct?
Posted: Mon Sep 15, 2014 4:47 pm
Hi!
I've just baked my ContextSwitch asm routine for x86. I'll give you some info before the code:
I have a TaskDigest struct which holds ONLY the necessary info for task switching and other critical operations. It is generated from both a Process* and a Thread*. I use it so to reduce the counting of stack elements when writing asm Here's its code:
Okay. Inside my PMSwitchContext(Bool autoselect, PID pid, Size tid):
My implementation of ContextSwitch() for x86 is:
I know you must compare some addressing with the TaskDigest implementation, but I need to know if this code is correct. Thanks!
I've just baked my ContextSwitch asm routine for x86. I'll give you some info before the code:
I have a TaskDigest struct which holds ONLY the necessary info for task switching and other critical operations. It is generated from both a Process* and a Thread*. I use it so to reduce the counting of stack elements when writing asm Here's its code:
Code: Select all
typedef struct
{
RegisterSet *regs; // Dynamically include the .h that defines it as per arch
MemorySpaceDescriptor *space; // Same as above
} TaskDigest;
Code: Select all
/* If autoselect == true, ignore pid/tid and continue the chain. If false, switch to pid/tid */
Process *newproc = autoselect ? _runningProcess->next : PMGetProcess(pid);
Thread *newthread = PMGetThread(newproc, tid);
TaskDigest olddigest = { .regs = &(PMCurrentThread->regs), .mmdesc = &(PMCurrentProc->memorySpace) };
TaskDigest newdigest = { .regs = &(newthread->regs), .mmdesc = &(newproc->memorySpace) };
/*
Atomic_NE() is a macro that acquires a specific system resource, but doesn't releases it. It is completely abstract.
It could be a spinlock, a interrupt-disable, or a semaphore. In this case, ATOM_MASTER is a interrupt-disable which
blocks all the entire system, because this operation is not for the faint of heart.
ContextSwitch() is an ArchImplement-ish function which (obviously) changes
to another context. ContextSwitch() is responsible for disabling the atomicity as
its final act before the actual far jump. If it fails, a Kernel Panic will ocurr. (An
`simple exception message here isn't enough to know how to repair the damage made).
*/
Atomic_NE(ATOM_MASTER,
ContextSwitch(&olddigest, &newdigest);
);
Code: Select all
.code32
.text
.global ContextSwitch
ContextSwitch:
# push everything before using anything, to freeze the data
pushad
pushfd
# Both these are of type TaskDigest, as defined in /proc/task.c
# The %ebx one MUST be PMCurrent!
movl 44(%esp), %ebx # %ebx is now address to save old context.
movl 48(%esp), %ecx # %ecx is now address to get new context
# now dump the old registers to %ebx
movl 40(%esp), %eax # Return address
movl %eax, (%ebx) # Save return address
movl 4(%esp), %eax # Old %eflags
movl %eax, 4(%ebx) # Save eflags
movl 36(%esp), %eax # %EDI
movl %eax, 36(%ebx)
movl 32(%esp), %eax # %ESI
movl %eax, 32(%ebx)
movl 28(%esp), %eax # %EBP
movl %eax, 28(%ebx)
movl 24(%esp), %eax # %ESP
movl %eax, 24(%ebx)
movl 20(%esp), %eax # %EBX
movl %eax, 20(%ebx)
movl 16(%esp), %eax # %EDX
movl %eax, 16(%ebx)
movl 12(%esp), %eax # %ECX
movl %eax, 12(%ebx)
movl 8(%esp), %eax # Finally, %EAX!!!
movl %eax, 8(%ebx)
# now, to the black magic for paging >:D
# first, save %CR3
movl %cr3, %eax
movl %eax, 40(%ebx)
# load the new one
movl 40(%ecx), %eax
movl %eax, %cr3
# note, if the kernel wasn't mapped on the new %cr3
# page fault now, then double, and finally die with triple
# Load the new registers (Again!!???)
movl 4(%ecx), %eax
push %eax
popfd # Just done some black magic for eflags
movl 8(%ecx), %eax
movl 12(%ecx), %ecx
movl 16(%ecx), %edx
movl 20(%ecx), %ebx
# %ESP must be here, but it would crash everything, so it must wait
movl 28(%ecx), %ebp
movl 32(%ecx), %esi
movl 36(%ecx), %edi
# Now the final (and most obscure) black magic. Unique for the x86
# First, make a false interrupt stack
push %eax # We'll use it for moving around
movl (%ecx), %eax
push %eax # Pushing EIP
movl 44(%ecx), %eax
push %eax # Pushing CS
movl 4(%ecx), %eax
push %eax # Pushing EFLAGS
movl 24(%ecx), %eax
push %eax # Remember ESP? It's here!
movl 48(%ecx), %eax
push %eax # Pushing SS
# Don't forget to retrieve %EAX! Else it would be equal to SS, jeje
movl 24(%esp), %eax
# Ladies and Gentlemen, awesome with an IRET, without a previous INT!!!
iret