source: mainline/kernel/arch/ia32/src/asm.S

Last change on this file was ebb3538, checked in by Martin Decky <martin@…>, 4 years ago

Improve early kernel debugging prints

Since the early kernel debugging prints are useful only in a few
debugging scenarios, define a configuration option that disables them by
default (if enabled, it produces duplicate output which might be
confusing).

Implement early kernel debugging prints for the HiKey960.

  • Property mode set to 100644
File size: 13.1 KB
RevLine 
[6dce6af]1/*
[8269769]2 * Copyright (c) 2010 Jakub Jermar
[6dce6af]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
[f761f1eb]28
[6dce6af]29/** Very low and hardware-level functions
30 *
31 */
[f761f1eb]32
[8844e70]33#include <abi/asmtool.h>
[da52547]34#include <arch/pm.h>
[0cd21bf]35#include <arch/cpu.h>
[da52547]36#include <arch/mm/page.h>
[8d427a3]37#include <arch/istate_struct.h>
[0f17bff]38#include <arch/smp/apic.h>
[f761f1eb]39
[da52547]40.text
[e3c762cd]41
[6dce6af]42#define MEMCPY_DST 4
43#define MEMCPY_SRC 8
44#define MEMCPY_SIZE 12
[e3c762cd]45
46/** Copy memory to/from userspace.
47 *
48 * This is almost conventional memcpy().
49 * The difference is that there is a failover part
50 * to where control is returned from a page fault
51 * if the page fault occurs during copy_from_uspace()
52 * or copy_to_uspace().
53 *
[6dce6af]54 * @param MEMCPY_DST(%esp) Destination address.
55 * @param MEMCPY_SRC(%esp) Source address.
56 * @param MEMCPY_SIZE(%esp) Size.
[e3c762cd]57 *
[da349da0]58 * @return MEMCPY_DST(%esp) on success and 0 on failure.
[6dce6af]59 *
[e3c762cd]60 */
[8844e70]61FUNCTION_BEGIN(memcpy_from_uspace)
62FUNCTION_BEGIN(memcpy_to_uspace)
[6dce6af]63 movl %edi, %edx /* save %edi */
64 movl %esi, %eax /* save %esi */
[a35b458]65
[e3c762cd]66 movl MEMCPY_SIZE(%esp), %ecx
[6dce6af]67 shrl $2, %ecx /* size / 4 */
[a35b458]68
[e3c762cd]69 movl MEMCPY_DST(%esp), %edi
70 movl MEMCPY_SRC(%esp), %esi
[a35b458]71
[6dce6af]72 /* Copy whole words */
73 rep movsl
[a35b458]74
[e3c762cd]75 movl MEMCPY_SIZE(%esp), %ecx
[6dce6af]76 andl $3, %ecx /* size % 4 */
[e3c762cd]77 jz 0f
[a35b458]78
[6dce6af]79 /* Copy the rest byte by byte */
80 rep movsb
[a35b458]81
[6dce6af]82 0:
[a35b458]83
[6dce6af]84 movl %edx, %edi
85 movl %eax, %esi
[a35b458]86
[6dce6af]87 /* MEMCPY_DST(%esp), success */
88 movl MEMCPY_DST(%esp), %eax
89 ret
[8844e70]90FUNCTION_END(memcpy_from_uspace)
91FUNCTION_END(memcpy_to_uspace)
[6dce6af]92
[e3c762cd]93/*
94 * We got here from as_page_fault() after the memory operations
95 * above had caused a page fault.
96 */
[8844e70]97SYMBOL(memcpy_from_uspace_failover_address)
98SYMBOL(memcpy_to_uspace_failover_address)
[e3c762cd]99 movl %edx, %edi
100 movl %eax, %esi
[a35b458]101
[6dce6af]102 /* Return 0, failure */
103 xorl %eax, %eax
[e3c762cd]104 ret
[da585a52]105
[6dce6af]106/** Turn paging on
107 *
108 * Enable paging and write-back caching in CR0.
109 *
110 */
[8844e70]111FUNCTION_BEGIN(paging_on)
[6c383b0]112 movl %cr0, %edx
[0f17bff]113 orl $CR0_PG, %edx /* paging on */
[a35b458]114
[6dce6af]115 /* Clear Cache Disable and not Write Though */
[0f17bff]116 andl $~(CR0_CD | CR0_NW), %edx
[6dce6af]117 movl %edx, %cr0
[f761f1eb]118 jmp 0f
[a35b458]119
[6dce6af]120 0:
121 ret
[8844e70]122FUNCTION_END(paging_on)
[da585a52]123
[6dce6af]124/** Enable local APIC
125 *
126 * Enable local APIC in MSR.
127 *
128 */
[8844e70]129FUNCTION_BEGIN(enable_l_apic_in_msr)
[0f17bff]130 movl $IA32_MSR_APIC_BASE, %ecx
[f761f1eb]131 rdmsr
[0f17bff]132 orl $(L_APIC_BASE | IA32_APIC_BASE_GE), %eax
[f761f1eb]133 wrmsr
134 ret
[8844e70]135FUNCTION_END(enable_l_apic_in_msr)
[f761f1eb]136
[6473d41]137/*
[6dce6af]138 * Size of the istate structure without the hardware-saved part
139 * and without the error word.
[6473d41]140 */
[1b20da0]141#define ISTATE_SOFT_SIZE ISTATE_SIZE - (6 * 4)
[6dce6af]142
[ed7998b]143/*
144 * The SYSENTER syscall mechanism can be used for syscalls with
145 * four or fewer arguments. To pass these four arguments, we
146 * use four registers: EDX, ECX, EBX, ESI. The syscall number
147 * is passed in EAX. We use EDI to remember the return address
148 * and EBP to remember the stack. The INT-based syscall mechanism
149 * can actually handle six arguments plus the syscall number
150 * entirely in registers.
151 */
[8844e70]152SYMBOL(sysenter_handler)
[c8cd9a8]153
154 /*
155 * Note that the space needed for the istate structure has been
156 * preallocated on the stack by before_thread_runs_arch().
157 */
[ed7998b]158
159 /*
160 * Save the return address and the userspace stack in the istate
161 * structure on locations that would normally be taken by them.
162 */
163 movl %ebp, ISTATE_OFFSET_ESP(%esp)
164 movl %edi, ISTATE_OFFSET_EIP(%esp)
165
166 /*
167 * Push syscall arguments onto the stack
168 */
169 movl %eax, ISTATE_OFFSET_EAX(%esp)
170 movl %ebx, ISTATE_OFFSET_EBX(%esp)
171 movl %ecx, ISTATE_OFFSET_ECX(%esp)
172 movl %edx, ISTATE_OFFSET_EDX(%esp)
173 movl %esi, ISTATE_OFFSET_ESI(%esp)
174 movl %edi, ISTATE_OFFSET_EDI(%esp) /* observability; not needed */
175 movl %ebp, ISTATE_OFFSET_EBP(%esp) /* observability; not needed */
[a35b458]176
[ed7998b]177 /*
178 * Fake up the stack trace linkage.
179 */
180 movl %edi, ISTATE_OFFSET_EIP_FRAME(%esp)
181 movl $0, ISTATE_OFFSET_EBP_FRAME(%esp)
182 leal ISTATE_OFFSET_EBP_FRAME(%esp), %ebp
183
184 /*
185 * Switch to kernel selectors.
186 */
[d6f9fff]187 movl $(GDT_SELECTOR(KDATA_DES)), %eax
188 movl %eax, %ds
189 movl %eax, %es
190 movl $(GDT_SELECTOR(VREG_DES)), %eax
191 movl %eax, %gs
[a35b458]192
[0cd21bf]193 /*
194 * Sanitize EFLAGS.
195 *
196 * SYSENTER does not clear the NT flag, which could thus proliferate
197 * from here to the IRET instruction via a context switch and result
198 * in crash.
199 *
200 * SYSENTER does not clear DF, which the ABI assumes to be cleared.
201 *
202 * SYSENTER clears IF, which we would like to be set for syscalls.
203 *
204 */
205 pushl $(EFLAGS_IF) /* specify EFLAGS bits that we want to set */
206 popfl /* set bits from the mask, clear or ignore others */
207
[ed7998b]208 call syscall_handler
[a35b458]209
[ed7998b]210 /*
211 * Prepare return address and userspace stack for SYSEXIT.
212 */
213 movl ISTATE_OFFSET_EIP(%esp), %edx
214 movl ISTATE_OFFSET_ESP(%esp), %ecx
215
216 sysexit /* return to userspace */
217
[44c69b66]218/*
219 * This is the legacy syscall handler using the interrupt mechanism.
[6dce6af]220 */
[8844e70]221SYMBOL(int_syscall)
[44c69b66]222 subl $(ISTATE_SOFT_SIZE + 4), %esp
[6473d41]223
[44c69b66]224 /*
225 * Push syscall arguments onto the stack
226 *
227 * NOTE: The idea behind the order of arguments passed
228 * in registers is to use all scratch registers
229 * first and preserved registers next. An optimized
230 * libc syscall wrapper can make use of this setup.
231 * The istate structure is arranged in the way to support
232 * this idea.
233 *
234 */
235 movl %eax, ISTATE_OFFSET_EAX(%esp)
236 movl %ebx, ISTATE_OFFSET_EBX(%esp)
237 movl %ecx, ISTATE_OFFSET_ECX(%esp)
238 movl %edx, ISTATE_OFFSET_EDX(%esp)
239 movl %edi, ISTATE_OFFSET_EDI(%esp)
240 movl %esi, ISTATE_OFFSET_ESI(%esp)
241 movl %ebp, ISTATE_OFFSET_EBP(%esp)
[0737078]242
[44c69b66]243 /*
[d6f9fff]244 * Save the segment registers.
[44c69b66]245 */
246 movl %gs, %ecx
247 movl %fs, %edx
[0737078]248
[44c69b66]249 movl %ecx, ISTATE_OFFSET_GS(%esp)
250 movl %edx, ISTATE_OFFSET_FS(%esp)
[0737078]251
[44c69b66]252 movl %es, %ecx
253 movl %ds, %edx
[a35b458]254
[44c69b66]255 movl %ecx, ISTATE_OFFSET_ES(%esp)
256 movl %edx, ISTATE_OFFSET_DS(%esp)
[0737078]257
[44c69b66]258 /*
259 * Switch to kernel selectors.
260 */
[1d3d2cf]261 movl $(GDT_SELECTOR(KDATA_DES)), %eax
[44c69b66]262 movl %eax, %ds
263 movl %eax, %es
[d6f9fff]264 movl $(GDT_SELECTOR(VREG_DES)), %eax
265 movl %eax, %gs
[a35b458]266
[44c69b66]267 movl $0, ISTATE_OFFSET_EBP_FRAME(%esp)
268 movl ISTATE_OFFSET_EIP(%esp), %eax
269 movl %eax, ISTATE_OFFSET_EIP_FRAME(%esp)
270 leal ISTATE_OFFSET_EBP_FRAME(%esp), %ebp
[a35b458]271
[44c69b66]272 cld
[a35b458]273
[44c69b66]274 /* Call syscall_handler(edx, ecx, ebx, esi, edi, ebp, eax) */
275 call syscall_handler
[a35b458]276
[44c69b66]277 /*
[d6f9fff]278 * Restore the segment registers.
[44c69b66]279 */
280 movl ISTATE_OFFSET_GS(%esp), %ecx
281 movl ISTATE_OFFSET_FS(%esp), %edx
[0737078]282
[44c69b66]283 movl %ecx, %gs
284 movl %edx, %fs
[0737078]285
[44c69b66]286 movl ISTATE_OFFSET_ES(%esp), %ecx
287 movl ISTATE_OFFSET_DS(%esp), %edx
[a35b458]288
[44c69b66]289 movl %ecx, %es
290 movl %edx, %ds
[a35b458]291
[44c69b66]292 /*
293 * Restore the preserved registers the handler cloberred itself
294 * (i.e. EBP).
295 */
296 movl ISTATE_OFFSET_EBP(%esp), %ebp
[a35b458]297
[44c69b66]298 addl $(ISTATE_SOFT_SIZE + 4), %esp
299 iret
[a35b458]300
[4e91239]301/**
302 * Mask for interrupts 0 - 31 (bits 0 - 31) where 0 means that int
303 * has no error word and 1 means interrupt with error word
304 *
305 */
306#define ERROR_WORD_INTERRUPT_LIST 0x00027d00
[44c69b66]307
308.macro handler i
[8844e70]309SYMBOL(int_\i)
[44c69b66]310 /*
311 * This macro distinguishes between two versions of ia32
312 * exceptions. One version has error word and the other
313 * does not have it. The latter version fakes the error
314 * word on the stack so that the handlers and istate_t
315 * can be the same for both types.
316 */
317 .iflt \i - 32
318 .if (1 << \i) & ERROR_WORD_INTERRUPT_LIST
319 /*
[246f939]320 * Exception with error word.
[44c69b66]321 */
[246f939]322 subl $ISTATE_SOFT_SIZE, %esp
[6dce6af]323 .else
324 /*
[44c69b66]325 * Exception without error word: fake up one
[6dce6af]326 */
[246f939]327 subl $(ISTATE_SOFT_SIZE + 4), %esp
[6dce6af]328 .endif
[44c69b66]329 .else
[6dce6af]330 /*
[1b20da0]331 * Interrupt: fake up an error word
[6dce6af]332 */
[246f939]333 subl $(ISTATE_SOFT_SIZE + 4), %esp
[44c69b66]334 .endif
[a35b458]335
[44c69b66]336 /*
337 * Save the general purpose registers.
338 */
339 movl %eax, ISTATE_OFFSET_EAX(%esp)
340 movl %ebx, ISTATE_OFFSET_EBX(%esp)
341 movl %ecx, ISTATE_OFFSET_ECX(%esp)
342 movl %edx, ISTATE_OFFSET_EDX(%esp)
343 movl %edi, ISTATE_OFFSET_EDI(%esp)
344 movl %esi, ISTATE_OFFSET_ESI(%esp)
345 movl %ebp, ISTATE_OFFSET_EBP(%esp)
[a35b458]346
[44c69b66]347 /*
[d6f9fff]348 * Save the segment registers.
[44c69b66]349 */
[b539f54]350 movl %gs, %ecx
351 movl %fs, %edx
352
353 movl %ecx, ISTATE_OFFSET_GS(%esp)
354 movl %edx, ISTATE_OFFSET_FS(%esp)
355
[44c69b66]356 movl %es, %ecx
357 movl %ds, %edx
[a35b458]358
[44c69b66]359 movl %ecx, ISTATE_OFFSET_ES(%esp)
360 movl %edx, ISTATE_OFFSET_DS(%esp)
[a35b458]361
[44c69b66]362 /*
363 * Switch to kernel selectors.
364 */
[1d3d2cf]365 movl $(GDT_SELECTOR(KDATA_DES)), %eax
[44c69b66]366 movl %eax, %ds
367 movl %eax, %es
[d6f9fff]368 movl $(GDT_SELECTOR(VREG_DES)), %eax
369 movl %eax, %gs
[a35b458]370
[44c69b66]371 /*
372 * Imitate a regular stack frame linkage.
373 * Stop stack traces here if we came from userspace.
374 */
[8078180]375 xorl %eax, %eax
[1d3d2cf]376 cmpl $(GDT_SELECTOR(KTEXT_DES)), ISTATE_OFFSET_CS(%esp)
[8c15255]377#ifdef PROCESSOR_i486
378 jz 0f
[1c99eae]379 movl %eax, %ebp
380 0:
[8c15255]381#else
[35599c9]382 cmovnzl %eax, %ebp
[8c15255]383#endif
[8078180]384
385 movl %ebp, ISTATE_OFFSET_EBP_FRAME(%esp)
386 movl ISTATE_OFFSET_EIP(%esp), %eax
387 movl %eax, ISTATE_OFFSET_EIP_FRAME(%esp)
388 leal ISTATE_OFFSET_EBP_FRAME(%esp), %ebp
[a35b458]389
[8078180]390 cld
[a35b458]391
[8078180]392 pushl %esp /* pass istate address */
393 pushl $(\i) /* pass intnum */
[a35b458]394
[8078180]395 /* Call exc_dispatch(intnum, istate) */
396 call exc_dispatch
[a35b458]397
[8078180]398 addl $8, %esp /* clear arguments from the stack */
[a35b458]399
[8078180]400 /*
401 * Restore the selector registers.
402 */
403 movl ISTATE_OFFSET_GS(%esp), %ecx
404 movl ISTATE_OFFSET_FS(%esp), %edx
[b539f54]405
[8078180]406 movl %ecx, %gs
407 movl %edx, %fs
[b539f54]408
[8078180]409 movl ISTATE_OFFSET_ES(%esp), %ecx
410 movl ISTATE_OFFSET_DS(%esp), %edx
[a35b458]411
[8078180]412 movl %ecx, %es
413 movl %edx, %ds
[a35b458]414
[8078180]415 /*
416 * Restore the scratch registers and the preserved
417 * registers the handler cloberred itself
418 * (i.e. EBP).
419 */
420 movl ISTATE_OFFSET_EAX(%esp), %eax
421 movl ISTATE_OFFSET_ECX(%esp), %ecx
422 movl ISTATE_OFFSET_EDX(%esp), %edx
423 movl ISTATE_OFFSET_EBP(%esp), %ebp
[a35b458]424
[8078180]425 addl $(ISTATE_SOFT_SIZE + 4), %esp
426 iret
[f761f1eb]427.endm
428
[b808660]429#define LIST_0_63 \
430 0, 1, 2, 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,\
431 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,\
432 53,54,55,56,57,58,59,60,61,62,63
[6dce6af]433
[f761f1eb]434interrupt_handlers:
[1b20da0]435.irp cnt, LIST_0_63
[b808660]436 handler \cnt
437.endr
[f761f1eb]438
[da52547]439/** Print Unicode character to EGA display.
440 *
441 * If CONFIG_EGA is undefined or CONFIG_FB is defined
442 * then this function does nothing.
443 *
444 * Since the EGA can only display Extended ASCII (usually
445 * ISO Latin 1) characters, some of the Unicode characters
[ca8f84f]446 * can be displayed in a wrong way. Only newline and backspace
447 * are interpreted, all other characters (even unprintable) are
[da52547]448 * printed verbatim.
449 *
450 * @param %ebp+0x08 Unicode character to be printed.
451 *
452 */
[28a5ebd]453FUNCTION_BEGIN(early_putuchar)
[a35b458]454
[ebb3538]455#if ((defined(CONFIG_DEBUG_EARLY_PRINT)) && (defined(CONFIG_EGA)) && (!defined(CONFIG_FB)))
[a35b458]456
[da52547]457 /* Prologue, save preserved registers */
458 pushl %ebp
459 movl %esp, %ebp
460 pushl %ebx
461 pushl %esi
462 pushl %edi
[a35b458]463
[da52547]464 movl $(PA2KA(0xb8000)), %edi /* base of EGA text mode memory */
465 xorl %eax, %eax
[a35b458]466
[da52547]467 /* Read bits 8 - 15 of the cursor address */
468 movw $0x3d4, %dx
469 movb $0xe, %al
470 outb %al, %dx
[a35b458]471
[da52547]472 movw $0x3d5, %dx
473 inb %dx, %al
474 shl $8, %ax
[a35b458]475
[da52547]476 /* Read bits 0 - 7 of the cursor address */
477 movw $0x3d4, %dx
478 movb $0xf, %al
479 outb %al, %dx
[a35b458]480
[da52547]481 movw $0x3d5, %dx
482 inb %dx, %al
[a35b458]483
[da52547]484 /* Sanity check for the cursor on screen */
485 cmp $2000, %ax
[28a5ebd]486 jb early_putuchar_cursor_ok
[a35b458]487
[da52547]488 movw $1998, %ax
[a35b458]489
[28a5ebd]490 early_putuchar_cursor_ok:
[a35b458]491
[da52547]492 movw %ax, %bx
493 shl $1, %eax
494 addl %eax, %edi
[a35b458]495
[da52547]496 movl 0x08(%ebp), %eax
[a35b458]497
[da52547]498 cmp $0x0a, %al
[28a5ebd]499 jne early_putuchar_backspace
[a35b458]500
[da52547]501 /* Interpret newline */
[a35b458]502
[da52547]503 movw %bx, %ax /* %bx -> %dx:%ax */
504 xorw %dx, %dx
[a35b458]505
[da52547]506 movw $80, %cx
507 idivw %cx, %ax /* %dx = %bx % 80 */
[a35b458]508
[da52547]509 /* %bx <- %bx + 80 - (%bx % 80) */
510 addw %cx, %bx
511 subw %dx, %bx
[a35b458]512
[28a5ebd]513 jmp early_putuchar_skip
[a35b458]514
[28a5ebd]515 early_putuchar_backspace:
[a35b458]516
[ca8f84f]517 cmp $0x08, %al
[28a5ebd]518 jne early_putuchar_print
[a35b458]519
[ca8f84f]520 /* Interpret backspace */
[a35b458]521
[ca8f84f]522 cmp $0x0000, %bx
[28a5ebd]523 je early_putuchar_skip
[a35b458]524
[ca8f84f]525 dec %bx
[28a5ebd]526 jmp early_putuchar_skip
[a35b458]527
[28a5ebd]528 early_putuchar_print:
[a35b458]529
[da52547]530 /* Print character */
[a35b458]531
[da52547]532 movb $0x0e, %ah /* black background, yellow foreground */
533 stosw
[b5382d4f]534 inc %bx
[a35b458]535
[28a5ebd]536 early_putuchar_skip:
[a35b458]537
[da52547]538 /* Sanity check for the cursor on the last line */
539 cmp $2000, %bx
[28a5ebd]540 jb early_putuchar_no_scroll
[a35b458]541
[da52547]542 /* Scroll the screen (24 rows) */
543 movl $(PA2KA(0xb80a0)), %esi
544 movl $(PA2KA(0xb8000)), %edi
[22c3444]545 movl $960, %ecx
546 rep movsl
[a35b458]547
[da52547]548 /* Clear the 24th row */
549 xorl %eax, %eax
[22c3444]550 movl $40, %ecx
551 rep stosl
[a35b458]552
[da52547]553 /* Go to row 24 */
554 movw $1920, %bx
[a35b458]555
[28a5ebd]556 early_putuchar_no_scroll:
[a35b458]557
[da52547]558 /* Write bits 8 - 15 of the cursor address */
559 movw $0x3d4, %dx
560 movb $0xe, %al
561 outb %al, %dx
[a35b458]562
[da52547]563 movw $0x3d5, %dx
564 movb %bh, %al
565 outb %al, %dx
[a35b458]566
[da52547]567 /* Write bits 0 - 7 of the cursor address */
568 movw $0x3d4, %dx
569 movb $0xf, %al
570 outb %al, %dx
[a35b458]571
[da52547]572 movw $0x3d5, %dx
573 movb %bl, %al
574 outb %al, %dx
[a35b458]575
[da52547]576 /* Epilogue, restore preserved registers */
577 popl %edi
578 popl %esi
579 popl %ebx
580 leave
[a35b458]581
[da52547]582#endif
[a35b458]583
[da52547]584 ret
[28a5ebd]585FUNCTION_END(early_putuchar)
[da52547]586
Note: See TracBrowser for help on using the repository browser.