Subversion Repositories shark

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 pj 1
/* Project:     OSLib
2
 * Description: The OS Construction Kit
3
 * Date:                1.6.2000
4
 * Idea by:             Luca Abeni & Gerardo Lamastra
5
 *
6
 * OSLib is an SO project aimed at developing a common, easy-to-use
7
 * low-level infrastructure for developing OS kernels and Embedded
8
 * Applications; it partially derives from the HARTIK project but it
9
 * currently is independently developed.
10
 *
11
 * OSLib is distributed under GPL License, and some of its code has
12
 * been derived from the Linux kernel source; also some important
13
 * ideas come from studying the DJGPP go32 extender.
14
 *
15
 * We acknowledge the Linux Community, Free Software Foundation,
16
 * D.J. Delorie and all the other developers who believe in the
17
 * freedom of software and ideas.
18
 *
19
 * For legalese, check out the included GPL license.
20
 */
21
 
22
/* File: Vm86.C
23
 *                             
24
 * VM86 mode switch routines!
25
 * This is basically an alternative way of invoking the
26
 * BIOS service routines; it is very useful to support
27
 * native VBE compliant Video card, without writing an explicit driver
28
 */
29
 
30
#include <ll/i386/mem.h>
31
#include <ll/i386/x-bios.h>
32
#include <ll/i386/hw-func.h>
33
#include <ll/i386/hw-instr.h>
34
#include <ll/i386/cons.h>
35
#include <ll/i386/error.h>
36
 
37
FILE(VM - 86);
38
 
39
/*
40
#define __LL_DEBUG__
41
#define __DUMB_CODE__
42
#define __CHK_IO__
43
*/
44
 
45
#define VM86_STACK_SIZE 1024
46
 
47
/* TSS optional section */
48
static BYTE vm86_stack0[VM86_STACK_SIZE];
49
 
50
static struct {
51
    TSS t;
52
    DWORD io_map[2048];
53
} vm86_TSS;
54
static LIN_ADDR vm86_stack;
55
static LIN_ADDR vm86_iretAddress;
56
 
57
 
58
DWORD *GLOBesp;
59
 
60
 
61
#ifdef __DUMB_CODE__
62
static LIN_ADDR vm86_code;
63
static BYTE prova86[] = {
64
    0x1e,                       /* push ds              */
65
    0xb8, 0x00, 0xb8,           /* mov ax,0xb800        */
66
    0x8e, 0xd8,                 /* mov ds,ax            */
67
    0xbf, 0x9e, 0x00,           /* mov di,0x009e (158)  */
68
    0xb0, 0x2a,                 /* mov ax,'*'           */
69
    0x88, 0x05,                 /* mov ds:[di],al       */
70
    0x1f,                       /* pop ds               */
71
    0xcd, 0x40,                 /*??? */
72
#ifdef __CHK_IO__
73
    0xb0, 0x00,                 /* movb   $0x0,%al */
74
    0x66, 0xba, 0x80, 0x00,     /* movw   $0x80,%dx */
75
    0x66, 0xef,                 /* outw %ax, (%dx) */
76
#endif
77
    0xcf,                       /* iret                 */
78
    0xf4,                       /* hlt                  */
79
 
80
};
81
#endif
82
 
83
#ifdef __LL_DEBUG__
84
static BYTE vm86_retAddr[] = {
85
    0x1e,                       /* push ds              */
86
    0xb8, 0x00, 0xb8,           /* mov ax,0xb800        */
87
    0x8e, 0xd8,                 /* mov ds,ax            */
88
    0xbf, 0x3e, 0x01,           /* mov di,0x013c (316)  */
89
    0xb0, '%',                  /* mov ax,'%'           */
90
    0x88, 0x05,                 /* mov ds:[di],al       */
91
    0x1f,                       /* pop ds               */
92
    0xcd, 0x48
93
};                              /* int 0x48             */
94
#else
95
static BYTE vm86_retAddr[] = { 0xcd, 0x48 };    /* int 48h              */
96
#endif
97
 
98
/* This is the return point from V86 mode, called through int 0x48
99
 * (see vm86-exc.s). We double check that this function is called in
100
 * the V86 TSS. Otherwise, Panic!!!
101
 */
102
void vm86_return(DWORD * tos)
103
{
104
    CONTEXT c = get_TR();
105
#ifdef __LL_DEBUG__
106
    DWORD cs, eip;
107
    void *esp;
108
    DWORD a;
109
/*    message("Gotta code=%d [0 called from GPF/1 int 0x48]\n",code);*/
110
#endif
111
    if (c == X_VM86_TSS) {
112
        GLOBesp = tos;
113
#ifdef __LL_DEBUG__
114
        message("TSS CS=%x IP=%lx\n", vm86_TSS.t.cs, vm86_TSS.t.eip);
115
        message("Switching to %x\n", vm86_TSS.t.back_link);
116
        a = (DWORD) (vm86_iretAddress);
117
        cs = (a & 0xFF000) >> 4;
118
        eip = (a & 0xFFF);
119
        message("Real-Mode Address is CS=%lx IP=%lx\nLinear=%lx\n", cs,
120
                eip, a);
121
        esp = (void *) (tos);
122
        message("Stack frame: %p %lx %lx\n",
123
                esp, vm86_TSS.t.esp0, vm86_TSS.t.esp);
124
        message("%lx ", lmempeekd(esp));        /* bp */
125
        message("%lx ", lmempeekd(esp + 4));    /* eip */
126
        message("%lx ", lmempeekd(esp + 8));    /* 0x0d */
127
        message("%lx\n", lmempeekd(esp + 12));  /* error code */
128
/* The error code is given by the selector causing shifted and or-ed with
129
   3 bits: [LDT/GDT | IDT | Ext/Int]
130
   If IDT == 1 -> the fault was provoked bu an interrupt (Internal if the
131
   Ext/Int bit is 0, External if the bit is 1)
132
   Else the LDT/GDT bit shows if the selector belongs to the LDT (if 1)
133
   or GDT (if 0)  
134
*/
135
        message("%lx ", lmempeekd(esp + 16));   /* EIP of faulting instr */
136
        message("%lx ", lmempeekd(esp + 20));   /* CS of faulting instr */
137
        message("%lx ", lmempeekd(esp + 24));   /* EFLAGS */
138
        message("%lx\n", lmempeekd(esp + 28));  /* old ESP */
139
        message("%lx ", lmempeekd(esp + 32));   /* old SS */
140
        message("%lx ", lmempeekd(esp + 36));   /* old ES */
141
        message("%lx ", lmempeekd(esp + 40));   /* old DS */
142
        message("%lx\n", lmempeekd(esp + 44));  /* old FS */
143
#endif
144
        ll_context_load(vm86_TSS.t.back_link);
145
    }
146
    halt();
147
}
148
 
149
extern void vm86_exc(void);
150
 
151
/* Just a debugging function; it dumps the status of the TSS */
152
void vm86_dump_TSS(void)
153
{
154
    BYTE acc, gran;
155
    DWORD base, lim;
156
    message("vm86_TSS.t dump\n");
157
    message("Flag: %lx\n", vm86_TSS.t.eflags);
158
    message("SS: %hx SP:%lx\n", vm86_TSS.t.ss, vm86_TSS.t.esp);
159
    message("Stack0: %hx:%lx\n", vm86_TSS.t.ss0, vm86_TSS.t.esp0);
160
    message("Stack1: %hx:%lx\n", vm86_TSS.t.ss1, vm86_TSS.t.esp1);
161
    message("Stack2: %hx:%lx\n", vm86_TSS.t.ss2, vm86_TSS.t.esp2);
162
    message("CS: %hx IP: %lx", vm86_TSS.t.cs, vm86_TSS.t.eip);
163
    message("DS: %hx\n", vm86_TSS.t.ds);
164
    base = GDT_read(X_VM86_TSS, &lim, &acc, &gran);
165
    message("Base : %lx Lim : %lx Acc : %x Gran %x\n",
166
            base, lim, (unsigned) (acc), (unsigned) (gran));
167
}
168
 
169
void vm86_init(void)
170
{
171
    int register i;
172
 
173
    /* Init the DOS memory allocator */
174
//    DOS_mem_init();
175
 
176
    /* First of all, we need to setup a GDT entries to
177
     * allow vm86 task execution. We just need a free 386 TSS, which
178
     * will be used to store the execution context of the virtual 8086
179
     * task
180
     */
181
    GDT_place(X_VM86_TSS, (DWORD) (&vm86_TSS),
182
              sizeof(vm86_TSS), FREE_TSS386, GRAN_16);
183
    IDT_place(0x48, vm86_exc);
184
 
185
    /* Prepare a real-mode stack, obtaining it from the
186
     * DOS memory allocator!
187
     * 8K should be OK! Stack top is vm86_stack + SIZE!
188
     */
189
    vm86_stack = DOS_alloc(VM86_STACK_SIZE * 2);
190
    vm86_stack += VM86_STACK_SIZE / 2;
191
 
192
    /* Create a location of DOS memory containing the
193
     * opcode sequence which will generate a GPF
194
     * We use the privileged instruction hlt to do it
195
     */
196
    vm86_iretAddress = DOS_alloc(sizeof(vm86_retAddr));
197
    memcpy(vm86_iretAddress, vm86_retAddr, sizeof(vm86_retAddr));
198
#ifdef __LL_DEBUG__
199
    message("PM reentry linear address=%p\n", vm86_iretAddress);
200
#endif
201
#ifdef __DUMB_CODE__
202
    vm86_code = DOS_alloc(2048);
203
    lmemcpy(vm86_code, prova86, sizeof(prova86));
204
#endif
205
    /* Zero the PM/Ring[1,2] ss:esp; they're unused! */
206
    vm86_TSS.t.esp1 = 0;
207
    vm86_TSS.t.esp2 = 0;
208
    vm86_TSS.t.ss1 = 0;
209
    vm86_TSS.t.ss2 = 0;
210
    /* Use only the GDT */
211
    vm86_TSS.t.ldt = 0;
212
    /* No paging activated */
213
    vm86_TSS.t.cr3 = 0;
214
    vm86_TSS.t.trap = 0;
215
    /* Yeah, free access to any I/O port; we trust BIOS anyway! */
216
    /* Here is the explanation: we have 65536 I/O ports... each bit
217
     * in the io_map masks/unmasks the exception for the given I/O port
218
     * If the bit is set, an exception is generated; otherwise, if the bit
219
     * is clear, everythings works fine...
220
     * Because of alignment problem, we need to add an extra byte all set
221
     * to 1, according to Intel manuals
222
     */
223
    vm86_TSS.t.io_base = (DWORD) (&(vm86_TSS.io_map)) -
224
        (DWORD) (&(vm86_TSS));
225
    for (i = 0; i < 2047; i++)
226
        vm86_TSS.io_map[i] = 0;
227
    vm86_TSS.io_map[2047] = 0xFF000000;
228
}
229
 
230
int vm86_callBIOS(int service, X_REGS16 * in, X_REGS16 * out,
231
                  X_SREGS16 * s)
232
{
233
    DWORD vm86_tmpAddr;
234
    DWORD vm86_flags, vm86_cs, vm86_ip;
235
    LIN_ADDR vm86_stackPtr;
236
    DWORD *IRQTable_entry;
237
 
238
    if (service < 0x10 || in == NULL)
239
        return -1;
240
    /* Setup the stack frame */
241
    vm86_tmpAddr = (DWORD) (vm86_stack);
242
    vm86_TSS.t.ss = (vm86_tmpAddr & 0xFF000) >> 4;
243
    vm86_TSS.t.ebp = vm86_TSS.t.esp = (vm86_tmpAddr & 0x0FFF)
244
        + VM86_STACK_SIZE - 6;
245
    /* Build an iret stack frame which returns to vm86_iretAddress */
246
    vm86_tmpAddr = (DWORD) (vm86_iretAddress);
247
    vm86_cs = (vm86_tmpAddr & 0xFF000) >> 4;
248
    vm86_ip = (vm86_tmpAddr & 0xFFF);
249
    vm86_flags = 0;             /* CPU_FLAG_VM | CPU_FLAG_IOPL; */
250
    vm86_stackPtr = vm86_stack + VM86_STACK_SIZE;
251
    lmempokew(vm86_stackPtr - 6, vm86_ip);
252
    lmempokew(vm86_stackPtr - 4, vm86_cs);
253
    lmempokew(vm86_stackPtr - 2, vm86_flags);
254
#ifdef __LL_DEBUG__
255
    message("Stack: %lx SS: %lx SP: %lx\n",
256
            vm86_tmpAddr + VM86_STACK_SIZE, (DWORD) vm86_TSS.t.ss,
257
            vm86_TSS.t.esp);
258
#endif
259
    /* Wanted VM86 mode + IOPL = 3! */
260
    vm86_TSS.t.eflags = CPU_FLAG_VM | CPU_FLAG_IOPL;
261
    /* Preload some standard values into the registers */
262
    vm86_TSS.t.ss0 = X_FLATDATA_SEL;
263
    vm86_TSS.t.esp0 = (DWORD) & (vm86_stack0[VM86_STACK_SIZE - 1]);
264
 
265
 
266
#ifdef __DUMB_CODE__
267
    vm86_TSS.t.cs = ((DWORD) (vm86_code) & 0xFFFF0) >> 4;
268
    vm86_TSS.t.eip = ((DWORD) (vm86_code) & 0x000F);
269
#ifdef __LL_DEBUG_
270
    message("(DUMB CODE) CS:%x IP:%x/%x\n",
271
            (DWORD) vm86_TSS.t.cs, vm86_TSS.t.eip, &prova86);
272
    message("(DUMB CODE) Go...\n");
273
#endif
274
    vm86_TSS.t.back_link = ll_context_save();
275
    ll_context_load(X_VM86_TSS);
276
 
277
#ifdef __LL_DEBUG_
278
    message("(DUMB CODE) I am back...\n");
279
#endif
280
#else
281
    /* Copy the parms from the X_*REGS structures in the vm86 TSS */
282
    vm86_TSS.t.eax = (DWORD) in->x.ax;
283
    vm86_TSS.t.ebx = (DWORD) in->x.bx;
284
    vm86_TSS.t.ecx = (DWORD) in->x.cx;
285
    vm86_TSS.t.edx = (DWORD) in->x.dx;
286
    vm86_TSS.t.esi = (DWORD) in->x.si;
287
    vm86_TSS.t.edi = (DWORD) in->x.di;
288
    /* IF Segment registers are required, copy them... */
289
    if (s != NULL) {
290
        vm86_TSS.t.es = (WORD) s->es;
291
        vm86_TSS.t.ds = (WORD) s->ds;
292
    } else {
293
        vm86_TSS.t.ds = vm86_TSS.t.ss;
294
        vm86_TSS.t.es = vm86_TSS.t.ss;
295
    }
296
    vm86_TSS.t.gs = vm86_TSS.t.ss;
297
    vm86_TSS.t.fs = vm86_TSS.t.ss;
298
    /* Execute the BIOS call, fetching the CS:IP of the real interrupt
299
     * handler from 0:0 (DOS irq table!)
300
     */
301
    IRQTable_entry = (void *) (0L);
302
    vm86_TSS.t.cs = ((IRQTable_entry[service]) & 0xFFFF0000) >> 16;
303
    vm86_TSS.t.eip = ((IRQTable_entry[service]) & 0x0000FFFF);
304
#ifdef __LL_DEBUG__
305
    message("CS:%hx IP:%lx\n", vm86_TSS.t.cs, vm86_TSS.t.eip);
306
#endif
307
    /* Let's use the ll standard call... */
308
    vm86_TSS.t.back_link = ll_context_save();
309
    ll_context_load(X_VM86_TSS);
310
#ifdef __LL_DEBUG__
311
    message("I am back...\n");
312
    message("TSS CS=%hx IP=%lx\n", vm86_TSS.t.cs, vm86_TSS.t.eip);
313
    {
314
        char *xp = (char *) (vm86_iretAddress + 0xe);
315
        message("PM reentry linear address=%p\n", vm86_iretAddress);
316
        message("Executing code: %x ", (unsigned char) (*xp));
317
        xp++;
318
        message("%x\n", (unsigned char) (*xp));
319
    }
320
#endif
321
    /* Send back in the X_*REGS structure the value obtained with
322
     * the real-mode interrupt call
323
     */
324
    if (out != NULL) {
325
/*
326
      out->x.ax = (WORD)vm86_TSS.t.eax;
327
      out->x.bx = (WORD)vm86_TSS.t.ebx;
328
      out->x.cx = (WORD)vm86_TSS.t.ecx;
329
      out->x.dx = (WORD)vm86_TSS.t.edx;
330
      out->x.si = (WORD)vm86_TSS.t.esi;
331
      out->x.di = (WORD)vm86_TSS.t.edi;
332
      out->x.cflag = (WORD)vm86_TSS.t.eflags;
333
*/
334
#ifdef __LL_DEBUG__
335
        message("%x\n", (WORD) * (GLOBesp));
336
        message("%x\n", (WORD) * (GLOBesp + 1));
337
        /*EDI*/ message("%x\n", (WORD) * (GLOBesp + 2));
338
        /*ESI*/ message("%x\n", (WORD) * (GLOBesp + 3));
339
        /*EBP*/ message("%x\n", (WORD) * (GLOBesp + 4));
340
        /*ESP*/ message("%x\n", (WORD) * (GLOBesp + 5));
341
        /*EBX*/ message("%x\n", (WORD) * (GLOBesp + 6));
342
        /*EDX*/
343
#endif
344
            out->x.ax = (WORD) * (GLOBesp + 8);
345
        out->x.bx = (WORD) * (GLOBesp + 5);
346
        out->x.cx = (WORD) * (GLOBesp + 7);
347
        out->x.dx = (WORD) * (GLOBesp + 6);
348
        out->x.si = (WORD) * (GLOBesp + 2);
349
        out->x.di = (WORD) * (GLOBesp + 1);
350
        out->x.cflag = (WORD) * (GLOBesp);
351
    }
352
    if (s != NULL) {
353
        s->es = vm86_TSS.t.es;
354
        s->ds = vm86_TSS.t.ds;
355
    }
356
#endif
357
    return 1;
358
}