Revision: linuxbios@linuxbios.org--devel/freebios--devel--2.0--patch-30
[coreboot.git] / src / devices / emulator / biosemu.c
1 #include <arch/io.h>
2 #include <console/console.h>
3 #include <device/device.h>
4 #include <device/pci.h>
5 #include <device/pci_ids.h>
6 #include <device/pci_ops.h>
7
8 #include <x86emu/x86emu.h>
9
10 #define MEM_WB(where, what) wrb(where, what)
11 #define MEM_WW(where, what) wrw(where, what)
12 #define MEM_WL(where, what) wrl(where, what)
13
14 #define MEM_RB(where) rdb(where)
15 #define MEM_RW(where) rdw(where)
16 #define MEM_RL(where) rdl(where)
17
18 u8 x_inb(u16 port);
19 u16 x_inw(u16 port);
20 void x_outb(u16 port, u8 val);
21 void x_outw(u16 port, u16 val);
22 u32 x_inl(u16 port);
23 void x_outl(u16 port, u32 val);
24
25 /* general software interrupt handler */
26 u32 getIntVect(int num)
27 {
28         return MEM_RW(num << 2) + (MEM_RW((num << 2) + 2) << 4);
29 }
30
31 /* FixME: There is already a push_word() in the emulator */
32 void pushw(u16 val)
33 {
34         X86_ESP -= 2;
35         MEM_WW(((u32) X86_SS << 4) + X86_SP, val);
36 }
37
38 int run_bios_int(int num)
39 {
40         u32 eflags;
41
42         eflags = X86_EFLAGS;
43         pushw(eflags);
44         pushw(X86_CS);
45         pushw(X86_IP);
46         X86_CS = MEM_RW((num << 2) + 2);
47         X86_IP = MEM_RW(num << 2);
48
49         return 1;
50 }
51
52 u8 x_inb(u16 port)
53 {
54         u8 val;
55
56         val = inb(port);
57
58         //if (port != 0x40)
59         //    printk_debug("inb(0x%04x) = 0x%02x\n", port, val);
60
61         return val;
62 }
63
64 u16 x_inw(u16 port)
65 {
66         u16 val;
67
68         val = inw(port);
69
70         //printk_debug("inw(0x%04x) = 0x%04x\n", port, val);
71         return val;
72 }
73
74 u32 x_inl(u16 port)
75 {
76         u32 val;
77
78         val = inl(port);
79
80         //printk_debug("inl(0x%04x) = 0x%08x\n", port, val);
81         return val;
82 }
83
84 void x_outb(u16 port, u8 val)
85 {
86         //if (port != 0x43)
87         //      printk_debug("outb(0x%02x, 0x%04x)\n", val, port);
88         outb(val, port);
89 }
90
91 void x_outw(u16 port, u16 val)
92 {
93         //printk_debug("outw(0x%04x, 0x%04x)\n", val, port);
94         outw(val, port);
95 }
96
97 void x_outl(u16 port, u32 val)
98 {
99         //printk_debug("outl(0x%08x, 0x%04x)\n", val, port);
100         outl(val, port);
101 }
102
103 X86EMU_pioFuncs myfuncs = {
104         x_inb, x_inw, x_inl,
105         x_outb, x_outw, x_outl
106 };
107
108
109
110 /* Interrupt multiplexer */
111
112 void do_int(int num)
113 {
114         int ret = 0;
115
116 //      printk_debug("int%x vector at %x\n", num, getIntVect(num));
117
118         switch (num) {
119 #ifndef _PC
120         case 0x10:
121         case 0x42:
122         case 0x6D:
123                 if (getIntVect(num) == 0x0000) {
124                         printk_debug("un-inited int vector\n");
125                         return 1;
126                 }
127                 if (getIntVect(num) == 0xFF065) {
128                         //ret = int42_handler();
129                         ret = 1;
130                 }
131                 break;
132 #endif
133         case 0x15:
134                 //ret = int15_handler();
135                 ret = 1;
136                 break;
137         case 0x16:
138                 //ret = int16_handler();
139                 ret = 0;
140                 break;
141         case 0x1A:
142                 ret = pcibios_handler();
143                 ret = 1;
144                 break;
145         case 0xe6:
146                 //ret = intE6_handler();
147                 ret = 0;
148                 break;
149         default:
150                 break;
151         }
152
153         if (!ret)
154                 ret = run_bios_int(num);
155
156 }
157 #if 0
158 #define SYS_BIOS 0xf0000
159 /*
160  * here we are really paranoid about faking a "real"
161  * BIOS. Most of this information was pulled from
162  * dosemu.
163  */
164 void setup_int_vect(void)
165 {
166         int i;
167
168         /* let the int vects point to the SYS_BIOS seg */
169         for (i = 0; i < 0x80; i++) {
170                 MEM_WW(i << 2, 0);
171                 MEM_WW((i << 2) + 2, SYS_BIOS >> 4);
172         }
173
174         reset_int_vect();
175
176         /* font tables default location (int 1F) */
177         MEM_WW(0x1f << 2, 0xfa6e);
178         /* int 11 default location (Get Equipment Configuration) */
179         MEM_WW(0x11 << 2, 0xf84d);
180         /* int 12 default location (Get Conventional Memory Size) */
181         MEM_WW(0x12 << 2, 0xf841);
182         /* int 15 default location (I/O System Extensions) */
183         MEM_WW(0x15 << 2, 0xf859);
184         /* int 1A default location (RTC, PCI and others) */
185         MEM_WW(0x1a << 2, 0xff6e);
186         /* int 05 default location (Bound Exceeded) */
187         MEM_WW(0x05 << 2, 0xff54);
188         /* int 08 default location (Double Fault) */
189         MEM_WW(0x08 << 2, 0xfea5);
190         /* int 13 default location (Disk) */
191         MEM_WW(0x13 << 2, 0xec59);
192         /* int 0E default location (Page Fault) */
193         MEM_WW(0x0e << 2, 0xef57);
194         /* int 17 default location (Parallel Port) */
195         MEM_WW(0x17 << 2, 0xefd2);
196         /* fdd table default location (int 1e) */
197         MEM_WW(0x1e << 2, 0xefc7);
198
199         /* Set Equipment flag to VGA */
200         i = MEM_RB(0x0410) & 0xCF;
201         MEM_WB(0x0410, i);
202         /* XXX Perhaps setup more of the BDA here.  See also int42(0x00). */
203 }
204
205 int setup_system_bios(void *base_addr)
206 {
207         char *base = (char *) base_addr;
208
209         /*
210          * we trap the "industry standard entry points" to the BIOS
211          * and all other locations by filling them with "hlt"
212          * TODO: implement hlt-handler for these
213          */
214         memset(base, 0xf4, 0x10000);
215
216         /* set bios date */
217         //strcpy(base + 0x0FFF5, "06/11/99");
218         /* set up eisa ident string */
219         //strcpy(base + 0x0FFD9, "PCI_ISA");
220         /* write system model id for IBM-AT */
221         //*((unsigned char *) (base + 0x0FFFE)) = 0xfc;
222
223         return 1;
224 }
225
226 void reset_int_vect(void)
227 {
228         /*
229          * This table is normally located at 0xF000:0xF0A4.  However, int 0x42,
230          * function 0 (Mode Set) expects it (or a copy) somewhere in the bottom
231          * 64kB.  Note that because this data doesn't survive POST, int 0x42 should
232          * only be used during EGA/VGA BIOS initialisation.
233          */
234         static const u8 VideoParms[] = {
235                 /* Timing for modes 0x00 & 0x01 */
236                 0x38, 0x28, 0x2d, 0x0a, 0x1f, 0x06, 0x19, 0x1c,
237                 0x02, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00,
238                 /* Timing for modes 0x02 & 0x03 */
239                 0x71, 0x50, 0x5a, 0x0a, 0x1f, 0x06, 0x19, 0x1c,
240                 0x02, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00,
241                 /* Timing for modes 0x04, 0x05 & 0x06 */
242                 0x38, 0x28, 0x2d, 0x0a, 0x7f, 0x06, 0x64, 0x70,
243                 0x02, 0x01, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00,
244                 /* Timing for mode 0x07 */
245                 0x61, 0x50, 0x52, 0x0f, 0x19, 0x06, 0x19, 0x19,
246                 0x02, 0x0d, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x00,
247                 /* Display page lengths in little endian order */
248                 0x00, 0x08,     /* Modes 0x00 and 0x01 */
249                 0x00, 0x10,     /* Modes 0x02 and 0x03 */
250                 0x00, 0x40,     /* Modes 0x04 and 0x05 */
251                 0x00, 0x40,     /* Modes 0x06 and 0x07 */
252                 /* Number of columns for each mode */
253                 40, 40, 80, 80, 40, 40, 80, 80,
254                 /* CGA Mode register value for each mode */
255                 0x2c, 0x28, 0x2d, 0x29, 0x2a, 0x2e, 0x1e, 0x29,
256                 /* Padding */
257                 0x00, 0x00, 0x00, 0x00
258         };
259         int i;
260
261         for (i = 0; i < sizeof(VideoParms); i++)
262                 MEM_WB(i + (0x1000 - sizeof(VideoParms)), VideoParms[i]);
263         MEM_WW(0x1d << 2, 0x1000 - sizeof(VideoParms));
264         MEM_WW((0x1d << 2) + 2, 0);
265
266         printk_debug("SETUP INT\n");
267         MEM_WW(0x10 << 2, 0xf065);
268         MEM_WW((0x10 << 2) + 2, SYS_BIOS >> 4);
269         MEM_WW(0x42 << 2, 0xf065);
270         MEM_WW((0x42 << 2) + 2, SYS_BIOS >> 4);
271         MEM_WW(0x6D << 2, 0xf065);
272         MEM_WW((0x6D << 2) + 2, SYS_BIOS >> 4);
273 }
274 #endif
275 void run_bios(struct device * dev, unsigned long addr)
276 {
277 #if 1
278         int i;
279         unsigned short initialcs = (addr & 0xF0000) >> 4;
280         unsigned short initialip = (addr + 3) & 0xFFFF;
281         unsigned short devfn = dev->bus->secondary << 8 | dev->path.u.pci.devfn;
282         X86EMU_intrFuncs intFuncs[256];
283
284         X86EMU_setMemBase(0, 0x100000);
285         X86EMU_setupPioFuncs(&myfuncs);
286         for (i = 0; i < 256; i++)
287                 intFuncs[i] = do_int;
288         X86EMU_setupIntrFuncs(intFuncs);
289
290         {
291                 char *date = "01/01/99";
292                 for (i = 0; date[i]; i++)
293                         wrb(0xffff5 + i, date[i]);
294                 wrb(0xffff7, '/');
295                 wrb(0xffffa, '/');
296         }
297
298         {
299             /* FixME: move PIT init to its own file */
300             outb(0x36, 0x43);
301             outb(0x00, 0x40);
302             outb(0x00, 0x40);
303         }
304         //setup_int_vect();
305
306         /* cpu setup */
307         X86_AX = devfn ? devfn : 0xff;
308         X86_DX = 0x80;
309         X86_EIP = initialip;
310         X86_CS = initialcs;
311
312         /* Initialize stack and data segment */
313         X86_SS = initialcs;
314         X86_SP = 0xfffe;
315         X86_DS = 0x0040;
316         X86_ES = 0x0000;
317
318         /* We need a sane way to return from bios
319          * execution. A hlt instruction and a pointer
320          * to it, both kept on the stack, will do.
321          */
322         pushw(0xf4f4);          /* hlt; hlt */
323         pushw(X86_SS);
324         pushw(X86_SP + 2);
325
326 //      X86EMU_trace_on();
327
328         X86EMU_exec();
329 #endif
330 }