works for PCI vga cards too
[coreboot.git] / src / devices / emulator / x86emu / sys.c
1 /****************************************************************************
2 *
3 *                                               Realmode X86 Emulator Library
4 *
5 *               Copyright (C) 1996-1999 SciTech Software, Inc.
6 *                                    Copyright (C) David Mosberger-Tang
7 *                                          Copyright (C) 1999 Egbert Eich
8 *
9 *  ========================================================================
10 *
11 *  Permission to use, copy, modify, distribute, and sell this software and
12 *  its documentation for any purpose is hereby granted without fee,
13 *  provided that the above copyright notice appear in all copies and that
14 *  both that copyright notice and this permission notice appear in
15 *  supporting documentation, and that the name of the authors not be used
16 *  in advertising or publicity pertaining to distribution of the software
17 *  without specific, written prior permission.  The authors makes no
18 *  representations about the suitability of this software for any purpose.
19 *  It is provided "as is" without express or implied warranty.
20 *
21 *  THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
22 *  INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
23 *  EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
24 *  CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
25 *  USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
26 *  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
27 *  PERFORMANCE OF THIS SOFTWARE.
28 *
29 *  ========================================================================
30 *
31 * Language:             ANSI C
32 * Environment:  Any
33 * Developer:    Kendall Bennett
34 *
35 * Description:  This file includes subroutines which are related to
36 *                               programmed I/O and memory access. Included in this module
37 *                               are default functions with limited usefulness. For real
38 *                               uses these functions will most likely be overriden by the
39 *                               user library.
40 *
41 ****************************************************************************/
42 /* $XFree86: xc/extras/x86emu/src/x86emu/sys.c,v 1.5 2000/08/23 22:10:01 tsi Exp $ */
43
44 #include <x86emu/x86emu.h>
45 #include <x86emu/regs.h>
46 #include "debug.h"
47 #include "prim_ops.h"
48 #ifdef LINUXBIOS_VERSION
49 #include "arch/io.h"
50 #else
51 #include <sys/io.h>
52 #endif
53
54 #ifdef IN_MODULE
55 #include "xf86_ansic.h"
56 #else
57 #include <string.h>
58 #endif
59 /*------------------------- Global Variables ------------------------------*/
60
61 X86EMU_sysEnv _X86EMU_env;      /* Global emulator machine state */
62 X86EMU_intrFuncs _X86EMU_intrTab[256];
63
64 /*----------------------------- Implementation ----------------------------*/
65 #if defined(__alpha__) || defined(__alpha)
66 /* to cope with broken egcs-1.1.2 :-(((( */
67
68 /*
69  * inline functions to do unaligned accesses
70  * from linux/include/asm-alpha/unaligned.h
71  */
72
73 /*
74  * EGCS 1.1 knows about arbitrary unaligned loads.  Define some
75  * packed structures to talk about such things with.
76  */
77
78 #if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
79 struct __una_u64 {
80         unsigned long x __attribute__ ((packed));
81 };
82 struct __una_u32 {
83         unsigned int x __attribute__ ((packed));
84 };
85 struct __una_u16 {
86         unsigned short x __attribute__ ((packed));
87 };
88 #endif
89
90 static __inline__ unsigned long ldq_u(unsigned long *r11)
91 {
92 #if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
93         const struct __una_u64 *ptr = (const struct __una_u64 *) r11;
94         return ptr->x;
95 #else
96         unsigned long r1, r2;
97       __asm__("ldq_u %0,%3\n\t" "ldq_u %1,%4\n\t" "extql %0,%2,%0\n\t" "extqh %1,%2,%1":"=&r"(r1),
98                 "=&r"
99                 (r2)
100       : "r"(r11), "m"(*r11),
101                 "m"(*(const unsigned long *) (7 + (char *) r11)));
102         return r1 | r2;
103 #endif
104 }
105
106 static __inline__ unsigned long ldl_u(unsigned int *r11)
107 {
108 #if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
109         const struct __una_u32 *ptr = (const struct __una_u32 *) r11;
110         return ptr->x;
111 #else
112         unsigned long r1, r2;
113       __asm__("ldq_u %0,%3\n\t" "ldq_u %1,%4\n\t" "extll %0,%2,%0\n\t" "extlh %1,%2,%1":"=&r"(r1),
114                 "=&r"
115                 (r2)
116       : "r"(r11), "m"(*r11),
117                 "m"(*(const unsigned long *) (3 + (char *) r11)));
118         return r1 | r2;
119 #endif
120 }
121
122 static __inline__ unsigned long ldw_u(unsigned short *r11)
123 {
124 #if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
125         const struct __una_u16 *ptr = (const struct __una_u16 *) r11;
126         return ptr->x;
127 #else
128         unsigned long r1, r2;
129       __asm__("ldq_u %0,%3\n\t" "ldq_u %1,%4\n\t" "extwl %0,%2,%0\n\t" "extwh %1,%2,%1":"=&r"(r1),
130                 "=&r"
131                 (r2)
132       : "r"(r11), "m"(*r11),
133                 "m"(*(const unsigned long *) (1 + (char *) r11)));
134         return r1 | r2;
135 #endif
136 }
137
138 /*
139  * Elemental unaligned stores 
140  */
141
142 static __inline__ void stq_u(unsigned long r5, unsigned long *r11)
143 {
144 #if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
145         struct __una_u64 *ptr = (struct __una_u64 *) r11;
146         ptr->x = r5;
147 #else
148         unsigned long r1, r2, r3, r4;
149
150       __asm__("ldq_u %3,%1\n\t" "ldq_u %2,%0\n\t" "insqh %6,%7,%5\n\t" "insql %6,%7,%4\n\t" "mskqh %3,%7,%3\n\t" "mskql %2,%7,%2\n\t" "bis %3,%5,%3\n\t" "bis %2,%4,%2\n\t" "stq_u %3,%1\n\t" "stq_u %2,%0":"=m"(*r11),
151                 "=m"(*(unsigned long *) (7 + (char *) r11)),
152                 "=&r"(r1), "=&r"(r2), "=&r"(r3), "=&r"(r4)
153       : "r"(r5), "r"(r11));
154 #endif
155 }
156
157 static __inline__ void stl_u(unsigned long r5, unsigned int *r11)
158 {
159 #if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
160         struct __una_u32 *ptr = (struct __una_u32 *) r11;
161         ptr->x = r5;
162 #else
163         unsigned long r1, r2, r3, r4;
164
165       __asm__("ldq_u %3,%1\n\t" "ldq_u %2,%0\n\t" "inslh %6,%7,%5\n\t" "insll %6,%7,%4\n\t" "msklh %3,%7,%3\n\t" "mskll %2,%7,%2\n\t" "bis %3,%5,%3\n\t" "bis %2,%4,%2\n\t" "stq_u %3,%1\n\t" "stq_u %2,%0":"=m"(*r11),
166                 "=m"(*(unsigned long *) (3 + (char *) r11)),
167                 "=&r"(r1), "=&r"(r2), "=&r"(r3), "=&r"(r4)
168       : "r"(r5), "r"(r11));
169 #endif
170 }
171
172 static __inline__ void stw_u(unsigned long r5, unsigned short *r11)
173 {
174 #if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
175         struct __una_u16 *ptr = (struct __una_u16 *) r11;
176         ptr->x = r5;
177 #else
178         unsigned long r1, r2, r3, r4;
179
180       __asm__("ldq_u %3,%1\n\t" "ldq_u %2,%0\n\t" "inswh %6,%7,%5\n\t" "inswl %6,%7,%4\n\t" "mskwh %3,%7,%3\n\t" "mskwl %2,%7,%2\n\t" "bis %3,%5,%3\n\t" "bis %2,%4,%2\n\t" "stq_u %3,%1\n\t" "stq_u %2,%0":"=m"(*r11),
181                 "=m"(*(unsigned long *) (1 + (char *) r11)),
182                 "=&r"(r1), "=&r"(r2), "=&r"(r3), "=&r"(r4)
183       : "r"(r5), "r"(r11));
184 #endif
185 }
186 #endif
187
188 /* compute a pointer. This replaces code scattered all over the place! */
189 u8 *mem_ptr(u32 addr, int size)
190 {
191         u8 *retaddr = 0;
192
193         if (addr > M.mem_size - size) {
194                 DB(printk("mem_ptr: address %#lx out of range!\n", addr);)
195                     HALT_SYS();
196         }
197         /* a or b segment? */
198         /* & with e to clear low-order bit, if it is a or b it will be a */
199 #if 0
200         if (((addr & 0xfffe0000) == 0xa0000) && M.abseg) {
201                 //printk("It's a0000\n");
202                 //addr &= ~0xfffe0000;
203                 retaddr = (u8 *) (M.mem_base + addr);
204                 //printk("retaddr now 0x%p\n", retaddr);
205         } else
206 #endif
207         if (addr < 0x200) {
208                 //printk("%x:%x updating int vector 0x%x\n",
209                 //       M.x86.R_CS, M.x86.R_IP, addr >> 2);
210
211                 retaddr = (u8 *) (M.mem_base + addr);
212         } else {
213                 retaddr = (u8 *) (M.mem_base + addr);
214         }
215
216         //printk_debug("%s, %x:%x ask address %x return address %x\n",
217         //           __func__,  M.x86.R_CS, M.x86.R_IP, addr, retaddr);
218
219         return retaddr;
220 }
221
222 /****************************************************************************
223 PARAMETERS:
224 addr    - Emulator memory address to read
225
226 RETURNS:
227 Byte value read from emulator memory.
228
229 REMARKS:
230 Reads a byte value from the emulator memory. 
231 ****************************************************************************/
232 u8 X86API rdb(u32 addr)
233 {
234         u8 val;
235         u8 *ptr;
236
237         ptr = mem_ptr(addr, 1);
238
239         val = *ptr;
240         DB(if (DEBUG_MEM_TRACE())
241            printk("%#08x 1 -> %#x\n", addr, val);)
242                 return val;
243 }
244
245 /****************************************************************************
246 PARAMETERS:
247 addr    - Emulator memory address to read
248
249 RETURNS:
250 Word value read from emulator memory.
251
252 REMARKS:
253 Reads a word value from the emulator memory.
254 ****************************************************************************/
255 u16 X86API rdw(u32 addr)
256 {
257         u16 val = 0;
258         u8 *ptr;
259
260         ptr = mem_ptr(addr, 2);
261
262         if (addr > M.mem_size - 2) {
263                 DB(printk("mem_read: address %#lx out of range!\n", (unsigned long) addr);
264                     )
265                     HALT_SYS();
266         }
267 #ifdef __BIG_ENDIAN__
268         if (addr & 0x1) {
269                 val = (*ptr | (*(ptr + 1) << 8));
270         } else
271 #endif
272 #if defined(__alpha__) || defined(__alpha)
273                 val = ldw_u((u16 *) (ptr));
274 #else
275                 val = *(u16 *) (ptr);
276 #endif
277         DB(if (DEBUG_MEM_TRACE())
278            printk("%#08x 2 -> %#x\n", addr, val);)
279                 return val;
280 }
281
282 /****************************************************************************
283 PARAMETERS:
284 addr    - Emulator memory address to read
285
286 RETURNS:
287 Long value read from emulator memory.
288 REMARKS:
289 Reads a long value from the emulator memory. 
290 ****************************************************************************/
291 u32 X86API rdl(u32 addr)
292 {
293         u32 val = 0;
294         u8 *ptr;
295
296         ptr = mem_ptr(addr, 4);
297
298 #ifdef __BIG_ENDIAN__
299         if (addr & 0x3) {
300                 val = (*(u8 *) (ptr + 0) |
301                        (*(u8 *) (ptr + 1) << 8) |
302                        (*(u8 *) (ptr + 2) << 16) | (*(u8 *) (ptr + 3) << 24));
303         } else
304 #endif
305 #if defined(__alpha__) || defined(__alpha)
306                 val = ldl_u((u32 *) (ptr));
307 #else
308                 val = *(u32 *) (ptr);
309 #endif
310         DB(if (DEBUG_MEM_TRACE())
311            printk("%#08x 4 -> %#x\n", addr, val);)
312                 return val;
313 }
314
315 /****************************************************************************
316 PARAMETERS:
317 addr    - Emulator memory address to read
318 val             - Value to store
319
320 REMARKS:
321 Writes a byte value to emulator memory.
322 ****************************************************************************/
323 void X86API wrb(u32 addr, u8 val)
324 {
325         u8 *ptr;
326
327         ptr = mem_ptr(addr, 1);
328         DB(if (DEBUG_MEM_TRACE())
329            printk("%#08x 1 <- %#x\n", addr, val);)
330                 *(u8 *) (ptr) = val;
331 }
332
333 /****************************************************************************
334 PARAMETERS:
335 addr    - Emulator memory address to read
336 val             - Value to store
337
338 REMARKS:
339 Writes a word value to emulator memory.
340 ****************************************************************************/
341 void X86API wrw(u32 addr, u16 val)
342 {
343         u8 *ptr;
344
345         ptr = mem_ptr(addr, 2);
346         DB(if (DEBUG_MEM_TRACE())
347            printk("%#08x 2 <- %#x\n", addr, val);)
348 #ifdef __BIG_ENDIAN__
349                 if (addr & 0x1) {
350                         *(u8 *) (ptr + 0) = (val >> 0) & 0xff;
351                         *(u8 *) (ptr + 1) = (val >> 8) & 0xff;
352                 } else
353 #endif
354 #if defined(__alpha__) || defined(__alpha)
355                         stw_u(val, (u16 *) (ptr));
356 #else
357                         *(u16 *) (ptr) = val;
358 #endif
359 }
360
361 /****************************************************************************
362 PARAMETERS:
363 addr    - Emulator memory address to read
364 val             - Value to store
365
366 REMARKS:
367 Writes a long value to emulator memory. 
368 ****************************************************************************/
369 void X86API wrl(u32 addr, u32 val)
370 {
371         u8 *ptr;
372
373         ptr = mem_ptr(addr, 4);
374         DB(if (DEBUG_MEM_TRACE())
375            printk("%#08x 4 <- %#x\n", addr, val);)
376 #ifdef __BIG_ENDIAN__
377                 if (addr & 0x1) {
378                         *(u8 *) (ptr + 0) = (val >> 0) & 0xff;
379                         *(u8 *) (ptr + 1) = (val >> 8) & 0xff;
380                         *(u8 *) (ptr + 2) = (val >> 16) & 0xff;
381                         *(u8 *) (ptr + 3) = (val >> 24) & 0xff;
382                 } else
383 #endif
384 #if defined(__alpha__) || defined(__alpha)
385                         stl_u(val, (u32 *) (ptr));
386 #else
387                         *(u32 *) (ptr) = val;
388 #endif
389 }
390
391 /****************************************************************************
392 PARAMETERS:
393 addr    - PIO address to read
394 RETURN:
395 0
396 REMARKS:
397 Default PIO byte read function. Doesn't perform real inb.
398 ****************************************************************************/
399 static u8 X86API p_inb(X86EMU_pioAddr addr)
400 {
401         DB(if (DEBUG_IO_TRACE())
402                 printk("inb %#04x \n", addr);)
403         return inb(addr);
404 }
405
406 /****************************************************************************
407 PARAMETERS:
408 addr    - PIO address to read
409 RETURN:
410 0
411 REMARKS:
412 Default PIO word read function. Doesn't perform real inw.
413 ****************************************************************************/
414 static u16 X86API p_inw(X86EMU_pioAddr addr)
415 {
416         DB(if (DEBUG_IO_TRACE())
417                 printk("inw %#04x \n", addr);)
418         return inw(addr);
419 }
420
421 /****************************************************************************
422 PARAMETERS:
423 addr    - PIO address to read
424 RETURN:
425 0
426 REMARKS:
427 Default PIO long read function. Doesn't perform real inl.
428 ****************************************************************************/
429 static u32 X86API p_inl(X86EMU_pioAddr addr)
430 {
431         DB(if (DEBUG_IO_TRACE())
432                 printk("inl %#04x \n", addr);)
433         return inl(addr);
434 }
435
436 /****************************************************************************
437 PARAMETERS:
438 addr    - PIO address to write
439 val     - Value to store
440 REMARKS:
441 Default PIO byte write function. Doesn't perform real outb.
442 ****************************************************************************/
443 static void X86API p_outb(X86EMU_pioAddr addr, u8 val)
444 {
445         DB(if (DEBUG_IO_TRACE())
446                 printk("outb %#02x -> %#04x \n", val, addr);)
447         outb(val, addr);
448         return;
449 }
450
451 /****************************************************************************
452 PARAMETERS:
453 addr    - PIO address to write
454 val     - Value to store
455 REMARKS:
456 Default PIO word write function. Doesn't perform real outw.
457 ****************************************************************************/
458 static void X86API p_outw(X86EMU_pioAddr addr, u16 val)
459 {
460         DB(if (DEBUG_IO_TRACE())
461                 printk("outw %#04x -> %#04x \n", val, addr);)
462         outw(val, addr);
463         return;
464 }
465
466 /****************************************************************************
467 PARAMETERS:
468 addr    - PIO address to write
469 val     - Value to store
470 REMARKS:
471 Default PIO ;ong write function. Doesn't perform real outl.
472 ****************************************************************************/
473 static void X86API p_outl(X86EMU_pioAddr addr, u32 val)
474 {
475         DB(if (DEBUG_IO_TRACE())
476                printk("outl %#08x -> %#04x \n", val, addr);)
477
478         outl(val, addr);
479         return;
480 }
481
482 /*------------------------- Global Variables ------------------------------*/
483
484 u8(X86APIP sys_rdb) (u32 addr) = rdb;
485 u16(X86APIP sys_rdw) (u32 addr) = rdw;
486 u32(X86APIP sys_rdl) (u32 addr) = rdl;
487 void (X86APIP sys_wrb) (u32 addr, u8 val) = wrb;
488 void (X86APIP sys_wrw) (u32 addr, u16 val) = wrw;
489 void (X86APIP sys_wrl) (u32 addr, u32 val) = wrl;
490 u8(X86APIP sys_inb) (X86EMU_pioAddr addr) = p_inb;
491 u16(X86APIP sys_inw) (X86EMU_pioAddr addr) = p_inw;
492 u32(X86APIP sys_inl) (X86EMU_pioAddr addr) = p_inl;
493 void (X86APIP sys_outb) (X86EMU_pioAddr addr, u8 val) = p_outb;
494 void (X86APIP sys_outw) (X86EMU_pioAddr addr, u16 val) = p_outw;
495 void (X86APIP sys_outl) (X86EMU_pioAddr addr, u32 val) = p_outl;
496
497 /*----------------------------- Setup -------------------------------------*/
498
499 /****************************************************************************
500 PARAMETERS:
501 funcs   - New memory function pointers to make active
502
503 REMARKS:
504 This function is used to set the pointers to functions which access
505 memory space, allowing the user application to override these functions
506 and hook them out as necessary for their application.
507 ****************************************************************************/
508 void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs)
509 {
510         sys_rdb = funcs->rdb;
511         sys_rdw = funcs->rdw;
512         sys_rdl = funcs->rdl;
513         sys_wrb = funcs->wrb;
514         sys_wrw = funcs->wrw;
515         sys_wrl = funcs->wrl;
516 }
517
518 /****************************************************************************
519 PARAMETERS:
520 funcs   - New programmed I/O function pointers to make active
521
522 REMARKS:
523 This function is used to set the pointers to functions which access
524 I/O space, allowing the user application to override these functions
525 and hook them out as necessary for their application.
526 ****************************************************************************/
527 void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs)
528 {
529         sys_inb = funcs->inb;
530         sys_inw = funcs->inw;
531         sys_inl = funcs->inl;
532         sys_outb = funcs->outb;
533         sys_outw = funcs->outw;
534         sys_outl = funcs->outl;
535 }
536
537 /****************************************************************************
538 PARAMETERS:
539 funcs   - New interrupt vector table to make active
540
541 REMARKS:
542 This function is used to set the pointers to functions which handle
543 interrupt processing in the emulator, allowing the user application to
544 hook interrupts as necessary for their application. Any interrupts that
545 are not hooked by the user application, and reflected and handled internally
546 in the emulator via the interrupt vector table. This allows the application
547 to get control when the code being emulated executes specific software
548 interrupts.
549 ****************************************************************************/
550 void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[])
551 {
552         int i;
553
554         for (i = 0; i < 256; i++)
555                 _X86EMU_intrTab[i] = NULL;
556         if (funcs) {
557                 for (i = 0; i < 256; i++)
558                         _X86EMU_intrTab[i] = funcs[i];
559         }
560 }
561
562 /****************************************************************************
563 PARAMETERS:
564 int     - New software interrupt to prepare for
565
566 REMARKS:
567 This function is used to set up the emulator state to exceute a software
568 interrupt. This can be used by the user application code to allow an
569 interrupt to be hooked, examined and then reflected back to the emulator
570 so that the code in the emulator will continue processing the software
571 interrupt as per normal. This essentially allows system code to actively
572 hook and handle certain software interrupts as necessary.
573 ****************************************************************************/
574 void X86EMU_prepareForInt(int num)
575 {
576         push_word((u16) M.x86.R_FLG);
577         CLEAR_FLAG(F_IF);
578         CLEAR_FLAG(F_TF);
579         push_word(M.x86.R_CS);
580         M.x86.R_CS = mem_access_word(num * 4 + 2);
581         push_word(M.x86.R_IP);
582         M.x86.R_IP = mem_access_word(num * 4);
583         M.x86.intr = 0;
584 }
585
586 void X86EMU_setMemBase(void *base, size_t size)
587 {
588         M.mem_base = (int) base;
589         M.mem_size = size;
590 }
591
592 void X86EMU_setabseg(void *abseg)
593 {
594         M.abseg = (unsigned long) abseg;
595 }