1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
4 * Copyright (c) 2010 coresystems GmbH
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
12 * IBM Corporation - initial implementation
13 *****************************************************************************/
20 #include <x86emu/x86emu.h>
21 #include <x86emu/regs.h>
22 #include "../x86emu/prim_ops.h"
27 #include "interrupt.h"
31 #include <device/device.h>
32 #include "compat/rtas.h"
34 static X86EMU_memFuncs my_mem_funcs = {
35 my_rdb, my_rdw, my_rdl,
36 my_wrb, my_wrw, my_wrl
39 static X86EMU_pioFuncs my_pio_funcs = {
40 my_inb, my_inw, my_inl,
41 my_outb, my_outw, my_outl
44 /* interrupt function override array (see biosemu.h) */
45 yabel_handleIntFunc yabel_intFuncArray[256];
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.
55 biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_addr)
59 #if CONFIG_X86EMU_DEBUG
61 #if CONFIG_X86EMU_DEBUG_JMP
62 debug_flags |= DEBUG_JMP;
64 #if CONFIG_X86EMU_DEBUG_TRACE
65 debug_flags |= DEBUG_TRACE_X86EMU;
67 #if CONFIG_X86EMU_DEBUG_PNP
68 debug_flags |= DEBUG_PNP;
70 #if CONFIG_X86EMU_DEBUG_DISK
71 debug_flags |= DEBUG_DISK;
73 #if CONFIG_X86EMU_DEBUG_PMM
74 debug_flags |= DEBUG_PMM;
76 #if CONFIG_X86EMU_DEBUG_VBE
77 debug_flags |= DEBUG_VBE;
79 #if CONFIG_X86EMU_DEBUG_INT10
80 debug_flags |= DEBUG_PRINT_INT10;
82 #if CONFIG_X86EMU_DEBUG_INTERRUPTS
83 debug_flags |= DEBUG_INTR;
85 #if CONFIG_X86EMU_DEBUG_CHECK_VMEM_ACCESS
86 debug_flags |= DEBUG_CHECK_VMEM_ACCESS;
88 #if CONFIG_X86EMU_DEBUG_MEM
89 debug_flags |= DEBUG_MEM;
91 #if CONFIG_X86EMU_DEBUG_IO
92 debug_flags |= DEBUG_IO;
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);
101 if (biosemu_dev_init(dev) != 0) {
102 printf("Error initializing device!\n");
105 if (biosemu_dev_check_exprom(rom_addr) != 0) {
106 printf("Error: Device Expansion ROM invalid!\n");
109 biosemu_add_special_memory(0, 0x500); // IVT + BDA
110 biosemu_add_special_memory(OPTION_ROM_CODE_SEGMENT << 4, 0x10000); // option ROM
112 rom_image = (u8 *) bios_device.img_addr;
113 DEBUG_PRINTF("executing rom_image from %p\n", rom_image);
114 DEBUG_PRINTF("biosmem at %p\n", biosmem);
116 DEBUG_PRINTF("Image Size: %d\n", bios_device.img_size);
118 // in case we jump somewhere unexpected, or execution is finished,
119 // fill the biosmem with hlt instructions (0xf4)
120 // But we have to be careful: If biosmem is 0x00000000 we're running
121 // in the lower 1MB and we must not wipe memory like that.
123 DEBUG_PRINTF("Clearing biosmem\n");
124 memset(biosmem, 0xf4, biosmem_size);
127 X86EMU_setMemBase(biosmem, biosmem_size);
129 DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M.mem_base,
132 // copy expansion ROM image to segment OPTION_ROM_CODE_SEGMENT
133 // NOTE: this sometimes fails, some bytes are 0x00... so we compare
134 // after copying and do some retries...
135 u8 *mem_img = (u8*)(OPTION_ROM_CODE_SEGMENT << 4);
141 memcpy(mem_img, rom_image, len);
144 // memcpy fails... try copy byte-by-byte with set/clr_ci
146 for (i = 0; i < bios_device.img_size; i++) {
148 c = *(rom_image + i);
149 if (c != *(rom_image + i)) {
151 printf("Copy failed at: %x/%x\n", i,
152 bios_device.img_size);
153 printf("rom_image(%x): %x, mem_img(%x): %x\n",
154 i, *(rom_image + i), i, *(mem_img + i));
158 my_wrb((u32)mem_img + i, c);
163 cmp_result = memcmp(mem_img, rom_image, bios_device.img_size);
166 while ((copy_count < 5) && (cmp_result != 0));
167 if (cmp_result != 0) {
169 ("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n",
170 copy_count, cmp_result);
171 dump(rom_image, 0x20);
175 // setup default Interrupt Vectors
176 // some expansion ROMs seem to check for these addresses..
177 // each handler is only an IRET (0xCF) instruction
178 // ROM BIOS Int 10 Handler F000:F065
179 my_wrl(0x10 * 4, 0xf000f065);
180 my_wrb(0x000ff065, 0xcf);
181 // ROM BIOS Int 11 Handler F000:F84D
182 my_wrl(0x11 * 4, 0xf000f84d);
183 my_wrb(0x000ff84d, 0xcf);
184 // ROM BIOS Int 12 Handler F000:F841
185 my_wrl(0x12 * 4, 0xf000f841);
186 my_wrb(0x000ff841, 0xcf);
187 // ROM BIOS Int 13 Handler F000:EC59
188 my_wrl(0x13 * 4, 0xf000ec59);
189 my_wrb(0x000fec59, 0xcf);
190 // ROM BIOS Int 14 Handler F000:E739
191 my_wrl(0x14 * 4, 0xf000e739);
192 my_wrb(0x000fe739, 0xcf);
193 // ROM BIOS Int 15 Handler F000:F859
194 my_wrl(0x15 * 4, 0xf000f859);
195 my_wrb(0x000ff859, 0xcf);
196 // ROM BIOS Int 16 Handler F000:E82E
197 my_wrl(0x16 * 4, 0xf000e82e);
198 my_wrb(0x000fe82e, 0xcf);
199 // ROM BIOS Int 17 Handler F000:EFD2
200 my_wrl(0x17 * 4, 0xf000efd2);
201 my_wrb(0x000fefd2, 0xcf);
202 // ROM BIOS Int 1A Handler F000:FE6E
203 my_wrl(0x1a * 4, 0xf000fe6e);
204 my_wrb(0x000ffe6e, 0xcf);
206 // setup BIOS Data Area (0000:04xx, or 0040:00xx)
207 // we currently 0 this area, meaning "we dont have
208 // any hardware" :-) no serial/parallel ports, floppys, ...
209 memset(biosmem + 0x400, 0x0, 0x100);
211 // at offset 13h in BDA is the memory size in kbytes
212 my_wrw(0x413, biosmem_size / 1024);
213 // at offset 0eh in BDA is the segment of the Extended BIOS Data Area
214 // see setup further down
215 my_wrw(0x40e, INITIAL_EBDA_SEGMENT);
216 // TODO: setup BDA Video Data ( offset 49h-66h)
217 // e.g. to store video mode, cursor position, ...
218 // in int10 (done) handler and VBE Functions
220 // TODO: setup BDA Fixed Disk Data
221 // 74h: Fixed Disk Last Operation Status
222 // 75h: Fixed Disk Number of Disk Drives
224 // TODO: check BDA for further needed data...
226 //setup Extended BIOS Data Area
227 //we currently 0 this area
228 memset(biosmem + (INITIAL_EBDA_SEGMENT << 4), 0, INITIAL_EBDA_SIZE);
229 // at offset 0h in EBDA is the size of the EBDA in KB
230 my_wrw((INITIAL_EBDA_SEGMENT << 4) + 0x0, INITIAL_EBDA_SIZE / 1024);
231 //TODO: check for further needed EBDA data...
233 // setup original ROM BIOS Area (F000:xxxx)
234 const char *date = "06/11/99";
235 for (i = 0; date[i]; i++)
236 my_wrb(0xffff5 + i, date[i]);
237 // set up eisa ident string
238 const char *ident = "PCI_ISA";
239 for (i = 0; ident[i]; i++)
240 my_wrb(0xfffd9 + i, ident[i]);
242 // write system model id for IBM-AT
243 // according to "Ralf Browns Interrupt List" Int15 AH=C0 Table 515,
244 // model FC is the original AT and also used in all DOSEMU Versions.
245 my_wrb(0xFFFFE, 0xfc);
247 //setup interrupt handler
248 X86EMU_intrFuncs intrFuncs[256];
249 for (i = 0; i < 256; i++)
250 intrFuncs[i] = handleInterrupt;
251 X86EMU_setupIntrFuncs(intrFuncs);
252 X86EMU_setupPioFuncs(&my_pio_funcs);
253 X86EMU_setupMemFuncs(&my_mem_funcs);
255 //setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0
256 u8 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
257 if (pmm_length <= 0) {
258 printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n",
262 CHECK_DBG(DEBUG_PMM) {
265 /* and clean it again by calling pmm_setup... */
266 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
270 M.x86.R_AH = bios_device.bus;
271 M.x86.R_AL = bios_device.devfn;
274 M.x86.R_CS = OPTION_ROM_CODE_SEGMENT;
276 // Initialize stack and data segment
277 M.x86.R_SS = STACK_SEGMENT;
278 M.x86.R_SP = STACK_START_OFFSET;
279 M.x86.R_DS = DATA_SEGMENT;
281 // push a HLT instruction and a pointer to it onto the stack
282 // any return will pop the pointer and jump to the HLT, thus
283 // exiting (more or less) cleanly
284 push_word(0xf4f4); // F4=HLT
285 push_word(M.x86.R_SS);
286 push_word(M.x86.R_SP + 2);
288 CHECK_DBG(DEBUG_TRACE_X86EMU) {
292 M.x86.debug |= DEBUG_SAVE_IP_CS_F;
293 M.x86.debug |= DEBUG_DECODE_F;
294 M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
297 CHECK_DBG(DEBUG_JMP) {
298 M.x86.debug |= DEBUG_TRACEJMP_F;
299 M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
300 M.x86.debug |= DEBUG_TRACECALL_F;
301 M.x86.debug |= DEBUG_TRACECALL_REGS_F;
304 DEBUG_PRINTF("Executing Initialization Vector...\n");
306 DEBUG_PRINTF("done\n");
308 /* According to the PNP BIOS Spec, Option ROMs should upon exit, return
309 * some boot device status in AX (see PNP BIOS Spec Section 3.3
311 DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX);
312 #if CONFIG_X86EMU_DEBUG
313 DEBUG_PRINTF("Exit Status Decode:\n");
314 if (M.x86.R_AX & 0x100) { // bit 8
316 (" IPL Device supporting INT 13h Block Device Format:\n");
317 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
319 DEBUG_PRINTF(" No IPL Device attached\n");
322 DEBUG_PRINTF(" IPL Device status unknown\n");
325 DEBUG_PRINTF(" IPL Device attached\n");
328 DEBUG_PRINTF(" IPL Device status RESERVED!!\n");
332 if (M.x86.R_AX & 0x80) { // bit 7
334 (" Output Device supporting INT 10h Character Output:\n");
335 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
337 DEBUG_PRINTF(" No Display Device attached\n");
340 DEBUG_PRINTF(" Display Device status unknown\n");
343 DEBUG_PRINTF(" Display Device attached\n");
346 DEBUG_PRINTF(" Display Device status RESERVED!!\n");
350 if (M.x86.R_AX & 0x40) { // bit 6
352 (" Input Device supporting INT 9h Character Input:\n");
353 switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
355 DEBUG_PRINTF(" No Input Device attached\n");
358 DEBUG_PRINTF(" Input Device status unknown\n");
361 DEBUG_PRINTF(" Input Device attached\n");
364 DEBUG_PRINTF(" Input Device status RESERVED!!\n");
369 /* Check whether the stack is "clean" i.e. containing the HLT
370 * instruction we pushed before executing and pointing to the original
371 * stack address... indicating that the initialization probably was
374 if ((pop_word() == 0xf4f4) && (M.x86.R_SS == STACK_SEGMENT)
375 && (M.x86.R_SP == STACK_START_OFFSET)) {
376 DEBUG_PRINTF("Stack is clean, initialization successfull!\n");
378 printf("Stack unclean, initialization probably NOT COMPLETE!\n");
379 DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n",
380 M.x86.R_SS, M.x86.R_SP, STACK_SEGMENT,
384 // TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting
386 // We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30