Page 1 of 1

INT 13 / 16 On Virtual 8086 Handler

Posted: Mon Jun 18, 2012 8:55 am
by arabasso
Hi. I am developing a 2 stages boot loader. The first stage is loaded with a FD / CD / HD by the BIOS, then it loads the second stage (which is generic for all). The second stage makes all the initialization of a generic GDT / IDT, and goes into protected mode. So far so good...

So as not to create a specific driver for each FD / HD / CD, I thought of using the INT 13H BIOS, read the memory information using INT 15H and INT 16H use for the keyboard keys, implementing a Virtual 8086 handler. My Virtual 8086 Handler is as follows:

Code: Select all

typedef struct
{
	unsigned int ds, es, fs, gs;
	unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;
	unsigned int intNo, errCode;
	unsigned int eip;
	unsigned int cs, eflags;
	unsigned int uesp, ss;

} Registers;

typedef struct
{
	unsigned int ds, es, fs, gs;
	unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;
	unsigned int intNo, errCode;
	unsigned int eip;
	unsigned int cs, eflags;
	unsigned int vesp, ss;
	unsigned int ves, vds, vfs, vgs;

} VirtualRegisters;

typedef struct
{
	Registers * regs;
	unsigned long stack;

} State;

typedef struct
{
	unsigned short ax, bx, cx, dx;
	unsigned short ds, es, fs, gs;
	unsigned short di, si, bp, sp;
	unsigned short flags;

} VirtualContext;

#define VIRTUAL_CMD_O32								0x66
#define VIRTUAL_CMD_A32								0x67
#define VIRTUAL_CMD_NOP								0x90
#define VIRTUAL_CMD_PUSHF							0x9C
#define VIRTUAL_CMD_POPF							0x9D
#define VIRTUAL_CMD_INT								0xCD
#define VIRTUAL_CMD_IRET							0xCF
#define VIRTUAL_CMD_HLT								0xF4
#define VIRTUAL_CMD_CLI								0xFA
#define VIRTUAL_CMD_STI								0xFB
#define VIRTUAL_CMD_INB								0xEC
#define VIRTUAL_CMD_INW								0xED
#define VIRTUAL_CMD_OUTB							0xEE
#define VIRTUAL_CMD_OUTW							0xEF

#define VIRTUAL_CMD_EFLAGS_IF						(1 << 9)
#define VIRTUAL_CMD_EFLAGS_VM						(1 << 17)

#define VIRTUAL_VALID_EFLAGS						0x7FFF

#define VIRTUAL_NP_TO_LINEAR(seg, off)				((void *) ((seg & 0xFFFF) << 4) + (off & 0xFFFF))
#define VIRTUAL_FP_TO_LINEAR(addr)					((void *) ((addr & 0xFFFF0000) >> 12) + (addr & 0xFFFF))

#define callVirtual()								asm("int	$0x00")

static int vif = 1;

unsigned long stack;

void handlerVirtual(State * state)
{
	unsigned char  * ip      = VIRTUAL_NP_TO_LINEAR(state->regs->cs, state->regs->eip);
    unsigned short * stack16 = VIRTUAL_NP_TO_LINEAR(state->regs->ss, state->regs->uesp);
	unsigned int   * stack32 = (unsigned int *) stack16;

	int done, o32;

	done = o32 = 0;

	while (!done)
	{
		switch (* ip)
		{
			case VIRTUAL_CMD_O32:
			{
				o32 = 1;

				ip++;
				state->regs->eip++;
			}
			break;

			case VIRTUAL_CMD_A32:
			{
				ip++;
				state->regs->eip++;
			}
			break;

			case VIRTUAL_CMD_PUSHF:
			{
				if (o32)
				{
					stack32--;
					stack32[0] = state->regs->eflags & VIRTUAL_VALID_EFLAGS;

					if (vif)
						stack32[0] |= VIRTUAL_CMD_EFLAGS_IF;

					else
						stack32[0] &= ~VIRTUAL_CMD_EFLAGS_IF;

					state->regs->uesp -= 4;
				}

				else
				{
					stack16--;
					stack16[0] = state->regs->eflags;

					if (vif)
						stack16[0] |= VIRTUAL_CMD_EFLAGS_IF;

					else
						stack16[0] &= ~VIRTUAL_CMD_EFLAGS_IF;

					state->regs->uesp -= 2;
				}

				state->regs->eip++;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_POPF:
			{
				if (o32)
				{
					state->regs->eflags = VIRTUAL_CMD_EFLAGS_VM | (stack32[0] & VIRTUAL_VALID_EFLAGS);

					state->regs->uesp += 4;

					vif = ((stack32[0] & VIRTUAL_CMD_EFLAGS_IF) != 0);
				}

				else
				{
					state->regs->eflags = VIRTUAL_CMD_EFLAGS_VM | stack16[0];

					state->regs->uesp += 2;

					vif = ((stack16[0] & VIRTUAL_CMD_EFLAGS_IF) != 0);
				}

				state->regs->eip++;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_INT:
			{
				unsigned short * ivt = (unsigned short *) 0x00;

				stack16 -= 3;
				state->regs->uesp -= 6;

				stack16[0] = state->regs->eip + 2;
				stack16[1] = state->regs->cs;
				stack16[2] = state->regs->eflags;

				if (vif)
					stack16[2] |= VIRTUAL_CMD_EFLAGS_IF;

				else
					stack16[2] &= ~VIRTUAL_CMD_EFLAGS_IF;

				state->regs->cs  = ivt[(ip[1] << 1) + 1];
				state->regs->eip = ivt[ip[1] << 1];

				done = 1;
			}
			break;

			case VIRTUAL_CMD_IRET:
			{
				state->regs->eip    = stack16[0];
				state->regs->cs     = stack16[1];
				state->regs->eflags = VIRTUAL_CMD_EFLAGS_VM | VIRTUAL_CMD_EFLAGS_IF | stack16[2];
				state->regs->uesp  += 6;

				vif = ((stack16[2] & VIRTUAL_CMD_EFLAGS_IF) != 0);

				done = 1;
			}
			break;

			case VIRTUAL_CMD_CLI:
			{
				state->regs->eip++;

				vif = 0;

				state->regs->eflags &= ~VIRTUAL_CMD_EFLAGS_IF;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_STI:
			{
				state->regs->eip++;

				vif = 1;

				state->regs->eflags |= VIRTUAL_CMD_EFLAGS_IF;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_INB:
			{
				state->regs->eax = inb(state->regs->edx);

				state->regs->eip++;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_OUTB:
			{
				outb(state->regs->edx, state->regs->eax);

				state->regs->eip++;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_INW:
			{
				state->regs->eax = inw(state->regs->edx);

				state->regs->eip++;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_OUTW:
			{
				if (o32)
					outw(state->regs->edx, state->regs->eax);

				else
					outw(state->regs->edx, state->regs->eax);

				state->regs->eip++;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_NOP:
			{
				state->regs->eip++;

				done = 1;
			}
			break;

			case VIRTUAL_CMD_HLT:
			{
				state->stack = stack;

				done = 1;
			}
			break;

			default:
			{
				printf("%X\n", * ip);

				state->stack = stack;

				done = 1;
			}
			break;
		}
	}
}
Well, my 8086 Virtual handler works perfectly well for (int install the handler in the IDT 13 (GPF), and initialize to "int 0" (which has no handler, forcing a GPF), and ends with a "HLT" instruction. My problem is that this handler simply does not work with the 13H and 16H specifically (the INTs 10H, 15H and others works ok), and by the way, I have to emulate other instructions that do not know. My question is: has anyone implemented the use of INTs 13H and 16H within the Virtual 8086 Monitor?

One more thing, also implemented a way to return to the "Real Mode" in my bootloader. It would be better to return to the RMODE to use BIOS functions, instead use the monitor V86?

Note: This code would only be used in the boot loader in my kernel I would not use THIS FORM.

Re: INT 13 / 16 On Virtual 8086 Handler

Posted: Mon Jun 18, 2012 9:23 am
by qw
OSDev Wiki wrote:Using VM86 for disk access

Though theoretically possible, it is probably not a good idea. Most BIOS disk access will include IRQ handlers, DMA transfers (uncontrollable from within your VM monitor), and may stick in VM86 task while the BIOS waits for an interrupt response while a 'good' driver would have let the CPU free for other processes.

Re: INT 13 / 16 On Virtual 8086 Handler

Posted: Mon Jun 18, 2012 9:42 am
by arabasso
Hobbes wrote:
OSDev Wiki wrote:Using VM86 for disk access

Though theoretically possible, it is probably not a good idea. Most BIOS disk access will include IRQ handlers, DMA transfers (uncontrollable from within your VM monitor), and may stick in VM86 task while the BIOS waits for an interrupt response while a 'good' driver would have let the CPU free for other processes.
As I said earlier, I have no intention of using this "model" in my kernel, only the bootloader to avoid having to create device drivers inside. So it would be advisable to return to RMODE run the int 13H, and return to the PMODE?

Re: INT 13 / 16 On Virtual 8086 Handler

Posted: Mon Jun 18, 2012 1:08 pm
by Combuster
No, it's not.

Running int 13h (disk services) or 16h (keyboard) live off IRQs. In a V8086 environment, you will have to forward the corresponding IRQs to the virtual machine. If you want to drop back to real mode, you have a bigger problem because you will have to revert all changes to the chipset you have made so far - and even that is not guaranteed to work as you might have already lost interrupts or tripped things out of an emulation mode. Similarly, real mode prevents interrupts from reaching your kernel, and breaks all forms of protection mechanism you might want.

So outside the bootloader, you shouldn't be doing that. Keep your trips to protected mode at minimum until you're ready to leave real mode until the next reboot.

Re: INT 13 / 16 On Virtual 8086 Handler

Posted: Mon Jun 18, 2012 6:43 pm
by miker00lz
I haven't done this from a V8086 mode myself, but I think the best way would be to have the monitor make equivalent native calls to your kernel and wait for them to return. In my 8086 PC emu, the only int 13h function calls that I support emulation of are: 0, 1, 2, 3, 4, 5, and 8. All other function numbers I just set the carry flag and return. I haven't had any compatibility problems yet.

I haven't ventured into protected mode OS dev yet, so maybe this is a bad idea for reasons I am not thinking of.

Re: INT 13 / 16 On Virtual 8086 Handler

Posted: Tue Jun 19, 2012 1:39 am
by qw
My advice would be to stay in real mode until it is no longer necessary, and then not use the BIOS again. If you want to stay in protected mode, you should supply your own device driver.