1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
4 * Copyright (c) 2010 coresystems GmbH
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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.
33 * IBM Corporation - initial implementation
34 *****************************************************************************/
41 #include <x86emu/x86emu.h>
42 #include <x86emu/regs.h>
43 #include "../x86emu/prim_ops.h"
48 #include "interrupt.h"
52 #include <device/device.h>
53 #include "compat/rtas.h"
55 #if CONFIG(X86EMU_DEBUG_TIMINGS)
57 struct mono_time zero
;
60 static X86EMU_memFuncs my_mem_funcs
= {
61 my_rdb
, my_rdw
, my_rdl
,
62 my_wrb
, my_wrw
, my_wrl
65 static X86EMU_pioFuncs my_pio_funcs
= {
66 my_inb
, my_inw
, my_inl
,
67 my_outb
, my_outw
, my_outl
70 /* interrupt function override array (see biosemu.h) */
71 yabel_handleIntFunc yabel_intFuncArray
[256];
74 mainboard_interrupt_handlers(int interrupt
, yabel_handleIntFunc func
)
76 yabel_intFuncArray
[interrupt
] = func
;
79 /* 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.
87 biosemu(u8
*biosmem
, u32 biosmem_size
, struct device
* dev
, unsigned long rom_addr
)
91 #if CONFIG(X86EMU_DEBUG)
93 #if CONFIG(X86EMU_DEBUG_JMP)
94 debug_flags
|= DEBUG_JMP
;
96 #if CONFIG(X86EMU_DEBUG_TRACE)
97 debug_flags
|= DEBUG_TRACE_X86EMU
;
99 #if CONFIG(X86EMU_DEBUG_PNP)
100 debug_flags
|= DEBUG_PNP
;
102 #if CONFIG(X86EMU_DEBUG_DISK)
103 debug_flags
|= DEBUG_DISK
;
105 #if CONFIG(X86EMU_DEBUG_PMM)
106 debug_flags
|= DEBUG_PMM
;
108 #if CONFIG(X86EMU_DEBUG_VBE)
109 debug_flags
|= DEBUG_VBE
;
111 #if CONFIG(X86EMU_DEBUG_INT10)
112 debug_flags
|= DEBUG_PRINT_INT10
;
114 #if CONFIG(X86EMU_DEBUG_INTERRUPTS)
115 debug_flags
|= DEBUG_INTR
;
117 #if CONFIG(X86EMU_DEBUG_CHECK_VMEM_ACCESS)
118 debug_flags
|= DEBUG_CHECK_VMEM_ACCESS
;
120 #if CONFIG(X86EMU_DEBUG_MEM)
121 debug_flags
|= DEBUG_MEM
;
123 #if CONFIG(X86EMU_DEBUG_IO)
124 debug_flags
|= DEBUG_IO
;
128 #if CONFIG(X86EMU_DEBUG_TIMINGS)
129 /* required for i915tool compatible output */
130 zero
.microseconds
= 0;
133 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
);
138 if (biosemu_dev_init(dev
) != 0) {
139 printf("Error initializing device!\n");
142 if (biosemu_dev_check_exprom(rom_addr
) != 0) {
143 printf("Error: Device Expansion ROM invalid!\n");
146 biosemu_add_special_memory(0, 0x500); // IVT + BDA
147 biosemu_add_special_memory(OPTION_ROM_CODE_SEGMENT
<< 4, 0x10000); // option ROM
149 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
);
153 DEBUG_PRINTF("Image Size: %d\n", bios_device
.img_size
);
155 // in case we jump somewhere unexpected, or execution is finished,
156 // fill the biosmem with hlt instructions (0xf4)
157 // 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.
160 DEBUG_PRINTF("Clearing biosmem\n");
161 memset(biosmem
, 0xf4, biosmem_size
);
164 X86EMU_setMemBase(biosmem
, biosmem_size
);
166 DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M
.mem_base
,
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...
172 u8
*mem_img
= (u8
*)(OPTION_ROM_CODE_SEGMENT
<< 4);
178 memcpy(mem_img
, rom_image
, len
);
181 // memcpy fails... try copy byte-by-byte with set/clr_ci
183 for (i
= 0; i
< bios_device
.img_size
; i
++) {
185 c
= *(rom_image
+ i
);
186 if (c
!= *(rom_image
+ i
)) {
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
));
195 my_wrb((uintptr_t)mem_img
+ i
, c
);
200 cmp_result
= memcmp(mem_img
, rom_image
, bios_device
.img_size
);
203 while ((copy_count
< 5) && (cmp_result
!= 0));
204 if (cmp_result
!= 0) {
206 ("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n",
207 copy_count
, cmp_result
);
208 dump(rom_image
, 0x20);
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);
243 // setup BIOS Data Area (0000:04xx, or 0040:00xx)
244 // we currently 0 this area, meaning "we don't have
245 // any hardware" :-) no serial/parallel ports, floppys, ...
246 memset(biosmem
+ 0x400, 0x0, 0x100);
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
257 // TODO: setup BDA Fixed Disk Data
258 // 74h: Fixed Disk Last Operation Status
259 // 75h: Fixed Disk Number of Disk Drives
261 // TODO: check BDA for further needed data...
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...
270 // setup original ROM BIOS Area (F000:xxxx)
271 const char *date
= "06/11/99";
272 for (i
= 0; date
[i
]; i
++)
273 my_wrb(0xffff5 + i
, date
[i
]);
274 // set up eisa ident string
275 const char *ident
= "PCI_ISA";
276 for (i
= 0; ident
[i
]; i
++)
277 my_wrb(0xfffd9 + i
, ident
[i
]);
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);
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
);
292 //setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0
293 u8 pmm_length
= pmm_setup(BIOS_DATA_SEGMENT
, 0x0);
294 if (pmm_length
<= 0) {
295 printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n",
299 CHECK_DBG(DEBUG_PMM
) {
302 /* and clean it again by calling pmm_setup... */
303 pmm_length
= pmm_setup(BIOS_DATA_SEGMENT
, 0x0);
307 M
.x86
.R_AH
= bios_device
.bus
;
308 M
.x86
.R_AL
= bios_device
.devfn
;
311 M
.x86
.R_CS
= OPTION_ROM_CODE_SEGMENT
;
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
;
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
321 push_word(0xf4f4); // F4=HLT
322 push_word(M
.x86
.R_SS
);
323 push_word(M
.x86
.R_SP
+ 2);
325 CHECK_DBG(DEBUG_TRACE_X86EMU
) {
329 M
.x86
.debug
|= DEBUG_SAVE_IP_CS_F
;
330 M
.x86
.debug
|= DEBUG_DECODE_F
;
331 M
.x86
.debug
|= DEBUG_DECODE_NOPRINT_F
;
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
;
341 DEBUG_PRINTF("Executing Initialization Vector...\n");
343 DEBUG_PRINTF("done\n");
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
348 DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M
.x86
.R_AX
);
349 #if CONFIG(X86EMU_DEBUG)
350 DEBUG_PRINTF("Exit Status Decode:\n");
351 if (M
.x86
.R_AX
& 0x100) { // bit 8
353 (" IPL Device supporting INT 13h Block Device Format:\n");
354 switch (((M
.x86
.R_AX
>> 4) & 0x3)) { // bits 5:4
356 DEBUG_PRINTF(" No IPL Device attached\n");
359 DEBUG_PRINTF(" IPL Device status unknown\n");
362 DEBUG_PRINTF(" IPL Device attached\n");
365 DEBUG_PRINTF(" IPL Device status RESERVED!!\n");
369 if (M
.x86
.R_AX
& 0x80) { // bit 7
371 (" Output Device supporting INT 10h Character Output:\n");
372 switch (((M
.x86
.R_AX
>> 4) & 0x3)) { // bits 5:4
374 DEBUG_PRINTF(" No Display Device attached\n");
377 DEBUG_PRINTF(" Display Device status unknown\n");
380 DEBUG_PRINTF(" Display Device attached\n");
383 DEBUG_PRINTF(" Display Device status RESERVED!!\n");
387 if (M
.x86
.R_AX
& 0x40) { // bit 6
389 (" Input Device supporting INT 9h Character Input:\n");
390 switch (((M
.x86
.R_AX
>> 4) & 0x3)) { // bits 5:4
392 DEBUG_PRINTF(" No Input Device attached\n");
395 DEBUG_PRINTF(" Input Device status unknown\n");
398 DEBUG_PRINTF(" Input Device attached\n");
401 DEBUG_PRINTF(" Input Device status RESERVED!!\n");
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
411 if ((pop_word() == 0xf4f4) && (M
.x86
.R_SS
== STACK_SEGMENT
)
412 && (M
.x86
.R_SP
== STACK_START_OFFSET
)) {
413 DEBUG_PRINTF("Stack is clean, initialization successful!\n");
415 printf("Stack unclean, initialization probably NOT COMPLETE!\n");
416 DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n",
417 M
.x86
.R_SS
, M
.x86
.R_SP
, STACK_SEGMENT
,
421 // TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting
423 // We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30