xref: /OK3568_Linux_fs/u-boot/arch/x86/lib/bios.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 = &reg_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