Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing a label from outside a function

Tags:

c

assembly

kernel

The code:

/* 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);
}

It's a context switcher for a small, cooperative, non-preemptive kernel. contextswitch() is called by dispatcher() with the stack pointer of the process to load. Once %esp and other general purpose registers have been loaded, iret is called and the user process starts running.

I need to setup an interrupt to return to the point in contextswitch() after the iret so I can restore the kernel context and return the value of the syscall to dispatcher().

How can I access the memory address of _entry_point from outside the function?

like image 460
Mike Douglas Avatar asked Feb 07 '10 01:02

Mike Douglas


3 Answers

Switch the implementation of the function around: make it look like this:

  • Context switch from user to kernel;
  • Call into kernel routines;
  • Context switch back from kernel to user.

Then you can just set up the interrupt to run the function from the start. There will need to be a global pointer for "the current user process" - to switch between processes, the kernel code that's run by "Call into kernel routines" just changes that variable to point at a different process.

You will need one special case - for the initial switch from kernel to user mode, for the initial process that's running after boot. After that though, the above function should be able to handle it.

like image 137
caf Avatar answered Nov 15 '22 08:11

caf


After a little while playing around with GCC, I've got an answer.

Dropping down to assembly silences GCC warnings about unused labels.

So,

_entry_point:

is replaced with

asm volatile("_entry_point:");

and

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

is replaced with

void contextinit() {
  long x;
  asm("movl $_entry_point, %%eax\n"
      "movl %%eax, %0": "=g" (x) : : "%eax");
  set_evec(49, x);
}
like image 26
Mike Douglas Avatar answered Nov 15 '22 09:11

Mike Douglas


Besides using inline assembly to access the _entry_point, you can also define it as a function, like:

asm volatile("_entry_point:");

void contextinit() {
  extern void _entry_point();
  set_evec(49, (long)&_entry_point);
}
like image 43
ZelluX Avatar answered Nov 15 '22 08:11

ZelluX