Merge branch 'master' of git://github.com/illumos/illumos-gate
[unleashed.git] / usr / src / grub / grub-0.97 / stage2 / asm.S
blob5d6faf70104cfc449ab82fdbae3f1c46a90abd4b
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
22  * Note: These functions defined in this file may be called from C.
23  *       Be careful of that you must not modify some registers. Quote
24  *       from gcc-2.95.2/gcc/config/i386/i386.h:
25         
26    1 for registers not available across function calls.
27    These must include the FIXED_REGISTERS and also any
28    registers that can be used without being saved.
29    The latter must include the registers where values are returned
30    and the register where structure-value addresses are passed.
31    Aside from that, you can include as many other registers as you like.
33   ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
34 {  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
35  */
37 #define ASM_FILE
39 #include "shared.h"
41 #ifdef STAGE1_5
42 # define        ABS(x)  ((x) - EXT_C(main) + 0x2200)
43 #else
44 # define        ABS(x)  ((x) - EXT_C(main) + 0x8200)
45 #endif
46         
47         .file   "asm.S"
49         .text
51         /* Tell GAS to generate 16-bit instructions so that this code works
52            in real mode. */
53         .code16
55 #ifndef STAGE1_5
56         /* 
57          * In stage2, do not link start.S with the rest of the source
58          * files directly, so define the start symbols here just to
59          * force ld quiet. These are not referred anyway.
60          */
61         .globl  start, _start
62 start:
63 _start:
64 #endif /* ! STAGE1_5 */
65         
66 ENTRY(main)
67         /*
68          *  Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
69          *  at 0x0:0x2200 in stage1.5.
70          */
71         ljmp $0, $ABS(codestart)
73         /*
74          *  Compatibility version number
75          *
76          *  These MUST be at byte offset 6 and 7 of the executable
77          *  DO NOT MOVE !!!
78          */
79         . = EXT_C(main) + 0x6
80         .byte   COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
82         /*
83          *  This is a special data area 8 bytes from the beginning.
84          */
86         . = EXT_C(main) + 0x8
88 VARIABLE(install_partition)
89         .long   0xFFFFFF
90 /* This variable is here only because of a historical reason.  */
91 VARIABLE(saved_entryno)
92         .long   0
93 VARIABLE(stage2_id)
94         .byte   STAGE2_ID
95 VARIABLE(force_lba)
96         .byte   0
97 VARIABLE(version_string)
98         .string VERSION
99 VARIABLE(config_file)
100 #ifndef STAGE1_5
101         .string "/boot/grub/menu.lst"
102 #else   /* STAGE1_5 */
103         .long   0xffffffff
104         .string "/boot/grub/stage2"
105 #endif  /* STAGE1_5 */
107         /*
108          *  Leave some breathing room for the config file name.
109          */
111         . = EXT_C(main) + 0x60
112 VARIABLE(fake_mboot)
113         .long   0x1BADB002
114         .long   0x00010003 
115         .long   -0x1BAEB005     
116         /* 
117          * installgrub will place the rest of the fake 
118          * multiboot header here.
119          */
120         .= EXT_C(main) + 0x140
121 /* the real mode code continues... */
122 codestart:
123         cli             /* we're not safe here! */
125         /* set up %ds, %ss, and %es */
126         xorw    %ax, %ax
127         movw    %ax, %ds
128         movw    %ax, %ss
129         movw    %ax, %es
131 #ifndef SUPPORT_DISKLESS
132         /*
133          * Save the sector number of the second sector (i.e. this sector)
134          * in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
135          */
136         ADDR32  movl    %ebp, EXT_C(install_second_sector)
137 #endif
138         
139         /* set up the real mode/BIOS stack */
140         movl    $STACKOFF, %ebp
141         movl    %ebp, %esp
143         sti             /* we're safe again */
145 #ifndef SUPPORT_DISKLESS
146         /* save boot drive reference */
147         ADDR32  movb    %dl, EXT_C(boot_drive)
149         /* reset disk system (%ah = 0) */
150         int     $0x13
151 #endif
153         /* transition to protected mode */
154         DATA32  call EXT_C(real_to_prot)
156         /* The ".code32" directive takes GAS out of 16-bit mode. */
157         .code32
159         /* clean out the bss */
161         /* set %edi to the bss starting address */
162 #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
163         movl    $__bss_start, %edi
164 #elif defined(HAVE_USCORE_EDATA_SYMBOL)
165         movl    $_edata, %edi
166 #elif defined(HAVE_EDATA_SYMBOL)
167         movl    $edata, %edi
168 #endif
170         /* set %ecx to the bss end */   
171 #if defined(HAVE_END_SYMBOL)
172         movl    $end, %ecx
173 #elif defined(HAVE_USCORE_END_SYMBOL)
174         movl    $_end, %ecx
175 #endif
177         /* compute the bss length */
178         subl    %edi, %ecx
179         
180         /* zero %al */
181         xorb    %al, %al
183         /* set the direction */
184         cld
185         
186         /* clean out */
187         rep
188         stosb
189         
190         /*
191          *  Call the start of main body of C code, which does some
192          *  of it's own initialization before transferring to "cmain".
193          */
194         call EXT_C(init_bios_info)
198  *  This call is special...  it never returns...  in fact it should simply
199  *  hang at this point!
200  */
202 ENTRY(stop)
203         call    EXT_C(prot_to_real)
205         /*
206          * This next part is sort of evil.  It takes advantage of the
207          * byte ordering on the x86 to work in either 16-bit or 32-bit
208          * mode, so think about it before changing it.
209          */
211 ENTRY(hard_stop)
212         hlt
213         jmp EXT_C(hard_stop)
215 #ifndef STAGE1_5
217 /**************************************************************************
218 UNDI_CALL - wrapper around real-mode UNDI API calls
219 **************************************************************************/
220 ENTRY(__undi_call)
221        pushl   %ebp
222        movl    %esp,%ebp
223        pushl   %esi
224        pushl   %edi
225        pushl   %ebx
227        movw    8(%ebp),%cx     /* Seg:off addr of undi_call_info_t struct */
228        movw    12(%ebp),%dx    /* Pass to 16-bit code in %cx:%dx */
230        call EXT_C(prot_to_real)
231        .code16
233        movw    %cx,%es         /* Seg:off addr of undi_call_info_t struct */
234        movw    %dx,%bx         /* into %es:%bx */
236        movw    %es:8(%bx),%ax  /* Transfer contents of undi_call_info_t */
237        pushw   %ax             /* structure to the real-mode stack */
238        movw    %es:6(%bx),%ax
239        pushw   %ax
240        movw    %es:4(%bx),%ax
241        pushw   %ax
243        lcall   *%es:0(%bx)     /* Do the UNDI call */
244        cld                     /* Don't know whether or not we need this */
245                                /* but pxelinux includes it for some reason, */
246                                /* so we put it in just in case. */
248        popw    %cx             /* Tidy up the stack */
249        popw    %cx
250        popw    %cx
251        movw    %ax,%cx         /* Return %ax via %cx */
253        DATA32 call EXT_C(real_to_prot)
254        .code32
256        xorl    %eax,%eax       /* %ax is returned via %cx */
257        movw    %cx,%ax
259        popl    %ebx
260        popl    %edi
261        popl     %esi
262        popl     %ebp
263        ret
265 /**************************************************************************
266 UNDI_IRQ_HANDLER - UNDI IRQ handler: calls PXENV_UNDI_ISR and send EOI
267 NOTE: For some reason, this handler needs to be aligned. Else, the
268         undi driver won't get the trigger count on some platforms.
269 **************************************************************************/
270         .align 4
271 ENTRY(_undi_irq_handler)
272         .code16
273         pushw   %ax
274         pushw   %bx
275         pushw   %cx
276         call    1f              /* Position-independent access to */
277 1:      popw    %bx             /* various locations.             */
278         pushw   %bx             /* save for after UNDI call */
280         /* set funcflag to PXENV_UNDI_ISR_IN_START */
281         movw    $1,%cs:(pxenv_undi_isr-1b+2)(%bx)
283         /* push pxenv_undi_isr struct on stack */
284         movl    $(ABS(pxenv_undi_isr)),%eax
285         movw    %ax,%cx
286         shrl    $4,%eax         /* get segment */
287         pushw   %ax
288         andw    $0xf,%cx        /* get offset */
289         pushw   %cx
290         movw    $0x14,%ax       /* opcode PXENV_UNDI_ISR */
291         pushw   %ax
293         lcall   *%cs:(pxenv_entrypointsp-1b)(%bx)       /* Do the UNDI call */
294         cld                     /* Don't know whether or not we need this */
295                                 /* but pxelinux includes it for some reason, */
296                                 /* so we put it in just in case. */
297         popw    %cx             /* Tidy up the stack */
298         popw    %cx
299         popw    %cx
301         popw    %bx             /* restore old position reg */
303         cmpw    $0,%ax          /* did the UNDI call succeed? */
304         jne     3f
305         movw    %cs:(pxenv_undi_isr-1b+2)(%bx),%ax
306         cmpw    $0,%ax          /* is this our interrupt? */
307         jne     3f
309         /* send EOI -- non specific for now */
310         movw    $0x20,%ax               /* ICR_EOI_NON_SPECIFIC */
311         movb    %cs:(pxenv_undi_irq-1b),%cl
312         cmpb    $8,%cl
313         jg      2f
314         outb    $0xa0                   /* PIC2_ICR */
315 2:      outb    $0x20                   /* PIC1_ICR */
317         /* increment trigger count */
318         incw    %cs:(EXT_C(_undi_irq_trigger_count)-1b)(%bx)
320         /* restore other registers */
321 3:      popw    %cx
322         popw    %bx
323         popw    %ax
324         iret
325 ENTRY(_undi_irq_trigger_count)
326 undi_irq_trigger_count:
327         .word   0
328 ENTRY(_undi_irq_chain_to)
329         .long   0
330 ENTRY(_undi_irq_chain)
331         .byte   0
332 ENTRY(_pxenv_undi_irq)
333 pxenv_undi_irq:
334         .byte   0
335 ENTRY(_pxenv_undi_entrypointsp)
336 pxenv_entrypointsp:
337         .word   0       /* offset */
338         .word   0       /* segment */
339 pxenv_undi_isr:
340         .word   0       /* status */
341         .word   0       /* funcflag */
342         .long   0       /* struct padding not used by ISR */
343         .long   0
344         .long   0
346         .code32
349  * stop_floppy()
351  * Stops the floppy drive from spinning, so that other software is
352  * jumped to with a known state.
353  */
354 ENTRY(stop_floppy)
355         pusha
356         call    EXT_C(prot_to_real)
357         .code16
358         xorb    %dl, %dl
359         int     $0x13
360         DATA32  call EXT_C(real_to_prot)
361         .code32
362         popa
363         ret
366  * grub_reboot()
368  * Reboot the system. At the moment, rely on BIOS.
369  */
370 ENTRY(grub_reboot)
371         call    EXT_C(prot_to_real)
372         .code16
373         /* cold boot */
374         movw    $0x0472, %di
375         movw    %ax, (%di)
376         ljmp    $0xFFFF, $0x0000
377         .code32
378         
380  * grub_halt(int no_apm)
382  * Halt the system, using APM if possible. If NO_APM is true, don't use
383  * APM even if it is available.
384  */
385 ENTRY(grub_halt)
386         /* get the argument */
387         movl    4(%esp), %eax
388         
389         /* see if zero */
390         testl   %eax, %eax
391         jnz     EXT_C(stop)
393         call    EXT_C(prot_to_real)
394         .code16
395         
396         /* detect APM */
397         movw    $0x5300, %ax
398         xorw    %bx, %bx
399         int     $0x15
400         jc      EXT_C(hard_stop)
401         /* don't check %bx for buggy BIOSes... */
403         /* disconnect APM first */
404         movw    $0x5304, %ax
405         xorw    %bx, %bx
406         int     $0x15
408         /* connect APM */
409         movw    $0x5301, %ax
410         xorw    %bx, %bx
411         int     $0x15
412         jc      EXT_C(hard_stop)
414         /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
415         movw    $0x530E, %ax
416         xorw    %bx, %bx
417         movw    $0x0101, %cx
418         int     $0x15
419         jc      EXT_C(hard_stop)
420         
421         /* set the power state to off */
422         movw    $0x5307, %ax
423         movw    $1, %bx
424         movw    $3, %cx
425         int     $0x15
427         /* shouldn't reach here */
428         jmp     EXT_C(hard_stop)
429         .code32
430         
432  * track_int13(int drive)
434  * Track the int13 handler to probe I/O address space.
435  */
436 ENTRY(track_int13)
437         pushl   %ebp
438         movl    %esp, %ebp
440         pushl   %ebx
441         pushl   %edi
443         /* copy the original int13 handler segment:offset */
444         movl    $0x4c, %edi
445         movl    (%edi), %eax
446         movl    %eax, track_int13_addr
447                 
448         /* replace the int1 handler */
449         movl    $0x4, %edi
450         pushl   (%edi)
451         movl    $ABS(int1_handler), %eax
452         movl    %eax, (%edi)
454         /* read the MBR to call int13 successfully */
455         movb    8(%ebp), %dl
456         
457         call    EXT_C(prot_to_real)
458         .code16
460         movw    $SCRATCHSEG, %ax
461         movw    %ax, %es
462         xorw    %bx, %bx
463         movw    $1, %cx
464         xorb    %dh, %dh
466         /* save FLAGS on the stack to emulate int13 */
467         pushfw
468         
469         /* set the TF flag */
470         /* FIXME: this can be simplified not to use AX */
471         pushfw
472         popw    %ax
473         orw     $0x100, %ax
474         pushw   %ax
475         popfw
477         movw    $0x0201, %ax
479         .byte   0x9a            /* lcall */
480 track_int13_addr:
481         .word   0               /* offset */
482         .word   0               /* segment */
484         /* TF is cleared here automatically */
485         
486         DATA32  call    EXT_C(real_to_prot)
487         .code32
489         /* restore the int1 handler */
490         movl    $0x4, %edi
491         popl    (%edi)
493         popl    %edi
494         popl    %ebx
495         popl    %ebp
496         
497         ret
501  * Check if the next instruction is I/O, and if this is true, add the
502  * port into the io map.
504  * Note: Probably this will make the execution of int13 very slow.
506  * Note2: In this implementation, all we can know is I/O-mapped I/O. It
507  * is impossible to detect memory-mapped I/O.
508  */
509 int1_handler:
510         .code16
511         
512         pushw   %bp
513         movw    %sp, %bp
514         pushw   %ds
515         pushw   %ax
516         pushw   %si
517         pushw   %dx
518         
519         /* IP */
520         movw    2(%bp), %si
521         /* CS */
522         movw    4(%bp), %ax
523         movw    %ax, %ds
525         /* examine the next instruction */
526 1:      lodsb   (%si), %al
527         /* skip this code if it is a prefix */
528         cmpb    $0x2E, %al
529         je      1b
530         cmpb    $0x36, %al
531         je      1b
532         cmpb    $0x3E, %al
533         je      1b
534         cmpb    $0x26, %al
535         je      1b
536         cmpb    $0x64, %al
537         jl      2f
538         cmpb    $0x67, %al
539         jle     1b
540 2:      cmpb    $0xF0, %al
541         jl      3f
542         cmpb    $0xF3, %al
543         jle     1b
544         
545 3:      /* check if this code is out* or in* */
547         /* ins? or outs? */
548         cmpb    $0x6C, %al
549         jl      4f
550         cmpb    $0x6F, %al
551         jle     5f
553 4:      /* in? or out? (register operand version) */
554         cmpb    $0xEC, %al
555         jl      6f
556         cmpb    $0xEF, %al
557         jle     5f
558         
559 6:      /* in? or out? (immediate operand version) */
560         cmpb    $0xE4, %al
561         jl      8f
562         cmpb    $0xE7, %al
563         jg      8f
565 7:      /* immediate has a port */
566         lodsb   (%si), %al
567         movzbw  %al, %dx
568         
569 5:      /* %dx has a port */
571         /* set %ds to zero */
572         xorw    %ax, %ax
573         movw    %ax, %ds
574                 
575         /* set %si to the io map */
576         movw    $ABS(EXT_C(io_map)), %si
578                 
579 9:      /* check if the io map already has the port */
580         lodsw   (%si), %ax
581         /* check if this is the end */
582         testw   %ax, %ax
583         jz      1f
584         /* check if this matches the port */
585         cmpw    %ax, %dx
586         jne     9b
587         /* if so, leave from this handler */
588         jmp     8f
589         
590 1:      /* check for the buffer overrun */
591         cmpw    $(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si
592         je      8f
593         /* add the port into the io map */
594         movw    %dx, -2(%si)
596 8:      /* restore registers */ 
597         popw    %dx
598         popw    %si
599         popw    %ax
600         popw    %ds
601         popw    %bp
603         iret
604         
605         .code32
607 ENTRY(io_map)
608         .space  (IO_MAP_SIZE + 1) * 2
609         
610         
612  * set_int15_handler(void)
614  * Set up int15_handler.
615  */
616 ENTRY(set_int15_handler)
617         pushl   %edi
618         
619         /* save the original int15 handler */
620         movl    $0x54, %edi
621         movw    (%edi), %ax
622         movw    %ax, ABS(int15_offset)
623         movw    2(%edi), %ax
624         movw    %ax, ABS(int15_segment)
626         /* save the new int15 handler */
627         movw    $ABS(int15_handler), %ax
628         movw    %ax, (%edi)
629         xorw    %ax, %ax
630         movw    %ax, 2(%edi)
632         popl    %edi
633         ret
637  * unset_int15_handler(void)
639  * Restore the original int15 handler
640  */
641 ENTRY(unset_int15_handler)
642         pushl   %edi
643         
644         /* check if int15_handler is set */
645         movl    $0x54, %edi
646         movw    $ABS(int15_handler), %ax
647         cmpw    %ax, (%edi)
648         jne     1f
649         xorw    %ax, %ax
650         cmpw    %ax, 2(%edi)
651         jne     1f
653         /* restore the original */
654         movw    ABS(int15_offset), %ax
655         movw    %ax, (%edi)
656         movw    ABS(int15_segment), %ax
657         movw    %ax, 2(%edi)
660         popl    %edi
661         ret
665  * Translate a key code to another.
667  * Note: This implementation cannot handle more than one length
668  * scancodes (such as Right Ctrl).
669  */
670         .code16
671 int15_handler:
672         /* if non-carrier, ignore it */
673         jnc     1f
674         /* check if AH=4F */
675         cmpb    $0x4F, %ah
676         jne     1f
678         /* E0 and E1 are special */
679         cmpb    $0xE1, %al
680         je      4f
681         cmpb    $0xE0, %al
682         /* this flag is actually the machine code (je or jmp) */
683 int15_skip_flag:        
684         je      4f
685         
686         pushw   %bp
687         movw    %sp, %bp
688         
689         pushw   %bx
690         pushw   %dx
691         pushw   %ds
692         pushw   %si
694         /* save bits 0-6 of %al in %dl */
695         movw    %ax, %dx
696         andb    $0x7f, %dl
697         /* save the highest bit in %bl */
698         movb    %al, %bl
699         xorb    %dl, %bl
700         /* set %ds to 0 */
701         xorw    %ax, %ax
702         movw    %ax, %ds
703         /* set %si to the key map */
704         movw    $ABS(EXT_C(bios_key_map)), %si
706         /* find the key code from the key map */
708         lodsw
709         /* check if this is the end */
710         testw   %ax, %ax
711         jz      3f
712         /* check if this matches the key code */
713         cmpb    %al, %dl
714         jne     2b
715         /* if so, perform the mapping */
716         movb    %ah, %dl
718         /* restore %ax */
719         movw    %dx, %ax
720         orb     %bl, %al
721         /* make sure that CF is set */
722         orw     $1, 6(%bp)
723         /* restore other registers */
724         popw    %si
725         popw    %ds
726         popw    %dx
727         popw    %bx
728         popw    %bp
729         iret
730         
732         /* tricky: jmp (0x74) <-> je (0xeb) */
733         xorb    $(0x74 ^ 0xeb), ABS(int15_skip_flag)
735         /* just cascade to the original */
736         /* ljmp */
737         .byte   0xea
738 int15_offset:   .word   0
739 int15_segment:  .word   0
741         .code32
743         .align  4       
744 ENTRY(bios_key_map)
745         .space  (KEY_MAP_SIZE + 1) * 2
746         
747         
749  * set_int13_handler(map)
751  * Copy MAP to the drive map and set up int13_handler.
752  */
753 ENTRY(set_int13_handler)
754         pushl   %ebp
755         movl    %esp, %ebp
757         pushl   %edi
758         pushl   %esi
760         /* copy MAP to the drive map */
761         movl    $(DRIVE_MAP_SIZE * 2), %ecx
762         movl    $ABS(drive_map), %edi
763         movl    8(%ebp), %esi
764         cld
765         rep
766         movsb
768         /* save the original int13 handler */
769         movl    $0x4c, %edi
770         movw    (%edi), %ax
771         movw    %ax, ABS(int13_offset)
772         movw    2(%edi), %ax
773         movw    %ax, ABS(int13_segment)
774         
775         /* decrease the lower memory size and set it to the BIOS memory */
776         movl    $0x413, %edi
777         decw    (%edi)
778         xorl    %eax, %eax
779         movw    (%edi), %ax
780         
781         /* compute the segment */
782         shll    $6, %eax
784         /* save the new int13 handler */
785         movl    $0x4c, %edi
786         movw    %ax, 2(%edi)
787         xorw    %cx, %cx
788         movw    %cx, (%edi)
790         /* copy int13_handler to the reserved area */
791         shll    $4, %eax
792         movl    %eax, %edi
793         movl    $ABS(int13_handler), %esi
794         movl    $(int13_handler_end - int13_handler), %ecx
795         rep
796         movsb
798         popl    %esi
799         popl    %edi
800         popl    %ebp
801         ret
803         
804 /* 
805  * Map a drive to another drive.
806  */
807         
808         .code16
809         
810 int13_handler:
811         pushw   %ax
812         pushw   %bp
813         movw    %sp, %bp
814         
815         pushw   %si
817         /* set %si to the drive map */
818         movw    $(drive_map - int13_handler), %si
819         /* find the drive number from the drive map */
820         cld
821 1:      
822         lodsw   %cs:(%si), %ax
823         /* check if this is the end */
824         testw   %ax, %ax
825         jz      2f
826         /* check if this matches the drive number */
827         cmpb    %al, %dl
828         jne     1b
829         /* if so, perform the mapping */
830         movb    %ah, %dl
832         /* restore %si */
833         popw    %si
834         /* save %ax in the stack */
835         pushw   %ax
836         /* simulate the interrupt call */
837         pushw   8(%bp)
838         /* set %ax and %bp to the original values */
839         movw    2(%bp), %ax
840         movw    (%bp), %bp
841         /* lcall */
842         .byte   0x9a
843 int13_offset:   .word   0
844 int13_segment:  .word   0
845         /* save flags */
846         pushf
847         /* restore %bp */
848         movw    %sp, %bp
849         /* save %ax */
850         pushw   %ax
851         /* set the flags in the stack to the value returned by int13 */
852         movw    (%bp), %ax
853         movw    %ax, 0xc(%bp)
854         /* check if should map the drive number */
855         movw    6(%bp), %ax
856         cmpw    $0x8, %ax
857         jne     3f
858         cmpw    $0x15, %ax
859         jne     3f
860         /* check if the mapping was performed */
861         movw    2(%bp), %ax
862         testw   %ax, %ax
863         jz      3f
864         /* perform the mapping */
865         movb    %al, %dl
867         popw    %ax
868         movw    4(%bp), %bp
869         addw    $8, %sp
870         iret
872         .align  4
873 drive_map:      .space  (DRIVE_MAP_SIZE + 1) * 2
874 int13_handler_end:
875         
876         .code32
877         
878         
880  * chain_stage1(segment, offset, part_table_addr)
882  *  This starts another stage1 loader, at segment:offset.
883  */
885 ENTRY(chain_stage1)
886         /* no need to save anything, just use %esp */
888         /* store %ESI, presuming %ES is 0 */
889         movl    0xc(%esp), %esi
891         /* store new offset */
892         movl    0x8(%esp), %eax
893         movl    %eax, offset
895         /* store new segment */
896         movw    0x4(%esp), %ax
897         movw    %ax, segment
899         /* set up to pass boot drive */
900         movb    EXT_C(boot_drive), %dl
902         call    EXT_C(prot_to_real)
903         .code16
905 #ifdef ABSOLUTE_WITHOUT_ASTERISK
906         DATA32  ADDR32  ljmp    (offset)
907 #else
908         DATA32  ADDR32  ljmp    *(offset)
909 #endif
910         .code32
911 #endif /* STAGE1_5 */
914 #ifdef STAGE1_5
916  * chain_stage2(segment, offset, second_sector)
918  *  This starts another stage2 loader, at segment:offset.  It presumes
919  *  that the other one starts with this same "asm.S" file, and passes
920  *  parameters by writing the embedded install variables.
921  */
923 ENTRY(chain_stage2)
924         /* no need to save anything, just use %esp */
926         /* store new offset */
927         movl    0x8(%esp), %eax
928         movl    %eax, offset
929         movl    %eax, %ebx
931         /* store new segment */
932         movw    0x4(%esp), %ax
933         movw    %ax, segment
934         shll    $4, %eax
936         /* generate linear address */
937         addl    %eax, %ebx
939         /* set up to pass the partition where stage2 is located in */
940         movl    EXT_C(current_partition), %eax
941         movl    %eax, (EXT_C(install_partition)-EXT_C(main))(%ebx)
943         /* set up to pass the drive where stage2 is located in */
944         movb    EXT_C(current_drive), %dl
946         /* set up to pass the second sector of stage2 */
947         movl    0xc(%esp), %ecx
949         call    EXT_C(prot_to_real)
950         .code16
952         movl    %ecx, %ebp
954 #ifdef ABSOLUTE_WITHOUT_ASTERISK
955         DATA32  ADDR32  ljmp    (offset)
956 #else
957         DATA32  ADDR32  ljmp    *(offset)
958 #endif
960         .code32
961 #endif /* STAGE1_5 */
962         
964  *  These next two routines, "real_to_prot" and "prot_to_real" are structured
965  *  in a very specific way.  Be very careful when changing them.
967  *  NOTE:  Use of either one messes up %eax and %ebp.
968  */
970 ENTRY(real_to_prot)
971         .code16
972         cli
974         /* load the GDT register */
975         DATA32  ADDR32  lgdt    gdtdesc
977         /* turn on protected mode */
978         movl    %cr0, %eax
979         orl     $CR0_PE_ON, %eax
980         movl    %eax, %cr0
982         /* jump to relocation, flush prefetch queue, and reload %cs */
983         DATA32  ljmp    $PROT_MODE_CSEG, $protcseg
985         /*
986          *  The ".code32" directive only works in GAS, the GNU assembler!
987          *  This gets out of "16-bit" mode.
988          */
989         .code32
991 protcseg:
992         /* reload other segment registers */
993         movw    $PROT_MODE_DSEG, %ax
994         movw    %ax, %ds
995         movw    %ax, %es
996         movw    %ax, %fs
997         movw    %ax, %gs
998         movw    %ax, %ss
1000         /* put the return address in a known safe location */
1001         movl    (%esp), %eax
1002         movl    %eax, STACKOFF
1004         /* get protected mode stack */
1005         movl    protstack, %eax
1006         movl    %eax, %esp
1007         movl    %eax, %ebp
1009         /* get return address onto the right stack */
1010         movl    STACKOFF, %eax
1011         movl    %eax, (%esp)
1013         /* zero %eax */
1014         xorl    %eax, %eax
1016         /* return on the old (or initialized) stack! */
1017         ret
1020 ENTRY(prot_to_real)
1021         /* just in case, set GDT */
1022         lgdt    gdtdesc
1024         /* save the protected mode stack */
1025         movl    %esp, %eax
1026         movl    %eax, protstack
1028         /* get the return address */
1029         movl    (%esp), %eax
1030         movl    %eax, STACKOFF
1032         /* set up new stack */
1033         movl    $STACKOFF, %eax
1034         movl    %eax, %esp
1035         movl    %eax, %ebp
1037         /* set up segment limits */
1038         movw    $PSEUDO_RM_DSEG, %ax
1039         movw    %ax, %ds
1040         movw    %ax, %es
1041         movw    %ax, %fs
1042         movw    %ax, %gs
1043         movw    %ax, %ss
1045         /* this might be an extra step */
1046         ljmp    $PSEUDO_RM_CSEG, $tmpcseg       /* jump to a 16 bit segment */
1048 tmpcseg:
1049         .code16
1051         /* clear the PE bit of CR0 */
1052         movl    %cr0, %eax
1053         andl    $CR0_PE_OFF, %eax
1054         movl    %eax, %cr0
1056         /* flush prefetch queue, reload %cs */
1057         DATA32  ljmp    $0, $realcseg
1059 realcseg:
1060         /* we are in real mode now
1061          * set up the real mode segment registers : DS, SS, ES
1062          */
1063         /* zero %eax */
1064         xorl    %eax, %eax
1066         movw    %ax, %ds
1067         movw    %ax, %es
1068         movw    %ax, %fs
1069         movw    %ax, %gs
1070         movw    %ax, %ss
1072         /* restore interrupts */
1073         sti
1075         /* return on new stack! */
1076         DATA32  ret
1078         .code32
1082  *   int biosdisk_int13_extensions (int ax, int drive, void *dap)
1084  *   Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP
1085  *   is passed for disk address packet. If an error occurs, return
1086  *   non-zero, otherwise zero.
1087  */
1089 ENTRY(biosdisk_int13_extensions)
1090         pushl   %ebp
1091         movl    %esp, %ebp
1093         pushl   %esi
1094         pushl   %ebx
1096         /* compute the address of disk_address_packet */
1097         movl    0x10(%ebp), %eax
1098         movw    %ax, %si
1099         xorw    %ax, %ax
1100         shrl    $4, %eax
1101         movw    %ax, %cx        /* save the segment to cx */
1103         /* drive */
1104         movb    0xc(%ebp), %dl
1105         /* ax */
1106         movw    0x8(%ebp), %bx
1107         /* enter real mode */
1108         call    EXT_C(prot_to_real)
1109         
1110         .code16
1111         movw    %bx, %ax
1112         movw    %cx, %ds
1113         int     $0x13           /* do the operation */
1114         movb    %ah, %dl        /* save return value */
1115         /* clear the data segment */
1116         xorw    %ax, %ax
1117         movw    %ax, %ds
1118         /* back to protected mode */
1119         DATA32  call    EXT_C(real_to_prot)
1120         .code32
1122         movb    %dl, %al        /* return value in %eax */
1124         popl    %ebx
1125         popl    %esi
1126         popl    %ebp
1128         ret
1129         
1131  *   int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
1132  *                          int nsec, int segment)
1134  *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
1135  *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
1136  *   return non-zero, otherwise zero.
1137  */
1139 ENTRY(biosdisk_standard)
1140         pushl   %ebp
1141         movl    %esp, %ebp
1143         pushl   %ebx
1144         pushl   %edi
1145         pushl   %esi
1147         /* set up CHS information */
1148         movl    0x10(%ebp), %eax
1149         movb    %al, %ch
1150         movb    0x18(%ebp), %al
1151         shlb    $2, %al
1152         shrw    $2, %ax
1153         movb    %al, %cl
1154         movb    0x14(%ebp), %dh
1155         /* drive */
1156         movb    0xc(%ebp), %dl
1157         /* segment */
1158         movw    0x20(%ebp), %bx
1159         /* save nsec and ah to %di */
1160         movb    0x8(%ebp), %ah
1161         movb    0x1c(%ebp), %al
1162         movw    %ax, %di
1163         /* enter real mode */
1164         call    EXT_C(prot_to_real)
1166         .code16
1167         movw    %bx, %es
1168         xorw    %bx, %bx
1169         movw    $3, %si         /* attempt at least three times */
1171 1:      
1172         movw    %di, %ax
1173         int     $0x13           /* do the operation */
1174         jnc     2f              /* check if successful */
1176         movb    %ah, %bl        /* save return value */
1177         /* if fail, reset the disk system */
1178         xorw    %ax, %ax
1179         int     $0x13
1180         
1181         decw    %si
1182         cmpw    $0, %si
1183         je      2f
1184         xorb    %bl, %bl
1185         jmp     1b              /* retry */
1186 2:      
1187         /* back to protected mode */
1188         DATA32  call    EXT_C(real_to_prot)
1189         .code32
1191         movb    %bl, %al        /* return value in %eax */
1192         
1193         popl    %esi
1194         popl    %edi
1195         popl    %ebx
1196         popl    %ebp
1198         ret
1202  *   int check_int13_extensions (int drive)
1204  *   Check if LBA is supported for DRIVE. If it is supported, then return
1205  *   the major version of extensions, otherwise zero.
1206  */
1208 ENTRY(check_int13_extensions)
1209         pushl   %ebp
1210         movl    %esp, %ebp
1212         pushl   %ebx
1214         /* drive */
1215         movb    0x8(%ebp), %dl
1216         /* enter real mode */
1217         call    EXT_C(prot_to_real)
1219         .code16
1220         movb    $0x41, %ah
1221         movw    $0x55aa, %bx
1222         int     $0x13           /* do the operation */
1223         
1224         /* check the result */
1225         jc      1f
1226         cmpw    $0xaa55, %bx
1227         jne     1f
1229         movb    %ah, %bl        /* save the major version into %bl */
1231         /* check if AH=0x42 is supported if FORCE_LBA is zero */
1232         movb    EXT_C(force_lba), %al
1233         testb   %al, %al
1234         jnz     2f
1235         andw    $1, %cx
1236         jnz     2f
1237         
1239         xorb    %bl, %bl
1241         /* back to protected mode */
1242         DATA32  call    EXT_C(real_to_prot)
1243         .code32
1245         movb    %bl, %al        /* return value in %eax */
1247         popl    %ebx
1248         popl    %ebp
1250         ret
1254  *   int get_diskinfo_standard (int drive, unsigned long *cylinders, 
1255  *                              unsigned long *heads, unsigned long *sectors)
1257  *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1258  *   error occurs, then return non-zero, otherwise zero.
1259  */
1261 ENTRY(get_diskinfo_standard)
1262         pushl   %ebp
1263         movl    %esp, %ebp
1265         pushl   %ebx
1266         pushl   %edi
1268         /* drive */
1269         movb    0x8(%ebp), %dl
1270         /* enter real mode */
1271         call    EXT_C(prot_to_real)
1273         .code16
1274         movb    $0x8, %ah
1275         int     $0x13           /* do the operation */
1276         /* check if successful */
1277         testb   %ah, %ah
1278         jnz     1f
1279         /* bogus BIOSes may not return an error number */
1280         testb   $0x3f, %cl      /* 0 sectors means no disk */
1281         jnz     1f              /* if non-zero, then succeed */
1282         /* XXX 0x60 is one of the unused error numbers */
1283         movb    $0x60, %ah
1285         movb    %ah, %bl        /* save return value in %bl */
1286         /* back to protected mode */
1287         DATA32  call    EXT_C(real_to_prot)
1288         .code32
1290         /* restore %ebp */
1291         leal    0x8(%esp), %ebp
1292         
1293         /* heads */
1294         movb    %dh, %al
1295         incl    %eax            /* the number of heads is counted from zero */
1296         movl    0x10(%ebp), %edi
1297         movl    %eax, (%edi)
1299         /* sectors */
1300         xorl    %eax, %eax
1301         movb    %cl, %al
1302         andb    $0x3f, %al
1303         movl    0x14(%ebp), %edi
1304         movl    %eax, (%edi)
1306         /* cylinders */
1307         shrb    $6, %cl
1308         movb    %cl, %ah
1309         movb    %ch, %al
1310         incl    %eax            /* the number of cylinders is 
1311                                    counted from zero */
1312         movl    0xc(%ebp), %edi
1313         movl    %eax, (%edi)
1315         xorl    %eax, %eax
1316         movb    %bl, %al        /* return value in %eax */
1318         popl    %edi
1319         popl    %ebx
1320         popl    %ebp
1322         ret
1325 #if 0           
1327  *   int get_diskinfo_floppy (int drive, unsigned long *cylinders, 
1328  *                            unsigned long *heads, unsigned long *sectors)
1330  *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1331  *   error occurs, then return non-zero, otherwise zero.
1332  */
1334 ENTRY(get_diskinfo_floppy)
1335         pushl   %ebp
1336         movl    %esp, %ebp
1338         pushl   %ebx
1339         pushl   %esi
1341         /* drive */
1342         movb    0x8(%ebp), %dl
1343         /* enter real mode */
1344         call    EXT_C(prot_to_real)
1346         .code16
1347         /* init probe value */
1348         movl    $probe_values-1, %esi
1350         xorw    %ax, %ax
1351         int     $0x13           /* reset floppy controller */
1353         incw    %si
1354         movb    (%si), %cl
1355         cmpb    $0, %cl         /* probe failed if zero */
1356         je      2f
1358         /* perform read */
1359         movw    $SCRATCHSEG, %ax
1360         movw    %ax, %es
1361         xorw    %bx, %bx
1362         movw    $0x0201, %ax
1363         movb    $0, %ch
1364         movb    $0, %dh
1365         int     $0x13
1367         /* FIXME: Read from floppy may fail even if the geometry is correct.
1368            So should retry at least three times.  */
1369         jc      1b              /* next value */
1370         
1371         /* succeed */
1372         jmp     2f
1373         
1374 probe_values:
1375         .byte   36, 18, 15, 9, 0
1376         
1378         /* back to protected mode */
1379         DATA32  call    EXT_C(real_to_prot)
1380         .code32
1382         /* restore %ebp */
1383         leal    0x8(%esp), %ebp
1384         
1385         /* cylinders */
1386         movl    0xc(%ebp), %eax
1387         movl    $80, %ebx
1388         movl    %ebx, (%eax)
1389         /* heads */
1390         movl    0x10(%ebp), %eax
1391         movl    $2, %ebx
1392         movl    %ebx, (%eax)
1393         /* sectors */
1394         movl    0x14(%ebp), %eax
1395         movzbl  %cl, %ebx
1396         movl    %ebx, (%eax)
1398         /* return value in %eax */
1399         xorl    %eax, %eax
1400         cmpb    $0, %cl
1401         jne     3f
1402         incl    %eax            /* %eax = 1 (non-zero) */
1404         popl    %esi
1405         popl    %ebx
1406         popl    %ebp
1408         ret
1409 #endif
1410         
1412 /* Source files are splitted, as they have different copyrights.  */
1413 #ifndef STAGE1_5
1414 # include "setjmp.S"
1415 # include "apm.S"
1416 #endif /* ! STAGE1_5 */
1417                 
1418         
1420 #ifndef STAGE1_5
1421 /* get_code_end() :  return the address of the end of the code
1422  * This is here so that it can be replaced by asmstub.c.
1423  */
1424 ENTRY(get_code_end)
1425         /* will be the end of the bss */
1426 # if defined(HAVE_END_SYMBOL)
1427         movl    $end, %eax
1428 # elif defined(HAVE_USCORE_END_SYMBOL)
1429         movl    $_end, %eax
1430 # endif
1431         shrl    $2, %eax                /* Round up to the next word. */
1432         incl    %eax
1433         shll    $2, %eax
1434         ret
1435 #endif /* ! STAGE1_5 */
1439  * get_memsize(i) :  return the memory size in KB. i == 0 for conventional
1440  *              memory, i == 1 for extended memory
1441  *      BIOS call "INT 12H" to get conventional memory size
1442  *      BIOS call "INT 15H, AH=88H" to get extended memory size
1443  *              Both have the return value in AX.
1445  */
1447 ENTRY(get_memsize)
1448         push    %ebp
1449         push    %ebx
1451         mov     0xc(%esp), %ebx
1453         call    EXT_C(prot_to_real)     /* enter real mode */
1454         .code16
1456         cmpb    $0x1, %bl
1457         DATA32  je      xext
1459         int     $0x12
1460         DATA32  jmp     xdone
1462 xext:
1463         movb    $0x88, %ah
1464         int     $0x15
1466 xdone:
1467         movw    %ax, %bx
1469         DATA32  call    EXT_C(real_to_prot)
1470         .code32
1472         movw    %bx, %ax
1473         pop     %ebx
1474         pop     %ebp
1475         ret
1478 #ifndef STAGE1_5
1482  * get_eisamemsize() :  return packed EISA memory map, lower 16 bits is
1483  *              memory between 1M and 16M in 1K parts, upper 16 bits is
1484  *              memory above 16M in 64K parts.  If error, return -1.
1485  *      BIOS call "INT 15H, AH=E801H" to get EISA memory map,
1486  *              AX = memory between 1M and 16M in 1K parts.
1487  *              BX = memory above 16M in 64K parts.
1489  */
1491 ENTRY(get_eisamemsize)
1492         push    %ebp
1493         push    %ebx
1495         call    EXT_C(prot_to_real)     /* enter real mode */
1496         .code16
1498         movw    $0xe801, %ax
1499         int     $0x15
1501         shll    $16, %ebx
1502         movw    %ax, %bx
1504         DATA32  call    EXT_C(real_to_prot)
1505         .code32
1507         movl    $0xFFFFFFFF, %eax
1508         cmpb    $0x86, %bh
1509         je      xnoteisa
1511         movl    %ebx, %eax
1513 xnoteisa:
1514         pop     %ebx
1515         pop     %ebp
1516         ret
1520  * get_mmap_entry(addr, cont) :  address and old continuation value (zero to
1521  *              start), for the Query System Address Map BIOS call.
1523  *  Sets the first 4-byte int value of "addr" to the size returned by
1524  *  the call.  If the call fails, sets it to zero.
1526  *      Returns:  new (non-zero) continuation value, 0 if done.
1528  * NOTE: Currently hard-coded for a maximum buffer length of 1024.
1529  */
1531 ENTRY(get_mmap_entry)
1532         push    %ebp
1533         push    %ebx
1534         push    %edi
1535         push    %esi
1537         /* place address (+4) in ES:DI */
1538         movl    0x14(%esp), %eax
1539         addl    $4, %eax
1540         movl    %eax, %edi
1541         andl    $0xf, %edi
1542         shrl    $4, %eax
1543         movl    %eax, %esi
1545         /* set continuation value */
1546         movl    0x18(%esp), %ebx
1548         /* set default maximum buffer size */
1549         movl    $0x14, %ecx
1551         /* set EDX to 'SMAP' */
1552         movl    $0x534d4150, %edx
1554         call    EXT_C(prot_to_real)     /* enter real mode */
1555         .code16
1557         movw    %si, %es
1558         movl    $0xe820, %eax
1559         int     $0x15
1561         DATA32  jc      xnosmap
1563         cmpl    $0x534d4150, %eax
1564         DATA32  jne     xnosmap
1566         cmpl    $0x14, %ecx
1567         DATA32  jl      xnosmap
1569         cmpl    $0x400, %ecx
1570         DATA32  jg      xnosmap
1572         DATA32  jmp     xsmap
1574 xnosmap:
1575         movl    $0, %ecx
1577 xsmap:
1578         DATA32  call    EXT_C(real_to_prot)
1579         .code32
1581         /* write length of buffer (zero if error) into "addr" */
1582         movl    0x14(%esp), %eax
1583         movl    %ecx, (%eax)
1585         /* set return value to continuation */
1586         movl    %ebx, %eax
1588         pop     %esi
1589         pop     %edi
1590         pop     %ebx
1591         pop     %ebp
1592         ret
1595  * get_rom_config_table()
1597  * Get the linear address of a ROM configuration table. Return zero,
1598  * if fails.
1599  */
1600         
1601 ENTRY(get_rom_config_table)
1602         pushl   %ebp
1603         pushl   %ebx
1605         /* zero %ebx for simplicity */
1606         xorl    %ebx, %ebx
1607         
1608         call    EXT_C(prot_to_real)
1609         .code16
1611         movw    $0xc0, %ax
1612         int     $0x15
1614         jc      no_rom_table
1615         testb   %ah, %ah
1616         jnz     no_rom_table
1617         
1618         movw    %es, %dx
1619         jmp     found_rom_table
1620         
1621 no_rom_table:
1622         xorw    %dx, %dx
1623         xorw    %bx, %bx
1624         
1625 found_rom_table:
1626         DATA32  call    EXT_C(real_to_prot)
1627         .code32
1629         /* compute the linear address */
1630         movw    %dx, %ax
1631         shll    $4, %eax
1632         addl    %ebx, %eax
1634         popl    %ebx
1635         popl    %ebp
1636         ret
1640  * int get_vbe_controller_info (struct vbe_controller *controller_ptr)
1642  * Get VBE controller information.
1643  */
1645 ENTRY(get_vbe_controller_info)
1646         pushl   %ebp
1647         movl    %esp, %ebp
1648         
1649         pushl   %edi
1650         pushl   %ebx
1652         /* Convert the linear address to segment:offset */
1653         movl    8(%ebp), %eax
1654         movl    %eax, %edi
1655         andl    $0x0000000f, %edi
1656         shrl    $4, %eax
1657         movl    %eax, %ebx
1659         call    EXT_C(prot_to_real)
1660         .code16
1662         movw    %bx, %es
1663         movw    $0x4F00, %ax
1664         int     $0x10
1666         movw    %ax, %bx
1667         DATA32  call    EXT_C(real_to_prot)
1668         .code32
1670         movzwl  %bx, %eax
1672         popl    %ebx
1673         popl    %edi
1674         popl    %ebp
1675         ret
1677         
1679  * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
1681  * Get VBE mode information.
1682  */
1684 ENTRY(get_vbe_mode_info)
1685         pushl   %ebp
1686         movl    %esp, %ebp
1687         
1688         pushl   %edi
1689         pushl   %ebx
1691         /* Convert the linear address to segment:offset */
1692         movl    0xc(%ebp), %eax
1693         movl    %eax, %edi
1694         andl    $0x0000000f, %edi
1695         shrl    $4, %eax
1696         movl    %eax, %ebx
1698         /* Save the mode number in %cx */
1699         movl    0x8(%ebp), %ecx
1700         
1701         call    EXT_C(prot_to_real)
1702         .code16
1704         movw    %bx, %es
1705         movw    $0x4F01, %ax
1706         int     $0x10
1708         movw    %ax, %bx
1709         DATA32  call    EXT_C(real_to_prot)
1710         .code32
1712         movzwl  %bx, %eax
1714         popl    %ebx
1715         popl    %edi
1716         popl    %ebp
1717         ret
1719         
1721  * int set_vbe_mode (int mode_number)
1723  * Set VBE mode. Don't support user-specified CRTC information.
1724  */
1726 ENTRY(set_vbe_mode)
1727         pushl   %ebp
1728         movl    %esp, %ebp
1729         
1730         pushl   %ebx
1732         /* Save the mode number in %bx */
1733         movl    0x8(%ebp), %ebx
1734         /* Clear bit D11 */
1735         andl    $0xF7FF, %ebx
1736         
1737         call    EXT_C(prot_to_real)
1738         .code16
1740         movw    $0x4F02, %ax
1741         int     $0x10
1743         movw    %ax, %bx
1744         DATA32  call    EXT_C(real_to_prot)
1745         .code32
1747         movzwl  %bx, %eax
1749         popl    %ebx
1750         popl    %ebp
1751         ret
1753                 
1755  * gateA20(int linear)
1757  * Gate address-line 20 for high memory.
1759  * This routine is probably overconservative in what it does, but so what?
1761  * It also eats any keystrokes in the keyboard buffer.  :-(
1762  */
1764 ENTRY(gateA20)
1765         /* first, try a BIOS call */
1766         pushl   %ebp
1767         movl    8(%esp), %edx
1768         
1769         call    EXT_C(prot_to_real)
1770         
1771         .code16
1772         movw    $0x2400, %ax
1773         testw   %dx, %dx
1774         jz      1f
1775         incw    %ax
1776 1:      stc
1777         int     $0x15
1778         jnc     2f
1780         /* set non-zero if failed */
1781         movb    $1, %ah
1783         /* save the status */
1784 2:      movb    %ah, %dl
1786         DATA32  call    EXT_C(real_to_prot)
1787         .code32
1789         popl    %ebp
1790         testb   %dl, %dl
1791         jnz     3f
1792         ret
1794 3:      /*
1795          * try to switch gateA20 using PORT92, the "Fast A20 and Init"
1796          * register
1797          */
1798         mov     $0x92, %dx
1799         inb     %dx, %al
1800         /* skip the port92 code if it's unimplemented (read returns 0xff) */
1801         cmpb    $0xff, %al
1802         jz      6f
1804         /* set or clear bit1, the ALT_A20_GATE bit */
1805         movb    4(%esp), %ah
1806         testb   %ah, %ah
1807         jz      4f
1808         orb     $2, %al
1809         jmp     5f
1810 4:      and     $0xfd, %al
1812         /* clear the INIT_NOW bit; don't accidently reset the machine */
1813 5:      and     $0xfe, %al
1814         outb    %al, %dx
1816 6:      /* use keyboard controller */
1817         pushl   %eax
1819         call    gloop1
1821         movb    $KC_CMD_WOUT, %al
1822         outb    $K_CMD
1824 gloopint1:
1825         inb     $K_STATUS
1826         cmpb    $0xff, %al
1827         jz      gloopint1_done
1828         andb    $K_IBUF_FUL, %al
1829         jnz     gloopint1
1831 gloopint1_done:
1832         movb    $KB_OUTPUT_MASK, %al
1833         cmpb    $0, 0x8(%esp)
1834         jz      gdoit
1836         orb     $KB_A20_ENABLE, %al
1837 gdoit:
1838         outb    $K_RDWR
1840         call    gloop1
1842         /* output a dummy command (USB keyboard hack) */
1843         movb    $0xff, %al
1844         outb    $K_CMD
1845         call    gloop1
1846         
1847         popl    %eax
1848         ret
1850 gloop1:
1851         inb     $K_STATUS
1852         cmpb    $0xff, %al
1853         jz      gloop2ret
1854         andb    $K_IBUF_FUL, %al
1855         jnz     gloop1
1857 gloop2:
1858         inb     $K_STATUS
1859         andb    $K_OBUF_FUL, %al
1860         jz      gloop2ret
1861         inb     $K_RDWR
1862         jmp     gloop2
1864 gloop2ret:
1865         ret
1868 ENTRY(patch_code)       /* labels start with "pc_" */
1869         .code16
1871         mov     %cs, %ax
1872         mov     %ax, %ds
1873         mov     %ax, %es
1874         mov     %ax, %fs
1875         mov     %ax, %gs
1876         ADDR32  movl    $0, 0
1877 pc_stop:
1878         hlt
1879         DATA32  jmp     pc_stop
1880 ENTRY(patch_code_end)
1882         .code32
1886  * linux_boot()
1888  * Does some funky things (including on the stack!), then jumps to the
1889  * entry point of the Linux setup code.
1890  */
1892 VARIABLE(linux_text_len)
1893         .long   0
1894         
1895 VARIABLE(linux_data_tmp_addr)
1896         .long   0
1897         
1898 VARIABLE(linux_data_real_addr)
1899         .long   0
1900         
1901 ENTRY(linux_boot)
1902         /* don't worry about saving anything, we're committed at this point */
1903         cld     /* forward copying */
1905         /* copy kernel */
1906         movl    EXT_C(linux_text_len), %ecx
1907         addl    $3, %ecx
1908         shrl    $2, %ecx
1909         movl    $LINUX_BZIMAGE_ADDR, %esi
1910         movl    $LINUX_ZIMAGE_ADDR, %edi
1912         rep
1913         movsl
1915 ENTRY(big_linux_boot)
1916         movl    EXT_C(linux_data_real_addr), %ebx
1917         
1918         /* copy the real mode part */
1919         movl    EXT_C(linux_data_tmp_addr), %esi
1920         movl    %ebx, %edi
1921         movl    $LINUX_SETUP_MOVE_SIZE, %ecx
1922         cld
1923         rep
1924         movsb
1926         /* change %ebx to the segment address */
1927         shrl    $4, %ebx
1928         movl    %ebx, %eax
1929         addl    $0x20, %eax
1930         movl    %eax, linux_setup_seg
1931                         
1932         /* XXX new stack pointer in safe area for calling functions */
1933         movl    $0x4000, %esp
1934         call    EXT_C(stop_floppy)
1936         /* final setup for linux boot */
1938         call    EXT_C(prot_to_real)
1939         .code16
1941         /* final setup for linux boot */
1942         cli
1943         movw    %bx, %ss
1944         movw    $LINUX_SETUP_STACK, %sp
1945         
1946         movw    %bx, %ds
1947         movw    %bx, %es
1948         movw    %bx, %fs
1949         movw    %bx, %gs
1951         /* jump to start */
1952         /* ljmp */
1953         .byte   0xea
1954         .word   0
1955 linux_setup_seg:        
1956         .word   0
1957         .code32
1961  * multi_boot(int start, int mb_info)
1963  *  This starts a kernel in the manner expected of the multiboot standard.
1964  */
1966 ENTRY(multi_boot)
1967         /* no need to save anything */
1968         call    EXT_C(stop_floppy)
1970         movl    $0x2BADB002, %eax
1971         movl    0x8(%esp), %ebx
1973         /* boot kernel here (absolute address call) */
1974         call    *0x4(%esp)
1976         /* error */
1977         call    EXT_C(stop)
1979 #endif /* ! STAGE1_5 */
1980         
1982  * void console_putchar (int c)
1984  * Put the character C on the console. Because GRUB wants to write a
1985  * character with an attribute, this implementation is a bit tricky.
1986  * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
1987  * (TELETYPE OUTPUT). Otherwise, save the original position, put a space,
1988  * save the current position, restore the original position, write the
1989  * character and the attribute, and restore the current position.
1991  * The reason why this is so complicated is that there is no easy way to
1992  * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't
1993  * support setting a background attribute.
1994  */
1995 ENTRY(console_putchar)
1996         movl    0x4(%esp), %edx
1997         pusha
1998 #ifdef STAGE1_5
1999         movb    $0x07, %bl
2000 #else
2001         movl    EXT_C(console_current_color), %ebx
2002 #endif
2003         
2004         call    EXT_C(prot_to_real)
2005         .code16
2006         movb    %dl, %al
2007         xorb    %bh, %bh
2009 #ifndef STAGE1_5
2010         /* use teletype output if control character */
2011         cmpb    $0x7, %al
2012         je      1f
2013         cmpb    $0x8, %al
2014         je      1f
2015         cmpb    $0xa, %al
2016         je      1f
2017         cmpb    $0xd, %al
2018         je      1f
2020         /* save the character and the attribute on the stack */
2021         pushw   %ax
2022         pushw   %bx
2023         
2024         /* get the current position */
2025         movb    $0x3, %ah
2026         int     $0x10
2028         /* check the column with the width */
2029         cmpb    $79, %dl
2030         jl      2f
2031         
2032         /* print CR and LF, if next write will exceed the width */      
2033         movw    $0x0e0d, %ax
2034         int     $0x10
2035         movb    $0x0a, %al
2036         int     $0x10
2037         
2038         /* get the current position */
2039         movb    $0x3, %ah
2040         int     $0x10
2042 2:      
2043         /* restore the character and the attribute */
2044         popw    %bx
2045         popw    %ax
2046         
2047         /* write the character with the attribute */
2048         movb    $0x9, %ah
2049         movw    $1, %cx
2050         int     $0x10
2052         /* move the cursor forward */
2053         incb    %dl
2054         movb    $0x2, %ah
2055         int     $0x10
2057         jmp     3f
2058 #endif /* ! STAGE1_5 */
2059         
2060 1:      movb    $0xe, %ah
2061         int     $0x10
2062         
2063 3:      DATA32  call    EXT_C(real_to_prot)
2064         .code32
2065         
2066         popa
2067         ret
2070 #ifndef STAGE1_5
2072 /* this table is used in translate_keycode below */
2073 translation_table:
2074         .word   KEY_LEFT, 2
2075         .word   KEY_RIGHT, 6
2076         .word   KEY_UP, 16
2077         .word   KEY_DOWN, 14
2078         .word   KEY_HOME, 1
2079         .word   KEY_END, 5
2080         .word   KEY_DC, 4
2081         .word   KEY_BACKSPACE, 8
2082         .word   KEY_PPAGE, 7
2083         .word   KEY_NPAGE, 3
2084         .word   0
2085         
2087  * translate_keycode translates the key code %dx to an ascii code.
2088  */
2089         .code16
2091 translate_keycode:
2092         pushw   %bx
2093         pushw   %si
2094         
2095         movw    $ABS(translation_table), %si
2096         
2097 1:      lodsw
2098         /* check if this is the end */
2099         testw   %ax, %ax
2100         jz      2f
2101         /* load the ascii code into %ax */
2102         movw    %ax, %bx
2103         lodsw
2104         /* check if this matches the key code */
2105         cmpw    %bx, %dx
2106         jne     1b
2107         /* translate %dx, if successful */
2108         movw    %ax, %dx
2110 2:      popw    %si
2111         popw    %bx
2112         ret
2114         .code32
2115         
2118  * remap_ascii_char remaps the ascii code %dl to another if the code is
2119  * contained in ASCII_KEY_MAP.
2120  */
2121         .code16
2122         
2123 remap_ascii_char:
2124         pushw   %si
2125         
2126         movw    $ABS(EXT_C(ascii_key_map)), %si
2128         lodsw
2129         /* check if this is the end */
2130         testw   %ax, %ax
2131         jz      2f
2132         /* check if this matches the ascii code */
2133         cmpb    %al, %dl
2134         jne     1b
2135         /* if so, perform the mapping */
2136         movb    %ah, %dl
2138         /* restore %si */
2139         popw    %si
2141         ret
2143         .code32
2145         .align  4
2146 ENTRY(ascii_key_map)
2147         .space  (KEY_MAP_SIZE + 1) * 2
2148         
2151  * int console_getkey (void)
2152  * BIOS call "INT 16H Function 00H" to read character from keyboard
2153  *      Call with       %ah = 0x0
2154  *      Return:         %ah = keyboard scan code
2155  *                      %al = ASCII character
2156  */
2158 ENTRY(console_getkey)
2159         push    %ebp
2161 wait_for_key:
2162         call    EXT_C(console_checkkey)
2163         incl    %eax
2164         jz      wait_for_key
2166         call    EXT_C(prot_to_real)
2167         .code16
2169         int     $0x16
2171         movw    %ax, %dx                /* real_to_prot uses %eax */
2172         call    translate_keycode
2173         call    remap_ascii_char
2174         
2175         DATA32  call    EXT_C(real_to_prot)
2176         .code32
2178         movw    %dx, %ax
2180         pop     %ebp
2181         ret
2185  * int console_checkkey (void)
2186  *      if there is a character pending, return it; otherwise return -1
2187  * BIOS call "INT 16H Function 01H" to check whether a character is pending
2188  *      Call with       %ah = 0x1
2189  *      Return:
2190  *              If key waiting to be input:
2191  *                      %ah = keyboard scan code
2192  *                      %al = ASCII character
2193  *                      Zero flag = clear
2194  *              else
2195  *                      Zero flag = set
2196  */
2197 ENTRY(console_checkkey)
2198         push    %ebp
2199         xorl    %edx, %edx
2200         
2201         call    EXT_C(prot_to_real)     /* enter real mode */
2202         .code16
2204         movb    $0x1, %ah
2205         int     $0x16
2207         DATA32  jz      notpending
2208         
2209         movw    %ax, %dx
2210         call    translate_keycode
2211         call    remap_ascii_char
2212         DATA32  jmp     pending
2214 notpending:
2215         movl    $0xFFFFFFFF, %edx
2217 pending:
2218         DATA32  call    EXT_C(real_to_prot)
2219         .code32
2221         mov     %edx, %eax
2223         pop     %ebp
2224         ret
2226         
2228  * int console_getxy (void)
2229  * BIOS call "INT 10H Function 03h" to get cursor position
2230  *      Call with       %ah = 0x03
2231  *                      %bh = page
2232  *      Returns         %ch = starting scan line
2233  *                      %cl = ending scan line
2234  *                      %dh = row (0 is top)
2235  *                      %dl = column (0 is left)
2236  */
2239 ENTRY(console_getxy)
2240         push    %ebp
2241         push    %ebx                    /* save EBX */
2243         call    EXT_C(prot_to_real)
2244         .code16
2246         xorb    %bh, %bh                /* set page to 0 */
2247         movb    $0x3, %ah
2248         int     $0x10                   /* get cursor position */
2250         DATA32  call    EXT_C(real_to_prot)
2251         .code32
2253         movb    %dl, %ah
2254         movb    %dh, %al
2256         pop     %ebx
2257         pop     %ebp
2258         ret
2262  * void console_gotoxy(int x, int y)
2263  * BIOS call "INT 10H Function 02h" to set cursor position
2264  *      Call with       %ah = 0x02
2265  *                      %bh = page
2266  *                      %dh = row (0 is top)
2267  *                      %dl = column (0 is left)
2268  */
2271 ENTRY(console_gotoxy)
2272         push    %ebp
2273         push    %ebx                    /* save EBX */
2275         movb    0xc(%esp), %dl           /* %dl = x */
2276         movb    0x10(%esp), %dh          /* %dh = y */
2278         call    EXT_C(prot_to_real)
2279         .code16
2281         xorb    %bh, %bh                /* set page to 0 */
2282         movb    $0x2, %ah
2283         int     $0x10                   /* set cursor position */
2285         DATA32  call    EXT_C(real_to_prot)
2286         .code32
2288         pop     %ebx
2289         pop     %ebp
2290         ret
2292         
2294  * void console_cls (void)
2295  * BIOS call "INT 10H Function 09h" to write character and attribute
2296  *      Call with       %ah = 0x09
2297  *                      %al = (character)
2298  *                      %bh = (page number)
2299  *                      %bl = (attribute)
2300  *                      %cx = (number of times)
2301  */
2304 ENTRY(console_cls)
2305         push    %ebp
2306         push    %ebx                    /* save EBX */
2308         call    EXT_C(prot_to_real)
2309         .code16
2311         /* move the cursor to the beginning */
2312         movb    $0x02, %ah
2313         xorb    %bh, %bh
2314         xorw    %dx, %dx
2315         int     $0x10
2317         /* write spaces to the entire screen */
2318         movw    $0x0920, %ax
2319         movw    $0x07, %bx
2320         movw    $(80 * 25), %cx
2321         int     $0x10
2323         /* move back the cursor */
2324         movb    $0x02, %ah
2325         int     $0x10
2327         DATA32  call    EXT_C(real_to_prot)
2328         .code32
2330         pop     %ebx
2331         pop     %ebp
2332         ret
2334         
2336  * int console_setcursor (int on)
2337  * BIOS call "INT 10H Function 01h" to set cursor type
2338  *      Call with       %ah = 0x01
2339  *                      %ch = cursor starting scanline
2340  *                      %cl = cursor ending scanline
2341  */
2343 console_cursor_state:
2344         .byte   1
2345 console_cursor_shape:
2346         .word   0
2347         
2348 ENTRY(console_setcursor)
2349         push    %ebp
2350         push    %ebx
2352         /* check if the standard cursor shape has already been saved */
2353         movw    console_cursor_shape, %ax
2354         testw   %ax, %ax
2355         jne     1f
2357         call    EXT_C(prot_to_real)
2358         .code16
2360         movb    $0x03, %ah
2361         xorb    %bh, %bh
2362         int     $0x10
2364         DATA32  call    EXT_C(real_to_prot)
2365         .code32
2367         movw    %cx, console_cursor_shape
2369         /* set %cx to the designated cursor shape */
2370         movw    $0x2000, %cx
2371         movl    0xc(%esp), %ebx
2372         testl   %ebx, %ebx
2373         jz      2f
2374         movw    console_cursor_shape, %cx
2375 2:      
2376         call    EXT_C(prot_to_real)
2377         .code16
2379         movb    $0x1, %ah
2380         int     $0x10 
2382         DATA32  call    EXT_C(real_to_prot)
2383         .code32
2385         movzbl  console_cursor_state, %eax
2386         movb    %bl, console_cursor_state
2387         
2388         pop     %ebx
2389         pop     %ebp
2390         ret
2392 /* graphics mode functions */
2393 #ifdef SUPPORT_GRAPHICS
2394 VARIABLE(cursorX)
2395 .word   0
2396 VARIABLE(cursorY)
2397 .word   0
2398 VARIABLE(cursorCount)
2399 .word 0
2400 VARIABLE(cursorBuf)
2401 .byte   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
2403         
2405  * int set_videomode(mode)
2406  * BIOS call "INT 10H Function 0h" to set video mode
2407  *      Call with       %ah = 0x0
2408  *                      %al = video mode
2409  *      Returns old videomode.
2410  */
2411 ENTRY(set_videomode)
2412         push    %ebp
2413         push    %ebx
2414         push    %ecx
2416         movb    0x10(%esp), %cl
2418         call    EXT_C(prot_to_real)
2419         .code16
2421         xorw    %bx, %bx
2422         movb    $0xf, %ah
2423         int     $0x10                   /* Get Current Video mode */
2424         movb    %al, %ch
2425         xorb    %ah, %ah
2426         movb    %cl, %al
2427         int     $0x10                   /* Set Video mode */
2429         DATA32  call    EXT_C(real_to_prot)
2430         .code32
2432         xorb    %ah, %ah
2433         movb    %ch, %al
2435         pop     %ecx
2436         pop     %ebx
2437         pop     %ebp
2438         ret
2442  * unsigned char * graphics_get_font()
2443  * BIOS call "INT 10H Function 11h" to set font
2444  *      Call with       %ah = 0x11
2445  */
2446 ENTRY(graphics_get_font)
2447         push    %ebp
2448         push    %ebx
2449         push    %ecx
2450         push    %edx
2452         call    EXT_C(prot_to_real)
2453         .code16
2455         movw    $0x1130, %ax
2456         movb    $6, %bh         /* font 8x16 */
2457         int     $0x10
2458         movw    %bp, %dx
2459         movw    %es, %cx
2461         DATA32  call    EXT_C(real_to_prot)
2462         .code32
2464         xorl    %eax, %eax
2465         movw    %cx, %ax
2466         shll    $4, %eax
2467         movw    %dx, %ax
2469         pop     %edx
2470         pop     %ecx
2471         pop     %ebx
2472         pop     %ebp
2473         ret
2474         
2476         
2478  * graphics_set_palette(index, red, green, blue)
2479  * BIOS call "INT 10H Function 10h" to set individual dac register
2480  *      Call with       %ah = 0x10
2481  *                      %bx = register number
2482  *                      %ch = new value for green (0-63)
2483  *                      %cl = new value for blue (0-63)
2484  *                      %dh = new value for red (0-63)
2485  */
2487 ENTRY(graphics_set_palette)
2488         push    %ebp
2489         push    %eax
2490         push    %ebx
2491         push    %ecx
2492         push    %edx
2494         movw    $0x3c8, %bx             /* address write mode register */
2496         /* wait vertical retrace */
2498         movw    $0x3da, %dx
2499 l1b:    inb     %dx, %al        /* wait vertical active display */
2500         test    $8, %al
2501         jnz     l1b
2503 l2b:    inb     %dx, %al        /* wait vertical retrace */
2504         test    $8, %al
2505         jnz     l2b
2507         mov     %bx, %dx
2508         movb    0x18(%esp), %al         /* index */
2509         outb    %al, %dx
2510         inc     %dx
2512         movb    0x1c(%esp), %al         /* red */
2513         outb    %al, %dx
2515         movb    0x20(%esp), %al         /* green */
2516         outb    %al, %dx
2518         movb    0x24(%esp), %al         /* blue */
2519         outb    %al, %dx
2521         movw    0x18(%esp), %bx
2523         call    EXT_C(prot_to_real)
2524         .code16
2526         movb    %bl, %bh
2527         movw    $0x1000, %ax
2528         int     $0x10
2530         DATA32  call    EXT_C(real_to_prot)
2531         .code32 
2533         pop     %edx
2534         pop     %ecx
2535         pop     %ebx
2536         pop     %eax
2537         pop     %ebp
2538         ret
2540 #endif /* SUPPORT_GRAPHICS */
2541                 
2543  * getrtsecs()
2544  *      if a seconds value can be read, read it and return it (BCD),
2545  *      otherwise return 0xFF
2546  * BIOS call "INT 1AH Function 02H" to check whether a character is pending
2547  *      Call with       %ah = 0x2
2548  *      Return:
2549  *              If RT Clock can give correct values
2550  *                      %ch = hour (BCD)
2551  *                      %cl = minutes (BCD)
2552  *                      %dh = seconds (BCD)
2553  *                      %dl = daylight savings time (00h std, 01h daylight)
2554  *                      Carry flag = clear
2555  *              else
2556  *                      Carry flag = set
2557  *                         (this indicates that the clock is updating, or
2558  *                          that it isn't running)
2559  */
2560 ENTRY(getrtsecs)
2561         push    %ebp
2563         call    EXT_C(prot_to_real)     /* enter real mode */
2564         .code16
2566         movb    $0x2, %ah
2567         int     $0x1a
2569         DATA32  jnc     gottime
2570         movb    $0xff, %dh
2572 gottime:
2573         DATA32  call    EXT_C(real_to_prot)
2574         .code32
2576         movb    %dh, %al
2578         pop     %ebp
2579         ret
2581         
2583  * currticks()
2584  *      return the real time in ticks, of which there are about
2585  *      18-20 per second
2586  */
2587 ENTRY(currticks)
2588         pushl   %ebp
2590         call    EXT_C(prot_to_real)     /* enter real mode */
2591         .code16
2593         /* %ax is already zero */
2594         int     $0x1a
2596         DATA32  call    EXT_C(real_to_prot)
2597         .code32
2599         movl    %ecx, %eax
2600         shll    $16, %eax
2601         movw    %dx, %ax
2603         popl    %ebp
2604         ret
2606 ENTRY(amd64_rdmsr)
2607         movl    4(%esp), %ecx
2608         rdmsr
2609         movl    8(%esp), %ecx
2610         movl    %eax, (%ecx)
2611         movl    %edx, 4(%ecx)
2612         ret
2614 ENTRY(amd64_wrmsr)
2615         movl    8(%esp), %ecx
2616         movl    (%ecx), %eax
2617         movl    4(%ecx), %edx
2618         movl    4(%esp), %ecx
2619         wrmsr
2620         ret
2622 ENTRY(amd64_cpuid_insn)
2623         pushl   %ebp
2624         movl    %esp, %ebp
2625         pushl   %ebx
2626         pushl   %esi
2627         movl    0x8(%ebp), %eax
2628         movl    0xc(%ebp), %esi
2629         cpuid
2630         movl    %eax, 0x0(%esi)
2631         movl    %ebx, 0x4(%esi)
2632         movl    %ecx, 0x8(%esi)
2633         movl    %edx, 0xc(%esi)
2634         popl    %esi
2635         popl    %ebx
2636         popl    %ebp
2637         ret
2639         /*
2640          * Based on code from AMD64 Volume 3
2641          */
2642 ENTRY(amd64_cpuid_supported)
2643         pushf
2644         popl    %eax
2645         mov     %eax, %edx              /* save %eax for later */
2646         xorl    %eax, 0x200000          /* toggle bit 21 */
2647         pushl   %eax
2648         popf                            /* save new %eax to EFLAGS */
2649         pushf                           /* save new EFLAGS */
2650         popl    %ecx                    /* copy EFLAGS to %eax */
2651         xorl    %eax, %eax
2652         cmpl    %ecx, %edx              /* see if bit 21 has changes */
2653         jne     1f
2654         incl    %eax
2656         ret
2658 ENTRY(get_target_operating_mode)
2659         pusha
2661         call    EXT_C(prot_to_real)
2662         .code16
2664         movw    $0xec00, %ax
2665         movw    $0x03, %bx
2666         int     $0x15
2668         setc    %al
2669         movw    %ax, %cx
2671         DATA32  call    EXT_C(real_to_prot)
2672         .code32
2674         xorl    %eax, %eax
2675         movw    %cx, %ax
2676         movl    %eax, 0x1c(%esp)
2678         popa
2679         ret
2681 #endif /* ! STAGE1_5 */
2684  *  This is the area for all of the special variables.
2685  */
2687         .p2align        2       /* force 4-byte alignment */
2689 protstack:
2690         .long   PROTSTACKINIT
2692 VARIABLE(boot_drive)
2693 #ifdef SUPPORT_DISKLESS
2694         .long   NETWORK_DRIVE
2695 #else
2696         .long   0
2697 #endif
2699 VARIABLE(install_second_sector)
2700         .long   0
2701         
2702         /* an address can only be long-jumped to if it is in memory, this
2703            is used by multiple routines */
2704 offset:
2705         .long   0x8000
2706 segment:
2707         .word   0
2709 VARIABLE(apm_bios_info)
2710         .word   0       /* version */
2711         .word   0       /* cseg */
2712         .long   0       /* offset */
2713         .word   0       /* cseg_16 */
2714         .word   0       /* dseg_16 */
2715         .word   0       /* cseg_len */
2716         .word   0       /* cseg_16_len */
2717         .word   0       /* dseg_16_len */
2718         
2720  * This is the Global Descriptor Table
2722  *  An entry, a "Segment Descriptor", looks like this:
2724  * 31          24         19   16                 7           0
2725  * ------------------------------------------------------------
2726  * |             | |B| |A|       | |   |1|0|E|W|A|            |
2727  * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |
2728  * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
2729  * ------------------------------------------------------------
2730  * |                             |                            |
2731  * |        BASE 15..0           |       LIMIT 15..0          |
2732  * |                             |                            |
2733  * ------------------------------------------------------------
2735  *  Note the ordering of the data items is reversed from the above
2736  *  description.
2737  */
2739         .p2align        2       /* force 4-byte alignment */
2740 gdt:
2741         .word   0, 0
2742         .byte   0, 0, 0, 0
2744         /* code segment */
2745         .word   0xFFFF, 0
2746         .byte   0, 0x9A, 0xCF, 0
2748         /* data segment */
2749         .word   0xFFFF, 0
2750         .byte   0, 0x92, 0xCF, 0
2752         /* 16 bit real mode CS */
2753         .word   0xFFFF, 0
2754         .byte   0, 0x9E, 0, 0
2756         /* 16 bit real mode DS */
2757         .word   0xFFFF, 0
2758         .byte   0, 0x92, 0, 0
2761 /* this is the GDT descriptor */
2762 gdtdesc:
2763         .word   0x27                    /* limit */
2764         .long   gdt                     /* addr */