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