Page 1 of 1

GPF upon task-switch to Ring 3

Posted: Sat Jun 04, 2011 2:22 pm
by mariuszp
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:

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(&current_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, &current_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");
};
And here's what gpf_handler() prints on my screen:

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
Any ideas why this may not work?

Re: GPF upon task-switch to Ring 3

Posted: Sun Jun 05, 2011 6:43 am
by mariuszp
anyone?

Re: GPF upon task-switch to Ring 3

Posted: Sun Jun 05, 2011 7:35 am
by thepowersgang
A little hard to tell, without seeing all the code, but a search of the intel manuals (2A IRET) suggests that you may not have set SS correctly, which is required for IRET to function correctly when changing privilege levels.

Re: GPF upon task-switch to Ring 3

Posted: Sun Jun 05, 2011 7:43 am
by mariuszp
thepowersgang wrote:A little hard to tell, without seeing all the code, but a search of the intel manuals (2A IRET) suggests that you may not have set SS correctly, which is required for IRET to function correctly when changing privilege levels.
Well, now I'm not getting the GPF, but I get a Page Fault, it seems that a NULL pointer is being referenced...

EDIT: Also it's telling me that it can't dispatch interrupt 0x21 (33), I wonder where that's coming from...

EDIT: You may want to see the paging code...

Re: GPF upon task-switch to Ring 3

Posted: Sun Jun 05, 2011 10:24 am
by thepowersgang
I suggest that you invest some time into learning Bochs and it's builtin debugger, and qemu's GDB stub, both will save you much anguish.

Also, read the intel manuals (and AMD ones too). Although they are sometimes hard to read, the document exactly what the CPU is doing, and why errors occur.

Re: GPF upon task-switch to Ring 3

Posted: Sun Jun 05, 2011 3:33 pm
by iocoder
well, try to do a "cli" before u switch to ring 3 "IRQs might be the reason why faults happen", set your first instruction in the ring 3 program to "jmp ." and see the results... if there is no error, you might need to edit your IDT.
Also, remember that u should have one TSS at least if you will use rings switch:
http://wiki.osdev.org/Context_Switching
Regards

Re: GPF upon task-switch to Ring 3

Posted: Mon Jun 06, 2011 12:00 pm
by mariuszp
I said I made the TSS... and i am definetely certain that IRQs don'tp cause exceptions, because the timer IRQ works just fine. Also, it appears that the code does not cause an exception under VirtualBox, but it still fires interrupt 0x21.

EDIT: Interrupt 33 (0x21) means IRQ1 in my OS, isn't that the keyboard IRQ??