xref: /aosp_15_r20/external/coreboot/src/device/oprom/yabel/biosemu.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /******************************************************************************
2  * Copyright (c) 2004, 2008 IBM Corporation
3  * Copyright (c) 2008, 2009 Pattrick Hueper <[email protected]>
4  * Copyright (c) 2010 coresystems GmbH
5  *
6  * All rights reserved.
7  *
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.
31  *
32  * Contributors:
33  *     IBM Corporation - initial implementation
34  *****************************************************************************/
35 
36 #include <string.h>
37 #include <types.h>
38 
39 #include "debug.h"
40 
41 #include <x86emu/x86emu.h>
42 #include <x86emu/regs.h>
43 #include "../x86emu/prim_ops.h"
44 
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 
52 #include <device/device.h>
53 #include "compat/rtas.h"
54 
55 #if CONFIG(X86EMU_DEBUG_TIMINGS)
56 #include <timer.h>
57 struct mono_time zero;
58 #endif
59 
60 static X86EMU_memFuncs my_mem_funcs = {
61 	my_rdb, my_rdw, my_rdl,
62 	my_wrb, my_wrw, my_wrl
63 };
64 
65 static 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) */
71 yabel_handleIntFunc yabel_intFuncArray[256];
72 
73 void
mainboard_interrupt_handlers(int interrupt,yabel_handleIntFunc func)74 mainboard_interrupt_handlers(int interrupt, yabel_handleIntFunc func)
75 {
76 	yabel_intFuncArray[interrupt] = func;
77 }
78 
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.
85  */
86 u32
biosemu(u8 * biosmem,u32 biosmem_size,struct device * dev,unsigned long rom_addr)87 biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_addr)
88 {
89 	u8 *rom_image;
90 	int i = 0;
91 #if CONFIG(X86EMU_DEBUG)
92 	debug_flags = 0;
93 #if CONFIG(X86EMU_DEBUG_JMP)
94 	debug_flags |= DEBUG_JMP;
95 #endif
96 #if CONFIG(X86EMU_DEBUG_TRACE)
97 	debug_flags |= DEBUG_TRACE_X86EMU;
98 #endif
99 #if CONFIG(X86EMU_DEBUG_PNP)
100 	debug_flags |= DEBUG_PNP;
101 #endif
102 #if CONFIG(X86EMU_DEBUG_DISK)
103 	debug_flags |= DEBUG_DISK;
104 #endif
105 #if CONFIG(X86EMU_DEBUG_PMM)
106 	debug_flags |= DEBUG_PMM;
107 #endif
108 #if CONFIG(X86EMU_DEBUG_VBE)
109 	debug_flags |= DEBUG_VBE;
110 #endif
111 #if CONFIG(X86EMU_DEBUG_INT10)
112 	debug_flags |= DEBUG_PRINT_INT10;
113 #endif
114 #if CONFIG(X86EMU_DEBUG_INTERRUPTS)
115 	debug_flags |= DEBUG_INTR;
116 #endif
117 #if CONFIG(X86EMU_DEBUG_CHECK_VMEM_ACCESS)
118 	debug_flags |= DEBUG_CHECK_VMEM_ACCESS;
119 #endif
120 #if CONFIG(X86EMU_DEBUG_MEM)
121 	debug_flags |= DEBUG_MEM;
122 #endif
123 #if CONFIG(X86EMU_DEBUG_IO)
124 	debug_flags |= DEBUG_IO;
125 #endif
126 
127 #endif
128 #if CONFIG(X86EMU_DEBUG_TIMINGS)
129 	/* required for i915tool compatible output */
130 	zero.microseconds = 0;
131 #endif
132 
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);
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 	}
146 	biosemu_add_special_memory(0, 0x500); // IVT + BDA
147 	biosemu_add_special_memory(OPTION_ROM_CODE_SEGMENT << 4, 0x10000); // option ROM
148 
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);
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)
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.
159 	if (biosmem) {
160 		DEBUG_PRINTF("Clearing biosmem\n");
161 		memset(biosmem, 0xf4, biosmem_size);
162 	}
163 
164 	X86EMU_setMemBase(biosmem, biosmem_size);
165 
166 	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...
172 	u8 *mem_img = (u8*)(OPTION_ROM_CODE_SEGMENT << 4);
173 	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();
195 			my_wrb((uintptr_t)mem_img + i, c);
196 		}
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)
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);
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)
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]);
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
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",
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
321 	push_word(0xf4f4);	// F4=HLT
322 	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();
327 #if 0
328 	} else {
329 		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;
339 	}
340 
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);
349 #if CONFIG(X86EMU_DEBUG)
350 	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)) {
413 		DEBUG_PRINTF("Stack is clean, initialization successful!\n");
414 	} else {
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,
418 			     STACK_START_OFFSET);
419 	}
420 
421 	// 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 }
427