userland vm86mm

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
lode

userland vm86mm

Post by lode »

Hello.
I just registered to the forum, and I'd first like to introduce myself:
My name is Matti B?ckman and I am 21 year old university student from Finland. I have been doing hobby os development for quite some time, so I finally feel I might have something to contribute:

I'm currently writing an user mode vm86mm for my OS because I don't want to include the monitor into a microkernel.
Since the kernel support required is pretty minimal, I thought maybe someone else would be intrested in using my program.

It is not yet suitable for releasing, but soon.. soon..

Anyway using the monitor is simple:

monitor_main.cc

Code: Select all


extern int start_16bit;
extern int end_16bit;

int main()
{
   vm86regs regs;
   memset(&regs, 0, sizeof(regs));
   vm86create(64); // 64k mem for virtual machine
   // the memory is now mapped at 0 - 64k,
   // real mode vectors are copied into place
   
   // in real code we will load the binary from a file   
   char *p = (char*)(regs.cs<<4)+regs.eip;
   int length = (unsigned int)&end_16bit;
   length -= (unsigned int)&start_16bit;
   memcpy(p, &start_16bit, length);
   printf("entering vm86\n");

   while (1)
     {
        if(!vm86run(&regs))
          break; // returns 0 if cannot continue
        // the regs struct now contains updated values
        // analyse the reason for stopping and emulate
        // the correct instructions here
         if (p[(regs.cs<<4)+regs.eip] == ASM_INT3)
             break; // the breakpoint asm instruction is just one byte
                      // and it should not exist in any real program,
                      // so i think it is safe to use as an end marker
             
            // TODO: add analysis here
      }

   printf("vm86 finished\n");
   vm86destroy(); // unmap vm86 memory, release internal
                        //  kernel structure (backup for pmode regs)
   return 0;
}
and

blah.asm

Code: Select all

[bits 16]
[section .text]
[global start_16bit]
[global end_16bit]

start_16bit:
        mov ax, cs
        inc ax
        mov ss, ax
        mov ax, 0xffe
        mov sp, ax

        int3 ; terminate
end_16bit:
The kernel part only swaps the program state and registers between vm86 and 32bit pmode. The GPF fault handler triggers exit from vm86.

Please tell me what you think of this.
I'll write more when I manage to find some free time.
lode

Re:Hello + userland vm86mm

Post by lode »

return from interrupt handler contains this:

VM86REGS equals 16; the size of the vm86 segment registers that the cpu pushes and pops while leaving or entering a vm86 task.

OFFSET equals to sizeof(Interrupt::Regs) - VMREGS;

Code: Select all

  mov eax, [currentprocess]
  add eax, OFFSET
  ; .... start mod ....
  mov ebx, [eax - 12]  ; get eflags from the struct
  test ebx, 0x20000    ; test vm86 flag
  jz .naah
  add eax, VMREGS     ; modify the tss_esp0 value
  .naah:
  mov [asm_tss_esp0],eax
  ; ... end mod ...
  mov eax, [currentprocess]
  mov esp, eax
  mov ax, 0x23
  mov ds, ax
  popad
note that cr3 is updated from c++ code to avoid unneccessary switching if returning to the same task.

Fault handler, 0x0d = General Protection Fault

Code: Select all

        case 0x0d:
            if (r->eflags & (1<<17))
            {
                     currentprocess->vm86GPF();
                     return;
            }
            currentprocess->end(ERR_GPF);
Coming up next: The kernel vm86 entry/exit code
lode

Re:Hello + userland vm86mm

Post by lode »

The kernel part:

Code: Select all

#include <process.h>
#include <syscalldef.h>
#include <vm86.h>

extern unsigned short real_first_bytes[]; // saved in kstart.asm

void Process::vm86() // this is the (sub)syscall entry point
{
   switch(r.ebx)
     {
      case VM86_PREPARE:
        r.eax = vm86create();
        break;
      case VM86_DESTROY:
        r.eax = vm86destroy();
        break;
      case VM86_RUN:
        r.eax = vm86in();
        break;
     }
}

int Process::vm86create()
{
   if (vm86save)
     return -1;
   if (r.eax > 640)
     return -1;

   vm86mem = r.eax*1024;
   this->cr3->alloc(0,vm86mem);
   memcpy(1-1,real_first_bytes,0x400);
   return 0;
}

int Process::vm86destroy()
{
   if (!vm86save)
     return -1;
   delete vm86save;
   this->cr3->unalloc(0,vm86mem);
   return 0;
}

void Process::vm86GPF()
{
   vm86out();
}

// TODO: Security check the vm86 eflags values
int Process::vm86in()
{
   if (!vm86save)
     return -1;
   memcpy(vm86save, &this->r,sizeof(Interrupt::Regs));
   memcpy(&this->r, (char*)r.eax, sizeof(Interrupt::Regs));
   r.eflags |= (1<<17) | 0x200; // vm86, interrupts enabled
   printf("vm86in @ 0x%0x\n",(r.cs<<4)+r.eip);
   return 0;
}

void Process::vm86out()
{
   if (!vm86save)
     return;
   r.eflags &= ~(1<<17); // deactivate the VM flag
   memcpy((char*)vm86save->eax,&this->r,sizeof(Interrupt::Regs));
   printf("vm86out @ 0x%0x\n",(r.cs<<4)+r.eip);
   memcpy(&this->r, vm86save, sizeof(Interrupt::Regs));
}
lode

Re:userland vm86mm

Post by lode »

Almost forgot this: vm86.h

Code: Select all

#ifndef __vm86_h__
#define __vm86_h__
#include <syscalldef.h>

struct vm86regs
{
   int edi;
   int esi;
   int ebp;
   int zero_1;
   int ebx;
   int edx;
   int ecx;
   int eax;
   int zero_2;
   int zero_3;
   int ip;
   int cs;
   int eflags;
   int sp;
   int ss;
   int es;
   int ds;
   int fs;
   int gs;
} __attribute__((packed));

#define VM86_RUN 0x01
#define VM86_PREPARE 0x02
#define VM86_DESTROY 0x03

inline int vm86run(vm86regs *a)
{
   int i;
   asm("int $0x30":"=a"(i):"d"(SYS_VM86),"a"(a),"b"(VM86_RUN):"memory");
   return i;
}

inline void vm86create(int memory)
{
   asm("int $0x30"::"d"(SYS_VM86),"a"(memory),"b"(VM86_PREPARE):"memory");
}

inline void vm86destroy()
{
   asm("int $0x30"::"d"(SYS_VM86),"b"(VM86_DESTROY):"memory");
}


#endif
And the Interrupt::Regs struct

Code: Select all

   struct Regs {
      unsigned int edi;
      unsigned int esi;
      unsigned int ebp;
      unsigned int temp;
      unsigned int ebx;
      unsigned int edx;
      unsigned int ecx;
      unsigned int eax;
      unsigned int intno;
      unsigned int error;
      unsigned int eip;
      unsigned int cs;
      unsigned int eflags;
      unsigned int esp;
      unsigned int ss;
      unsigned int ves;
      unsigned int vds;
      unsigned int vfs;
      unsigned int vgs;
   } __attribute__((packed));
RetainSoftware

Re:userland vm86mm

Post by RetainSoftware »

I don't want to start but i can't help myself why the hell do you have attribute packed at every item. It suffices to do

Code: Select all

typedef struct m86regs
{
  int edi;
  int esi;
  int ebp;
  int zero_1;
  int ebx;
  int edx;
  int ecx;
  int eax;
  int zero_2;
  int zero_3;
  int ip;
  int cs;
  int eflags;
  int sp;
  int ss;
  int es;
  int ds;
  int fs;
  int gs;
} vm86regs __attribute__((packed));
lode

Re:userland vm86mm

Post by lode »

Oops.

Better? :)

As you may have guessed I haven't used packed structs very often before this project :P
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:userland vm86mm

Post by Solar »

Actually, it suffices to pass [tt]-fpack-struct[/tt] to GCC's command line unless there are structs in the same translation units you do not want to have packed...
Every good solution is obvious once you've found it.
User avatar
df
Member
Member
Posts: 1076
Joined: Fri Oct 22, 2004 11:00 pm
Contact:

Re:userland vm86mm

Post by df »

*cough* or your gcc doesnt pack on the struct level but does it on the item level.. (i remember a long conversation about this...:)
-- Stu --
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:userland vm86mm

Post by Pype.Clicker »

packing fields of a structure that's only made of ints ?? am i missing something here ?
lode

Re:userland vm86mm

Post by lode »

Nah, only that I should think (and/or) sleep before I code anything. ;)
Post Reply