1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * From Coreboot file device/oprom/realmode/x86.c
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2007 Advanced Micro Devices, Inc.
5*4882a593Smuzhiyun * Copyright (C) 2009-2010 coresystems GmbH
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun #include <common.h>
10*4882a593Smuzhiyun #include <bios_emul.h>
11*4882a593Smuzhiyun #include <vbe.h>
12*4882a593Smuzhiyun #include <linux/linkage.h>
13*4882a593Smuzhiyun #include <asm/cache.h>
14*4882a593Smuzhiyun #include <asm/processor.h>
15*4882a593Smuzhiyun #include <asm/i8259.h>
16*4882a593Smuzhiyun #include <asm/io.h>
17*4882a593Smuzhiyun #include <asm/post.h>
18*4882a593Smuzhiyun #include "bios.h"
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun /* Interrupt handlers for each interrupt the ROM can call */
21*4882a593Smuzhiyun static int (*int_handler[256])(void);
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun /* to have a common register file for interrupt handlers */
24*4882a593Smuzhiyun X86EMU_sysEnv _X86EMU_env;
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun asmlinkage void (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
27*4882a593Smuzhiyun u32 esi, u32 edi);
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun asmlinkage void (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx,
30*4882a593Smuzhiyun u32 edx, u32 esi, u32 edi);
31*4882a593Smuzhiyun
setup_realmode_code(void)32*4882a593Smuzhiyun static void setup_realmode_code(void)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun memcpy((void *)REALMODE_BASE, &asm_realmode_code,
35*4882a593Smuzhiyun asm_realmode_code_size);
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /* Ensure the global pointers are relocated properly. */
38*4882a593Smuzhiyun realmode_call = PTR_TO_REAL_MODE(asm_realmode_call);
39*4882a593Smuzhiyun realmode_interrupt = PTR_TO_REAL_MODE(__realmode_interrupt);
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun debug("Real mode stub @%x: %d bytes\n", REALMODE_BASE,
42*4882a593Smuzhiyun asm_realmode_code_size);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
setup_rombios(void)45*4882a593Smuzhiyun static void setup_rombios(void)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun const char date[] = "06/11/99";
48*4882a593Smuzhiyun memcpy((void *)0xffff5, &date, 8);
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun const char ident[] = "PCI_ISA";
51*4882a593Smuzhiyun memcpy((void *)0xfffd9, &ident, 7);
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun /* system model: IBM-AT */
54*4882a593Smuzhiyun writeb(0xfc, 0xffffe);
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
int_exception_handler(void)57*4882a593Smuzhiyun static int int_exception_handler(void)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun /* compatibility shim */
60*4882a593Smuzhiyun struct eregs reg_info = {
61*4882a593Smuzhiyun .eax = M.x86.R_EAX,
62*4882a593Smuzhiyun .ecx = M.x86.R_ECX,
63*4882a593Smuzhiyun .edx = M.x86.R_EDX,
64*4882a593Smuzhiyun .ebx = M.x86.R_EBX,
65*4882a593Smuzhiyun .esp = M.x86.R_ESP,
66*4882a593Smuzhiyun .ebp = M.x86.R_EBP,
67*4882a593Smuzhiyun .esi = M.x86.R_ESI,
68*4882a593Smuzhiyun .edi = M.x86.R_EDI,
69*4882a593Smuzhiyun .vector = M.x86.intno,
70*4882a593Smuzhiyun .error_code = 0,
71*4882a593Smuzhiyun .eip = M.x86.R_EIP,
72*4882a593Smuzhiyun .cs = M.x86.R_CS,
73*4882a593Smuzhiyun .eflags = M.x86.R_EFLG
74*4882a593Smuzhiyun };
75*4882a593Smuzhiyun struct eregs *regs = ®_info;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun debug("Oops, exception %d while executing option rom\n", regs->vector);
78*4882a593Smuzhiyun cpu_hlt();
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun return 0;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
int_unknown_handler(void)83*4882a593Smuzhiyun static int int_unknown_handler(void)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun debug("Unsupported software interrupt #0x%x eax 0x%x\n",
86*4882a593Smuzhiyun M.x86.intno, M.x86.R_EAX);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun return -1;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun /* setup interrupt handlers for mainboard */
bios_set_interrupt_handler(int intnum,int (* int_func)(void))92*4882a593Smuzhiyun void bios_set_interrupt_handler(int intnum, int (*int_func)(void))
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun int_handler[intnum] = int_func;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
setup_interrupt_handlers(void)97*4882a593Smuzhiyun static void setup_interrupt_handlers(void)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun int i;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun /*
102*4882a593Smuzhiyun * The first 16 int_handler functions are not BIOS services,
103*4882a593Smuzhiyun * but the CPU-generated exceptions ("hardware interrupts")
104*4882a593Smuzhiyun */
105*4882a593Smuzhiyun for (i = 0; i < 0x10; i++)
106*4882a593Smuzhiyun int_handler[i] = &int_exception_handler;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /* Mark all other int_handler calls as unknown first */
109*4882a593Smuzhiyun for (i = 0x10; i < 0x100; i++) {
110*4882a593Smuzhiyun /* Skip if bios_set_interrupt_handler() isn't called first */
111*4882a593Smuzhiyun if (int_handler[i])
112*4882a593Smuzhiyun continue;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /*
115*4882a593Smuzhiyun * Now set the default functions that are actually needed
116*4882a593Smuzhiyun * to initialize the option roms. The board may override
117*4882a593Smuzhiyun * these with bios_set_interrupt_handler()
118*4882a593Smuzhiyun */
119*4882a593Smuzhiyun switch (i) {
120*4882a593Smuzhiyun case 0x10:
121*4882a593Smuzhiyun int_handler[0x10] = &int10_handler;
122*4882a593Smuzhiyun break;
123*4882a593Smuzhiyun case 0x12:
124*4882a593Smuzhiyun int_handler[0x12] = &int12_handler;
125*4882a593Smuzhiyun break;
126*4882a593Smuzhiyun case 0x16:
127*4882a593Smuzhiyun int_handler[0x16] = &int16_handler;
128*4882a593Smuzhiyun break;
129*4882a593Smuzhiyun case 0x1a:
130*4882a593Smuzhiyun int_handler[0x1a] = &int1a_handler;
131*4882a593Smuzhiyun break;
132*4882a593Smuzhiyun default:
133*4882a593Smuzhiyun int_handler[i] = &int_unknown_handler;
134*4882a593Smuzhiyun break;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
write_idt_stub(void * target,u8 intnum)139*4882a593Smuzhiyun static void write_idt_stub(void *target, u8 intnum)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun unsigned char *codeptr;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun codeptr = (unsigned char *)target;
144*4882a593Smuzhiyun memcpy(codeptr, &__idt_handler, __idt_handler_size);
145*4882a593Smuzhiyun codeptr[3] = intnum; /* modify int# in the code stub. */
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
setup_realmode_idt(void)148*4882a593Smuzhiyun static void setup_realmode_idt(void)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun struct realmode_idt *idts = NULL;
151*4882a593Smuzhiyun int i;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun /*
154*4882a593Smuzhiyun * Copy IDT stub code for each interrupt. This might seem wasteful
155*4882a593Smuzhiyun * but it is really simple
156*4882a593Smuzhiyun */
157*4882a593Smuzhiyun for (i = 0; i < 256; i++) {
158*4882a593Smuzhiyun idts[i].cs = 0;
159*4882a593Smuzhiyun idts[i].offset = 0x1000 + (i * __idt_handler_size);
160*4882a593Smuzhiyun write_idt_stub((void *)((ulong)idts[i].offset), i);
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun /*
164*4882a593Smuzhiyun * Many option ROMs use the hard coded interrupt entry points in the
165*4882a593Smuzhiyun * system bios. So install them at the known locations.
166*4882a593Smuzhiyun */
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun /* int42 is the relocated int10 */
169*4882a593Smuzhiyun write_idt_stub((void *)0xff065, 0x42);
170*4882a593Smuzhiyun /* BIOS Int 11 Handler F000:F84D */
171*4882a593Smuzhiyun write_idt_stub((void *)0xff84d, 0x11);
172*4882a593Smuzhiyun /* BIOS Int 12 Handler F000:F841 */
173*4882a593Smuzhiyun write_idt_stub((void *)0xff841, 0x12);
174*4882a593Smuzhiyun /* BIOS Int 13 Handler F000:EC59 */
175*4882a593Smuzhiyun write_idt_stub((void *)0xfec59, 0x13);
176*4882a593Smuzhiyun /* BIOS Int 14 Handler F000:E739 */
177*4882a593Smuzhiyun write_idt_stub((void *)0xfe739, 0x14);
178*4882a593Smuzhiyun /* BIOS Int 15 Handler F000:F859 */
179*4882a593Smuzhiyun write_idt_stub((void *)0xff859, 0x15);
180*4882a593Smuzhiyun /* BIOS Int 16 Handler F000:E82E */
181*4882a593Smuzhiyun write_idt_stub((void *)0xfe82e, 0x16);
182*4882a593Smuzhiyun /* BIOS Int 17 Handler F000:EFD2 */
183*4882a593Smuzhiyun write_idt_stub((void *)0xfefd2, 0x17);
184*4882a593Smuzhiyun /* ROM BIOS Int 1A Handler F000:FE6E */
185*4882a593Smuzhiyun write_idt_stub((void *)0xffe6e, 0x1a);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
vbe_get_mode_info(struct vbe_mode_info * mi)188*4882a593Smuzhiyun static u8 vbe_get_mode_info(struct vbe_mode_info *mi)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun u16 buffer_seg;
191*4882a593Smuzhiyun u16 buffer_adr;
192*4882a593Smuzhiyun char *buffer;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun debug("VBE: Getting information about VESA mode %04x\n",
195*4882a593Smuzhiyun mi->video_mode);
196*4882a593Smuzhiyun buffer = PTR_TO_REAL_MODE(asm_realmode_buffer);
197*4882a593Smuzhiyun buffer_seg = (((unsigned long)buffer) >> 4) & 0xff00;
198*4882a593Smuzhiyun buffer_adr = ((unsigned long)buffer) & 0xffff;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun realmode_interrupt(0x10, VESA_GET_MODE_INFO, 0x0000, mi->video_mode,
201*4882a593Smuzhiyun 0x0000, buffer_seg, buffer_adr);
202*4882a593Smuzhiyun memcpy(mi->mode_info_block, buffer, sizeof(struct vbe_mode_info));
203*4882a593Smuzhiyun mi->valid = true;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun return 0;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
vbe_set_mode(struct vbe_mode_info * mi)208*4882a593Smuzhiyun static u8 vbe_set_mode(struct vbe_mode_info *mi)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun int video_mode = mi->video_mode;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun debug("VBE: Setting VESA mode %#04x\n", video_mode);
213*4882a593Smuzhiyun /* request linear framebuffer mode */
214*4882a593Smuzhiyun video_mode |= (1 << 14);
215*4882a593Smuzhiyun /* don't clear the framebuffer, we do that later */
216*4882a593Smuzhiyun video_mode |= (1 << 15);
217*4882a593Smuzhiyun realmode_interrupt(0x10, VESA_SET_MODE, video_mode,
218*4882a593Smuzhiyun 0x0000, 0x0000, 0x0000, 0x0000);
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun return 0;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
vbe_set_graphics(int vesa_mode,struct vbe_mode_info * mode_info)223*4882a593Smuzhiyun static void vbe_set_graphics(int vesa_mode, struct vbe_mode_info *mode_info)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun unsigned char *framebuffer;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun mode_info->video_mode = (1 << 14) | vesa_mode;
228*4882a593Smuzhiyun vbe_get_mode_info(mode_info);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun framebuffer = (unsigned char *)(ulong)mode_info->vesa.phys_base_ptr;
231*4882a593Smuzhiyun debug("VBE: resolution: %dx%d@%d\n",
232*4882a593Smuzhiyun le16_to_cpu(mode_info->vesa.x_resolution),
233*4882a593Smuzhiyun le16_to_cpu(mode_info->vesa.y_resolution),
234*4882a593Smuzhiyun mode_info->vesa.bits_per_pixel);
235*4882a593Smuzhiyun debug("VBE: framebuffer: %p\n", framebuffer);
236*4882a593Smuzhiyun if (!framebuffer) {
237*4882a593Smuzhiyun debug("VBE: Mode does not support linear framebuffer\n");
238*4882a593Smuzhiyun return;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun mode_info->video_mode &= 0x3ff;
242*4882a593Smuzhiyun vbe_set_mode(mode_info);
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
bios_run_on_x86(struct udevice * dev,unsigned long addr,int vesa_mode,struct vbe_mode_info * mode_info)245*4882a593Smuzhiyun void bios_run_on_x86(struct udevice *dev, unsigned long addr, int vesa_mode,
246*4882a593Smuzhiyun struct vbe_mode_info *mode_info)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun pci_dev_t pcidev = dm_pci_get_bdf(dev);
249*4882a593Smuzhiyun u32 num_dev;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun num_dev = PCI_BUS(pcidev) << 8 | PCI_DEV(pcidev) << 3 |
252*4882a593Smuzhiyun PCI_FUNC(pcidev);
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /* Needed to avoid exceptions in some ROMs */
255*4882a593Smuzhiyun interrupt_init();
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun /* Set up some legacy information in the F segment */
258*4882a593Smuzhiyun setup_rombios();
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun /* Set up C interrupt handlers */
261*4882a593Smuzhiyun setup_interrupt_handlers();
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun /* Set up real-mode IDT */
264*4882a593Smuzhiyun setup_realmode_idt();
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun /* Make sure the code is placed. */
267*4882a593Smuzhiyun setup_realmode_code();
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun debug("Calling Option ROM at %lx, pci device %#x...", addr, num_dev);
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun /* Option ROM entry point is at OPROM start + 3 */
272*4882a593Smuzhiyun realmode_call(addr + 0x0003, num_dev, 0xffff, 0x0000, 0xffff, 0x0,
273*4882a593Smuzhiyun 0x0);
274*4882a593Smuzhiyun debug("done\n");
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun if (vesa_mode != -1)
277*4882a593Smuzhiyun vbe_set_graphics(vesa_mode, mode_info);
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
interrupt_handler(u32 intnumber,u32 gsfs,u32 dses,u32 edi,u32 esi,u32 ebp,u32 esp,u32 ebx,u32 edx,u32 ecx,u32 eax,u32 cs_ip,u16 stackflags)280*4882a593Smuzhiyun asmlinkage int interrupt_handler(u32 intnumber, u32 gsfs, u32 dses,
281*4882a593Smuzhiyun u32 edi, u32 esi, u32 ebp, u32 esp,
282*4882a593Smuzhiyun u32 ebx, u32 edx, u32 ecx, u32 eax,
283*4882a593Smuzhiyun u32 cs_ip, u16 stackflags)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun u32 ip;
286*4882a593Smuzhiyun u32 cs;
287*4882a593Smuzhiyun u32 flags;
288*4882a593Smuzhiyun int ret = 0;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun ip = cs_ip & 0xffff;
291*4882a593Smuzhiyun cs = cs_ip >> 16;
292*4882a593Smuzhiyun flags = stackflags;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun #ifdef CONFIG_REALMODE_DEBUG
295*4882a593Smuzhiyun debug("oprom: INT# 0x%x\n", intnumber);
296*4882a593Smuzhiyun debug("oprom: eax: %08x ebx: %08x ecx: %08x edx: %08x\n",
297*4882a593Smuzhiyun eax, ebx, ecx, edx);
298*4882a593Smuzhiyun debug("oprom: ebp: %08x esp: %08x edi: %08x esi: %08x\n",
299*4882a593Smuzhiyun ebp, esp, edi, esi);
300*4882a593Smuzhiyun debug("oprom: ip: %04x cs: %04x flags: %08x\n",
301*4882a593Smuzhiyun ip, cs, flags);
302*4882a593Smuzhiyun debug("oprom: stackflags = %04x\n", stackflags);
303*4882a593Smuzhiyun #endif
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun /*
306*4882a593Smuzhiyun * Fetch arguments from the stack and put them to a place
307*4882a593Smuzhiyun * suitable for the interrupt handlers
308*4882a593Smuzhiyun */
309*4882a593Smuzhiyun M.x86.R_EAX = eax;
310*4882a593Smuzhiyun M.x86.R_ECX = ecx;
311*4882a593Smuzhiyun M.x86.R_EDX = edx;
312*4882a593Smuzhiyun M.x86.R_EBX = ebx;
313*4882a593Smuzhiyun M.x86.R_ESP = esp;
314*4882a593Smuzhiyun M.x86.R_EBP = ebp;
315*4882a593Smuzhiyun M.x86.R_ESI = esi;
316*4882a593Smuzhiyun M.x86.R_EDI = edi;
317*4882a593Smuzhiyun M.x86.intno = intnumber;
318*4882a593Smuzhiyun M.x86.R_EIP = ip;
319*4882a593Smuzhiyun M.x86.R_CS = cs;
320*4882a593Smuzhiyun M.x86.R_EFLG = flags;
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun /* Call the interrupt handler for this interrupt number */
323*4882a593Smuzhiyun ret = int_handler[intnumber]();
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun /*
326*4882a593Smuzhiyun * This code is quite strange...
327*4882a593Smuzhiyun *
328*4882a593Smuzhiyun * Put registers back on the stack. The assembler code will pop them
329*4882a593Smuzhiyun * later. We force (volatile!) changing the values of the parameters
330*4882a593Smuzhiyun * of this function. We know that they stay alive on the stack after
331*4882a593Smuzhiyun * we leave this function.
332*4882a593Smuzhiyun */
333*4882a593Smuzhiyun *(volatile u32 *)&eax = M.x86.R_EAX;
334*4882a593Smuzhiyun *(volatile u32 *)&ecx = M.x86.R_ECX;
335*4882a593Smuzhiyun *(volatile u32 *)&edx = M.x86.R_EDX;
336*4882a593Smuzhiyun *(volatile u32 *)&ebx = M.x86.R_EBX;
337*4882a593Smuzhiyun *(volatile u32 *)&esi = M.x86.R_ESI;
338*4882a593Smuzhiyun *(volatile u32 *)&edi = M.x86.R_EDI;
339*4882a593Smuzhiyun flags = M.x86.R_EFLG;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun /* Pass success or error back to our caller via the CARRY flag */
342*4882a593Smuzhiyun if (ret) {
343*4882a593Smuzhiyun flags &= ~1; /* no error: clear carry */
344*4882a593Smuzhiyun } else {
345*4882a593Smuzhiyun debug("int%02x call returned error\n", intnumber);
346*4882a593Smuzhiyun flags |= 1; /* error: set carry */
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun *(volatile u16 *)&stackflags = flags;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun return ret;
351*4882a593Smuzhiyun }
352