Virtual Mode Monitor problems

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
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:

Virtual Mode Monitor problems

Post by pcmattman »

I'm trying to implement my Virtual 8086 mode monitor to handle interrupts (so far) from v8086 code.

I have a couple of questions. Firstly, which stack do I push the EFLAGS, CS, and EIP so that when the interrupt I'm jumping to in the IVT's iret takes it to where I want it. Is it the user stack, or the tss stack? I ask this because I keep on getting an invalid opcode error on every iret from the handled interrupts (at EIP 0x3?).

Secondly, this is my IVT dump:

Code: Select all

<snip>Offsets were wrong...
I looked up Bochs code and found that F000:FF53 is just a dummy interrupt handler, nothing more than an 'iret'. Any ideas why my IVT is wrong? Does GRUB toy with it?

Finally, how do you guys handle invalid instructions that can't be handled? Do you just kill the process and then reschedule?

Edit: OK, this is my virtual mode monitor ATM:

Code: Select all

		// the IVT
		uint16_t* ivt = (uint16_t*) 0;
		
		// the stack
		uint16_t* stack = (uint16_t*) r->useresp;
		
		// instruction pointer
		uint8_t* ip = (uint8_t*) r->eip;
		
		// what was the instruction?
		switch( ip[0] )
		{
			// 0xCD: INT n
			case 0xCD:
				// interrupt!
				
				// decrement the stack 3 times
				stack -= 3;
				
				// set the new stack pointer
				r->useresp = ( ( r->useresp & 0xFFFF ) - 6 ) & 0xFFFF;
				
				// put the data on the stack
				stack[0] = (uint16_t) ( r->eip + 2 );
				stack[1] = r->cs;
				stack[2] = (uint16_t) r->eflags;
				
				// clear the interrupt flag
				if( r->eflags & 0x200 )
					r->eflags ^= 0x200;
				
				// jump to the necessary location
				r->cs = ivt[ ip[1] * 2 + 1 ];
				r->eip = ivt[ ip[1] * 2 ];
				
				// return, this will take us to the interrupt handler
				return;
			
			// default: ???
			default:
			
				// tell the user
				kprintf( "Unknown opcode at EIP 0x%x: 0x%x!\n", r->eip, ip[0] );
		}
This always crashes:

Code: Select all

I've been rudely interrupted by the processor with this message for you:
Invalid Opcode
Crash at 0x       3   EFLAGS: 0x   33082
Registers at time of crash:
Error code: 0
Interrupt number: 6
EAX: 0x       0   EBX: 0x       0 ECX: 0x       0 EDX: 0x       0
EBP: 0x       0   ESP: 0x  110784 ESI: 0x       0 EDI: 0x       0
V8086 SS:ESP
SS: 0x      20  ESP: 0x    28fe
Segments at time of crash:
CS: 0x       0  DS: 0x       0  ES: 0x       0
FS: 0x       0  GS: 0x       0  SS: 0x      20
Control registers:
CR0: 0x      11   CR2: 0x       0 CR3: 0x       0
User avatar
crackers
Member
Member
Posts: 27
Joined: Wed Nov 15, 2006 6:31 am

Re: Virtual Mode Monitor problems

Post by crackers »

pcmattman wrote:I'm trying to implement my Virtual 8086 mode monitor to handle interrupts (so far) from v8086 code.

I have a couple of questions. Firstly, which stack do I push the EFLAGS, CS, and EIP so that when the interrupt I'm jumping to in the IVT's iret takes it to where I want it. Is it the user stack, or the tss stack? I ask this because I keep on getting an invalid opcode error on every iret from the handled interrupts (at EIP 0x3?).

Secondly, this is my IVT dump:

Code: Select all

<snip>Offsets were wrong...
I looked up Bochs code and found that F000:FF53 is just a dummy interrupt handler, nothing more than an 'iret'. Any ideas why my IVT is wrong? Does GRUB toy with it?
iret is one of instructions that always (i think :? ) makes GPF, so inside your V86 monitor you should fill registers with data on stack, delete these infos from stack and jump back to program.
pcmattman wrote: Finally, how do you guys handle invalid instructions that can't be handled? Do you just kill the process and then reschedule?
It depends on the instruction. In most cases there is no other good solution than killing task, but sometimes it's possible to emulate some behaviour (for example SSE on procesor which does not support them).
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 »

Actually, IRET doesn't cause it. I can't actually get to the interrupt handle itself. I just get redirected every time to 0x3... I have no idea why this happens, and of course 0x3 is not executable (unless I load data there).

I think the problem is in my stack... When the GPF fires because of the instruction I'm meant to handle, what stack does it pop stuff off from? Is it the PL0 stack or the v8086 stack?

And can anyone point out any problems in my ISR handler?

Code: Select all

void fault_handler(struct regs *r)
{
	// check if it's a GPF and we're in V8086 mode
	if( r->eflags & 0x20000 && r->int_no == 13 )
	{
		// tell the user
		kprintf( "GPF in Virtual 8086 Mode task...\n" );
		
		// the IVT
		uint16_t* ivt = (uint16_t*) 0;
		
		// the stack
		uint16_t* stack = (uint16_t*) r->useresp;
		
		// instruction pointer
		uint8_t* ip = (uint8_t*) r->eip;
		
		// what was the instruction?
		switch( ip[0] )
		{
			// 0xCD: INT n
			case 0xCD:
				// interrupt!
				
				// decrement the stack 3 times
				stack -= 3;
				
				// set the new stack pointer
				r->useresp -= 6; // ( ( r->useresp & 0xFFFF ) - 6 ) & 0xFFFF;
				
				// put the data on the stack
				stack[0] = (uint16_t) ( r->eip + 2 );
				stack[1] = r->cs;
				stack[2] = (uint16_t) r->eflags;
				
				// set eflags
				r->eflags = 0x23002;
				
				// jump to the necessary location
				kprintf( "Jumping to 0x%x:0x%x\n", ivt[ ip[1] * 4 + 1 ], ivt[ ip[1] * 4 ] );
				r->cs = 0; //ivt[ ip[1] * 4 + 1 ];
				r->eip = 0x1003; // ivt[ ip[1] * 4 ];
				
				// return, this will take us to the interrupt handler
				return;
			
			// default: ???
			default:
			
				// tell the user
				kprintf( "Unknown opcode at EIP 0x%x: 0x%x!\n", r->eip, ip[0] );
		}
		
		// loop forever
		while( true );
	}
	
	// tell me info
	kprintf( "\nI've been rudely interrupted by the processor with this message for you:\n%s\n", exception_messages[r->int_no] );
	
	// print the registers
	kprintf( "Crash at 0x%8x\tEFLAGS: 0x%8x\n", r->eip, r->eflags );
	kprintf( "Registers at time of crash:\n" );
	kprintf( "Error code: %d\nInterrupt number: %d\n", r->err_code, r->int_no );
	kprintf( "EAX: 0x%8x\tEBX: 0x%8x\tECX: 0x%8x\tEDX: 0x%8x\n", r->eax, r->ebx, r->ecx, r->edx );
	kprintf( "EBP: 0x%8x\tESP: 0x%8x\tESI: 0x%8x\tEDI: 0x%8x\n", r->ebp, r->esp, r->esi, r->edi );
	if( r->eflags & 0x20000 )
	{
		kprintf( "V8086 SS:ESP\n" );
		kprintf( "SS: 0x%8x\tESP: 0x%8x\n", r->ss, r->useresp );
	}
	kprintf( "Segments at time of crash:\n" );
	kprintf( "CS: 0x%8x\tDS: 0x%8x\tES: 0x%8x\nFS: 0x%8x\tGS: 0x%8x\tSS: 0x%8x\n", r->cs, r->ds, r->es, r->fs, r->gs, r->ss );
	kprintf( "Control registers:\n" );
	kprintf( "CR0: 0x%8x\tCR2: 0x%8x\tCR3: 0x%8x\n", read_cr0(), read_cr2(), read_cr3() );

	// loop
	while( true );
}
Any ideas?
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 »

I figured out the problem: I was running the code in IOPL3, which wasn't letting my GPF handler do its magic...
Post Reply