[BACK]Return to context.c CVS log [TXT][DIR] Up to [local] / prex-old / sys / arch / i386 / i386

File: [local] / prex-old / sys / arch / i386 / i386 / context.c (download)

Revision 1.1, Tue Jun 3 09:38:45 2008 UTC (15 years, 11 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*-
 * Copyright (c) 2005, Kohsuke Ohtani
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * context.c - context management routines
 */

/*
 * The context consists of kernel/user mode registers, and
 * kernel stack. The user mode registers are always saved to the
 * kernel stack when processor enters kernel mode by H/W or S/W events.
 *
 * The user mode registers are located in the interrupt/trap frame
 * at the top of the kernel stack. Before the control returns to user
 * mode next time, these register value will be restored automatically.
 *
 * All thread owns its context to keep its execution state. The
 * scheduler will switch the context to change an active thread.
 */

#include <kernel.h>
#include <cpu.h>
#include <locore.h>

/*
 * Exception frame - stack layout for exception handler
 */
struct exc_frame {
	void *ret;		/* Return address */
	u_long code;		/* Argument 1 */
	struct cpu_regs *uregs;	/* Argument 2 */
};

/*
 * Initialize specified context.
 * @ctx: context id (pointer)
 * @kstack: kernel stack for the context
 *
 * All thread will start at syscall_ret().
 * In this time, the interrupt and I/O access are enabled.
 */
void
context_init(context_t ctx, u_long kstack)
{
	struct kern_regs *k;
	struct cpu_regs *u;

	ctx->uregs = (struct cpu_regs *)(kstack - sizeof(struct cpu_regs));
	ctx->esp0 = kstack;

	/* Initialize kernel mode registers */
	k = &ctx->kregs;
	k->eip = (u_long)syscall_ret;
	k->esp = (u_long)ctx->uregs - sizeof(u_long);

	/* Reset minimum user mode registers */
	u = ctx->uregs;
	u->eax = 0;
	u->eflags = EFL_IF | EFL_IOPL_KERN;
}

/*
 * Set data to the specific register stored in context.
 * @type: register type to be set
 * @val: register value to be set
 *
 * Note: When user mode program counter is set, all register
 * values except stack pointer are reset to default value.
 */
void
context_set(context_t ctx, int type, u_long val)
{
	struct kern_regs *k;
	struct cpu_regs *u;

	switch (type) {
	case CTX_UENTRY:	/* User mode program counter */
		u = ctx->uregs;
		u->eax = u->ebx = u->ecx = u->edx =
		    u->edi = u->esi = u->ebp = 0;
		u->cs = USER_CS | 3;
		u->ds = u->es = USER_DS | 3;
		u->eflags = EFL_IF | EFL_IOPL_KERN;
		u->eip = val;
		break;
	case CTX_USTACK:	/* User mode stack pointer */
		u = ctx->uregs;
		u->esp = val;
		u->ss = USER_DS | 3;
		break;
	case CTX_KENTRY:	/* Kernel mode program counter */
		k = &ctx->kregs;
		k->eip = val;
		break;
	case CTX_KARG:		/* Kernel mode argument */
		k = &ctx->kregs;
		*(u_long *)(k->esp + sizeof(u_long) * 2) = val;
		break;
	}
}

/*
 * Switch to new context
 *
 * Kernel mode registers and kernel stack pointer are switched to the
 * next context.
 *
 * We don't use x86 task switch mechanism to minimize the context space.
 * The system has only one TSS(task state segment), and the context
 * switching is done by changing the register value in this TSS. Processor
 * will reload them automatically when it enters to the kernel mode in
 * next time.
 *
 * It is assumed all interrupts are disabled by caller.
 *
 * TODO: FPU context is not switched as of now.
 */
void
context_switch(context_t prev, context_t next)
{
	/* Set kernel stack pointer in TSS (esp0). */
	tss_set((u_long)next->esp0);

	/* Save the previous context, and restore the next context */
	cpu_switch(&prev->kregs, &next->kregs);
}

/*
 * Save user mode context to handle exceptions.
 * @exc: exception code passed to the exception handler
 *
 * Copy current user mode registers in the kernel stack to the user
 * mode stack. The user stack pointer is adjusted for this area.
 * So that the exception handler can get the register state of
 * the target thread.
 *
 * It builds arguments for the exception handler in the following
 * format.
 *
 *   void exception_handler(int exc, void *regs);
 */
void
context_save(context_t ctx, int exc)
{
	struct cpu_regs *cur, *sav;
	struct exc_frame *frm;

	/* Copy current register context into user mode stack */
	cur = ctx->uregs;
	sav = (struct cpu_regs *)(cur->esp - sizeof(struct cpu_regs));
	memcpy(sav, cur, sizeof(struct cpu_regs));

	/* Setup exception frame for exception handler */
	frm = (struct exc_frame *)(sav - sizeof(struct exc_frame));
	frm->uregs = sav;
	frm->code = exc;
	frm->ret = NULL;
	cur->esp = (u_long)frm;
}

/*
 * Restore register context to return from the exception handler.
 * @regs: pointer to user mode register context.
 */
void
context_restore(context_t ctx, void *regs)
{
	struct cpu_regs *cur;

	/* Restore user mode context */
	cur = ctx->uregs;
	memcpy(cur, regs, sizeof(struct cpu_regs));

	/* Correct some registers for fail safe */
	cur->cs = USER_CS | 3;
	cur->ss = cur->ds = cur->es = USER_DS | 3;
	cur->eflags |= EFL_IF;

	ASSERT(cur->eip && user_area(cur->eip));
	ASSERT(cur->esp && user_area(cur->esp));
}