GPF upon task-switch to Ring 3
Posted: Sat Jun 04, 2011 2:22 pm
I really have no idea how to fix this. My scheduler works perfectly as long as I don't use Ring 3 segments. When I attempt to switch to Ring 3 (during I task switch), what I do is update the register values on the stack, so that POPA and IRET do the "dirty work" for me. I get a GPF when IRET executes. But I really don't see what's wrong with my code! I set up a TSS and User Mode segments, so it should work... Here's the code just in case:
And here's what gpf_handler() prints on my screen:
Any ideas why this may not work?
Code: Select all
#define UCODE_START 0x600000
typedef u32int pid_t;
pid_t pid_top=1;
typedef struct process_struct
{
program_t *program; /* program desciptor for this process */
char cmdline[256]; /* command line that started the program */
registers_t regs; /* saved program registers */
pid_t pid;
page_directory_t *pdir; /* page directory for the process */
video_t vid; /* process video information */
void *kstack; /* kernel-based stack */
struct process_struct *next;
} __attribute__ ((packed)) process_t;
process_t *pqueue;
process_t *current_proc;
volatile void idle();
int sched_first=1;
void schedule(registers_t *regs)
{
/* switch processes. save registers and update context... */
if (!sched_first) memcpy(¤t_proc->regs, regs, sizeof(registers_t));
sched_first = 0;
if (current_proc->next != NULL)
{
current_proc = current_proc->next;
} else {
current_proc = pqueue;
};
memcpy(regs, ¤t_proc->regs, sizeof(registers_t));
set_kernel_stack(current_proc->kstack);
switch_page_directory(current_proc->pdir);
};
volatile void idle()
{
/* idle process */
printk("this is idle process!\n");
int x=0;
while (1)
{
x += 0;
};
};
char idle_stack[0x6000];
void gpf_handler(registers_t*);
/* init_sched() is here in my kernel. I omit it because it's irrelevant to the problem. */
int spawn(program_t *program, char *name, char *cmdline)
{
/* spawn a loaded program */
asm ("cli");
process_t *proc = (process_t*) kmalloc(sizeof(process_t));
if (!proc) return error(E_MEMORY);
proc->program = program;
strcpy(program->name, name);
strcpy(proc->cmdline, cmdline);
/* set up initial process registers */
proc->regs.ds = 0x23;
proc->regs.cs = 0x1B;
proc->regs.eax = proc->cmdline; /* point EAX to the command line */
proc->regs.ebx = program->name; /* the program may also need its name */
proc->regs.eip = UCODE_START;
u32int eflags;
asm volatile ("pushf; pop %0;" : "=r" (eflags));
eflags |= FLAG_BIT(9); /* wikipedia sais this should enable interrupts atomically */
proc->regs.eflags = eflags;
proc->kstack = UCODE_START + program->size + MT_STACK_SIZE + MT_STACK_SIZE;
proc->regs.esp = proc->regs.useresp = UCODE_START + program->size + MT_STACK_SIZE;
/* the rest */
int size = program->size;
int end = UCODE_START+size+(MT_STACK_SIZE*2)+1;
page_directory_t *pdir = create_user_directory(UCODE_START, UCODE_START+size+(MT_STACK_SIZE*2));
printhex(end); printk("\n");
/* make sure the program knowns it is at UCODE_START */
reloc(UCODE_START-(int) program->code, program->rtab, program->code, program->reloc_num);
/* move the code to a new location */
page_directory_t *tempdir = current_directory;
switch_page_directory(pdir);
memcpy(UCODE_START, program->code, size);
kfree(program->code);
program->code = UCODE_START;
switch_page_directory(tempdir);
/* other things */
proc->pdir = pdir;
proc->pid = pid_top++;
proc->next = NULL;
/* add it to the end of the list */
process_t *last = pqueue;
while (last->next != NULL) last = last->next;
//printk("spawn: last="); printhex(last); printk("\n");
last->next = proc;
printhex(proc->regs.ds); printk("\n");
asm ("sti");
return proc->pid;
};
void gpf_handler(registers_t *regs)
{
asm ("cli");
//regs->ds = 0x10;
printk("General Protection Fault\n");
printk("pid:\t"); printdec(current_proc->pid); printk("\n");
printk("eip:\t"); printhex(current_proc->regs.eip); printk("\n");
printk("reip:\t"); printhex(regs->eip); printk("\n");
printk("ds:\t"); printhex(regs->ds); printk("\n");
printk("cs:\t"); printhex(regs->cs); printk("\n");
printk("pds:\t"); printhex(current_proc->regs.ds); printk("\n");
printk("pcs:\t"); printhex(current_proc->regs.cs); printk("\n");
printk("ercd:\t"); printhex(regs->err_code); printk("\n");
printk("pesp:\t"); printhex(current_proc->regs.useresp); printk("\n");
u8int *val = (u8int*) regs->eip;
printhexb(*val); printk("\n");
asm ("hlt");
};
Code: Select all
General Protection Fault
pid: 1
eip: 0x00600000 <-- saved EIP (process)
reip: 0x00100245 <-- where GPF occures
ds: 0x00300023 <-- i have no idea why this suddenly has 2 extra bits set...
cs: 0x00000008
pds: 0x00000023 <-- saved DS value (so THAT's correct)
pcs: 0x0000001B
ercd: 0x00000000 <-- error code pushed by GPF
pesp: 0x0061001C <-- process stack