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 | } |