Context Switch from XV6

Flow of the Context Switch

Before Context Switch Interrupt happens

vector 32:
pushl $0 //error code
pushl $32 //vector number of timer interrupt
jmp alltraps
status after pushing vector to kernel stack
 # trap frame.globl alltrapsalltraps:
pushl %ds #data segment register
pushl %es #extra segment register
pushl %fs #general segment register
pushl %gs #general segment register
pushal #all local registers
# Set up data segments.
movw $(SEG_KDATA<<3), %ax
movw %ax, %ds
movw %ax, %es
# Call trap(tf), where tf=%esp by calling convention
pushl %esp
call trap
stack status before context switch ( Recall: calling convention will generate EIP(alltraps, trap, yield) as return address before calling )

Context Switch starts !

// defs.h 129
void swtch(struct context** old, struct context* new);
movl 4(%esp) %eax # address of **old !!little endian
movl 8(%esp) %edx # address of *new
#save all callee registers
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
# change esp pointer
movl %esp, (%eax) # (%eax) means *old (points to address of old)
movl %edx, %esp
# load new callee-save registers
# do not pop the EIP since this is the return address
popl %edi
popl %esi
popl %ebx
popl %ebp
// reverse order from pushl registers
struct context {
uint edi;
uint esi;
uint ebx;
uint ebp;
uint eip;
Status after moving ESP to new context

CPU initialization

// main.c 39
mpmain(); // finish this processor's set up
// Common CPU setup code
static void
cprintf("cpu%d: starting %d\n", cpuid(), cpuid());
idtinit(); // load idt register
xchg(&(mycpu()->started), 1); // tell startothers() we're up
scheduler(); // start running processes
// proc.c 326
// inside loop of the ptable
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
if(p->state != RUNNABLE)
// switch to chosen process
c->proc = p;
p->state = RUNNING;

// do context switch
// c->scheduler points to current context & restore the context of p
swtch(&(c->scheduler), p->context );
c->proc = 0;

How context is created?

static struct proc* 
// after finding an UNUSED process
p->state = EMBRYO;
p->pid = nextpid++;

// Allocate kernel stack.
if((p->kstack = kalloc()) == 0){
p->state = UNUSED;
return 0;
// stack pointer
sp = p->kstack + KSTACKSIZE;
//Leave room for trap frame
sp -= sizeof *p->tf;
p->tf = (struct trapframe*)sp;

// Set up new context to start executing at forkret,
// which returns to trapret.
sp -= 4;
*(uint*)sp = (uint)trapret;
sp -= sizeof *p->context;
p->context = (struct context*)sp;
memset(p->context, 0, sizeof *p->context);
// that's why it is not required to push eip in swtch.S
p->context->eip = (uint) forkret;
return p; }

Dig in more on Context Switch

swtch(&p->context, mycpu()->scheduler);
status before context switch
# move esp value to what %eax points to
movl %esp, (%eax) # (%eax) means *old (points to address of old)
movl %edx, %esp
save p’s context
movl %edx, %esp
ESP points to new Context

Context Switch Flow

After Context Switch

stack status before returning back to user level
stack after trap() returns



Site Reliability Engineer in Greater LA