Rev 1618 | Details | Compare with Previous | 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 | |||
40 | pj | 30 | #include <ll/i386/hw-data.h> |
31 | #include <ll/i386/hw-instr.h> |
||
32 | #include <ll/i386/hw-func.h> |
||
2 | pj | 33 | #include <ll/i386/mem.h> |
34 | #include <ll/i386/x-bios.h> |
||
40 | pj | 35 | #include <ll/i386/x-dosmem.h> |
2 | pj | 36 | #include <ll/i386/cons.h> |
37 | #include <ll/i386/error.h> |
||
451 | giacomo | 38 | #include <ll/i386/apic.h> |
39 | #include <ll/i386/advtimer.h> |
||
2 | pj | 40 | |
40 | pj | 41 | FILE(VM-86); |
2 | pj | 42 | |
43 | /* |
||
44 | #define __LL_DEBUG__ |
||
45 | #define __DUMB_CODE__ |
||
46 | #define __CHK_IO__ |
||
47 | */ |
||
48 | |||
443 | giacomo | 49 | //#define __LL_DEBUG__ |
313 | giacomo | 50 | |
444 | giacomo | 51 | #define VM86_STACK_SIZE 8192 |
2 | pj | 52 | |
40 | pj | 53 | extern DWORD ll_irq_table[256]; |
650 | giacomo | 54 | extern unsigned char use_apic, use_tsc; |
40 | pj | 55 | |
2 | pj | 56 | /* TSS optional section */ |
40 | pj | 57 | static BYTE vm86_stack0[VM86_STACK_SIZE]; |
2 | pj | 58 | |
477 | giacomo | 59 | static BYTE init = 0; |
60 | |||
2 | pj | 61 | static struct { |
62 | TSS t; |
||
63 | DWORD io_map[2048]; |
||
64 | } vm86_TSS; |
||
65 | static LIN_ADDR vm86_stack; |
||
66 | static LIN_ADDR vm86_iretAddress; |
||
67 | |||
40 | pj | 68 | struct registers *global_regs; |
443 | giacomo | 69 | WORD VM86_ret_ctx; |
2 | pj | 70 | |
71 | #ifdef __DUMB_CODE__ |
||
72 | static LIN_ADDR vm86_code; |
||
73 | static BYTE prova86[] = { |
||
40 | pj | 74 | 0x1e, /* push ds */ |
75 | 0xb8,0x00,0xb8, /* mov ax,0xb800 */ |
||
76 | 0x8e,0xd8, /* mov ds,ax */ |
||
77 | 0xbf,0x9e,0x00, /* mov di,0x009e (158) */ |
||
78 | 0xb0,0x2a, /* mov ax,'*' */ |
||
79 | 0x88,0x05, /* mov ds:[di],al */ |
||
80 | 0x1f, /* pop ds */ |
||
81 | 0xcd, 0x40, /*???*/ |
||
82 | #ifdef __CHK_IO__ |
||
83 | 0xb0, 0x00, /* movb $0x0,%al*/ |
||
84 | 0x66,0xba, 0x80, 0x00, /* movw $0x80,%dx */ |
||
85 | 0x66,0xef, /* outw %ax, (%dx) */ |
||
86 | #endif |
||
87 | 0xcf, /* iret */ |
||
88 | 0xf4, /* hlt */ |
||
89 | 0}; |
||
2 | pj | 90 | #endif |
91 | |||
444 | giacomo | 92 | static BYTE vm86_retAddr[] = {0xcd, 0x48, /* int 48h */ |
93 | 0xf4, |
||
94 | 0}; |
||
2 | pj | 95 | |
40 | pj | 96 | TSS *vm86_get_tss(void) |
97 | { |
||
98 | return &(vm86_TSS.t); |
||
99 | } |
||
100 | |||
2 | pj | 101 | /* Just a debugging function; it dumps the status of the TSS */ |
102 | void vm86_dump_TSS(void) |
||
103 | { |
||
40 | pj | 104 | BYTE acc,gran; |
105 | DWORD base,lim; |
||
2 | pj | 106 | message("vm86_TSS.t dump\n"); |
40 | pj | 107 | message("Flag: %lx\n",vm86_TSS.t.eflags); |
108 | message("SS: %hx SP:%lx\n", vm86_TSS.t.ss,vm86_TSS.t.esp); |
||
109 | message("Stack0: %hx:%lx\n",vm86_TSS.t.ss0,vm86_TSS.t.esp0); |
||
110 | message("Stack1: %hx:%lx\n",vm86_TSS.t.ss1,vm86_TSS.t.esp1); |
||
111 | message("Stack2: %hx:%lx\n",vm86_TSS.t.ss2,vm86_TSS.t.esp2); |
||
112 | message("CS: %hx IP: %lx",vm86_TSS.t.cs, vm86_TSS.t.eip); |
||
113 | message("DS: %hx\n",vm86_TSS.t.ds); |
||
114 | base = GDT_read(X_VM86_TSS,&lim,&acc,&gran); |
||
2 | pj | 115 | message("Base : %lx Lim : %lx Acc : %x Gran %x\n", |
40 | pj | 116 | base,lim,(unsigned)(acc),(unsigned)(gran)); |
2 | pj | 117 | } |
118 | |||
119 | void vm86_init(void) |
||
120 | { |
||
121 | int register i; |
||
40 | pj | 122 | |
477 | giacomo | 123 | if (init != 0) return; |
124 | init = 1; |
||
125 | |||
2 | pj | 126 | /* First of all, we need to setup a GDT entries to |
127 | * allow vm86 task execution. We just need a free 386 TSS, which |
||
128 | * will be used to store the execution context of the virtual 8086 |
||
129 | * task |
||
130 | */ |
||
40 | pj | 131 | GDT_place(X_VM86_TSS,(DWORD)(&vm86_TSS), |
132 | sizeof(vm86_TSS),FREE_TSS386,GRAN_16); |
||
2 | pj | 133 | |
443 | giacomo | 134 | /* Return Registers */ |
135 | global_regs = DOS_alloc(sizeof(struct registers)); |
||
40 | pj | 136 | |
2 | pj | 137 | /* Prepare a real-mode stack, obtaining it from the |
138 | * DOS memory allocator! |
||
139 | * 8K should be OK! Stack top is vm86_stack + SIZE! |
||
140 | */ |
||
40 | pj | 141 | vm86_stack = DOS_alloc(VM86_STACK_SIZE*2); |
142 | vm86_stack += VM86_STACK_SIZE/2; |
||
143 | |||
2 | pj | 144 | vm86_iretAddress = DOS_alloc(sizeof(vm86_retAddr)); |
40 | pj | 145 | memcpy(vm86_iretAddress,vm86_retAddr,sizeof(vm86_retAddr)); |
2 | pj | 146 | #ifdef __LL_DEBUG__ |
40 | pj | 147 | message("PM reentry linear address=0x%lx\n", (DWORD)vm86_iretAddress); |
2 | pj | 148 | #endif |
149 | #ifdef __DUMB_CODE__ |
||
150 | vm86_code = DOS_alloc(2048); |
||
40 | pj | 151 | lmemcpy(vm86_code,prova86,sizeof(prova86)); |
2 | pj | 152 | #endif |
153 | /* Zero the PM/Ring[1,2] ss:esp; they're unused! */ |
||
154 | vm86_TSS.t.esp1 = 0; |
||
155 | vm86_TSS.t.esp2 = 0; |
||
156 | vm86_TSS.t.ss1 = 0; |
||
157 | vm86_TSS.t.ss2 = 0; |
||
158 | /* Use only the GDT */ |
||
159 | vm86_TSS.t.ldt = 0; |
||
160 | /* No paging activated */ |
||
161 | vm86_TSS.t.cr3 = 0; |
||
162 | vm86_TSS.t.trap = 0; |
||
163 | /* Yeah, free access to any I/O port; we trust BIOS anyway! */ |
||
164 | /* Here is the explanation: we have 65536 I/O ports... each bit |
||
165 | * in the io_map masks/unmasks the exception for the given I/O port |
||
166 | * If the bit is set, an exception is generated; otherwise, if the bit |
||
167 | * is clear, everythings works fine... |
||
168 | * Because of alignment problem, we need to add an extra byte all set |
||
169 | * to 1, according to Intel manuals |
||
170 | */ |
||
40 | pj | 171 | vm86_TSS.t.io_base = (DWORD)(&(vm86_TSS.io_map)) - |
172 | (DWORD)(&(vm86_TSS)); |
||
173 | for (i = 0; i < 2047; i++) vm86_TSS.io_map[i] = 0; |
||
2 | pj | 174 | vm86_TSS.io_map[2047] = 0xFF000000; |
175 | } |
||
176 | |||
40 | pj | 177 | int vm86_callBIOS(int service,X_REGS16 *in,X_REGS16 *out,X_SREGS16 *s) |
2 | pj | 178 | { |
179 | DWORD vm86_tmpAddr; |
||
40 | pj | 180 | DWORD vm86_flags, vm86_cs,vm86_ip; |
2 | pj | 181 | LIN_ADDR vm86_stackPtr; |
182 | DWORD *IRQTable_entry; |
||
444 | giacomo | 183 | BYTE p1,p2; |
619 | mauro | 184 | DWORD msr1 = 0,msr2 = 0; |
443 | giacomo | 185 | |
186 | SYS_FLAGS f; |
||
444 | giacomo | 187 | |
443 | giacomo | 188 | if (service < 0x10 || in == NULL) return -1; |
40 | pj | 189 | |
443 | giacomo | 190 | f = ll_fsave(); |
191 | |||
2 | pj | 192 | /* Setup the stack frame */ |
40 | pj | 193 | vm86_tmpAddr = (DWORD)(vm86_stack); |
2 | pj | 194 | vm86_TSS.t.ss = (vm86_tmpAddr & 0xFF000) >> 4; |
195 | vm86_TSS.t.ebp = vm86_TSS.t.esp = (vm86_tmpAddr & 0x0FFF) |
||
40 | pj | 196 | + VM86_STACK_SIZE - 6; |
2 | pj | 197 | /* Build an iret stack frame which returns to vm86_iretAddress */ |
40 | pj | 198 | vm86_tmpAddr = (DWORD)(vm86_iretAddress); |
2 | pj | 199 | vm86_cs = (vm86_tmpAddr & 0xFF000) >> 4; |
200 | vm86_ip = (vm86_tmpAddr & 0xFFF); |
||
444 | giacomo | 201 | vm86_flags = 0; |
2 | pj | 202 | vm86_stackPtr = vm86_stack + VM86_STACK_SIZE; |
40 | pj | 203 | lmempokew(vm86_stackPtr-6,vm86_ip); |
204 | lmempokew(vm86_stackPtr-4,vm86_cs); |
||
205 | lmempokew(vm86_stackPtr-2,vm86_flags); |
||
2 | pj | 206 | #ifdef __LL_DEBUG__ |
207 | message("Stack: %lx SS: %lx SP: %lx\n", |
||
40 | pj | 208 | vm86_tmpAddr + VM86_STACK_SIZE,(DWORD)vm86_TSS.t.ss,vm86_TSS.t.esp); |
2 | pj | 209 | #endif |
210 | /* Wanted VM86 mode + IOPL = 3! */ |
||
443 | giacomo | 211 | vm86_TSS.t.eflags = CPU_FLAG_VM + CPU_FLAG_IOPL; |
2 | pj | 212 | /* Preload some standard values into the registers */ |
213 | vm86_TSS.t.ss0 = X_FLATDATA_SEL; |
||
40 | pj | 214 | vm86_TSS.t.esp0 = (DWORD)&(vm86_stack0[VM86_STACK_SIZE-1]); |
215 | |||
2 | pj | 216 | #ifdef __DUMB_CODE__ |
40 | pj | 217 | vm86_TSS.t.cs = ((DWORD)(vm86_code) & 0xFFFF0) >> 4; |
218 | vm86_TSS.t.eip = ((DWORD)(vm86_code) & 0x000F); |
||
2 | pj | 219 | #ifdef __LL_DEBUG_ |
220 | message("(DUMB CODE) CS:%x IP:%x/%x\n", |
||
40 | pj | 221 | (DWORD)vm86_TSS.t.cs,vm86_TSS.t.eip,&prova86); |
2 | pj | 222 | message("(DUMB CODE) Go...\n"); |
223 | #endif |
||
443 | giacomo | 224 | |
225 | p1 = inp(0x21); |
||
226 | p2 = inp(0xA1); |
||
227 | outp(0x21,0xFF); |
||
228 | outp(0xA1,0xFF); |
||
444 | giacomo | 229 | |
619 | mauro | 230 | if (use_apic) { |
451 | giacomo | 231 | rdmsr(APIC_BASE_MSR,msr1,msr2); |
232 | disable_APIC_timer(); |
||
619 | mauro | 233 | } |
451 | giacomo | 234 | |
444 | giacomo | 235 | vm86_TSS.t.back_link = ll_context_save(); |
236 | VM86_ret_ctx = vm86_TSS.t.back_link |
||
451 | giacomo | 237 | sti(); |
2 | pj | 238 | ll_context_load(X_VM86_TSS); |
451 | giacomo | 239 | cli(); |
2 | pj | 240 | |
619 | mauro | 241 | if (use_apic) { |
451 | giacomo | 242 | wrmsr(APIC_BASE_MSR,msr1,msr2); |
243 | enable_APIC_timer(); |
||
619 | mauro | 244 | } |
451 | giacomo | 245 | |
443 | giacomo | 246 | outp(0x21,p1); |
247 | outp(0xA1,p2); |
||
248 | |||
2 | pj | 249 | #ifdef __LL_DEBUG_ |
250 | message("(DUMB CODE) I am back...\n"); |
||
251 | #endif |
||
252 | #else |
||
253 | /* Copy the parms from the X_*REGS structures in the vm86 TSS */ |
||
40 | pj | 254 | vm86_TSS.t.eax = (DWORD)in->x.ax; |
255 | vm86_TSS.t.ebx = (DWORD)in->x.bx; |
||
256 | vm86_TSS.t.ecx = (DWORD)in->x.cx; |
||
257 | vm86_TSS.t.edx = (DWORD)in->x.dx; |
||
258 | vm86_TSS.t.esi = (DWORD)in->x.si; |
||
259 | vm86_TSS.t.edi = (DWORD)in->x.di; |
||
2 | pj | 260 | /* IF Segment registers are required, copy them... */ |
261 | if (s != NULL) { |
||
40 | pj | 262 | vm86_TSS.t.es = (WORD)s->es; |
263 | vm86_TSS.t.ds = (WORD)s->ds; |
||
2 | pj | 264 | } else { |
40 | pj | 265 | vm86_TSS.t.ds = vm86_TSS.t.ss; |
266 | vm86_TSS.t.es = vm86_TSS.t.ss; |
||
2 | pj | 267 | } |
40 | pj | 268 | vm86_TSS.t.gs = vm86_TSS.t.ss; |
269 | vm86_TSS.t.fs = vm86_TSS.t.ss; |
||
2 | pj | 270 | /* Execute the BIOS call, fetching the CS:IP of the real interrupt |
271 | * handler from 0:0 (DOS irq table!) |
||
272 | */ |
||
40 | pj | 273 | IRQTable_entry = (void *)(0L); |
274 | vm86_TSS.t.cs= ((IRQTable_entry[service]) & 0xFFFF0000) >> 16; |
||
2 | pj | 275 | vm86_TSS.t.eip = ((IRQTable_entry[service]) & 0x0000FFFF); |
40 | pj | 276 | #ifdef __LL_DEBUG__ |
277 | message("CS:%x IP:%lx\n", vm86_TSS.t.cs, vm86_TSS.t.eip); |
||
2 | pj | 278 | #endif |
279 | /* Let's use the ll standard call... */ |
||
443 | giacomo | 280 | |
281 | p1 = inp(0x21); |
||
282 | p2 = inp(0xA1); |
||
283 | outp(0x21,0xFF); |
||
284 | outp(0xA1,0xFF); |
||
444 | giacomo | 285 | |
619 | mauro | 286 | if (use_apic) { |
451 | giacomo | 287 | rdmsr(APIC_BASE_MSR,msr1,msr2); |
288 | disable_APIC_timer(); |
||
619 | mauro | 289 | } |
451 | giacomo | 290 | |
444 | giacomo | 291 | vm86_TSS.t.back_link = ll_context_save(); |
292 | VM86_ret_ctx = vm86_TSS.t.back_link; |
||
443 | giacomo | 293 | sti(); |
2 | pj | 294 | ll_context_load(X_VM86_TSS); |
451 | giacomo | 295 | cli(); |
443 | giacomo | 296 | |
619 | mauro | 297 | if (use_apic) { |
451 | giacomo | 298 | wrmsr(APIC_BASE_MSR,msr1,msr2); |
299 | enable_APIC_timer(); |
||
619 | mauro | 300 | } |
451 | giacomo | 301 | |
443 | giacomo | 302 | outp(0x21,p1); |
303 | outp(0xA1,p2); |
||
304 | |||
40 | pj | 305 | #ifdef __LL_DEBUG__ |
2 | pj | 306 | message("I am back...\n"); |
307 | message("TSS CS=%hx IP=%lx\n", vm86_TSS.t.cs, vm86_TSS.t.eip); |
||
308 | #endif |
||
309 | /* Send back in the X_*REGS structure the value obtained with |
||
310 | * the real-mode interrupt call |
||
311 | */ |
||
312 | if (out != NULL) { |
||
443 | giacomo | 313 | |
40 | pj | 314 | out->x.ax = global_regs->eax; |
315 | out->x.bx = global_regs->ebx; |
||
316 | out->x.cx = global_regs->ecx; |
||
317 | out->x.dx = global_regs->edx; |
||
318 | out->x.si = global_regs->esi; |
||
319 | out->x.di = global_regs->edi; |
||
320 | out->x.cflag = global_regs->flags; |
||
443 | giacomo | 321 | |
322 | //message("ax = %d bx = %d cx = %d dx = %d\n",out->x.ax,out->x.bx,out->x.cx,out->x.dx); |
||
323 | //message("si = %d di = %d\n",out->x.si,out->x.di); |
||
324 | |||
2 | pj | 325 | } |
326 | if (s != NULL) { |
||
40 | pj | 327 | s->es = vm86_TSS.t.es; |
328 | s->ds = vm86_TSS.t.ds; |
||
2 | pj | 329 | } |
330 | #endif |
||
443 | giacomo | 331 | |
444 | giacomo | 332 | ll_frestore(f); |
443 | giacomo | 333 | |
444 | giacomo | 334 | return 1; |
335 | |||
2 | pj | 336 | } |