Blame |
Last modification |
View Log
| RSS feed
/* Project: OSLib
* Description: The OS Construction Kit
* Date: 1.6.2000
* Idea by: Luca Abeni & Gerardo Lamastra
*
* OSLib is an SO project aimed at developing a common, easy-to-use
* low-level infrastructure for developing OS kernels and Embedded
* Applications; it partially derives from the HARTIK project but it
* currently is independently developed.
*
* OSLib is distributed under GPL License, and some of its code has
* been derived from the Linux kernel source; also some important
* ideas come from studying the DJGPP go32 extender.
*
* We acknowledge the Linux Community, Free Software Foundation,
* D.J. Delorie and all the other developers who believe in the
* freedom of software and ideas.
*
* For legalese, check out the included GPL license.
*/
/* File: Vm86.C
*
* VM86 mode switch routines!
* This is basically an alternative way of invoking the
* BIOS service routines; it is very useful to support
* native VBE compliant Video card, without writing an explicit driver
*/
#include <ll/i386/mem.h>
#include <ll/i386/x-bios.h>
#include <ll/i386/hw-func.h>
#include <ll/i386/hw-instr.h>
#include <ll/i386/cons.h>
#include <ll/i386/error.h>
FILE
(VM
- 86);
/*
#define __LL_DEBUG__
#define __DUMB_CODE__
#define __CHK_IO__
*/
#define VM86_STACK_SIZE 1024
/* TSS optional section */
static BYTE vm86_stack0
[VM86_STACK_SIZE
];
static struct {
TSS t
;
DWORD io_map
[2048];
} vm86_TSS
;
static LIN_ADDR vm86_stack
;
static LIN_ADDR vm86_iretAddress
;
DWORD
*GLOBesp
;
#ifdef __DUMB_CODE__
static LIN_ADDR vm86_code
;
static BYTE prova86
[] = {
0x1e, /* push ds */
0xb8, 0x00, 0xb8, /* mov ax,0xb800 */
0x8e, 0xd8, /* mov ds,ax */
0xbf, 0x9e, 0x00, /* mov di,0x009e (158) */
0xb0, 0x2a, /* mov ax,'*' */
0x88, 0x05, /* mov ds:[di],al */
0x1f, /* pop ds */
0xcd, 0x40, /*??? */
#ifdef __CHK_IO__
0xb0, 0x00, /* movb $0x0,%al */
0x66, 0xba, 0x80, 0x00, /* movw $0x80,%dx */
0x66, 0xef, /* outw %ax, (%dx) */
#endif
0xcf, /* iret */
0xf4, /* hlt */
0
};
#endif
#ifdef __LL_DEBUG__
static BYTE vm86_retAddr
[] = {
0x1e, /* push ds */
0xb8, 0x00, 0xb8, /* mov ax,0xb800 */
0x8e, 0xd8, /* mov ds,ax */
0xbf, 0x3e, 0x01, /* mov di,0x013c (316) */
0xb0, '%', /* mov ax,'%' */
0x88, 0x05, /* mov ds:[di],al */
0x1f, /* pop ds */
0xcd, 0x48
}; /* int 0x48 */
#else
static BYTE vm86_retAddr
[] = { 0xcd, 0x48 }; /* int 48h */
#endif
/* This is the return point from V86 mode, called through int 0x48
* (see vm86-exc.s). We double check that this function is called in
* the V86 TSS. Otherwise, Panic!!!
*/
void vm86_return
(DWORD
* tos
)
{
CONTEXT c
= get_TR
();
#ifdef __LL_DEBUG__
DWORD cs
, eip
;
void *esp
;
DWORD a
;
/* message("Gotta code=%d [0 called from GPF/1 int 0x48]\n",code);*/
#endif
if (c
== X_VM86_TSS
) {
GLOBesp
= tos
;
#ifdef __LL_DEBUG__
message
("TSS CS=%x IP=%lx\n", vm86_TSS.
t.
cs, vm86_TSS.
t.
eip);
message
("Switching to %x\n", vm86_TSS.
t.
back_link);
a
= (DWORD
) (vm86_iretAddress
);
cs
= (a
& 0xFF000) >> 4;
eip
= (a
& 0xFFF);
message
("Real-Mode Address is CS=%lx IP=%lx\nLinear=%lx\n", cs
,
eip
, a
);
esp
= (void *) (tos
);
message
("Stack frame: %p %lx %lx\n",
esp
, vm86_TSS.
t.
esp0, vm86_TSS.
t.
esp);
message
("%lx ", lmempeekd
(esp
)); /* bp */
message
("%lx ", lmempeekd
(esp
+ 4)); /* eip */
message
("%lx ", lmempeekd
(esp
+ 8)); /* 0x0d */
message
("%lx\n", lmempeekd
(esp
+ 12)); /* error code */
/* The error code is given by the selector causing shifted and or-ed with
3 bits: [LDT/GDT | IDT | Ext/Int]
If IDT == 1 -> the fault was provoked bu an interrupt (Internal if the
Ext/Int bit is 0, External if the bit is 1)
Else the LDT/GDT bit shows if the selector belongs to the LDT (if 1)
or GDT (if 0)
*/
message
("%lx ", lmempeekd
(esp
+ 16)); /* EIP of faulting instr */
message
("%lx ", lmempeekd
(esp
+ 20)); /* CS of faulting instr */
message
("%lx ", lmempeekd
(esp
+ 24)); /* EFLAGS */
message
("%lx\n", lmempeekd
(esp
+ 28)); /* old ESP */
message
("%lx ", lmempeekd
(esp
+ 32)); /* old SS */
message
("%lx ", lmempeekd
(esp
+ 36)); /* old ES */
message
("%lx ", lmempeekd
(esp
+ 40)); /* old DS */
message
("%lx\n", lmempeekd
(esp
+ 44)); /* old FS */
#endif
ll_context_load
(vm86_TSS.
t.
back_link);
}
halt
();
}
extern void vm86_exc
(void);
/* Just a debugging function; it dumps the status of the TSS */
void vm86_dump_TSS
(void)
{
BYTE acc
, gran
;
DWORD base
, lim
;
message
("vm86_TSS.t dump\n");
message
("Flag: %lx\n", vm86_TSS.
t.
eflags);
message
("SS: %hx SP:%lx\n", vm86_TSS.
t.
ss, vm86_TSS.
t.
esp);
message
("Stack0: %hx:%lx\n", vm86_TSS.
t.
ss0, vm86_TSS.
t.
esp0);
message
("Stack1: %hx:%lx\n", vm86_TSS.
t.
ss1, vm86_TSS.
t.
esp1);
message
("Stack2: %hx:%lx\n", vm86_TSS.
t.
ss2, vm86_TSS.
t.
esp2);
message
("CS: %hx IP: %lx", vm86_TSS.
t.
cs, vm86_TSS.
t.
eip);
message
("DS: %hx\n", vm86_TSS.
t.
ds);
base
= GDT_read
(X_VM86_TSS
, &lim
, &acc
, &gran
);
message
("Base : %lx Lim : %lx Acc : %x Gran %x\n",
base
, lim
, (unsigned) (acc
), (unsigned) (gran
));
}
void vm86_init
(void)
{
int register i
;
/* Init the DOS memory allocator */
// DOS_mem_init();
/* First of all, we need to setup a GDT entries to
* allow vm86 task execution. We just need a free 386 TSS, which
* will be used to store the execution context of the virtual 8086
* task
*/
GDT_place
(X_VM86_TSS
, (DWORD
) (&vm86_TSS
),
sizeof(vm86_TSS
), FREE_TSS386
, GRAN_16
);
IDT_place
(0x48, vm86_exc
);
/* Prepare a real-mode stack, obtaining it from the
* DOS memory allocator!
* 8K should be OK! Stack top is vm86_stack + SIZE!
*/
vm86_stack
= DOS_alloc
(VM86_STACK_SIZE
* 2);
vm86_stack
+= VM86_STACK_SIZE
/ 2;
/* Create a location of DOS memory containing the
* opcode sequence which will generate a GPF
* We use the privileged instruction hlt to do it
*/
vm86_iretAddress
= DOS_alloc
(sizeof(vm86_retAddr
));
memcpy(vm86_iretAddress
, vm86_retAddr
, sizeof(vm86_retAddr
));
#ifdef __LL_DEBUG__
message
("PM reentry linear address=%p\n", vm86_iretAddress
);
#endif
#ifdef __DUMB_CODE__
vm86_code
= DOS_alloc
(2048);
lmemcpy
(vm86_code
, prova86
, sizeof(prova86
));
#endif
/* Zero the PM/Ring[1,2] ss:esp; they're unused! */
vm86_TSS.
t.
esp1 = 0;
vm86_TSS.
t.
esp2 = 0;
vm86_TSS.
t.
ss1 = 0;
vm86_TSS.
t.
ss2 = 0;
/* Use only the GDT */
vm86_TSS.
t.
ldt = 0;
/* No paging activated */
vm86_TSS.
t.
cr3 = 0;
vm86_TSS.
t.
trap = 0;
/* Yeah, free access to any I/O port; we trust BIOS anyway! */
/* Here is the explanation: we have 65536 I/O ports... each bit
* in the io_map masks/unmasks the exception for the given I/O port
* If the bit is set, an exception is generated; otherwise, if the bit
* is clear, everythings works fine...
* Because of alignment problem, we need to add an extra byte all set
* to 1, according to Intel manuals
*/
vm86_TSS.
t.
io_base = (DWORD
) (&(vm86_TSS.
io_map)) -
(DWORD
) (&(vm86_TSS
));
for (i
= 0; i
< 2047; i
++)
vm86_TSS.
io_map[i
] = 0;
vm86_TSS.
io_map[2047] = 0xFF000000;
}
int vm86_callBIOS
(int service
, X_REGS16
* in
, X_REGS16
* out
,
X_SREGS16
* s
)
{
DWORD vm86_tmpAddr
;
DWORD vm86_flags
, vm86_cs
, vm86_ip
;
LIN_ADDR vm86_stackPtr
;
DWORD
*IRQTable_entry
;
if (service
< 0x10 || in
== NULL
)
return -1;
/* Setup the stack frame */
vm86_tmpAddr
= (DWORD
) (vm86_stack
);
vm86_TSS.
t.
ss = (vm86_tmpAddr
& 0xFF000) >> 4;
vm86_TSS.
t.
ebp = vm86_TSS.
t.
esp = (vm86_tmpAddr
& 0x0FFF)
+ VM86_STACK_SIZE
- 6;
/* Build an iret stack frame which returns to vm86_iretAddress */
vm86_tmpAddr
= (DWORD
) (vm86_iretAddress
);
vm86_cs
= (vm86_tmpAddr
& 0xFF000) >> 4;
vm86_ip
= (vm86_tmpAddr
& 0xFFF);
vm86_flags
= 0; /* CPU_FLAG_VM | CPU_FLAG_IOPL; */
vm86_stackPtr
= vm86_stack
+ VM86_STACK_SIZE
;
lmempokew
(vm86_stackPtr
- 6, vm86_ip
);
lmempokew
(vm86_stackPtr
- 4, vm86_cs
);
lmempokew
(vm86_stackPtr
- 2, vm86_flags
);
#ifdef __LL_DEBUG__
message
("Stack: %lx SS: %lx SP: %lx\n",
vm86_tmpAddr
+ VM86_STACK_SIZE
, (DWORD
) vm86_TSS.
t.
ss,
vm86_TSS.
t.
esp);
#endif
/* Wanted VM86 mode + IOPL = 3! */
vm86_TSS.
t.
eflags = CPU_FLAG_VM
| CPU_FLAG_IOPL
;
/* Preload some standard values into the registers */
vm86_TSS.
t.
ss0 = X_FLATDATA_SEL
;
vm86_TSS.
t.
esp0 = (DWORD
) & (vm86_stack0
[VM86_STACK_SIZE
- 1]);
#ifdef __DUMB_CODE__
vm86_TSS.
t.
cs = ((DWORD
) (vm86_code
) & 0xFFFF0) >> 4;
vm86_TSS.
t.
eip = ((DWORD
) (vm86_code
) & 0x000F);
#ifdef __LL_DEBUG_
message
("(DUMB CODE) CS:%x IP:%x/%x\n",
(DWORD
) vm86_TSS.
t.
cs, vm86_TSS.
t.
eip, &prova86
);
message
("(DUMB CODE) Go...\n");
#endif
vm86_TSS.
t.
back_link = ll_context_save
();
ll_context_load
(X_VM86_TSS
);
#ifdef __LL_DEBUG_
message
("(DUMB CODE) I am back...\n");
#endif
#else
/* Copy the parms from the X_*REGS structures in the vm86 TSS */
vm86_TSS.
t.
eax = (DWORD
) in
->x.
ax;
vm86_TSS.
t.
ebx = (DWORD
) in
->x.
bx;
vm86_TSS.
t.
ecx = (DWORD
) in
->x.
cx;
vm86_TSS.
t.
edx = (DWORD
) in
->x.
dx;
vm86_TSS.
t.
esi = (DWORD
) in
->x.
si;
vm86_TSS.
t.
edi = (DWORD
) in
->x.
di;
/* IF Segment registers are required, copy them... */
if (s
!= NULL
) {
vm86_TSS.
t.
es = (WORD
) s
->es
;
vm86_TSS.
t.
ds = (WORD
) s
->ds
;
} else {
vm86_TSS.
t.
ds = vm86_TSS.
t.
ss;
vm86_TSS.
t.
es = vm86_TSS.
t.
ss;
}
vm86_TSS.
t.
gs = vm86_TSS.
t.
ss;
vm86_TSS.
t.
fs = vm86_TSS.
t.
ss;
/* Execute the BIOS call, fetching the CS:IP of the real interrupt
* handler from 0:0 (DOS irq table!)
*/
IRQTable_entry
= (void *) (0L);
vm86_TSS.
t.
cs = ((IRQTable_entry
[service
]) & 0xFFFF0000) >> 16;
vm86_TSS.
t.
eip = ((IRQTable_entry
[service
]) & 0x0000FFFF);
#ifdef __LL_DEBUG__
message
("CS:%hx IP:%lx\n", vm86_TSS.
t.
cs, vm86_TSS.
t.
eip);
#endif
/* Let's use the ll standard call... */
vm86_TSS.
t.
back_link = ll_context_save
();
ll_context_load
(X_VM86_TSS
);
#ifdef __LL_DEBUG__
message
("I am back...\n");
message
("TSS CS=%hx IP=%lx\n", vm86_TSS.
t.
cs, vm86_TSS.
t.
eip);
{
char *xp
= (char *) (vm86_iretAddress
+ 0xe);
message
("PM reentry linear address=%p\n", vm86_iretAddress
);
message
("Executing code: %x ", (unsigned char) (*xp
));
xp
++;
message
("%x\n", (unsigned char) (*xp
));
}
#endif
/* Send back in the X_*REGS structure the value obtained with
* the real-mode interrupt call
*/
if (out
!= NULL
) {
/*
out->x.ax = (WORD)vm86_TSS.t.eax;
out->x.bx = (WORD)vm86_TSS.t.ebx;
out->x.cx = (WORD)vm86_TSS.t.ecx;
out->x.dx = (WORD)vm86_TSS.t.edx;
out->x.si = (WORD)vm86_TSS.t.esi;
out->x.di = (WORD)vm86_TSS.t.edi;
out->x.cflag = (WORD)vm86_TSS.t.eflags;
*/
#ifdef __LL_DEBUG__
message
("%x\n", (WORD
) * (GLOBesp
));
message
("%x\n", (WORD
) * (GLOBesp
+ 1));
/*EDI*/ message
("%x\n", (WORD
) * (GLOBesp
+ 2));
/*ESI*/ message
("%x\n", (WORD
) * (GLOBesp
+ 3));
/*EBP*/ message
("%x\n", (WORD
) * (GLOBesp
+ 4));
/*ESP*/ message
("%x\n", (WORD
) * (GLOBesp
+ 5));
/*EBX*/ message
("%x\n", (WORD
) * (GLOBesp
+ 6));
/*EDX*/
#endif
out
->x.
ax = (WORD
) * (GLOBesp
+ 8);
out
->x.
bx = (WORD
) * (GLOBesp
+ 5);
out
->x.
cx = (WORD
) * (GLOBesp
+ 7);
out
->x.
dx = (WORD
) * (GLOBesp
+ 6);
out
->x.
si = (WORD
) * (GLOBesp
+ 2);
out
->x.
di = (WORD
) * (GLOBesp
+ 1);
out
->x.
cflag = (WORD
) * (GLOBesp
);
}
if (s
!= NULL
) {
s
->es
= vm86_TSS.
t.
es;
s
->ds
= vm86_TSS.
t.
ds;
}
#endif
return 1;
}