[prev in list] [next in list] [prev in thread] [next in thread]
List: linux-assembly
Subject: Re: How to Use the vm86 System Call
From: "Richard Cooper" <peajay () funrestraints ! com>
Date: 2005-09-16 6:28:11
Message-ID: op.sw6mw9tnnro9m0 () sucks ! airplane ! fire
[Download RAW message or body]
I've looked over the source for LRMI, and here's what I've discovered about the vm86 \
call, just in case someone cares. Feel free to turn this into a webpage or something \
if you feel the urge.
I should add that LRMI (Linux Real Mode Interface) is the finest piece of code I've \
ever seen. I'm used to not liking other people's code at all, but I like this code \
better than code that I write myself. It's easy to read, there are very few comments \
but the variable and function names are nearly always descriptive, and it looks as if \
the author considered every possible thing that might happen and made the code handle \
it appropriately. So if it provides everything that you need, I'd recommend that you \
just use the LRMI code instead of writing your own. Unless you're some kind of \
genius, I don't think your code will be any better. This code is just awesome.
Overview:
=========
When you call vm86 (the number 113 vm86, at least), the real-mode process inherits \
your processes address space. So before calling vm86, you need to map everything you \
want in the real-mode address space into your address space, at the offsets where you \
want it to be at. You also need to allocate any RAM that you want to exist in real \
mode.
Zero the entire vm86_struct, as it seems that fields not mentioned here should be set \
to zero.
Then you set the int_revectored field of vm86_struct to specify which interrupts you \
want to receive from real-mode, and which ones you want Linux to handle automatically \
for you. (I assume it just calls the real-mode interrupt handler.)
Then you load the vm86_regs structure in vm_86_struct with what you want the initial \
register contents of the real mode process to be. Depending on what you are doing, \
you will probably want to set up a stack at least.
The real-mode process inherits your FS and GS selectors, the fs and gs fields in \
vm86_regs are (I guess) only for return values. You can either leave them as they \
are, giving the real-mode process access to your protected mode address space via FS \
and GS, or you can load them with real-mode selectors before calling vm86.
Then you call vm86. vm86 runs until either a real-mode interrupt occurs, a \
not-allowed-in-vm86-mode opcode is executed, or some other miscellaneous error \
occurs. A handy way to allow your real-mode code to exit the vm86 call is to have it \
call interrupt 255, which you previously set to be one which you wish to receive.
Upon return from vm86, the vm86_regs structure contains all of the register contents \
from the real-mode process.
The return value of vm86 contains two fields, a type field and an argument field, the \
type field being the lowest 8 bits, and the argument field being the eight bits above \
that. If the type field is VM86_INTx, then the argument field is the number of the \
interrupt that was called.
Various Details:
================
There are two vm86 system calls. LRMI makes sure to get the one that is numbered \
113. This is the one declared as such:
int vm86(struct vm86_struct *);
The other one takes an additional parameter, about which I know nothing.
Here is vm86_struct:
struct vm86_struct {
struct vm86_regs regs;
unsigned long flags;
unsigned long screen_bitmap;
unsigned long cpu_type;
struct revectored_struct int_revectored;
struct revectored_struct int21_revectored;
};
LRMI begins by simply setting the entire thing to zero.
flags, screen_bitmap, cpu_type, and int21_revectored are never accessed by LRMI, \
except when it zeros the entire structure, so I don't know what any of them do.
int_revectored appears to be a bitmask, with one bit for each interrupt. Setting a \
bit to 0 tells Linux that you want it to emulate that interrupt. Setting a bit to 1 \
tells Linux that you want to service that interrupt. I'm not sure if this is \
reliable, as there's also code in LRMI that emulates the interrupt if Linux doesn't, \
but maybe that's just a failsafe.
vm86_regs looks like this:
struct vm86_regs {
/*
* normal regs, with special meaning for the segment descriptors..
*/
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
long __null_ds;
long __null_es;
long __null_fs;
long __null_gs;
long orig_eax;
long eip;
unsigned short cs, __csh;
long eflags;
long esp;
unsigned short ss, __ssh;
/*
* these are specific to v86 mode:
*/
unsigned short es, __esh;
unsigned short ds, __dsh;
unsigned short fs, __fsh;
unsigned short gs, __gsh;
};
All of the register fields are what you would expect. LRMI never accesses orig_eax, \
or any of the __null_ segment registers.
Linux ignores the fs and gs fields. (I think that's worth mentioning twice.) LRMI \
loads it's own FS and GS with the real-mode process' FS and GS before calling vm86, \
and restores it's old FS and GS after the call returns. It does not save the \
contents of FS and GS upon return from vm86, leading me to believe that Linux saves \
them in vm86_regs.
LRMI maps address 0x00000 size 0x400 into it's memory space, so that it has the \
origional interrupt table from when the computer booted. It also maps address \
0x00400 size 0x00102, as this is the BIOS data area. It also maps address 0xA0000 \
size 0x60000, to map in any ROMs that may be in that area.
LRMI maps an anonymous block of /dev/zero of size 0x40000 to address 0x10000. It \
uses this memory in it's own internal LRMI_alloc_real function which allocates memory \
in the real-mode address space.
Although the real-mode process inherits your address space, it does not appear to \
inherit your I/O permissions. Either that or the code to emulate I/O instructions in \
LRMI is never being used.
These are the possible type codes for the vm86 return value:
#define VM86_SIGNAL 0 /* return due to signal */
#define VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
#define VM86_INTx 2 /* int3/int x instruction (ARG = x) */
#define VM86_STI 3 /* sti/popf/iret instruction enabled virtual \
interrupts */
If the return code is VM86_UNKNOWN, then LRMI checks to see if it's an opcode that it \
knows how to emulate. It emulates every opcode that operates on an I/O port, and \
thus I assume that those opcodes must be emulated. Additionally, it emulates segment \
overrides, data size overrides, and repeat prefixes, taking into account the \
direction flag. It ignores the address size override, as well as the F0 and F2 \
prefixes.
If the instruction isn't one which it knows how to emulate, it appears to have a nice \
piece of code that prints a register dump of the real mode registers. It also calls \
this function if the return value type is anything other than VM86_INTx or \
VM86_UNKNOWN.
I believe that it's possible that nothing has to be done for VM86_STI. Judging from \
the comment in the structure above, it sounds like it's mearly a signal to let you \
know that interrupts were enabled.
Additionally, I believe VM86_SIGNAL is just like the EINTR return value of many other \
functions, that is, it simply indicates that your process recieved a signal, but that \
the vm86 process is otherwise just fine. This might be useful if you wish to only \
allow the vm86 process to run for a limited time. You could use the alarm or itimer \
system calls to schedual an alarm signal, and when that signal occurs, the vm86 call \
will exit.
That's it, hopefully I didn't forget anything.
-
To unsubscribe from this list: send the line "unsubscribe linux-assembly" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic