2 * From Coreboot file device/oprom/realmode/x86.c
4 * Copyright (C) 2007 Advanced Micro Devices, Inc.
5 * Copyright (C) 2009-2010 coresystems GmbH
7 * SPDX-License-Identifier: GPL-2.0
10 #include <bios_emul.h>
12 #include <linux/linkage.h>
13 #include <asm/cache.h>
14 #include <asm/processor.h>
15 #include <asm/i8259.h>
20 /* Interrupt handlers for each interrupt the ROM can call */
21 static int (*int_handler
[256])(void);
23 /* to have a common register file for interrupt handlers */
24 X86EMU_sysEnv _X86EMU_env
;
26 asmlinkage
void (*realmode_call
)(u32 addr
, u32 eax
, u32 ebx
, u32 ecx
, u32 edx
,
29 asmlinkage
void (*realmode_interrupt
)(u32 intno
, u32 eax
, u32 ebx
, u32 ecx
,
30 u32 edx
, u32 esi
, u32 edi
);
32 static void setup_realmode_code(void)
34 memcpy((void *)REALMODE_BASE
, &asm_realmode_code
,
35 asm_realmode_code_size
);
37 /* Ensure the global pointers are relocated properly. */
38 realmode_call
= PTR_TO_REAL_MODE(asm_realmode_call
);
39 realmode_interrupt
= PTR_TO_REAL_MODE(__realmode_interrupt
);
41 debug("Real mode stub @%x: %d bytes\n", REALMODE_BASE
,
42 asm_realmode_code_size
);
45 static void setup_rombios(void)
47 const char date
[] = "06/11/99";
48 memcpy((void *)0xffff5, &date
, 8);
50 const char ident
[] = "PCI_ISA";
51 memcpy((void *)0xfffd9, &ident
, 7);
53 /* system model: IBM-AT */
54 writeb(0xfc, 0xffffe);
57 static int int_exception_handler(void)
59 /* compatibility shim */
60 struct eregs reg_info
= {
69 .vector
= M
.x86
.intno
,
73 .eflags
= M
.x86
.R_EFLG
75 struct eregs
*regs
= ®_info
;
77 debug("Oops, exception %d while executing option rom\n", regs
->vector
);
83 static int int_unknown_handler(void)
85 debug("Unsupported software interrupt #0x%x eax 0x%x\n",
86 M
.x86
.intno
, M
.x86
.R_EAX
);
91 /* setup interrupt handlers for mainboard */
92 void bios_set_interrupt_handler(int intnum
, int (*int_func
)(void))
94 int_handler
[intnum
] = int_func
;
97 static void setup_interrupt_handlers(void)
102 * The first 16 int_handler functions are not BIOS services,
103 * but the CPU-generated exceptions ("hardware interrupts")
105 for (i
= 0; i
< 0x10; i
++)
106 int_handler
[i
] = &int_exception_handler
;
108 /* Mark all other int_handler calls as unknown first */
109 for (i
= 0x10; i
< 0x100; i
++) {
110 /* Skip if bios_set_interrupt_handler() isn't called first */
115 * Now set the default functions that are actually needed
116 * to initialize the option roms. The board may override
117 * these with bios_set_interrupt_handler()
121 int_handler
[0x10] = &int10_handler
;
124 int_handler
[0x12] = &int12_handler
;
127 int_handler
[0x16] = &int16_handler
;
130 int_handler
[0x1a] = &int1a_handler
;
133 int_handler
[i
] = &int_unknown_handler
;
139 static void write_idt_stub(void *target
, u8 intnum
)
141 unsigned char *codeptr
;
143 codeptr
= (unsigned char *)target
;
144 memcpy(codeptr
, &__idt_handler
, __idt_handler_size
);
145 codeptr
[3] = intnum
; /* modify int# in the code stub. */
148 static void setup_realmode_idt(void)
150 struct realmode_idt
*idts
= NULL
;
154 * Copy IDT stub code for each interrupt. This might seem wasteful
155 * but it is really simple
157 for (i
= 0; i
< 256; i
++) {
159 idts
[i
].offset
= 0x1000 + (i
* __idt_handler_size
);
160 write_idt_stub((void *)((ulong
)idts
[i
].offset
), i
);
164 * Many option ROMs use the hard coded interrupt entry points in the
165 * system bios. So install them at the known locations.
168 /* int42 is the relocated int10 */
169 write_idt_stub((void *)0xff065, 0x42);
170 /* BIOS Int 11 Handler F000:F84D */
171 write_idt_stub((void *)0xff84d, 0x11);
172 /* BIOS Int 12 Handler F000:F841 */
173 write_idt_stub((void *)0xff841, 0x12);
174 /* BIOS Int 13 Handler F000:EC59 */
175 write_idt_stub((void *)0xfec59, 0x13);
176 /* BIOS Int 14 Handler F000:E739 */
177 write_idt_stub((void *)0xfe739, 0x14);
178 /* BIOS Int 15 Handler F000:F859 */
179 write_idt_stub((void *)0xff859, 0x15);
180 /* BIOS Int 16 Handler F000:E82E */
181 write_idt_stub((void *)0xfe82e, 0x16);
182 /* BIOS Int 17 Handler F000:EFD2 */
183 write_idt_stub((void *)0xfefd2, 0x17);
184 /* ROM BIOS Int 1A Handler F000:FE6E */
185 write_idt_stub((void *)0xffe6e, 0x1a);
188 static u8
vbe_get_mode_info(struct vbe_mode_info
*mi
)
194 debug("VBE: Getting information about VESA mode %04x\n",
196 buffer
= PTR_TO_REAL_MODE(asm_realmode_buffer
);
197 buffer_seg
= (((unsigned long)buffer
) >> 4) & 0xff00;
198 buffer_adr
= ((unsigned long)buffer
) & 0xffff;
200 realmode_interrupt(0x10, VESA_GET_MODE_INFO
, 0x0000, mi
->video_mode
,
201 0x0000, buffer_seg
, buffer_adr
);
202 memcpy(mi
->mode_info_block
, buffer
, sizeof(struct vbe_mode_info
));
208 static u8
vbe_set_mode(struct vbe_mode_info
*mi
)
210 int video_mode
= mi
->video_mode
;
212 debug("VBE: Setting VESA mode %#04x\n", video_mode
);
213 /* request linear framebuffer mode */
214 video_mode
|= (1 << 14);
215 /* don't clear the framebuffer, we do that later */
216 video_mode
|= (1 << 15);
217 realmode_interrupt(0x10, VESA_SET_MODE
, video_mode
,
218 0x0000, 0x0000, 0x0000, 0x0000);
223 static void vbe_set_graphics(int vesa_mode
, struct vbe_mode_info
*mode_info
)
225 unsigned char *framebuffer
;
227 mode_info
->video_mode
= (1 << 14) | vesa_mode
;
228 vbe_get_mode_info(mode_info
);
230 framebuffer
= (unsigned char *)(ulong
)mode_info
->vesa
.phys_base_ptr
;
231 debug("VBE: resolution: %dx%d@%d\n",
232 le16_to_cpu(mode_info
->vesa
.x_resolution
),
233 le16_to_cpu(mode_info
->vesa
.y_resolution
),
234 mode_info
->vesa
.bits_per_pixel
);
235 debug("VBE: framebuffer: %p\n", framebuffer
);
237 debug("VBE: Mode does not support linear framebuffer\n");
241 mode_info
->video_mode
&= 0x3ff;
242 vbe_set_mode(mode_info
);
245 void bios_run_on_x86(struct udevice
*dev
, unsigned long addr
, int vesa_mode
,
246 struct vbe_mode_info
*mode_info
)
248 pci_dev_t pcidev
= dm_pci_get_bdf(dev
);
251 num_dev
= PCI_BUS(pcidev
) << 8 | PCI_DEV(pcidev
) << 3 |
254 /* Needed to avoid exceptions in some ROMs */
257 /* Set up some legacy information in the F segment */
260 /* Set up C interrupt handlers */
261 setup_interrupt_handlers();
263 /* Set up real-mode IDT */
264 setup_realmode_idt();
266 /* Make sure the code is placed. */
267 setup_realmode_code();
269 debug("Calling Option ROM at %lx, pci device %#x...", addr
, num_dev
);
271 /* Option ROM entry point is at OPROM start + 3 */
272 realmode_call(addr
+ 0x0003, num_dev
, 0xffff, 0x0000, 0xffff, 0x0,
277 vbe_set_graphics(vesa_mode
, mode_info
);
280 asmlinkage
int interrupt_handler(u32 intnumber
, u32 gsfs
, u32 dses
,
281 u32 edi
, u32 esi
, u32 ebp
, u32 esp
,
282 u32 ebx
, u32 edx
, u32 ecx
, u32 eax
,
283 u32 cs_ip
, u16 stackflags
)
294 #ifdef CONFIG_REALMODE_DEBUG
295 debug("oprom: INT# 0x%x\n", intnumber
);
296 debug("oprom: eax: %08x ebx: %08x ecx: %08x edx: %08x\n",
298 debug("oprom: ebp: %08x esp: %08x edi: %08x esi: %08x\n",
300 debug("oprom: ip: %04x cs: %04x flags: %08x\n",
302 debug("oprom: stackflags = %04x\n", stackflags
);
306 * Fetch arguments from the stack and put them to a place
307 * suitable for the interrupt handlers
317 M
.x86
.intno
= intnumber
;
320 M
.x86
.R_EFLG
= flags
;
322 /* Call the interrupt handler for this interrupt number */
323 ret
= int_handler
[intnumber
]();
326 * This code is quite strange...
328 * Put registers back on the stack. The assembler code will pop them
329 * later. We force (volatile!) changing the values of the parameters
330 * of this function. We know that they stay alive on the stack after
331 * we leave this function.
333 *(volatile u32
*)&eax
= M
.x86
.R_EAX
;
334 *(volatile u32
*)&ecx
= M
.x86
.R_ECX
;
335 *(volatile u32
*)&edx
= M
.x86
.R_EDX
;
336 *(volatile u32
*)&ebx
= M
.x86
.R_EBX
;
337 *(volatile u32
*)&esi
= M
.x86
.R_ESI
;
338 *(volatile u32
*)&edi
= M
.x86
.R_EDI
;
339 flags
= M
.x86
.R_EFLG
;
341 /* Pass success or error back to our caller via the CARRY flag */
343 flags
&= ~1; /* no error: clear carry */
345 debug("int%02x call returned error\n", intnumber
);
346 flags
|= 1; /* error: set carry */
348 *(volatile u16
*)&stackflags
= flags
;