blob: e53ffdbe48b89f7ca9f33c7b8524a1c537cf02ea [file] [log] [blame]
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00001/******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
Uwe Hermann01ce6012010-03-05 10:03:50 +00004 * Copyright (c) 2010 coresystems GmbH
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00005 * 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>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000016#include <types.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000017
18#include "debug.h"
19
20#include <x86emu/x86emu.h>
21#include <x86emu/regs.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000022#include "../x86emu/prim_ops.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000023
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
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000031#include <device/device.h>
Uwe Hermann01ce6012010-03-05 10:03:50 +000032#include "compat/rtas.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000033
Denis 'GNUtoo' Carikli4cdc5d62013-05-15 00:19:49 +020034#if CONFIG_X86EMU_DEBUG_TIMINGS
35struct mono_time zero;
36#endif
37
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000038static X86EMU_memFuncs my_mem_funcs = {
39 my_rdb, my_rdw, my_rdl,
40 my_wrb, my_wrw, my_wrl
41};
42
43static X86EMU_pioFuncs my_pio_funcs = {
44 my_inb, my_inw, my_inl,
45 my_outb, my_outw, my_outl
46};
47
48/* interrupt function override array (see biosemu.h) */
49yabel_handleIntFunc yabel_intFuncArray[256];
50
Patrick Georgif3a163a2012-08-16 15:39:35 +020051void
52mainboard_interrupt_handlers(int interrupt, yabel_handleIntFunc func)
53{
54 yabel_intFuncArray[interrupt] = func;
55}
56
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000057/* main entry into YABEL biosemu, arguments are:
58 * *biosmem = pointer to virtual memory
59 * biosmem_size = size of the virtual memory
60 * *dev = pointer to the device to be initialised
61 * rom_addr = address of the OptionROM to be executed, if this is = 0, YABEL
62 * will look for an ExpansionROM BAR and use the code from there.
63 */
64u32
65biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_addr)
66{
67 u8 *rom_image;
68 int i = 0;
Uwe Hermann01ce6012010-03-05 10:03:50 +000069#if CONFIG_X86EMU_DEBUG
70 debug_flags = 0;
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000071#if CONFIG_X86EMU_DEBUG_JMP
Uwe Hermann01ce6012010-03-05 10:03:50 +000072 debug_flags |= DEBUG_JMP;
73#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000074#if CONFIG_X86EMU_DEBUG_TRACE
Uwe Hermann01ce6012010-03-05 10:03:50 +000075 debug_flags |= DEBUG_TRACE_X86EMU;
76#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000077#if CONFIG_X86EMU_DEBUG_PNP
Uwe Hermann01ce6012010-03-05 10:03:50 +000078 debug_flags |= DEBUG_PNP;
79#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000080#if CONFIG_X86EMU_DEBUG_DISK
Uwe Hermann01ce6012010-03-05 10:03:50 +000081 debug_flags |= DEBUG_DISK;
82#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000083#if CONFIG_X86EMU_DEBUG_PMM
Uwe Hermann01ce6012010-03-05 10:03:50 +000084 debug_flags |= DEBUG_PMM;
85#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000086#if CONFIG_X86EMU_DEBUG_VBE
Uwe Hermann01ce6012010-03-05 10:03:50 +000087 debug_flags |= DEBUG_VBE;
88#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000089#if CONFIG_X86EMU_DEBUG_INT10
Uwe Hermann01ce6012010-03-05 10:03:50 +000090 debug_flags |= DEBUG_PRINT_INT10;
91#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000092#if CONFIG_X86EMU_DEBUG_INTERRUPTS
Uwe Hermann01ce6012010-03-05 10:03:50 +000093 debug_flags |= DEBUG_INTR;
94#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000095#if CONFIG_X86EMU_DEBUG_CHECK_VMEM_ACCESS
Uwe Hermann01ce6012010-03-05 10:03:50 +000096 debug_flags |= DEBUG_CHECK_VMEM_ACCESS;
97#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +000098#if CONFIG_X86EMU_DEBUG_MEM
Uwe Hermann01ce6012010-03-05 10:03:50 +000099 debug_flags |= DEBUG_MEM;
100#endif
Stefan Reinauerd4814bd2011-04-21 20:45:45 +0000101#if CONFIG_X86EMU_DEBUG_IO
Uwe Hermann01ce6012010-03-05 10:03:50 +0000102 debug_flags |= DEBUG_IO;
103#endif
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000104
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000105#endif
Denis 'GNUtoo' Carikli4cdc5d62013-05-15 00:19:49 +0200106#if CONFIG_X86EMU_DEBUG_TIMINGS
107 /* required for i915tool compatible output */
108 zero.microseconds = 0;
109#endif
110
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000111 if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) {
112 printf("Error: Not enough virtual memory: %x, required: %x!\n",
113 biosmem_size, MIN_REQUIRED_VMEM_SIZE);
114 return -1;
115 }
116 if (biosemu_dev_init(dev) != 0) {
117 printf("Error initializing device!\n");
118 return -1;
119 }
120 if (biosemu_dev_check_exprom(rom_addr) != 0) {
121 printf("Error: Device Expansion ROM invalid!\n");
122 return -1;
123 }
Patrick Georgi91443042011-01-13 11:38:46 +0000124 biosemu_add_special_memory(0, 0x500); // IVT + BDA
125 biosemu_add_special_memory(OPTION_ROM_CODE_SEGMENT << 4, 0x10000); // option ROM
126
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000127 rom_image = (u8 *) bios_device.img_addr;
128 DEBUG_PRINTF("executing rom_image from %p\n", rom_image);
129 DEBUG_PRINTF("biosmem at %p\n", biosmem);
130
131 DEBUG_PRINTF("Image Size: %d\n", bios_device.img_size);
132
133 // in case we jump somewhere unexpected, or execution is finished,
134 // fill the biosmem with hlt instructions (0xf4)
Stefan Reinauerd650e992010-02-22 04:33:13 +0000135 // But we have to be careful: If biosmem is 0x00000000 we're running
136 // in the lower 1MB and we must not wipe memory like that.
137 if (biosmem) {
138 DEBUG_PRINTF("Clearing biosmem\n");
139 memset(biosmem, 0xf4, biosmem_size);
140 }
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000141
Stefan Reinauerd650e992010-02-22 04:33:13 +0000142 X86EMU_setMemBase(biosmem, biosmem_size);
143
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000144 DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M.mem_base,
145 (int) M.mem_size);
146
147 // copy expansion ROM image to segment OPTION_ROM_CODE_SEGMENT
148 // NOTE: this sometimes fails, some bytes are 0x00... so we compare
149 // after copying and do some retries...
Patrick Georgif0bf4b52011-01-21 12:45:37 +0000150 u8 *mem_img = (u8*)(OPTION_ROM_CODE_SEGMENT << 4);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000151 u8 copy_count = 0;
152 u8 cmp_result = 0;
153 do {
154#if 0
155 set_ci();
156 memcpy(mem_img, rom_image, len);
157 clr_ci();
158#else
159 // memcpy fails... try copy byte-by-byte with set/clr_ci
160 u8 c;
161 for (i = 0; i < bios_device.img_size; i++) {
162 set_ci();
163 c = *(rom_image + i);
164 if (c != *(rom_image + i)) {
165 clr_ci();
166 printf("Copy failed at: %x/%x\n", i,
167 bios_device.img_size);
168 printf("rom_image(%x): %x, mem_img(%x): %x\n",
169 i, *(rom_image + i), i, *(mem_img + i));
170 break;
171 }
172 clr_ci();
Stefan Reinauer49190472015-10-21 13:02:37 -0700173 my_wrb((uintptr_t)mem_img + i, c);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000174 }
175#endif
176 copy_count++;
177 set_ci();
178 cmp_result = memcmp(mem_img, rom_image, bios_device.img_size);
179 clr_ci();
180 }
181 while ((copy_count < 5) && (cmp_result != 0));
182 if (cmp_result != 0) {
183 printf
184 ("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n",
185 copy_count, cmp_result);
186 dump(rom_image, 0x20);
187 dump(mem_img, 0x20);
188 return 0;
189 }
190 // setup default Interrupt Vectors
191 // some expansion ROMs seem to check for these addresses..
192 // each handler is only an IRET (0xCF) instruction
193 // ROM BIOS Int 10 Handler F000:F065
194 my_wrl(0x10 * 4, 0xf000f065);
195 my_wrb(0x000ff065, 0xcf);
196 // ROM BIOS Int 11 Handler F000:F84D
197 my_wrl(0x11 * 4, 0xf000f84d);
198 my_wrb(0x000ff84d, 0xcf);
199 // ROM BIOS Int 12 Handler F000:F841
200 my_wrl(0x12 * 4, 0xf000f841);
201 my_wrb(0x000ff841, 0xcf);
202 // ROM BIOS Int 13 Handler F000:EC59
203 my_wrl(0x13 * 4, 0xf000ec59);
204 my_wrb(0x000fec59, 0xcf);
205 // ROM BIOS Int 14 Handler F000:E739
206 my_wrl(0x14 * 4, 0xf000e739);
207 my_wrb(0x000fe739, 0xcf);
208 // ROM BIOS Int 15 Handler F000:F859
209 my_wrl(0x15 * 4, 0xf000f859);
210 my_wrb(0x000ff859, 0xcf);
211 // ROM BIOS Int 16 Handler F000:E82E
212 my_wrl(0x16 * 4, 0xf000e82e);
213 my_wrb(0x000fe82e, 0xcf);
214 // ROM BIOS Int 17 Handler F000:EFD2
215 my_wrl(0x17 * 4, 0xf000efd2);
216 my_wrb(0x000fefd2, 0xcf);
217 // ROM BIOS Int 1A Handler F000:FE6E
218 my_wrl(0x1a * 4, 0xf000fe6e);
219 my_wrb(0x000ffe6e, 0xcf);
220
221 // setup BIOS Data Area (0000:04xx, or 0040:00xx)
Martin Roth63373ed2013-07-08 16:24:19 -0600222 // we currently 0 this area, meaning "we don't have
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000223 // any hardware" :-) no serial/parallel ports, floppys, ...
224 memset(biosmem + 0x400, 0x0, 0x100);
225
226 // at offset 13h in BDA is the memory size in kbytes
227 my_wrw(0x413, biosmem_size / 1024);
228 // at offset 0eh in BDA is the segment of the Extended BIOS Data Area
229 // see setup further down
230 my_wrw(0x40e, INITIAL_EBDA_SEGMENT);
231 // TODO: setup BDA Video Data ( offset 49h-66h)
232 // e.g. to store video mode, cursor position, ...
233 // in int10 (done) handler and VBE Functions
234
235 // TODO: setup BDA Fixed Disk Data
236 // 74h: Fixed Disk Last Operation Status
237 // 75h: Fixed Disk Number of Disk Drives
238
239 // TODO: check BDA for further needed data...
240
241 //setup Extended BIOS Data Area
242 //we currently 0 this area
243 memset(biosmem + (INITIAL_EBDA_SEGMENT << 4), 0, INITIAL_EBDA_SIZE);
244 // at offset 0h in EBDA is the size of the EBDA in KB
245 my_wrw((INITIAL_EBDA_SEGMENT << 4) + 0x0, INITIAL_EBDA_SIZE / 1024);
246 //TODO: check for further needed EBDA data...
247
248 // setup original ROM BIOS Area (F000:xxxx)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000249 const char *date = "06/11/99";
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000250 for (i = 0; date[i]; i++)
251 my_wrb(0xffff5 + i, date[i]);
252 // set up eisa ident string
Uwe Hermann01ce6012010-03-05 10:03:50 +0000253 const char *ident = "PCI_ISA";
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000254 for (i = 0; ident[i]; i++)
255 my_wrb(0xfffd9 + i, ident[i]);
256
257 // write system model id for IBM-AT
258 // according to "Ralf Browns Interrupt List" Int15 AH=C0 Table 515,
259 // model FC is the original AT and also used in all DOSEMU Versions.
260 my_wrb(0xFFFFE, 0xfc);
261
262 //setup interrupt handler
263 X86EMU_intrFuncs intrFuncs[256];
264 for (i = 0; i < 256; i++)
265 intrFuncs[i] = handleInterrupt;
266 X86EMU_setupIntrFuncs(intrFuncs);
267 X86EMU_setupPioFuncs(&my_pio_funcs);
268 X86EMU_setupMemFuncs(&my_mem_funcs);
269
270 //setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0
Stefan Reinauer14e22772010-04-27 06:56:47 +0000271 u8 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000272 if (pmm_length <= 0) {
273 printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n",
274 pmm_length);
275 return 0;
276 } else {
277 CHECK_DBG(DEBUG_PMM) {
278 /* test the PMM */
279 pmm_test();
280 /* and clean it again by calling pmm_setup... */
281 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
282 }
283 }
284 // setup the CPU
285 M.x86.R_AH = bios_device.bus;
286 M.x86.R_AL = bios_device.devfn;
287 M.x86.R_DX = 0x80;
288 M.x86.R_EIP = 3;
289 M.x86.R_CS = OPTION_ROM_CODE_SEGMENT;
290
291 // Initialize stack and data segment
292 M.x86.R_SS = STACK_SEGMENT;
293 M.x86.R_SP = STACK_START_OFFSET;
294 M.x86.R_DS = DATA_SEGMENT;
295
296 // push a HLT instruction and a pointer to it onto the stack
297 // any return will pop the pointer and jump to the HLT, thus
298 // exiting (more or less) cleanly
Uwe Hermann01ce6012010-03-05 10:03:50 +0000299 push_word(0xf4f4); // F4=HLT
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000300 push_word(M.x86.R_SS);
301 push_word(M.x86.R_SP + 2);
302
303 CHECK_DBG(DEBUG_TRACE_X86EMU) {
304 X86EMU_trace_on();
Uwe Hermann01ce6012010-03-05 10:03:50 +0000305#if 0
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000306 } else {
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000307 M.x86.debug |= DEBUG_SAVE_IP_CS_F;
308 M.x86.debug |= DEBUG_DECODE_F;
309 M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
310#endif
311 }
312 CHECK_DBG(DEBUG_JMP) {
313 M.x86.debug |= DEBUG_TRACEJMP_F;
314 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
315 M.x86.debug |= DEBUG_TRACECALL_F;
316 M.x86.debug |= DEBUG_TRACECALL_REGS_F;
Uwe Hermann01ce6012010-03-05 10:03:50 +0000317 }
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000318
319 DEBUG_PRINTF("Executing Initialization Vector...\n");
320 X86EMU_exec();
321 DEBUG_PRINTF("done\n");
322
323 /* According to the PNP BIOS Spec, Option ROMs should upon exit, return
324 * some boot device status in AX (see PNP BIOS Spec Section 3.3
325 */
326 DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX);
Stefan Reinauerd4814bd2011-04-21 20:45:45 +0000327#if CONFIG_X86EMU_DEBUG
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000328 DEBUG_PRINTF("Exit Status Decode:\n");
329 if (M.x86.R_AX & 0x100) { // bit 8
330 DEBUG_PRINTF
331 (" IPL Device supporting INT 13h Block Device Format:\n");
332 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
333 case 0:
334 DEBUG_PRINTF(" No IPL Device attached\n");
335 break;
336 case 1:
337 DEBUG_PRINTF(" IPL Device status unknown\n");
338 break;
339 case 2:
340 DEBUG_PRINTF(" IPL Device attached\n");
341 break;
342 case 3:
343 DEBUG_PRINTF(" IPL Device status RESERVED!!\n");
344 break;
345 }
346 }
347 if (M.x86.R_AX & 0x80) { // bit 7
348 DEBUG_PRINTF
349 (" Output Device supporting INT 10h Character Output:\n");
350 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
351 case 0:
352 DEBUG_PRINTF(" No Display Device attached\n");
353 break;
354 case 1:
355 DEBUG_PRINTF(" Display Device status unknown\n");
356 break;
357 case 2:
358 DEBUG_PRINTF(" Display Device attached\n");
359 break;
360 case 3:
361 DEBUG_PRINTF(" Display Device status RESERVED!!\n");
362 break;
363 }
364 }
365 if (M.x86.R_AX & 0x40) { // bit 6
366 DEBUG_PRINTF
367 (" Input Device supporting INT 9h Character Input:\n");
368 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
369 case 0:
370 DEBUG_PRINTF(" No Input Device attached\n");
371 break;
372 case 1:
373 DEBUG_PRINTF(" Input Device status unknown\n");
374 break;
375 case 2:
376 DEBUG_PRINTF(" Input Device attached\n");
377 break;
378 case 3:
379 DEBUG_PRINTF(" Input Device status RESERVED!!\n");
380 break;
381 }
382 }
383#endif
384 /* Check whether the stack is "clean" i.e. containing the HLT
385 * instruction we pushed before executing and pointing to the original
386 * stack address... indicating that the initialization probably was
387 * successful
388 */
389 if ((pop_word() == 0xf4f4) && (M.x86.R_SS == STACK_SEGMENT)
390 && (M.x86.R_SP == STACK_START_OFFSET)) {
Martin Roth63373ed2013-07-08 16:24:19 -0600391 DEBUG_PRINTF("Stack is clean, initialization successful!\n");
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000392 } else {
Uwe Hermann01ce6012010-03-05 10:03:50 +0000393 printf("Stack unclean, initialization probably NOT COMPLETE!\n");
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000394 DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n",
395 M.x86.R_SS, M.x86.R_SP, STACK_SEGMENT,
396 STACK_START_OFFSET);
397 }
398
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000399 // TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting
400 // the status.
401 // We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30
402 // (also for Int19)
403 return 0;
404}