Entering V8086 Mode

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
paul
Posts: 21
Joined: Sun Dec 31, 2006 10:19 am

Entering V8086 Mode

Post by paul »

Ok, so in my little kernel I have setup some basic multitasking, it can currently only handle tasks in ring 0, but thats not a problem for the moment.

I'm trying to setup a v8086 task (so that I can call int 10h) but all I can seem to get is an invalid opcode exception... I know my multitasking code works for 32bit pmode tasks, so I think the problem must be in how I setup the v8086 task...

In my init thread, I do this to call int 10h with 10c (132x60 text mode):

Code: Select all

struct regs *r = (struct regs *)mem.Calloc(sizeof(struct regs));
r->eax = 0x10c;
cpu.Interrupt(0x10, r);
mem.Free(r);
cpu.Interrupt looks like this:

Code: Select all

void ia32CPU::Interrupt(int num, struct regs *r)
{
	unsigned char *code = (unsigned char *)0x10000;

	*code++ = 0xCD;		// INT
	*code++ = num;
	*code++ = 0xCD;		// INT
	*code++ = 0x80;		// 0x80 = Kill task
	*code++ = 0xEB;		// Infinite...
	*code++ = 0xFE;		// ...Loop

	struct threadStruct *v8086thread = (struct threadStruct *)mem.Calloc(sizeof(struct threadStruct));
	scheduler.CreateThread(scheduler._kernelProcess, v8086thread, 0x1000);

	mem.Copy(&((ia32ThreadData *)v8086thread->archdata)->v8086regs, r, sizeof(struct threadStruct));
	r->cs = r->ds = r->es = r->fs = r->gs = r->ss = 0x1000;
	r->eip = 0;

	tasks.InitThreadv86(v8086thread, r);
	v8086thread->name = "V8086 Thread";

	scheduler.AddSchedule(v8086thread);

	while (v8086thread->state != TS_DEAD) ;

	mem.Copy(r, &((ia32ThreadData *)v8086thread->archdata)->v8086regs, sizeof(struct threadStruct));

	mem.Free(v8086thread);
}
and InitThreadv86 (where I suspect the problem is):

Code: Select all

void ia32Tasks::InitThreadv86(struct threadStruct *thread, struct regs *r)
{
	struct ia32ThreadData *adata = (struct ia32ThreadData *)mem.Calloc(sizeof(struct ia32ThreadData));

	unsigned int *stack = (unsigned int *)0x1FFF8;

	*--stack = r->gs;
	*--stack = r->fs;
	*--stack = r->ds;
	*--stack = r->es;

	*--stack = GDT_KDATA;
	*--stack = r->eip;

	*--stack = 0x20202;			// EFLAGS
	*--stack = r->ebp;
	*--stack = 0xFFF8;				// ESP
	*--stack = r->edi;
	*--stack = r->esi;
	*--stack = r->edx;
	*--stack = r->ecx;
	*--stack = r->ebx;
	*--stack = r->eax;

	*--stack = 0x10;
	*--stack = 0x10;
	*--stack = 0x10;
	*--stack = 0x10;

	adata->stack = stack;
	adata->esp = (unsigned int)stack;
	adata->v8086 = false;

	thread->archdata = (void *)adata;
}
I switch tasks like this:

Code: Select all

void ia32Tasks::Switch(struct threadStruct *old, struct threadStruct *thread)
{
	if ((old != null) && (((struct ia32ThreadData*)old->archdata)->v8086))
		return;

	cpu.BeginAtomic();

	//console.WriteLine("Switch from (%s::%s) to (%s::%s)",
	//	(old == null) ? "null" : old->proc->name,
	//	(old == null) ? "null" : old->name,
	//	thread->proc->name, thread->name);

	struct ia32ThreadData *data = (struct ia32ThreadData *)thread->archdata;

	oldesp = (old != null) ? &((struct ia32ThreadData*)old->archdata)->esp : null;
	newesp = data->esp;

	if (thread->proc->system)
	{
		if (data->v8086)
			globalTSS.esp = 0x1000;
		else
			globalTSS.esp = null;

		globalTSS.ss = null;
	}
	else
	{
		globalTSS.esp = newesp;
		globalTSS.ss = GDT_UDATA;
	}

	cpu.EndAtomic();

	//write_cr3((unsigned long)data->pageDirectory);
	taskDoSwitch();
}

Code: Select all

extern oldesp, newesp

global taskDoSwitch
taskDoSwitch:
	pushf                   ; Save registers
	pusha

	push ds
	push es
	push fs
	push gs

	mov eax, [oldesp]      ; Check if oldesp is null
	cmp eax, 0
	je switch

	mov [eax], esp			; Save old esp value

switch:
	mov esp, [newesp]     ; Switch to the new task

	pop gs
	pop fs
	pop es
	pop ds

	popa                    ; Restore registers
	popf
	ret
Oh, and the output I get from my OS in bochs:

Code: Select all

Fatal Error

An error has occured which the system cannot recover from. Please reboot the sys
tem and report any information shown below should the problem persist

Invalid Opcode Exception

GS 0x10  FS 0x10  ES 0x10  DS 0x10
EDI 0x10C  ESI 0x0  EBP 0x0  ESP 0x1FFCC  EBX 0x0  EDX 0x0  ECX 0xFFF8  EAX 0x0
INT 6h/6  ERR 0
EIP 0x3  CS 0x8  EFLAGS 0x10282  USRESP 0x0  SS 0x10
Sorry for the large amount of code posted, but I've been trying to get this working all day and am getting nowhere (now I'll wait for you gurus to spot hundreds of errors and make me seem stupid :P (please! any help would be appreciated))...
User avatar
B.E
Member
Member
Posts: 275
Joined: Sat Oct 21, 2006 5:29 pm
Location: Brisbane Australia
Contact:

Re: Entering V8086 Mode

Post by B.E »

paul wrote:

Code: Select all

void ia32CPU::Interrupt(int num, struct regs *r)
{
	unsigned char *code = (unsigned char *)0x10000;
        .......
	r->eip = 0;
        ........
}
.....

Code: Select all

GS 0x10  FS 0x10  ES 0x10  DS 0x10
EDI 0x10C  ESI 0x0  EBP 0x0  ESP 0x1FFCC  EBX 0x0  EDX 0x0  ECX 0xFFF8  EAX 0x0
INT 6h/6  ERR 0
EIP 0x3  CS 0x8  EFLAGS 0x10282  USRESP 0x0  SS 0x10
From the value of EIP I, would say it's starting at the wrong address, try changing

Code: Select all

	r->eip = 0;
To:

Code: Select all

	r->eip = 0x10000;
Image
Microsoft: "let everyone run after us. We'll just INNOV~1"
paul
Posts: 21
Joined: Sun Dec 31, 2006 10:19 am

Post by paul »

Thanks for the reply, if I do that I now get a coprocessor fault exception, so it looks like I'm not in v8086 mode at all... I notice the EFLAGS are 0x202 even though I set them to 0x20202 in InitThreadv86???

Code: Select all

Fatal Error

An error has occured which the system cannot recover from. Please reboot the sys
tem and report any information shown below should the problem persist

Coprocessor Fault Exception

GS 0x10  FS 0x10  ES 0x10  DS 0x10
EDI 0x10C  ESI 0x0  EBP 0x0  ESP 0x1FFD0  EBX 0x0  EDX 0x0  ECX 0xFFF8  EAX 0x0
INT 10h/16  ERR 0
EIP 0x10002  CS 0x8  EFLAGS 0x202  USRESP 0x10  SS 0x1000
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

You cannot enter v8086 mode with an popf. You should use an iret instruction to load the VM bit.

Also, you should be aware that ds/es/fs/gs will still contain real-mode values, and that your task switcher will break on the second occasion it changes to a v8086 task.

I really suggest you check the intel manuals for all the cheesy details
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
digo_rp
Member
Member
Posts: 233
Joined: Sun Jun 05, 2005 11:00 pm

Post by digo_rp »

like Combuster, to use vm86 tasks you only have to ways.

first: switching back RM.

second: setup a vm86 task like a pm32bits tasks. you just set

eflags: 0x20000 <- why that eflags. cuz you have all irq disable while running at vm86 mode, until you handle all irq in your kernel.

setup cs,ds,es,fs,gs to all segment must be below 1MB mark

like 0x1000 you´r using. and then all four real mode regs that came first in your task layout. before ss

*--p = cs; /* gs */
*--p = cs; /* fs */
*--p = cs; /* ds */
*--p = cs; /* es */
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

digo_rp wrote:eflags: 0x20000 <- why that eflags. cuz you have all irq disable while running at vm86 mode, until you handle all irq in your kernel.
Bad idea! This means that a vm86 task will not be pre-emptible at all - a user has to wait for the vm85 task to finish its work before he can continue. On an IRQ the processor switches back to PMode anyway (how do you expect to get control back from the vm86 task - usually you use a syscall interrupt).
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

pcmattman wrote:a user has to wait for the vm85 task to finish its work before he can continue.
Perhaps a vm85 task is the cooperative version of a preemptible vm86 task? :)
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Oops... I was really tired when I posted that... More coffee?
Post Reply