Since some people disapprove of white space cleanups mixed in regular commits
[coreboot.git] / src / devices / oprom / yabel / biosemu.c
1 /******************************************************************************
2  * Copyright (c) 2004, 2008 IBM Corporation
3  * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
4  * Copyright (c) 2010 coresystems GmbH
5  * All rights reserved.
6  * This program and the accompanying materials
7  * are made available under the terms of the BSD License
8  * which accompanies this distribution, and is available at
9  * http://www.opensource.org/licenses/bsd-license.php
10  *
11  * Contributors:
12  *     IBM Corporation - initial implementation
13  *****************************************************************************/
14
15 #include <string.h>
16 #include <types.h>
17
18 #include "debug.h"
19
20 #include <x86emu/x86emu.h>
21 #include <x86emu/regs.h>
22 #include "../x86emu/prim_ops.h"
23
24 #include "biosemu.h"
25 #include "io.h"
26 #include "mem.h"
27 #include "interrupt.h"
28 #include "device.h"
29 #include "pmm.h"
30
31 #include <device/device.h>
32 #include "compat/rtas.h"
33
34 static X86EMU_memFuncs my_mem_funcs = {
35         my_rdb, my_rdw, my_rdl,
36         my_wrb, my_wrw, my_wrl
37 };
38
39 static X86EMU_pioFuncs my_pio_funcs = {
40         my_inb, my_inw, my_inl,
41         my_outb, my_outw, my_outl
42 };
43
44 /* interrupt function override array (see biosemu.h) */
45 yabel_handleIntFunc yabel_intFuncArray[256];
46
47 /* main entry into YABEL biosemu, arguments are:
48  * *biosmem = pointer to virtual memory
49  * biosmem_size = size of the virtual memory
50  * *dev = pointer to the device to be initialised
51  * rom_addr = address of the OptionROM to be executed, if this is = 0, YABEL
52  *      will look for an ExpansionROM BAR and use the code from there.
53  */
54 u32
55 biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_addr)
56 {
57         u8 *rom_image;
58         int i = 0;
59 #if CONFIG_X86EMU_DEBUG
60         debug_flags = 0;
61 #if defined(CONFIG_X86EMU_DEBUG_JMP) && CONFIG_X86EMU_DEBUG_JMP
62         debug_flags |= DEBUG_JMP;
63 #endif
64 #if defined(CONFIG_X86EMU_DEBUG_TRACE) && CONFIG_X86EMU_DEBUG_TRACE
65         debug_flags |= DEBUG_TRACE_X86EMU;
66 #endif
67 #if defined(CONFIG_X86EMU_DEBUG_PNP) && CONFIG_X86EMU_DEBUG_PNP
68         debug_flags |= DEBUG_PNP;
69 #endif
70 #if defined(CONFIG_X86EMU_DEBUG_DISK) && CONFIG_X86EMU_DEBUG_DISK
71         debug_flags |= DEBUG_DISK;
72 #endif
73 #if defined(CONFIG_X86EMU_DEBUG_PMM) && CONFIG_X86EMU_DEBUG_PMM
74         debug_flags |= DEBUG_PMM;
75 #endif
76 #if defined(CONFIG_X86EMU_DEBUG_VBE) && CONFIG_X86EMU_DEBUG_VBE
77         debug_flags |= DEBUG_VBE;
78 #endif
79 #if defined(CONFIG_X86EMU_DEBUG_INT10) && CONFIG_X86EMU_DEBUG_INT10
80         debug_flags |= DEBUG_PRINT_INT10;
81 #endif
82 #if defined(CONFIG_X86EMU_DEBUG_INTERRUPTS) && CONFIG_X86EMU_DEBUG_INTERRUPTS
83         debug_flags |= DEBUG_INTR;
84 #endif
85 #if defined(CONFIG_X86EMU_DEBUG_CHECK_VMEM_ACCESS) && CONFIG_X86EMU_DEBUG_CHECK_VMEM_ACCESS
86         debug_flags |= DEBUG_CHECK_VMEM_ACCESS;
87 #endif
88 #if defined(CONFIG_X86EMU_DEBUG_MEM) && CONFIG_X86EMU_DEBUG_MEM
89         debug_flags |= DEBUG_MEM;
90 #endif
91 #if defined(CONFIG_X86EMU_DEBUG_IO) && CONFIG_X86EMU_DEBUG_IO
92         debug_flags |= DEBUG_IO;
93 #endif
94
95 #endif
96         if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) {
97                 printf("Error: Not enough virtual memory: %x, required: %x!\n",
98                        biosmem_size, MIN_REQUIRED_VMEM_SIZE);
99                 return -1;
100         }
101         if (biosemu_dev_init(dev) != 0) {
102                 printf("Error initializing device!\n");
103                 return -1;
104         }
105         if (biosemu_dev_check_exprom(rom_addr) != 0) {
106                 printf("Error: Device Expansion ROM invalid!\n");
107                 return -1;
108         }
109         rom_image = (u8 *) bios_device.img_addr;
110         DEBUG_PRINTF("executing rom_image from %p\n", rom_image);
111         DEBUG_PRINTF("biosmem at %p\n", biosmem);
112
113         DEBUG_PRINTF("Image Size: %d\n", bios_device.img_size);
114
115         // in case we jump somewhere unexpected, or execution is finished,
116         // fill the biosmem with hlt instructions (0xf4)
117         // But we have to be careful: If biosmem is 0x00000000 we're running
118         // in the lower 1MB and we must not wipe memory like that.
119         if (biosmem) {
120                 DEBUG_PRINTF("Clearing biosmem\n");
121                 memset(biosmem, 0xf4, biosmem_size);
122         }
123
124         X86EMU_setMemBase(biosmem, biosmem_size);
125
126         DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M.mem_base,
127                      (int) M.mem_size);
128
129         // copy expansion ROM image to segment OPTION_ROM_CODE_SEGMENT
130         // NOTE: this sometimes fails, some bytes are 0x00... so we compare
131         // after copying and do some retries...
132         u8 *mem_img = biosmem + (OPTION_ROM_CODE_SEGMENT << 4);
133         u8 copy_count = 0;
134         u8 cmp_result = 0;
135         do {
136 #if 0
137                 set_ci();
138                 memcpy(mem_img, rom_image, len);
139                 clr_ci();
140 #else
141                 // memcpy fails... try copy byte-by-byte with set/clr_ci
142                 u8 c;
143                 for (i = 0; i < bios_device.img_size; i++) {
144                         set_ci();
145                         c = *(rom_image + i);
146                         if (c != *(rom_image + i)) {
147                                 clr_ci();
148                                 printf("Copy failed at: %x/%x\n", i,
149                                        bios_device.img_size);
150                                 printf("rom_image(%x): %x, mem_img(%x): %x\n",
151                                        i, *(rom_image + i), i, *(mem_img + i));
152                                 break;
153                         }
154                         clr_ci();
155                         *(mem_img + i) = c;
156                 }
157 #endif
158                 copy_count++;
159                 set_ci();
160                 cmp_result = memcmp(mem_img, rom_image, bios_device.img_size);
161                 clr_ci();
162         }
163         while ((copy_count < 5) && (cmp_result != 0));
164         if (cmp_result != 0) {
165                 printf
166                     ("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n",
167                      copy_count, cmp_result);
168                 dump(rom_image, 0x20);
169                 dump(mem_img, 0x20);
170                 return 0;
171         }
172         // setup default Interrupt Vectors
173         // some expansion ROMs seem to check for these addresses..
174         // each handler is only an IRET (0xCF) instruction
175         // ROM BIOS Int 10 Handler F000:F065
176         my_wrl(0x10 * 4, 0xf000f065);
177         my_wrb(0x000ff065, 0xcf);
178         // ROM BIOS Int 11 Handler F000:F84D
179         my_wrl(0x11 * 4, 0xf000f84d);
180         my_wrb(0x000ff84d, 0xcf);
181         // ROM BIOS Int 12 Handler F000:F841
182         my_wrl(0x12 * 4, 0xf000f841);
183         my_wrb(0x000ff841, 0xcf);
184         // ROM BIOS Int 13 Handler F000:EC59
185         my_wrl(0x13 * 4, 0xf000ec59);
186         my_wrb(0x000fec59, 0xcf);
187         // ROM BIOS Int 14 Handler F000:E739
188         my_wrl(0x14 * 4, 0xf000e739);
189         my_wrb(0x000fe739, 0xcf);
190         // ROM BIOS Int 15 Handler F000:F859
191         my_wrl(0x15 * 4, 0xf000f859);
192         my_wrb(0x000ff859, 0xcf);
193         // ROM BIOS Int 16 Handler F000:E82E
194         my_wrl(0x16 * 4, 0xf000e82e);
195         my_wrb(0x000fe82e, 0xcf);
196         // ROM BIOS Int 17 Handler F000:EFD2
197         my_wrl(0x17 * 4, 0xf000efd2);
198         my_wrb(0x000fefd2, 0xcf);
199         // ROM BIOS Int 1A Handler F000:FE6E
200         my_wrl(0x1a * 4, 0xf000fe6e);
201         my_wrb(0x000ffe6e, 0xcf);
202
203         // setup BIOS Data Area (0000:04xx, or 0040:00xx)
204         // we currently 0 this area, meaning "we dont have
205         // any hardware" :-) no serial/parallel ports, floppys, ...
206         memset(biosmem + 0x400, 0x0, 0x100);
207
208         // at offset 13h in BDA is the memory size in kbytes
209         my_wrw(0x413, biosmem_size / 1024);
210         // at offset 0eh in BDA is the segment of the Extended BIOS Data Area
211         // see setup further down
212         my_wrw(0x40e, INITIAL_EBDA_SEGMENT);
213         // TODO: setup BDA Video Data ( offset 49h-66h)
214         // e.g. to store video mode, cursor position, ...
215         // in int10 (done) handler and VBE Functions
216
217         // TODO: setup BDA Fixed Disk Data
218         // 74h: Fixed Disk Last Operation Status
219         // 75h: Fixed Disk Number of Disk Drives
220
221         // TODO: check BDA for further needed data...
222
223         //setup Extended BIOS Data Area
224         //we currently 0 this area
225         memset(biosmem + (INITIAL_EBDA_SEGMENT << 4), 0, INITIAL_EBDA_SIZE);
226         // at offset 0h in EBDA is the size of the EBDA in KB
227         my_wrw((INITIAL_EBDA_SEGMENT << 4) + 0x0, INITIAL_EBDA_SIZE / 1024);
228         //TODO: check for further needed EBDA data...
229
230         // setup  original ROM BIOS Area (F000:xxxx)
231         const char *date = "06/11/99";
232         for (i = 0; date[i]; i++)
233                 my_wrb(0xffff5 + i, date[i]);
234         // set up eisa ident string
235         const char *ident = "PCI_ISA";
236         for (i = 0; ident[i]; i++)
237                 my_wrb(0xfffd9 + i, ident[i]);
238
239         // write system model id for IBM-AT
240         // according to "Ralf Browns Interrupt List" Int15 AH=C0 Table 515,
241         // model FC is the original AT and also used in all DOSEMU Versions.
242         my_wrb(0xFFFFE, 0xfc);
243
244         //setup interrupt handler
245         X86EMU_intrFuncs intrFuncs[256];
246         for (i = 0; i < 256; i++)
247                 intrFuncs[i] = handleInterrupt;
248         X86EMU_setupIntrFuncs(intrFuncs);
249         X86EMU_setupPioFuncs(&my_pio_funcs);
250         X86EMU_setupMemFuncs(&my_mem_funcs);
251
252         //setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0
253         u8 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
254         if (pmm_length <= 0) {
255                 printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n",
256                      pmm_length);
257                 return 0;
258         } else {
259                 CHECK_DBG(DEBUG_PMM) {
260                         /* test the PMM */
261                         pmm_test();
262                         /* and clean it again by calling pmm_setup... */
263                         pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
264                 }
265         }
266         // setup the CPU
267         M.x86.R_AH = bios_device.bus;
268         M.x86.R_AL = bios_device.devfn;
269         M.x86.R_DX = 0x80;
270         M.x86.R_EIP = 3;
271         M.x86.R_CS = OPTION_ROM_CODE_SEGMENT;
272
273         // Initialize stack and data segment
274         M.x86.R_SS = STACK_SEGMENT;
275         M.x86.R_SP = STACK_START_OFFSET;
276         M.x86.R_DS = DATA_SEGMENT;
277
278         // push a HLT instruction and a pointer to it onto the stack
279         // any return will pop the pointer and jump to the HLT, thus
280         // exiting (more or less) cleanly
281         push_word(0xf4f4);      // F4=HLT
282         push_word(M.x86.R_SS);
283         push_word(M.x86.R_SP + 2);
284
285         CHECK_DBG(DEBUG_TRACE_X86EMU) {
286                 X86EMU_trace_on();
287 #if 0
288         } else {
289                 M.x86.debug |= DEBUG_SAVE_IP_CS_F;
290                 M.x86.debug |= DEBUG_DECODE_F;
291                 M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
292 #endif
293         }
294         CHECK_DBG(DEBUG_JMP) {
295                 M.x86.debug |= DEBUG_TRACEJMP_F;
296                 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
297                 M.x86.debug |= DEBUG_TRACECALL_F;
298                 M.x86.debug |= DEBUG_TRACECALL_REGS_F;
299         }
300
301         DEBUG_PRINTF("Executing Initialization Vector...\n");
302         X86EMU_exec();
303         DEBUG_PRINTF("done\n");
304
305         /* According to the PNP BIOS Spec, Option ROMs should upon exit, return
306          * some boot device status in AX (see PNP BIOS Spec Section 3.3
307          */
308         DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX);
309 #if defined(CONFIG_X86EMU_DEBUG) && CONFIG_X86EMU_DEBUG
310         DEBUG_PRINTF("Exit Status Decode:\n");
311         if (M.x86.R_AX & 0x100) {       // bit 8
312                 DEBUG_PRINTF
313                     ("  IPL Device supporting INT 13h Block Device Format:\n");
314                 switch (((M.x86.R_AX >> 4) & 0x3)) {    // bits 5:4
315                 case 0:
316                         DEBUG_PRINTF("    No IPL Device attached\n");
317                         break;
318                 case 1:
319                         DEBUG_PRINTF("    IPL Device status unknown\n");
320                         break;
321                 case 2:
322                         DEBUG_PRINTF("    IPL Device attached\n");
323                         break;
324                 case 3:
325                         DEBUG_PRINTF("    IPL Device status RESERVED!!\n");
326                         break;
327                 }
328         }
329         if (M.x86.R_AX & 0x80) {        // bit 7
330                 DEBUG_PRINTF
331                     ("  Output Device supporting INT 10h Character Output:\n");
332                 switch (((M.x86.R_AX >> 4) & 0x3)) {    // bits 5:4
333                 case 0:
334                         DEBUG_PRINTF("    No Display Device attached\n");
335                         break;
336                 case 1:
337                         DEBUG_PRINTF("    Display Device status unknown\n");
338                         break;
339                 case 2:
340                         DEBUG_PRINTF("    Display Device attached\n");
341                         break;
342                 case 3:
343                         DEBUG_PRINTF("    Display Device status RESERVED!!\n");
344                         break;
345                 }
346         }
347         if (M.x86.R_AX & 0x40) {        // bit 6
348                 DEBUG_PRINTF
349                     ("  Input Device supporting INT 9h Character Input:\n");
350                 switch (((M.x86.R_AX >> 4) & 0x3)) {    // bits 5:4
351                 case 0:
352                         DEBUG_PRINTF("    No Input Device attached\n");
353                         break;
354                 case 1:
355                         DEBUG_PRINTF("    Input Device status unknown\n");
356                         break;
357                 case 2:
358                         DEBUG_PRINTF("    Input Device attached\n");
359                         break;
360                 case 3:
361                         DEBUG_PRINTF("    Input Device status RESERVED!!\n");
362                         break;
363                 }
364         }
365 #endif
366         /* Check whether the stack is "clean" i.e. containing the HLT
367          * instruction we pushed before executing and pointing to the original
368          * stack address... indicating that the initialization probably was
369          * successful
370          */
371         if ((pop_word() == 0xf4f4) && (M.x86.R_SS == STACK_SEGMENT)
372             && (M.x86.R_SP == STACK_START_OFFSET)) {
373                 DEBUG_PRINTF("Stack is clean, initialization successfull!\n");
374         } else {
375                 printf("Stack unclean, initialization probably NOT COMPLETE!\n");
376                 DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n",
377                              M.x86.R_SS, M.x86.R_SP, STACK_SEGMENT,
378                              STACK_START_OFFSET);
379         }
380
381         // TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting
382         // the status.
383         // We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30
384         // (also for Int19)
385         return 0;
386 }