2010-02-07 12 views
7

El código:Acceso a una etiqueta desde fuera de una función

/* ctsw.c : context switcher 
*/ 

#include <kernel.h> 

static void *kstack; 
extern int set_evec(int, long); 

/* contextswitch - saves kernel context, switches to proc */ 
enum proc_req contextswitch(struct proc_ctrl_blk *proc) { 
    enum proc_req call; 

    kprintf("switching to %d\n", getpid(proc)); 

    asm volatile("pushf\n"   // save kernel flags 
       "pusha\n"   // save kernel regs 
       "movl %%esp, %0\n" // save kernel %esp 
       "movl %1, %%esp\n" // load proc %esp 
       "popa\n"   // load proc regs (from proc stack) 
       "iret"    // switch to proc 
       : "=g" (kstack) 
       : "g" (proc->esp) 
       ); 

_entry_point: 
    asm volatile("pusha\n"   // save proc regs 
       "movl %%esp, %0\n" // save proc %esp 
       "movl %2, %%esp\n" // restore kernel %esp 
       "movl %%eax, %1\n" // grabs syscall from process 
       "popa\n"   // restore kernel regs (from kstack) 
       "popf"    // restore kernel flags 
       : "=g" (proc->esp), "=g" (call) 
       : "g" (kstack) 
       ); 
    kprintf("back to the kernel!\n"); 

    return call; 
} 

void contextinit() { 
    set_evec(49, (long)&&_entry_point); 
} 

Es un selector de contexto para un pequeño núcleo, cooperativo, no preferente. contextswitch() se llama por dispatcher() con el puntero de pila del proceso para cargar. Una vez que se han cargado% esp y otros registros de propósito general, se llama al iret y el proceso de usuario comienza a ejecutarse.

Necesito fijar una interrupción para volver al punto en contextswitch() después de la iret para que pueda restaurar el contexto del núcleo y devolver el valor de la llamada al sistema de dispatcher().

¿Cómo puedo acceder a la dirección de memoria _entry_point desde fuera de la función?

Respuesta

3

Después de un tiempo jugando con GCC, tengo una respuesta.

Bajando a silencios de montaje Advertencias GCC sobre etiquetas no utilizadas.

Así,

_entry_point: 

se sustituye con

asm volatile("_entry_point:"); 

y

void contextinit() { 
    set_evec_(49, &&_entry_point); 
} 

se sustituye con

void contextinit() { 
    long x; 
    asm("movl $_entry_point, %%eax\n" 
     "movl %%eax, %0": "=g" (x) : : "%eax"); 
    set_evec(49, x); 
} 
4

interruptor de la implementación de la función en torno a: hacer que se vea como esto:

  • cambio de contexto del usuario al núcleo;
  • Llamar a las rutinas del kernel;
  • Cambio de contexto de kernel a usuario.

Luego puede configurar la interrupción para ejecutar la función desde el principio. Deberá haber un puntero global para "el proceso de usuario actual": para alternar entre los procesos, el código del kernel que se ejecuta con "Llamar a las rutinas del kernel" simplemente cambia esa variable para apuntar a un proceso diferente.

Necesitará un caso especial: para el cambio inicial del kernel al modo de usuario, para el proceso inicial que se ejecuta después del arranque. Sin embargo, después de eso, la función anterior debería ser capaz de manejarlo.

+0

Esta es una buena idea, y si no tuviera ya el código despachador y control de procesos ya escrito asumiendo el otro modelo, podría usarlo. –

1

Además de usar ensamblador en línea para acceder a la _entry_point, también se puede definir como una función, como:

asm volatile("_entry_point:"); 

void contextinit() { 
    extern void _entry_point(); 
    set_evec(49, (long)&_entry_point); 
} 
Cuestiones relacionadas