GPF upon task-switch to Ring 3

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

GPF upon task-switch to Ring 3

Post 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?
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: GPF upon task-switch to Ring 3

Post by mariuszp »

anyone?
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: GPF upon task-switch to Ring 3

Post 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.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: GPF upon task-switch to Ring 3

Post 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...
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: GPF upon task-switch to Ring 3

Post 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.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
User avatar
iocoder
Member
Member
Posts: 208
Joined: Sun Oct 18, 2009 5:47 pm
Libera.chat IRC: iocoder
Location: Alexandria, Egypt | Ottawa, Canada
Contact:

Re: GPF upon task-switch to Ring 3

Post 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
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: GPF upon task-switch to Ring 3

Post 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??
Post Reply