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?
Switch the implementation of the function around: make it look like this:
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.
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);
}
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With