blob: d0586c27836b3cdadda145c36d51ecdb3be4f2e6 [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
Martin Rotha9e1a222016-01-14 14:15:24 -07005 *
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00006 * All rights reserved.
Martin Rotha9e1a222016-01-14 14:15:24 -07007 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000031 *
32 * Contributors:
33 * IBM Corporation - initial implementation
34 *****************************************************************************/
35
36#include <string.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000037#include <types.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000038
39#include "debug.h"
40
41#include <x86emu/x86emu.h>
42#include <x86emu/regs.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000043#include "../x86emu/prim_ops.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000044
45#include "biosemu.h"
46#include "io.h"
47#include "mem.h"
48#include "interrupt.h"
49#include "device.h"
50#include "pmm.h"
51
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000052#include <device/device.h>
Uwe Hermann01ce6012010-03-05 10:03:50 +000053#include "compat/rtas.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000054
Julius Wernercd49cce2019-03-05 16:53:33 -080055#if CONFIG(X86EMU_DEBUG_TIMINGS)
Nico Huberbaa070a2019-04-06 18:00:35 +020056#include <timer.h>
Denis 'GNUtoo' Carikli4cdc5d62013-05-15 00:19:49 +020057struct mono_time zero;
58#endif
59
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000060static X86EMU_memFuncs my_mem_funcs = {
61 my_rdb, my_rdw, my_rdl,
62 my_wrb, my_wrw, my_wrl
63};
64
65static X86EMU_pioFuncs my_pio_funcs = {
66 my_inb, my_inw, my_inl,
67 my_outb, my_outw, my_outl
68};
69
70/* interrupt function override array (see biosemu.h) */
71yabel_handleIntFunc yabel_intFuncArray[256];
72
Patrick Georgif3a163a2012-08-16 15:39:35 +020073void
74mainboard_interrupt_handlers(int interrupt, yabel_handleIntFunc func)
75{
76 yabel_intFuncArray[interrupt] = func;
77}
78
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000079/* main entry into YABEL biosemu, arguments are:
80 * *biosmem = pointer to virtual memory
81 * biosmem_size = size of the virtual memory
82 * *dev = pointer to the device to be initialised
83 * rom_addr = address of the OptionROM to be executed, if this is = 0, YABEL
84 * will look for an ExpansionROM BAR and use the code from there.
85 */
86u32
87biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_addr)
88{
89 u8 *rom_image;
90 int i = 0;
Julius Wernercd49cce2019-03-05 16:53:33 -080091#if CONFIG(X86EMU_DEBUG)
Uwe Hermann01ce6012010-03-05 10:03:50 +000092 debug_flags = 0;
Julius Wernercd49cce2019-03-05 16:53:33 -080093#if CONFIG(X86EMU_DEBUG_JMP)
Uwe Hermann01ce6012010-03-05 10:03:50 +000094 debug_flags |= DEBUG_JMP;
95#endif
Julius Wernercd49cce2019-03-05 16:53:33 -080096#if CONFIG(X86EMU_DEBUG_TRACE)
Uwe Hermann01ce6012010-03-05 10:03:50 +000097 debug_flags |= DEBUG_TRACE_X86EMU;
98#endif
Julius Wernercd49cce2019-03-05 16:53:33 -080099#if CONFIG(X86EMU_DEBUG_PNP)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000100 debug_flags |= DEBUG_PNP;
101#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800102#if CONFIG(X86EMU_DEBUG_DISK)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000103 debug_flags |= DEBUG_DISK;
104#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800105#if CONFIG(X86EMU_DEBUG_PMM)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000106 debug_flags |= DEBUG_PMM;
107#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800108#if CONFIG(X86EMU_DEBUG_VBE)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000109 debug_flags |= DEBUG_VBE;
110#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800111#if CONFIG(X86EMU_DEBUG_INT10)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000112 debug_flags |= DEBUG_PRINT_INT10;
113#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800114#if CONFIG(X86EMU_DEBUG_INTERRUPTS)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000115 debug_flags |= DEBUG_INTR;
116#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800117#if CONFIG(X86EMU_DEBUG_CHECK_VMEM_ACCESS)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000118 debug_flags |= DEBUG_CHECK_VMEM_ACCESS;
119#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800120#if CONFIG(X86EMU_DEBUG_MEM)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000121 debug_flags |= DEBUG_MEM;
122#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800123#if CONFIG(X86EMU_DEBUG_IO)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000124 debug_flags |= DEBUG_IO;
125#endif
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000126
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000127#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800128#if CONFIG(X86EMU_DEBUG_TIMINGS)
Denis 'GNUtoo' Carikli4cdc5d62013-05-15 00:19:49 +0200129 /* required for i915tool compatible output */
130 zero.microseconds = 0;
131#endif
132
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000133 if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) {
134 printf("Error: Not enough virtual memory: %x, required: %x!\n",
135 biosmem_size, MIN_REQUIRED_VMEM_SIZE);
136 return -1;
137 }
138 if (biosemu_dev_init(dev) != 0) {
139 printf("Error initializing device!\n");
140 return -1;
141 }
142 if (biosemu_dev_check_exprom(rom_addr) != 0) {
143 printf("Error: Device Expansion ROM invalid!\n");
144 return -1;
145 }
Patrick Georgi91443042011-01-13 11:38:46 +0000146 biosemu_add_special_memory(0, 0x500); // IVT + BDA
147 biosemu_add_special_memory(OPTION_ROM_CODE_SEGMENT << 4, 0x10000); // option ROM
148
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000149 rom_image = (u8 *) bios_device.img_addr;
150 DEBUG_PRINTF("executing rom_image from %p\n", rom_image);
151 DEBUG_PRINTF("biosmem at %p\n", biosmem);
152
153 DEBUG_PRINTF("Image Size: %d\n", bios_device.img_size);
154
155 // in case we jump somewhere unexpected, or execution is finished,
156 // fill the biosmem with hlt instructions (0xf4)
Stefan Reinauerd650e992010-02-22 04:33:13 +0000157 // But we have to be careful: If biosmem is 0x00000000 we're running
158 // in the lower 1MB and we must not wipe memory like that.
159 if (biosmem) {
160 DEBUG_PRINTF("Clearing biosmem\n");
161 memset(biosmem, 0xf4, biosmem_size);
162 }
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000163
Stefan Reinauerd650e992010-02-22 04:33:13 +0000164 X86EMU_setMemBase(biosmem, biosmem_size);
165
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000166 DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M.mem_base,
167 (int) M.mem_size);
168
169 // copy expansion ROM image to segment OPTION_ROM_CODE_SEGMENT
170 // NOTE: this sometimes fails, some bytes are 0x00... so we compare
171 // after copying and do some retries...
Patrick Georgif0bf4b52011-01-21 12:45:37 +0000172 u8 *mem_img = (u8*)(OPTION_ROM_CODE_SEGMENT << 4);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000173 u8 copy_count = 0;
174 u8 cmp_result = 0;
175 do {
176#if 0
177 set_ci();
178 memcpy(mem_img, rom_image, len);
179 clr_ci();
180#else
181 // memcpy fails... try copy byte-by-byte with set/clr_ci
182 u8 c;
183 for (i = 0; i < bios_device.img_size; i++) {
184 set_ci();
185 c = *(rom_image + i);
186 if (c != *(rom_image + i)) {
187 clr_ci();
188 printf("Copy failed at: %x/%x\n", i,
189 bios_device.img_size);
190 printf("rom_image(%x): %x, mem_img(%x): %x\n",
191 i, *(rom_image + i), i, *(mem_img + i));
192 break;
193 }
194 clr_ci();
Stefan Reinauer49190472015-10-21 13:02:37 -0700195 my_wrb((uintptr_t)mem_img + i, c);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000196 }
197#endif
198 copy_count++;
199 set_ci();
200 cmp_result = memcmp(mem_img, rom_image, bios_device.img_size);
201 clr_ci();
202 }
203 while ((copy_count < 5) && (cmp_result != 0));
204 if (cmp_result != 0) {
205 printf
206 ("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n",
207 copy_count, cmp_result);
208 dump(rom_image, 0x20);
209 dump(mem_img, 0x20);
210 return 0;
211 }
212 // setup default Interrupt Vectors
213 // some expansion ROMs seem to check for these addresses..
214 // each handler is only an IRET (0xCF) instruction
215 // ROM BIOS Int 10 Handler F000:F065
216 my_wrl(0x10 * 4, 0xf000f065);
217 my_wrb(0x000ff065, 0xcf);
218 // ROM BIOS Int 11 Handler F000:F84D
219 my_wrl(0x11 * 4, 0xf000f84d);
220 my_wrb(0x000ff84d, 0xcf);
221 // ROM BIOS Int 12 Handler F000:F841
222 my_wrl(0x12 * 4, 0xf000f841);
223 my_wrb(0x000ff841, 0xcf);
224 // ROM BIOS Int 13 Handler F000:EC59
225 my_wrl(0x13 * 4, 0xf000ec59);
226 my_wrb(0x000fec59, 0xcf);
227 // ROM BIOS Int 14 Handler F000:E739
228 my_wrl(0x14 * 4, 0xf000e739);
229 my_wrb(0x000fe739, 0xcf);
230 // ROM BIOS Int 15 Handler F000:F859
231 my_wrl(0x15 * 4, 0xf000f859);
232 my_wrb(0x000ff859, 0xcf);
233 // ROM BIOS Int 16 Handler F000:E82E
234 my_wrl(0x16 * 4, 0xf000e82e);
235 my_wrb(0x000fe82e, 0xcf);
236 // ROM BIOS Int 17 Handler F000:EFD2
237 my_wrl(0x17 * 4, 0xf000efd2);
238 my_wrb(0x000fefd2, 0xcf);
239 // ROM BIOS Int 1A Handler F000:FE6E
240 my_wrl(0x1a * 4, 0xf000fe6e);
241 my_wrb(0x000ffe6e, 0xcf);
242
243 // setup BIOS Data Area (0000:04xx, or 0040:00xx)
Martin Roth63373ed2013-07-08 16:24:19 -0600244 // we currently 0 this area, meaning "we don't have
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000245 // any hardware" :-) no serial/parallel ports, floppys, ...
246 memset(biosmem + 0x400, 0x0, 0x100);
247
248 // at offset 13h in BDA is the memory size in kbytes
249 my_wrw(0x413, biosmem_size / 1024);
250 // at offset 0eh in BDA is the segment of the Extended BIOS Data Area
251 // see setup further down
252 my_wrw(0x40e, INITIAL_EBDA_SEGMENT);
253 // TODO: setup BDA Video Data ( offset 49h-66h)
254 // e.g. to store video mode, cursor position, ...
255 // in int10 (done) handler and VBE Functions
256
257 // TODO: setup BDA Fixed Disk Data
258 // 74h: Fixed Disk Last Operation Status
259 // 75h: Fixed Disk Number of Disk Drives
260
261 // TODO: check BDA for further needed data...
262
263 //setup Extended BIOS Data Area
264 //we currently 0 this area
265 memset(biosmem + (INITIAL_EBDA_SEGMENT << 4), 0, INITIAL_EBDA_SIZE);
266 // at offset 0h in EBDA is the size of the EBDA in KB
267 my_wrw((INITIAL_EBDA_SEGMENT << 4) + 0x0, INITIAL_EBDA_SIZE / 1024);
268 //TODO: check for further needed EBDA data...
269
270 // setup original ROM BIOS Area (F000:xxxx)
Uwe Hermann01ce6012010-03-05 10:03:50 +0000271 const char *date = "06/11/99";
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000272 for (i = 0; date[i]; i++)
273 my_wrb(0xffff5 + i, date[i]);
274 // set up eisa ident string
Uwe Hermann01ce6012010-03-05 10:03:50 +0000275 const char *ident = "PCI_ISA";
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000276 for (i = 0; ident[i]; i++)
277 my_wrb(0xfffd9 + i, ident[i]);
278
279 // write system model id for IBM-AT
280 // according to "Ralf Browns Interrupt List" Int15 AH=C0 Table 515,
281 // model FC is the original AT and also used in all DOSEMU Versions.
282 my_wrb(0xFFFFE, 0xfc);
283
284 //setup interrupt handler
285 X86EMU_intrFuncs intrFuncs[256];
286 for (i = 0; i < 256; i++)
287 intrFuncs[i] = handleInterrupt;
288 X86EMU_setupIntrFuncs(intrFuncs);
289 X86EMU_setupPioFuncs(&my_pio_funcs);
290 X86EMU_setupMemFuncs(&my_mem_funcs);
291
292 //setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0
Stefan Reinauer14e22772010-04-27 06:56:47 +0000293 u8 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000294 if (pmm_length <= 0) {
295 printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n",
296 pmm_length);
297 return 0;
298 } else {
299 CHECK_DBG(DEBUG_PMM) {
300 /* test the PMM */
301 pmm_test();
302 /* and clean it again by calling pmm_setup... */
303 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
304 }
305 }
306 // setup the CPU
307 M.x86.R_AH = bios_device.bus;
308 M.x86.R_AL = bios_device.devfn;
309 M.x86.R_DX = 0x80;
310 M.x86.R_EIP = 3;
311 M.x86.R_CS = OPTION_ROM_CODE_SEGMENT;
312
313 // Initialize stack and data segment
314 M.x86.R_SS = STACK_SEGMENT;
315 M.x86.R_SP = STACK_START_OFFSET;
316 M.x86.R_DS = DATA_SEGMENT;
317
318 // push a HLT instruction and a pointer to it onto the stack
319 // any return will pop the pointer and jump to the HLT, thus
320 // exiting (more or less) cleanly
Uwe Hermann01ce6012010-03-05 10:03:50 +0000321 push_word(0xf4f4); // F4=HLT
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000322 push_word(M.x86.R_SS);
323 push_word(M.x86.R_SP + 2);
324
325 CHECK_DBG(DEBUG_TRACE_X86EMU) {
326 X86EMU_trace_on();
Uwe Hermann01ce6012010-03-05 10:03:50 +0000327#if 0
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000328 } else {
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000329 M.x86.debug |= DEBUG_SAVE_IP_CS_F;
330 M.x86.debug |= DEBUG_DECODE_F;
331 M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
332#endif
333 }
334 CHECK_DBG(DEBUG_JMP) {
335 M.x86.debug |= DEBUG_TRACEJMP_F;
336 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
337 M.x86.debug |= DEBUG_TRACECALL_F;
338 M.x86.debug |= DEBUG_TRACECALL_REGS_F;
Uwe Hermann01ce6012010-03-05 10:03:50 +0000339 }
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000340
341 DEBUG_PRINTF("Executing Initialization Vector...\n");
342 X86EMU_exec();
343 DEBUG_PRINTF("done\n");
344
345 /* According to the PNP BIOS Spec, Option ROMs should upon exit, return
346 * some boot device status in AX (see PNP BIOS Spec Section 3.3
347 */
348 DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX);
Julius Wernercd49cce2019-03-05 16:53:33 -0800349#if CONFIG(X86EMU_DEBUG)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000350 DEBUG_PRINTF("Exit Status Decode:\n");
351 if (M.x86.R_AX & 0x100) { // bit 8
352 DEBUG_PRINTF
353 (" IPL Device supporting INT 13h Block Device Format:\n");
354 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
355 case 0:
356 DEBUG_PRINTF(" No IPL Device attached\n");
357 break;
358 case 1:
359 DEBUG_PRINTF(" IPL Device status unknown\n");
360 break;
361 case 2:
362 DEBUG_PRINTF(" IPL Device attached\n");
363 break;
364 case 3:
365 DEBUG_PRINTF(" IPL Device status RESERVED!!\n");
366 break;
367 }
368 }
369 if (M.x86.R_AX & 0x80) { // bit 7
370 DEBUG_PRINTF
371 (" Output Device supporting INT 10h Character Output:\n");
372 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
373 case 0:
374 DEBUG_PRINTF(" No Display Device attached\n");
375 break;
376 case 1:
377 DEBUG_PRINTF(" Display Device status unknown\n");
378 break;
379 case 2:
380 DEBUG_PRINTF(" Display Device attached\n");
381 break;
382 case 3:
383 DEBUG_PRINTF(" Display Device status RESERVED!!\n");
384 break;
385 }
386 }
387 if (M.x86.R_AX & 0x40) { // bit 6
388 DEBUG_PRINTF
389 (" Input Device supporting INT 9h Character Input:\n");
390 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
391 case 0:
392 DEBUG_PRINTF(" No Input Device attached\n");
393 break;
394 case 1:
395 DEBUG_PRINTF(" Input Device status unknown\n");
396 break;
397 case 2:
398 DEBUG_PRINTF(" Input Device attached\n");
399 break;
400 case 3:
401 DEBUG_PRINTF(" Input Device status RESERVED!!\n");
402 break;
403 }
404 }
405#endif
406 /* Check whether the stack is "clean" i.e. containing the HLT
407 * instruction we pushed before executing and pointing to the original
408 * stack address... indicating that the initialization probably was
409 * successful
410 */
411 if ((pop_word() == 0xf4f4) && (M.x86.R_SS == STACK_SEGMENT)
412 && (M.x86.R_SP == STACK_START_OFFSET)) {
Martin Roth63373ed2013-07-08 16:24:19 -0600413 DEBUG_PRINTF("Stack is clean, initialization successful!\n");
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000414 } else {
Uwe Hermann01ce6012010-03-05 10:03:50 +0000415 printf("Stack unclean, initialization probably NOT COMPLETE!\n");
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000416 DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n",
417 M.x86.R_SS, M.x86.R_SP, STACK_SEGMENT,
418 STACK_START_OFFSET);
419 }
420
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000421 // TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting
422 // the status.
423 // We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30
424 // (also for Int19)
425 return 0;
426}